4.3 Libgnomeui


4.3 Libgnomeui

GNOME's libgnomeui includes several widgets and a support API to ensure that your programs are consistent and interact with other applications. In addition, they can keep your own code base small ” you need worry about only what is important to your application; libgnomeui takes care of the boring parts such as menu bars and druid pages.

4.3.1 Application Windows

In Chapter 3, you placed widgets into otherwise empty GtkWindow objects. GNOME applications use a standardized window with these elements:

  • Menu bar: At the top of the window.

  • Status bar: At the bottom of the window.

  • Toolbars : At least one, perhaps more; can be configured as movable and draggable.

  • Docked elements: Any other draggable and/or detachable elements in the window.

GNOME has a single mechanism for creating these elements according to a global configuration. For example, your application will conform to the user 's preference for icons in toolbars ( Applications > Desktop Preferences > Menus & Toolbars ).

Use the GnomeApp ( GNOME_TYPE_APP ) widget as the main window for all of your applications. When you create a GnomeApp object, always set its app-id property to the application identifier first mentioned in Section 4.2.1. GnomeApp is a subclass of GtkWindow , so you can also set its title and initial size as usual.

To set an application window title according to a document name , use

 gnome_window_toplevel_set_title(  window  ,  document  ,  app  ,  extension  ) 

Here, window is a top-level window casted as a GtkWindow object, document is a document filename, app is your application name, and extension is a filename extension. You can set extension to NULL to keep the document name intact in the window title.

You should outfit your window with an icon that appears in the window list and other places.

Here is how an application might set the title and icons:

 GnomeApp *window, *apple_window; gchar *icon;   << ... >> /* set title */ gnome_window_toplevel_set_title(GTK_WINDOW(window),                                 "order-2003-08.mtx",                                 "MiracleText",                                 ".mtx"); /* set icon for the apple window */ icon = gnome_program_locate_file(program, GNOME_FILE_DOMAIN_PIXMAP,                                  "apple-green.png", TRUE, NULL); gtk_window_icon_set_from_file(GTK_WINDOW(apple_window), icon); g_free(icon); 

This section concludes with a full working sample application that also provides your first real exposure to GTK+ menus.

Start with the normal GTK+ event handlers:

 /* -*-coding: utf-8;-*- */ /* appdemo.c -- bare-bones GnomeApp */ #include <gnome.h> /* standard event handlers */ gint delete_event(GtkWidget *widget, GdkEvent event, gpointer data) {   return FALSE; } void end_program(GtkWidget *widget, gpointer data) {   gtk_main_quit(); } 

Here is the event handler for all items on the Edit menu:

 void edit_handler(GtkWidget *item, gpointer data) {   g_print("`%s' called\n", (gchar *)data); } 

All GtkWidget declarations in the following main program are for menu items:

 int main(int argc, char **argv) {   GnomeProgram *program;   GnomeApp *window;   GtkAccelGroup *accel_g;   GtkMenuBar *menu_bar;   GtkWidget *file, *edit, *help,             *file_close,             *edit_cut, *edit_copy, *edit_paste,             *help_index, *help_about;   GtkMenu *file_menu, *edit_menu, *help_menu;   GtkToolbar *tools;   GtkStatusbar *status;   GtkLabel *label; 

The application initialization includes the GNOME initialization from libgnome , the GnomeApp class from libgnomeui , and the usual event handlers from GTK+:

 /* initialize GNOME */   program = gnome_program_init("appdemo",                                "0.1",                                LIBGNOMEUI_MODULE,                                argc, argv,                                GNOME_PROGRAM_STANDARD_PROPERTIES,                                GNOME_PARAM_HUMAN_READABLE_NAME, "AppDemo",                                GNOME_PARAM_ENABLE_SOUND, TRUE,                                NULL);   /* create main window */   window = g_object_new(GNOME_TYPE_APP,                         "title", "Application Window",                         "app-id", "appdemo",                         "default-width", 300,                         "default-height", 300,                         NULL);   /* attach standard event handlers */   g_signal_connect(window, "delete_event", G_CALLBACK(delete_event), NULL);   g_signal_connect(window, "destroy", G_CALLBACK(end_program), NULL); 

Menu item generator functions want an accelerator group argument, so we'll create one here:

 /* add accelerator group */   accel_g = g_object_new(GTK_TYPE_ACCEL_GROUP, NULL);   gtk_window_add_accel_group(GTK_WINDOW(window), accel_g); 

Menu bars, toolbars, and status bars have their own GTK+ widgets.

 /* create menu/tool/status bars */   menu_bar = g_object_new(GTK_TYPE_MENU_BAR, NULL);   tools = g_object_new(GTK_TYPE_TOOLBAR, NULL);   status = g_object_new(GTK_TYPE_STATUSBAR, NULL); 

Now it's time to create the File menu. Start with the menu items and work your way up. Each menu item is an individual object. Because Close is a very common menu item, it is in the stock library. Notice the activate signal, emitted when the user chooses the menu item.

 /* create file close item */   file_close = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, accel_g);   /* attach the standard window destroy handler for the close item */   g_signal_connect(file_close, "activate", G_CALLBACK(end_program), NULL); 

A menu is a container widget that holds menu items:

 /* create file menu and attach the close item */   file_menu = g_object_new(GTK_TYPE_MENU, NULL);   gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_close); 

To see the menu, the user clicks yet another item. The following code creates an item labeled File , puts it into the menu bar, and attaches its menu ( file_menu ).

 /* create the item for "File" in the menu bar and attach the menu */   file = gtk_menu_item_new_with_mnemonic("_File");   gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file);   gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), GTK_WIDGET(file_menu)); 

Edit and Help menus are nearly identical to those for File :

 /* create edit menu items */   edit_cut = gtk_image_menu_item_new_from_stock(GTK_STOCK_CUT, accel_g);   /* use a custom handler */   g_signal_connect(edit_cut, "activate", G_CALLBACK(edit_handler), "Cut");   /* do the same for copy and paste items */   edit_copy = gtk_image_menu_item_new_from_stock(GTK_STOCK_COPY, accel_g);   g_signal_connect(edit_copy, "activate", G_CALLBACK(edit_handler), "Copy");   edit_paste = gtk_image_menu_item_new_from_stock(GTK_STOCK_PASTE, accel_g);   g_signal_connect(edit_paste, "activate", G_CALLBACK(edit_handler), "Paste");   /* create edit menu and put items inside */   edit_menu = g_object_new(GTK_TYPE_MENU, NULL);   gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), edit_cut);   gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), edit_copy);   gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), edit_paste);   /* create the item for "Edit" in the menu bar and attach the menu */   edit = gtk_menu_item_new_with_mnemonic("_Edit");   gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), edit);   gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit), GTK_WIDGET(edit_menu));   /* make a help menu */   help_index = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, accel_g);   help_about = gtk_image_menu_item_new_from_stock(GNOME_STOCK_ABOUT, accel_g);   help_menu = g_object_new(GTK_TYPE_MENU, NULL);   gtk_menu_shell_append(GTK_MENU_SHELL(help_menu), help_index);   gtk_menu_shell_append(GTK_MENU_SHELL(help_menu), help_about);   help = gtk_menu_item_new_with_mnemonic("_Help");   gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), help);   gtk_menu_item_set_submenu(GTK_MENU_ITEM(help), GTK_WIDGET(help_menu)); 

Now you can declare the menu bar from earlier as the main application menu bar. Notice that you must show the menu bar separately from the rest of the application, because menus aren't normally visible (the menu bar is a special kind of menu).

 /* place menus into application window and show everything */   gnome_app_set_menus(window, menu_bar);   gtk_widget_show_all(GTK_WIDGET(menu_bar)); 

Creating toolbar buttons and attaching handlers is simple:

 /* put some buttons in the toolbar */   gtk_toolbar_insert_stock(tools, GTK_STOCK_CUT,                            "Delete selection and place into clipboard",                            NULL, G_CALLBACK(edit_handler), "Cut", -1);   gtk_toolbar_insert_stock(tools, GTK_STOCK_COPY,                            "Copy selection to clipboard",                            NULL, G_CALLBACK(edit_handler), "Copy", -1);   gtk_toolbar_insert_stock(tools, GTK_STOCK_PASTE,                            "Paste clipboard",                            NULL, G_CALLBACK(edit_handler), "Paste", -1);   /* put toolbar in main application window */   gnome_app_set_toolbar(window, tools); 

This application has a status bar, but does nothing with it.

 /* add the status bar */   gnome_app_set_statusbar(window, GTK_WIDGET(status)); 

The main window in this application contains a single label:

 /* a simple placeholder label as the content */   label = g_object_new(GTK_TYPE_LABEL, "label", "Your idea goes here.", NULL);   /* set the label as the application content */   gnome_app_set_contents(window, GTK_WIDGET(label)); 

Show the window and everything inside (except the menus) just as you did with GTK+. Figure 4.1 on the next page shows this application.


Figure 4.1: Basic GNOME application ” menu bar, toolbar, contents, and status bar.
 /* show everything and start GTK+ main event loop */   gtk_widget_show_all(GTK_WIDGET(window));   gtk_main();   return 0; } 

The big picture that you should take away from this example is that after you create a GnomeApp , you need to put things into its menu bar, toolbar, main content window, and status bar. Otherwise, a GNOME application's widget setup is not significantly different from a GTK+ application.

Menus

There are two menu container widgets: GtkMenu ( GTK_TYPE_MENU ) to hold menu items, and GtkMenuBar to hold several menus. Technically, these two classes are the same ” menus are vertical containers and menu bars are horizontal containers. They are both subclasses of GtkMenuShell , so you frequently see menu_shell API when placing widgets into menus.

Note  

You can have more than one menu bar in your application, but it would violate nearly every axiom of user-friendly GUI development. GnomeApp defines only one built-in menu bar.

To get started with menus, you need to create a number of items . Each item in a menu container is an object of the GtkMenuItem ( GTK_TYPE_MENU_ITEM ) class or its subclasses.

GtkMenuItem displays only text. Its subclasses can do more:

  • GtkImageMenuItem ( GTK_TYPE_IMAGE_MENU_ITEM ): Icons and other images.

  • GtkCheckMenuItem ( GTK_TYPE_CHECK_MENU_ITEM ): Check (toggled) items.

  • GtkRadioMenuItem ( GTK_TYPE_RADIO_MENU_ITEM ): Radio items.

  • GtkSeparatorMenuItem ( GTK_TYPE_CHECK_MENU_ITEM ): Menu separators.

You can create menu items with generator functions. For normal GtkMenuItem objects, call

 GtkWidget *  item  ; item = gtk_menu_item_new_with_mnemonic(  label  ); 

where label is the item text. You can put an underscore ( _ ) in front of the character that you want as a keyboard shortcut.

Note  

All menu item generator functions return objects of the GTK_TYPE_WIDGET type, and menu API functions expect this type. Therefore, it's much easier to declare menu item variables as GtkWidget * rather than GtkMenuItem * . In theory, this looks somewhat unclean, but in practice, you will save a lot of space and development time because you won't need to cast the object every time you want to use it.

To create a menu entry containing an image, use

 gtk_image_menu_item_new_with_mnemonic(  label  ) 

After you get the item object, set its image property to a GtkImage widget (see Section 3.3.2). Although you can put any widget into the item, you should stick to images that are 16 — 16 pixels.

For the most part, you can choose stock images and labels for menu items. There is a special function for creating an image widget from the stock item library:

 gtk_image_menu_item_new_from_stock(  stock_id  ,  accel_group  ) 

Here, stock_id is an identifier as described in Section 3.3.2. If you want the item's keyboard operation defined by the stock item library, supply a GtkAccelGroup object as accel_group (see Section 3.8.2). If you don't care, use NULL .

GTK+ emits the activate signal when the user selects a menu item. The signal handler prototype looks like this:

 void handler(GtkWidget *item, gpointer data); 
Note  

This signal and handler work with the more complex menu items described in a moment, but aren't terribly useful with those items.

To create a check (box) menu item, use

 gtk_check_menu_item_new_with_mnemonic(  label  ) 

Like regular check buttons, these items have the gboolean properties active and inconsistent for the items' current state. You can detect state changes by attaching a handler to the toggled signal. The prototype here is

 void handler(GtkCheckMenuItem *item, gpointer data); 

Radio button menu items are somewhat more complex because you must group them with a GSList parameter:

 gtk_radio_menu_item_new_with_mnemonic(  group  ,  label  ) 

You can build group as you create a set of radio menu items. Start with NULL , and after you create each item, call

  group  = gtk_radio_menu_item_get_group(  item  ); 

With this method, you rarely need to deal with the actual elements of the radio group. Here is an example:

 GSList *group = NULL; GtkWidget *radio[4]; gint i; gchar *text[] = {   "_CD",   "_Phono",   "_Tape",   "Tune_r" }; for (i = 0; i < 4; i++) {   radio[i] = gtk_radio_menu_item_new_with_mnemonic(group, text[i]);   group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(radio[i])); } g_object_set(radio[1], "active", TRUE, NULL); 
Note  

Make sure that you set the default item's active property to TRUE after you create a group of radio items.

GtkRadioMenuItem is a subclass of GtkCheckMenuItem , so it inherits the toggled signal in addition to the active property.

If you do not want to allow keyboard operation with your menu items, use one of these functions (they operate just like their _with_mnemonic() counterparts, but without the underscore in the label):

 gtk_menu_item_new_with_label  (label)  gtk_image_menu_item_new_with_label  (label)  gtk_check_menu_item_new_with_label  (label)  gtk_radio_menu_item_new_with_label  (group, label)  

To create a line in your menu (for example, to separate a group of radio items from other items), use

 gtk_separator_menu_item_new() 

Now that you have your menu items, you must add them to a menu. The GtkMenuShell API performs these operations:

  • void gtk_menu_shell_append(GtkMenuShell *menu, GtkWidget *child)

    Adds child to the bottom (or right) of menu .

  • void gtk_menu_shell_prepend(GtkMenuShell * menu , GtkWidget * child )

    Adds child to the top (or left) of menu .

  • void gtk_menu_shell_insert(GtkMenuShell * menu , GtkWidget * child , gint position )

    Inserts child at slot position in menu . The first position in a menu is 0.

After all of your items are in a menu, you need some way to activate the menu. Build a GtkMenuBar container filled with menu heading items (for example, File and Edit ). The idea is that the user clicks one of these to activate the regular vertical menu, and therefore, what you're really trying to do is attach a submenu to an item in the menu bar (if this sounds confusing, refer back to the example program to see how this works in practice). To attach a submenu to a menu item, use

 gtk_menu_item_set_submenu(  menu_item  ,  submenu  ) 
Note  

For this function, menu_item is a GtkMenuItem object, and submenu is a GtkWidget . If you declared your menu items as GtkWidget , you need to do some casting for this function.

To remove an item's submenu, use

 gtk_menu_item_remove_submenu(  menu_item  ) 

In summary, to build a menu hierarchy with a menu bar, follow these steps:

  1. Create the menu items according to these guidelines:

    • In general, use the _with_mnemonic generator functions to create labels with unique keyboard shortcuts.

    • If a plain GtkMenuItem object does not contain a submenu, set its activate signal handler.

    • For GtkImageMenuItem objects, set the image property to specify a custom image. When using stock items, you need a GtkAccelGroup object to make the keyboard operation work.

    • For GtkCheckMenuItem objects, you might want to attach a handler to the toggled signal.

    • For GtkRadioMenuItem objects, you need a GSList for the radio button group. You should also set one of the radio items in a group to active (meaning that it is the default button).

  2. Create a menu bar and fill it with items to activate each of your menus. As before, assign unique keyboard operators.

  3. Create submenu objects for the menu bar and fill them with the items in Step 1. You can do this before (or alongside ) Step 2 if you like.

  4. Attach each of the submenus to its corresponding items in the menu bar.

Once you have your menu bar, place it into your GNOME application window with

 gnome_app_set_menus(  window  ,  menu_bar  ) 

Finally, show the menu bar with

 gtk_widget_show_all(  menu_bar  ) 
Warning  

You must perform this final step. As mentioned in the example, GTK+ ignores menus when it shows an entire widget tree because a menu normally appears only when the user activates the menu. However, you want to see the menu bar all of the time.

Toolbars

Your application should provide toolbar buttons for frequent actions such as Save (this is in addition to menu items and keyboard combinations). You can also place other compact widgets into the toolbar. For example, a zoom (scaling) widget works here.

The GTK+ toolbar widget class is GtkToolbar ( GTK_TYPE_TOOLBAR ), with these two properties:

  • orientation : The current orientation of the toolbar; one of

    • GTK_ORIENTATION_HORIZONTAL

    • GTK_ORIENTATION_VERTICAL

  • toolbar-style : The current orientation of the toolbar; one of

    • GTK_TOOLBAR_ICONS : Toolbar contains images only.

    • GTK_TOOLBAR_TEXT : Toolbar contains text only.

    • GTK_TOOLBAR_BOTH : Toolbar contains images and text.

    • GTK_TOOLBAR_BOTH_HORIZ : Like the preceding choice, but labels are next to (rather than under) images.

Note  

GNOME applications should not alter these properties. With the exception of some toolbar orientations, the user should be able to choose the style with the GNOME Menus & Toolbars control panel. If you want to give your users the opportunity to override the toolbar style in an application, make sure that you also provide a "Use GNOME defaults" option. To set your toolbar's properties back to the GNOME defaults, call:

 gtk_toolbar_unset_style(  toolbar  ) 

Toolbars aren't as complicated to operate or program as menu bars. There is no separate "toolbar entry" widget ” only a number of functions to insert toolbar items. Each of these functions returns a new toolbar item widget; to remove the item, destroy the widget.

A toolbar can contain four kinds of objects:

  • Buttons that contain an icon and label of your specification.

  • Buttons that contain stock items and images.

  • Arbitrary widgets.

  • Empty space (not a true item).

Here are the toolbar API functions:

  • GtkWidget *gtk_toolbar_append_item(GtkToolbar * toolbar , const char * text , const char * tooltip , "", GtkWidget * icon , GtkSignalFunc func , gpointer data )

    Appends a new item to toolbar with the label text and image icon . This function attaches the tooltip text tooltip to the item and returns a new item widget. When the user clicks the item, GTK+ calls func with the item widget and data as arguments.

    The prototype for func should resemble

     void handler(GtkWidget *item, gpointer data); 
  • GtkWidget *gtk_toolbar_prepend_item(GtkToolbar * toolbar , const char * text , const char * tooltip , "", GtkWidget * icon , GtkSignalFunc func , gpointer data )

    Like the preceding function, but places the new item at the start of the toolbar.

  • GtkWidget *gtk_toolbar_insert_item(GtkToolbar * toolbar , const char * text , const char * tooltip , "", GtkWidget * icon , GtkSignalFunc func , gpointer data , gint position )

    Like the preceding function, but inserts the new item at position in toolbar . The first position is 0.

  • GtkWidget *gtk_toolbar_insert_stock(GtkToolbar * toolbar , const char * stock_id , const char * tooltip , "", GtkWidget * icon , GtkSignalFunc func , gpointer data , gint position )

    Like the preceding function, but inserts a stock item stock_item . If position is -1, the item goes to the end of the toolbar.

  • GtkWidget *gtk_toolbar_append_widget(GtkToolbar * toolbar , GtkWidget * widget , const char * tooltip , "")

    Like gtk_toolbar_append_item() , but appends an arbitrary widget to the toolbar.

  • GtkWidget *gtk_toolbar_prepend_widget(GtkToolbar * toolbar , GtkWidget * widget , const char * tooltip , "")

    Like the preceding function, but inserts widget at the front of the toolbar.

  • GtkWidget *gtk_toolbar_insert_widget(GtkToolbar * toolbar , GtkWidget * widget , const char * tooltip , "", gint position )

    Like the preceding function, but inserts widget at position .

  • void gtk_toolbar_append_space(GtkToolbar * toolbar )

    Appends an empty space to the end of toolbar .

  • void gtk_toolbar_prepend_space(GtkToolbar * toolbar )

    Inserts an empty space at the start of toolbar .

  • void gtk_toolbar_insert_space(GtkToolbar * toolbar , gint position )

    Inserts an empty space at position in toolbar .

  • void gtk_toolbar_remove_space(GtkToolbar * toolbar , gint position )

    Removes the empty space at position in toolbar . (This little utility is necessary because spaces in toolbars are not objects.)

Section 3.8.1 covers the significance of the "" argument in some of these functions.

To pack a toolbar widget into a main GNOME application window, use

 gnome_app_set_toolbar(  window  ,  toolbar  ) 

Status Bars

Many applications display their current state in a short strip at the bottom of the window. You can also put descriptions of menu items into a status bar.

In GTK+, the status bar widget class is GtkStatusbar ( GTK_TYPE_STATUSBAR ). You do not need a special generator function to create a status bar. To link a status bar to a GNOME application window, use

 gnome_app_set_statusbar(  window  ,  status_bar  ) 
Note  

You must cast status_bar to a GtkWidget in this function call. Any other widget can go into the GNOME status bar location, but there are few reasonable alternatives to GtkStatusBar ; a horizontal box containing a progress bar is one.

In some applications, the status bar may have a secondary purpose: to resize the window through a small grip in one corner.

The status bar works like a stack with push and pop operations for messages. The message at the top of the stack shows up in the status bar display. Here are the stack manipulation functions:

  • guint gtk_statusbar_push(GtkStatusBar * status_bar , guint context , const gchar * message )

    Places message on top of the stack for status_bar and returns a numeric identifier for the new message.

    Here, context is an identifier for the part of the application that sent the message. Very few applications use this, and you should enter 0 if you don't want to bother with it. (If you really want to know, look in the API documentation for gtk_statusbar_get_context_id() .)

  • void gtk_statusbar_pop(GtkStatusBar * status_bar , guint context )

    Removes the message at the top of a status bar's stack.

  • void gtk_statusbar_remove(GtkStatusBar * status_bar , guint context , guint message_id )

    Removes message_id from a status bar's stack.

Note  

To remove the status bar grip, use

 gtk_statusbar_set_has_resize_grip(  status_bar  , FALSE) 

Do this only when there's a really good reason (for example, if the status bar is not at the bottom of the window).

4.3.2 Context Menus

You may recall that you can define a menu at any spot in the application with a GTK+ context menu; they need a little help with signal handlers for the window focus.

It's much easier with GNOME. To put a context menu in your application, follow these steps:

  1. Create a popup widget.

     GtkWidget *  popup  , *  widget  ; popup = gnome_popup_menu_new(  uiinfo  ); 

    This function creates a new menu from the GnomeUIInfo items in uiinfo and returns a new GtkWidget for the popup menu.

  2. Call

     gnome_popup_menu_attach(  popup  ,  widget  ,  data  ); 

    where widget is the target widget in your application (users will right-click this widget to get the popup menu).

Warning  

You can attach a content menu only to a widget that accepts events ” don't try it with a label or a plain image.

Here are some other GNOME functions for manipulating context menus:

  • GtkWidget *gnome_popup_menu_new_with_accelgroup(GnomeUIInfo * uiinfo , GtkAccelGroup * accelgroup )

    Like gnome_popup_menu_new() , but with keyboard operation defined by accelgroup .

  • GtkAccelGroup *gnome_popup_menu_get_accel_group(GtkWidget * menu )

    Returns the accelerator group for menu .

  • void gnome_popup_menu_append(GtkWidget * popup , GnomeUIInfo * uiinfo )

    Adds the items in uiinfo to popup .

  • void gnome_gtk_widget_add_popup_items(GtkWidget * widget , GnomeUIInfo * uiinfo , gpointer data)

    Adds the items in uiinfo to the popup menu for widget . If widget doesn't have a context menu, this function creates a popup menu for it and uses data as the callback data pointer.

4.3.3 Enhanced Data Entry Widgets

The libgnomeui library offers a number of extensions to GTK+ data entry widgets such as GtkEntry . These tend to be far more capable than their bare GTK+ counterparts. This section's example program demonstrates enhanced text entry boxes, file choosers , font pickers, color pickers, icon pickers, image pickers, and date entry widgets. Each widget type has a detailed explanation in subsequent sections.

The only new concept in this program is the history feature. As you read this program, take a look at the history-id property for widgets such as GnomeEntry . GNOME stores the history for one of these widgets based on the application and history identifier.

Otherwise, you should be able to tell how these widgets work by reading the code and comments, so there is no inline explanation for this program as there has been in previous examples in this book. Figure 4.2 shows the results of this program.

click to expand
Figure 4.2: GNOME data entry widgets.
 /* -*-coding: utf-8;-*- */ /* choosers.c -- demonstrate GNOME choosers */ #include <gnome.h> #define NUM_WIDGETS 9 gchar *widget_label[NUM_WIDGETS] = {   "_Text entry with history:",   "_File chooser:",   "Font _picker with default icon:",   "Font picker _with font display:",   "Font picker with custom _label:",   "_Color picker:",   "_Icon picker:",   "Image picker and _viewer:",   "_Date entry:" }; /* handler for GnomeEntry::activate */ void textentry_activated(GnomeEntry *text_widget, gpointer data) {   GtkEntry *gtk_entry;   gchar *contents;   g_object_get(text_widget, "gtk-entry", &gtk_entry, NULL);   g_object_get(gtk_entry, "text", &contents, NULL);   g_object_unref(gtk_entry);   g_print("Text entry box activated; new contents: %s\n", contents);   g_free(contents); } /* handler for GnomeFileEntry::activate */ void fileentry_activated(GnomeFileEntry *file_widget, gpointer data) {   gchar *path;   path = gnome_file_entry_get_full_path(file_widget, FALSE);   g_print("File chooser activated; new File: %s\n", path);   g_free(path); } /* handler for GnomeFontPicker::font-set    gets a picker id through the data pointer    so that you know what picker changed */ void fontpicker_changed(GnomeFontPicker *picker, gchar *font_name, gpointer id) {   g_print("Font picker %d changed; new font: %s\n", (gint)id, font_name); } /* handler for GnomeColorPicker::color-set */ void colorpicker_changed(GnomeColorPicker *picker,                          guint red, guint green, guint blue, guint alpha,                          gpointer data) {   g_print("Color picker changed; new color: R=%d; G=%d; B=%d; A=%d\n",           red, green, blue, alpha); } /* handler for GnomeIconEntry::changed */ void iconpicker_changed(GnomeIconEntry *picker, gpointer data) {   gchar *path;   g_object_get(picker, "filename", &path, NULL);   g_print("Icon picker changed; new icon: %s\n", path);   g_free(path); } /* handler for GnomePixmapEntry::activate */ void pixmap_changed(GnomePixmapEntry *picker, gpointer data) {   gchar *path;   g_print("Pixmap changed; ");   path = gnome_pixmap_entry_get_filename(picker);   if (path == NULL)   {      g_print("selection invalid.\n");   } else {      g_print("new image file: %s\n", path);      g_free(path);   } } /* handler for GnomeDateEdit::date-changed and GnomeDateEdit::time-changed    If this is a date, is_date_ptr is TRUE */ void date_or_time_changed(GnomeDateEdit *widget, gpointer is_date_ptr) {   gboolean is_date = (gboolean)is_date_ptr;   time_t time;   gchar *date_or_time;   date_or_time = is_date? "date" : "time";   g_print("%s changed; ", date_or_time);   g_object_get(widget, "time", (gulong*)(&time), NULL);   g_print("new %s: %s", date_or_time, ctime(&time)); } /* standard event handlers */ gint delete_event(GtkWidget *widget, GdkEvent event, gpointer data) {   return FALSE; } void end_program(GtkWidget *widget, gpointer data) {   gtk_main_quit(); } int main(int argc, char **argv) {   GnomeProgram *program;   GtkWindow *window;   GtkTable *table;   GtkWidget *widgets[NUM_WIDGETS];   gint i;   program = gnome_program_init("choosers",                                "0.1", LIBGNOMEUI_MODULE,                                argc, argv,                              GNOME_PROGRAM_STANDARD_PROPERTIES,                              GNOME_PARAM_HUMAN_READABLE_NAME,                              "GNOME Choosers Demo",                              GNOME_PARAM_ENABLE_SOUND, TRUE,                              NULL); window = g_object_new(GTK_TYPE_WINDOW,                       "title", "GNOME Choosers",                       "default-height", 300,                       "default-width", 300,                       "border-width", 12,                       NULL); /* attach standard event handlers */ g_signal_connect(window, "delete_event", G_CALLBACK(delete_event), NULL); g_signal_connect(window, "destroy", G_CALLBACK(end_program), NULL); /* create entry/chooser widgets and bind handlers */ /* text widget with history */ widgets[0] = g_object_new(GNOME_TYPE_ENTRY, "history-id", "textentry", NULL); g_signal_connect(widgets[0], "activate", G_CALLBACK(textentry_activated),                  NULL); /* file chooser */ widgets[1] = g_object_new(GNOME_TYPE_FILE_ENTRY, "history-id", "fileentry",                           NULL); g_signal_connect(widgets[1], "activate", G_CALLBACK(fileentry_activated),                  NULL); /* font picker with default font picker image */ widgets[2] = g_object_new(GNOME_TYPE_FONT_PICKER,                           "mode", GNOME_FONT_PICKER_MODE_PIXMAP,                           NULL); /* font picker with selected font on label */ widgets[3] = g_object_new(GNOME_TYPE_FONT_PICKER,                           "mode", GNOME_FONT_PICKER_MODE_FONT_INFO,                           "use-font-in-label", TRUE,                           NULL); /* font picker with custom label */ widgets[4] = g_object_new(GNOME_TYPE_FONT_PICKER,                           "mode", GNOME_FONT_PICKER_MODE_USER_WIDGET,                           NULL); gnome_font_picker_uw_set_widget(    GNOME_FONT_PICKER(widgets[4]),    GTK_WIDGET(g_object_new(GTK_TYPE_LABEL, "label", "My Text", NULL))); /* attach signal handlers for all three font pickers */ for (i=2; i<=4; i++) {    g_signal_connect(widgets[i],         "font-set", G_CALLBACK(fontpicker_changed), (gpointer)(i-1)); } /* color picker */ widgets[5] = g_object_new(GNOME_TYPE_COLOR_PICKER,                           "dither", TRUE,                           "use-alpha", TRUE,                           NULL); g_signal_connect(widgets[5], "color-set", G_CALLBACK(colorpicker_changed),                  NULL); /* icon picker */ widgets[6] = g_object_new(GNOME_TYPE_ICON_ENTRY,                           "history-id", "iconpicker",                           "browse-dialog-title", "Select icon",                           NULL); g_signal_connect(widgets[6], "changed", G_CALLBACK(iconpicker_changed),                  NULL); /* image picker/viewer */ widgets[7] = gnome_pixmap_entry_new("pixmapentry", "Select image", TRUE); g_signal_connect(widgets[7], "activate", G_CALLBACK(pixmap_changed),                  NULL); /* date/time entry: workday from 9 'til 5 */ widgets[8] = g_object_new(GNOME_TYPE_DATE_EDIT,                           "time", (gulong)time(NULL),                           "lower-hour", 9,                           "upper-hour", 17,                           NULL); /* distinguish date and time to event handler with gboolean */ g_signal_connect(widgets[8],                  "date-changed", G_CALLBACK(date_or_time_changed),                  (gpointer)TRUE); g_signal_connect(widgets[8],                  "time-changed", G_CALLBACK(date_or_time_changed),                  (gpointer)FALSE); /* create a two-column table for all of the widgets */ table = g_object_new(GTK_TYPE_TABLE,                      "n-rows", 9,                      "n-columns", 2,                      "column-spacing", 6,                      "row-spacing", 6,                      NULL); /* pack a widget with its label in each row */ for (i = 0; i < NUM_WIDGETS; i++) {    gtk_table_attach(table, g_object_new(GTK_TYPE_LABEL,                                           "label", widget_label[i],                                           "use-underline", TRUE,                                           "mnemonic-widget", widgets[i],                                           "xalign", 1.0,                                           "justify", GTK_JUSTIFY_RIGHT,                                           NULL),                       0, 1, i, i+1, GTK_EXPANDGTK_FILL, 0, 0, 0);      gtk_table_attach(table, g_object_new(GTK_TYPE_ALIGNMENT,                                           "xalign", 0.0,                                           "child", widgets[i],                                           NULL),                       1, 2, i, i+1, GTK_EXPANDGTK_FILL, 0, 0, 0);   }   /* pack table, show all widgets, and start GTK+ main event loop */   gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(table));   gtk_widget_show_all(GTK_WIDGET(window));   gtk_main();   return 0; } 

4.3.4 Text Entry Widgets with History

GnomeEntry ( GNOME_TYPE_ENTRY ) widgets are slightly enhanced GtkEntry text entry widgets. The GNOME variant has a built-in history feature that allows the users to select a previous entry from a drop-down list. Therefore, it is a subclass of GtkCombo . You can access the entry box in a GnomeEntry object as a GtkEntry object through the gtk-entry property.

To take advantage of the history feature, set the history-id property to a string identifier unique to your application. The GNOME libraries store the history based on this identifier. The widget's history persists if you quit and restart the application.

The entry widget records a new history item in the widget when the user activates the widget. However, if you need to insert a few default suggestions or do some housekeeping, you can manipulate the history independently with these functions:

  • void gnome_entry_prepend_history(GnomeEntry *entry, gboolean save, const gchar *text)

    Places text at the start of the history for entry . If you set save to TRUE , the GNOME libraries store this entry for later use.

  • void gnome_entry_append_history(GnomeEntry * entry, gboolean save , const gchar * text )

    Like the preceding function, but places text at the end of the history list.

  • void gnome_entry_clear_history(GnomeEntry * entry)

    Clears the history for entry .

  • void gnome_entry_set_max_saved(GnomeEntry * entry , guint max )

    Sets the maximum number of items in entry to max .

  • guint gnome_entry_get_max_saved(GnomeEntry * entry )

    Returns the maximum number of items in entry .

GnomeEntry has an activate signal that works like GtkEntry ; its handler is

 void handler(GnomeEntry *entry, gpointer data); 
Note  

Use GnomeEntry instead of GtkEntry when you feel that the user will frequently retrieve previously entered items, and keep in mind that there is a limit on the number of items in the history.

If you feel that the user will not (or should not) want to change any of the entries in the list, try a GtkCombo widget instead.

If the user won't likely repeat anything in the entry box (for example, in an installation dialog), use GtkEntry .

4.3.5 File Choosers

File entry boxes are GNOME text entry boxes for filenames, incorporating a history feature and a Browse... button nearby to summon a file browser. After the user selects a file in the file browser, GNOME places the filename in the entry box and then activates the entry box. These file choosers combine the convenience of file browsing with the speed of typing the filename directly without additional dialogs. File choosers can also verify that a file exists.

This widget's implementation is the GnomeFileEntry class ( GNOME_TYPE_FILE_ENTRY ). Its properties are as follows :

  • history-id ( gchararray ): The history identifier (you must supply this property).

  • browse-dialog-title ( gchararray ): The file browser dialog title (for when the user clicks the Browse... button).

  • directory-entry ( gboolean ): Set this to TRUE if the widget should choose adirectory.

  • modal ( gboolean ): If TRUE , the browser dialog is modal (monopolizes the input focus). Set this only if absolutely necessary.

  • filename ( gchararray ): The name currently in the entry box.

  • default-path ( gchararray ): The default path in for the file chooser (for when the user types a relative pathname into the box).

  • gnome-entry ( GnomeEntry * , read-only): The GnomeEntry widget in the file chooser.

  • gtk-entry ( GtkEntry * , read-only): The GtkEntry widget in the file chooser.

To read a full file path from a GnomeEntry widget, use

 char *filename;  filename  = gnome_file_entry_get_full_path(  widget  ,  verify  ); 

Set verify to TRUE if you want GNOME to make sure that the file actually exists (in that case, this function returns NULL if the file does not exist).

Like GnomeEntry and GtkEntry , file chooser widgets have an activate signal with this handler prototype:

 void handler(GnomeFileEntry *entry, gpointer data); 

4.3.6 Font Pickers

Picking fonts with the GnomeFontPicker ( GNOME_TYPE_FONT_PICKER ) widget is similar to choosing files, except that there is no entry box (it doesn't make much sense to type the font name). Therefore, a font picker widget is just a button that the user clicks to get the font selection dialog from Section 3.6.8.

A font picker's configuration primarily consists of a button's appearance, reflected in several of the following properties:

  • mode (enumeration): The button's appearance. Possible values are as follows:

    • GNOME_FONT_PICKER_MODE_PIXMAP : The button has an icon label.

    • GNOME_FONT_PICKER_MODE_FONT_INFO : The button displays the selected font name.

    • GNOME_FONT_PICKER_MODE_USER_WIDGET : The button displays an arbitrary widget.

  • use-font-in-label ( gboolean ): Set the button's label in the selected font. This is useful in conjunction with GNOME_FONT_PICKER_MODE_FONT_INFO (see the preceding property).

  • show-size ( gboolean ): When displaying the font name in the label with GNOME_FONT_PICKER_MODE_FONT_INFO , include the font size. The default is TRUE .

  • label-font-size ( gint ): The font size in the label. Even when using GNOME_FONT_PICKER_MODE_FONT_INFO , GNOME sets the label in this size.

  • font-name ( gchararray ): The current font.

  • title ( gchararray ): The title for the font picker dialog window.

  • preview-text ( gchararray ): The dialog preview text. The default for font choosers is the alphabet set in uppercase and lowercase, but you can use something like "The quick brown fox jumped over the lazy dogs" if you feel the need for variety.

If you set mode to GNOME_FONT_PICKER_MODE_USER_WIDGET , you can put any widget that you like inside the picker button with this function:

 gnome_font_picker_uw_set_widget(  picker  ,  widget  ) 
Note  

Like regular buttons and other similar elements, the only two widgets that make much sense here are GtkLabel and GtkImage .

GnomeFontPicker has a font-set signal, emitted when the user chooses a font from the dialog. Its handler prototype looks like this:

 void handler(GnomeFontPicker *picker, gchar *font_name, gpointer data); 

4.3.7 Color Pickers

The GnomeColorPicker ( GNOME_TYPE_COLOR_PICKER ) widget is like the GNOME font picker, but simpler, because the button in the color picker displays only a sample of the current color. When you click the button, a GtkColorSelection dialog appears.

A GNOME color picker's properties are as follows:

  • red ( guint ): The current color's red component.

  • green ( guint ): The current color's green component.

  • blue ( guint ): The current color's blue component.

  • use-alpha ( gboolean ): If TRUE , the color has an alpha channel (transparency factor).

  • alpha ( guint ): The alpha channel value.

  • title ( gchararray ): The GtkColorSelection dialog title.

  • dither ( gboolean ): If TRUE , GNOME dithers the color sample in the button if it cannot display the color precisely.

By default, color components are 16 bits wide; that is, they are between 0 and 65535.

GnomeColorPicker has one significant signal, color-set , emitted when the user chooses a color. Its handler prototype is

 void handler(GnomeColorPicker *picker,              guint red, guint green, guint blue, guint alpha); 

4.3.8 Icon Pickers

The GnomeIconEntry GNOME_TYPE_ICON_ENTRY widget is a small square button that displays an icon. When the user clicks the button, a dialog appears with a choice of icons (see Figure 4.3).

click to expand
Figure 4.3: GNOME icon selection dialog.

GnomeIconEntry properties include the following:

  • filename ( gchararray ): The selected icon's filename.

  • pixmap-subdir ( gchararray ): The icon directory that the dialog searches (relative to $(PREFIX)/share/pixmaps ).

  • history-id ( gchararray ): The icon picker's history identifier.

  • browse-dialog-title ( gchararray ): The icon selection dialog title.

  • pick-dialog ( GtkDialog * , read-only): The dialog widget (for changing its properties).

When the user selects an icon, GNOME emits a changed signal. The handler prototype is

 void handler(GnomeIconEntry *entry, gpointer data); 

4.3.9 Image Pickers

To allow the user to choose an arbitrary image instead of an icon, you can put a GnomePixmapEntry ( GNOME_PIXMAP_ENTRY ) widget in your application. This subclass of GnomeFileEntry displays the selected image in addition to the filename.

To create a GnomePixmapEntry widget, use this generator function:

 GtkWidget *  image_entry  ;  image_entry  = gnome_pixmap_entry_new(  history_id  ,  dialog_title  , TRUE); 

Here, history_id is a GNOME history identifier for previous images, and dialog_title is the picker's dialog box title.

Note  

The third parameter in the preceding function enables the view of the image in the picker widget. Under most circumstances, you should set this parameter to TRUE . Otherwise, this widget does not verify that the file is an image and is therefore nearly identical to GnomeFileEntry ” the only difference is that there is a small thumbnail in the image picker dialog.

These functions operate on GnomePixmapEntry widget objects:

  • void gnome_pixmap_entry_set_pixmap_subdir(GnomePixmapEntry * entry , const char * subdir )

    Sets a default directory for the entry file browser; subdir should be relative to $(PREFIX)/share/pixmaps .

  • void gnome_pixmap_entry_set_preview_size(GnomePixmapEntry * entry , gint width , gint height )

    Sets the image picker entry view size to width by height pixels.

  • gchar *gnome_pixmap_entry_get_filename(GnomePixmapEntry * entry )

    Returns the current image in the picker as a newly allocated string. When the preview is active, this function verifies that the selected file is an image and returns NULL if the file is not an image.

GnomePixmapEntry inherits the activate signal from its GnomeFileEntry parent class (see Section 4.3.5).

4.3.10 Date/Time Widgets

For your application's date and time entry needs, you could try to come up with a collection of spin buttons and entry widgets, or you could make your life a lot easier with GnomeDateEdit ( GNOME_TYPE_DATE_EDIT ) widgets. These consist of text entry boxes for the date and time. The date box comes with a drop-down calendar, and the time box has an option menu. (If you run the example in Section 4.3.3, you'll get the idea.)

These GnomeDateEdit properties are available:

  • dateedit-flags : A bitwise OR of the following options:

    • GNOME_DATE_EDIT_SHOW_TIME : Shows the time in addition to the date.

      In the current implementation, omitting this option appears to have no effect.

    • GNOME_DATE_EDIT_24_HR : Shows the time in 24-hour format (military and continental European formats).

    • GNOME_DATE_EDIT_WEEK_STARTS_ON_MONDAY : Weeks will start on Monday in the drop-down calendar. Many continental European calendars use this format.

    The default is GNOME_DATE_EDIT_SHOW_TIME .

  • lower-hour ( gint ): The first hour to show in the time entry option menu. Note that this is in 24-hour format.

  • upper-hour ( gint ): The last hour to show in the time entry option menu.

  • time ( gulong ): The current time and date. Use a time_t cast for this property.

Warning  

Make sure that you set the time property when you create the widget; otherwise, you'll get a few critical log messages when you first try to access the widget. To get the current time and date from the system, use (gulong)time(NULL) .

GnomeDateEdit has two signals, date-changed and time-changed , emitted when the user changes the date or time. The handler prototype is the same for both:

 void handler(GnomeDateEdit *entry, gpointer data); 

4.3.11 Hyperlinks

Your application may occasionally need to provide a link to information available on the Internet. The GnomeHRef ( GNOME_TYPE_HREF ) widget is a subclass of GtkButton that looks like a link in a web browser. When the user clicks the link, GNOME opens the URL.

When you create a GnomeHRef object, use its text property to supply a label, and set the url property to the target URL (both of these properties are strings).

Here is a short example; see Figure 4.4 on the next page for the final result. Although a link usually shows up as underlined , blue text, its ultimate appearance depends on the user's GTK+ theme.


Figure 4.4: GNOME hyperlinks.
 /* -*-coding: utf-8;-*- */ /* href.c -- demonstrate GNOME hyperlinks */ #include <gnome.h>   << standard event handlers >> int main(int argc, char **argv) {   GnomeProgram *program;   GtkWindow *window;   GtkVBox *vbox;   GnomeHRef *link[5];   gint i;   /* initialize application */   program = gnome_program_init("href", "0.1", LIBGNOMEUI_MODULE,                                argc, argv,                                GNOME_PROGRAM_STANDARD_PROPERTIES,                                GNOME_PARAM_HUMAN_READABLE_NAME, "HRef",                                GNOME_PARAM_ENABLE_SOUND, TRUE,                                NULL);   /* create main window */   window = g_object_new(GTK_TYPE_WINDOW,                         "title", "GNOME Links",                         "border-width", 12,                         NULL);   << attach standard event handlers >>   /* create five hyperlink objects */   link[0] = g_object_new(GNOME_TYPE_HREF,                          "url", "http://www.gnome.org",                          "text", "www.gnome.org",                          NULL);   link[1] = g_object_new(GNOME_TYPE_HREF,                          "url", "http://news.gnome.org/gnome-news/",                          "text", "Gnotices",                          NULL);   link[2] = g_object_new(GNOME_TYPE_HREF,                          "url", "http://www.gnomedesktop.org",                          "text", "FootNotes",                          NULL);   link[3] = g_object_new(GNOME_TYPE_HREF,                          "url", "http://download.gnome.org",                          "text", "download.gnome.org",                          NULL);   link[4] = g_object_new(GNOME_TYPE_HREF,                          "url", "http://developer.gnome.org",                          "text", "developer.gnome.org",                          NULL);   /* pack all of these into a VBox */   vbox = g_object_new(GTK_TYPE_VBOX, "spacing", 6, NULL);   for (i=0; i<5; i++)   {    gtk_box_pack_start_defaults(GTK_BOX(vbox), GTK_WIDGET(link[i]));   }   /* pack VBox, show everything, start GTK+ main event loop */   gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));   gtk_widget_show_all(GTK_WIDGET(window));   gtk_main();   return 0; } 

4.3.12 High Scores

As mentioned in Section 4.2.4, libgnome has a facility for video game scores. The libgnomeui library has a special widget ( GnomeScores ) to display the top ten scores. To create one of these special dialog windows, call

 gnome_scores_display_with_pixmap  (image_file, game_name, level, rank)  

Here, image_file is the filename of a representative image, game_name is the game's name (from gnome_score_init() ), level is the game level (a string), and rank is the last game's rank in the high score list (obtained from gnome_score_log() ). If you don't have a rank, use 0.

You'll get a new GnomeScores object for the new dialog window; see Figure 4.5. GnomeScores is a subclass of GtkDialog with an OK button to close the window.

click to expand
Figure 4.5: A GnomeScores high score list.

Although this book does not show any actual high score code, the accompanying code available on the Web contains a sample program called high-scores .

4.3.13 About (Credits) Windows

Nearly all applications have a window to give some credit to the developers. This is the About window that lists the application name, version, and developers' names . All GNOME applications should have such a window attached to the Help > About menu. As you might expect, GNOME offers a GnomeAbout ( GNOME_TYPE_ABOUT ) widget, and you can create one with a single function call:

 gnome_about_new(  name  ,  version  ,  copyright  ,  comments  ,  programmers  ,  documenters  ,  translator  ,  logo  ) 

These parameters are

  • name ( gchar * ): The application name.

  • version ( gchar * ): The application version.

  • copyright ( gchar * ): A copyright statement.

  • comments ( gchar * ): Other comments.

  • programmers ( gchar ** ): The people who wrote the code (a NULL - terminated array).

  • documenters ( gchar ** ): The people who wrote the documentation (a NULL- terminated array).

  • translator ( gchar * ): The translator name for the current locale (or NULL if there wasn't any).

  • logo ( GdkPixbuf * ): The application's logo image file.

Here is an example that should give you the idea. Figure 4.6 shows the window in action; you can click the Credits button to see who wrote the program. The OK button closes the window.

click to expand
Figure 4.6: About dialog.
 /* -*-coding: utf-8;-*- */ /* about.c -- demonstrate gnome "about" windows */ #include <gnome.h> /* standard event handlers */ gint delete_event(GtkWidget *widget, GdkEvent event, gpointer data) {   return FALSE; } void end_program(GtkWidget *widget, gpointer data) {   gtk_main_quit(); } int main(int argc, char **argv) {   GnomeProgram *program;   GnomeAbout *info;   gchar *translator;   gchar *apple;   GdkPixbuf *logo;   const gchar *programmers[] = {      "George Ricshaw",      "Edna Kolakowski",      "Merideth Gainer",      NULL   };   const gchar *documenters[] = {      "Walter Zimmerman",      "Harold Fenner",      "Heather Kunkle",      NULL   };   /* Initialize GNOME */   program = gnome_program_init(                "about", "3.0",                LIBGNOMEUI_MODULE,                argc, argv,                GNOME_PROGRAM_STANDARD_PROPERTIES,                GNOME_PARAM_HUMAN_READABLE_NAME, "AboutDemo(GrannySmith)",                GNOME_PARAM_ENABLE_SOUND, TRUE,                NULL);   /* determine if the program is running in a translated environment;      if no, set translator to NULL */   if (_("Translator") == "Translator")   {      translator = NULL;   } else {      translator = g_strdup(_("Put your translator here."));   }   /* find the green apple image; it should be in the standard      GNOME pixmap directory */   apple = gnome_program_locate_file(program,                                     GNOME_FILE_DOMAIN_PIXMAP,                                     "apple-green.png",                                     TRUE,                                     NULL);   /* allocate logo pixmap */   logo = gdk_pixbuf_new_from_file(apple, NULL);   g_free(apple);   /* create "about" window */   info = GNOME_ABOUT(             gnome_about_new("GrannySmith", "3.0",                             "(C) 2003 Rixico Inc.",                             "The Malus domestica Borkh Solutions People",                             programmers, documenters, translator, logo));   /* because this data was copied, it should be freed */   g_free(translator);   g_object_unref(logo);   /* attach standard event handlers to info window */   g_signal_connect(info, "delete-event", G_CALLBACK(delete_event), NULL);   g_signal_connect(info, "destroy", G_CALLBACK(end_program), NULL);   /* show widget, start GTK+ main loop */   gtk_widget_show(GTK_WIDGET(info));   gtk_main();   return 0; } 

4.3.14 GNOME Stock Item Additions

The GTK+ stock item library originated in GNOME. Some of the GNOME 1 stock items are somewhat too specialized for GTK+, so libgnomeui offers these as extensions as soon as you initialize a GNOME program. See Appendix A for the list.

4.3.15 Druids

Druid is the GNOME name for what other systems call wizards or assistants: a means of stepping users through a somewhat complicated process that doesn't come up often. A druid displays a series of pages with input widgets and with detailed explanations .

Note  

Among typical druid tasks are an application's first-time setup and new-profile configuration (for example, a new email account or application project environment). Never use druids for frequent tasks such as sending email or adding a new source code file.

Here is a complete druid example that illustrates the GNOME druid classes. The first part contains some declarations, global constants, and an event handler to finish the program:

 /* -*-coding: utf-8;-*- */ /* druid.c -- sample GNOME druid */ #include <gnome.h> GtkEntry *last_name_entry; GtkEntry *first_name_entry; GtkWindow *window; gchar *flavors[] = {   "Vanilla", "Chocolate", "Strawberry", "Pistachio", "Mustard onion" }; #define NUM_FLAVORS 5 #define NUM_MUSTARD NUM_FLAVORS - 1 GtkRadioButton *ice_cream[NUM_FLAVORS]; /* standard handler to terminate event loop */ void end_program(GtkWidget *widget, gpointer data) {   gtk_main_quit(); } 

The auxiliary following function creates a warning dialog in case one of the pages in the druid isn't filled out correctly.

 /* warning dialog using GNOME guidelines, as in dialog.c */ void warn_dialog(gchar *message) {   gchar *markup;   GtkDialog *dialog;   GtkHBox *hbox;   GtkImage *icon;   GtkLabel *text;   markup = g_strdup_printf("<big><b>Druid configuration</b></big>\n\n%s",                            message);   dialog = GTK_DIALOG(gtk_dialog_new_with_buttons(                       "Warning", window, GTK_DIALOG_MODAL,                       "OK", GTK_RESPONSE_OK, NULL));   gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);   g_signal_connect_swapped(dialog, "response", G_CALLBACK(gtk_widget_destroy),                            GTK_WIDGET(dialog));   hbox = g_object_new(GTK_TYPE_HBOX, "border-width", 8, NULL);   icon = g_object_new(GTK_TYPE_IMAGE,                       "stock", GTK_STOCK_DIALOG_WARNING,                       "icon-size", GTK_ICON_SIZE_DIALOG,                       "xalign", 0.5, "yalign", 0.5, NULL);   gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(icon), FALSE, FALSE, 0);   text = g_object_new(GTK_TYPE_LABEL,                       "wrap", TRUE,                       "label", markup,                       "use-markup", TRUE, NULL);   g_free(markup);   gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(text), TRUE, TRUE, 0);   gtk_box_pack_start(GTK_BOX(dialog->vbox), GTK_WIDGET(hbox),                      FALSE, FALSE, 0);   gtk_widget_show_all(GTK_WIDGET(dialog)); } 

The following signal handler makes sure that the user entered their first and last names on the page. Notice the return value; if a handler that is supposed to verify a page returns TRUE , the druid does not allow the user to proceed to the next page.

 /* verify the first and last name (page 1) */ gboolean check_page1(GnomeDruidPage *page, GtkWidget *druid, gpointer data) {   gchar *last_name = NULL;   gchar *first_name = NULL;   gboolean return_val;   g_object_get(last_name_entry, "text", &last_name, NULL);   g_object_get(first_name_entry, "text", &first_name, NULL);   if ((!*last_name)  (!*first_name))   {      warn_dialog("You must supply your first and last names.");      return_val = TRUE;   } else {     return_val = FALSE;   }   g_free(last_name);   g_free(first_name);   return return_val; } 

Here is a slight variation on the preceding handler. This time, the handler asks the user to double-check a certain input value, but will not complain if it gets the same value again.

 /* check the favorite ice cream flavor (page 2) */ /* if someone picks "mustard onion" as their favorite ice cream    flavor, show a warning, but only once. If the user insists, they    get it the second time. */ gboolean check_page2(GnomeDruidPage *page, GtkWidget *druid, gpointer data) {   static gboolean already_warned = FALSE;   gboolean mustard_onion;   if (!already_warned)   {      g_object_get(ice_cream[NUM_MUSTARD], "active", &mustard_onion, NULL);      if (mustard_onion)      {         warn_dialog("Do you really want mustard onion? If you're\  sure about this, click Forward again.");         already_warned = TRUE;         return TRUE;      }   }   return FALSE; } 

The finish_config() function prints the final configuration. The druid emits a signal to call this handler when the user is done.

 /* signal handler to finish configuration */ void finish_config(GnomeDruidPage *page, GtkWidget *druid, gpointer data) {   gboolean active;   gchar *first, *last;   gint i;   for (i = 0; i < NUM_FLAVORS; i++)   {      g_object_get(ice_cream[i], "active", &active, NULL);      if (active)      {         break;      }   }   g_object_get(first_name_entry, "text", &first, NULL);   g_object_get(last_name_entry, "text", &last, NULL);   g_print("Druid finished. Configuration:\n");   g_print("First name: %s\nLast name: %s\nFavorite flavor: %s\n",           first, last, flavors[i]);   g_free(first); g_free(last);   /* end the program */   g_signal_emit_by_name(window, "destroy", NULL); } 
Note  

The last line in the preceding code terminates the application because there is nothing more to this example. However, in a real program, you would want to destroy druid instead of window , to let the main program run as usual.

The first part of the main program contains the usual declarations, as well as some image loading:

 int main(int argc, char **argv) {   GnomeProgram *program;   GdkColor color;   GnomeDruid *druid;   GdkPixbuf *logo, *watermark, *watermark_top;   GnomeDruidPageEdge *title_page, *finish_page;   GnomeDruidPageStandard *page_1, *page_2;   GtkVBox *ice_vbox;   gint i;   /* initialize GNOME */   program = gnome_program_init("druid", "0.1",                                LIBGNOMEUI_MODULE,                                argc, argv,                                GNOME_PARAM_HUMAN_READABLE_NAME, "Druid",                                NULL);   /* load images */   logo = gdk_pixbuf_new_from_file("settings.png", NULL);   watermark = gdk_pixbuf_new_from_file("watermark.png", NULL);   watermark_top = gdk_pixbuf_new_from_file("watermark-top.png", NULL); 

Now you're ready to create the first page in the druid with a special generator function (see Figure 4.7 on page 295):

 /* create title page */   title_page = GNOME_DRUID_PAGE_EDGE(      gnome_druid_page_edge_new_with_vals(         GNOME_EDGE_START, TRUE,         "Welcome to the Sample Druid!",         "This is the explanation on the title page.\n\ Like other druid pages, this page has a title and images: \n\  - A logo in the upper right corner,\n\  - A watermark at the top (empty in this druid), and\n\  - A watermark along the left edge.",         logo, watermark, watermark_top));   /* title page uses black text on a white background */   gdk_color_parse("white", &color);   gnome_druid_page_edge_set_bg_color(title_page, &color);   gdk_color_parse("black", &color);   gnome_druid_page_edge_set_title_color(title_page, &color); 
click to expand
Figure 4.7: Druid title page.

Notice, however, that page 2 does not use the generator function ” in fact, it has a different type.

 /* create page 1 (the one after the title) */   page_1 = g_object_new(GNOME_TYPE_DRUID_PAGE_STANDARD,                         "title", "Your Name",                         "logo", logo,                         "top-watermark", watermark_top,                         NULL); 

There are some entry widgets in this page. Figure 4.8 on page 296 shows the final page.

click to expand
Figure 4.8: Final druid page.
 /* fill page 1 main content */   first_name_entry = g_object_new(GTK_TYPE_ENTRY, NULL);   gnome_druid_page_standard_append_item(      page_1,      "This is page 1.\nThere are no graphics to the left, but \ the bar at the top remains.\n\n What is your first name?",      GTK_WIDGET(first_name_entry),      "Your parents probably supplied your first name. Ask them \ if you can't remember what it is.");   last_name_entry = g_object_new(GTK_TYPE_ENTRY, NULL);   gnome_druid_page_standard_append_item(      page_1,      "What is your last name?",      GTK_WIDGET(last_name_entry),      "If you don't know, open a telephone book and pick one that looks nice."); 

You can also see that the next signal is for when the user clicks the Next button:

 /* attach handler for verifying the information on this page */   g_signal_connect(page_1, "next", G_CALLBACK(check_page1), NULL); 

The next page is similar (see Figure 4.10 on page 297):

 /* create page 2 */   page_2 = g_object_new(GNOME_TYPE_DRUID_PAGE_STANDARD,                         "title", "Favorite Ice Cream",                         "logo", logo,                         "top-watermark", watermark_top,                         NULL);   /* fill second page */   ice_vbox = g_object_new(GTK_TYPE_VBOX, NULL);   for (i = 0; i < NUM_FLAVORS; i++)             /* ice cream widgets */   {     ice_cream[i] = g_object_new(GTK_TYPE_RADIO_BUTTON, NULL);     gtk_box_pack_start_defaults(GTK_BOX(ice_vbox), GTK_WIDGET(ice_cream[i]));     g_object_set(ice_cream[i], "label", flavors[i], NULL);     if (i != 0)     {         g_object_set(ice_cream[i], "group", ice_cream[0], NULL);      }   }   gnome_druid_page_standard_append_item(      page_2,      "Choose your favorite ice cream flavor.",      GTK_WIDGET(ice_vbox),      "Please note that mustard onion is an acquired taste.");   /* attach signal to check the page input */   g_signal_connect(page_2, "next", G_CALLBACK(check_page2), NULL); 

However, for the final page (Figure 4.8 on page 296), you need the same type and generator function as the first page.

 /* create final page */   finish_page = GNOME_DRUID_PAGE_EDGE(      gnome_druid_page_edge_new_with_vals(         GNOME_EDGE_FINISH,         TRUE,         "Configuration Finished",         "Thanks for using this sample druid!",         logo, watermark, watermark_top));   /* text and title black on white */   gdk_color_parse("white", &color);   gnome_druid_page_edge_set_bg_color(finish_page, &color);   gnome_druid_page_edge_set_textbox_color(finish_page, &color);   gdk_color_parse("black", &color);   gnome_druid_page_edge_set_text_color(finish_page, &color);   gnome_druid_page_edge_set_title_color(finish_page, &color); 

To define an action for when the user finishes with the druid, attach a handler to the finish signal with this page:

 /* connect signal for the finish button */   g_signal_connect(finish_page, "finish", G_CALLBACK(finish_config), NULL); 

Now that you have all of the pages, you can create and show the druid and get on with the rest of the program:

 /* create main druid widget */   druid = GNOME_DRUID(gnome_druid_new_with_window(              "Druid Demonstration", NULL, TRUE, (GtkWidget**)(&window)));   /* add all of the pages to the druid and show the widget */   gnome_druid_append_page(druid, GNOME_DRUID_PAGE(title_page));   gtk_widget_show_all(GTK_WIDGET(title_page));   gnome_druid_append_page(druid, GNOME_DRUID_PAGE(page_1));   gtk_widget_show_all(GTK_WIDGET(page_1));   gnome_druid_append_page(druid, GNOME_DRUID_PAGE(page_2));   gtk_widget_show_all(GTK_WIDGET(page_2));   gnome_druid_append_page(druid, GNOME_DRUID_PAGE(finish_page));   gtk_widget_show_all(GTK_WIDGET(finish_page));   /* attach destroy handler (end program when druid cancels) */   g_signal_connect(druid, "destroy", G_CALLBACK(end_program), NULL);   /* no longer need references to these images */   g_object_unref(logo);   g_object_unref(watermark);   g_object_unref(watermark_top);   /* start GTK+ main event loop */   gtk_main();   return 0; } 

On to the details of the druid components.

Druid Pages

A druid is a digital workbook for the user to fill out. Druid widgets are GnomeDruid objects filled with page objects. All of the page classes described here inherit characteristics from the abstract GnomeDruidPage parent class:

  • A title page explaining the druid's purpose. This page belongs to the GnomeDruidPageEdge class.

  • Two or more normal pages, usually containing input widgets. These pages are GnomeDruidPageStandard objects.

  • A final page explaining that the user is finished with the druid. Like the title, this is a GnomeDruidPageEdge object.

All druid pages have three buttons at the bottom: Cancel to abort the druid, Back to move back a page, and Forward to go to the next page. On the final page, the Forward button becomes an Apply button to complete the process.

Note  

All druids should have at least four pages (counting the title and finish pages). There is no real difference between a druid with one normal page and a simple dialog box, other than that the druid version is annoying.

Title and Final Pages

Title and final pages contain only text and graphics; their appearance should differ slightly from normal pages. If you have a lot of fancy graphics that you need to get out of your system, consider using them in these pages instead of the normal pages. Figure 4.7 shows a title page; Figure 4.8 on page 296 shows a final page.

To create a GnomeDruidPageEdge ( GNOME_TYPE_DRUID_PAGE_EDGE ) object, use this generator function:

 GtkWidget  *page;  page  =  gnome_druid_page_edge_new_with_vals  (position,   antialiased,   title,   text  ,  logo  ,  watermark  ,  top_watermark  ); 
  • position ( GnomeEdgePosition ): The type of page. Possible values are

    • GNOME_EDGE_START : Title page.

    • GNOME_EDGE_FINISH : Final page.

    • GNOME_EDGE_OTHER : An internal page (for long druids).

  • antialiased ( gboolean ): Set this to TRUE if you want GNOME to smooth edges in the druid.

  • title ( gchar * ): The title to appear at the top of the page.

  • text ( gchar * ): The text inside the page.

  • logo ( GdkPixbuf * ): A pixbuf for the top-right corner of the druid (next to the title).

  • watermark ( GdkPixbuf * ): A pixbuf for the druid's left side. This image should be tall and narrow.

  • top_watermark ( GdkPixbuf * ): A pixbuf to place underneath the title. If you feel that this is absolutely necessary, make sure that the image is wide, thin, and as transparent as possible.

The return value is a GtkWidget , but you may want to cast it to GnomeDruidPageEdge so that you can use it with these API functions to set the colors:

  • void gnome_druid_page_edge_set_bg_color(GnomeDruidPageEdge * page , GdkColor * color )

    Sets the edge and top background color of a title/final page to color . By default, this is a shade of blue.

  • void gnome_druid_page_edge_set_textbox_color(GnomeDruidPageEdge * page , GdkColor * color )

    Sets the text background color of page to color .

  • void gnome_druid_page_edge_set_logo_bg_color(GnomeDruidPageEdge * page , GdkColor * color )

    Sets the background color behind the logo in page to color .

  • void gnome_druid_page_edge_set_title_color(GnomeDruidPageEdge * page , GdkColor * color )

    Sets the title text color in page to color .

  • void gnome_druid_page_edge_set_text_color(GnomeDruidPageEdge * page , GdkColor * color )

    Sets the body text color in page to color .

Remember that these are for title and final pages only; for normal pages, use the properties in the next section.

To define a color for use in one of the preceding functions, fill a GdkColor structure like this:

 GdkColor  color  ; gdk_color_parse("  color_name  ",  &color  ); 

Here, color_name is an X11 name such as white , honeydew , or #000050 . After you call gdk_color_parse() , you can use &color as a color parameter in the GnomeDruidPageEdge functions.

To attach a handler to the Apply button in a final page, refer to the next signal, described on page 299.

Normal Druid Pages

GnomeDruidPageStandard ( GNOME_TYPE_DRUID_PAGE_STANDARD ) objects do not have side images. Normal pages may contain any kind of widget and explanatory text, as you can see from Figures 4.9 and 4.10.

click to expand
Figure 4.9: A normal druid page.
click to expand
Figure 4.10: Another normal druid page.

You do not need a special generator function for normal druid pages. To manipulate the page, these properties are at your disposal:

  • title ( gchararray ): The page title.

  • logo ( GdkPixBuf * ): The logo at the top right.

  • top-watermark ( GdkPixBuf * ): A watermark image to place under the title.

  • title-foreground ( gchararray ): The title text color.

  • title-foreground-set ( gboolean ): If FALSE , the preceding property has no effect.

  • background ( gchararray ): The title and border background color.

  • background-set ( gboolean ): If FALSE , the preceding property has no effect.

  • logo-background ( gchararray ): The background color for the logo in the upper right corner.

  • logo-background-set ( gboolean ): If FALSE , the preceding property has no effect.

To add a widget to a normal druid page object called page , use

 gnome_druid_page_standard_append_item(  page  ,  question  ,  widget  ,  more_info  ) 

Here, widget is an input widget such as a text entry box or collection of input widgets in a VBox, such as a group of radio buttons; question is a question or instruction string asking the user to set the widget to an appropriate value. You can add an additional hint with more_info ; this appears below the widget.

Assembling a Druid

Now that you have several pages for a druid, you're ready to put them into a GnomeDruid ( GTK_TYPE_DRUID ) object. When you create a druid widget, you likely want it in a separate window. This generator function comes in handy for just that:

 GtkWidget *druid; druid = gnome_druid_new_with_window(  title  ,  window  ,  close_on_cancel  ,  window_ptr  ); 
  • title ( char * ): The title of the new druid window. You can set this to NULL if you want to use the application identifier.

  • window ( GtkWindow * ): The parent window that owns the druid. This can be NULL if this druid is a stand-alone application.

  • close_on_cancel ( gboolean ): If TRUE , GNOME destroys the druid window when the user clicks the Cancel button.

  • window_ptr ( GtkWidget ** ): If you need to store the window container that holds druid , pass an address of a GtkWidget pointer here. If you don't care, use NULL .

After you get the new druid, you can fill it with pages using the following functions.

Warning  

Make sure that you show a page with gtk_widget_show_all(page) when adding it to a druid.

  • void gnome_druid_append_page(GnomeDruid *druid, GnomeDruidPage *page)

    Adds page to the end of druid .

  • void gnome_druid_prepend_page(GnomeDruid * druid , GnomeDruidPage * page )

    Places page at the start of druid .

  • void gnome_druid_insert_page(GnomeDruid * druid , GnomeDruidPage * prev_page , GnomeDruidPage * page )

    Inserts page after prev_page in druid .

Note  

After calling gnome_druid_new_with_window() , you do not need to show the new druid window; GNOME does that for you. However, you still need to show each page (see the preceding discussion).

The GNOME druid API lets you reconfigure and redisplay druid pages at run time. This can be handy for several tasks, including showing a summary of the user's choices on the final page.

Interacting with Druids

A GnomeDruidPage widget has several signals:

  • next

    gboolean handler(GnomeDruidPage *page, GtkWidget *druid, gpointer data) GNOME emits this signal when the user clicks the Next button in the page belonging to druid . If handler returns TRUE , the signal emission stops, and therefore druid does not proceed to the next page. Use this signal to check for valid input on a page or to skip to a different page in the druid (see the following discussion).

  • back

    gboolean handler(GnomeDruidPage *page, GtkWidget *druid, gpointer data) Like the preceding signal, but for the Back button.

  • cancel

    gboolean handler(GnomeDruidPage *page, GtkWidget *druid, gpointer data) Like the preceding signal, but for the Cancel button. If you don't want the default handler to run (possibly destroying the druid), install an appropriate handler here that returns TRUE .

  • finish

    void handler(GnomeDruidPage *page, GtkWidget *druid, gpointer data) Like the preceding signal, but for a finish page's Apply button. Note than you cannot stop the signal emission because there is no return value.

Warning  

Don't use finish to check for valid input. You should do that with the previous pages' next handlers.

To skip to a different page in a signal handler, use

 gnome_druid_set_page(  druid  ,  page  ) 

Make sure that you return TRUE in your signal handler after you make this call, or the default handler will try to advance the page.

The example program does not skip pages, but does check for valid input.

4.3.16 Session Management

Session management saves an application's state in a user's session. The state is normally the set of document names in all of the application's windows. As a general rule of thumb, an application should be able to go back to its previous state with command-line arguments.

Therefore, a session manager's task is to ask an application for a command that restarts the application, going back to the current state. This session manager request is known as "SaveYourself." For example, if the MiracleText application presented earlier had open windows containing letter.mtx , omissions.mtx , and diary.mtx , the response to the session manager's SaveYourself command might look something like this:

 miracletext -f letter.mtx -f omissions.mtx -f diary.mtx 
Note  

It's important that the application start with the same number of windows as earlier. Otherwise, the window manager won't put the windows back in their previous positions and sizes.

The session manager allows for parallel sessions, where each session has a unique identifier. To use a certain session identifier when starting an application, use the command-line parameter --sm-client-id=id . The GNOME session manager API creates and processes this parameter for you.

Note  

It's also possible to save more complicated information than that mentioned here. For example, an application can save its state with GConf under a unique key (perhaps made from the process ID, time, and other information) at every Save operation and then pass the key and an appropriate parameter to the session manager.

However, this makes session management much more complicated because the program also needs to keep track of its own configuration somewhere else on the system. Therefore, you should always try to put all states in the command line, so that you don't have to deal with auxiliary files or other pieces that can disappear without notice. This book explains only the parts of the session manager dealing with command-line arguments.

Working with the Session Manager

To make your application aware of the session manager, create a GnomeClient object:

 GnomeClient *  client  ; client = gnome_master_client(); 

If you decide that you need to verify that your application can talk to the session manager through client , use this macro:

 GNOME_CLIENT_CONNECTED(  client  ) 

The code behind this macro expansion returns TRUE when the client is connected.

GnomeClient objects come with these signals:

  • connect

    void handler(GnomeClient *client, gboolean restart, gpointer data);

    Emitted when the session manager wants to talk to the application. If restart is TRUE , the session manager is trying to start the application using the state from the previous session.

  • save-yourself

    gboolean handler(GnomeClient *client, gint phase, GnomeSaveStyle what, gboolean end, GnomeInteractStyle interaction, gboolean fast, gpointer data); Emitted when the session manager wants the application to save its state. Here, phase is the save phase (either 1 or 2), but is essentially irrelevant; end is TRUE if the end of the session is close at hand; and fast is true if the program should try to save its state quickly.

    what indicates the program data to save:

    • GNOME_SAVE_GLOBAL : Saves general configuration data. If you use GConf to store your configuration data, you can ignore this (see Chapter 7).

    • GNOME_SAVE_LOCAL : Saves open document names and similar data.

    • GNOME_SAVE_BOTH : Saves global and local data.

    The interaction parameter tells the application if it should tell the user about any problems in saving the state:

    • GNOME_INTERACT_NONE : The user doesn't need to know anything.

    • GNOME_INTERACT_ERRORS : Tells the user if any problems occur.

    • GNOME_INTERACT_ANY : Tells the user whatever you want it to.

    The signal handler must return TRUE if it successfully saves the state and FALSE if an error occurs.

  • save-complete

    void handler(GnomeClient *client, gpointer data);

    Emitted when the session manager completes a round of SaveYourself commands to its clients .

  • disconnect

    void handler(GnomeClient *client, gpointer data);

    Emitted when the session manager is about to disconnect from the client.

  • die

    void handler(GnomeClient *client, gpointer data);

    Emitted when the session manager wants the application to terminate.

The handlers for these signals are at the heart of an application's session management code. The most important are save-yourself and die . Because it wouldn't be right to ask the program to terminate before saving its state, a SaveYourself command arrives before every Die command.

If your application needs only to save a command line in response to a SaveYourself command, implementing the handler for save-yourself is easy. Use these functions on your client object:

  • void gnome_client_set_clone_command(GnomeClient * client , gint * argc , gchar * argv [])

    Gives the session manager the command line for a completely new instance of the application by way of argc and argv . Normally, you can just supply the command without any options.

  • void gnome_client_set_restart_command(GnomeClient * client , gint * argc , gchar * argv [])

    Like the preceding function, but the command line should reflect the current state of the application.

  • void gnome_client_set_discard_command(GnomeClient * client , gint * argc , gchar * argv [])

    Gives the session manager a command to run when the application leaves the management session. If you store the application state with command lines only, you do not need to use this function.

  • void gnome_client_set_current_directory(GnomeClient * client , const gchar * dir )

    Tells the session manager that it should run the commands in dir .

  • void gnome_client_set_environment(GnomeClient * client , const gchar * varname , const gchar * value )

    Tells the session manager that it should set the environment variable varname to value before running any commands.

If you have to interact with the user by way of a dialog in your save-yourself signal handlers, do it with these functions:

  • void gnome_client_save_any_dialog(GnomeClient * client , GtkDialog * dialog )

    Ask the session manager to show dialog if the interaction mode is GNOME_INTERACT_ANY . The session manager adds Cancel and Log out buttons to dialog if the session is at an end.

  • void gnome_client_save_error_dialog(GnomeClient * client , GtkDialog * dialog )

    Like the preceding function, but operates when an error occurs while saving the state.

Warning  

It's pointless to use these outside a save-yourself signal handler. They do not guarantee that the session manager will actually show the dialog box.

The Async example in Section 8.5.5 contains minimal session management. Here is a template for how you might use session management in your program:

 gboolean save_yourself(GnomeClient *client, int phase,                        GnomeSaveStyle what,                        gboolean end,                        GnomeInteractStyle interaction,                        gboolean fast,                        gpointer user_data) {   << other declarations >>   gint save_argc;   gchar *save_argv[];   gchar *working_dir;   << set working_dir >>   << build save_argv >>   << set save_argc to the length of save_argv >>   gnome_client_set_current_directory(client, working_dir);   gnome_client_set_clone_command(client, 1, save_argv);   gnome_client_set_restart_command(client, save_argc, save_argv);   return TRUE; } void die(GnomeClient *client, gpointer data) {   gtk_main_quit(); } int main(int argc, char **argv) {   GnomeProgram *program;   GnomeClient *client;   << other declarations >>   program = gnome_program_init(<< ... >>);   /* initialize session management */   client = gnome_master_client();   /* bind session management handlers */   g_signal_connect(client, "save_yourself", G_CALLBACK(save_yourself), NULL);   g_signal_connect(client, "die", G_CALLBACK(die), NULL);   << build application >>   << parse command line >>   gtk_main();   << ... >> } 

If you want to know more about session management (though few applications use many of its features), have a look at the X11 API [Mor] and the protocol specification [Wexler]. For something a little specific to GNOME, have a look at the session management chapter in [Tredinnick].




The Official GNOME 2 Developers Guide
The Official GNOME 2 Developers Guide
ISBN: 1593270305
EAN: 2147483647
Year: 2004
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net