Canvas Items

The GNOME Canvas wouldn't amount to as much without its ample selection of Canvas items. By choosing your Canvas items carefully, you can drastically reduce the amount of coding you need to do to mold the Canvas to your purposes. Out of the box, the Canvas comes with several vector-based items, a couple of pixmap items, a text-rendering item, and even an item for embedding GTK+ widgets into the Canvas. If these aren't enough for you, you can always create new customized Canvas items for your application. Unfortunately, we don't have room in this book to describe how to create new Canvas items. If you're interested, take a look at the source code for the current crop of Canvas items and read up on how to derive new objects from GtkObject.

Vector-Based Drawing Items

The Canvas comes with four vector-based drawing items: line, rectangle, ellipse, and polygon. These Canvas items share certain common properties for defining the appearance of the drawing items (see Table 11.1).

The three categories of properties here are the fill color, the stipple pattern, and the line width. Each of these three categories can hold only one value, so, for example, if you set the fill color property as fill_color, and then as fill_color_rgba, the second option would override the first. This is also the case with the two width properties.

Let's look at fill_color first. This property determines the item's interior color. For a closed polygon, rectangle, or ellipse, this is the color for the inside of the shape; for a GnomeCanvasLine item, fill_color refers to the color of the line itself. This is somewhat confusing because the line item doesn't have an interior area to fill. The interior of the line is the line.

The fill_color property has a gchar* type, which it passes on to gdk_color_parse( ) to retrieve an RGB value for that color (see Section 10.2.2). The other two related properties let you set the fill color with a GdkColor you've already allocated for fill_color_gdk, or with the native RGBA guint value for the fill_color_rgba property. Don't forget that the alpha channel is ignored in a GDK-mode Canvas, so unless you are using the anti-aliased Canvas, you should always set the alpha channel to 0xff.

In addition to setting the color of the item's fill area, you can set up a bitmap to mask out a repeating pattern throughout the area of your fill. The fill_stipple property expects a GdkBitmap* value, most likely created with gdk_bitmap_create_from_data( ). The respective Canvas item will tile this bitmap across the entire fill area covered by the fill_color properties. In the case of the GnomeCanvasLine item, the fill_stipple pattern will mask out the line itself. Note that the stipple is not adaptive at all. If your line changes direction, the stipple will not change direction. If you want a stipple pattern that, for example, is always perpendicular to your line, you will have to create different stipple patterns for each new angle of line segment. An alternative to this is the line_style property, which we'll discuss in Section 11.4.2. Note that the AA Canvas does not currently support the stipple properties, so stippling works only in a GDK Canvas.

The width properties are a little more straightforward; they refer to the width of the lines. The width_pixels property sets the width of all lines in that Canvas item- whether it's a line item or the outline of a closed shape-to a fixed width in canvas coordinates (i.e., screen pixels). If you zoom the Canvas in or out with gnome_canvas_set_pixels_per_unit( ), your lines will stay the same visible width. Conversely, width_units sets the line width in world coordinates, so when you zoom in, your lines will be visibly wider and vice versa. Note that width_pixels has a type of guint, just like the canvas coordinate system, and width_units has a type of gdouble, like world coordinates. Be very careful not to mix these units up in the parameter list because the two types are of different size and will disrupt the variable argument pars- ing if reversed. To illustrate, this function call would probably crash your application:

gnome_canvas_item_set(item, "width_units", 2, NULL);

The correct way to specify width_units is either to cast the value to a gdouble type or to explicitly include the decimal point, as in these two examples:

gnome_canvas_item_set(item, "width_units", (gdouble)2, NULL);
gnome_canvas_item_set(item, "width_units", 2.0, NULL);

The closed shapes have another set of properties for dealing with the appearance of their outlines. These properties have a one-to-one correspondence with the fill properties. Thus you can arrange for colors, transparencies, and stipple patterns for the outlines of an ellipse, rectangle, or polygon that are separate from those you have established for its interior. Conversely, you won't find the outline properties in a GnomeCanvasLine item because exactly the same characteristics are covered by the line's fill properties. Table 11.2 lists the outline properties.

We'll wrap up our discussion of fills and stipples with a brief example. The source code is given in Listing 11.1. This example will draw a tan ellipse with a red outline, 6.0 world coordinate units wide. The tan interior will have a diagonal hatching pattern, while the red outline will have a stipple pattern with horizontal stripes. Figure 11.3 shows how we arrived at the hexadecimal contents of the stipple bitmaps, and Figure 11.4 shows how this ellipse item will look on a Canvas. The code assumes that you've already created the Canvas elsewhere, and that group is a pointer to a GnomeCanvasGroup item inside that Canvas. For now, you can take for granted that the x1, y1, x2, and y2 properties define the ellipse's bounding box. Notice that we must put the top and left coordinates at 3.0 rather than 0.0, to avoid clipping the 6.0-unit-wide outline (assuming that our scrolling region starts at 0.0 in both axes and has a one-to-one scaling ratio, of course). This demonstrates the policy that the true coordinates of Canvas lines are centered in the middle of the line rather than on a rendered edge. We'll delve into shapes and sizes more deeply in Section 11.4.3.

Figure 11-3. Bitmaps for Stipple Masks

Figure 11-4. Stipple Mask Example

Listing 11.1 Sample Code for Stippled Ellipse Canvas Item

GnomeCanvasItem *item;
GdkBitmap *dstipple;
GdkBitmap *hstipple;
static char diagonal_stipple[] = { 0x0d, 0x0e, 0x07, 0x0b };
static char horizontal_stipple[] = { 0x03, 0x00, 0x00 };

/* Create bitmaps for stipple masks */
dstipple = gdk_bitmap_create_from_data(NULL,
  diagonal_stipple, 4, 4);
hstipple = gdk_bitmap_create_from_data(NULL,
  horizontal_stipple, 2, 3);

item = gnome_canvas_item_new(group, GNOME_TYPE_CANVAS_ELLIPSE,
  "x1", 3.0, "y1", 3.0, "x2", 250.0, "y2", 100.0,
  "fill_color", "tan",
  "fill_stipple", dstipple,
  "outline_color", "red",
  "outline_stipple", hstipple,
  "width_units", 6.0, NULL);

Line Styles

Just as the closed-shape Canvas items have outline properties that the line item doesn't have, the line item has some special properties that none of the closed-shape items have. Table 11.3 lists these properties. The GnomeCanvasLine item has quite a number of interesting features, allowing you to customize the arrowheads, the dotted line style, elbow shapes, and the general quality of the lines. Each feature has exactly one property name, unlike the fill, outline, and width properties.

Some of the line style properties correspond directly to native GDK line styles, and as such they work only in GDK mode. Don't be confused if your line styles suddenly disappear when you switch to AA mode, because they may not be supported in that mode. Listings 11.2 through 11.4 show the possible values of these enumerations, as defined in gtk+/gdk/gdktypes.h.

Listing 11.2 GdkCapStyle Enumeration

typedef enum
} GdkCapStyle;
Listing 11.3 GdkJoinStyle Enumeration

typedef enum
} GdkJoinStyle;
Listing 11.4 GdkLineStyle Enumeration

typedef enum
} GdkLineStyle;

Arrowheads are available in both Canvas modes. Each GnomeCanvasLine item can optionally have one arrowhead at the starting point and another at the ending point of the line. The Boolean first_arrowhead and last_arrowhead properties determine whether or not the line item should display an arrowhead at that position in the line. By default, the arrowheads are turned off. If a line item has more than one line segment (e.g., a line with four points and three line segments), the arrowheads will show up only at the first and last points. To support an arrowhead at every vertex, the Canvas item would in theory have to declare a variable-length array of Boolean nth_arrowhead properties, one for each point in the line. For reasons of simplicity, GnomeCanvasLine does not go this extra mile. If you want arrowheads at each point, you have to use a different GnomeCanvasLine item for each line segment.

The three remaining arrow_shape_* properties define the shape and size of all arrowheads on that line item. Each property determines a measurable distance, in world coordinates, that GnomeCanvasLine will trace when drawing the arrowheads. Starting from a point at the end of the line, arrow_shape_a runs straight out, parallel to the line, measuring how far forward the arrow will stretch. The arrow_shape_b property measures how far backward from the arrow's tip the arrowhead will extend. The final property, arrow_shape_c, measures how far perpendicularly from the line the arrow extends-that is, the arrowhead's width. Figure 11.5 illustrates what the shape properties look like in practice.

Figure 11-5. Arrow Shapes for GnomeCanvasLine Item

The Shape of the Item

Of the four vector-based Canvas items-rectangle, ellipse, polygon, and line- we have covered all the available properties except for the most important ones: the coordinates that determine their locations on the Canvas. Table 11.4 lists these properties.

The rectangle and ellipse items both use the opposite corners of a bounding box to determine their size and location on the Canvas. The rectangle inscribes itself directly on the bounding box, so that the bounding box be- comes the rectangle. The ellipse item renders itself inside of a similar bounding box. If you created a rectangle item and an ellipse item with the same bounding box, the rectangle would completely enclose the ellipse; the ellipse would meet up with the rectangle at the midpoint of each of the rectangle's four sides (see Figure 11.6).

Figure 11-6. Rectangle and Ellipse Canvas Items with the Same Bounding Box

In fact, the rectangle and ellipse items are derived from the same abstract Canvas item, GnomeCanvasRE, which defines all the properties used by both derived items, along with quite a lot of shared code for handling the bounding box, the fill colors, the stippling, and all the other properties. Rectangles and ellipses both use the x1, y1, x2, and y2 properties to define their bounding boxes. These properties are all world coordinates, which means they must be passed in as gdouble types. As we learned earlier, one of the most common reasons for crash-causing bugs in Canvas applications is accidentally passing an integer here instead of a gdouble, so be careful!

The line and polygon Canvas items use similar properties to define their locations, although their free-form, variable-point nature dictates that they use a technique different from that of the rectangle and the ellipse. A line or polygon can have as few as two points (in the case of the line) or three (in the case of the polygon), and as many points as you care to render. If these items were to support the same style of properties as the rectangle, they would have to define a potentially infinite number of item properties, along the lines of x1, y1, x2, y2, x3, y3, . . ., xn, yn. These two Canvas items solve the problem by wrapping their variable-length coordinate arrays inside a GnomeCanvasPoints structure and passing a pointer to that structure as the points item property. The GnomeCanvasPoints structure looks like this:

typedef struct
  int num_points;
  double *coords;
  int ref_count;
} GnomeCanvasPoints;

You can create the array and populate the structure with this function:

GnomeCanvasPoints *gnome_canvas_points_new (int num_points);

The num_points field holds the number of points in the array rather than the total length of the array. Thus a pentagon would have a num_points value of 5 and an array of 10 doubles, dynamically allocated and stored in the coords field. The x coordinates are always in the even array elements, the y coordinates in the odd elements. After you create the GnomeCanvasPoints structure, you can fill the array by accessing the coords array directly. You must be very careful not to overrun this array, and it's up to you to make sure you don't. The simplest way to fill the array is to set each element explicitly:

GnomeCanvasPoints *points;
points = gnome_canvas_points_new(3);
points->coords[0] = 0.0;     /* x1 */
points->coords[1] = 100.0;   /* y1 */
points->coords[2] = 50.0;    /* x2 */
points->coords[3] = 0.0;     /* y2 */
points->coords[4] = 100.0;   /* x3 */
points->coords[5] = 100.0;   /* y3 */

This method is not very scalable, however, and it can become rather messy for a complex polygon with lots of points. An alternative is to create your points in a static array, in which you can declare the series of points in a compact manner, and then use memcpy( ) to copy them to the existing coords array. This approach leads to leaner code but carries with it the dangers of pointer operations, which can increase the likelihood of crash-inducing bugs in your application. Keep your calls to memcpy( ) to a minimum, and pay particular attention to the difference between the number of points in the array (i.e., num_points), and the actual length of the array (i.e., num_points 2). Here's what the previous example would look like with the second approach:

GnomeCanvasPoints *points;
gdouble pts_array[] = { 0.0, 100.0, 50.0, 0.0, 100.0, 100.0 };
points = gnome_canvas_points_new(3);
memcpy(points->coords, pts_array, sizeof(pts_array));

When you're done with your GnomeCanvasPoints structure, you call the unref function to release it. GnomeCanvasPoints keeps a reference count so that you can safely share a points array with other items if necessary. Call the _ref function instead of the _new function for each extra item that wants to use the same array, after the first one. Later, as each item finishes with the array, the item unreferences it, thereby decrementing the ref_count field. When ref_count reaches zero, the GnomeCanvasPoints array automatically cleans itself up. The _ref and _unref functions look like this:

GnomeCanvasPoints *gnome_canvas_points_ref(
    GnomeCanvasPoints *points);
void gnome_canvas_points_unref(GnomeCanvasPoints *points);

An alternative line-based Canvas item that is not included in the 1.2 version of gnome-libs is the GnomeCanvasBpath item, from the gnome-print package. This item is very similar to GnomeCanvasLine, except it uses an ArtBpath structure to describe its shape rather than a GnomeCanvasPoints structure, and it includes an extra property to describe its winding rules. It may or may not someday be included in the gnome-libs package.

Widgets in the Canvas

Although the Canvas is most commonly used as a buffered drawing surface, its object-oriented nature makes it easy to extend into new areas. One such area is the ability to embed a widget into the Canvas as a full-fledged Canvas item. Since the GnomeCanvasWidget item is just a wrapper for a widget, it doesn't need many properties (see Table 11.5).

The widget property holds a pointer to the widget itself. The x, y, width, and height properties determine the widget's placement inside the Canvas, in world coordinates. If you need to resize a GnomeCanvasWidget item, you should do it with these properties rather than trying to call gtk_widget_set_usize( ) on it. If you try to bypass the Canvas properties, the Canvas will just return the widget to whatever size it thinks the widget should be during the next repaint.

The x and y coordinates don't necessarily point to the upper left corner of the widget. By default, if you don't set the anchor property, that's where the anchor will end up. Sometimes, however, it's nice to be able to align widgets with a different corner, at the widget's center, or even at the midpoint of a side. The anchor property gives you this power, borrowing the GtkAnchorType enumeration from GTK+. Listing 11.5 lists the alignment possibilities. The anchor types are based on the compass points, and should be self-explanatory.

Listing 11.5 GtkAnchorType Enumeration

typedef enum
} GtkAnchorType;

Text Canvas Items

So far, we've discussed Canvas items for drawing lines and polygons and for embedding widgets. Another important feature you'll need sooner or later is the ability to render text into the Canvas. The GnomeCanvasText item provides this functionality.

The text item shares several of its properties with the other items. Its x, y, and anchor properties work in the same way as those for the GnomeCanvasWidget item; they specify where the text is attached to the Canvas. GnomeCanvasText also shares the fill_color, fill_color_gdk, and fill_stipple properties with the vector-based items (see Table 11.1), to define the color and stipple pattern of the text. Table 11.6 shows all the properties of the text item.

You can set the text itself with the text property, and the font of that text with one of the three font properties: font, fontset, and font_gdk. Like the fill color properties of the vector-based canvas items, the font properties all affect the same value within the item. This value resolves more or less to a GdkFont* pointer, regardless of which font property you use. Changing the font may also trigger some formatting and size recalculations because the text may lie differently in the new font, especially if the text is centered. You can change the text justification with the justification property, which takes a value of GtkJustification (see Listing 11.6).

Listing 11.6 GtkJustification Enumeration

typedef enum
} GtkJustification;

The font_gdk property sets the GdkFont* value directly; you would use it if you already had a font allocated. The font and fontset properties allow you to set the font on the basis of the contents of a string. The format of this string is very carefully defined in the X11 specifications, in the X Logical Font Description (XLFD) document (at XLFD/). When taken to its full power, XLFD is quite complex and cumbersome, but thanks to its support for wildcard characters, you can manage with simple, generic usage. The GDK API reference documents (at also have a clear, concise description of XLFD. Essentially, the font property contains a single XLFD, while fontset holds a list of XLFDs through which X11 will continue to search until it finds a match.

Here are some examples of what XLFDs look like. You can probably get by with something very simple, like the first example:


The xfontsel application distributed with X11 is an excellent tool for browsing the fonts installed on your system, and you can even use it for building XLFD strings of your own, although you should be careful not to hard-code any obscure, narrowly defined fonts.

Because text layout has so many constraints, the text item offers a double handful of properties for controlling how the Canvas does it. The simplest properties, x_offset and y_offset, allow you to specify an additional offset in world coordinates from the anchor point to display the text. You can use the offsets to fine tune the position of the text without moving the anchor point. The offsets are useful if you have a changing message that needs a little tweaking from time to time. To automate the calculation of these offset tweaks, you'll need to find out the actual size of the rendered text. The read-only properties text_width and text_height supply this information, through the gtk_object_getv( ) function. The following code fragment creates a text item and then prints out the width and height of the text:

GtkArg args[2];
GnomeCanvasItem *item;

item = gnome_canvas_item_new(group, GNOME_TYPE_CANVAS_TEXT,
  "x", 20.0, "y", 50.0,
  "text", "Print this on the Canvas",
  "justification", GTK_JUSTIFY_CENTER,
  "anchor", GTK_ANCHOR_CENTER,
  "fill_color", "black",
  "font", "-*-clean-medium-r-normal-*-12-*-*-*-*-*-*",

args[0].name = "GnomeCanvasText::text_width";
args[1].name = "GnomeCanvasText::text_height";
gtk_object_getv(GTK_OBJECT(item), 2, args);

g_message("text width = %f, height = %f",
    GTK_VALUE_DOUBLE(args[0]), GTK_VALUE_DOUBLE(args[1]));

Another thing you may need to do if your Canvas is divided into discrete regions is clip the rendered text to a rectangular region. You certainly don't want text bleeding into other areas of your Canvas, especially if you give the user control over the displayable fonts and font sizes. It's often better to clip off the text than to let it clutter the surrounding interface. The clip_width and clip_height properties set the relative bounding box for your text item. The clip property switches the clipping on and off.

Although the GnomeCanvasText item provides a decent interface for adding text to a Canvas, it does have its weaknesses. The XLFD strings are fairly unwieldy, and the resolution of the fonts that GnomeCanvasText can load is not very good, especially when you need to scale them. A TrueType font server helps the fonts look better, but because they are X11 fonts they are still rendered from bitmaps and aren't anti-aliased.

The gnome-print package has an experimental Canvas item for rendering text, called GnomeCanvasHacktext. This item converts Adobe Type 1 fonts into an ArtBpath Bezier path and then renders it onto the Canvas for a smooth, anti-aliased rendering. You probably shouldn't use GnomeCanvasHacktext for day-to-day use until you're sure it has stabilized (at least until it no longer has the word "hack" in its name). GnomeCanvasHacktext was developed primarily as a stopgap to allow high-quality rendering for gnome-print without having to wait for anti-aliased fonts in X11. You can use it, but at your own risk: The GnomeCanvasHacktext API has tended to be rather unstable, and it may change again at any time. It may or may not eventually be included in the core gnome-libs package.

GTK+ 2.0 will have an entirely different font system, using the new Pango library ( With Pango, GTK+ 2.0 will support the Unicode specification and right-to-left languages like Hebrew, as well as the picto- graphic fonts of China, Japan, and so on. At this time the Canvas text item will likely be overhauled to bring it up to speed with the new font system.

Graphical Canvas Items

Graphics support for the Canvas has grown and matured throughout its illustrious life. When the Canvas was first introduced to gnome-libs, gdk-pixbuf did not exist, so GnomeCanvasImage, the original Canvas item for displaying graphics, was written to use the Imlib image-loading library. The properties for the GnomeCanvasImage item (see Table 11.7) closely match the properties for GnomeCanvasWidget (compare Table 11.5).

The main difference is that GnomeCanvasImage uses the image property rather than the widget property for setting its contents. You can create a GdkImlibImage structure with a quick call to gdk_imlib_load_image( ) and destroy it later with gdk_imlib_destroy_image( ). The following code frag- ment shows how to create a Canvas item in this way. The easiest approach is to set up a callback function to destroy the GdkImlibImage when the Canvas item is destroyed:

void destroy_imlib_image_cb(GtkObject *object, gpointer data)
  gdk_imlib_destroy_image (data);

GnomeCanvasImage* create_image_item(char *imagefile,
    GnomeCanvasGroup *group, gdouble x, gdouble y)
  GnomeCanvasItem *item;
  GdkImlibImage *image;

  image = gdk_imlib_load_image(imagefile);
  item = gnome_canvas_item_new(group,
    gnome_canvas_image_get_type(  ),
    "image", image,
    "x", x, "y", y,
    "width", (double)image->rgb_width,
    "height", (double)image->rgb_height,
    "anchor", GTK_ANCHOR_CENTER,

  /* Clean up GdkImlibImage when Canvas item is destroyed */
  gtk_signal_connect(GTK_OBJECT (item), "destroy",
    (GtkSignalFunc) destroy_imlib_image_cb, image);

  return item;

This code will work in both GDK and AA modes, even though the Imlib color map will clash with the color map used by libart in AA mode. GnomeCanvasImage averts this problem by converting the image from Imlib format to libart format behind the scenes when using an AA Canvas. You still set and retrieve the graphic with the image property, but the actual rendering is done with ArtPixBuf instead. If you already have an ArtPixBuf structure, you can set it directly with the pixbuf property and avoid the conversion process, but be aware that if you do this, you will not be able to retrieve the value through either property, image or pixbuf. The pixbuf property is write-only, and setting it will also set the image property to NULL.

If you want to use libart instead of Imlib, you should use the newer GnomeCanvasPixbuf item instead. Furthermore, since Imlib is being phased out in GNOME 2.0, you should avoid using it at all if you can use GnomeCanvasPixbuf instead. The sooner you convert your application from Imlib to gdk-pixbuf, the better.

GnomeCanvasPixbuf is fully integrated with gdk-pixbuf and libart; in fact, it is distributed with the gdk-pixbuf package.2 As a result of this integration and a fresher approach, its properties (see Table 11.8) are completely different from those of the other Canvas items we've discussed.

The pixbuf property allows you to read and write the GdkPixbuf structure used by the Canvas item. The item takes care of incrementing the reference count when you set pixbuf, and it decrements the reference count when the item is destroyed. Setting pixbuf to a new value will unreference the previous GdkPixbuf instance. Reading the pixbuf property does not affect its reference count, so if you want to use it for something else, you should probably reference it yourself to make sure it remains.

The remaining 12 properties allow you to override the default size and position of the image, and enable or disable affine scaling on the image with very fine granularity. The four basic properties are width, height, x, and y. The values of these properties are ignored if their _set property is set to FALSE; a value of TRUE in their _set property overrides the default value with whatever is in that property. Thus if width is set to 50 on an image that is normally 100 pixels wide, but width_set is FALSE, the item will ignore width and display the image at 100 pixels-assuming that no scaling affines are applied to that item, of course. If width_set is TRUE, the item will instead ignore the image's true size and force it to display at width units. The _set properties act independently of each other, so you can lock the width of an image to a fixed size, while letting it expand freely in the vertical direction.

The _in_pixels properties override affine transformations; any base property with its _in_pixels (and _set) property set to TRUE will use the property as a fixed value, in pixels instead of in world coordinates as it usually would. In the case of width and height, the item won't apply any scaling transforms on the image; in the case of x and y, the item won't apply any translation offsets in that direction. Table 11.9 explores these relationships.

The GnomeCanvasPixbuf item underwent a bit of a personality crisis in its early days, caused primarily by the transition from GNOME 1.0 to GNOME 1.2, and on to GNOME 2.0. This item really belongs in gnome-libs because it is such a fundamental drawing item, especially with Imlib and GnomeCanvasImage both deprecated in GNOME 2.0. However, gdk-pixbuf does not exist as far as the GNOME 1.0 gnome-libs is concerned, so retrofitting gnome-libs with GnomeCanvasPixbuf would have resulted in an added depen- dency of gnome-libs on the gdk-pixbuf library. Such a radical change in dependencies should occur only between significant version increases, so in order for GnomeCanvasPixbuf to exist in 1.0 and 1.2 applications, it was added to the gdk-pixbuf distribution instead. With the arrival of GNOME 2.0, GnomeCanvasPixbuf will likely move to gnome-libs; as we learned in Chapter 10, gdk-pixbuf itself will be rolled into GTK+ 2.0, making the dependency issue a moot point.

This tangled web leads to the strange situation in which the pixbuf Canvas item is part of the gdk-pixbuf library for 1.x applications, but part of the gnome-libs library for 2.0 applications. In the latter case, you won't have to touch a thing; GnomeCanvasPixbuf will be linked into your application along with the rest of gnome-libs. In 1.0 and 1.2 applications, however, GnomeCanvasPixbuf is linked into a library of its own, You will have to add a couple of invocations of gnome-config to pull that library into your project:

gnome-config gnomecanvaspixbuf --cflags
gnome-config gnomecanvaspixbuf --libs