NULL. The tear−off button has a Separator−like appearance; you can specify its background, foreground, and top and
bottom shadow colors using the standard resources, as well as the XmNseparatorType resource. You can also set
these resources in a resource file using the name of the button, which is TearOffControl.
16.4 General Menu Creation Techniques
Now we have addressed each of the fundamental elements of the MenuBar and the resources used to provide the user
with the appropriate feedback. Using this information, we can generalize the way we build MenuBars, enabling us to
create arbitrarily large MenuBars and PulldownMenus using a substantially smaller amount of code.
In the examples that follow, we use many of the recommended elements for a standard Motif MenuBar. You can
adjust the algorithms and data structures to fit the needs of your own application. Although we use hard−coded values
for widget resources, this technique is by no means a requirement, nor should it be construed as recommended usage.
If you choose to specify resources in a resource file, you should write an application defaults file that contains the
appropriate resource values.
16.4.1 Building Pulldown Menus
Let's begin by identifying each of the attributes of a menu item:
Label
Mnemonic
Accelerator
Accelerator text
Callback routine
Callback data
Using this information, we can construct a data structure that describes all of the important aspects of a menu item.
We define the MenuItem structure as follows:
typedef struct _menu_item {
char *label; /* the label for the item */
WidgetClass *class; /* pushbutton, label, separator, ... */
char mnemonic; /* mnemonic; NULL if none */
char *accelerator; /* accelerator; NULL if none */
char *accel_text; /* to be converted to compound string */
void (*callback)(); /* routine to call; NULL if none */
XtPointer callback_data; /* client_data for callback() */
} MenuItem;
To create a PulldownMenu, all we need to do is initialize an array of MenuItem structures and pass it to a routine
that iterates through the array and creates the items using the appropriate information. For example, the following
declaration describes the elements for a File menu:
MenuItem file_items[] = {
{ "New", &xmPushButtonGadgetClass, 'N',
NULL, NULL, do_open, NEW },
{ "Open...", &xmPushButtonGadgetClass, 'O',
NULL, NULL, do_open, OPEN },
{ "Save", &xmPushButtonGadgetClass, 'S',
NULL, NULL, do_save, SAVE },
{ "Save As...", &xmPushButtonGadgetClass, 'A',
NULL, NULL, do_save, SAVE_AS },
16 Menus 16.4 General Menu Creation Techniques
452
{ "Print...", &xmPushButtonGadgetClass, 'P',
NULL, NULL, do_print, NULL },
{ "", &xmSeparatorGadgetClass, NULL,
NULL, NULL, NULL, NULL },
{ "Exit", &xmPushButtonGadgetClass, 'x',
"Ctrl<Key>C", "Ctrl+C", do_quit, NULL },
NULL,
};
Each element in the MenuItem data structure is filled with default values for each menu item. If a resource value is
not meaningful, or is not going to be hard−coded, we initialize the field to NULL. If you don't need a callback function
or client data for an item, the field may be set to NULL. The only field that cannot be NULL is the widget class. The
final terminating NULL in the label field indicates the end of the list.
We have not specified any accelerators except for the Exit item. The Separator gadget is completely unspecified, since
none of the resources even apply to Separators. This design makes modification and maintenance very simple. If you
want to add an accelerator for the Save item, all you need to do is change the appropriate fields in the data structure,
instead of having to search through the source code looking for where that item is created.
One particular point of interest is the way the WidgetClass field is initialized. It is declared as a pointer to a widget
class rather than just a widget class, so we initialize the field with the address of the widget class variable that is
declared in the widget's header file. The use of &xmPushButtonGadgetClass is one such example. The structure
must be initialized this way because the compiler requires a specific value in order to initialize a static data structure.
The xmPushButtonWidgetClass pointer does not have a value until the program is actually running, but the
address of the variable does have a value. Once the program is running, the pointer can be dereferenced to access the
real PushButton widget class.
Now we can write a routine that uses the MenuItem data structure to create a PulldownMenu. The
BuildPulldownMenu() function is shown in the source code The routine loops through each element in an array
of pre−initialized MenuItem structures and creates menu items based on the information.
XmStringCreateLocalized() is only available in Motif 1.2; XmStringCreateSimple() is the
corresponding function in Motif 1.1. The XmNtearOffModel resource is only available in Motif 1.2; it should not
be specified in Motif 1.1.
Widget
BuildPulldownMenu(parent, menu_title, menu_mnemonic, tear_off, items)
Widget parent;
char *menu_title, menu_mnemonic;
Boolean tear_off;
MenuItem *items;
{
Widget PullDown, cascade, widget;
int i;
XmString str;
PullDown = XmCreatePulldownMenu (parent, "_pulldown", NULL, 0);
if (tear_off)
XtVaSetValues (PullDown, XmNtearOffModel, XmTEAR_OFF_ENABLED, NULL);
str = XmStringCreateLocalized (menu_title);
cascade = XtVaCreateManagedWidget (menu_title,
xmCascadeButtonWidgetClass, parent,
XmNsubMenuId, PullDown,
XmNlabelString, str,
XmNmnemonic, menu_mnemonic,
NULL);
XmStringFree (str);
16 Menus 16.4 General Menu Creation Techniques
453
/* Now add the menu items */
for (i = 0; items[i].label != NULL; i++) {
widget = XtVaCreateManagedWidget (items[i].label,
*items[i].class, PullDown,
NULL);
if (items[i].mnemonic)
XtVaSetValues (widget, XmNmnemonic, items[i].mnemonic, NULL);
if (items[i].accelerator) {
str = XmStringCreateLocalized (items[i].accel_text);
XtVaSetValues (widget,
XmNaccelerator, items[i].accelerator,
XmNacceleratorText, str,
NULL);
XmStringFree (str);
}
if (items[i].callback)
XtAddCallback (widget, XmNactivateCallback,
items[i].callback, items[i].callback_data);
}
return cascade;
}
The function takes five parameters. parent is a handle to a MenuBar widget that must have already been created,
menu_title indicates the title of the menu, menu_mnemonic specifies the mnemonic, tear_off indicates
whether or not the menu can be torn off, and items is an array of MenuItem structures.
The first thing the routine does is create a PulldownMenu. Since the name of this widget is not terribly important, we
use a predefined name, prefixed with an underscore, to indicate that the name is not intended to be referenced in a
resource file. This use of the underscore is our own convention, by the way, not one adopted by the X Toolkit
Intrinsics. We came up with this "unwritten rule" because Xt has no such naming conventions for widgets that do not
wish to have their resources specified externally.
After creating the PulldownMenu, the routine creates the CascadeButton that acts as the title for the menu on the
MenuBar. The name of the widget is taken from the second parameter, menu_title. The routine also sets the
mnemonic and the XmNtearOffModel resource at this point. All MenuBar titles should have mnemonics associated
with them.
Now the function loops through the array of MenuItem structures creating menu items until it finds an entry with a
NULL label name. We use this value as an end−of−menu indicator in our initialization. When each widget is created,
the mnemonic, accelerator, and callback function are added only if they are specified in the MenuItem structure.
BuildPulldownMenu() must be called from another function that passes the appropriate data structures and other
parameters. In our design, this would be the routine that creates the MenuBar itself. the source code shows the code
for the CreateMenuBar() routine. This simple function creates a MenuBar widget, calls
BuildPulldownMenu() for each menu, manages the MenuBar, and returns it to the calling function.
Widget
CreateMenuBar(MainWindow)
Widget MainWindow;
{
Widget MenuBar, widget, BuildPulldownMenu();
MenuBar = XmCreateMenuBar (MainWindow, "MenuBar", NULL, 0);
(void) BuildPulldownMenu (MenuBar, "File", 'F', True, file_items);
16 Menus 16.4 General Menu Creation Techniques
454

Get Volume 6A: Motif Programming Manual now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.