Migration Checklist This chapter includes a checklist of things you need to do to ensure that your programs are good citizens in the GTK+ world. By paying attention to the points in the checklist, you ensure that many automatic features of GTK+ will work correctly in your program.
Implement GtkWidget::popup_menu Why By handling this signal, you let widgets have context-sensitive menus that can be invoked with the standard key bindings. The GtkWidget::popup_menu signal instructs the widget for which it is emitted to create a context-sensitive popup menu. By default, the key binding mechanism is set to emit this signal when the ShiftF10 or Menu keys are pressed while a widget has the focus. If a widget in your application shows a popup menu when you press a mouse button, you can make it work as well through the normal key binding mechanism in the following fahion: Write a function to create and show a popup menu. This function needs to know the button number and the event's time to pass them to gtk_menu_popup(). You can implement such a function like this: static void do_popup_menu (GtkWidget *my_widget, GdkEventButton *event) { GtkWidget *menu; int button, event_time; menu = gtk_menu_new (); g_signal_connect (menu, "deactivate", G_CALLBACK (gtk_widget_destroy), NULL); /* ... add menu items ... */ if (event) { button = event->button; event_time = event->time; } else { button = 0; event_time = gtk_get_current_event_time (); } gtk_menu_popup (GTK_MENU (popup), NULL, NULL, NULL, NULL, button, event_time); } In your button_press handler, call this function when you need to pop up a menu: static gboolean my_widget_button_press_event_handler (GtkWidget *widget, GdkEventButton *event) { /* Ignore double-clicks and triple-clicks */ if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { do_popup_menu (widget, event); return TRUE; } return FALSE; } Implement a handler for the popup_menu signal: static gboolean my_widget_popup_menu_handler (GtkWidget *widget) { do_popup_menu (widget, NULL); return TRUE; } If you do not pass a positioning function to gtk_menu_popup(), it will show the menu at the mouse position by default. This is what you usually want when the menu is shown as a result of pressing a mouse button. However, if you press the ShiftF10 or Menu keys while the widget is focused, the mouse cursor may not be near the widget at all. In the example above, you may want to provide your own menu-positioning function in the case where the event is NULL. This function should compute the desired position for a menu when it is invoked through the keyboard. For example, aligns the top edge of its popup menu with the bottom edge of the entry. For the standard key bindings to work, your widget must be able to take the keyboard focus. In general, widgets should be fully usable through the keyboard and not just the mouse. The very first step of this is to ensure that your widget turns on the GTK_CAN_FOCUS FLAG.
Use GdkEventExpose.region Why The region field of GdkEventExpose allows you to redraw less than the traditional GdkEventRegion.area. In GTK+ 1.x, the GdkEventExpose structure only had an area field to let you determine the region that you needed to redraw. In GTK+ 2.x, this field exists for compatibility and as a simple interface. However, there is also a region field which contains a fine-grained region. The area field is simply the bounding rectangle of the region. Widgets that are very expensive to re-render, such as an image editor, may prefer to use the GdkEventExpose.region field to paint as little as possible. Widgets that just use a few drawing primitives, such as labels and buttons, may prefer to use the traditional GdkEventExpose.area field for simplicity. Regions have an internal representation that is accessible as a list of rectangles. To turn the GdkEventExpose.region field into such a list, use gdk_region_get_rectangles(): static gboolean my_widget_expose_event_handler (GtkWidget *widget, GdkEventExpose *event) { GdkRectangle *rects; int n_rects; int i; gdk_region_get_rectangles (event->region, &rects, &n_rects); for (i = 0; i < n_rects; i++) { /* Repaint rectangle: (rects[i].x, rects[i].y), * (rects[i].width, rects[i].height) */ } g_free (rects); return FALSE; }
Test for modifier keys correctly Why With gtk_accelerator_get_default_mod_mask() you can test for modifier keys reliably; this way your key event handlers will work correctly even if NumLock or CapsLock are activated. In a GdkEventKey, the state field is a bit mask which indicates the modifier state at the time the key was pressed. Modifiers are keys like Control and NumLock. When implementing a GtkWidget::key_press_event handler, you should use gtk_accelerator_get_default_mod_mask() to test against modifier keys. This function returns a bit mask which encompasses all the modifiers which the user may be actively pressing, such as Control, Shift, and Alt, but ignores "inocuous" modifiers such as NumLock and CapsLock. Say you want to see if ControlF10 was pressed. Doing a simple test like event->keysym == GDK_F10 && event->state == GDK_CONTROL_MASK is not enough. If CapsLock is pressed, then event->state will be equal to GDK_CONTROL_MASK | GDK_LOCK_MASK, and the simple test will fail. By taking the logical-and of event->state and gtk_accelerator_get_default_mod_mask(), you can ignore the modifiers which are not actively pressed by the user at the same time as the base key. The following example correctly tests for ControlF10 being pressed. static gboolean my_widget_key_press_event_handler (GtkWidget *widget, GdkEventKey *event) { guint modifiers; modifiers = gtk_accelerator_get_default_mod_mask (); if (event->keysym == GDK_F10 && (event->state & modifiers) == GDK_CONTROL_MASK) { g_print ("Control-F10 was pressed\n"); return TRUE; } return FALSE; }