Using the GnomeApp Framework

The GnomeApp application framework is part of the libgnomeui library, but it really deserves sesparate treatment. This is the part which really makes a GNOME application a GNOME application. It also makes programming apps very simple and straightforward, and makes the apps featurefull, consistent and user customizable. With plain GTK+ you have to do a lot of things by yourself, reinventing the wheel every time, but GnomeApp takes care of the UI setup for you and still allows the user to configure that behavior and have it be consistent over different applications.

GnomeApp Overview

GnomeApp is the basic widget behind each app. It is the main window of the application, containing the document being worked on and the applications menus, tool-bars and status bars. It also remembers the docked positions of menu bars and tool-bars and such for you so that the user gets the window the way he left it when he left the application last time.

Creating a GnomeApp Window

Creating a new GnomeApp widget is as easy as calling gnome_app_new with the application name, which is usually the name of the executable or something else that is unique to your application and the title of the main window. Then you create the content of the main window and add it to the GnomeApp widget by calling gnome_app_set_contents with your contents as the argument.

Adding menu-bars, tool-bars and status-bars is equally easy, you call gnome_app_set_toolbar, gnome_app_set_menus or gnome_app_set_statusbar. gnome_app_set_toolbar is for simple applications that have only one tool-bar, for more complicated applications you need to use gnome_app_add_toolbar, which allows you to add as many docked tool-bars as you need.

Menu and Tool-bar Creation

Automatic Menu and Tool-bar Creation

Most of the time, you don't really want to create your menu-bars and tool-bars by yourself. You can use functions from libgnomeui/gnome-app-helper.h to construct menus and tool-bars for you. All you need is to fill in a couple of structures with the your information, and call gnome_app_create_menus or gnome_app_create_toolbar with that structure and voila, your application has menus and tool-bars. Sometimes you wish to pass a data pointer to all the callbacks from those structures to work with, then you'd use the gnome_app_create_toolbar_with_data and gnome_app_create_menus_with_data, and pass an extra parameter which will be passed in the data field of the callbacks.

GnomeUIInfo Structure Definition

Here is the definition of the structure you need to fill (actually you fill in an array of such structures). Also note I included the enums that you will need to fill that structure.

/* These values identify the type of pixmap used in an item */
typedef enum {
        GNOME_APP_PIXMAP_NONE,          /* No pixmap specified */
        GNOME_APP_PIXMAP_STOCK,         /* Use a stock pixmap
                                           (GnomeStock) */
        GNOME_APP_PIXMAP_DATA,          /* Use a pixmap from inline
                                           xpm data */
        GNOME_APP_PIXMAP_FILENAME       /* Use a pixmap from the
                                           specified filename */
} GnomeUIPixmapType;

/* This is the structure that defines an item in a menu bar
 * or tool-bar.  The idea is to create an array of such
 * structures with the information needed to create menus or
 * tool-bars.  The most convenient way to create such a structure
 * is to use the GNOMEUIINFO_* macros provided below. */
typedef struct {
        GnomeUIInfoType type;     /* Type of item */
        gchar *label;             /* String to use in the label */
        gchar *hint;              /* For tool-bar items, the
                                     tool-tip. For menu items, the
                                     status bar message */
        gpointer moreinfo;        /* For an item, toggle-item, or 
                                     radio-item, this is a pointer
                                     to the function to call when
                                     the item is activated. For
                                     a subtree, a pointer to
                                     another array of GnomeUIInfo 
                                     structures. For a radio-item
                                     lead entry, a pointer to an
                                     array of GnomeUIInfo
                                     structures for the radio 
                                     item group. For a help item, 
                                     specifies the help node to
                                     load (i.e. the application's
                                     identifier) or NULL for the
                                     main program's name.  For
                                     builder data, points to the 
                                     GnomeUIBuilderData structure
                                     for the following items */
        gpointer user_data;       /* Data pointer to pass to
                                     callbacks */
        gpointer unused_data;     /* Reserved for future expansion, 
                                     should be NULL */
        GnomeUIPixmapType pixmap_type;  /* Type of pixmap for
                                           the item */
        gpointer pixmap_info;     /* Pointer to the pixmap
                                     information:
                                    
                                     For GNOME_APP_PIXMAP_STOCK, a 
                                     pointer to the stock icon name.
                                    
                                     For GNOME_APP_PIXMAP_DATA, a 
                                     pointer to the inline xpm data.
                                    
                                     For GNOME_APP_PIXMAP_FILENAME, a 
                                     pointer to the filename
                                     string. */
        guint accelerator_key;    /* Accelerator key, or 0 for none */
        GdkModifierType ac_mods;  /* Mask of modifier keys for the 
                                     accelerator */

        GtkWidget *widget;        /* Filled in by gnome_app_create*,
                                     you can use this to tweak the
                                     widgets once they have been
                                     created */
} GnomeUIInfo;

Don't worry if you don't know all the items or what they mean. If you don't know what it is, just leave it NULL or 0. However, most of the time, it's easiest to copy the menu's from another app and just modify them for your needs, that way you will also know much better what does what then by just looking at the structure.

Helper Macros

Most of the time, menu entries are very simple, so one can just use one of the simple macros provided. For example, for the end of a menu, one would use the GNOMEUIINFO_END macro, for a separator one uses the GNOMEUIINFO_SEPARATOR macro. Now for the actual items there are also macros, which require you to fill in less info. For example if you have an item that you provide an xpm format data for, you can use the GNOMEUIINFO_ITEM(label, tooltip, callback, xpm_data) macro, where label is the text of the label, tool-tip is the tool-tip that the user gets when he goes over that item (or it can be NULL), callback is the function that gets called when the user presses that item, and the xpm_data is a pointer to an xpm data you want to use as the icon. If you have no icon you can just use the GNOMEUIINFO_ITEM_NONE(label, tooltip, callback) macro. If what you are adding is a standard item for which there is a stock icon (we'll talk about those next), you can use the GNOMEUIINFO_ITEM_STOCK(label, tooltip, callback, stock_id) macro where the stock_id is the id of the stock icon you want to use. Then for your main menu bar, or to put sub-menus inside your menus, you can use GNOMEUIINFO_SUBTREE(label, tree) and GNOMEUIINFO_SUBTREE_STOCK(label, tree, stock_id), where the tree is the array of GnomeUIInfo structures that you want to use as that sub-menu. There are a few other macros, but most of the time you will get by with just these macros, so you don't need to learn the entire structure of the GnomeUIInfo.

Standard Menu Item Macros

Just about all application contain a couple of standard menu items, so to keep things more consistent there are a bunch of macros that fill in everything for you except for the callback function and the data. The advantage of using the macros is consistency across applications, user customization, and translation.

Menu Items

Most of these macros have the form: GNOMEUIINFO_MENU_<name>_ITEM (callback, data). However, there is an exception, the "New xxx" item. The GNOME style guide Requires that you put what the "New" thing is into the item name. Not to mention that it will have a different hint as well. So the "New xxx" item has the structure of: GNOMEUIINFO_MENU_NEW_ITEM(label, hint, callback, data). The "label" should start with "New ". Also note that if you have more new items, you need to use the "New" subtree macro, which is explained later.

Table 3-4. The File Menu

MacroDescription
GNOMEUIINFO_MENU_NEW_ITEM(label, hint, cb, data)"New" menu item (you need to provide label and hint yourself here)
GNOMEUIINFO_MENU_OPEN_ITEM(cb, data)"Open" menu item
GNOMEUIINFO_MENU_SAVE_ITEM(cb, data)"Save" menu item
GNOMEUIINFO_MENU_SAVE_AS_ITEM(cb, data)"Save as..." menu item
GNOMEUIINFO_MENU_REVERT_ITEM(cb, data)"Revert" menu item
GNOMEUIINFO_MENU_PRINT_ITEM(cb, data)"Print" menu item
GNOMEUIINFO_MENU_PRINT_SETUP_ITEM(cb, data)"Print Setup" menu item
GNOMEUIINFO_MENU_CLOSE_ITEM(cb, data)"Close" menu item
GNOMEUIINFO_MENU_EXIT_ITEM(cb, data)"Exit" menu item

Table 3-5. The Edit Menu

MacroDescription
GNOMEUIINFO_MENU_CUT_ITEM(cb, data)"Cut" menu item
GNOMEUIINFO_MENU_COPY_ITEM(cb, data)"Copy" menu item
GNOMEUIINFO_MENU_PASTE_ITEM(cb, data)"Paste" menu item
GNOMEUIINFO_MENU_SELECT_ALL_ITEM(cb, data)"Select" menu item
GNOMEUIINFO_MENU_CLEAR_ITEM(cb, data)"Clear" menu item
GNOMEUIINFO_MENU_UNDO_ITEM(cb, data)"Undo" menu item
GNOMEUIINFO_MENU_REDO_ITEM(cb, data)"Redo" menu item
GNOMEUIINFO_MENU_FIND_ITEM(cb, data)"Find" menu item
GNOMEUIINFO_MENU_FIND_AGAIN_ITEM(cb, data)"Find Again" menu item
GNOMEUIINFO_MENU_REPLACE_ITEM(cb, data)"Replace" menu item
GNOMEUIINFO_MENU_PROPERTIES_ITEM(cb, data)"Properties" menu item, for properties of a manipulated object

Table 3-6. The Settings Menu

MacroDescription
GNOMEUIINFO_MENU_PREFERENCES_ITEM(cb, data)"Preferences" menu item, for application preferences

Table 3-7. The Windows Menu

MacroDescription
GNOMEUIINFO_MENU_NEW_WINDOW_ITEM(cb, data)"New window" menu item
GNOMEUIINFO_MENU_CLOSE_WINDOW_ITEM(cb, data)"Close window" menu item

Table 3-8. The Help Menu

MacroDescription
GNOMEUIINFO_MENU_ABOUT_ITEM(cb, data)"About" menu item

Table 3-9. The Game Menu

MacroDescription
GNOMEUIINFO_MENU_NEW_GAME_ITEM(cb, data)"New game" menu item
GNOMEUIINFO_MENU_PAUSE_GAME_ITEM(cb, data)"Pause game" menu item
GNOMEUIINFO_MENU_RESTART_GAME_ITEM(cb, data)"Restart game" menu item
GNOMEUIINFO_MENU_UNDO_MOVE_ITEM(cb, data)"Undo move" menu item
GNOMEUIINFO_MENU_REDO_MOVE_ITEM(cb, data)"Redo move" menu item
GNOMEUIINFO_MENU_HINT_ITEM(cb, data)"Hint" menu item
GNOMEUIINFO_MENU_SCORES_ITEM(cb, data)"Scores" menu item
GNOMEUIINFO_MENU_END_GAME_ITEM(cb, data)"End game" menu item

Menu Trees and Subtrees

We have already mentioned a "New" subtree. For this you should use the GNOMEUIINFO_MENU_NEW_SUBTREE (tree) macro, where the tree argument is another GnomeUIInfo structure array of the different new items.

There are also the standard top level menus. Again you pass the array of GnomeUIInfo structures to the macro.

Table 3-10. The Toplevel Menu Macros

MacroDescription
GNOMEUIINFO_MENU_FILE_TREE (tree)"File" menu
GNOMEUIINFO_MENU_EDIT_TREE (tree)"Edit" menu
GNOMEUIINFO_MENU_VIEW_TREE (tree)"View" menu
GNOMEUIINFO_MENU_SETTINGS_TREE (tree)"Settings" menu
GNOMEUIINFO_MENU_FILES_TREE (tree)"Files" menu
GNOMEUIINFO_MENU_WINDOWS_TREE (tree)"Windows" menu
GNOMEUIINFO_MENU_HELP_TREE (tree)"Help" menu
GNOMEUIINFO_MENU_GAME_TREE (tree)"Game" menu

Sometimes you may want to refer to menu path of these menus, such as for adding items to a "Windows" menu. For this you should use the macros of the form GNOME_MENU_<name>_STRING and GNOME_MENU_<name>_PATH. These will expand to the appropriate string. The macro ending with _STRING will expand to just the menu name, and the macro ending with _PATH to the menu name followed by a "/". The <name> can be one of the following: FILE, EDIT, VIEW, SETTINGS, NEW, FILES or WINDOWS.

Help Menu

Your application should contain a help menu, the help menu can be defined as:

     GNOMEUIINFO_HELP("app_name"),
     GNOMEUIINFO_MENU_ABOUT_ITEM(callback, data),
     GNOMEUIINFO_END

The GNOMEUIINFO_HELP macro takes the name of your application and expects the help files to be installed as per normal gnome procedures. FIXME: we need to add some section on help files and stuff

Example

Here is a very simple application that makes use of these:

/*
 * A simple Gnome program, outside of GNOME tree, not using i18n
 * uiinfo.c
 */
/* the very basic gnome include */
#include <gnome.h>

/* a callback for the buttons */
static void
a_callback(GtkWidget *button, gpointer data)
{
        /*just print a string so that we know we got there*/
        g_print("Inside Callback\n");
}

GnomeUIInfo file_menu[] = {
        GNOMEUIINFO_MENU_EXIT_ITEM(gtk_main_quit,NULL),
        GNOMEUIINFO_END
};

GnomeUIInfo some_menu[] = {
        GNOMEUIINFO_ITEM_NONE("_Menuitem","Just a menuitem",
                              a_callback),
        GNOMEUIINFO_SEPARATOR,
        GNOMEUIINFO_ITEM_NONE("M_enuitem2","Just a menuitem",
                              a_callback),
        GNOMEUIINFO_END
};

GnomeUIInfo menubar[] = {
        GNOMEUIINFO_MENU_FILE_TREE(file_menu),
        GNOMEUIINFO_SUBTREE("_Some menu",some_menu),
        GNOMEUIINFO_END
};

GnomeUIInfo toolbar[] = {
        GNOMEUIINFO_ITEM_STOCK("Exit","Exit the application",
                               gtk_main_quit,
                               GNOME_STOCK_PIXMAP_EXIT),
        GNOMEUIINFO_END
};

int
main(int argc, char *argv[])
{
        GtkWidget *app;
        GtkWidget *button;
        GtkWidget *hbox;
        GtkWidget *label;

        /* Initialize GNOME, this is very similar to gtk_init */
        gnome_init ("menu-basic-example", "0.1", argc, argv);
        
        /* Create a Gnome app widget, which sets up a basic
           window for your application */
        app = gnome_app_new ("menu-basic-example",
                             "Basic GNOME Application");

        /* bind "delete_event", which is the event we get when
           the user closes the window with the window manager,
           to gtk_main_quit, which is a function that causes
           the gtk_main loop to exit, and consequently to quit
           the application */
        gtk_signal_connect (GTK_OBJECT (app), "delete_event",
                            GTK_SIGNAL_FUNC (gtk_main_quit),
                            NULL);

        /*make a label as the contents*/
        label = gtk_label_new("BLAH BLAH BLAH BLAH BLAH");

        /*add the label as contents of the window*/
        gnome_app_set_contents (GNOME_APP (app), label);

        /*create the menus for the application*/
        gnome_app_create_menus (GNOME_APP (app), menubar);

        /*create the tool-bar for the application*/
        gnome_app_create_toolbar (GNOME_APP (app), toolbar);

        /* show everything inside this app widget and the app
           widget itself */
        gtk_widget_show_all(app);
        
        /* enter the main loop */
        gtk_main ();
        
        return 0;
}

Voila, an application with a menu and a tool-bar. As you see, adding extra menu items is just adding extra definitions to the GnomeUIInfo structure array.

Accelerator Keys

You have probably noticed the underlines in the labels for the menu items, these specify the accelerators for that menu. That's really all you need to do to add accelerators for menu items. The way accelerators work is very similar to the other windowing systems out there, alt-<key> if you are not browsing the menus or just the <key> if you have the menu open.

GnomeAppBar, The Status Bar

Every app should also include an application status bar on the bottom of the window. This status bar should give flashes and warnings about the application status, this is done according to the preferences as long as you use the GnomeApp message functions (described in Talking to the user section). The status bar also gives hints about menuitems and can provide a progress bar. Here are the important appbar functions.

Table 3-11. Important GnomeAppBar Methods

PrototypeDescription
GtkWidget * gnome_appbar_new (gboolean has_progress, gboolean has_status, GnomePreferencesType interactivity)Creates a new appbar widget, optionally with a progress bar if 'has_progress' is TRUE, and a statusbar if 'has_status' is true. The 'interactivity' tells us if when we want to be interactive, it can be never, always, or depending on user preferences, GNOME_PREFERENCES_USER, and that's what should probably be used. Though note that an interactive status bar is not finished yet.
void gnome_appbar_set_status (GnomeAppBar * appbar, const gchar * status)Set the text of the status bar, without changing AppBar state.
void gnome_appbar_set_default (GnomeAppBar * appbar, const gchar * default_status)Set tthe default text of the statusbar when there is nothing to display
void gnome_appbar_push (GnomeAppBar * appbar, const gchar * status)Push a message onto the appbar stack of status messages.
void gnome_appbar_pop (GnomeAppBar * appbar)Pop a message from the appbar status message stack.
void gnome_appbar_clear_stack (GnomeAppBar * appbar)Clear the status message stack
void gnome_appbar_refresh (GnomeAppBar * appbar)Refresh the status bar and set it to the current value of the stack or to the default. Useful for clearing a message put on by the gnome_appbar_set_status method.
void gnome_appbar_set_progress (GnomeAppBar *appbar, gfloat percentage)Sets the percantage on the progress bar
GtkProgress * gnome_appbar_get_progress (GnomeAppBar * appbar)Get the GtkProgress widget that is the progress bar so that you can manipulate it.

To get the appbar onto the GnomeApp window, you use the gnome_app_set_statusbar method. To also get the menu hints onto the status bar, you need to call gnome_app_install_menu_hints with the pointer of your GnomeUIInfo definition for the main menu bar. For the above example, we could add such functionality by adding:

GtkWidget *w;

w = gnome_appbar_new(FALSE, TRUE, GNOME_PREFERENCES_USER);
gnome_app_set_statusbar(GNOME_APP(app), w);

gnome_app_install_menu_hints(GNOME_APP(app), menubar);

Assume that 'app' is your GnomeApp application window and 'menubar' is your GnomeUIInfo pointer to your menu bar definition.

The way the status bar works, is that you have a stack of status messages and the most recent one is displayed in the status bar. This is useful since a lot of times your status will be hierachial anyway, and you might have a long term message that you want to be there unless some short term message needs to be displayed for a while, after this short term one is taken down the one that is ont he stack below it (the long term one) will be displayed.

You should also note that a lot of things are taken care of automatically without you having to actually touch the appbar at all. For example displaying messages on the status bar can be achieved by say gnome_app_flash described later in the Talking to the user section.

Talking To The User

A lot of times you will need to cummunicate something to the user, such as that an error orccured or inform him of something. There are preferences the user can set as to where he sees such data, so you should use the following functions rather then just making a new dialog box. This will save you time and will make the application more consistent and flexible.

Table 3-12. GnomeApp Message Functions

PrototypeDescription
GtkWidget * gnome_app_message (GnomeApp * app, const gchar * message)Display a message to the user and require confirmation from the user. In case it was a dialog, it returns the pointer to the dialog, but you can ignore that unless you wish to do something with it.
void gnome_app_flash (GnomeApp * app, const gchar * flash)Flash a quick message to the user on the status bar for a couple of seconds. Usefull for quick, non-crtical status updates.
GtkWidget * gnome_app_error (GnomeApp * app, const gchar * error)Similiar to gnome_app_message, but for displaying errors to the user.
GtkWidget * gnome_app_warning (GnomeApp * app, const gchar * warning)Also similiar to gnome_app_message, but for a warning.

An example might be when you say want to open a file and you fail.

        FILE *fp;

        fp = fopen(filename,"r");
        if(!fp) {
                char *err = g_strdup_printf(_("Cannot open file %s"),
                                            filename);
                gnome_app_error(GNOME_APP(app),err);
                g_free(err);
                ...
        }

Sometimes you might want to get some feedback from the user about a specific situation, instead of just telling him something. One of the following functions might be appropriate. These function take a function pointer, either GnomeReplyCallback or GnomeStringCalback. These callbacks take an integer reply number or a character string and a data pointer. However don't count on the callback ever being called. All of these functions return the widget of the dialog or NULL if the status line was used. Another thing to note is that these are nonblocking, which means that you cannot immediately act as if they are done.

Table 3-13. GnomeApp Query Functions

PrototypeDescription
GtkWidget * gnome_app_question (GnomeApp * app, const gchar * question, GnomeReplyCallback callback, gpointer data)A yes or no type question, the reply callback gets 0 or 1.
GtkWidget * gnome_app_question_modal (GnomeApp * app, const gchar * question, GnomeReplyCallback callback, gpointer data)Yes or no type of question, but it will not allow the user to interact with the rest of the application until he answers or closes the dialog.
GtkWidget * gnome_app_ok_cancel (GnomeApp * app, const gchar * message, GnomeReplyCallback callback, gpointer data)An OK or Cancel type of a question.
GtkWidget * gnome_app_ok_cancel_modal (GnomeApp * app, const gchar * message, GnomeReplyCallback callback, gpointer data)Modal version of the gnome_app_ok_cancel function.
GtkWidget * gnome_app_request_string (GnomeApp * app, const gchar * prompt, GnomeStringCallback callback, gpointer data)Ask the user for a string with 'prompt'. The callback may get NULL if the user cancels. The string passed to the callback is newly allocated so free it after use.
GtkWidget * gnome_app_request_password (GnomeApp * app, const gchar * prompt, GnomeStringCallback callback, gpointer data)Ask the user for a password with 'prompt'. Like gnome_app_request_string, but the text is not displayed as it's typed.

For example say we want to ask if one wants to really delete an object.

static void
really_delete_handler(int reply, gpointer data)
{
        GtkWidget *some_widget;

        some_widget = GTK_WIDGET(data);

        if(reply == 0) {
                ... /* yes was selected */
        }
}

...

/* ask a yes no question, passing some_widget as the data
   argument to the handler */
gnome_app_question(GNOME_APP(app),_("Really delete object?"),
                   really_delete_handler,
                   some widget);