12.3. Using ItemFactory

Now that we've shown you the hard way, here's how you do it using the gtk_item_factory calls.

ItemFactory creates a menu out of an array of ItemFactory entries. This means you can define your menu in its simplest form and then create the menu/menubar widgets with a minimum of function calls.

12.3.1. ItemFactory entries

At the core of ItemFactory is the ItemFactoryEntry. This structure defines one menu item, and when an array of these entries is defined a whole menu is formed. The ItemFactory entry struct definition looks like this:

struct _GtkItemFactoryEntry
{
  gchar *path;
  gchar *accelerator;

  GtkItemFactoryCallback callback;
  guint                  callback_action;

  gchar          *item_type;
};

Each field defines part of the menu item.

*path is a string which defines both the name and the path of a menu item, for example, "/File/Open" would be the name of a menu item which would come under the ItemFactory entry with path "/File". Note however that "/File/Open" would be displayed in the File menu as "Open". Also note since the forward slashes are used to define the path of the menu, they cannot be used as part of the name. A letter preceded by an underscore indicates an accelerator (shortcut) key once the menu is open.

*accelerator is a string that indicates a key combination that can be used as a shortcut to that menu item. The string can be made up of either a single character, or a combination of modifier keys with a single character. It is case insensitive.

The available modifier keys are:

"<ALT>                             - alt
"<CTL>" or "<CTRL>" or "<CONTROL>" - control
"<MOD1>" to "<MOD5>"               - modn
"<SHFT>" or "<SHIFT>"              - shift

Examples:

"<ConTroL>a"
"<SHFT><ALT><CONTROL>X"

callback is the function that is called when the menu item emits the "activate" signal. The form of the callback is described in the Callback Description section.

The value of callback_action is passed to the callback function. It also affects the function prototype, as shown in the Callback Description section.

item_type is a string that defines what type of widget is packed into the menu items container. It can be:

NULL or "" or "<Item>" - create a simple item
"<Title>"              - create a title item
"<CheckItem>"          - create a check item
"<ToggleItem>"         - create a toggle item
"<RadioItem>"          - create a (root) radio item
"Path"                 - create a sister radio item
"<Tearoff>"            - create a tearoff
"<Separator>"          - create a separator
"<Branch>"             - create an item to hold submenus (optional)
"<LastBranch>"         - create a right justified branch
"<StockItem>"          - create a simple item with a stock image. 
                               see gtkstock.h for builtin stock items
 

Note that <LastBranch> is only useful for one submenu of a menubar.

12.3.1.1. Callback Description

The callback for an ItemFactory entry can take two forms. If callback_action is zero, it is of the following form:

void callback( void )

otherwise it is of the form:

void callback( gpointer    callback_data,
               guint       callback_action,
               GtkWidget  *widget )

callback_data is a pointer to an arbitrary piece of data and is set during the call to gtk_item_factory_create_items().

callback_action is the same value as callback_action in the ItemFactory entry.

*widget is a pointer to a menu item widget (described in Manual Menu Creation).

12.3.1.2. ItemFactory entry examples

Creating a simple menu item:

GtkItemFactoryEntry entry = {"/_File/_Open...", "<CTRL>O", print_hello,
				0, "<Item>"};

This will define a new simple menu entry "/File/Open" (displayed as "Open"), under the menu entry "/File". It has the accelerator (shortcut) control+'O' that when clicked calls the function print_hello(). print_hello() is of the form void print_hello(void) since the callback_action field is zero. When displayed the 'O' in "Open" will be underlined and if the menu item is visible on the screen pressing 'O' will activate the item. Note that "File/_Open" could also have been used as the path instead of "/_File/_Open".

Creating an entry with a more complex callback:

GtkItemFactoryEntry entry = {"/_View/Display _FPS", NULL, print_state,
				7,"<CheckItem>"};

This defines a new menu item displayed as "Display FPS" which is under the menu item "View". When clicked the function print_state() will be called. Since callback_action is not zero print_state() is of the form:

void print_state( gpointer    callback_data,
                  guint       callback_action,
                  GtkWidget  *widget )

with callback_action equal to 7.

Creating a radio button set:

GtkItemFactoryEntry entry1 = {"/_View/_Low Resolution", NULL, change_resolution,
				1, "<RadioButton>"};
GtkItemFactoryEntry entry2 = {"/_View/_High Resolution", NULL, change_resolution,
				2, "/View/Low Resolution"};

entry1 defines a lone radio button that when toggled calls the function change_resolution() with the parameter callback_action equal to 1. change_resolution() is of the form:

void change_resolution(gpointer    callback_data,
                       guint       callback_action,
                       GtkWidget  *widget)

entry2 defines a radio button that belongs to the radio group that entry1 belongs to. It calls the same function when toggled but with the parameter callback_action equal to 2. Note that the item_type of entry2 is the path of entry1 without the accelerators ('_'). If another radio button was required in the same group then it would be defined in the same way as entry2 was with its item_type again equal to "/View/Low Resolution".

12.3.1.3. ItemFactoryEntry Arrays

An ItemFactoryEntry on it's own however isn't useful. An array of entries is what's required to define a menu. Below is an example of how you'd declare this array.

static GtkItemFactoryEntry entries[] = {
  { "/_File",         NULL,      NULL,         0, "<Branch>" },
  { "/File/tear1",    NULL,      NULL,         0, "<Tearoff>" },
  { "/File/_New",     "<CTRL>N", new_file,     1, "<Item>" },
  { "/File/_Open...", "<CTRL>O", open_file,    1, "<Item>" },
  { "/File/sep1",     NULL,      NULL,         0, "<Separator>" },
  { "/File/_Quit",    "<CTRL>Q", quit_program, 0, "<StockItem>", GTK_STOCK_QUIT } };

12.3.2. Creating an ItemFactory

An array of GtkItemFactoryEntry items defines a menu. Once this array is defined then the item factory can be created. The function that does this is:

GtkItemFactory* gtk_item_factory_new( GtkType        container_type,
                                      const gchar   *path,
                                      GtkAccelGroup *accel_group );

container_type can be one of:

GTK_TYPE_MENU
GTK_TYPE_MENU_BAR
GTK_TYPE_OPTION_MENU

container_type defines what type of menu you want, so when you extract it later it is either a menu (for pop-ups for instance), a menu bar, or an option menu (like a combo box but with a menu of pull downs).

path defines the path of the root of the menu. Basically it is a unique name for the root of the menu, it must be surrounded by "<>". This is important for the naming of the accelerators and should be unique. It should be unique both for each menu and between each program. For example in a program named 'foo', the main menu should be called "<FooMain>", and a pop-up menu "<FooImagePopUp>", or similar. What's important is that they're unique.

accel_group is a pointer to a gtk_accel_group. The item factory sets up the accelerator table while generating menus. New accelerator groups are generated by gtk_accel_group_new().

But this is just the first step. To convert the array of GtkItemFactoryEntry information into widgets the following function is used:

void gtk_item_factory_create_items( GtkItemFactory      *ifactory,
                                    guint                n_entries,
                                    GtkItemFactoryEntry *entries,
                                    gpointer             callback_data );

*ifactory a pointer to the above created item factory.

n_entries is the number of entries in the GtkItemFactoryEntry array.

*entries is a pointer to the GtkItemFactoryEntry array.

callback_data is what gets passed to all the callback functions for all the entries with callback_action != 0.

The accelerator group has now been formed, so you'll probably want to attach it to the window the menu is in:

void gtk_window_add_accel_group( GtkWindow     *window,
                                 GtkAccelGroup *accel_group);

12.3.3. Making use of the menu and its menu items

The last thing to do is make use of the menu. The following function extracts the relevant widgets from the ItemFactory:

GtkWidget* gtk_item_factory_get_widget( GtkItemFactory *ifactory,
                                        const gchar    *path );

For instance if an ItemFactory has two entries "/File" and "/File/New", using a path of "/File" would retrieve a menu widget from the ItemFactory. Using a path of "/File/New" would retrieve a menu item widget. This makes it possible to set the initial state of menu items. For example to set the default radio item to the one with the path "/Shape/Oval" then the following code would be used:

gtk_check_menu_item_set_active(
	GTK_CHECK_MENU_ITEM (gtk_item_factory_get_item (item_factory, "/Shape/Oval")),
	TRUE);

Finally to retrieve the root of the menu use gtk_item_factory_get_item() with a path of "<main>" (or whatever path was used in gtk_item_factory_new()). In the case of the ItemFactory being created with type GTK_TYPE_MENU_BAR this returns a menu bar widget. With type GTK_TYPE_MENU a menu widget is returned. With type GTK_TYPE_OPTION_MENU an option menu widget is returned.

Remember for an entry defined with path "/_File" the path here is actually "/File".

Now you have a menubar or menu which can be manipulated in the same way as shown in the Manual Menu Creation section.