menu = (Menu)xv_create(NULL, MENU,
MENU_TITLE_ITEM, "Mail Folders",
MENU_NOTIFY_PROC, change_to_folder,
MENU_STRINGS, "/usr/spool/mail", "˜/mbox", NULL,
MENU_ITEM,
MENU_STRING, "˜/Mail"
MENU_GEN_PULLRIGHT, gen_folder_menu,
NULL,
NULL);
There is a shortcut attribute that allows you to specify both the menu item’s string and the
MENU_GEN_PULLRIGHT procedure in the same call. The attribute is MENU_GEN_
PULLRIGHT_ITEM
. It is used as follows:
MENU_GEN_PULLRIGHT_ITEM, "˜/Mail", gen_folder_menu,
The menu-generating routine may do whatever is necessary to build a new menu, but you
should be careful that the routine does not take too much processing time, since the user is
waiting with the MENU button pressed for the menu to be displayed. Also remember that a
pointer grab is going on, so the routine should avoid any interaction with the user (such as
error dialog boxes).
The form of the menu-generating procedure is:
Menu
menu_gen_proc(menu_item, op)
Menu_item menu_item;
Menu_generate op;
This routine may be called each time the menu is needed. If the menu only needs to be
created once, you can return to the same menu each time you need the menu.
The op parameter is one of the following enumerated types:
typedef enum {
MENU_DISPLAY,
MENU_DISPLAY_DONE,
MENU_NOTIFY,
MENU_NOTIFY_DONE
} Menu_generate;
op indicates the condition in which your routine has been called. The MENU_DISPLAY value
indicates that the menu is going to be displayed while MENU_DISPLAY_DONE indicates that
the menu has been displayed and dismissed. If the user makes a selection in the menu, the
routine is called with
MENU_NOTIFY before the menu’s callback routine is called, then again
with MENU_NOTIFY_DONE after the routine is called. If the user makes no selection, the latter
two cases are not called. If they are called (the user did make a selection), then the latter two
cases are called after MENU_DISPLAY_DONE is called.
Because you create your menus, you would think that you should destroy them as well.
However, because of the unpredictable sequence of actions taken by the user, there is no way
to determine when to free the menu. Therefore, you should never destroy menus at all. If the
menu-generating procedure is called multiple times for the same menu, just reconstruct the
menu from the same menu handle that you have.
288 XView Programming Manual
Furthermore, the menu-generating routine must always return the same menu that it passed to
you. You cannot return other menus to display. If the new menu is going to contain a com-
pletely different set of menu items, you should destroy all the menu items before creating the
new list. As it is the same with PANEL_LIST items, menu items are destroyed in reverse
order.
The special case for this problem is: what if there is no menu to redisplay again? In this case,
you are allowed to build a new menu and return a handle to it. The following code shows an
example, testing to see if there already is a menu associated with a particular pullright menu
item.
Menu
menu_gen_proc(menu_item, op)
Menu_item menu_item;
Menu_generate op;
{
int i;
Menu menu;
...
switch (op) {
...
case MENU_DISPLAY :
if (menu = (Menu)xv_get(menu_item, MENU_PULLRIGHT)) {
/* first destroy old menu items */
for (i = (int)xv_get(menu, MENU_NITEMS); i > 0; i--) {
xv_set(menu, MENU_REMOVE, i, NULL);
xv_destroy(xv_get(menu, MENU_NTH_ITEM, i));
}
else
menu = (Menu)xv_create(NULL, menu, NULL);
/* now rebuild the menu items */
...
}
...
}
In the above code fragment, we are removing the menu items sequentially in reverse order by
using the
MENU_REMOVE attribute. We start with the last item and move to item 1. The first
item, remember, is the title item, if it exists. If you want to retain this item, stop at menu item
2.
The sample program menu_dir2.c in Appendix F, Example Programs, demonstrates how a
menu-generating routine is used.
A debugging hint: If your menu-generating routine generates a run-time error, be careful
when trying to debug the program under a debugger. The problem is that when you run the
program in a debugger and the program generates a run-time error, the debugger will stop
execution and wait for input. In the meantime, the server has a pointer grab so keyboard
focus is directed to the menu’s window which is not able to receive input.
At this point, there is no way to interact with any program on the console—you will have to
go to another server, computer or terminal connected to your workstation and kill the
debugger (this will terminate the program and release the pointer grab). You may think of
more clever ways to handle this situation depending on your workstation configuration, but
Menus
Menus 289
the point is that you should be aware of the extremely inconvenient side effects whenever
you play with server grabs.
11.9.1 Parent Menus
Recall that the menu notification routines take two parameters: the menu that was popped up
and the menu item that was selected. However, if the user chose an item from a long cascade
of pullright menus, it may be necessary to determine the initial (root) menu of the cascade.
To support this, the attribute MENU_PARENT is used to get the owner of a menu or menu item.
This attribute can only be used with xv_get(). When MENU_PARENT is used with a menu
item, xv_get() returns the handle of the enclosing menu.
Menu menu;
menu = xv_get(item, MENU_PARENT);
On the other hand, if xv_get() is passed a menu, the menu item returned is the menu item
from which the submenu was pulled-right.
Menu_item item;
item = xv_get(menu, MENU_PARENT);
If the item returned is NULL, the menu is the root menu.
MENU_PARENT is only valid while the menu is active. Since menus can be shared, saying that
a menu’s parent is the one who uses it as a MENU_PULLRIGHT is not valid, since many menus
could have that one menu as a MENU_PULLRIGHT.
The following code fragment shows how the entire menu cascade is traversed, starting from
the leaf of the menu tree (the item the user selected).
Menu menu, item;
while (item = (Xv_opaque)xv_get(menu, MENU_PARENT))
if ((Xv_pkg *)xv_get(item, XV_TYPE) != MENUITEM)
break;
else
menu = xv_get(item, MENU_PARENT);
The above loop starts by getting the parent of an arbitrary menu. This menu could be the
menu parameter in a menu item’s callback routine. If the parent menu returned is
NULL, then
the menu is already the top level menu. Otherwise, get the type of the object returned. If the
menu is a pullright menu, then the parent of the menu should be a
MENUITEM (since its
pullright is a menu). If it is not, then it could be a server object. Whatever it is, we have
reached the top level of the menu cascade and should break out of the loop.
290 XView Programming Manual
11.9.2 Using MENU_GEN_PROC
MENU_GEN_PROC specifies a function that is used to modify, add, or delete menu items from
the menu whose handle is passed to the procedure. The op argument tells the state of the
menu when the function is called. The argument op is one of the values: MENU_DISPLAY,
MENU_DISPLAY_DONE, MENU_NOTIFY, or MENU_NOTIFY_DONE as defined by
Menu_generate in openmenu.h. You do not destroy the menu itself. If you do not know
what the item will show as text or as an image at the time the menu is created or if there is
other unknown information, you can defer the creation of the menu item until the item is
actually needed by specifying the item creation routine.
11.10 Using Toggle Menus
Toggle menus are menus with nonexclusive settings. The user can toggle menu items, turn-
ing them on or off. More than one menu item may be selected at a time. The only difference
for creating these menu items is that MENU_TOGGLE_MENU is used as the package parameter
to xv_create() and that menu items may not have pullright menus associated with them.
Therefore, these are typically simple menus.
In the code below, we build a toggle menu that has three items in it. If the menu has been
displayed and the user makes a selection, on or off, the notification routine is called no differ-
ently from any other menu notification procedure:
void toggle_bold(), toggle_size(), toggle_italic();
Menu menu;
menu = (Menu)xv_create(NULL, MENU_TOGGLE_MENU,
MENU_TITLE_ITEM, "Text Rendering",
MENU_ACTION_ITEM, "Bold Style", toggle_bold,
MENU_ACTION_ITEM, "Large Font", toggle_size,
MENU_ACTION_ITEM, "Italics", toggle_italic,
NULL);
In this case, we are specifying three different notify procedures for each menu item. Since
each performs a completely separate function, the menu items need not call the same routine.
To determine exactly which menu items are selected, you must loop through all the items in
the menu:
Menus
Menus 291
toggle_notify(menu, item)
Menu menu
Menu_item item;
{
int i;
for (i = (int)xv_get(font_menu, MENU_NITEMS); i > 0; i--)
if (xv_get(xv_get(font_menu, MENU_NTH_ITEM, i),
MENU_SELECTED)) {
printf("item %d selected\n", i);
/* do whatever other processing may need to be done */
}
}
This loop starts at the last item and works towards the first. The first item starts at 1, not 0;
the 0th item is the menu’s title item and cannot be retrieved.
11.11 Menu Layout
By default, pop-up menus place their items vertically. If there are too many items, a new col-
umn may be started in order to display the entire menu on the screen. You can specify the
number of rows and columns for the menu by using the attributes MENU_NROWS and
MENU_NCOLS.
Although specifying menu item layout is certainly legal and acceptable to
OPEN LOOK,
explicit menu item layout should be avoided for anything other than static menus. Dynamic
menus will have problems maintaining menu item order, and if you use a pin-up menu, the
command frame will almost certainly not match the appearance of the menu. To guarantee
that your pin-up menu looks the same as the menu, specify your own pin-up procedure. (See
the following section for more information.)
11.12 Making Pin-up Menus
As the programmer, you may give the user the option of pinning up a menu by providing the
pushpin in the pop-up menu. To accomplish this, XView provides the attribute
MENU_GEN_PIN_WINDOW. If specified, XView generates the pin window frame automatically
by creating a command frame, a panel and a series of panel items that correspond to the menu
items. These pin window components are actually created the first time the user pulls down
the menu and pushes the pin in. You cannot use xv_get to retrieve the command frame for
the menu until after the menu is pinned. This new frame is dynamic, so any changes to the
menu are reflected in the pinup frame provided it is not currently being displayed. If the
pinup frame is currently being displayed and the menu contents change, the pinned menu will
not reflect the new changes. The changes will appear the next time the menu is pinned.
Since menu items are translated into panel items in a pinned menu, programmers should not
allow more than 32 unique values for a pinned menu (32 is the size of an unsigned int on
most machines).
292 XView Programming Manual
Get Volume 7A: XView 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.