Menus and Toolbars with GnomeUIInfo

It's tedious to create large menus, especially if they have features such as icons and keyboard accelerators. Gnome provides a simple solution. You assemble a GnomeUIInfo struct as a template for each menu item, simply listing its characteristics: name, icon, accelerator, and so on. The Gnome libraries can automatically create menus from arrays of GnomeUIInfo templates. The same method works with toolbars.

Here's the declaration of struct GnomeUIInfo.


typedef struct {
  GnomeUIInfoType type;
  gchar* label;
  gchar* hint;  
  gpointer moreinfo;    
  gpointer user_data;
  gpointer unused_data;
  GnomeUIPixmapType pixmap_type;
  gpointer pixmap_info;
  guint accelerator_key;
  GdkModifierType ac_mods;
  GtkWidget* widget;
} GnomeUIInfo;

A static initializer is the most convenient way to fill in the struct (but of course you can create it dynamically if you prefer). Gnome's routines accept an array of GnomeUIInfo, and macros are provided to simplify and standardize the most common static initializers. Here's a typical example, a File menu:


static GnomeUIInfo file_menu[] = {
  GNOMEUIINFO_MENU_NEW_ITEM(N_("_New Window"),
                            N_("Create a new text viewer window"), 
                            new_app_cb, NULL),
  GNOMEUIINFO_MENU_OPEN_ITEM(open_cb,NULL),
  GNOMEUIINFO_MENU_SAVE_AS_ITEM(save_as_cb,NULL),
  GNOMEUIINFO_SEPARATOR,
  GNOMEUIINFO_MENU_CLOSE_ITEM(close_cb,NULL),
  GNOMEUIINFO_MENU_EXIT_ITEM(exit_cb,NULL),
  GNOMEUIINFO_END
};

There isn't always a nice macro for the menu item you want, so sometimes you must manually specify each element of the struct:


{ 
  GNOME_APP_UI_ITEM, N_("_Select All"),
  N_("Select all cells in the spreadsheet"), 
  select_all_cb, NULL,
  NULL, 0, 0, 'a', GDK_CONTROL_MASK 
}

By now you're probably wondering what the struct members mean. Simple enough. Here's a breakdown:

You might also be wondering why the menu item names contain an underscore. The underscore is used to mark the key shortcut for the menu item; translators can move it around as needed to make it intuitive in their language. Gnome will parse the menu item name to obtain the accelerator, then remove the underscore.

Table 1 summarizes the possible values for the type field of a GnomeUIInfo struct. See libgnomeui/gnome-app-helper.h for more details. There are actually a few more possible values, but the others are used internally by the library. The values in Table 1 should be sufficient for application code.

Table 1. GnomeUIInfoType Values

GnomeUIInfoType Description moreinfo Field
GNOME_APP_UI_ENDOFINFO Terminates a table of GnomeUIInfo None
GNOME_APP_UI_ITEM Normal item (or radio item inside radio group) Callback function
GNOME_APP_UI_TOGGLEITEM Toggle/check item Callback function
GNOME_APP_UI_RADIOITEMS Radio item group Array of radio items in the group
GNOME_APP_UI_SUBTREE Submenu Array of GnomeUIInfo in the subtree
GNOME_APP_UI_SEPARATOR Separator None
GNOME_APP_UI_HELP Help item Help node to load

To create an entire menu tree, you include pointers to previous menu tables using the GNOMEUIINFO_SUBTREE() macro:


static GnomeUIInfo main_menu[] = {
  GNOMEUIINFO_SUBTREE(N_("_File"), file_menu),
  GNOMEUIINFO_END
};

In this particular case, there is a better macro to use, however:


static GnomeUIInfo main_menu[] = {
  GNOMEUIINFO_MENU_FILE_TREE(file_menu),
  GNOMEUIINFO_END
};

The main advantage of this macro is standardization; it ensures that all Gnome file menus use the same key shortcut and have the same name. There are quite a few analagous macros; see libgnomeui/gnome-app-helper.h for the complete list.

Converting GnomeUIInfo to a Widget

Once you have a menu table, Gnome will process it and convert it to a widget. The functions are listed in Figure 4

#include <libgnomeui/gnome-app-helper.h>

void gnome_app_create_menus(GnomeApp* app, GnomeUIInfo* uiinfo);

void gnome_app_create_menus_with_data(GnomeApp* app, GnomeUIInfo* uiinfo, gpointer user_data);

void gnome_app_create_toolbar(GnomeApp* app, GnomeUIInfo* uiinfo);

void gnome_app_create_toolbar_with_data(GnomeApp* app, GnomeUIInfo* uiinfo, gpointer user_data);

void gnome_app_fill_toolbar(GtkToolbar* toolbar, GnomeUIInfo* uiinfo, GtkAccelGroup* accel_group);

void gnome_app_fill_toolbar_with_data(GtkToolbar* toolbar, GnomeUIInfo* uiinfo, GtkAccelGroup* accel_group, gpointer data);

void gnome_app_fill_menu(GtkMenuShell* menushell, GnomeUIInfo* uiinfo, GtkAccelGroup* accel_group, gboolean uline_accels, gint pos);

void gnome_app_fill_menu_with_data(GtkMenuShell* menushell, GnomeUIInfo* uiinfo, GtkAccelGroup* accel_group, gboolean uline_accels, gint pos, gpointer user_data);

Figure 4. Creating Widgets from GnomeUIInfo

If you are using the GnomeApp widget, gnome_app_create_menus() and gnome_app_create_toolbar() create a menubar or toolbar from the GnomeUIInfo table you provide, then attach that to the GnomeApp. Most of the time these are the functions you want to use; they do everything automatically. There is a _with_data() variant of each function which overrides the user_data field of GnomeUIInfo.

If you have more specialized needs, you can manually fill a menubar or toolbar and then add it to the container of your choice. The fill functions require you to specify an accelerator group to add accelerators to; for GnomeApp, an accelerator group already exists in the widget struct (the accel_group member). The fill functions for menus take two additional arguments: you can toggle whether to parse the underscores in menu item labels to extract accelerators, and you can specify at which position in the GtkMenuShell the function will begin inserting menu items.

(GtkMenuShell is simply the base class for GtkMenuBar and GtkMenu, i.e. it is a widget that contains menu items. An accelerator group is just a collection of accelerators, normally attached to a GtkWindow; use GNOME_APP(widget)->accel_group in this case.)

When you use a GnomeUIInfo table to create a menubar or toolbar, pointers to the individual menu item or toolbar button widgets are placed in the widget member of each GnomeUIInfo struct. You can use these pointers to access the individual widgets; for example, if you create a check menu item you might want to set the state of the check. The pointer is also useful if you want to manually create some part of the menu; you can create an empty subtree item, for example, and manually build the contents of the subtree.

GnomeHello: An Example

The GnomeHello application has menus and a toolbar created using the GnomeUIInfo method; the relevant code is in a separate file, menus.c. Appendix E lists this file in its entirety.