Adding menus to our example

The primary mechanism for creating menus and toolbars in GNOME is a C structure called GnomeUIInfo. To ease the use of this struct the GNOME developers have provided a set of macros to wrap the initialization. The Java-GNOME bindings have a java class called UIInfo that wraps the GnomeUIInfo C structure. The UIInfo class also provides access to the stock menus provided by GNOME.

"What is a stock menu?", you ask. The GNOME libraries provide a set of stock menus, toolbar buttons, and dialog buttons that can be used by applications. Whenever possible it is suggested you use these stock widgets as it facilitates a common look-and- feel from application to application and makes new applications easier to learn.

Let's get started by looking at the UIInfo class. The table below shows the initialization parameters and their possible values. Some of the difficulty in this class is due to the fact that the C structure contains numerous pointers that actually point to different data types depending on other values in the struct. Don't worry, it is not necessary to understand the C structure or pointers to use UIInfo.

Table 1. UIInfo

Type/NameDescription
UIInfoType typeThis determines the type of item being described. The list of possible types are listed in a table later in this chapter.
String labelThis is the label that will appear in the menu item or on the toolbar.
String hintThis is the string that will appear in the statusbar for a menu or as a tooltip for a toolbar button.
moreInfoThis is one of the more complex elements in the UIInfo class. If the type is an item, toggle item, or radio item this contains the information (method, object/class, and user data) for the callback to be invoked when the menu or toolbar item is activated. For a radio item lead or a subtree type this contains an array of UIInfo objects. For a help type this contains the string that specifies the name of the help node to load. We will have more about the help node later in this chapter.
UIPixmapType pixmapTypeThis describes the type of pixmap. You will find a table of the possible values below.
pixmapInfoBased upon the type of pixmap, this contains the actual data used to render the image. If the type is none then this value contains null. If the type is filename or stock this contains a string that points to the filename or a name describing the stock pixmap to use. If the type is data this contains an array of chars that hold the pixmap data. All of the GNOME stock pixmaps are described by the GnomeStockPixmapName class.
int acceleratorKeyThis defines the accelerator key. A value of 0 means that no accelerator key is used.
GdkModifierType acModsThis contains the mask of modifier keys for the accelerator.
GtkWidget widgetThis is a read-only reference to the widget that is created by GNOME. You can use this to get the widget in order to enable/disable it, etc.

Without further delay let's describe the possible types defined in GnomeUIInfoType.

Table 2. GnomeUIInfoType

TypeDescription
ENDOFINFOThis is a special entry that lets GNOME know this is the end of a list of GnomeUIInfo elements.
ITEMThis is a normal menu item or radio menu item if it is inside of a radio list.
TOGGLEITEMThis is a toggle menu item.
RADIOITEMSThis is a list of radio items that belong to a radio group.
SUBTREEThis contains an array of sub-menu items.
SEPARATORThis is a special entry that represents a separator on a menu or toolbar.
HELPThis is a list of help topics for the help menu. We will describe the help mechanism in GNOME in detail later in this chapter.
BUILDERDATAThis feature is not currently supported by Java-GNOME.
CONFIGURABLEThis contains a menu or toolbar item that can be completely configured. More on this later.
SUBTREE_STOCKThis is the same as a SUBTREE except that the hints, lable, and icon used come from the Gnome STOCK.

The last data element that I wish to describe before moving on is the GnomeUIPixmapType class.

Table 3. GnomeUIPixmapType

TypeDescription
NONEThis pixmap type tells GNOME that there is no pixmap associated with this menu or toolbar item.
STOCKThis pixmap type tells GNOME to use one of its' stock icons.
DATAThis pixmap type provides GNOME with the raw data used to render its' icon.
FILENAMEThis pixmap type tells GNOME to read a file that contains the pixmap data.

As you can see, UIInfo can be quite confusing. If at all possible you should use one of the convenience methods provided by UIInfo. These convenience methods wrap the initialization of the UIInfo factory with easy to use static methods that closely mimic the macros provided by GNOME.

The best way to demonstrate how to use UIInfo is through an example. Let's take our previous example and add a few menus.

Example 2. Second.java - second take


import org.gnu.gnome.App;
import org.gnu.gnome.Program;
import org.gnu.gnome.UIInfo;
import org.gnu.gtk.AboutDialog;
import org.gnu.gtk.ButtonsType;
import org.gnu.gtk.DialogFlags;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.MessageDialog;
import org.gnu.gtk.MessageType;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;
import org.gnu.gtk.event.MenuItemEvent;
import org.gnu.gtk.event.MenuItemListener;

public class Second implements MenuItemListener {
    private App app = null;
    public static final String appVersion = "0.1";

    public Second() {
        createMainWindow();
        createMenus();
        app.showAll();
    }

    private void createMainWindow() {
        app = new App("Second", "Second App");
        app.setDefaultSize(200, 200);
        app.addListener(new LifeCycleListener() {
            public void lifeCycleEvent(LifeCycleEvent event) {}
            public boolean lifeCycleQuery(LifeCycleEvent event) {
                Gtk.mainQuit();
                return false;
            }
        });
    }

    private void createMenus() {

        UIInfo fileMenu[] = {
            UIInfo.newItem("New Window", "Open a new application window", this),
            UIInfo.separator(),
            UIInfo.openItem(this),
            UIInfo.saveItem(this),
            UIInfo.saveAsItem(this),
            UIInfo.separator(),
            UIInfo.closeItem(this),
            UIInfo.quitItem(new MenuItemListener() {
                public void menuItemEvent(MenuItemEvent event) {
                  fileExit();
                }
            }),
            UIInfo.end()
        };

        UIInfo editMenu[] = {
            UIInfo.undoItem(this),
            UIInfo.redoItem(this),
            UIInfo.separator(),
            UIInfo.cutItem(this),
            UIInfo.copyItem(this),
            UIInfo.pasteItem(this),
            UIInfo.separator(),
            UIInfo.findItem(this),
            UIInfo.findAgainItem(this),
            UIInfo.replaceItem(this),
            UIInfo.propertiesItem(this),
            UIInfo.end()
        };

        UIInfo moveMenu[] = {
            UIInfo.item("_Up", "Move selection up", this),
            UIInfo.item("D_own", "Move selection down", this),
            UIInfo.end()
        };

        UIInfo helpMenu[] = {
            UIInfo.help("second"),
            UIInfo.aboutItem(new MenuItemListener() {
                public void menuItemEvent(MenuItemEvent event) {
                    helpAbout();
                }
            }),
            UIInfo.end()
        };

        UIInfo mainMenu[] = {
            UIInfo.subtree("_File", fileMenu),
            UIInfo.subtree("_Edit", editMenu),
            UIInfo.subtree("_Move", moveMenu),
            UIInfo.subtree("_Help", helpMenu),
            UIInfo.end()
        };

        app.createMenus(mainMenu);
    }

    public void helpAbout() {
	    String appname = "Java-GNOME Tutorial";
	    String version = "0.1";
	    String license = "GPL";
	    String description = "Java-GNOME Tutorial.";
	    String authors[] = { 
	        "http://java-gnome.sf.net", 
	        "http://www.gtk.org" 
	    };
	    String documentors[] = { 
	        "java-gnome-developer@lists.sf.net>", 
	        "http://www.gnome.org"
	    };
	    String translators = "Language Guys Inc.";
	    String website = "http://java-gnome.sf.net";

	    AboutDialog about = new AboutDialog();
        about.setName(appname);
        about.setVersion(version);
        about.setLicense(license);
        about.setComments(description);
        about.setAuthors(authors);
        about.setDocumenters(documentors);
        about.setTranslatorCredits(translators);
        about.setWebsite(website);
		about.show();
    }

    public void fileExit() {
        Gtk.mainQuit();
    }

    public void menuItemEvent(MenuItemEvent event) {
        MessageDialog dialog = new MessageDialog(app, DialogFlags.MODAL, 
                                             MessageType.INFO, ButtonsType.OK,
                                             "Not implemented", false);
        dialog.run();
        dialog.destroy();
    }

    public static void main(String[] args) {
        Program.initGnomeUI("Second", Second.appVersion, args);
        new Second();
        Gtk.main();
    }
}

Most of the work is done in the createMenus() method which is called from the constructor. This method creates an arrays of UIInfo objects using the static methods in UIInfo. In this example we create a "File" menu and an "Edit" menu that use stock menu items. We also create a "Move" menu that creates two generic menu items. The "Move" menu uses the item() method of UIInfo. This method specifies a generic menu item with no icon. A few other UIInfo methods are use in this example. For a reference to all of the methods for UIInfo please refer to the javadocs. Also note that all UIInfo arrays end with a call to UIInfo.end() This is a requirement imposed by the GNOME libraries.

Once the top level UIInfo array is constructed it is passed to the App via the createMenus() method. This is where GNOME works its' magic.

One of the menu items warrents further discussion. The help() method of UIInfo creates one or more menu items under a help menu that utilize the GNOME help facilities. The string that is passed as a parameter is the name of the help node that the system tries to load. The list of help topics available is read from a data file located in the /usr/share/gnome/help/"help_node"/C directory by default. The "help_node" portion of the directory is the string specified in the help() method. "C" is the default directory but if another language is specified it will be used. For example, if the users current locale settings specify the German language the directory would be /usr/share/gnome/help/"help_node"/de.

Our example application uses a help node of "second". Therefore, our data file should be located in the /usr/share/ome/help/second/C/ directory. The file should always be named " topic.dat" and could contain the following information:

Example 3. second.dat

index.html Help _Topics
overview.html _Overview
userguide.html _User Guide

This would add three menu items to our help menu. The help system will invoke the browser with the appropriate page when the user selects the menu item. The "html" files should be located in the same directory as the ".dat" file.