Friday, December 18, 2009

Useful Keyboard Shortcuts with AppleScript on Mac OS X

I've been using Ubuntu as my main OS for a while now. Gnome has a GUI for configuring global keyboard shortcuts (System -> Preferences -> Keyboard Shortcuts). For example I've set "Launch email client" to Ctrl+Alt+M and "Launch web browser" to Ctrl+Alt+W. I've also added a custom shortcut for launching GVim, another application that I frequently launch and close. Another action that I often perform via a shortcut is "Maximize window vertically" (Shift+Ctrl+Alt+Down). On high resolution screens (like 1920x1200), maximizing windows becomes less useful. When I need to see a lot of output in a terminal window for example, maximizing vertically makes much more sense. Being able to toggle play/pause on your music player of choice is also very useful when clicking on youtube videos embedded in blog posts or answering the phone. Banshee hooks into the Gnome mechanism, so I set Ctrl-F8 to toggle play/pause. What's interesting is that some other applications also support this mechanism. So when I launch Totem, it hijacks my shortcut. This is not really what I want most of the time.

The other day I was using my old Macbook and found myself using these shortcuts. Of course, nothing happened so I set out to fix this. Actually, I started using the shortcuts for launching a browser, shell and email client on Mac OS X. Back then I was using Quicksilver and had those shortcuts configured as global triggers. (Don't use the Quicksilver app from that link, BTW. Use Google Quick Search Box or Launchbar.) For toggling iTunes I used Sizzling Keys, which still works nicely. Nowadays, Quicksilver is bitrotting, so I was looking for something else. Surprisingly (to me, anyway), AppleScript is actually pretty useful. One nice feature of the Quicksilver triggers was that applications that are already running are not launched again. Instead, the running instance was activated (like when alt-tabbing to it). This was probably due to the one-instance-per-app behaviour that is common on Mac OS X. In my scripts, I check whether the app is running, which might not be neccessary. But it doesn't hurt and might be faster, so whatever. At any rate, for most apps I prefer this behaviour to the one on Ubuntu, where you can easily launch two instances of Thunderbird, for example. Which usually doesn't make sense.

Well, launching apps via AppleScript is not all that interesting. What about "Maximize window vertically"? The below code fragment determines the size of your screen, grabs the window that has the focus and resizes it vertically. You don't even need to figure out whether the dock is visible and how large it is. It will automatically do the right thing.
tell application "Finder"
    set desktopFrame to bounds of window of desktop
    set screenHeight to item 4 of desktopFrame
end tell

tell application "System Events"
    set activeApplication to name of the first process whose frontmost is true
end tell

tell application activeApplication
    set appFrame to bounds of the first window
    set xpos to item 1 of appFrame
    set ypos to 0
    set width to item 3 of appFrame
    set height to screenHeight
    set bounds of the first window to {xpos, ypos, width, height}
end tell
If you don't consider this to be the right thing, try Alt+Cmd+D to hide the dock, then maximize vertically again and unhide the dock (Alt+Cmd+D again). But I'm getting ahead of myself. So you have a bunch of scripts for manipulating the active window. How do you put those on global keyboard shortcuts? This might be possible without any third-party tools. I think you can put custom scripts in the services menu and assign those to keyboard shortcuts in System Preferences. I didn't try that though. There's a nice free tool called FastScripts Lite from the awesome indie mac developer Daniel Jalkut. As the "Lite" suggests, there is an even better version available that costs some money.
While we're at it, let's add some more useful window manipulation. As you can see in the screenshot above, there is a script to maximize the active window. Admit it, you sometimes miss a simple, consistent maximize function on the Mac. Yes, as a proper fanboy I should be claiming that the mac "maximize" behaviour is much smarter, resizes windows to the optimal size, etc. etc. Bullshit. As pointed out above, on a very large monitor maximizing a window to take up the whole screen is often useless. But my Macbook only does 1280x800. And oftentimes I want Safari to maximize, dammit. So now it does. The other two, "Maximize Window Left" and "Maximize Window Right" replicate a feature of Windows 7 that I found to be quite useful. "Maximize Window Left" puts the active window on the left side of the screen, maximizes it vertically and resizes it horizontally to take up half of the space. "Maximize Window Right" does the same thing on the right half of the screen. I already miss this on Ubuntu. It probably can be done in a window manager independent way by sending the right kind of data to the X server. This python library seems to be useful for doing that. I'm too lazy to try right now, though.

For the most part I like the whole shortcut functionality on the Mac better. Not launching apps that are already running for example is nice. Of course, everything can be done on Linux, but when you find yourself knee-deep in yak hair, consider putting down the text editor, scripting language and man page and go back to doing something useful. ;) What sucks about the AppleScript solution is that it doesn't work with all applications. I had some issues with MacVim and Firefox, IIRC. Anyway, Good Enough(tm), 80/20 and all that. Ah, almost forgot; you can download my scripts here.



Sunday, November 8, 2009

Using the Mozilla ActiveX Control in a Qt Application

The cross-platform application framework Qt includes WebKit, one of the three rendering engines that power todays web browsers. Creating a basic browser with Qt and WebKit takes less than five minutes.
But what about the other two engines, Internet Explorer and Gecko (used by Firefox)? Windows has a component technology called COM which allows applications to offer and consume object-oriented interfaces in a programming language independent way. One flavor of this technology is called ActiveX, which, if my understanding of this is correct, basically means "COM controls that put something on the screen". I haven't written much code that made use of Windows specific technologies like COM, so I might get some things wrong. But reading that Wikipedia article, I feel like I have a pretty good idea of how this is supposed to work. This should be sufficient to make sense of the Qt documentation on their ActiveQt Framework. All the tedious plumbing details are hopefully hidden inside ActiveQt. So let's get to it!

We'll start with embedding the existing Internet Explorer ActiveX control in a Qt application. If you don't have it already, download the Qt SDK for Windows, install it and, no, don't launch QtCreator just yet. According to the documentation, we first need to build ActiveQt. Assuming you got the latest SDK version at the time of writing and you installed it in the default location, building and installing the neccessary files is done on the command line in the following way:
C:\Documents and Settings\Your Name> cd \Qt\2009.04\bin

C:\Qt\2009.04\bin>qtenv
Setting up a MinGW/Qt only environment...
-- QTDIR set to C:\Qt\2009.04\qt
-- PATH set to C:\Qt\2009.04\qt\bin
-- Adding C:\Qt\2009.04\bin to PATH
-- Adding C:\WINDOWS\System32 to PATH
-- QMAKESPEC set to win32-g++

C:\Qt\2009.04\bin>cd ..\qt\src\activeqt

C:\Qt\2009.04\qt\src\activeqt>qmake

C:\Qt\2009.04\qt\src\activeqt>mingw32-make

---- Lots of output... -----

C:\Qt\2009.04\qt\src\activeqt>mingw32-make install

---- Some more output... -----
You should now have the files libQaxContainer.a, libQaxContainerd.a, libQaxServer.a and libQaxServerd.a in C:\Qt\2009.04\qt\lib.
Now it's time to fire up QtCreator and create a new project. Click File -> New, under Projects choose "Qt4 Gui Application" and give it a name and location. I chose "MultiBrowser" and C:\code. Accept the defaults for everything else. In the tree on the left, displaying the files of your project, double click MultiBrowser.pro and add a line containing:
CONFIG += qaxcontainer
The next step is to build the GUI. To do this open the file mainwindow.ui. This will load the GUI designer inside QtCreator. I won't give a step by step explanation of how to use the GUI designer. It's reasonably intuitive if you've ever used a similar tool. The first thing I added was a "File" menu with a "Quit" item that sends the close() signal to the MainWindow instance when clicked. Then I removed the toolbar, added two buttons for navigating back and forward and a QLineEdit for the URL. Below those, I put a QWidget. We need to replace this with a QAxWidget, which can host ActiveX controls. To do this, right click the object on the tree view in the upper left corner and choose "Promote to...". In the dialog enter the information as seen on the screenshot, click "Add" and then "Promote".

After telling QtCreator to apply a grid layout to the widgets, the result looks something like this:




Now open mainwindow.cpp and add the following line to the constructor
ui->browserWidget->setControl("{8856F961-340A-11D0-A96B-00C04FD705A2}");
Hit Ctrl-R to check whether what you have so far compiles successfully. What's left for a working browser is some event handling. If a user enters a URL in the QLineEdit and hits enter, the browser should open that URL and display pictures of cute kittens. Maybe the forward and back buttons should work too. So what we need for that are some slots that we will connect to the signals emitted by editUrl, backButton and forwardButton. In mainwindow.h add one of those slots, so that the file looks like this

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtGui/QMainWindow>

namespace Ui
{
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

public slots:
    void navigate();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

We don't need slots for forward and back, because those buttons can be hooked up directly to the QAxWidget instance. Our mainwindow.cpp file contains the implementation of the "navigate" slot as well as some calls to connect() in the constructor.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QAxWidget>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->browserWidget->setControl("{8856F961-340A-11D0-A96B-00C04FD705A2}");

    connect(ui->urlEdit,
            SIGNAL(returnPressed()),
            this,
            SLOT(navigate()));

    connect(ui->backButton,
            SIGNAL(clicked()),
            ui->browserWidget,
            SLOT(GoBack()));

    connect(ui->forwardButton,
            SIGNAL(clicked()),
            ui->browserWidget,
            SLOT(GoForward()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::navigate()
{
    ui->browserWidget->dynamicCall("Navigate(const QString&)",
                                   ui->urlEdit->text());
}

That's all we need for embedding IE in our app. Run it, type in a URL and hit enter. We got signal!

Ok, fine, but originally the plan was to embed the Gecko rendering engine in our Qt application. As the title already gives away, there is in fact an ActiveX control that lets us do that. It was written by former Netscape employee Adam Lock with the goal to be API compatible with the IE control. That means we can use this control exactly like the one we are already using. Download the installer from Adams website and run it. Once that is done, we need to slightly change our GUI to accommodate a second browser control. I chose a tab widget for this task. Open mainwindow.ui, delete the browserWidget and drag a QTabWidget in its place. The tab widget already contains two tabs. In the object tree in the upper left, select the first tab. The type of the objects that are contained in the tabs is QWidget. We can promote them to QAxWidget just like we did with the original QWidget we used above. I called the first one ieTab and the second geckoTab. The code inside mainwindow.cpp has to be adapted as well. Change every occurrence of browserWidget to ieTab and add the same code for geckoTab. Afterwards, mainwindow.cpp should look something like this

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QAxWidget>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->ieTab->setControl("{8856F961-340A-11D0-A96B-00C04FD705A2}");

    connect(ui->urlEdit,
            SIGNAL(returnPressed()),
            this,
            SLOT(navigate()));

    connect(ui->backButton,
            SIGNAL(clicked()),
            ui->ieTab,
            SLOT(GoBack()));

    connect(ui->forwardButton,
            SIGNAL(clicked()),
            ui->ieTab,
            SLOT(GoForward()));

    ui->geckoTab->setControl("{1339B54C-3453-11D2-93B9-000000000000}");

    connect(ui->backButton,
            SIGNAL(clicked()),
            ui->geckoTab,
            SLOT(GoBack()));

    connect(ui->forwardButton,
            SIGNAL(clicked()),
            ui->geckoTab,
            SLOT(GoForward()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::navigate()
{
    ui->ieTab->dynamicCall("Navigate(const QString&)",
                           ui->urlEdit->text());
    ui->geckoTab->dynamicCall("Navigate(const QString&)",
                              ui->urlEdit->text());
} 

If you run the app, you should now have both browsers embedded and displaying whatever URL was entered in the QLineEdit widget. We can check that we have in fact two different browsers by opening a website that displays the HTTP headers that are sent by IE and Gecko.

The headers sent by the embedded Internet Explorer control.


The headers sent by the embedded Mozilla Gecko control.

There's just one little issue. Notice in the second screenshot, the user agent contains the string "Gecko/20050410". That means our Mozilla ActiveX control uses a very old version of the Gecko rendering engine (You might see a slightly different string. I installed version 1.7.7 of the control for some reason, not 1.7.12. But that one is totally out of date as well). You can get a recent version of the control as part of the XULRunner SDK from the Mozilla FTP Server. Depending on how adventurous you feel, you can either choose the latest release or the nightly build from trunk. I get the impression that the Mozilla project does not really advertise the fact that there is a recent version of the ActiveX control inside the XULRunner SDK very much. I stumbled upon its existence via a comment by Mozilla Platform Evangelist Mark Finkle on this entry in Nick Bradburys blog. Grab the SDK, extract it somewhere, uninstall the old Mozilla control and register the new one in Windows. To do this, open a command line window, navigate to the directory where you put the SDK, change to the "bin" directory and enter "regsvr32 mozctlx.dll". To get an idea about how much better the recent Gecko engine is compared to the old one, we can look at the ACID browser test.



old and busted




latest'n'greatest

Pretty sweet, innit?


Friday, August 21, 2009

Switched to Zine

Update: Since writing this post, I've moved the blog to Blogger. Turns out, I'm not fond of system administration. Besides, Blogger has shiny gadgets, widgets and whatnots.

I’ve moved the blog to Zine and to a different server. Zine is quite nice. I’d like to see a plugin that implements one of the common APIs so that I can use a blogging client like ScribeFire. After installing this ReST plugin I was able to migrate the (little) content from my semi-homegrown blog into Zine. The import from an atom feed didn’t work for me for some reason.
The old blog was down quite a bit. When I noticed that the other day I had a look and found out what the problem was. There was a cron job, running imapfilter to filter my mails directly on the mail servers, so they would be filed correctly in all clients on all machines I use. Among those mail servers are those from Google. Apparently Google had some problems with their SSL certificates, since running imapfilter from the shell produced the following output:
Server certificate subject: /C=US/ST=California/L=Mountain View/O=Google Inc/CN=imap.gmail.com
Server certificate issuer: /C=US/O=Google Inc/CN=Google Internet Authority
Server key fingerprint: 35:D1:0A:42:F3:FE:61:4E:CD:0C:02:05:D1:CC:D9:52
ATTENTION: SSL/TLS certificate fingerprint mismatch.
Proceed with the connection (y/n)?
Sigh. So there were hundreds of imapfilter processes sitting there, waiting for input. The CPU was maxed out, but sorting by CPU load in top didn’t identify the culprit. No process was using much CPU time by itself. Death by scheduling. So, Google: Fix your SSL certificates. Or was it a mitm attack…? Of course, it couldn't possibly be my fault, right? ;)

Tuesday, June 2, 2009

Using Sphinx and git as a simple CMS

Suppose you want to be able to easily publish content to a website without using a CMS like Plone or Drupal. Let’s say in order to minimize the attack surface of your website you don’t want any dynamic HTML generation to take place. In this post I’ll demonstrate one way this can be done using existing tools. The main building blocks are Sphinx and git. First, I’ll set up a self-contained installation using virtualenv:
$ mkdir sphinx_playground
$ virtualenv --no-site-packages sphinx_playground
New python executable in sphinx_playground/bin/python
Installing setuptools............done.
$ cd sphinx_playground
$ source bin/activate
(sphinx_playground)$ easy_install -U Sphinx
Searching for Sphinx
Reading http://pypi.python.org/simple/Sphinx/
Reading http://sphinx.pocoo.org/
Best match: Sphinx 0.6.1
Downloading http://pypi.python.org/packages/2.6/S/Sphinx/Sphinx-0.6.1-py2.6.egg#md5=0c5baac650e48792124f71eabb0eb029
[...]
Finished processing dependencies for Sphinx
(sphinx_playground)$
  
Installing Sphinx with easy_install installs all dependencies, including docutils and Pygments. The next step is to create directories for the git repository containing files in restructuredText and for the HTML that Sphinx will produce from these files. Sphinx includes a script named sphinx-quickstart, that will do this, as well as create some initial files to get started.
(sphinx_playground)$ sphinx-quickstart
[...]
(sphinx_playground)$ cd source
(sphinx_playground)$ git init
Initialized empty Git repository in /some/path/sphinx_playground/source/.git/
(sphinx_playground)$ git add conf.py index.rst
(sphinx_playground)$ git commit -m "initial commit"
Created initial commit ce317ec: initial commit
 2 files changed, 214 insertions(+), 0 deletions(-)
 create mode 100644 conf.py
 create mode 100644 index.rst
(sphinx_playground)$
  
sphinx-quickstart asks a few questions regarding the configuration. I chose seperate directories for source and build and accepted the default for most of the other choices. The script created a makefile, which I can use to generate the HTML by typing
(sphinx_playground)$ make html
  
The HTML files will end up in build/html. By default, the directory containing the stylesheets used in these files is build/html/_static. You probably want to change how the resulting HTML looks. Luckily, Sphinx has the concept of themes. I won’t go into detail on how to use this feature, since I don’t have the slightest clue. It’s probably not very hard, though. Check out the Sphinx documentation on how to use HTML themes. What’s left is adding a post commit hook in the git repository. Creating hooks in git is just a matter of putting a file named post-commit (or pre-commit, etc.) in /path/to/repo/.git/hooks. Here’s an example for a simple post-commit script that calls make to generate the HTML:
(sphinx_playground)$ cat source/.git/hooks/post-commit
#!/bin/sh

PROJECT_DIR=/some/path/sphinx_playground

cd $PROJECT_DIR
make html
(sphinx_playground)$ chmod 744 source/.git/hooks/post-commit
  
Make sure the script is executable and then modify the file source/index.rst and commit the changes.
(sphinx_playground)$ cat index.rst
.. sphinx-playground documentation master file, created by
sphinx-quickstart on Sun May 31 23:01:49 2009.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.

Welcome to sphinx-playground's documentation!
=============================================

I'm a headline! Look at me!
---------------------------

Contents:

.. toctree::
:maxdepth: 2

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

(sphinx_playground)$ git add index.rst
(sphinx_playground)$ git commit -m "added silly headline"
sphinx-build -b html -d build/doctrees   source build/html
Making output directory...
Running Sphinx v0.6.1
loading pickled environment... not found
building [html]: targets for 1 source files that are out of date
updating environment: 1 added, 0 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
writing output... [100%] index
writing additional files... genindex search
copying static files... done
dumping search index... done
dumping object inventory... done
build succeeded.

Build finished. The HTML pages are in build/html.
Created commit 43a506c: added silly headline
1 files changed, 3 insertions(+), 0 deletions(-)
(sphinx_playground)$
  
If you point your browser to /some/path/build/html/index.html you should see the new headline on the page. Updates work as well, just change the file and commit again:
(sphinx_playground)$ cat index.rst
.. sphinx-playground documentation master file, created by
sphinx-quickstart on Sun May 31 23:01:49 2009.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.

Welcome to sphinx-playground's documentation!
=============================================

I'm a different headline! Look at me!
-------------------------------------

Contents:

.. toctree::
:maxdepth: 2

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

(sphinx_playground)$ git add index.rst
(sphinx_playground)$ git commit -m "changed silly headline"
sphinx-build -b html -d build/doctrees   source build/html
Running Sphinx v0.6.1
loading pickled environment... done
building [html]: targets for 1 source files that are out of date
updating environment: 0 added, 1 changed, 0 removed
reading sources... [100%] index
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
writing output... [100%] index
writing additional files... genindex search
copying static files... done
dumping search index... done
dumping object inventory... done
build succeeded.

Build finished. The HTML pages are in build/html.
Created commit 0d6ef5a: changed silly headline
1 files changed, 2 insertions(+), 2 deletions(-)
(sphinx_playground)$
  
You might wonder whether Sphinx regenerates the HTML for all source files, regardless of them being out of date or not. Luckily, Sphinx is smarter than that, as you can see below:
(sphinx_playground)$ cat newfile.rst
I'm just a new file
===================

Move along, there is nothing to see here.

(sphinx_playground)$ git add newfile.rst
(sphinx_playground)$ git commit -m "added new file"
sphinx-build -b html -d build/doctrees   source build/html
Running Sphinx v0.6.1
loading pickled environment... done
building [html]: targets for 0 source files that are out of date
updating environment: 1 added, 0 changed, 0 removed
reading sources... [100%] newfile
looking for now-outdated files... none found
pickling environment... done
checking consistency... /some/path/sphinx_playground/source/newfile.rst:: WARNING: document isn't included in any toctree
done
preparing documents... done
writing output... [100%] newfile
writing additional files... genindex search
copying static files... done
dumping search index... done
dumping object inventory... done
build succeeded, 1 warning.

Build finished. The HTML pages are in build/html.
Created commit 5a287cd: added new file
 1 files changed, 5 insertions(+), 0 deletions(-)
 create mode 100644 newfile.rst
(sphinx_playground)$
  
As you can see in the output, Sphinx correctly determined, that existing source files have not changed. Also note, that Sphinx emits a warning, saying that the new file is not mentioned in a table of contents. Well, that’s pretty much it. Obviously you can enhance the post commit hook in any way you see fit. Like uploading the generated HTML to a web server via FTP or ssh, for example.

Sunday, March 29, 2009

Bembel With Care

Check out http://www.bembel-with-care.de/

went out last night to do some bemblin'
my head is huge my hands are tremblin'
the room is turning all around
despite me lying on the ground
this ain't no harmless apple juice
i got the careless bemblin' blues

i got bad news the other day
what can i do, what can i say
thursday a ramblin bemblin' man
friday no more a bembel fan
i have to say, it is the truth
i have the careless bemblin' blues

i have no memory of last night
maybe i got into a fight
maybe i was raped by a bear
why did i not bembel with care
i might have puked on my new shoes
i have the careless bemblin' blues

Saturday, March 28, 2009

Building Okular on Kubuntu Jaunty

The idea behind Okular is to have a universal document viewer, instead of one application for PDF, one for CHM, etc. Much like Preview.app on Mac OS X, only better of course. ;)
Unfortunately, on the current release of Kubuntu, Jaunty Jackalope, Okular doesn’t display CHM files. There’s a bug report about this problem with status “fix released”. This isn’t actually as good as it sounds, since the fix is to remove the claim that the kdegraphics package (which contains Okular) supports the CHM format from the package description. Reminds me of what they say about how mathematicians answer questions; the answer is both correct and useless. ;)
So, what to do if you would like your Okular to actually display CHM files? How about building it from source? I know, this is tedious and basically defeats the whole point of having a distribution with “smart” package management. It would be slightly better to build a .deb from this custom binary and install it properly. I never bother doing this, though.
Anyway, here’s what I did. First make sure that you have your system set up to compile KDE stuff. It’s a good idea to pick a directory where you put all the code that you check out from KDE repositories (I chose ~/code/kde4) and a directory where you put the binaries (i.e. ~/apps/kde4). It’s recommended to set up some shell scripts to make the process more convenient. Check out this procedure on how to build KDevelop and/or the techbase article.
Install the development packages of the libraries that enable Okular to read all those file formats.
$ sudo apt-get install libchm-dev libepub-dev libdjvulibre-dev libpoppler-qt4-dev libspectre-dev libtiff-dev
You might assume that you can go ahead and just build Okular now, as described at http://okular.kde.org/download.php. That didn’t work for me. CMake failed with the following message:
CMake Error at ui/data/icons/CMakeLists.txt:1 (kde4_install_icons):
  Unknown CMake command "kde4_install_icons".


CMake Warning (dev) in CMakeLists.txt:
  No cmake_minimum_required command is present.  A line of code such as

    cmake_minimum_required(VERSION 2.6)

  should be added at the top of the file.  The version specified may be lower
  if you wish to support older CMake versions for this project.  For more
  information run "cmake --help-policy CMP0000".
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Configuring incomplete, errors occurred!
The macro CMake talks about can be found in the file KDE4Macros.cmake, which is part of the package kdelibs5-dev. I had this package installed, but apparently CMake doesn’t look for modules where kdelibs5-dev puts it. The file resides in the directory /usr/share/kde4/apps/cmake/modules/. Sounds like a directory that CMake really should be looking for modules in, doesn’t it? To fix this, I added the line
prepend CMAKE_MODULE_PATH /usr/share/kde4/apps/cmake/modules
to ~/.kde/env/kde4_setup_build_environment.sh. If you ran CMake before doing this (like I did when I got the error message shown above), make sure to delete the file kdegraphics/CMakeCache.txt. I didn’t plan to build the entire kdegraphics package, but when I ran cmake in kdegraphics/okular I got the error about the missing kde4_install_icons macro again. Therefore I followed the steps given on the Okular download page. CMake then informed me, that I’m missing some optional dependencies, which I installed with:
sudo apt-get install libqimageblitz-dev libgphoto2-dev libsane-dev libxxf86vm-dev libexiv2-dev libqca2-dev
That only set me back about 32 MB. Bring on the bloat! I’m well prepared. My root partition is 50 GB large. :) After that running
cmake -DCMAKE_INSTALL_PREFIX=$KDEDIR ..
in kdegraphics/build finally finished without complaining and make did what it’s supposed to. Unfortunately, the binary that fell out of make refused to open chm files, showing the same error message as before. I got fed up with it and stopped trying to get Okular working. I got back to it tonight, trying to figure out what went wrong. But after svn up and make install it worked. So, I don’t know what problem was, but at least it works now. :)


Friday, March 20, 2009

First Post!

Update: Since writing this post, I've moved the blog to Blogger. Turns out, I'm not fond of system administration. Besides, Blogger has shiny gadgets, widgets and whatnots.

So I decided to try this newfangled blogging thing that everyone is talking about. Maybe it's not just a fad after all. At this adoption rate expect me to start using twitter in about five years. ;)

In good internet tradition the first post is completely self-referential, describing what software I used to set this blog up.


Tools

One of the coolest features of the Django framework is its support for reusable applications. The concept of a generic foreign key is crucial for this to work, as well as some conventions like naming url patterns and adding a parameter for template names to view functions. By using some existing reusable apps, I was able to create this blog in a short amount of time while still having more flexibility for modification and extension than using a shrink-wrapped blog app like wordpress would provide. It seems to be a rite of passage for django programmers to write their own blog. I find the idea of doing that to be boring and pointless. Luckily, there's basic-apps. Since there are so many blogs built with Django out there I stole everything that seemed useful from them, like syntax highlighting and the help text for comment formatting.

Development and deployment

Using virtualenv I created a bootstrap script with an after_install function that installs pip and fetches some code from svn repositories.
import os, subprocess
def after_install(options, home_dir):
     subprocess.call([join(home_dir, 'bin', 'easy_install'), 'pip'])
     src = join(home_dir, 'src')
     if not os.path.exists(src):
         os.makedirs(src)
     curdir = os.getcwd()
     os.chdir('src')
     subprocess.call(['svn', 'co', 
      'http://django-basic-apps.googlecode.com/svn/trunk/', 'basic'])
     subprocess.call(['svn', 'co',
      'http://django-trackback.googlecode.com/svn/trunk/', 'django-trackback'])
     os.chdir(curdir)
     target = join(curdir, 'src', 'basic')
     link = join(curdir, 'lib', 'python2.5', 'site-packages', 'basic')
     os.symlink(target, link)
     target = join(curdir, 'src', 'django-trackback', 'trackback')
     link = join(curdir, 'lib', 'python2.5', 'site-packages', 'trackback')
     os.symlink(target, link)

Calling svn in a subprocess and symlinking to site-packages is pretty kludgy. Normally those repository URLs belong in the requirements.txt file. But basic-apps and django-trackback were missing setup.py files when I checked out the source. Maybe pip has a way to deal with that. Maybe I'll get around to look for this in the docs some day.

Speaking of pip, here's how my requirements.txt file looks like. I use rope for code completion in vim. Along the same line, ipython, django-extensions and Werkzeug are useful for development and debugging, but not really neccessary for running the blog.
flup
ipython
rope
markdown
docutils
BeautifulSoup
Werkzeug
pygments
-e svn+http://django-tagging.googlecode.com/svn/trunk/#egg=django-tagging
-e svn+http://code.djangoproject.com/svn/django/trunk/#egg=django-trunk
-e git+git://github.com/django-extensions/django-extensions.git#egg=django-extensions

After creating those files I created a virtualenv and ran pip to install the requirements.
$ mkdir myblog && cd myblog
$ python myblog-boot.py --no-site-packages .
[...]
$ source bin/activate
$ pip install -r requirements.txt


Django project

To glue all this goodness together I performed the following steps.

  • Create django project
  • $ django-admin startproject myblog
  • Edit settings.py, add to INSTALLED_APPS
'django.contrib.admin',<br />'django.contrib.markup',<br />'django.contrib.comments',<br />'django_extensions',<br />'tagging',<br />'basic.inlines',<br />'basic.blog',<br />'trackback',<br />

Here's my urls.py

# -*- coding: UTF-8 -*-
# vim: set fileencoding: utf-8

from django.conf import settings
from django.conf.urls.defaults import *
from django.contrib import admin
from basic.blog import views as blog_views
from basic.blog.feeds import BlogPostsFeed
from basic.blog.feeds import BlogPostsByCategory
from feeds import AllCommentsFeed
from feeds import AtomAllCommentsFeed
from feeds import AtomBlogPostsFeed
from feeds import AtomBlogPostsByCategory
from feeds import AtomCommentsForEntryFeed
from feeds import CommentsForEntryFeed
import views

admin.autodiscover()

rss_feeds = {
    'entries': BlogPostsFeed,
    'full-entries': BlogPostsFeed,
    'categories': BlogPostsByCategory,
    'entry-comments': CommentsForEntryFeed,
    'comments': AllCommentsFeed,
}

atom_feeds = {
    'entries': AtomBlogPostsFeed,
    'full-entries': AtomBlogPostsFeed,
    'categories': AtomBlogPostsByCategory,
    'entry-comments': AtomCommentsForEntryFeed,
    'comments': AtomAllCommentsFeed,
}

urlpatterns = patterns('',
    url(r'^(?P\d{4})/(?P\w{3})/(?P\d{1,2})/(?P[-\w]+)/$',
        view=blog_views.post_detail,
        name='blog_detail'),

    url(r'^(?P\d{4})/(?P\w{3})/(?P\d{1,2})/$',
        view=blog_views.post_archive_day,
        name='blog_archive_day'),

    url(r'^(?P\d{4})/(?P\w{3})/$',
        view=blog_views.post_archive_month,
        name='blog_archive_month'),

    url(r'^(?P\d{4})/$',
        view=blog_views.post_archive_year,
        name='blog_archive_year'),

    url('^$',
        view=blog_views.post_list,
        name='blog_index'),

    url('^archive/$',
        view=views.archive_list,
        name='archive_list'),

    url(r'^categories/(?P[-\w]+)/$',
        view=blog_views.category_detail,
        name='blog_category_detail'),

    url (r'^categories/$',
        view=blog_views.category_list,
        name='blog_category_list'),

    url (r'^search/$',
        view=blog_views.search,
        name='blog_search'),

    url(r'^page/(?P\w)/$',
        view=blog_views.post_list,
        name='blog_index_paginated'),

    url(r'^ping/', include('trackback.urls')),

    (r'^rss/(?P.*)/$', 'django.contrib.syndication.views.feed',
        {'feed_dict': rss_feeds}),

    (r'^atom/(?P.*)/$', 'django.contrib.syndication.views.feed',
        {'feed_dict': atom_feeds}),

    (r'^comments/', include('django.contrib.comments.urls')),

    (r'^admin/doc/', include('django.contrib.admindocs.urls')),
    (r'^admin/(.*)', admin.site.root),

    (r'^dev/random/$', views.randomize),

    (r'^pygments_lexers/$', views.pygments_lexers),
)

I wrote three little view functions of my own, but didn't bother creating an app for them. One is a primitive archive page, the second is a list of all lexers that pygments has installed and the last one is /dev/random. It will return either 4 or NINE NINE NINE NINE NINE NINE. I've linked to stackoverflow so you can see that I even stole that idea somewhere. But at least I improved it by adding the second value. It's actually using the python random module making it a true random device. ;)

For now I can only receive trackbacks/pingbacks, not send them. And I haven't really tried that, so it probably doesn't work either. I'll worry about that later, when there is some evidence that someone is actually reading this blog. The same strategy will be used for dealing with comment spam. ;)

Webserver Configuration

  • lighttpd
  • FastCGI
  • Hint: You might want to add FORCE_SCRIPT_NAME = "" to your settings.py.
TODO:

Yeah, I kinda got lazy in the end. ;) Anyway, that's it. Let's see whether I can come up with something more interesting for the next post.