Canvas Events

As we learned in Section 11.1.4, only the Canvas widget as a whole can receive true GDK events. To pass events on to individual Canvas items, the Canvas must crack open the GDK events and translate them into internal Canvas events. In this section we'll investigate how to hook into the Canvas's synthetic event queue and react to a variety of mouse and keyboard events.

The Event Callback

To avoid reinventing the wheel and to keep things simpler and more consistent for the developer, the GNOME Canvas reuses the existing GdkEvent structure, with a few minor modifications to its contents, before passing it on to the targeted Canvas item. The most important difference is that the event coordinates are translated from regular pixel-based window coordinates into abstract world coordinates. Fortunately, the coordinate fields in GdkEvent are already of type gdouble, because of the subpixel resolution of certain drawing devices, like drawing tablets, so the Canvas can reuse the GdkEvent structure ex- actly as it's defined in GTK+.

The item event callback looks like any other event callbacks you'll see for a normal GTK+ widget, depending on whether or not you care about the extra data parameter:

gint (* event) (GnomeCanvasItem *item, GdkEvent *event);
gint (* event) (GnomeCanvasItem *item, GdkEvent *event,
    gpointer data);
        

You set up the callback and connect the signal just as you would with a normal event handler. As far as your Canvas item is concerned (aside from coordinate system remapping), it is registering itself for a true GDK event. Naturally, you'll have to connect an event signal for every Canvas item you want to accept outside events. Any items you don't connect will ignore all keyboard and mouse events and will behave like a static part of the background. The application can still move the nonconnected items around and change their properties programmatically, through the API; the Canvas just won't be able to react to GDK events directed at those particular items. Listing 11.7 shows what your event handler might look like.

Listing 11.7 GnomeCanvasItem Event Handler

gint handle_canvas_event(GnomeCanvasItem *item, GdkEvent *event,
    gpointer data)
{
  switch (event->type)
  {
  case GDK_BUTTON_PRESS:
    /* Handle mouse button press */
    return TRUE;
  case GDK_BUTTON_RELEASE:
    /* Handle mouse button release */
    return TRUE;
  case GDK_MOTION_NOTIFY:
    /* Handle mouse movement */
    return TRUE;
  }

  /* Event not handled; try parent item */
  return FALSE;
}

int main(int argc, char *argv[])
{
  GnomeCanvasItem *item;
  gpointer *itemdata;
  ...
  gtk_signal_connect(GTK_OBJECT(item), "event",
    GTK_SIGNAL_FUNC(handle_canvas_event), itemdata);
}
        

The Canvas first sends the event to the item with which the user is interacting; in the case of a mouse event, this is the item the cursor is directly over. If that item's event handler returns a TRUE, the item swallows the event and the event processing stops there. However, if the item's event handler returns a FALSE, indicating that the item chose not to process the event or doesn't care if its parent groups or items see the event too, the Canvas continues to pass the event up the hierarchy, from child item to parent item, until an event handler returns TRUE or the event reaches the Canvas's root item. In the example in Listing 11.7 we return TRUE for all the events we handle, but it's just as legal to allow a Canvas event to be handled multiple times, by returning a FALSE from the callback function.

Clicks and Double Clicks

Handling a mouse click on a Canvas item is just like handling a mouse click on a true GTK+ widget. You check the GdkEvent for the type of event and react accordingly. The most common way to do this is with a switch( ) statement, as we did in Listing 11.7.

At first, it may seem intimidating to have to create an event handler for every Canvas item you want to interact with. If you had to do that with every normal widget, your application would quickly bloat into a maze of event- processing code. (Fortunately, most of this event code is hidden inside each widget, safely tucked away in the respective GTK+ or GNOME library.)

Really, though, you don't need that much event code for Canvas items. Items can receive only a small subset of the possible GDK events. The only Canvas item events you'll have to worry about are mouse clicks, mouse movements, keyboard events, and any item-specific events a derived Canvas item might declare. The Canvas takes care of all the rest, including the painting and exposures, focus switching, selection, and creation. You can also share the same event handler with multiple similar items, using the handler's user data parameter if you need to distinguish among them.

Let's dive a little deeper into mouse click events. The item can receive two types of click events: a button press and a button release. You can find out which type of click event, if any, the current event is by checking the type field of the GdkEvent structure, and you can react to that value in a switch( ) statement, as in Listing 11.7. The GdkEvent structure is really a union of similar structures, each one populated with fields useful to a specific event type, as shown in Listing 11.8. Peruse the GDK documentation for more information about these various event types. For now, we're interested in only the GdkEventButton structure (see Listing 11.9).

Listing 11.8 GdkEvent Structure

union _GdkEvent
{
  GdkEventType type;
  GdkEventAny any;
  GdkEventExpose expose;
  GdkEventNoExpose no_expose;
  GdkEventVisibility visibility;
  GdkEventMotion motion;
  GdkEventButton button;
  GdkEventKey key;
  GdkEventCrossing crossing;
  GdkEventFocus focus_change;
  GdkEventConfigure configure;
  GdkEventProperty property;
  GdkEventSelection selection;
  GdkEventProximity proximity;
  GdkEventClient client;
  GdkEventDND dnd;
};
        
Listing 11.9 GdkEventButton Structure

struct _GdkEventButton
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble pressure;
  gdouble xtilt;
  gdouble ytilt;
  guint state;
  guint button;
  GdkInputSource source;
  guint32 deviceid;
  gdouble x_root, y_root;
};
        

The GdkEvent union carries GdkEventButton as its button field. Thus you can find out the coordinates of the button click (in world coordinates) inside your event handler with event->button.x and event->button.y. If you need to differentiate which mouse button the user clicked, you can check the value of event->button.button. This value can be any integer from 1 through 5. The left button is typically 1, the middle button 2, and the right button 3, although the user is allowed to remap these values through the X server. Buttons 4 and 5 are most often used by wheel mice. GTK+ does its best to handle these values for you, so you're better off ignoring these last two buttons.

The other useful GdkEventButton field is state. The state field tells you what else was being held down at the time of the current event, not including the button or key that caused the event. You can use this field to determine if the user made a multibutton click, a Ctrl-click, a Shift-Alt-click, or something else. To express more than one concurrent state, GTK+ defines an enumeration of bit flags, as shown in Listing 11.10. The corresponding bit is turned on in GdkEventButton's state field for each modifier that was active at the time of the event.

Listing 11.10 GdkModifierType Enumeration

typedef enum
{
  GDK_SHIFT_MASK    = 1 << 0,
  GDK_LOCK_MASK     = 1 << 1,
  GDK_CONTROL_MASK  = 1 << 2,
  GDK_MOD1_MASK     = 1 << 3,
  GDK_MOD2_MASK     = 1 << 4,
  GDK_MOD3_MASK     = 1 << 5,
  GDK_MOD4_MASK     = 1 << 6,
  GDK_MOD5_MASK     = 1 << 7,
  GDK_BUTTON1_MASK  = 1 << 8,
  GDK_BUTTON2_MASK  = 1 << 9,
  GDK_BUTTON3_MASK  = 1 << 10,
  GDK_BUTTON4_MASK  = 1 << 11,
  GDK_BUTTON5_MASK  = 1 << 12,
  GDK_RELEASE_MASK  = 1 << 13,
  GDK_MODIFIER_MASK = 0x3fff
} GdkModifierType;
        

Of particular note are the Shift and Ctrl keys (GDK_SHIFT_MASK and GDK_CONTROL_MASK), the Alt or Meta key (GDK_MOD1_MASK), the Caps Lock key (GDK_LOCK_MASK), and the mouse buttons (GDK_BUTTON?_MASK). Thus if the user made a Shift-Ctrl-click with the first mouse button, the state field would equal (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_BUTTON1_MASK), and the button field would be set to 1. The GDK_RELEASE_MASK flag isn't used much for Canvas items.

Be careful not to make direct equality tests against the state field because often multiple flags are set. For example, the aforementioned Shift-Ctrl-click would result in a state field of 261 (256 + 4 + 1), which is not equal to GDK_SHIFT_MASK's value of 1, nor to GDK_CONTROL_MASK's value of 4. You should use the binary & operator in your comparisons:

if((event->button.state & GDK_SHIFT_MASK) &&
  (event->button.state & GDK_CONTROL_MASK))
{
  /* Process event for Shift-Ctrl combination, but ignore
   * Shift-only and Ctrl-only combinations. */
}
        

For the most part you can ignore the remaining fields of GdkEventButton. They are used primarily for full-blooded GTK+ event handling and don't really apply to Canvas item events. Most of them exist to handle alternative pointer hardware, like graphics tablets. The time field will come in handy later, when we need to grab a Canvas item (see Section 11.5.6).

Mouse Motion

In addition to tracking mouse clicks, the Canvas keeps track of mouse motion events, using the GdkEventMotion structure, which is the motion field from Listing 11.8. As you can see in Listing 11.11, the only difference between this event and GdkEventButton is that the is_hint field replaces the button field of GdkEventButton. The is_hint field describes the volume of motion events that the X server is sending. If is_hint is TRUE (which implies that GDK_POINTER_MOTION_MASK is not enabled), the application knows that the X server is sending only one unsolicited motion event at a time-only hints of motion-and will send further motion events only if the user clicks another button or key, or moves the cursor to a different GdkWindow. Conversely, an is_hint value of FALSE tells the application that the X server is sending a constant stream of updates for every change in mouse position: Each motion event is the real thing, not just a hint. Hinting can be turned on and off separately for each GdkWindow but is typically left on unless a window needs detailed knowledge of the mouse position. As we'll see, the Canvas requires the full, unthrottled flow of mouse events and will thus set the GDK_POINTER_MOTION_MASK on its GdkWindow. This means that the is_hint flag will always be FALSE for Canvas item motion events.

Listing 11.11 GdkEventMotion Structure

struct _GdkEventMotion
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  gdouble x;
  gdouble y;
  gdouble pressure;
  gdouble xtilt;
  gdouble ytilt;
  guint state;
  gint16 is_hint;
  GdkInputSource source;
  guint32 deviceid;
  gdouble x_root, y_root;
};
        

Listing 11.12 shows an example of a diagnostic event handler that prints out a potential flood of messages as you move the cursor around inside a Canvas item. The handler also prints out messages when you click mouse buttons and hold down modifier keys:

Listing 11.12 Sample Motion Event Handler

gint handle_canvas_event(GnomeCanvasItem *item, GdkEvent *event,
    gpointer data)
{
  gchar keystate[30];
  gboolean shift_pressed = event->button.state & GDK_SHIFT_MASK;
  gboolean ctrl_pressed = event->button.state & GDK_CONTROL_MASK;
  gboolean alt_pressed = event->button.state & GDK_MOD1_MASK;

  g_snprintf(keystate, sizeof(keystate), "%s%s%s",
    shift_pressed ? "[Shift] " : "",
    ctrl_pressed ? "[Ctrl] " : "",
    alt_pressed ? "[Alt] " : "");

  switch ((int)event->type)
  {
  case GDK_BUTTON_PRESS:
    g_message("Button %d press (state = %d) %s",
      event->button.button, event->button.state, keystate);
    return TRUE;
  case GDK_BUTTON_RELEASE:
    g_message("Button %d release (state = %d) %s",
      event->button.button, event->button.state, keystate);
    return TRUE;
  case GDK_MOTION_NOTIFY:
    g_message("Mouse movement (state = %d) (%4.1f, %4.1f) %s",
      event->button.state, event->motion.x, event->motion.y,
      keystate);
    return TRUE;
  }

  /* Event not handled; try parent item */
  return FALSE;
}
        

The sample output will look something like this excerpt:

Message: Mouse movement (state = 0) (174.0, 41.0)
Message: Mouse movement (state = 0) (174.0, 47.0)
Message: Mouse movement (state = 0) (174.0, 50.0)
Message: Mouse movement (state = 1) (174.0, 51.0) [Shift]
Message: Mouse movement (state = 1) (174.0, 54.0) [Shift]
Message: Mouse movement (state = 1) (174.0, 55.0) [Shift]
Message: Button 1 press (state = 1) [Shift]
Message: Mouse movement (state = 257) (174.0, 56.0) [Shift]
Message: Mouse movement (state = 257) (174.0, 58.0) [Shift]
Message: Mouse movement (state = 257) (174.0, 61.0) [Shift]
Message: Mouse movement (state = 257) (173.0, 65.0) [Shift]
Message: Mouse movement (state = 257) (173.0, 67.0) [Shift]
Message: Button 1 release (state = 257) [Shift]
Message: Mouse movement (state = 1) (173.0, 68.0) [Shift]
Message: Mouse movement (state = 1) (173.0, 70.0) [Shift]
Message: Mouse movement (state = 0) (172.0, 71.0)
Message: Mouse movement (state = 0) (172.0, 73.0)
Message: Mouse movement (state = 0) (172.0, 74.0)
        

In this slightly unrealistic example, the user enters the item from above and moves the mouse downward. During the drag, the user presses the Shift button and then the left mouse button, then releases the mouse button and the Shift key, and then stops. Note that the motion events don't enjoy a one-to-one correspondence to world coordinates. Also, the faster the mouse is moving, the larger the jump is between each consecutive point. This is a side effect of the sampling rate of the mouse's movement by the X server. Notice that the state value during the Shift-drag is 257, which is exactly equal to (GDK_SHIFT_MASK | GDK_BUTTON1_MASK).

Rollovers

Now you can detect when and how the user clicks and moves around inside a Canvas item. But how can you tell when the cursor enters or leaves a specific item? Normally GDK sends only GDK_ENTER_NOTIFY and GDK_LEAVE_NOTIFY events to GdkWindow instances. GDK has no knowledge of the Canvas's abstract notion of Canvas items, and it can't be expected to know when the cursor rolls into a new item. As far as GDK is concerned, you are still just moving around in the same widget window, and it will continue to generate GDK_MOTION_NOTIFY events until you completely leave the Canvas window.

Only the Canvas can know when you enter and leave a Canvas item, so it is up to the Canvas to handle these notifications. To make things easier for the developer, the Canvas watches the cursor position and internally synthesizes enter and leave events from scratch. GTK+ is oblivious to these synthetic events. The Canvas items are aware of them only because the Canvas sends the fake events to the same event handlers that it sends the other item events. All of this is transparent to the Canvas-using developer, though. You can set up entries in your handler's switch( ) statement for enter and leave events just as you would with any normal GTK+ event handler.

Thus rollover events are as simple as adding a GDK_ENTER_NOTIFY case to your handler's switch( ) statement. If you need to clean up anything after the rollover-perhaps you changed the color or shape of the item and need to revert back to its original state-just add a GDK_LEAVE_NOTIFY case as well. Listing 11.13 shows a modified version of the event handler template from Listing 11.7.

Listing 11.13 GnomeCanvasItem Event Handler with Rollover Support

gint handle_canvas_event(GnomeCanvasItem *item, GdkEvent *event,
    gpointer data)
{
  switch (event->type)
  {
  case GDK_BUTTON_PRESS:
    /* Handle mouse button press */
    return TRUE;
  case GDK_BUTTON_RELEASE:
    /* Handle mouse button release */
    return TRUE;
  case GDK_MOTION_NOTIFY:
    /* Handle mouse movement */
    return TRUE;
  case GDK_ENTER_NOTIFY:
    /* Handle rollover */
    return TRUE;
  case GDK_LEAVE_NOTIFY:
    /* Clean up after rollover */
    return TRUE;
  }

  /* Event not handled; try parent item */
  return FALSE;
}
        

Keyboard Events

Handling keyboard events is analogous to handling button-press events. GTK+ generates GDK_KEY_PRESS and GDK_KEY_RELEASE events (although the key-release event is not always as predictable as the key-press event, depending on your hardware, so be careful not to base too much on receiving the release event) and passes the concurrent key modifiers as bit flags in the state field of the GdkEventKey structure (see Listing 11.14), which is just like the state field in GdkEventButton.

Listing 11.14 GdkEventKey Structure

struct _GdkEventKey
{
  GdkEventType type;
  GdkWindow *window;
  gint8 send_event;
  guint32 time;
  guint state;
  guint keyval;
  gint length;
  gchar *string;
};
        

The only fields we haven't seen before are the last three, which tell us which key the user just pressed. The keyval field is the one you'll most often use in Canvas item keyboard event handlers. It contains the GDK value of the key, as defined in gtk+/gdk/gdkkeysyms.h. Each such key value is prefixed by "GDK_" followed by a descriptive name for the key-for example, GDK_t for the lowercase letter t, GDK_Up for the up arrow, GDK_Shift_L for the left Shift key, and GDK_F5 for the F5 function key. You can use the gdk_keyval_*( ) functions to further process this value.

The length and string fields represent a printable version of the key, if any, which you might use to render the user's text input to a text display or entry widget. If you decide to use the string field, you should make sure the value of length is greater than 0 to avoid printing out potential junk to your text buffer.

The key-press event sits in the item's event handler, alongside the other events, like this:

gint handle_canvas_event(GnomeCanvasItem *item, GdkEvent *event,
    gpointer data)
{
  switch (event->type)
  {
  ...
  case GDK_KEY_PRESS:
    /* Handle keyboard input */
    return TRUE;
  ...
  }

  /* Event not handled; try parent item */
  return FALSE;
}
        

Grabbing Items

GTK+, or more specifically GDK, allows you to grab a window and funnel some or all mouse events to that window. A window grab does for the mouse what window focus does for the keyboard. Just as the currently focused window receives all keyboard input until it loses focus, a grabbed window potentially receives all mouse events until it relinquishes the grab. This feature is critical for drag-and-drop operations, as we'll see in Section 11.5.7. The GDK function for this is gdk_pointer_grab( ); its companion function is gdk_pointer_ungrab( ). These functions act on a GdkWindow instance as a whole.

The Canvas supplies its own function for grabbing, called gnome_canvas_item_grab( ):

int gnome_canvas_item_grab (GnomeCanvasItem *item,
    unsigned int event_mask, GdkCursor *cursor, guint32 etime);
void gnome_canvas_item_ungrab (GnomeCanvasItem *item,
    guint32 etime);
        

This function lets you apply grab operations to single Canvas items instead of an entire GdkWindow. The Canvas version is merely a wrapper around the gdk_pointer_grab( ) function, with a few customizations to help distinguish a specific Canvas item from the other items in that particular Canvas. These functions are much more intrusive and dangerous than gnome_canvas_item_grab_focus( ), which grabs the keyboard focus only. The grab function locks the focus at the X11 level, whereas the grab_focus function claims input focus only until the next GTK+ widget asks for it, and it doesn't need to be explicitly released.

The event_mask parameter specifies which events you want to grab. You load it just like the state field for GdkEvent structures, as a bitmask of GdkEventMask values that has been processed by the binary OR (|) operator. At the very least you'll want to pass in a value of (GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK) to allow mouse motion and button-release events into the item while the drag is active. You should release the drag during the next GDK_BUTTON_RELEASE event, so you must make sure you allow that event to pass into the grabbed item. On the other hand, the GDK_BUTTON_PRESS_MASK flag isn't normally necessary because you'll usually call gnome_canvas_item_grab( ) after you're already inside the GDK_BUTTON_PRESS event. The only time you would need to allow the button-press event into a Canvas item grab is if you wanted to support functionality for clicking more than one button at the same time during a drag. This is very strange, confusing behavior, and you should think long and hard before trying it. In nearly all cases you can ignore the button-press event during a grab.

It is usually a good idea to give users some visual feedback when they make a grab. Such feedback lets them know that the application's behavior is different from usual. It also lets them know if the initial grab click has failed. They will expect the cursor to change, and if it doesn't they will know that they clicked in the wrong place, instead of blaming the failed grab on the application. The cursor parameter of the grab function lets you change the mouse cursor for the duration of the grab. You can either create a cursor and pass it into the grab function, or pass in NULL and make no change to the cursor.

Creating a cursor is simple. You call gdk_cursor_new( ), passing in a single parameter of GdkCursorType, as defined in gtk+/gdk/gdkcursors.h. After invoking the grab, you can immediately destroy the cursor you just created. The grab function copies the cursor to the X server, where it resides as a server-side resource until the grab is over, so there's no need to keep a local copy. The process looks something like this:

/* Create four-arrowed cursor for drag operation */
GdkCursor *cursor = gdk_cursor_new(GDK_FLEUR);

gnome_canvas_item_grab(item,
  GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
  cursor, event->button.time);
gdk_cursor_destroy(cursor);
        

This example also shows what you can do with the final parameter, etime. The etime parameter is used mostly to break ties between two simultaneous attempts to grab windows. A GDK grab is a global possession, which applies even across applications. GDK allows only one window on the entire X server to own the current grab, so if GDK notices two grab requests at the same time, it uses the etime value to settle the dispute. You can pass in the value of event->button.time from the event handler, or GDK_CURRENT_TIME if you aren't inside an event handler.

When you're done with the grab, you must release the grab, using gnome_canvas_item_ungrab( ) if the grab is on a Canvas item. This step is mandatory, because the grab is such a global action. If your application grabs the cursor and never lets it go, other applications won't be able to receive the grabbed mouse events until you close the errant application. This is a very serious bug, so be very careful when using grabs. As long as you always release the grab in the GDK_BUTTON_RELEASE event and don't try anything too complicated, you should be fine.

Dragging and Dropping Canvas Items

Drag-and-drop inside the Canvas is pretty straightforward to implement with what we know so far. You start by grabbing the cursor in the GDK_BUTTON_PRESS event; then you update the grabbed item's position in the Canvas according to the stream of GDK_MOTION_NOTIFY events you receive during the grab, and finally you release the grab in the GDK_BUTTON_RELEASE event. The only tricky parts are keeping track of when you're dragging and when you're not, and calculating the offsets for each successive motion event. Listing 11.15 shows one way to implement all this.

Listing 11.15 Drag-and-Drop Event Handler

gint handle_canvas_event (GnomeCanvasItem *item, GdkEvent *event,
    gpointer data)
{
  /* Static variables for drag-and-drop */
  static gboolean dragging = FALSE;
  static double previous_x, previous_y;

  double event_x = event->button.x;
  double event_y = event->button.y;

  switch (event->type)
  {
  case GDK_BUTTON_PRESS:
    if (event->button.button == 1)
    {
      GdkCursor *cursor;

      /* Store these coordinates for later... */
      previous_x = event_x;
      previous_y = event_y;

      cursor = gdk_cursor_new(GDK_FLEUR);
      gnome_canvas_item_grab(item,
        GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
        NULL, event->button.time);
      gdk_cursor_destroy(cursor);
      dragging = TRUE;
    }
    break;
  case GDK_MOTION_NOTIFY:
    if (dragging && (event->motion.state & GDK_BUTTON1_MASK))
    {
      gnome_canvas_item_move(item, event_x - previous_x,
        event_y - previous_y);

      /* Remember these for the next pass */
      previous_x = event_x;
      previous_y = event_y;
    }
    break;
  case GDK_BUTTON_RELEASE:
    gnome_canvas_item_ungrab(item, event->button.time);
    dragging = FALSE;
    break;
  default:
    break;
  }

  return FALSE;
}
        

The key to keeping the drag state and the motion offsets straight is to declare both as static variables inside the handler. We set dragging to TRUE when we grab, then back to FALSE when we ungrab, so that we know when to track the cursor movement and update the item's position inside the GDK_MOTION_NOTIFY event, and when to just ignore it. We don't have to worry about any other Canvas item receiving a GDK_MOTION_NOTIFY event during a drag because we've explicitly claimed those events with the GDK_POINTER_MOTION_MASK in the grab command. By definition, only the grabbed item can receive those events until the ungrab.

The offsets are a little more work. Each time we pass through the event handler, we update the current location of the pointer. We can compare the new position of the cursor (event_x and event_y) with the position we saved the last time around (previous_x and previous_y). By moving the grabbed Canvas item by the offset between the two sets of coordinates, we can keep the item in sync with the cursor position. We're not really dragging the Canvas item around, so much as mimicking the exact motions of the cursor.

Table 11.1 Common Properties for All Vector-Based Canvas Items
        
Table 11.2 Unique Properties for Closed-Shape Canvas Items
        
Table 11.3 Unique Properties for Canvas Line Items
        
Table 11.4 Placement Properties for Vector-Based Canvas Items
        
Table 11.5 Properties for GnomeCanvasWidget
        
Table 11.6 Properties for GnomeCanvasText
        
Table 11.7 Properties for GnomeCanvasImage
        
Table 11.8 Properties for GnomeCanvasPixbuf
        
Table 11.9 Behavior of Boolean Properties