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.
A D V E R T I S E M E N T
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.
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
Macro
Description
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
Macro
Description
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
Macro
Description
GNOMEUIINFO_MENU_PREFERENCES_ITEM(cb, data)
"Preferences" menu item,
for application preferences
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
Macro
Description
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:
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.
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.
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.
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:
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.
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.
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.
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.