Home | History | Annotate | Line # | Download | only in libmenu
menu.c revision 1.17.2.1
      1  1.17.2.1     tls /*	$NetBSD: menu.c,v 1.17.2.1 2013/02/25 00:27:58 tls Exp $	*/
      2       1.1   blymn 
      3       1.1   blymn /*-
      4       1.4   blymn  * Copyright (c) 1998-1999 Brett Lymn (blymn (at) baea.com.au, brett_lymn (at) yahoo.com.au)
      5       1.1   blymn  * All rights reserved.
      6       1.1   blymn  *
      7       1.1   blymn  * Redistribution and use in source and binary forms, with or without
      8       1.1   blymn  * modification, are permitted provided that the following conditions
      9       1.1   blymn  * are met:
     10       1.1   blymn  * 1. Redistributions of source code must retain the above copyright
     11       1.1   blymn  *    notice, this list of conditions and the following disclaimer.
     12       1.1   blymn  * 2. The name of the author may not be used to endorse or promote products
     13      1.10     wiz  *    derived from this software without specific prior written permission
     14       1.1   blymn  *
     15       1.1   blymn  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16       1.1   blymn  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17       1.1   blymn  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18       1.1   blymn  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19       1.1   blymn  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20       1.1   blymn  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21       1.1   blymn  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22       1.1   blymn  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23       1.1   blymn  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24       1.1   blymn  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25       1.1   blymn  *
     26       1.1   blymn  *
     27       1.1   blymn  */
     28      1.16   lukem 
     29      1.16   lukem #include <sys/cdefs.h>
     30  1.17.2.1     tls __RCSID("$NetBSD: menu.c,v 1.17.2.1 2013/02/25 00:27:58 tls Exp $");
     31       1.1   blymn 
     32       1.3  kleink #include <ctype.h>
     33       1.1   blymn #include <menu.h>
     34       1.3  kleink #include <string.h>
     35       1.1   blymn #include <stdlib.h>
     36       1.1   blymn #include "internals.h"
     37       1.1   blymn 
     38       1.1   blymn MENU _menui_default_menu = {
     39       1.1   blymn 	16,         /* number of item rows that will fit in window */
     40       1.1   blymn         1,          /* number of columns of items that will fit in window */
     41       1.1   blymn 	0,          /* number of rows of items we have */
     42       1.1   blymn 	0,          /* number of columns of items we have */
     43       1.1   blymn         0,          /* current cursor row */
     44       1.1   blymn         0,          /* current cursor column */
     45       1.1   blymn         {NULL, 0},  /* mark string */
     46       1.1   blymn         {NULL, 0},  /* unmark string */
     47       1.1   blymn         O_ONEVALUE, /* menu options */
     48       1.1   blymn         NULL,       /* the pattern buffer */
     49       1.1   blymn 	0,          /* length of pattern buffer */
     50       1.1   blymn 	0,          /* the length of matched buffer */
     51       1.1   blymn         0,          /* is the menu posted? */
     52       1.1   blymn         A_REVERSE, /* menu foreground */
     53       1.1   blymn         A_NORMAL,   /* menu background */
     54       1.1   blymn         A_UNDERLINE,      /* unselectable menu item */
     55       1.1   blymn         ' ',        /* filler between name and description */
     56       1.1   blymn         NULL,       /* user defined pointer */
     57       1.1   blymn 	0,          /* top row of menu */
     58       1.1   blymn 	0,          /* widest item in the menu */
     59       1.1   blymn 	0,          /* the width of a menu column */
     60       1.1   blymn 	0,          /* number of items attached to the menu */
     61       1.1   blymn         NULL,       /* items in the menu */
     62       1.1   blymn         0,          /* current menu item */
     63       1.1   blymn 	0,          /* currently in a hook function */
     64       1.1   blymn         NULL,       /* function called when menu posted */
     65       1.1   blymn         NULL,       /* function called when menu is unposted */
     66       1.1   blymn         NULL,       /* function called when current item changes */
     67       1.1   blymn         NULL,       /* function called when current item changes */
     68       1.1   blymn         NULL,       /* the menu window */
     69       1.1   blymn 	NULL,       /* the menu subwindow */
     70      1.11   blymn 	NULL,       /* the window to write to */
     71       1.1   blymn };
     72       1.1   blymn 
     73       1.1   blymn 
     74       1.1   blymn 
     75       1.1   blymn /*
     76       1.1   blymn  * Set the menu mark character
     77       1.1   blymn  */
     78       1.1   blymn int
     79       1.6   blymn set_menu_mark(MENU *m, char *mark)
     80       1.1   blymn {
     81       1.1   blymn 	MENU *menu = m;
     82       1.1   blymn 
     83       1.1   blymn 	if (m == NULL) menu = &_menui_default_menu;
     84       1.1   blymn 
     85       1.1   blymn           /* if there was an old mark string, free it first */
     86       1.1   blymn         if (menu->mark.string != NULL) free(menu->mark.string);
     87       1.1   blymn 
     88      1.14   blymn         if ((menu->mark.string = (char *) malloc(strlen(mark) + 1)) == NULL)
     89       1.1   blymn                 return E_SYSTEM_ERROR;
     90       1.1   blymn 
     91       1.1   blymn         strcpy(menu->mark.string, mark);
     92       1.1   blymn 	menu->mark.length = strlen(mark);
     93       1.1   blymn 
     94       1.1   blymn 	  /* max item size may have changed - recalculate. */
     95       1.4   blymn 	_menui_max_item_size(menu);
     96       1.1   blymn         return E_OK;
     97       1.1   blymn }
     98       1.1   blymn 
     99       1.1   blymn /*
    100       1.1   blymn  * Return the menu mark string for the menu.
    101       1.1   blymn  */
    102       1.1   blymn char *
    103       1.6   blymn menu_mark(MENU *menu)
    104       1.1   blymn {
    105       1.1   blymn 	if (menu == NULL)
    106       1.1   blymn 		return _menui_default_menu.mark.string;
    107       1.1   blymn 	else
    108       1.1   blymn 		return menu->mark.string;
    109       1.1   blymn }
    110       1.1   blymn 
    111       1.1   blymn /*
    112       1.1   blymn  * Set the menu unmark character
    113       1.1   blymn  */
    114       1.1   blymn int
    115       1.6   blymn set_menu_unmark(MENU *m, char *mark)
    116       1.1   blymn {
    117       1.1   blymn 	MENU *menu = m;
    118       1.1   blymn 
    119       1.1   blymn 	if (m == NULL) menu = &_menui_default_menu;
    120       1.1   blymn 
    121       1.1   blymn           /* if there was an old mark string, free it first */
    122       1.1   blymn         if (menu->unmark.string != NULL) free(menu->unmark.string);
    123       1.1   blymn 
    124      1.14   blymn         if ((menu->unmark.string = (char *) malloc(strlen(mark) + 1)) == NULL)
    125       1.1   blymn                 return E_SYSTEM_ERROR;
    126       1.1   blymn 
    127       1.1   blymn         strcpy(menu->unmark.string, mark);
    128       1.1   blymn 	menu->unmark.length = strlen(mark);
    129       1.1   blymn 	  /* max item size may have changed - recalculate. */
    130       1.4   blymn 	_menui_max_item_size(menu);
    131       1.1   blymn         return E_OK;
    132       1.1   blymn }
    133       1.1   blymn 
    134       1.1   blymn /*
    135       1.1   blymn  * Return the menu unmark string for the menu.
    136       1.1   blymn  */
    137       1.1   blymn char *
    138      1.17    matt menu_unmark(MENU *menu)
    139       1.1   blymn {
    140       1.1   blymn 	if (menu == NULL)
    141       1.1   blymn 		return _menui_default_menu.unmark.string;
    142       1.1   blymn 	else
    143       1.1   blymn 		return menu->unmark.string;
    144       1.1   blymn }
    145       1.1   blymn 
    146       1.1   blymn /*
    147       1.1   blymn  * Set the menu window to the window passed.
    148       1.1   blymn  */
    149       1.1   blymn int
    150       1.6   blymn set_menu_win(MENU *menu, WINDOW *win)
    151       1.1   blymn {
    152      1.11   blymn 	if (menu == NULL) {
    153       1.1   blymn 		_menui_default_menu.menu_win = win;
    154      1.11   blymn 		_menui_default_menu.scrwin = win;
    155      1.11   blymn 	} else {
    156      1.11   blymn 		if (menu->posted == TRUE) {
    157      1.11   blymn 			return E_POSTED;
    158      1.11   blymn 		} else {
    159      1.11   blymn 			menu->menu_win = win;
    160      1.11   blymn 			menu->scrwin = win;
    161      1.11   blymn 		}
    162      1.11   blymn 	}
    163      1.11   blymn 
    164       1.1   blymn         return E_OK;
    165       1.1   blymn }
    166       1.1   blymn 
    167       1.1   blymn /*
    168       1.1   blymn  * Return the pointer to the menu window
    169       1.1   blymn  */
    170       1.1   blymn WINDOW *
    171       1.6   blymn menu_win(MENU *menu)
    172       1.1   blymn {
    173       1.1   blymn 	if (menu == NULL)
    174       1.1   blymn 		return _menui_default_menu.menu_win;
    175       1.1   blymn 	else
    176       1.1   blymn 		return menu->menu_win;
    177       1.1   blymn }
    178       1.1   blymn 
    179       1.1   blymn /*
    180       1.1   blymn  * Set the menu subwindow for the menu.
    181       1.1   blymn  */
    182       1.1   blymn int
    183      1.17    matt set_menu_sub(MENU *menu, WINDOW *sub)
    184       1.1   blymn {
    185      1.11   blymn 	if (menu == NULL) {
    186       1.1   blymn 		_menui_default_menu.menu_subwin = sub;
    187      1.11   blymn 		_menui_default_menu.scrwin = sub;
    188      1.11   blymn 	} else {
    189      1.11   blymn 		if (menu->posted == TRUE)
    190      1.11   blymn 			return E_POSTED;
    191      1.11   blymn 
    192       1.1   blymn 		menu->menu_subwin = sub;
    193      1.11   blymn 		menu->scrwin = sub;
    194      1.11   blymn 	}
    195      1.11   blymn 
    196       1.1   blymn         return E_OK;
    197       1.1   blymn }
    198       1.1   blymn 
    199       1.1   blymn /*
    200       1.1   blymn  * Return the subwindow pointer for the menu
    201       1.1   blymn  */
    202       1.1   blymn WINDOW *
    203       1.6   blymn menu_sub(MENU *menu)
    204       1.1   blymn {
    205       1.1   blymn 	if (menu == NULL)
    206       1.1   blymn 		return _menui_default_menu.menu_subwin;
    207       1.1   blymn 	else
    208       1.1   blymn 		return menu->menu_subwin;
    209       1.1   blymn }
    210       1.1   blymn 
    211       1.1   blymn /*
    212       1.1   blymn  * Set the maximum number of rows and columns of items that may be displayed.
    213       1.1   blymn  */
    214       1.1   blymn int
    215       1.6   blymn set_menu_format(MENU *param_menu, int rows, int cols)
    216       1.1   blymn {
    217       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    218       1.1   blymn 
    219       1.1   blymn         menu->rows = rows;
    220       1.1   blymn         menu->cols = cols;
    221       1.1   blymn 
    222       1.1   blymn 	if (menu->items != NULL)
    223       1.1   blymn 		  /* recalculate the item neighbours */
    224       1.4   blymn 		return _menui_stitch_items(menu);
    225       1.1   blymn 
    226       1.1   blymn 	return E_OK;
    227       1.1   blymn }
    228       1.1   blymn 
    229       1.1   blymn /*
    230       1.1   blymn  * Return the max number of rows and cols that may be displayed.
    231       1.1   blymn  */
    232       1.1   blymn void
    233       1.6   blymn menu_format(MENU *param_menu, int *rows, int *cols)
    234       1.1   blymn {
    235       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    236       1.1   blymn 
    237       1.1   blymn         *rows = menu->rows;
    238       1.1   blymn         *cols = menu->cols;
    239       1.1   blymn }
    240       1.1   blymn 
    241       1.1   blymn /*
    242       1.1   blymn  * Set the user defined function to call when a menu is posted.
    243       1.1   blymn  */
    244       1.1   blymn int
    245       1.6   blymn set_menu_init(MENU *menu, Menu_Hook func)
    246       1.1   blymn {
    247       1.1   blymn 	if (menu == NULL)
    248       1.1   blymn 		_menui_default_menu.menu_init = func;
    249       1.1   blymn 	else
    250       1.1   blymn 		menu->menu_init = func;
    251       1.1   blymn         return E_OK;
    252       1.1   blymn }
    253       1.1   blymn 
    254       1.1   blymn /*
    255       1.1   blymn  * Return the pointer to the menu init function.
    256       1.1   blymn  */
    257       1.4   blymn Menu_Hook
    258       1.1   blymn menu_init(MENU *menu)
    259       1.1   blymn {
    260       1.1   blymn 	if (menu == NULL)
    261       1.1   blymn 		return _menui_default_menu.menu_init;
    262       1.1   blymn 	else
    263       1.1   blymn 		return menu->menu_init;
    264       1.1   blymn }
    265       1.1   blymn 
    266       1.1   blymn /*
    267       1.1   blymn  * Set the user defined function called when a menu is unposted.
    268       1.1   blymn  */
    269       1.1   blymn int
    270       1.6   blymn set_menu_term(MENU *menu, Menu_Hook func)
    271       1.1   blymn {
    272       1.1   blymn 	if (menu == NULL)
    273       1.1   blymn 		_menui_default_menu.menu_term = func;
    274       1.1   blymn 	else
    275       1.1   blymn 		menu->menu_term = func;
    276       1.1   blymn         return E_OK;
    277       1.1   blymn }
    278       1.1   blymn 
    279       1.1   blymn /*
    280       1.1   blymn  * Return the user defined menu termination function pointer.
    281       1.1   blymn  */
    282       1.4   blymn Menu_Hook
    283       1.6   blymn menu_term(MENU *menu)
    284       1.1   blymn {
    285       1.1   blymn 	if (menu == NULL)
    286       1.1   blymn 		return _menui_default_menu.menu_term;
    287       1.1   blymn 	else
    288       1.1   blymn 		return menu->menu_term;
    289       1.1   blymn }
    290       1.1   blymn 
    291       1.1   blymn /*
    292       1.1   blymn  * Return the current menu options set.
    293       1.1   blymn  */
    294       1.1   blymn OPTIONS
    295       1.6   blymn menu_opts(MENU *menu)
    296       1.1   blymn {
    297       1.1   blymn 	if (menu == NULL)
    298       1.1   blymn 		return _menui_default_menu.opts;
    299       1.1   blymn 	else
    300       1.1   blymn 		return menu->opts;
    301       1.1   blymn }
    302       1.1   blymn 
    303       1.1   blymn /*
    304       1.1   blymn  * Set the menu options to the given options.
    305       1.1   blymn  */
    306       1.1   blymn int
    307       1.6   blymn set_menu_opts(MENU *param_menu, OPTIONS opts)
    308       1.1   blymn {
    309      1.12   blymn 	int i, seen;
    310       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    311       1.1   blymn 	OPTIONS old_opts = menu->opts;
    312       1.1   blymn 
    313       1.1   blymn         menu->opts = opts;
    314       1.1   blymn 
    315      1.12   blymn 	  /*
    316      1.12   blymn 	   * If the radio option is selected then make sure only one
    317      1.12   blymn 	   * item is actually selected in the items.
    318      1.12   blymn 	   */
    319      1.12   blymn 	if (((opts & O_RADIO) == O_RADIO) && (menu->items != NULL) &&
    320      1.12   blymn 	    (menu->items[0] != NULL)) {
    321      1.12   blymn 		seen = 0;
    322      1.12   blymn 		for (i = 0; i < menu->item_count; i++) {
    323      1.12   blymn 			if (menu->items[i]->selected == 1) {
    324      1.12   blymn 				if (seen == 0) {
    325      1.12   blymn 					seen = 1;
    326      1.12   blymn 				} else {
    327      1.12   blymn 					menu->items[i]->selected = 0;
    328      1.12   blymn 				}
    329      1.12   blymn 			}
    330      1.12   blymn 		}
    331      1.12   blymn 
    332      1.12   blymn 		  /* if none selected, select the first item */
    333      1.12   blymn 		if (seen == 0)
    334      1.12   blymn 			menu->items[0]->selected = 1;
    335      1.12   blymn 	}
    336      1.12   blymn 
    337       1.1   blymn  	if ((menu->opts & O_ROWMAJOR) != (old_opts &  O_ROWMAJOR))
    338       1.1   blymn 		  /* changed menu layout - need to recalc neighbours */
    339       1.4   blymn 		_menui_stitch_items(menu);
    340       1.1   blymn 
    341       1.1   blymn         return E_OK;
    342       1.1   blymn }
    343       1.1   blymn 
    344       1.1   blymn /*
    345       1.1   blymn  * Turn on the options in menu given by opts.
    346       1.1   blymn  */
    347       1.1   blymn int
    348       1.6   blymn menu_opts_on(MENU *param_menu, OPTIONS opts)
    349       1.1   blymn {
    350      1.12   blymn 	int i, seen;
    351       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    352       1.1   blymn 	OPTIONS old_opts = menu->opts;
    353       1.1   blymn 
    354       1.1   blymn         menu->opts |= opts;
    355       1.1   blymn 
    356      1.12   blymn 	  /*
    357      1.12   blymn 	   * If the radio option is selected then make sure only one
    358      1.12   blymn 	   * item is actually selected in the items.
    359      1.12   blymn 	   */
    360      1.12   blymn 	if (((opts & O_RADIO) == O_RADIO) && (menu->items != NULL) &&
    361      1.12   blymn 	    (menu->items[0] != NULL)) {
    362      1.12   blymn 		seen = 0;
    363      1.12   blymn 		for (i = 0; i < menu->item_count; i++) {
    364      1.12   blymn 			if (menu->items[i]->selected == 1) {
    365      1.12   blymn 				if (seen == 0) {
    366      1.12   blymn 					seen = 1;
    367      1.12   blymn 				} else {
    368      1.12   blymn 					menu->items[i]->selected = 0;
    369      1.12   blymn 				}
    370      1.12   blymn 			}
    371      1.12   blymn 		}
    372      1.12   blymn 		  /* if none selected then select the top item */
    373      1.12   blymn 		if (seen == 0)
    374      1.12   blymn 			menu->items[0]->selected = 1;
    375      1.12   blymn 	}
    376      1.12   blymn 
    377       1.1   blymn 	if ((menu->items != NULL) &&
    378       1.1   blymn 	    (menu->opts & O_ROWMAJOR) != (old_opts &  O_ROWMAJOR))
    379       1.1   blymn 		  /* changed menu layout - need to recalc neighbours */
    380       1.4   blymn 		_menui_stitch_items(menu);
    381       1.1   blymn 
    382       1.1   blymn         return E_OK;
    383       1.1   blymn }
    384       1.1   blymn 
    385       1.1   blymn /*
    386       1.1   blymn  * Turn off the menu options given in opts.
    387       1.1   blymn  */
    388       1.1   blymn int
    389       1.6   blymn menu_opts_off(MENU *param_menu, OPTIONS opts)
    390       1.1   blymn {
    391       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    392       1.1   blymn 	OPTIONS old_opts = menu->opts;
    393       1.1   blymn 
    394       1.1   blymn         menu->opts &= ~(opts);
    395       1.1   blymn 
    396       1.1   blymn 	if ((menu->items != NULL ) &&
    397       1.1   blymn 	    (menu->opts & O_ROWMAJOR) != (old_opts &  O_ROWMAJOR))
    398       1.1   blymn 		  /* changed menu layout - need to recalc neighbours */
    399       1.4   blymn 		_menui_stitch_items(menu);
    400       1.1   blymn 
    401       1.1   blymn         return E_OK;
    402       1.1   blymn }
    403       1.1   blymn 
    404       1.1   blymn /*
    405       1.1   blymn  * Return the menu pattern buffer.
    406       1.1   blymn  */
    407       1.1   blymn char *
    408       1.6   blymn menu_pattern(MENU *menu)
    409       1.1   blymn {
    410       1.1   blymn 	if (menu == NULL)
    411       1.1   blymn 		return _menui_default_menu.pattern;
    412       1.1   blymn 	else
    413       1.1   blymn 		return menu->pattern;
    414       1.1   blymn }
    415       1.1   blymn 
    416       1.1   blymn /*
    417       1.1   blymn  * Set the menu pattern buffer to pat and attempt to match the pattern in
    418       1.1   blymn  * the item list.
    419       1.1   blymn  */
    420       1.1   blymn int
    421       1.6   blymn set_menu_pattern(MENU *param_menu, char *pat)
    422       1.1   blymn {
    423       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    424       1.1   blymn 	char *p = pat;
    425       1.1   blymn 
    426       1.1   blymn 	  /* check pattern is all printable characters */
    427       1.1   blymn 	while (*p)
    428       1.9   itohy 		if (!isprint((unsigned char) *p++)) return E_BAD_ARGUMENT;
    429       1.1   blymn 
    430       1.1   blymn         if ((menu->pattern = (char *) realloc(menu->pattern,
    431      1.14   blymn                                      sizeof(char) * strlen(pat) + 1)) == NULL)
    432       1.1   blymn                 return E_SYSTEM_ERROR;
    433       1.1   blymn 
    434       1.1   blymn         strcpy(menu->pattern, pat);
    435       1.1   blymn 	menu->plen = strlen(pat);
    436       1.1   blymn 
    437       1.1   blymn           /* search item list for pat here */
    438       1.4   blymn 	return _menui_match_items(menu, MATCH_FORWARD, &menu->cur_item);
    439       1.1   blymn }
    440       1.1   blymn 
    441       1.1   blymn /*
    442       1.1   blymn  * Allocate a new menu structure and fill it in.
    443       1.1   blymn  */
    444       1.1   blymn MENU *
    445       1.6   blymn new_menu(ITEM **items)
    446       1.1   blymn {
    447       1.1   blymn         MENU *the_menu;
    448  1.17.2.1     tls         char mark[2];
    449  1.17.2.1     tls 
    450       1.1   blymn         if ((the_menu = (MENU *)malloc(sizeof(MENU))) == NULL)
    451       1.1   blymn                 return NULL;
    452       1.1   blymn 
    453       1.1   blymn           /* copy the defaults */
    454       1.3  kleink 	(void)memcpy(the_menu, &_menui_default_menu, sizeof(MENU));
    455       1.1   blymn 
    456       1.1   blymn 	  /* set a default window if none already set. */
    457       1.1   blymn 	if (the_menu->menu_win == NULL)
    458      1.11   blymn 		the_menu->scrwin = stdscr;
    459      1.13   blymn 
    460      1.13   blymn 	  /* make a private copy of the mark string */
    461      1.13   blymn 	if (_menui_default_menu.mark.string != NULL) {
    462      1.13   blymn 		if ((the_menu->mark.string =
    463      1.13   blymn 		     (char *) malloc((unsigned) _menui_default_menu.mark.length + 1))
    464      1.13   blymn 		    == NULL) {
    465      1.13   blymn 			free(the_menu);
    466      1.13   blymn 			return NULL;
    467      1.13   blymn 		}
    468      1.13   blymn 
    469      1.13   blymn 		strlcpy(the_menu->mark.string, _menui_default_menu.mark.string,
    470      1.14   blymn 			(unsigned) _menui_default_menu.mark.length + 1);
    471      1.13   blymn 	}
    472       1.1   blymn 
    473      1.13   blymn 	  /* make a private copy of the unmark string too */
    474      1.13   blymn 	if (_menui_default_menu.unmark.string != NULL) {
    475      1.13   blymn 		if ((the_menu->unmark.string =
    476      1.13   blymn 		     (char *) malloc((unsigned) _menui_default_menu.unmark.length + 1))
    477      1.13   blymn 		    == NULL) {
    478      1.13   blymn 			free(the_menu);
    479      1.13   blymn 			return NULL;
    480      1.13   blymn 		}
    481      1.13   blymn 
    482      1.13   blymn 		strlcpy(the_menu->unmark.string,
    483      1.13   blymn 			_menui_default_menu.unmark.string,
    484      1.14   blymn 			(unsigned) _menui_default_menu.unmark.length+ 1 );
    485      1.13   blymn 	}
    486      1.13   blymn 
    487  1.17.2.1     tls 	/* default mark needs to be set */
    488  1.17.2.1     tls 	mark[0] = '-';
    489  1.17.2.1     tls 	mark[1] = '\0';
    490  1.17.2.1     tls 
    491  1.17.2.1     tls 	set_menu_mark(the_menu, mark);
    492  1.17.2.1     tls 
    493       1.1   blymn           /* now attach the items, if any */
    494       1.1   blymn         if (items != NULL) {
    495       1.1   blymn 		if(set_menu_items(the_menu, items) < 0) {
    496      1.13   blymn 			if (the_menu->mark.string != NULL)
    497      1.13   blymn 				free(the_menu->mark.string);
    498      1.13   blymn 			if (the_menu->unmark.string != NULL)
    499      1.13   blymn 				free(the_menu->unmark.string);
    500       1.1   blymn 			free(the_menu);
    501       1.1   blymn 			return NULL;
    502       1.1   blymn 		}
    503       1.1   blymn 	}
    504       1.1   blymn 
    505       1.1   blymn 	return the_menu;
    506       1.1   blymn }
    507       1.1   blymn 
    508       1.1   blymn /*
    509       1.1   blymn  * Free up storage allocated to the menu object and destroy it.
    510       1.1   blymn  */
    511       1.1   blymn int
    512       1.6   blymn free_menu(MENU *menu)
    513       1.1   blymn {
    514       1.1   blymn 	int i;
    515       1.1   blymn 
    516       1.1   blymn 	if (menu == NULL)
    517       1.1   blymn 		return E_BAD_ARGUMENT;
    518       1.1   blymn 
    519       1.1   blymn 	if (menu->posted != 0)
    520       1.1   blymn 		return E_POSTED;
    521       1.1   blymn 
    522       1.1   blymn 	if (menu->pattern != NULL)
    523       1.1   blymn 		free(menu->pattern);
    524       1.1   blymn 
    525       1.1   blymn 	if (menu->mark.string != NULL)
    526       1.1   blymn 		free(menu->mark.string);
    527       1.1   blymn 
    528       1.1   blymn 	if (menu->items != NULL) {
    529       1.1   blymn 		  /* disconnect the items from this menu */
    530       1.1   blymn 		for (i = 0; i < menu->item_count; i++) {
    531       1.1   blymn 			menu->items[i]->parent = NULL;
    532       1.1   blymn 		}
    533       1.1   blymn 	}
    534       1.1   blymn 
    535       1.1   blymn 	free(menu);
    536       1.1   blymn 	return E_OK;
    537       1.1   blymn }
    538       1.1   blymn 
    539       1.1   blymn /*
    540       1.1   blymn  * Calculate the minimum window size for the menu.
    541       1.1   blymn  */
    542       1.1   blymn int
    543       1.6   blymn scale_menu(MENU *param_menu, int *rows, int *cols)
    544       1.1   blymn {
    545       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    546       1.1   blymn 
    547       1.1   blymn 	if (menu->items == NULL)
    548       1.1   blymn 		return E_BAD_ARGUMENT;
    549       1.1   blymn 
    550       1.1   blymn 	  /* calculate the max item size */
    551       1.4   blymn 	_menui_max_item_size(menu);
    552       1.1   blymn 
    553       1.1   blymn 	*rows = menu->rows;
    554       1.1   blymn 	*cols = menu->cols * menu->max_item_width;
    555       1.1   blymn 
    556       1.1   blymn 	  /*
    557       1.1   blymn 	   * allow for spacing between columns...
    558       1.1   blymn 	   */
    559      1.15   blymn 	*cols += (menu->cols - 1);
    560       1.1   blymn 
    561       1.1   blymn 	return E_OK;
    562       1.1   blymn }
    563       1.1   blymn 
    564       1.1   blymn /*
    565       1.1   blymn  * Set the menu item list to the one given.
    566       1.1   blymn  */
    567       1.1   blymn int
    568       1.6   blymn set_menu_items(MENU *param_menu, ITEM **items)
    569       1.1   blymn {
    570       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    571      1.12   blymn 	int i, new_count = 0, sel_count = 0;
    572       1.1   blymn 
    573       1.1   blymn 	  /* don't change if menu is posted */
    574       1.1   blymn 	if (menu->posted == 1)
    575       1.1   blymn 		return E_POSTED;
    576       1.1   blymn 
    577       1.1   blymn 	  /* count the new items and validate none are connected already */
    578       1.1   blymn 	while (items[new_count] != NULL) {
    579       1.1   blymn 		if ((items[new_count]->parent != NULL) &&
    580       1.1   blymn 		    (items[new_count]->parent != menu))
    581       1.1   blymn 			return E_CONNECTED;
    582      1.12   blymn 		if (items[new_count]->selected == 1)
    583      1.12   blymn 			sel_count++;
    584       1.1   blymn 		new_count++;
    585       1.1   blymn 	}
    586      1.12   blymn 
    587      1.12   blymn 	  /*
    588      1.12   blymn 	   * don't allow multiple selected items if menu is radio
    589      1.12   blymn 	   * button style.
    590      1.12   blymn 	   */
    591      1.12   blymn 	if (((menu->opts & O_RADIO) == O_RADIO) &&
    592      1.12   blymn 	    (sel_count > 1))
    593      1.12   blymn 		return E_BAD_ARGUMENT;
    594       1.1   blymn 
    595       1.1   blymn 	  /* if there were items connected then disconnect them. */
    596       1.1   blymn 	if (menu->items != NULL) {
    597       1.1   blymn 		for (i = 0; i < menu->item_count; i++) {
    598       1.1   blymn 			menu->items[i]->parent = NULL;
    599       1.1   blymn 			menu->items[i]->index = -1;
    600       1.1   blymn 		}
    601       1.1   blymn 	}
    602       1.1   blymn 
    603       1.1   blymn 	menu->item_count = new_count;
    604       1.1   blymn 
    605       1.1   blymn 	  /* connect the new items to the menu */
    606       1.1   blymn 	for (i = 0; i < new_count; i++) {
    607       1.1   blymn 		items[i]->parent = menu;
    608       1.1   blymn 		items[i]->index = i;
    609       1.1   blymn 	}
    610       1.1   blymn 
    611       1.1   blymn 	menu->items = items;
    612       1.1   blymn 	menu->cur_item = 0; /* reset current item just in case */
    613       1.1   blymn 	menu->top_row = 0; /* and the top row too */
    614       1.1   blymn 	if (menu->pattern != NULL) { /* and the pattern buffer....sigh */
    615       1.1   blymn 		free(menu->pattern);
    616       1.1   blymn 		menu->plen = 0;
    617       1.1   blymn 		menu->match_len = 0;
    618       1.1   blymn 	}
    619      1.12   blymn 
    620      1.12   blymn 	  /*
    621      1.12   blymn 	   * make sure at least one item is selected on a radio
    622      1.12   blymn 	   * button style menu.
    623      1.12   blymn 	   */
    624      1.12   blymn 	if (((menu->opts & O_RADIO) == O_RADIO) && (sel_count == 0))
    625      1.12   blymn 		menu->items[0]->selected = 1;
    626      1.12   blymn 
    627       1.1   blymn 
    628       1.4   blymn 	_menui_stitch_items(menu); /* recalculate the item neighbours */
    629       1.1   blymn 
    630       1.1   blymn 	return E_OK;
    631       1.1   blymn }
    632       1.1   blymn 
    633       1.1   blymn /*
    634       1.1   blymn  * Return the pointer to the menu items array.
    635       1.1   blymn  */
    636       1.1   blymn ITEM **
    637       1.6   blymn menu_items(MENU *menu)
    638       1.1   blymn {
    639       1.1   blymn 	if (menu == NULL)
    640       1.1   blymn 		return _menui_default_menu.items;
    641       1.1   blymn 	else
    642       1.1   blymn 		return menu->items;
    643       1.1   blymn }
    644       1.1   blymn 
    645       1.1   blymn /*
    646       1.1   blymn  * Return the count of items connected to the menu
    647       1.1   blymn  */
    648       1.1   blymn int
    649       1.6   blymn item_count(MENU *menu)
    650       1.1   blymn {
    651       1.1   blymn 	if (menu == NULL)
    652       1.1   blymn 		return _menui_default_menu.item_count;
    653       1.1   blymn 	else
    654       1.1   blymn 		return menu->item_count;
    655       1.1   blymn }
    656       1.1   blymn 
    657       1.1   blymn /*
    658       1.1   blymn  * Set the menu top row to be the given row.  The current item becomes the
    659       1.1   blymn  * leftmost item on that row in the menu.
    660       1.1   blymn  */
    661       1.1   blymn int
    662       1.6   blymn set_top_row(MENU *param_menu, int row)
    663       1.1   blymn {
    664       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    665       1.1   blymn 	int i, cur_item, state = E_SYSTEM_ERROR;
    666       1.1   blymn 
    667       1.1   blymn 	if (row > menu->item_rows)
    668       1.1   blymn 		return E_BAD_ARGUMENT;
    669       1.1   blymn 
    670       1.1   blymn 	if (menu->items == NULL)
    671       1.1   blymn 		return E_NOT_CONNECTED;
    672       1.1   blymn 
    673       1.1   blymn 	if (menu->in_init == 1)
    674       1.1   blymn 		return E_BAD_STATE;
    675       1.1   blymn 
    676       1.8   blymn 	cur_item = 0;
    677       1.8   blymn 
    678       1.1   blymn 	for (i = 0; i < menu->item_count; i++) {
    679       1.1   blymn 		  /* search for first item that matches row - this will be
    680       1.1   blymn 		     the current item. */
    681       1.1   blymn 		if (row == menu->items[i]->row) {
    682       1.1   blymn 			cur_item = i;
    683       1.1   blymn 			state = E_OK;
    684       1.1   blymn 			break; /* found what we want - no need to go further */
    685       1.1   blymn 		}
    686       1.1   blymn 	}
    687       1.1   blymn 
    688       1.1   blymn 	menu->in_init = 1; /* just in case we call the init/term routines */
    689       1.1   blymn 
    690       1.1   blymn 	if (menu->posted == 1) {
    691       1.1   blymn 		if (menu->menu_term != NULL)
    692       1.1   blymn 			menu->menu_term(menu);
    693       1.1   blymn 		if (menu->item_term != NULL)
    694       1.1   blymn 			menu->item_term(menu);
    695       1.1   blymn 	}
    696       1.1   blymn 
    697       1.1   blymn 	menu->cur_item = cur_item;
    698       1.1   blymn 	menu->top_row = row;
    699       1.1   blymn 
    700       1.1   blymn 	if (menu->posted == 1) {
    701       1.1   blymn 		if (menu->menu_init != NULL)
    702       1.1   blymn 			menu->menu_init(menu);
    703       1.1   blymn 		if (menu->item_init != NULL)
    704       1.1   blymn 			menu->item_init(menu);
    705       1.1   blymn 	}
    706       1.1   blymn 
    707       1.1   blymn 	menu->in_init = 0;
    708       1.1   blymn 
    709       1.1   blymn 	  /* this should always be E_OK unless we are really screwed up */
    710       1.1   blymn 	return state;
    711       1.1   blymn }
    712       1.1   blymn 
    713       1.1   blymn /*
    714       1.1   blymn  * Return the current top row number.
    715       1.1   blymn  */
    716       1.1   blymn int
    717       1.6   blymn top_row(MENU *param_menu)
    718       1.1   blymn {
    719       1.1   blymn 	MENU *menu = (param_menu != NULL) ? param_menu : &_menui_default_menu;
    720       1.1   blymn 
    721       1.1   blymn 	if (menu->items == NULL)
    722       1.1   blymn 		return E_NOT_CONNECTED;
    723       1.1   blymn 
    724       1.1   blymn 	return menu->top_row;
    725       1.1   blymn }
    726       1.1   blymn 
    727       1.1   blymn /*
    728       1.1   blymn  * Position the cursor at the correct place in the menu.
    729       1.1   blymn  *
    730       1.1   blymn  */
    731       1.1   blymn int
    732       1.6   blymn pos_menu_cursor(MENU *menu)
    733       1.1   blymn {
    734       1.1   blymn 	int movx, maxmark;
    735       1.1   blymn 
    736       1.1   blymn 	if (menu == NULL)
    737       1.1   blymn 		return E_BAD_ARGUMENT;
    738       1.1   blymn 
    739       1.1   blymn 	maxmark = max(menu->mark.length, menu->unmark.length);
    740       1.1   blymn 	movx = maxmark + (menu->items[menu->cur_item]->col
    741      1.15   blymn 		* (menu->col_width + 1));
    742       1.1   blymn 
    743       1.1   blymn 	if (menu->match_len > 0)
    744       1.1   blymn 		movx += menu->match_len - 1;
    745       1.1   blymn 
    746      1.11   blymn 	wmove(menu->scrwin,
    747       1.1   blymn 	      menu->items[menu->cur_item]->row - menu->top_row, movx);
    748       1.1   blymn 
    749       1.1   blymn 	return E_OK;
    750       1.1   blymn }
    751