Menulib - A simple menu library

Original Version by Louis Tremblet CERN ECP/DS

Modified by Jason Leake RAL


Menulib is a contribution by Fido Dittus and Dani Frei from the Numass experiment. Initially running on OS9, it has been ported to the UNIX environment.

Menulib is a collection of C callable functions allowing simple (basic) user interaction with a running program. Menu items are selected with keyboard input only (no mouse and no arrow keys). Applications making use of this package can even run on a dumb terminal though better results will be achieved on a VT100 compatible terminal or VT100 terminal emulator. Menulib is hierarchical, thus allowing the use of sub-menus for more complex processing.


Introduction

Calling Sequences

menu_init - initialise Menulib.

menu_define - define a new menu.

menu_add_item - add an item to an existing menu.

menu_find_item - find menu item for a given key.

menu_expose - expose a menu on the screen.

A Complete Example


Introduction

For each menu, Menulib displays a menu title followed by menu items (one per line), each including: and finally a prompt message. Selection from the custom options is made by entering the one character key, each selection may have associated an action that will be executed when the selection is made. Text input is case sensitive except for the one character key input Menulib can handle the following data types: Menulib checks as far as possible for correctness of data type input. It checks as well for lower and upper limits of input values if the user asked for that option. In case of invalid input Menulib automatically reprompts the user with this item, displaying in addition default value and limits if appropriate.

Calling Sequences

menu_init - initialise Menulib.

Syntax

#include "menu.h"
void menu_init()

Description

menu_init initialises the Menu package and determines the capabilities (cursor addressing, inverse video, blinking, ...) of your terminal. This must be the first function to be used in the Menu package.

Errors

Warning messages are issued when no or incorrect terminal type (TERM environment variable) is detected. In this case the terminal type is set to dumb.

menu_define - define a new menu.

Syntax

#include "menu.h"
menu_t *menu_define(title)
char *title;

Description

menu_define creates and initialises a new menu strucrure. The returned value is a pointer to a newly created structure of the following format:
struct menu {
   char title[ ];
   struct menu_item *first_item_ptr;
} menu_t;

menu_add_item - add an item to an existing menu.

Syntax

#include "menu.h"

menu_item_t *menu_add_item(mp, key, text, isvalid, action, hasdata)
menu_t *mp;
char key;
char *text;
int isvalid;
int (*action)();
int hasdata;

Description

menu_add_item creates a new item and adds it to the menu structure referenced by mp. This item is activated when the user hits the character `key'. The argument text is a pointer to a character string containing the item description. The value of the action argument, if not NULL, specifies the address of a user function to be activated when the user hits that `key'. This function gets called with two parameters: a pointer to the current menu structure as well as a pointer to the menu item selected by the user. On return from a user action function, the screen is cleared and a menu is exposed. The argument isvalid is a flag which controls whether this menu item is valid i.e. wether the item is displayed when the menu is exposed (1 means the item is valid, 0 otherwise). The argument hasdata is a flag which controls whether this menu item is to be allocate storage to store user supplied data (1 in that case, 0 otherwise). The returned value is a pointer to a newly created menu_item structure of the following format:
struct menu_item {
  struct menu_item *prev;
  struct menu_item *next;
  char key;
  int valid;
  int (*action)();
  item_data_t *data_p;
} menu_item_t;

typedef enum {
  integer_type,
  float_type,
  enum_type,
  toggle_type,
  string_type,
  text_type
} valtyp_t;

struct item_data {
  valtyp_t type;
  int value;
  float fvalue;
  int limits[2];
  char valtxt[ ] [ ];
} item_data_t;

menu_find_item - find menu item for a given key.

Syntax

#include "menu.h"
menu_item_t *menu_find_item(key, mp)
char key;
menu_t *mp;

Description

menu_find_item looks for a menu_item corresponding to the given key in the menu pointed to by mp . The returned value is a pointer to a menu_item in case of success or the NULL pointer if no menu_item is found.
struct menu_item {
   struct menu_item *prev;
   struct menu_item *next;
   char key;
   int valid;
   int (*action)();
   item_data_t *data_p;
} menu_item_t;

menu_expose - expose a menu on the screen.

Syntax

#include "menu.h"
void menu_expose(mp)
menu_t *mp;

Description

menu_expose displays the menu pointed to by mp on a screen, prompts the user to select an item in the item list, handles user input, finds the item associated with the selected key and calls the action function if any. The only way to leave menu_expose is to quit the menu pointed to by mp. This is achieved by returning 0 in an action function

Linking

Linking on UNIX systems

Applications making use of the Menu package must be linked with: libmenu.a and libcurses. (or libcursesX.a if running from an xterm) or libtermcap.a

Linking on OS9 systems

Applications making use of the Menu package must be linked with: menulib.l and termlib.l

A Complete Example

/* <test.c> Test program for the menu package */

#include 

#include "menu.h"

static menu_t *mtp;
static menu_item_t *mitp;
int toggle_action(mp, mip)
menu_t *mp; menu_item_t mip;
   {
   menu_find_item('0',mtp)->valid = menu_find_item('0',mtp)->valid ? 0 : 1;
   return 1;       /* Normal return */
}
int user_action(mp, mip)
menu_t *mp; menu_item_t mip;
   {
   menu_item_t *mitp;
   printf("\\n\\n\\n  You selected key item (%c)\\n", mip->key);
   printf("Press ");
   scanf("%*c");
   return 1;       /* Normal return */
}
int quit_action()
 {
   return 0;       /* Quit return */
}
main()
   {
   
menu_init
();
   mtp = 
menu_define
("Main menu");
   mitp=
menu_add_item
(mtp, '0', "Item0: initially invalid item", 0, NULL, 0);
   mitp=
menu_add_item
(mtp, '1', "Item1: validate/invalidate item 0", 1, toggle_action, 0);
   mitp=
menu_add_item
(mtp, '2', "Item2: integer data, action",
       1, user_action, 1);
   mitp->data_p->type = integer_type;
/* integer only */
   mitp->data_p->value = 17;
/* default value */
   mitp->data_p->limits[0] = 1;
/* lower limit */
   mitp->data_p->limits[1] = 99;
/* upper limit */
   mitp=
menu_add_item
(mtp, '3', "Item3: float data, action", 1, user_action, 1);
   mitp->data_p->type = float_type;
/* floating only */
   mitp->data_p->fvalue = 3.14159;
/* default value */
   mitp=
menu_add_item
(mtp, '4', "Item4: toggle data, action", 1, user_action, 1);
   mitp->data_p->type = toggle_type;
/* toggle only */
   mitp->data_p->value = 0;
/* default value */
   strcpy(mitp->data_p->valtxt[0], "ON");
/* text for 1st comtponent */
   strcpy(mitp->data_p->valtxt[1], "OFF");
/* text for 2nd comtponent */
   mitp=
menu_add_item
(mtp, '5', "Item5: enum data, action", 1, user_action, 1);
   mitp->data_p->type = enum_type;
/* enumeration only */
   mitp->data_p->limits[0] = 3;
/* number of components */
   strcpy(mitp->data_p->valtxt[0], "Low");
/* text for 1st comtponent */
   strcpy(mitp->data_p->valtxt[1], "Medium");
/* text for 2nd comtponent */
   strcpy(mitp->data_p->valtxt[2], "High");
/* text for 3rd comtponent */
   mitp=
menu_add_item
(mtp, '6', "Item6: string data, action", 1, user_action, 1);
   mitp->data_p->type = string_type;
/* string only */
   mitp->data_p->value = 32;
/* maximum length of string */
   strcpy(mitp->data_p->valtxt[0], "a string with 32 chars max");   /* initial value */
   
menu_add_item
(mtp, 'Q', "return action, no data", 1, quit_action, 0);
   
menu_expose
(mtp);
/* On return, the user has chosen to exit */
}