As mentioned previously, I've been updating a graphical application written 10 years ago using the then-current GTK+ 1.2 libraries and GTK-- 1.2 C++ wrappers. To a fair extent most of the changes have been fairly rote changes as mentioned in the previous post. I've now got to the lower level code that implements things like custom drawing classes to represent the scientific information the application is used to process, which highlighted a few more changes in the interveening 10 years.

New wrapper classes

The most obvious lower level changes is that while GTK-- 1.2 had an extremely thin wrapper around the GDK functions (ie, the lower level drawing functions), gtkmm 2.x has a fairly complete set of drawing classes that wrap each set of the GDK functions. The next most obvious change is that all these functions use the Glib::RefPtr Smart Pointer, to handle reference counting. These two things combined mean that something like:

Gdk_GC                    gc;    // Drawing Graphics Context

become:

Glib::RefPtr{ Gdk::GC }  gc;    // Drawing Graphics Context
gc = Gdk::GC::create(drawable);

(where you have to imagine the "{" and "}" replaced by angle brackets, because the RSS feed fails to parse otherwise :-( )

As a result of this change, all of the calls of the form:

gc.set_foreground(...);

become:

gc->set_foreground(....);

because we now have a (smart) pointer rather than the actual object. And a few "do we really have the object" checks become a test for whether the pointer is true or not.

New Array Handling

Where the older Gdk functions expected to receive C arrays of various things plus a size, the newer Gdk C++ wrappers expect to receive an Glib::ArrayHandle. This is actually easier than it looks. Glib::arrayHandle is simply an alias for an appropriate standard container that behaves like an array, and any parameter expecting one of those types can take an std::vector or std::list or similar transparently. Glib::ArrayHandle also provides the usual basic iterators that one would expect, so for many purposes it can be treated like any other Standard C++ container.

Rendering text

In GTK-- 1.2, drawing simple strings was easily acomplished with something like:

Gdk_Font font("6x10");
drawingarea.draw_string(font, gc, x, y, "My Text");

and this worked for most simple cases (English text, rendered to a single line, etc).

In gtkmm 2.x, pangomm is involved which uses the Pango text rendering library that can handle more complex situations (bidirectional text, strings with mixed languages, multiple lines, etc). Which is nice if you need it, but inconvenient if you still just want to render a single short string in English on a single line -- since the old draw_string() method has been removed, with directions to use Pango instead.

The pangomm documentation does not help the situation, as one rapidly seems to be in a "can't get there from here" situation where many of the methods seem to require other Pango objects, and many of those need to be created by yet other Pango objects. After much hunting one discovers that Pango::Context can create many of the required objects, and the Gtk::Widget will give you a Pango::Context on demand. Unfortunately if you follow that path too far you discover that you can create a string of text to be rendered, but not actually find a way to render it without something else; the most common "something else" seems to be Cairo perhaps via cairomm -- see for example this quick summary by someone clearly struggling with the same issues, or the gtkmm guide to printing (which does use Cairo). However I didn't want to add yet another library dependency and set of methods to call so I kept struggling.

Fortunately there's a relatively easy equivilent to the old draw_string() method if you just keep digging long enough to find it out (the pangomm documentation leaves much to be desired from the point of view of how to get started). I eventually found this hint on using pango to draw text with gtkmm (which I see is supposed to be updated to require Cairo; sigh):

"The easiest way to create a Pango::Layout is to use create_pango_layout. Once created, the layout can be manipulated in various ways, including changing the text, font, etc. Finally, the layout can be rendered using the draw_layout method of Gdk::Drawable, which takes a Gdk::GC object, an x-position, a y-position and the layout itself."

So the replacement code is something like:

Pango::Layout layout = drawingarea.create_pango_layout(text);
layout.set_font_description(Pango::FontDescription("Monospace 6"));
layout.set_height(0);       // Magic "make one line" value
drawingarea.draw_layout(gc, x, y, layout);

and where the size of the text to be drawn needs to be known (eg, to clear out the background behind it), this can be established with:

layout.get_pixel_ink_extents();

which returns a Pango::Rectangle with the usual height, width, etc, values.

"Monospace 6" was chosen by eye to be approximately the same size as the old "6x10" font, which was an alias for one of the X bitmapped fixed size fonts; at 6pt its a relatively small font, and in this application is used for labels that shouldn't obscure the thing where they are being shown. The old "6x10" bitmapped font was, literally, 6 pixels wide and 10 pixels tall, always; the new pango font handling pretty much exclusively uses scalable fonts, so the results are not identical but should end up fairly similar. For simplicity I managed to eliminate the other fonts being set in the application, since they were set to "fixed" and a suitable font should be set automatically. (See also using X fonts in pango from the original pango (design) documentation.)

Speaking of fonts, the default font for menus and buttons seems to have changed to a larger size font between GTK+ 1.2 and GTK+ 2.x (or perhaps just paying more attention to the claimed DPI resulting in a larger font being chosen); to my eye the newly chosen font is too large since I'd rather not have large portions of my screen dedicated to the menus, buttons, etc, surrounding what I'm actually working on. Fortunately thanks to a hint on the Ubuntu forums, it's fairly easy to override GTK+ 2.x's default font, by creating ~/.gtkrc-2.0 containing something like:

style "font"
{
  font_name = "Sans 8"
}
widget_class "*" style "font"
gtk-font-name = "Sans 8"  

where the font specified could be different (but "Sans 8" comes out at about the same size and weight as with GTK+ 1.2), and presumably one could be more selective about widget classes this applies to.

Miscellaneous changes

A few methods have been renamed:

  • size() becomes set_size_request()

  • width() becomes get_width()

  • height() becomes get_height()

  • Colourmap alloc() becomes alloc_color()

  • draw_pixmap() becomes draw_drawable() (which took a while to track down)

  • Generic graphics contexts can be obtained by getting the Style object (with get_style()), and then calling get_white_gc(), etc on that.

And now for something different: A Brief, Incomplete, and Mostly Wrong History of Programming Languages, via threemonkeys. It's filled with geeky in jokes, and even occassionally touches on reality.

ETA, 2010-05-19: Fixed way to create graphics contexts after debugging experience; fixed font name for pango, and described more about choosing appropriate fonts.