A menu is a screen display that presents a set of items from which the user selects one or more, depending on the type of menu. Once the user makes a selection, your application program responds accordingly. This response may be to generate a message, display another menu, or take some other action. The following screen displays a sample menu.
A Sample Menu
Black Charcoal Light Gray Brown Camel Navy Light Blue Hunter Green Gold Burgundy Rust White
To use the menu routines, you specify
#include <menu.h>
in your C program files and compile and link with the command line
cc [ flags ] files -lmenu -lcurses [libraries ]
If you use the panel routines as well, specify-lpanel before -lcurses on the command line.
This section introduces basic ETI menu terminology, lists the steps in a typical menu application program, and reviews the code in a simple example.
The following terms will be helpful:
In general, a menu application program will
The following screen shows the ETI code necessary for generating the menu of colors.
Sample Menu Program to Create a Menu in ETI
#include <menu.h> char * colors[13] = { "Black", "Charcoal", "Light Gray", "Brown", "Camel", "Navy", "Light Blue", "Hunter Green", "Gold", "Burgundy", "Rust", "White", (char *) 0 }; ITEM * items[13]; main () { MENU * m; ITEM ** i = items; char * c = colors; low-level ETI (curses) initialization *.sp .45 initscr (); nonl (); raw (); noecho (); wclear (stdscr); create items *.sp .45 while (*c) *i++ = new_item (*c++, ""); *i = (ITEM *) 0; create and display menu *.sp .45 m = new_menu (i = items); post_menu (m); refresh; sleep (5); erase menu and free both menu and items *.sp .45 unpost_menu (m); refresh; free_menu (m); while (*i) free_item (*i++); low-level ETI (curses) termination *endwin (); exit (0); }
To get an overview of ETI menu routines, we will now briefly walk through this menu program. In later sections, we discuss these and remaining ETI routines in detail.
Every menu program should have the line
#include <menu.h>
to instruct the C preprocessor to make the file of ETI menu declarations available. The initial low-level ETI routines establish the best terminal characteristics for working with the ETI menu routines.
The while loop creates each item for the menu using the ETI function new_item(). This function takes as its name argument a color from array colors[]. The optional description argument is here the null string. The new item pointers are assigned to a NULL-terminated array.
Next, the menu is created and connected to the item pointer array using function new_menu(). The menu is then posted to stdscr and the screen is refreshed to display the menu.The sleep() command makes the menu visible for five seconds.
To erase the menu, you unpost it and refresh the screen. Function free_menu disconnects the menu from its item pointer array and deallocates the space for the menu. The last while loop uses function free_item() to free the space allocated for each item.
Finally, functions endwin() and exit() terminate low-level ETI and the program itself.
The following sections explain how to use all ETI menu routines. Program fragments illustrating the menu routines occur throughout this chapter. Many of these fragments are portions of a larger program example. The current example and others are included in the set of high-level ETI demonstration programs delivered with the ETI product. Low-level ETI demonstration programs are reproduced in the last section of this guide.
NOTE: Like all form routines that return an int value, all menu routines that do so return the value E_OK when they execute successfully.
Normally, to create a menu, you must first create the items comprising it. To create a menu item, you use function new_item().
SYNOPSIS ITEM * new_item (name, description) char * name; char *description;
Function new_item() creates a new item by allocating space for the new item and initializing it. ETI displays the string name when the menu is later posted, but calling new_item() does not alone connect the item to a menu. The item name is also used in pattern-matching operations. If name is NULL or the null string, then new_item() returns NULL to indicate an error.
The argument description is a descriptive string associated with the item. It may or may not be displayed depending on the O_SHOWDESC option, which you can turn on or off with the set_menu_opts() and related functions described below. If description is NULL or the null string, no description is associated with the menu item.
If successful, new_item() returns a pointer to the new item. This pointer is the key to working with all item routines. When you pass it to them, it enables the menu subsystem to change, record, and examine the item's attributes.
If there is insufficient memory for the item, or name is NULL or the null string, then new_item() returns NULL.
In general, you use an array to store the item pointers returned by new_item(). The following screen shows how you might create an item array of the planets of our solar system.
Creating an Array of Items
ITEM * planets[10]; planets[0] = new_item ("Mercury", "The first planet"); planets[1] = new_item ("Venus", "The second planet"); planets[2] = new_item ("Earth", "The third planet"); planets[3] = new_item ("Mars", "The forth planet"); planets[4] = new_item ("Jupiter", "The fifth planet"); planets[5] = new_item ("Saturn", "The sixth planet"); planets[6] = new_item ("Uranus", "The seventh planet"); planets[7] = new_item ("Neptune", "The eighth planet"); planets[8] = new_item ("Pluto", "The ninth planet"); planets[9] = (ITEM *) 0;
Function new_item() does not copy the name or description strings themselves, but saves the pointers to them. So once you call new_item(), you should not change the strings until you call free_item().
SYNOPSIS free_item(item); ITEM * item;
Function free_item() frees an item. It does not, however, deallocate the space for the item's name or description.
The argument to free_item() is a pointer previously obtained from new_item().
NOTE: To free an item, you must have already created it with new_item() and it must not be connected to a menu. If these conditions are not met, free_item() returns one of the error values listed below.
Once an item is freed, you must not use it again. If a freed item's pointer is passed to an ETI routine, undefined results will occur.
If successful, free_item() returns E_OK. If it encounters an error, it returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - null item E_CONNECTED - item is connected to a menu
Menus are of two kinds:
By default, every menu is single-valued. To create a multi-valued menu, you turn off menu option O_ONEVALUE using function set_menu_opts() or menu_opts_off(). These functions are treated in the section below, "Setting and Fetching Menu Options.''
Menus of both types always have a current item. With single-valued menus, you determine the item selected by noting the current item. With multi-valued menus, you determine all items selected by applying function item_value() to each menu item and noting the value returned. Most menu functions pertain to menus whether they are single- or multi-valued. Function set_item_value(), however, may be used only with multi-valued menus.
Select values of an item are either TRUE (selected) or FALSE (not selected). Function set_item_value() sets the select value of an item, while item_value() returns it.
SYNOPSIS int set_item_value (item, value) ITEM * item; int value; int item_value (item) ITEM * item;
Function set_item_value() fails if given an item that is not selectable (the O_SELECTABLE option was previously turned off) or the item is connected to a single-valued menu (connecting items to menus is described in the section below, "Creating and Freeing Menu Items"). If successful, set_item_value() returns E_OK. Otherwise, one of the following is returned.
E_SYSTEM_ERROR - system error E_REQUEST_DENIED - item not selectable or single value menu
If the argument to item_value() is an item pointer connected to a single-valued menu, item_value() returns FALSE.
You might want to place the code in the following screen after your user responds to a menu. Function process_menu() determines which items have been selected, processes them appropriately, and marks them as unselected to prepare for further user response.
Using item_value() in Menu Processing
void process_menu (m) /* process multi-valued menu */ MENU * m; { ITEM ** i = menu_items (m); while (*i) { { if (item_value (*i)) { { take action appropriate for selection of this item * set_item_value (*i, FALSE); } ++i; } }
An attribute is any feature whose value can be set or read by an appropriate ETI function. An item attribute is any item feature whose value can be set or read by an appropriate ETI function. Item names, descriptions, options, and visibility are examples of item attributes.
The routines item_name() and item_description() take an item pointer as their argument. Function item_name() returns the item's name, while function item_description() returns its description.
SYNOPSIS char * item_name (item) ITEM * item; char * item_description (item) ITEM * item;
Both functions return NULL if given a NULL item pointer.
An option is an attribute whose value may be either on or off. The current release of ETI provides the item option O_SELECTABLE. (In the future, ETI may provide additional options.) Setting the O_SELECTABLE option lets your user select the item. By default, O_SELECTABLE is set for every item. Function set_item_opts() lets you turn on or turn off this and any future options for an item, while item_opts() lets you examine the option(s) set for a given item.
SYNOPSIS int set_item_opts (item, opts) ITEM * item; OPTIONS opts; OPTIONS item_opts (item) ITEM * item; options: O_SELECTABLE
In addition to turning on the named item options, function set_item_opts() turns off any other item options.
If successful, set_item_opts() returns E_OK. Otherwise, it returns the following:
E_SYSTEM_ERROR - system error
If function set_item_opt()s is passed a NULL item pointer, like other functions it sets the new current default. If function item_opts() is passed a NULL pointer, it returns the current default.
If you turn off option O_SELECTABLE, the item cannot be selected. You might want to make an item unselectable to emphasize certain things your application program is doing. Unselectable items are displayed using the grey display attribute, described below in the section "Fetching and Changing a Menu's Display Attributes."
Because options are Boolean values (they are either on or off), you use C Boolean operators with item_opts() to turn them on and off. Consequently, to turn off option O_SELECTABLE for item i0 and turn on the same option for item i1, you can write:
ITEM * i0, * i1; set_item_opts (i0, item_opts (i0) & ~O_SELECTABLE); /* turn option off */ set_item_opts (i1, item_opts (i1) | O_SELECTABLE); /* turn option on */
ETI also enables you to turn on and off specific item options without affecting others, if any. The following functions change only the options specified.
SYNOPSIS int item_opts_on (item, opts) ITEM * item; OPTIONS opts; int item_opts_off (item, opts) ITEM * item; OPTIONS opts;
These functions return the same error conditions as set_item_opts().
As an example, the following code turns option O_SELECTABLE off for item i0 and on for item i1.
ITEM * i0, * i1; item_opts_off (i0, O_SELECTABLE); /*turn option off */ item_opts_on (i1, O_SELECTABLE); /* turn option on */
To change the current default to not O_SELECTABLE, you can write either
/* set current defaults for all new items */ set_item_opts ((ITEM *) 0, item_opts( (ITEM *) 0) & ~O_SELECTABLE);
or
item_opts_off ((ITEM *) 0, O_SELECTABLE); /* turn default option off */
A menu item is visible if it appears in the subwindow of the posted menu to which it is connected. (Connecting and posting menus is described below.) Function item_visible() enables your application program to determine if an item is visible.
SYNOPSIS int item_visible (item) ITEM * item;
If the item is connected to a posted menu and it appears in the menu subwindow, item_visible() returns TRUE. Otherwise, it returns FALSE.
To check if the first menu item is currently visible on the display, you can write
int at_top (m) /* check visibility of first menu item */ MENU * m; { ITEM ** i = menu_items (m); ITEM * firstitem = i[0]; return item_visible (firstitem); }
For another example, see the section below, "Counting the Number of Menu Items".
ETI establishes initial current default values for item attributes. During item initialization, each item attribute is assigned the current default value of the attribute. You can change or retrieve the current default attribute values by calling the appropriate function with a NULL item pointer. After the current default value changes, all subsequent items created with new_item() will have the new default value.
NOTE: Items created before changing the current default value retain their previously assigned values.
The following sections offer many examples of how to change item attributes.
For each item created, ETI automatically allocates a special user pointer that enables you to associate arbitrary data with the item. By default, the user pointer's value is NULL. You may set its value to whatever you want or not use it at all.
SYNOPSIS int set_item_userptr (item, userptr) ITEM * item; char *userptr; char * item_userptr (item) ITEM * item;
These two functions are helpful for creating item data such as title strings, help messages, and the like.
Any defined structure can be connected to an item using the item's user pointer. The pointer must be cast to (char *) and then later recast back to (defined-struct *). The following screen shows how to use an item's user pointer with a struct ITEM_ID, which stores biological information.
Using an Item User Pointer
typedef struct { int id; char * name; char * type; } ITEM_ID; ITEM_ID ids[7] = { 1, "apple", "fruit", 2, "ant", "insect", 3, "cow", "mammal", 4, "lizard", "reptile", 5, "potato", "vegetable", 6, "zebra", "mammal", 0, "", "", }; ITEM * items[7]; for (i = 0; ids[i]; ++i) { items[i] = new_item (ids[i].name, ""); /* create item from each ids.name */ /* set user pointer to point to start of each struct in ids[] */ set_item_userptr (items[i], (char *) &ids[i]); } items[i] = (ITEM *) 0;
Note that the pointer to each entry in array ids is cast to char *, which set_userptr() requires. You might then write a function that uses function item_userptr() to return the information. The following function returns the item type:
char * get_type (i) ITEM * i; { ITEM_ID * id = (ITEM_ID *) item_userptr (i); return id -> type; }
Here the value returned by item_userptr() is recast to ITEM_ID * so the item's type may be found.
Finally, you might call get_type() to write the type, thus:
WINDOW * win; waddstr (win,get_type(i));
If successful, set_item_userptr() returns E_OK. Otherwise, it returns the following:
E_SYSTEM_ERROR - system error
If function set_item_userptr() is passed a NULL item pointer, the argument userptr becomes the new default user pointer for all subsequently created items. As an example, the following sets the new default user pointer to point to the string "You are Here":
set_item_userptr( (ITEM *) 0, "You are Here");
Once you create the items for your menu, you can create the menu itself. To create and initialize a menu, you use function new_menu().
SYNOPSIS MENU * new_menu (items) ITEM ** items;
The argument to new_menu() is a NULL-terminated, ordered array of ITEM pointers. These pointers define the items on the menu. Their order determines the order in which the items are visited during menu driver processing, described below.
Function new_menu() does not copy the array of item pointers. Instead, it saves the pointer to the array for future use.
NOTE: Once your application program has called new_menu(), it should not change the array of item pointers until the menu is freed by free_menu() or the item array is replaced by set_menu_items, described below.
Items passed to new_menu() are connected to the menu created. They cannot be simultaneously connected to another menu. To disconnect the items from a menu, you can use function free_menu() or function set_menu_items(), which changes the items connected to a menu from one set to another. See the section "Fetching and Changing Menu Items."
If successful, new_menu() returns a pointer to the new menu. The following error conditions hold:
In addition, if new_menu()'s argument items is NULL, as in
MENU * m; m = new_menu ((MENU *) 0);
it creates the menu with no items connected to it and assigns the menu pointer to m.
The menu pointer returned by new_menu() is the key to working with all menu routines. You pass it to the appropriate menu routine to do such tasks as post menus, call the menu driver, set the current item, and record or examine menu attributes.
Turn to section "A Sample Menu Program" for an example showing how to create a menu. In general, you want to use a while loop as illustrated to create the menu items and assign the item pointers to the item pointer array. Note the NULL terminator assigned to the item pointer array before the menu is created with new_menu().
When you no longer need a menu, you should free the space allocated for it. To do this, you use function free_menu().
SYNOPSIS int free_menu (menu) MENU * menu;
Function free_menu() takes as its argument a menu pointer previously obtained from new_menu(). It disconnects all items from the menu and frees the space allocated for the menu. The items associated with the menu are not freed, however, because you may want to connect them to another menu. If not, you can free them by calling free_item().
Remember that once a menu is freed, you must not pass its menu pointer to another routine. If you do, undefined results occur.
If successful, calls to free_menu() return E_OK. If free_menu() encounters an error, it returns one of the following:
E_BAD_ARGUMENT - NULL menu pointer E_POSTED - menu is posted E_SYSTEM_ERROR - system error
For E_POSTED, see the section,"Posting and Unposting Menus"
Recall that an attribute is any feature whose value can be set or read by an appropriate ETI function. A menu attribute is any menu feature whose value can be set or read by an appropriate ETI function. The set of items connected to a menu and the number of items in the menu are examples of menu attributes.
During processing, you may sometimes want to change the set of items connected to a menu. Function set_menu_items() enables you to do this.
SYNOPSIS int set_menu_items (menu, items) MENU * menu; ITEM ** items; ITEM ** menu_items (menu) MENU * menu;
Like the argument to new_menu(), the second argument to set_menu_items() is a NULL-terminated, ordered array of ITEM pointers that defines the items on the menu. Like new_menu(), function set_menu_items does not copy the array of item pointers. Instead, it saves the pointer to the array for future use.
The items previously connected to the given menu when set_menu_items() is called are disconnected from the menu(but not freed) before the new items are connected. The new items cannot be given to other menus unless first disconnected by free_menu() or another set_menu_items() call.
If items is NULL, the items associated with the given menu are disconnected from it, but no new items are connected.
If function set_menu_items() is successful, it returns E_OK. If it encounters an error, it returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - NULL menu pointer or NULL associated item array E_POSTED - menu is posted E_CONNECTED - connected item
Function menu_items() returns the array of item pointers associated with its menu argument. In the next section, the application-defined function at_bottom() illustrates its use.
If no items are connected to the menu or the menu pointer argument is NULL, menu_items() returns NULL.
As an example of set_menu_items(), consider the following screen whose code changes the items associated with a previously created menu.
Changing the Items Associated with a Menu
MENU *m; ITEMS ** olditems, ** newitems; /* create items */ m = new_menu(olditems); /* create menu m */ /* process menu with olditems */ set_menu_items (m,newitems); /* change items associated with menu m */
Occasionally, you may want to do different processing depending on the number of items connected to your current menu. Function item_count() returns the number of items connected to a menu.
SYNOPSIS int item_count (menu) MENU * menu;
If menu is NULL, function item_count() returns -1.
As an example of the use of this function, consider the following routine. Because the index to the last menu item is one less than the number of items, this routine determines whether the last item is displayed.
int at_bottom (m) /* check visibility of last menu item */ MENU * m; { ITEM ** i = menu_items (m); ITEM * lastitem = i[item_count(m)-1]; return item_visible (lastitem); }
As it does with the attributes of other objects, ETI establishes initial current default values for menu attributes. During menu creation, each menu attribute is assigned the current default value of the attribute. You can change or retrieve the current default attribute values by calling the appropriate function with a NULL menu pointer. After the current default value changes, all subsequent menus created with new_menu() will have the new default value.
NOTE: Menus created before changing the current default value retain their previously assigned values.
The following sections offer many examples of how to change menu attributes.
In general, to display a menu, you determine the menu's dimensions, optionally associate a window and subwindow with the menu, optionally set the menu's display attributes, post the menu, and refresh the screen.
The simplest way to display a menu is to use stdscr as your default window and subwindow. Any titles, borders, or other decorative matter are displayed in the menu window; the menu proper is displayed in the menu subwindow. If you want to specify a menu window or subwindow, you use the functions set_menu_win() or set_menu_sub(). (These routines are treated below in the section, "Associating Windows and Subwindows with Menus") Whether or not you choose a menu window, ETI calculates the minimum window (or subwindow) size for your menu.
To determine the minimum window size for a menu, ETI considers five factors:
ETI knows the size and number of items in a menu as soon as you call new_menu(), discussed above. By default, options O_ROWMAJOR and O_SHOWDESC are on. Option O_ROW_MAJOR ensures that the items are displayed in row major order - fanning out left to right, then top to bottom. How to change this and other menu options is discussed below in the section "Fetching and Changing Menu Items" Option O_SHOWDESC ensures that an item's description, if any, is displayed with the item's name.
This section first describes the menu's format and mark string. It then describes the routine scale_menu(), which uses the above information to set the window size for the menu.
NOTE: The five factors that determine the minimum window size have default values. You need not worry about them until you want to customize your menus.
In general, the items comprising a menu do not fill a single screen. Sometimes they occupy considerably less space, sometimes considerably more. The following functions enable you to set the maximum number of rows and columns of menu items to be displayed at any one time.
SYNOPSIS int set_menu_format (menu, maxrows, maxcols) MENU * menu; int maxrows, maxcols; void menu_format (menu, maxrows, maxcols) MENU * menu; int * maxrows, * maxcols;
A menu page is the collection of currently visible items. Function set_menu_format() establishes the maximum number of rows and columns of items that may be displayed on a menu page.
The actual number of rows and columns displayed may be less than maxrows or maxcols depending on the number of items and whether the O_ROWMAJOR option is on. (Menu options are described below in the section "Setting and Fetching Menu Options") Function menu_format() returns the maximum number of rows and columns of items that you set for the given menu.
The default number of item rows is 16, while the default number of item columns is one. If either maxrows or maxcols equals zero in the call to set_menu_format(), the current value is not changed. An error occurs, however, if the value of either of these arguments is less than zero.
ETI calculates the total number of rows and columns in a row major menu as follows:
#define minimum(a,b) ((a) < (b) ? (a) : (b)) total_rows = (number_of_items - 1) / maxcols + 1; total_cols = minimum (number_of_items, maxcols);
ETI calculates the total number of rows and columns in a column major menu as follows:
total_rows = (number_of_items - 1) /maxcols + 1; total_cols = (number_of_items - 1) /total_rows + 1;
Whether or not the O_ROW_MAJOR option is on, the number of rows and columns of items that are displayed at one time on a menu page is
displayed_rows = minimum (total_rows, maxrows); displayed_cols = minimum (total_cols, maxcols);
If total_rows is greater than maxrows, the menu is scrollable - your end-user can scroll up or down through the menu by making the appropriate menu driver request. See the section below, "ETI Menu Requests"
As an example, consider the displays in the following figures. They portray menus consisting of five items. The numbers 0 through 4 signify menu items in the order in which they live in the item pointer array. The first figure shows the menu displayed with a format of maximum number of rows two, maximum number of columns two. To stipulate this format for menu m, you write
set_menu_format(m,2,2);
Using the formulas above, we see that total_rows is 3 and total_cols is 2 in all four cases displayed in the two figures. The first display in each figure shows the menu in row major format (O_ROW_MAJOR on), the second in column major format. The displayed number of rows and columns in the first figure is 2. To see the last row of items, your user can make the REQ_SCR_DLINE request to scroll down. If, instead, you set the format of this menu to three rows, two columns, you get one of the two displays in the second figure. The enclosing block in each case indicates the items displayed at one time.
For a larger example, consider the following figure. Here the number of items is 18 and the format in both cases is four rows, three columns. In both cases, the total number of rows comes to six, the total number of columns to three, and the displayed number of rows to four. Calculation shows that changing the number of items in this example to 19 changes the number of rows to seven.
The column major examples emphasize that when the total number of rows is greater than the maximum number of rows, the items displayed do not exactly follow the order of the items in the array of item pointers. The items are arranged in column-major format throughout the entire menu, not within each displayed page. This conception agrees with your user's ability to scroll through the menu.
If successful, function set_menu_format() returns E_OK. If an error occurs, it returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - rows < 0 or cols < 0 E_POSTED - menu is posted
If function set_menu_format() is passed a NULL menu pointer, it sets a new system default. Suppose, for instance, that you want to change the default maximum number of rows of items displayed to ten, and the default maximum number of columns displayed to three. You can write
set_menu_format((MENU *)0,10,3);
The function set_menu_format() resets the value of top_row() to 0. See the section below, "Fetching and Changing the Top Row,'' for details.
Finally, if function menu_format() receives a NULL menu pointer, it returns the current default format.
The mark string distinguishes
The mark string appears just to the left of the item name.
SYNOPSIS int set_menu_mark (menu, mark) MENU * menu; char * mark; char * menu_mark (menu) MENU * menu;
Function set_menu_mark() sets the mark string, while menu_mark() returns the string. The initial default markstring is a minus sign (-). The mark string may be as long as you want, provided each item fits on one line of the menu's subwindow.
NOTE: Do not change the mark string area as long as you want that mark because ETI does not copy it.
If mark is NULL, no mark string appears.
You can call set_menu_mark() either before or after the menu is posted. (See the section below, "Posting and Unposting Menus") However, there is a restriction to calling it afterwards.
NOTE: If you call set_menu_mark() with a posted menu, the length of the mark string must stay the same.
If the menu is posted and the length of the mark string changes, the function returns E_BAD_ARGUMENT and leaves the mark unchanged.
To change the mark string for menu m to ---> you can write
MENU * m; set_menu_mark (m, "--->"); /* change mark string for menu m */
If successful, function set_menu_mark() returns E_OK. If an error occurs, function set_menu_mark() returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - menu is posted: change in string length impossible
Note that you can change the current default mark string for all subsequently created menus in your program by passing set_menu_mark() a NULL menu pointer. To change the current default mark string to ---> you write
set_menu_mark ((MENU *) 0, "--->"); /* change default mark string */
All subsequently created menus will have ---> as their mark string. To return the current default mark string, you call menu_mark() with NULL:
char * mark = menu_mark ((MENU *) 0); /* default mark string */
Remember that the size of menu items, the O_ROWMAJOR menu option, the menu format, and the menu mark determine the smallest window size for a menu. Function scale_menu() returns this smallest window size in terms of the number of character rows and columns.
SYNOPSIS int scale_menu (menu, rows, cols) MENU * menu; int * rows, *cols;
Because function scale_menu() must return more than one value (namely, the minimum number of rows and columns for the menu) and C passes parameters "by value'' only, the arguments of scale_menu() are pointers. The pointer arguments rows and cols point to locations used to return the minimum number of rows and columns for displaying the menu.
NOTE: You should call scale_menu() only after the menu's items have been connected to the menu using new_menu() or set_menu_items().
The following code places the minimal number of rows and columns necessary for menu m in rows and cols:
MENU *m; int rows, cols; scale_menu (m, &rows, &cols); /* return dimensions of menu m */
You use the values returned from scale_menu() to create menu windows and subwindows. In the next section, we will see how to do this.
If successful, scale_menu() returns E_OK. If an error occurs, the function returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - NULL menu pointer E_NOT_CONNECTED - no connected items
Two windows are associated with each menu - the menu window and the menu subwindow. The following functions assign windows and subwindows to menus and fetch those previously assigned to them.
SYNOPSIS int set_menu_win (menu, window) MENU * menu; WINDOW * window; WINDOW * menu_win (menu) MENU * menu; int set_menu_sub (menu, window) MENU * menu; WINDOW * window; WINDOW * menu_sub (menu) MENU * menu;
To place a border around your menu or give it a title, you call set_menu_win() and write to the associated window.
NOTE: By default, (1) the menu window is NULL, which by convention means that ETI uses stdscr as the menu window; and (2) the menu subwindow is NULL, which means that ETI uses the menu window as the menu subwindow.
If you do not want to use the system defaults, you may create a window and a subwindow for every menu. ETI automatically writes all output of the menu proper on the menu's subwindow. You may write additional output (such as borders, titles, and the like) on the menu's window. The relationship between ETI menu routines, your application program, a menu window, and a menu subwindow is illustrated in the following figure.
NOTE: You should apply all output and refresh operations to the menu window, not its subwindow.
The following screen shows how you can create and display a menu with a border of the default characters, ACS_VLINE and ACS_HLINE. (See the box() command in the curses(3X) manual pages.)
Creating a Menu with a Border
MENU * m; WINDOW * w; int rows, cols; scale_menu (m, &rows, &cols); /* get dimensions of menu */ /* create window 2 characters larger than menu dimensions with top left corner at (0, 0). subwindow is positioned at (1, 1) relative to menu window origin with dimensions equal to the menu dimensions. */ if (w = newwin (rows+2, cols+2, 0, 0)) { set_menu_win (m, w); set_menu_sub (m, derwin (w, rows, cols, 1, 1)); box (w, 0, 0); /* draw border in w */ }
Variables rows and cols provide the menu dimensions without the border. The dimensions of the menu subwindow are set to these values. In general, if you want a simple border, you should set the number of rows and columns in the menu's window to be two more than the numbers in its subwindow, as in the example.
Remember that the initial default menu window and subwindow are NULL. (By convention, this means that stdscr is used as the menu window and the menu window is used as the menu subwindow.) If you want to change the current default menu window or subwindow, you can pass functions set_menu_win() and set_menu_sub() a NULL menu pointer. Thus, the code
WINDOW * dftwin; set_menu_win ((MENU *) 0, dftwin); /* sets default menu window to dftwin */
changes the current default window to dftwin.
If successful, functions set_menu_win() and set_menu_sub return E_OK. If not, they return one of the following:
E_SYSTEM_ERROR - system error E_POSTED - menu is posted
Menu display attributes are visible menu characteristics that distinguish classes of menu items from each other. Low-level ETI (curses) video attributes are used to differentiate the menu display attributes. These menu display attributes include
The following functions enable you to set and read these attributes.
SYNOPSIS int set_menu_fore (menu, attr) MENU * menu; chtype attr; chtype menu_fore (menu) MENU * menu; int set_menu_back (menu, attr) MENU * menu; chtype attr; chtype menu_back (menu) MENU * menu; int set_menu_grey (menu, attr) MENU * menu; chtype attr; chtype menu_grey (menu) MENU * menu; int set_menu_pad (menu, pad) MENU * menu; int pad; int menu_pad (menu) MENU * menu;
In general, to establish uniformity throughout your program, you should set the menu display attributes with these functions at the start of the program.
Function set_menu_fore() sets the curses foreground attribute. The default is A_STANDOUT.
Function set_menu_back() sets the curses background attribute. The default is A_NORMAL.
Function set_menu_grey() sets the curses attribute used to display nonselectable items in multi-valued menus. The default is A_UNDERLINE.
To set the foreground attribute of menu m to A_BOLD and its background attribute to A_DIM,you write
MENU *m; set_menu_fore(m,A_BOLD); set_menu_back(m,A_DIM);
All these functions can change or fetch the current default if passed a NULL menu pointer. As an example, to set the default grey attribute to A_NORMAL, you write
set_menu_grey((MENU *)0, A_NORMAL);
If functions set_menu_fore(), set_menu_back(), and set_menu_grey() encounter an error, they return one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - bad curses attribute
Function set_menu_pad() sets the pad character for a menu. The initial default pad character is a blank. The pad character must be a printable ASCII character.
To change the pad character for menu m to a dot (.), you write
MENU * m; set_menu_pad(m,'.');
If function set_menu_pad() encounters an error, it returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - non-printable pad character
To post a menu is to write it on the menu's subwindow. To unpost a menu is to erase it from the menu's subwindow, but not destroy its internal data structure. ETI provides two routines for these actions.
SYNOPSIS int post_menu (menu) MENU * menu; int unpost_menu (menu) MENU * menu;
Note that neither of these functions actually change what is displayed on the terminal. After posting or unposting a menu, you must call wrefresh() (or its equivalents, wnoutrefresh() and doupdate()) to do so.
If function post_menu() encounters an error, it returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - NULL menu pointer E_POSTED - menu is already posted E_NOT_CONNECTED - noconnected items E_NO_ROOM - menu does not fit in subwindow
Regarding E_NO_ROOM, recall from the section "Querying the Menu Dimensions'' that function scale_menu() returns the number of rows and columns necessary to display the menu. It does not, however, know the size of the subwindow you are associating with the menu. Only when the menu is posted is this point checked. Any failure of the menu to fit in the subwindow is then detected.
If function unpost_menu() executes successfully, it returns E_OK. In the following situations, it fails and returns the indicated values:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - NULL menu pointer E_NOT_POSTED - menu is not posted E_BAD_STATE - called from init or term
You might, for instance, receive E_NOT_POSTED if you forgot to post the menu in the first place or you mistakenly tried to unpost it twice.
The following screen illustrates two routines you might write to post and unpost menus. Function display_menu() creates the window and subwindow for the menu and posts it. Function erase_menu unposts the menu and erases its associated window and subwindow.
Sample Routines Displaying and Erasing Menus
static void display_menu (m) /* create menu windows and post */ MENU * m; { WINDOW *w; int rows; int cols; scale_menu (m, &rows, &cols); /* get dimensions of menu */ /* create menu window, subwindow, and border */ if (w = newwin (rows+2, cols+2, 0, 0)) { set_menu_win (m, w); set_menu_sub (m, derwin (w, rows, cols, 1, 1)); box (w, 0, 0); /* create border of 0's */ keypad (w, 1); /* set for each data entry window */ } else error ("error return from newwin", NULL); post menu */ if (post_menu (m) != E_OK) error ("error return from post_menu", NULL); else wrefresh (w); } static void erase_menu (m) /* unpost and delete menu windows */ MENU * m; { WINDOW * w = menu_win (m); WINDOW * s = menu_sub (m); unpost_menu (m); /* unpost menu */ werase (w); /* erase menu window */ wrefresh (w); /* refresh screen */ delwin (s); /* delete menu windows */ delwin (w); }
Function keypad() is called with a second argument of 1 to enable virtual keys KEY_LL,KEY_LEFT, and others to be properly interpreted in the routine get_request() described in the section below, "Menu Driver Processing." See the discussion of keypad() in the curses(3X) manual pages for details. Finally, note the placement of checks for error returns in this example.
The menu_driver() is the workhorse of the menu system. Once the menu is posted, the menu_driver() handles all interaction with the end-user. It responds to
SYNOPSIS int menu_driver (menu, c) MENU * menu; int c;
Your application program passes a character to the menu driver for processing and evaluates the results.
To enable your application program to fetch the character for the menu driver, you write a routine that defines the input key virtualization. This is the correspondence between specific input keys, control characters, or escape sequences on the one hand and menu driver requests on the other. The virtualization routine returns a specific menu request or application command that the menu driver can process. Upon return from the menu driver, your application can check if the input was processed appropriately. If not, your application specifies the action to be taken. These actions may include terminating interaction with the menu, responding to help requests, generating an error message, and so forth.
To illustrate a key virtualization routine, consider the following screen which shows the key virtualization routine get_request(). Nearly all the values it returns are the ETI menu requests to be discussed in the sections below.
Sample Routine that Translates Keys into Menu Requests
/* define application commands */ #define QUIT (MAX_COMMAND + 1) /* Note that ^X represents the character control-X. ^Q - end menu processing ^N - move to next item ^P - move to previous item home key - move to first item home down - move to last item left arrow - move left to item right arrow - move right to item down arrow - move down to item up arrow - move up to item ^U - scroll up a line ^D - scroll down a line ^B - scroll up a page ^F - scroll down a page ^X - clear pattern buffer ^H <BS> - delete character from pattern buffer ^A - request next pattern match ^Z - request previous pattern match ^T - toggle item */ static int get_request (w)/* virtual key mapping */ WINDOW * w; { int c = wgetch (w);/* read a character */ switch (c) { case 0x11: /* ^Q */return QUIT; case 0x0e: /* ^N */return REQ_NEXT_ITEM; case 0x10: /* ^P */return REQ_PREV_ITEM; case KEY_HOME:return REQ_FIRST_ITEM; case KEY_LL:return REQ_LAST_ITEM; case KEY_LEFT:return REQ_LEFT_ITEM; case KEY_RIGHT:return REQ_RIGHT_ITEM; case KEY_UP:return REQ_UP_ITEM; case KEY_DOWN:return REQ_DOWN_ITEM; case 0x15: /* ^U */return REQ_SCR_ULINE; case 0x04: /* ^D */return REQ_SCR_DLINE; case 0x06: /* ^F */return REQ_SCR_DPAGE; case 0x02: /* ^B */return REQ_SCR_UPAGE; case 0x18: /* ^X */return REQ_CLEAR_PATTERN; case 0x08: /* ^H */return REQ_BACK_PATTERN; case 0x01: /* ^A */return REQ_NEXT_MATCH; case 0x1a: /* ^Z */return REQ_PREV_MATCH; case 0x14: /* ^T */return REQ_TOGGLE_ITEM; } return c; }
Note that because wgetch here automatically does a refresh before reading a character, you can omit explicit calls to wrefresh in applications that do character input.
ETI menu requests are made by calling function menu_driver() with an int value that signifies the request. To appreciate the effects of some requests, bear in mind what a menu page is.
A menu page is the collection of currently visible menu items, i.e., those displayed in the menu subwindow.
A menu page is distinct from a form page, which is a logical portion of a form. Form pages are treated in the upcoming chapter, "Forms."
These requests enable your end user to navigate from item to item whether or not the items are displayed at the moment.
REQ_NEXT_ITEM - move to next item REQ_PREV_ITEM - move to previous item REQ_FIRST_ITEM - move to first item REQ_LAST_ITEM - move to last item
The order of the items in the array originally passed to new_menu() or set_menu_items() determines the order in which items are visited in response to these requests.
The REQ_NEXT_ITEM and REQ_PREV_ITEM requests are not cyclic. A REQ_NEXT_ITEM request from the last item or a REQ_PREV_ITEM request from the first item returns the value E_REQUEST_DENIED.
Often, a scrolling operation not explicitly requested by the user may nonetheless take place in response to these requests. For example, the REQ_FIRST_ITEM request on a menu that is not currently displaying the first item may scroll to display the menu's first item at the top of the screen.
These requests enable your end-user to navigate from item to item in different directions.
REQ_LEFT_ITEM - move left to item REQ_RIGHT_ITEM - move right to item REQ_UP_ITEM - move up to item REQ_DOWN_ITEM - move down to item
Directional item navigation requests are not cyclic. If there is no item on the current page to the left or right of the current item, the menu driver returns E_REQUEST_DENIED in response to the corresponding request.
On the other hand, if the menu is scrollable and there are more items above or below the current menu page, the corresponding requests REQ_UP_ITEM and REQ_DOWN_ITEM generate an automatic scrolling operation. If not, the menu driver returns E_REQUEST_DENIED.
These requests enable your users to scroll easily through menus that span more than one menu page.
REQ_SCR_DLINE - scroll menu down a line REQ_SCR_ULINE - scroll menu up a line REQ_SCR_DPAGE - scroll menu down a page REQ_SCR_UPAGE - scroll menu up a page
The current and top items are adjusted by these operations.
Menu scrolling requests are also not cyclic. Attempts to scroll up from the first menu page, or scroll down from the last, return from the menu driver the value E_REQUEST_DENIED.
This request enables your end user to select or deselect an item in a multi-valued menu.
REQ_TOGGLE_ITEM - select/deselect item
If the item is currently selected, this request deselects it, and vice versa.
To use this request, the O_ONEVALUE option must be off. (See the section below, "Setting and Fetching Menu Options'') If the option is on, you have a single-valued menu. In that case, this request fails and E_REQUEST_DENIED is returned from the menu driver.
The pattern buffer is an area automatically allocated for your menu application programs. It is used to position the current menu item at an item name that matches the pattern. You can modify the pattern buffer
Each non-printable ASCII character that is received by the menu driver is assumed to be a menu request. On the other hand, each printable ASCII character that is received by the menu driver is entered into the pattern buffer. At the same time, the current item advances to the first matching item. If no matching item is found, the current item remains unchanged, the character is deleted from the pattern buffer, and the menu driver returns E_NO_MATCH.
The following requests enable you to change and read the pattern buffer.
REQ_CLEAR_PATTERN - clear pattern buffer REQ_BACK_PATTERN - delete last character from pattern buffer REQ_NEXT_MATCH - move to next pattern match REQ_PREV_MATCH - move to previous match Request REQ_CLEAR_PATTERN - clears the pattern buffer entirely.
NOTE: Without request REQ_CLEAR_PATTERN, the pattern buffer is automatically cleared after each successful scrolling or item navigation operation. In other words, any time the top item or current item changes, the pattern buffer is cleared automatically.
REQ_BACK_PATTERN deletes the last character from the pattern buffer. This request can be used to support a backspace operation on the pattern buffer.
Sometimes more than one menu item will match the character(s) entered by the user. REQ_NEXT_MATCH moves the user forward on the displayed menu to the next array item that matches the data in the pattern buffer. REQ_PREV_MATCH, on the other hand, moves the user backward on the displayed menu to the previous array item that matches the pattern buffer. In both cases, if no additional match is found, the current item remains unchanged and E_NO_MATCH is returned from the menu driver.
Requests REQ_NEXT_MATCH and REQ_PREV_MATCH are cyclic through all menu items. In addition, these requests generate automatic scrolling requests if the menu is scrollable and the next or previous matching item is not visible.
NOTE: An empty pattern buffer matches all items.
ETI menu requests are implemented as integers above the curses maximum key value KEY_MAX. A symbolic constant MAX_COMMAND is provided to enable your applications to implement their own requests (commands) without conflicting with the ETI form and menu system. All menu requests are greater than KEY_MAX and less than or equal to MAX_COMMAND. Your application-defined requests should be greater than MAX_COMMAND. Two illustrations occur in the example in the next section. The following diagram shows this relationship between ETI key values, ETI menu requests, and your application program's menu requests.
The menu driver checks whether the virtualized character passed to it is an ETI menu request. If so, it performs the request and reports the results. If the character is not a menu request, the menu driver checks if the character is data, i.e., a printable ASCII character. If so, it enters the character in the pattern buffer and looks for the first match among the item names. If no match is found, the menu driver deletes the character from the pattern buffer and returns E_NO_MATCH. If the character is not recognized as a menu request or data, the menu driver assumes the character is an application-defined command and returns E_UNKNOWN_COMMAND.
To illustrate a sample design for calling the menu driver, we will consider a program that permits interaction with a menu of astrological signs. The following screen displays the menu.
Sample Menu Output (2)
+-----------------------------+ | Aries The Ram | | Taurus The Bull | | Gemini The Twins | | Cancer The Crab | | Leo The Lion | | Virgo The Virgin | | Libra The Balance | | Scorpio The Scorpion | | Sagittarius The Archer | | Capricorn The Goat | | Aquarius The Water Bearer| | Pisces The Fishes | +-----------------------------+
You have already seen much of the astrological sign program in previous examples. Its function get_request(), for instance, appeared in "Defining the Key Virtualization Correspondence" section. The following screen shows its remaining routines.
Sample Program Calling the Menu Driver
/* This program displays a sample menu. Omitted here are the key mapping defined by get_request; application-defined routines display_menu and erase_menu; and the curses initialization routine start_curses in section "ETI Low-level Interface to High-level Functions" */ #include <string.h> #include <menu.h> static char * PGM = (char *) 0;/* program name */ static int my_driver (m, c)/* handle application commands */ MENU * m; int c; { switch (c) { case QUIT: return TRUE; break; } beep (); /* signal error */ return FALSE; } main (argc, argv) int argc; char * argv[]; { WINDOW * w; MENU * m; ITEM ** i; ITEM ** make_items (); void free_items (); int c, done = FALSE; PGM = argv[0]; start_curses (); if (! (m = new_menu (make_items ()))) error ("error return from new_menu", NULL); display_menu (m); /* interact with user */ w = menu_win (m); while (! done) { switch (menu_driver (m, c = get_request (w))) { case E_OK: break; case E_UNKNOWN_COMMAND: done = my_driver (m, c); break; default: beep ();/* signal error */ break; } } erase_menu (m); end_curses (); i = menu_items (m); free_menu (m); free_items (i); exit (0); } typedef struct { char * name; char * desc; } ITEM_RECORD; /* item definitions */ static ITEM_RECORD signs [] = { "Aries", "The Ram", "Taurus", "The Bull", "Gemini", "The Twins", "Cancer", "The Crab", "Leo", "The Lion", "Virgo", "The Virgin", "Libra", "The Balance", "Scorpio", "The Scorpion", "Sagittarius", "The Archer", "Capricorn", "The Goat", "Aquarius", "The Water Bearer", "Pisces", "The Fishes", (char *) 0, (char *) 0, }; #define MAX_ITEM 512 static ITEM * items [MAX_ITEM + 1]; /* item buffer */ static ITEM ** make_items () /* create the items */ { int i; for (i = 0; i < MAX_ITEM && signs[i].name; ++i) items[i] = new_item (signs[i].name, signs[i].desc); items[i] = (ITEM *) 0; return items; } static void free_items (i) /* free the items */ ITEM ** i; { while (*i) free_item (*i++); }
Function main() first calls the application-defined routine make_items() to create the items from the array signs. The value returned is passed to new_menu() to create the menu. Function main() then initializes curses using start_curses() and displays the menu using display_menu().
In its while loop, main() repeatedly calls menu_driver() with the character returned by get_request(). If the menu driver does not recognize the character as a request or data, it returns E_UNKNOWN_COMMAND, whereupon the application-defined routine my_driver() is called with the same character. Routine my_driver() processes the application-defined commands. In this example, there is only one, QUIT. If the character passed does not signify QUIT, my_driver() signals an error and returns FALSE and the signal prompts the user to re-enter the character. If the character passed is the QUIT character, my_driver() returns TRUE. In turn, this sets done to TRUE, and the while loop is exited.
Finally, main() erases the menu, terminates low-level ETI (curses), frees the menu and its items, and exits the program.
This example shows a typical design for calling the menu driver, but it is only one of several ways you can structure a menu application. For another example, see the demonstration program menu2.c delivered with the ETI product.
If the menu_driver() recognizes and processes the input character argument, it returns E_OK. In the following error situations, the menu_driver() returns the indicated value:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - NULL menu E_BAD_STATE - called from init/term routines E_NOT_POSTED - menu is not posted E_UNKNOWN_COMMAND - unknown command E_NO_MATCH - item match failed E_REQUEST_DENIED - recognized request failed
NOTE: Because the menu driver calls the initialization and termination routines described in the next section, it may not be called from within them. Any attempt to do so returns E_BAD_STATE.
Sometimes, you may want the menu driver to execute a specific routine during the change of an item or menu. The following functions let you do this easily.
SYNOPSIS typedef void (*PTF_void) (); int set_menu_init (menu, func) MENU * menu; PTF_void func; PTF_void menu_init (menu) MENU * menu; int set_menu_term (menu, func) MENU * menu; PTF_void func; PTF_void menu_term (menu) MENU * menu; int set_item_init (menu, func) MENU * menu; PTF_void func; PTF_void item_init (menu) MENU * menu; int set_item_term (menu, func) MENU * menu; PTF_void func; PTF_void item_term (menu) MENU * menu;
The argument func is a pointer to the specific function you want executed by the menu driver. This application-defined function takes a menu pointer as an argument.
If you want your application to execute an application-defined function at one of the initialization or termination points listed below, you should call the appropriate set_ routine at the start of your program. If you do not want a specific function executed in these cases, you may refrain from calling these routines altogether.
The following subsections summarize when each initialization and termination routine is executed.
The argument func to this function is automatically called by the menu system
The argument func is automatically called by the menu system
The argument func is automatically called by the menu system
The argument func is automatically called by the menu system
If functions set_menu_init(), set_menu_term(), set_item_init(), or set_item_term() encounter an error, they return
E_SYSTEM_ERROR - system error
The following screen shows how you can use function set_item_init() to implement a menu prompting feature as your end-user moves from item to item.
Using an Initialization Routine to Generate Item Prompts
WINDOW * prompt_window; void display_prompt (s) char * s; { WINDOW * w = prompt_window; werase (w); wmove (w, 0, 0); /* move to window origin */ waddstr (w, s); /* write prompt in window */ wrefresh (w); /* display prompt */ } void generate_prompt (m) MENU * m; { /* display the prompt string associated with the current item */ char * s = item_userptr (current_item (m)); display_prompt (s); } ITEM * items[NUMBER_OF_ITEMS + 1]; main () { MENU * m; for (i = 0; i < NUMBER_OF_ITEMS; ++i) { /* read in name and prompt strings here */ items[i] = new_item (name, ""); set_item_userptr (items[i], prompt); } items[i] = (ITEM *) 0; m = new_menu (items); set_item_init (m, generate_prompt); /* set initialization routine */ }
Function set_item_init() arranges to call generate_prompt() whenever the menu item changes. Function generate_prompt() fetches the item user pointer associated with the current item and calls display_prompt(), which displays the item prompt. Function display_prompt() is a separate function to enable you to use it for other prompts as well.
The current item is the item where your end-user is positioned on the screen. Unless it is invisible, this item is highlighted and the cursor rests on the item. To have your application program set or determine the current item, you use the following functions.
SYNOPSIS int set_current_item (menu, item) MENU * menu; ITEM * item; ITEM * current_item (menu) MENU * menu; int item_index (item) ITEM * item;
Function set_current_item() enables you to set the current item bypassing an item pointer, while function current_item() returns the pointer to the current item.
The function item_index() takes an item pointer argument and returns the index to that item in the item pointer array. The value of this index ranges from 0 through N-1, where N is the total number of items connected to the menu.
Because the menu driver satisfies ETI-defined item navigation requests automatically, your application program need not call set_current_item(), unless you want to implement additional item navigation requests for your application. You may, for instance, want a request to jump to a particular item or an item, say, two items down from the current one on the menu page.
When a menu is created by new_menu() or the items associated with a menu are changed by set_menu_items, the current item is set to the first item of the menu.
As an example of set_current_item(), the following function sets the current item of menu m to the first item of the menu:
int set_first_item (m) /* set current item to first item */ MENU * m; { ITEM ** i = menu_items (m); return set_current_item (m, i[0]); }
As an example of current_item(), the following routine checks if the first menu item is the current one:
int first_item (m) /* check if current item is first item */ MENU * m; { ITEM * i = current_item (m); return item_index (i) == 0; }
If successful, function set_current_item() returns E_OK. If an error occurs, function set_current_item() returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - NULL menu pointer or item not connected to menu E_BAD_STATE - called from initialization or termination routines
Function current_item() returns (ITEM *) 0 if given a NULL menu pointer or there are no items connected to the menu.
Function item_index() returns -1 if the item pointer is NULL or the item is not connected to a menu.
Function top_row() returns the number of the menu row currently displayed at the top of your end-user's menu. Function set_top_row() sets the top of the menu to the named row,unless the row does not start a complete page of items. In this case, it returns E_BAD_ARGUMENT.
SYNOPSIS <nt set_top_row(menu, row) MENU * menu; int row; int top_row(menu) MENU * menu;
Function set_top_row() sets the current item to the leftmost item in the new top row. Variable row must be in the range of 0 through TR- VR, where TR is the total number of rows as determined by the menu format and VR is the number of visible rows. If the value of row is greater, the row does not start a complete page of items. See the section "Specifying the Menu Format'' for details on menu display.
When a menu is created by new_menu() or the items associated with the menu are changed by set_menu_items, the top row is set to 0.
NOTE: If the menu format or the O_ROWMAJOR option is changed, the top row is automatically set to 0. See the sections "Specifying the Menu Format'' and "Setting and Fetching Menu Options'' for details on changing these menu attributes.
In addition, if the current item is changed by set_current_item() or set_menu_pattern() to an item that is not currently visible, the top row is generally set to the row that contains the new current item. The sole exception occurs when, as noted above, the top row does not start a complete page of items.
If successful, function set_top_row() returns E_OK. If an error occurs, set_top_item() returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - NULL menu pointer or index out of range E_BAD_STATE - called from init/term routines E_NOT_CONNECTED - no connected items
Function top_row() returns -1 if given a NULL menu pointer or no items are connected to the menu.
Some applications may need to move the menu's window cursor from the position required for continued processing by the ETI menu driver. To move the cursor back to where it belongs, you use function pos_menu_cursor().
SYNOPSIS int pos_menu_cursor (menu) MENU * menu;
If your application does not change the cursor position in the menu window, calling this function is unnecessary.
Your application might change the cursor position automatically because of prior calls to menu driver initialization routines such as set_item_init(). Or it might do so because of explicit calls to application routines such as writing a prompt. The following screen illustrates this usage.
Returning Cursor to its Correct Position for Menu Driver Processing
void generate_prompt (m) MENU * m; { /* display the prompt string associated with the current item */ WINDOW * w = menu_win (m); char * s = item_userptr (current_item (m)); box (w, 0, 0); wmove (w, 0, 0); waddstr (w, s); pos_menu_cursor (m); }
If function pos_menu_cursor() is successful, it returns E_OK. In the following error situations, it fails and returns the indicated value:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - NULL menu pointer E_NOT_POSTED - menu is not posted
Remember that the pattern buffer is used to make the first item that matches the pattern be the current item. In general, to match the current menu item, your application program inserts characters into the pattern buffer that have been passed to the menu driver from the user's data entry. As an alternative, you can insert characters into the pattern buffer with the function set_menu_pattern().
SYNOPSIS int set_menu_pattern (menu, pattern) MENU * menu; char *pattern; char * menu_pattern (menu) MENU * menu;
Function set_menu_pattern() first clears the pattern buffer and then adds the characters in pattern to the buffer until pattern is exhausted. The function next tries to find the first item that matches the pattern. If it does not find a complete match, the pattern buffer is cleared and the current item does not change. If pattern is the null string (""), the pattern buffer is simply cleared. The pattern buffer is automatically cleared whenever
If successful, function set_menu_pattern() returns E_OK. If an error occurs, function set_menu_pattern() returns one of the following:
E_SYSTEM_ERROR - system error E_BAD_ARGUMENT - NULL menu pointer or NULL pattern pointer E_NO_MATCH - complete match failed
Function menu_pattern() returns the value of the string in the pattern buffer. If the pattern buffer is empty (the null string ""), it returns the null string (""). If the menu pointer argument is NULL, it returns NULL, i.e., (char *) 0.
To determine if your user has entered data that matches an item, you might write a routine that uses set_menu_pattern(), as follows:
int find_match (m, newpattern) /* returns TRUE or FALSE */ MENU * m; char * newpattern; { return set_menu_pattern(m, newpattern) == E_OK; }
If the newpattern matches a menu item, function set_menu_pattern() returns E_OK and hence find_match() returns TRUE. In addition,find_match() advances the current item to the matching item.
As it does for panels and forms, ETI provides user pointers for each menu. You can use these pointers to reference menu messages, titles, and the like.
SYNOPSIS int set_menu_userptr (menu, userptr) MENU * menu; char *userptr; char * menu_userptr (menu) MENU * menu;
By default, the menu user pointer (what menu_userptr() returns) is NULL.
If successful, set_menu_userptr() returns E_OK. If an error occurs, it returns the following:
E_SYSTEM_ERROR - system error
The code in following screen illustrates how you can use these two functions to display a title for your menu. Function main() sets the menu user pointer to point to the title of the menu. Later, function display_menu() initializes the title with the value returned by menu_userptr(). We have previously seen a version of display_menu().
Example Setting and Using A Menu User Pointer
static void display_menu (m) /* create menu windows and post */ MENU * m; { char * title = menu_userptr (m); /* fetch menu title */ WINDOW * w; int rows; int cols; scale_menu (m, &rows, &cols); /* get dimensions of menu */ /* create menu window and subwindow */ if (w = newwin (rows+2, cols+2, 0, 0)) { set_menu_win (m, w); set_menu_sub (m, derwin (w, rows, cols, 1, 1)); box (w, 0, 0); keypad (w, 1); } else error ("error return from newwin", NULL); if (post_menu (m) != E_OK) error ("error return from post_menu", NULL); if (title) /* if title set */ { size = strlen (title); wmove (w, 0, (cols-size)/2+1); /* position cursor */ waddstr (w, title);/* write title */ } } main () { MENU * m; char * menutitle; /* initialize menutitle to desired string */ set_menu_userptr (m, menutitle); /* set user pointer to point to title */ display_menu (m); }
If function set_menu_userptr() is passed a NULL menu pointer, like all ETI functions, it assigns a new current default menu user pointer. In the following, the new default is the string Default Menu Title.
MENU * m; char * userprtr = "Default Menu Title"; set_menu_userptr( (MENU *) 0, userptr); /* sets new default userptr */
ETI provides several menu options, some of which we have already met. Two functions manipulate options: one sets them, the other returns their settings.
SYNOPSIS int set_menu_opts (menu, opts) MENU * menu; OPTIONS opts; OPTIONS menu_opts (menu) MENU * menu; options: O_ONEVALUE O_SHOWDESC O_ROWMAJOR O_IGNORECASE O_SHOWMATCH
Besides turning the named options on, function set_menu_opts() turns off all other menu options. By default, all menu options are on.
The menu options and their effects are as follows:
O_ONEVALUE
determines whether the menu is a single-valued or multi-valued. In general, menus are single-valued and this option is on. Recall that upon exit from single-valued menus, your application queries the current item to ascertain the item selected. Turning off this option signifies a multi-valued menu. One way to select several items is to use the REQ_TOGGLE_ITEM request, another is to call set_item_value. (See the previous sections "Multi-valued Menu Selection Request" and "Manipulating an Item's Select Value in a Multi-valued Menu") Recall that your application must examine each item's select value to determine whether it has been selected. When this option is on, all item select values are FALSE.
O_SHOWDESC
determines whether or not the description of an item is displayed. By default, this option is on and both the item name and description are displayed. If this option is off, only the name is displayed.
O_ROWMAJOR
determines how the menu items are presented on the screen - in row-major or column-major order. In row-major order, menu items are displayed first left to right, then top to bottom. In column-major order, they are displayed first top to bottom, then left to right. By default, this option is on, so menu items are displayed in row-major order. If the option is off, the items are displayed in column-major order. See the section above, "Specifying the Menu Format,'' for more on how menus are displayed.
O_IGNORECASE
instructs the menu driver to ignore upper-and lower-case during the item match operation. If this option is off, character case is not ignored and the match must be exact.
O_SHOWMATCH
determines whether visual feedback is provided as each item's data entry is processed. Ordinarily, as soon as a match occurs, the cursor is advanced through the item to reflect the contents of the pattern buffer. If this option is off, however, the cursor remains to the left of the current item.
Like all ETI options, menu OPTIONS are Boolean values, so you use Boolean operators to turn them on or off with functions set_menu_opts() and menu_opts(). For example, to turn off option O_SHOWDESC for menu m0 and turn on the same option for menu m1, you can write:
MENU * m0, * m1; set_menu_opts (m0, menu_opts (m0) & ~O_SHOWDESC); /* turn option off */ set_menu_opts (m1, menu_opts (m1) | O_SHOWDESC); /* turn option on */
ETI provides two alternative functions for turning options on and off for a given menu.
SYNOPSIS <nt menu_opts_on (menu, opts) MENU * menu; OPTIONS opts; int menu_opts_off (menu, opts) MENU * menu; OPTIONS opts;
Unlike function set_menu_opts(), these functions do not affect options that are unmentioned in their second argument. In addition, if you want to change one option, you need not apply Boolean operators or use menu_opts().
As an example, the following code turns option O_SHOWDESC off for menu m0 and on for menu m1:
MENU * m0, * m1; menu_opts_off (m0, O_SHOWDESC); /* turn option off */ menu_opts_on (m1, O_SHOWDESC); /* turn option on */
As usual, you can change the current default for each option by passing a NULL menu pointer. For instance, to turn the default option O_SHOWDESC off, you write
menu_opts_off ((MENU *) 0, O_SHOWDESC);
/* turn default option off */
In general, functions set_menu_opts(), menu_opts_on(), and menu_opts_off() return E_OK. If an error occurs, they return one of the following:
E_SYSTEM_ERROR - system error E_POSTED - menu is posted