Home | History | Annotate | Line # | Download | only in libmenu
internals.c revision 1.15.2.2
      1  1.15.2.2       tls /*	$NetBSD: internals.c,v 1.15.2.2 2014/08/20 00:02:18 tls Exp $	*/
      2       1.1     blymn 
      3       1.1     blymn /*-
      4       1.3     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.8       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.12     lukem 
     29      1.12     lukem #include <sys/cdefs.h>
     30  1.15.2.2       tls __RCSID("$NetBSD: internals.c,v 1.15.2.2 2014/08/20 00:02:18 tls Exp $");
     31       1.1     blymn 
     32       1.1     blymn #include <menu.h>
     33       1.1     blymn #include <ctype.h>
     34       1.1     blymn #include <stdlib.h>
     35       1.1     blymn #include <string.h>
     36       1.1     blymn #include "internals.h"
     37       1.1     blymn 
     38       1.1     blymn /* internal function prototypes */
     39       1.1     blymn static void
     40      1.15     blymn _menui_calc_neighbours(MENU *menu, int item_no);
     41       1.6     blymn static void _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item);
     42       1.1     blymn 
     43       1.1     blymn   /*
     44       1.1     blymn    * Link all the menu items together to speed up navigation.  We need
     45       1.1     blymn    * to calculate the widest item entry, then work out how many columns
     46      1.13  christos    * of items the window will accommodate and then how many rows there will
     47       1.1     blymn    * be.  Once the layout is determined the neighbours of each item is
     48       1.1     blymn    * calculated and the item structures updated.
     49       1.1     blymn    */
     50       1.1     blymn int
     51       1.6     blymn _menui_stitch_items(MENU *menu)
     52       1.1     blymn {
     53      1.15     blymn 	int i, row_major;
     54       1.1     blymn 
     55       1.1     blymn 	row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
     56       1.1     blymn 
     57       1.1     blymn 	if (menu->posted == 1)
     58       1.1     blymn 		return E_POSTED;
     59       1.1     blymn 	if (menu->items == NULL)
     60       1.1     blymn 		return E_BAD_ARGUMENT;
     61       1.1     blymn 
     62      1.15     blymn 	menu->item_rows = menu->item_count / menu->cols;
     63      1.15     blymn 	menu->item_cols = menu->cols;
     64      1.15     blymn 	if (menu->item_count > (menu->item_rows * menu->item_cols))
     65      1.15     blymn 		menu->item_rows += 1;
     66       1.1     blymn 
     67       1.3     blymn 	_menui_max_item_size(menu);
     68       1.1     blymn 
     69       1.1     blymn 	for (i = 0; i < menu->item_count; i++) {
     70       1.1     blymn 		  /* fill in the row and column value of the item */
     71       1.1     blymn 		if (row_major) {
     72       1.1     blymn 			menu->items[i]->row = i / menu->item_cols;
     73       1.1     blymn 			menu->items[i]->col = i % menu->item_cols;
     74       1.1     blymn 		} else {
     75       1.1     blymn 			menu->items[i]->row = i % menu->item_rows;
     76       1.1     blymn 			menu->items[i]->col = i / menu->item_rows;
     77       1.1     blymn 		}
     78      1.15     blymn 
     79      1.15     blymn 		_menui_calc_neighbours(menu, i);
     80       1.1     blymn 	}
     81      1.14     blymn 
     82       1.1     blymn 	return E_OK;
     83       1.1     blymn }
     84       1.1     blymn 
     85       1.1     blymn   /*
     86      1.15     blymn    * Calculate the neighbours for an item in menu.
     87       1.1     blymn    */
     88       1.1     blymn static void
     89      1.15     blymn _menui_calc_neighbours(MENU *menu, int item_no)
     90       1.1     blymn {
     91      1.15     blymn 	int neighbour, cycle, row_major, edge;
     92      1.15     blymn 	ITEM *item;
     93      1.15     blymn 
     94      1.15     blymn 	row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
     95      1.15     blymn 	cycle = ((menu->opts & O_NONCYCLIC) != O_NONCYCLIC);
     96      1.15     blymn 	item = menu->items[item_no];
     97       1.1     blymn 
     98      1.15     blymn 	if (menu->item_rows < 2) {
     99       1.1     blymn 		if (cycle) {
    100      1.15     blymn 			item->up = item;
    101      1.15     blymn 			item->down = item;
    102       1.1     blymn 		} else {
    103      1.15     blymn 			item->up = NULL;
    104      1.15     blymn 			item->down = NULL;
    105       1.1     blymn 		}
    106       1.1     blymn 	} else {
    107      1.15     blymn 
    108      1.15     blymn 		/* up */
    109      1.15     blymn 		if (menu->item_cols < 2) {
    110      1.15     blymn 			if (item_no == 0) {
    111      1.15     blymn 				if (cycle)
    112      1.15     blymn 					item->up =
    113      1.15     blymn 					    menu->items[menu->item_count - 1];
    114      1.15     blymn 				else
    115      1.15     blymn 					item->up = NULL;
    116       1.1     blymn 			} else
    117      1.15     blymn 				item->up = menu->items[item_no - 1];
    118      1.15     blymn 		} else {
    119      1.15     blymn 			edge = 0;
    120      1.15     blymn 			if (row_major) {
    121      1.15     blymn 				if (item->row == 0) {
    122      1.15     blymn 					neighbour =
    123      1.15     blymn 				    	(menu->item_rows - 1) * menu->item_cols
    124      1.15     blymn 						+ item->col;
    125      1.15     blymn 					if (neighbour >= menu->item_count)
    126      1.15     blymn 						neighbour -= menu->item_cols;
    127      1.15     blymn 					edge = 1;
    128      1.15     blymn 				} else
    129      1.15     blymn 					neighbour = item_no - menu->item_cols;
    130      1.15     blymn 			} else {
    131      1.15     blymn 				if (item->row == 0) {
    132      1.15     blymn 					neighbour = menu->item_rows * item->col
    133      1.15     blymn 						+ menu->item_rows - 1;
    134      1.15     blymn 					if (neighbour >= menu->item_count)
    135      1.15     blymn 						neighbour = menu->item_count - 1;
    136      1.15     blymn 					edge = 1;
    137      1.15     blymn 				} else
    138      1.15     blymn 					neighbour = item_no - 1;
    139      1.15     blymn 			}
    140      1.15     blymn 
    141      1.14     blymn 
    142      1.15     blymn 			item->up = menu->items[neighbour];
    143      1.15     blymn 			if ((!cycle) && (edge == 1))
    144      1.15     blymn 				item->up = NULL;
    145      1.15     blymn 		}
    146      1.14     blymn 
    147      1.15     blymn 		/* Down */
    148      1.15     blymn 		if (menu->item_cols < 2) {
    149      1.15     blymn 			if (item_no == (menu->item_count - 1)) {
    150      1.15     blymn 				if (cycle)
    151      1.15     blymn 					item->down = menu->items[0];
    152      1.15     blymn 				else
    153      1.15     blymn 					item->down = NULL;
    154      1.15     blymn 			} else
    155      1.15     blymn 				item->down = menu->items[item_no + 1];
    156      1.15     blymn 		} else {
    157      1.15     blymn 			edge = 0;
    158      1.15     blymn 			if (row_major) {
    159      1.15     blymn 				if (item->row == menu->item_rows - 1) {
    160      1.15     blymn 					neighbour = item->col;
    161      1.15     blymn 					edge = 1;
    162      1.15     blymn 				} else {
    163      1.15     blymn 					neighbour = item_no + menu->item_cols;
    164      1.15     blymn 					if (neighbour >= menu->item_count) {
    165      1.15     blymn 						neighbour = item->col;
    166      1.15     blymn 						edge = 1;
    167      1.15     blymn 					}
    168      1.15     blymn 				}
    169      1.15     blymn 			} else {
    170      1.15     blymn 				if (item->row == menu->item_rows - 1) {
    171      1.15     blymn 					neighbour = item->col * menu->item_rows;
    172      1.15     blymn 					edge = 1;
    173       1.1     blymn 				} else {
    174      1.15     blymn 					neighbour = item_no + 1;
    175      1.15     blymn 					if (neighbour >= menu->item_count) {
    176      1.15     blymn 						neighbour = item->col
    177      1.15     blymn 						    * menu->item_rows;
    178      1.15     blymn 						edge = 1;
    179      1.15     blymn 					}
    180      1.15     blymn 				}
    181      1.15     blymn 			}
    182       1.1     blymn 
    183      1.15     blymn 			item->down = menu->items[neighbour];
    184      1.15     blymn 			if ((!cycle) && (edge == 1))
    185      1.15     blymn 				item->down = NULL;
    186      1.15     blymn 		}
    187       1.1     blymn 	}
    188      1.14     blymn 
    189      1.15     blymn 	if (menu->item_cols < 2) {
    190       1.1     blymn 		if (cycle) {
    191      1.15     blymn 			item->left = item;
    192      1.15     blymn 			item->right = item;
    193      1.15     blymn 		} else {
    194      1.15     blymn 			item->left = NULL;
    195      1.15     blymn 			item->right = NULL;
    196      1.15     blymn 		}
    197      1.15     blymn 	} else {
    198      1.15     blymn 		/* left */
    199      1.15     blymn 		if (menu->item_rows < 2) {
    200      1.15     blymn 			if (item_no == 0) {
    201      1.15     blymn 				if (cycle)
    202      1.15     blymn 					item->left =
    203      1.15     blymn 					    menu->items[menu->item_count - 1];
    204      1.15     blymn 				else
    205      1.15     blymn 					item->left = NULL;
    206      1.15     blymn 			} else
    207      1.15     blymn 				item->left = menu->items[item_no - 1];
    208      1.15     blymn 		} else {
    209      1.15     blymn 			edge = 0;
    210      1.15     blymn 			if (row_major) {
    211      1.15     blymn 				if (item->col == 0) {
    212      1.15     blymn 					neighbour = item_no + menu->cols - 1;
    213      1.15     blymn 					if (neighbour >= menu->item_count)
    214      1.15     blymn 						neighbour = menu->item_count - 1;
    215      1.15     blymn 					edge = 1;
    216      1.15     blymn 				} else
    217      1.15     blymn 					neighbour = item_no - 1;
    218       1.1     blymn 			} else {
    219      1.15     blymn 				if (item->col == 0) {
    220      1.15     blymn 					neighbour = menu->item_rows
    221      1.15     blymn 					    * (menu->item_cols - 1) + item->row;
    222      1.15     blymn 					if (neighbour >= menu->item_count)
    223      1.15     blymn 						neighbour -= menu->item_rows;
    224      1.15     blymn 					edge = 1;
    225       1.1     blymn 				} else
    226      1.15     blymn 					neighbour = item_no - menu->item_rows;
    227       1.1     blymn 			}
    228      1.14     blymn 
    229      1.15     blymn 			item->left = menu->items[neighbour];
    230      1.15     blymn 			if ((!cycle) && (edge == 1))
    231      1.15     blymn 				item->left = NULL;
    232      1.15     blymn 		}
    233      1.15     blymn 
    234      1.15     blymn 		/* right */
    235      1.15     blymn 		if (menu->item_rows < 2) {
    236      1.15     blymn 			if (item_no == menu->item_count - 1) {
    237      1.15     blymn 				if (cycle)
    238      1.15     blymn 					item->right = menu->items[0];
    239      1.15     blymn 				else
    240      1.15     blymn 					item->right = NULL;
    241      1.15     blymn 			} else
    242      1.15     blymn 				item->right = menu->items[item_no + 1];
    243      1.15     blymn 		} else {
    244      1.15     blymn 			edge = 0;
    245      1.15     blymn 			if (row_major) {
    246      1.15     blymn 				if (item->col == menu->item_cols - 1) {
    247      1.15     blymn 					neighbour = item_no - menu->item_cols
    248      1.15     blymn 					    + 1;
    249      1.15     blymn 					edge = 1;
    250      1.15     blymn 				} else if (item_no == menu->item_count - 1) {
    251      1.15     blymn 					neighbour = item->row * menu->item_cols;
    252      1.15     blymn 					edge = 1;
    253      1.15     blymn 				} else
    254      1.15     blymn 					neighbour = item_no + 1;
    255       1.1     blymn 			} else {
    256      1.15     blymn 				if (item->col == menu->item_cols - 1) {
    257      1.15     blymn 					neighbour = item->row;
    258      1.15     blymn 					edge = 1;
    259      1.15     blymn 				} else {
    260      1.15     blymn 					neighbour = item_no + menu->item_rows;
    261      1.15     blymn 					if (neighbour >= menu->item_count) {
    262      1.15     blymn 						neighbour = item->row;
    263      1.15     blymn 						edge = 1;
    264       1.1     blymn 					}
    265      1.15     blymn 				}
    266       1.1     blymn 			}
    267      1.15     blymn 
    268      1.15     blymn 			item->right = menu->items[neighbour];
    269      1.15     blymn 			if ((!cycle) && (edge == 1))
    270      1.15     blymn 				item->right = NULL;
    271      1.15     blymn 		}
    272       1.1     blymn 	}
    273       1.1     blymn }
    274       1.1     blymn 
    275       1.1     blymn /*
    276       1.1     blymn  * Goto the item pointed to by item and adjust the menu structure
    277       1.1     blymn  * accordingly.  Call the term and init functions if required.
    278       1.1     blymn  */
    279       1.1     blymn int
    280       1.6     blymn _menui_goto_item(MENU *menu, ITEM *item, int new_top_row)
    281       1.1     blymn {
    282       1.1     blymn 	int old_top_row = menu->top_row, old_cur_item = menu->cur_item;
    283      1.14     blymn 
    284       1.1     blymn 	  /* If we get a null then the menu is not cyclic so deny request */
    285       1.1     blymn 	if (item == NULL)
    286       1.1     blymn 		return E_REQUEST_DENIED;
    287       1.1     blymn 
    288       1.1     blymn 	menu->in_init = 1;
    289       1.3     blymn 	if (menu->top_row != new_top_row) {
    290       1.1     blymn 		if ((menu->posted == 1) && (menu->menu_term != NULL))
    291       1.1     blymn 			menu->menu_term(menu);
    292       1.3     blymn 		menu->top_row = new_top_row;
    293       1.1     blymn 
    294       1.1     blymn 		if ((menu->posted == 1) && (menu->menu_init != NULL))
    295       1.1     blymn 			menu->menu_init(menu);
    296       1.1     blymn 	}
    297      1.14     blymn 
    298       1.1     blymn 	  /* this looks like wasted effort but it can happen.... */
    299       1.1     blymn 	if (menu->cur_item != item->index) {
    300       1.1     blymn 
    301       1.1     blymn 		if ((menu->posted == 1) && (menu->item_term != NULL))
    302       1.1     blymn 			menu->item_term(menu);
    303       1.1     blymn 
    304       1.1     blymn 		menu->cur_item = item->index;
    305       1.1     blymn 		menu->cur_row = item->row;
    306       1.1     blymn 		menu->cur_col = item->col;
    307       1.1     blymn 
    308       1.1     blymn 		if (menu->posted == 1)
    309       1.3     blymn 			_menui_redraw_menu(menu, old_top_row, old_cur_item);
    310      1.14     blymn 
    311       1.1     blymn 		if ((menu->posted == 1) && (menu->item_init != NULL))
    312       1.1     blymn 			menu->item_init(menu);
    313      1.14     blymn 
    314       1.1     blymn 	}
    315       1.1     blymn 
    316       1.1     blymn 	menu->in_init = 0;
    317       1.1     blymn 	return E_OK;
    318       1.1     blymn }
    319       1.1     blymn 
    320       1.1     blymn /*
    321       1.1     blymn  * Attempt to match items with the pattern buffer in the direction given
    322       1.1     blymn  * by iterating over the menu items.  If a match is found return E_OK
    323       1.1     blymn  * otherwise return E_NO_MATCH
    324       1.1     blymn  */
    325       1.1     blymn int
    326       1.6     blymn _menui_match_items(MENU *menu, int direction, int *item_matched)
    327       1.1     blymn {
    328       1.1     blymn 	int i, caseless;
    329       1.1     blymn 
    330       1.1     blymn 	caseless = ((menu->opts & O_IGNORECASE) == O_IGNORECASE);
    331      1.14     blymn 
    332       1.1     blymn 	i = menu->cur_item;
    333       1.1     blymn 	if (direction == MATCH_NEXT_FORWARD) {
    334       1.1     blymn 		if (++i >= menu->item_count) i = 0;
    335       1.1     blymn 	} else if (direction == MATCH_NEXT_REVERSE) {
    336       1.1     blymn 		if (--i < 0) i = menu->item_count - 1;
    337       1.1     blymn 	}
    338       1.1     blymn 
    339      1.14     blymn 
    340       1.1     blymn 	do {
    341       1.1     blymn 		if (menu->items[i]->name.length >= menu->plen) {
    342       1.1     blymn 			  /* no chance if pattern is longer */
    343       1.1     blymn 			if (caseless) {
    344       1.1     blymn 				if (strncasecmp(menu->items[i]->name.string,
    345       1.1     blymn 						menu->pattern,
    346       1.3     blymn 						(size_t) menu->plen) == 0) {
    347       1.1     blymn 					*item_matched = i;
    348       1.1     blymn 					menu->match_len = menu->plen;
    349       1.1     blymn 					return E_OK;
    350       1.1     blymn 				}
    351       1.1     blymn 			} else {
    352       1.1     blymn 				if (strncmp(menu->items[i]->name.string,
    353       1.3     blymn 					    menu->pattern,
    354       1.3     blymn 					    (size_t) menu->plen) == 0) {
    355       1.1     blymn 					*item_matched = i;
    356       1.1     blymn 					menu->match_len = menu->plen;
    357       1.1     blymn 					return E_OK;
    358       1.1     blymn 				}
    359       1.1     blymn 			}
    360       1.1     blymn 		}
    361      1.14     blymn 
    362       1.1     blymn 		if ((direction == MATCH_FORWARD) ||
    363       1.1     blymn 		    (direction == MATCH_NEXT_FORWARD)) {
    364       1.1     blymn 			if (++i >= menu->item_count) i = 0;
    365       1.1     blymn 		} else {
    366       1.1     blymn 			if (--i <= 0) i = menu->item_count - 1;
    367       1.1     blymn 		}
    368       1.1     blymn 	} while (i != menu->cur_item);
    369       1.1     blymn 
    370       1.1     blymn 	menu->match_len = 0; /* match did not succeed - kill the match len. */
    371       1.1     blymn 	return E_NO_MATCH;
    372       1.1     blymn }
    373       1.1     blymn 
    374       1.1     blymn /*
    375      1.14     blymn  * Attempt to match the pattern buffer against the items.  If c is a
    376       1.1     blymn  * printable character then add it to the pattern buffer prior to
    377       1.1     blymn  * performing the match.  Direction determines the direction of matching.
    378       1.1     blymn  * If the match is successful update the item_matched variable with the
    379       1.1     blymn  * index of the item that matched the pattern.
    380       1.1     blymn  */
    381       1.1     blymn int
    382       1.7     itohy _menui_match_pattern(MENU *menu, int c, int direction, int *item_matched)
    383       1.1     blymn {
    384       1.1     blymn 	if (menu == NULL)
    385       1.1     blymn 		return E_BAD_ARGUMENT;
    386       1.1     blymn 	if (menu->items == NULL)
    387       1.1     blymn 		return E_BAD_ARGUMENT;
    388       1.1     blymn 	if (*menu->items == NULL)
    389       1.1     blymn 		return E_BAD_ARGUMENT;
    390       1.1     blymn 
    391       1.1     blymn 	if (isprint(c)) {
    392       1.1     blymn 		  /* add char to buffer - first allocate room for it */
    393       1.1     blymn 		if ((menu->pattern = (char *)
    394       1.1     blymn 		     realloc(menu->pattern,
    395       1.1     blymn 			     menu->plen + sizeof(char) +
    396       1.1     blymn 			     ((menu->plen > 0)? 0 : 1)))
    397       1.1     blymn 		    == NULL)
    398       1.1     blymn 			return E_SYSTEM_ERROR;
    399       1.1     blymn 		menu->pattern[menu->plen] = c;
    400       1.1     blymn 		menu->pattern[++menu->plen] = '\0';
    401       1.1     blymn 
    402       1.1     blymn 		  /* there is no chance of a match if pattern is longer
    403       1.1     blymn 		     than all the items */
    404       1.1     blymn 		if (menu->plen >= menu->max_item_width) {
    405       1.1     blymn 			menu->pattern[--menu->plen] = '\0';
    406       1.1     blymn 			return E_NO_MATCH;
    407       1.1     blymn 		}
    408      1.14     blymn 
    409       1.3     blymn 		if (_menui_match_items(menu, direction,
    410       1.1     blymn 					item_matched) == E_NO_MATCH) {
    411       1.1     blymn 			menu->pattern[--menu->plen] = '\0';
    412       1.1     blymn 			return E_NO_MATCH;
    413       1.1     blymn 		} else
    414       1.1     blymn 			return E_OK;
    415       1.1     blymn 	} else {
    416       1.3     blymn 		if (_menui_match_items(menu, direction,
    417       1.1     blymn 					item_matched) == E_OK) {
    418       1.1     blymn 			return E_OK;
    419       1.1     blymn 		} else {
    420       1.1     blymn 			return E_NO_MATCH;
    421       1.1     blymn 		}
    422       1.1     blymn 	}
    423       1.1     blymn }
    424       1.1     blymn 
    425       1.1     blymn /*
    426       1.1     blymn  * Draw an item in the subwindow complete with appropriate highlighting.
    427       1.1     blymn  */
    428       1.1     blymn void
    429       1.6     blymn _menui_draw_item(MENU *menu, int item)
    430       1.1     blymn {
    431       1.1     blymn 	int j, pad_len, mark_len;
    432      1.14     blymn 
    433       1.1     blymn 	mark_len = max(menu->mark.length, menu->unmark.length);
    434      1.14     blymn 
    435       1.9     blymn 	wmove(menu->scrwin,
    436       1.1     blymn 	      menu->items[item]->row - menu->top_row,
    437      1.11     blymn 	      menu->items[item]->col * (menu->col_width + 1));
    438       1.6     blymn 
    439      1.10     blymn 	if (menu->cur_item == item)
    440       1.9     blymn 		wattrset(menu->scrwin, menu->fore);
    441       1.1     blymn 	if ((menu->items[item]->opts & O_SELECTABLE) != O_SELECTABLE)
    442       1.9     blymn 		wattron(menu->scrwin, menu->grey);
    443       1.1     blymn 
    444       1.1     blymn 	  /* deal with the menu mark, if  one is set.
    445       1.1     blymn 	   * We mark the selected items and write blanks for
    446       1.1     blymn 	   * all others unless the menu unmark string is set in which
    447       1.1     blymn 	   * case the unmark string is written.
    448       1.1     blymn 	   */
    449  1.15.2.1       tls 	if ((menu->items[item]->selected == 1) ||
    450  1.15.2.1       tls 	    (((menu->opts & O_ONEVALUE) == O_ONEVALUE) &&
    451  1.15.2.1       tls 		(menu->cur_item == item))) {
    452       1.1     blymn 		if (menu->mark.string != NULL) {
    453       1.1     blymn 			for (j = 0; j < menu->mark.length; j++) {
    454       1.9     blymn 				waddch(menu->scrwin,
    455       1.1     blymn 				       menu->mark.string[j]);
    456       1.1     blymn 			}
    457       1.1     blymn 		}
    458       1.1     blymn 		  /* blank any length difference between mark & unmark */
    459       1.1     blymn 		for (j = menu->mark.length; j < mark_len; j++)
    460       1.9     blymn 			waddch(menu->scrwin, ' ');
    461       1.1     blymn 	} else {
    462       1.1     blymn 		if (menu->unmark.string != NULL) {
    463       1.1     blymn 			for (j = 0; j < menu->unmark.length; j++) {
    464       1.9     blymn 				waddch(menu->scrwin,
    465       1.1     blymn 				       menu->unmark.string[j]);
    466       1.1     blymn 			}
    467       1.1     blymn 		}
    468       1.1     blymn 		  /* blank any length difference between mark & unmark */
    469       1.1     blymn 		for (j = menu->unmark.length; j < mark_len; j++)
    470       1.9     blymn 			waddch(menu->scrwin, ' ');
    471       1.1     blymn 	}
    472      1.14     blymn 
    473       1.1     blymn 	  /* add the menu name */
    474       1.1     blymn 	for (j=0; j < menu->items[item]->name.length; j++)
    475       1.9     blymn 		waddch(menu->scrwin,
    476       1.1     blymn 		       menu->items[item]->name.string[j]);
    477      1.14     blymn 
    478       1.1     blymn 	pad_len = menu->col_width - menu->items[item]->name.length
    479       1.1     blymn 		- mark_len - 1;
    480       1.1     blymn 	if ((menu->opts & O_SHOWDESC) == O_SHOWDESC) {
    481       1.1     blymn 		pad_len -= menu->items[item]->description.length - 1;
    482       1.1     blymn 		for (j = 0; j < pad_len; j++)
    483       1.9     blymn 			waddch(menu->scrwin, menu->pad);
    484       1.1     blymn 		for (j = 0; j < menu->items[item]->description.length; j++) {
    485       1.9     blymn 			waddch(menu->scrwin,
    486       1.1     blymn 			       menu->items[item]->description.string[j]);
    487       1.1     blymn 		}
    488       1.1     blymn 	} else {
    489       1.1     blymn 		for (j = 0; j < pad_len; j++)
    490       1.9     blymn 			waddch(menu->scrwin, ' ');
    491       1.1     blymn 	}
    492       1.1     blymn 	menu->items[item]->visible = 1;
    493      1.14     blymn 
    494      1.11     blymn 	  /* kill any special attributes... */
    495      1.11     blymn 	wattrset(menu->scrwin, menu->back);
    496      1.11     blymn 
    497       1.6     blymn 	  /*
    498       1.6     blymn 	   * Fill in the spacing between items, annoying but it looks
    499       1.6     blymn 	   * odd if the menu items are inverse because the spacings do not
    500       1.6     blymn 	   * have the same attributes as the items.
    501       1.6     blymn 	   */
    502      1.11     blymn 	if ((menu->items[item]->col > 0) &&
    503      1.11     blymn 	    (menu->items[item]->col < (menu->item_cols - 1))) {
    504       1.9     blymn 		wmove(menu->scrwin,
    505       1.6     blymn 		      menu->items[item]->row - menu->top_row,
    506      1.11     blymn 		      menu->items[item]->col * (menu->col_width + 1) - 1);
    507       1.9     blymn 		waddch(menu->scrwin, ' ');
    508       1.6     blymn 	}
    509      1.14     blymn 
    510       1.1     blymn 	  /* and position the cursor nicely */
    511       1.1     blymn 	pos_menu_cursor(menu);
    512       1.1     blymn }
    513       1.1     blymn 
    514       1.1     blymn /*
    515       1.1     blymn  * Draw the menu in the subwindow provided.
    516       1.1     blymn  */
    517       1.1     blymn int
    518       1.6     blymn _menui_draw_menu(MENU *menu)
    519       1.1     blymn {
    520  1.15.2.2       tls 	int rowmajor, i, j, k, row = -1, stride;
    521      1.15     blymn 	int incr, cur_row, offset, row_count;
    522      1.14     blymn 
    523       1.1     blymn 	rowmajor = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
    524       1.1     blymn 
    525      1.15     blymn 	if (rowmajor) {
    526      1.15     blymn 		stride = 1;
    527      1.15     blymn 		incr = menu->item_cols;
    528      1.15     blymn 	} else {
    529      1.15     blymn 		stride = menu->item_rows;
    530      1.15     blymn 		incr = 1;
    531      1.15     blymn 	}
    532      1.15     blymn 	row_count = 0;
    533      1.15     blymn 
    534      1.15     blymn 	for (i = 0;  i < menu->item_count; i += incr) {
    535       1.1     blymn 		if (menu->items[i]->row == menu->top_row)
    536       1.1     blymn 			break;
    537      1.15     blymn 		row_count++;
    538      1.15     blymn 		for (j = 0; j < menu->item_cols; j++) {
    539      1.15     blymn 			offset = j * stride + i;
    540      1.15     blymn 			if (offset >= menu->item_count)
    541      1.15     blymn 				break; /* done */
    542      1.15     blymn 			menu->items[offset]->visible = 0;
    543      1.15     blymn 		}
    544       1.1     blymn 	}
    545       1.1     blymn 
    546       1.9     blymn 	wmove(menu->scrwin, 0, 0);
    547       1.1     blymn 
    548       1.9     blymn 	menu->col_width = getmaxx(menu->scrwin) / menu->cols;
    549       1.1     blymn 
    550      1.15     blymn 	for (cur_row = 0; cur_row < menu->rows; cur_row++) {
    551      1.15     blymn 		for (j = 0; j < menu->cols; j++) {
    552      1.15     blymn 			offset = j * stride + i;
    553      1.15     blymn 			if (offset >= menu->item_count) {
    554      1.15     blymn 			   /* no more items to draw, write background blanks */
    555      1.15     blymn 				wattrset(menu->scrwin, menu->back);
    556      1.15     blymn 				if (row < 0) {
    557      1.15     blymn 					row = menu->items[menu->item_count - 1]->row;
    558      1.15     blymn 				}
    559       1.1     blymn 
    560      1.15     blymn 				wmove(menu->scrwin, cur_row,
    561      1.15     blymn 				      j * (menu->col_width + 1));
    562      1.15     blymn 				for (k = 0; k < menu->col_width; k++)
    563      1.15     blymn 					waddch(menu->scrwin, ' ');
    564       1.1     blymn 			} else {
    565      1.15     blymn 				_menui_draw_item(menu, offset);
    566       1.1     blymn 			}
    567       1.1     blymn 		}
    568       1.1     blymn 
    569      1.15     blymn 		i += incr;
    570      1.15     blymn 		row_count++;
    571       1.1     blymn 	}
    572       1.1     blymn 
    573      1.15     blymn 	if (row_count < menu->item_rows) {
    574      1.15     blymn 		for (cur_row = row_count;  cur_row < menu->item_rows; cur_row++) {
    575      1.15     blymn 			for (j = 0; j < menu->item_cols; j++) {
    576      1.15     blymn 				offset = j * stride + i;
    577      1.15     blymn 				if (offset >= menu->item_count)
    578      1.15     blymn 					break; /* done */
    579      1.15     blymn 				menu->items[offset]->visible = 0;
    580      1.15     blymn 			}
    581      1.15     blymn 			i += incr;
    582      1.15     blymn 		}
    583       1.1     blymn 	}
    584      1.14     blymn 
    585       1.1     blymn 	return E_OK;
    586       1.1     blymn }
    587       1.1     blymn 
    588      1.14     blymn 
    589       1.1     blymn /*
    590       1.1     blymn  * Calculate the widest menu item and stash it in the menu struct.
    591       1.1     blymn  *
    592       1.1     blymn  */
    593       1.1     blymn void
    594       1.6     blymn _menui_max_item_size(MENU *menu)
    595       1.1     blymn {
    596       1.1     blymn 	int i, with_desc, width;
    597       1.1     blymn 
    598       1.1     blymn 	with_desc = ((menu->opts & O_SHOWDESC) == O_SHOWDESC);
    599      1.14     blymn 
    600       1.1     blymn 	for (i = 0; i < menu->item_count; i++) {
    601       1.1     blymn 		width = menu->items[i]->name.length
    602       1.1     blymn 			+ max(menu->mark.length, menu->unmark.length);
    603       1.1     blymn 		if (with_desc)
    604       1.1     blymn 			width += menu->items[i]->description.length + 1;
    605       1.1     blymn 
    606       1.1     blymn 		menu->max_item_width = max(menu->max_item_width, width);
    607       1.1     blymn 	}
    608       1.1     blymn }
    609       1.1     blymn 
    610       1.1     blymn 
    611       1.1     blymn /*
    612       1.1     blymn  * Redraw the menu on the screen.  If the current item has changed then
    613       1.1     blymn  * unhighlight the old item and highlight the new one.
    614       1.1     blymn  */
    615       1.1     blymn static void
    616       1.6     blymn _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item)
    617       1.1     blymn {
    618       1.1     blymn 
    619       1.1     blymn 	if (menu->top_row != old_top_row) {
    620       1.1     blymn 		  /* top row changed - redo the whole menu
    621       1.1     blymn 		   * XXXX this could be improved if we had wscrl implemented.
    622       1.1     blymn 
    623       1.1     blymn 		   * XXXX we could scroll the window and just fill in the
    624       1.1     blymn 		   * XXXX changed lines.
    625       1.1     blymn 		   */
    626       1.9     blymn 		wclear(menu->scrwin);
    627       1.3     blymn 		_menui_draw_menu(menu);
    628       1.1     blymn 	} else {
    629       1.1     blymn 		if (menu->cur_item != old_cur_item) {
    630       1.1     blymn 			  /* redo the old item as a normal one. */
    631       1.3     blymn 			_menui_draw_item(menu, old_cur_item);
    632       1.1     blymn 		}
    633       1.1     blymn 		  /* and then redraw the current item */
    634       1.3     blymn 		_menui_draw_item(menu, menu->cur_item);
    635       1.1     blymn 	}
    636       1.1     blymn }
    637