Previous Page TOC Next Page

Character User Interface (FMLI and ETI)

Menus

Introduction


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

Compiling and Linking Menu Programs


To use the menu routines, you specify


in your C program files and compile and link with the command line


If you use the panel routines as well, specify-lpanel before -lcurses on the command line.

Overview: Writing Menu Programs in ETI


This section introduces basic ETI menu terminology, lists the steps in a typical menu application program, and reviews the code in a simple example.

Some Important Menu Terminology


The following terms will be helpful:


item
a character string consisting of a name and an optional description


menu
a screen display that presents a set of items from which the user selects one or more, depending on the type of menu


connecting items to a menu
associating an array of item pointers with a menu


menu subwindow
a subwindow on which an associated menu is written


menu window
a window on which an associated menu subwindow and titles and borders, if any, are displayed


posting a menu
writing a menu on its associated subwindow


unposting a menu
erasing a menu from its associated subwindow


pattern matching
checking whether characters entered by the user match an item name of the menu


freeing a menu
deallocating the space for a menu and, as a by product, disconnecting an associated array of item pointers from a menu


freeing an item
deallocating the space for an item


NULL
generic term for a null pointer cast to the type of the particular object (item, menu, field, form, and so on)

What a Menu Application Program Does


In general, a menu application program will

A Sample Menu Program


The following screen shows the ETI code necessary for generating the menu of colors.

Sample Menu Program to Create a Menu in ETI


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


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.

Creating and Freeing Menu Items


Normally, to create a menu, you must first create the items comprising it. To create a menu item, you use function new_item().


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


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().


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:

Two Kinds of Menus: Single- or Multi-valued


Menus are of two kinds:


Single-valued menus
from which the user may select only one item


Multi-valued menus
from which the user may select one or more items


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.

Manipulating an Item's Select Value in a Multi-valued Menu


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.


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.


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

Manipulating Item Attributes


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.

Fetching Item Names and Descriptions


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.


Both functions return NULL if given a NULL item pointer.

Setting Item Options


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.


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:


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:


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.


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.


To change the current default to not O_SELECTABLE, you can write either


or

Checking an Item's Visibility


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.


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


For another example, see the section below, "Counting the Number of Menu Items".

Changing the Current Default Values for Item Attributes


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.

Setting the Item User Pointer


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.


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


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:


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:


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":

Creating and Freeing Menus


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().


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


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().


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:


For E_POSTED, see the section,"Posting and Unposting Menus"

Manipulating Menu Attributes


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.

Fetching and Changing Menu Items


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.


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:


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

Counting the Number of Menu Items


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.


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.

Changing the Current Default Values for Menu Attributes


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.

Displaying Menus


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.

Determining the Dimensions of Menus


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.

Specifying the Menu Format


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.


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:


ETI calculates the total number of rows and columns in a column major menu as follows:


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


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

Undisplayed Graphic

Undisplayed Graphic


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.

Undisplayed Graphic

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:


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


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.

Changing Your Menu's Mark String


The mark string distinguishes


The mark string appears just to the left of the item name.


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


If successful, function set_menu_mark() returns E_OK. If an error occurs, function set_menu_mark() returns one of the following:


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


All subsequently created menus will have ---> as their mark string. To return the current default mark string, you call menu_mark() with NULL:

Querying the Menu Dimensions


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.


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:


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:

Associating Windows and Subwindows with Menus


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.


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.

Undisplayed Graphic

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


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


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:

Fetching and Changing a Menu's Display Attributes


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


foreground attribute
distinguishes the current item, if selectable, on all menus and selected items on multi-valued menus


background attribute
distinguishes selectable, but unselected, items on all menus


grey attribute
distinguishes unselectable items on multi-valued menus


pad character
the character that fills (pads) the space between a menu item's name and description


The following functions enable you to set and read these attributes.


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


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


If functions set_menu_fore(), set_menu_back(), and set_menu_grey() encounter an error, they return one of the following:


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


If function set_menu_pad() encounters an error, it returns one of the following:

Posting and Unposting Menus


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.


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:


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:


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


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.

Menu Driver Processing


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


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.

Defining the Key Virtualization Correspondence


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


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


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."

Item Navigation Requests


These requests enable your end user to navigate from item to item whether or not the items are displayed at the moment.


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.

Directional Item Navigation Requests


These requests enable your end-user to navigate from item to item in different directions.


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.

Menu Scrolling Requests


These requests enable your users to scroll easily through menus that span more than one menu 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.

Multi-valued Menu Selection Request


This request enables your end user to select or deselect an item in a multi-valued menu.


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.

Pattern Buffer Requests


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.


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.

Application-defined Commands


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.

Calling the Menu Driver


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)


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


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:


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.

Establishing Item and Menu Initialization and Termination Routines


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.


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.

Function set_menu_init()


The argument func to this function is automatically called by the menu system

Function set_item_init()


The argument func is automatically called by the menu system

Function set_item_term()


The argument func is automatically called by the menu system

Function set_menu_term()


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


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


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.

Fetching and Changing the Current Item


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.


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:


As an example of current_item(), the following routine checks if the first menu item is the current one:


If successful, function set_current_item() returns E_OK. If an error occurs, function set_current_item() returns one of the following:


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.

Fetching and Changing the Top Row


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.


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:


Function top_row() returns -1 if given a NULL menu pointer or no items are connected to the menu.

Positioning the Menu Cursor


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().


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


If function pos_menu_cursor() is successful, it returns E_OK. In the following error situations, it fails and returns the indicated value:

Changing and Fetching the Pattern Buffer


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().


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:


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:


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.

Manipulating the Menu User Pointer


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.


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:


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


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.

Setting and Fetching Menu Options


ETI provides several menu options, some of which we have already met. Two functions manipulate options: one sets them, the other returns their settings.


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:


ETI provides two alternative functions for turning options on and off for a given menu.


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:


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);


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: