636 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			636 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*	$NetBSD: internals.c,v 1.15 2012/06/27 11:53:36 blymn Exp $	*/
 | |
| 
 | |
| /*-
 | |
|  * Copyright (c) 1998-1999 Brett Lymn (blymn@baea.com.au, brett_lymn@yahoo.com.au)
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. The name of the author may not be used to endorse or promote products
 | |
|  *    derived from this software without specific prior written permission
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 | |
|  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | |
|  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | |
|  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | |
|  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | |
|  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
|  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
|  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | |
|  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  *
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <sys/cdefs.h>
 | |
| __RCSID("$NetBSD: internals.c,v 1.15 2012/06/27 11:53:36 blymn Exp $");
 | |
| 
 | |
| #include <menu.h>
 | |
| #include <ctype.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include "internals.h"
 | |
| 
 | |
| /* internal function prototypes */
 | |
| static void
 | |
| _menui_calc_neighbours(MENU *menu, int item_no);
 | |
| static void _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item);
 | |
| 
 | |
|   /*
 | |
|    * Link all the menu items together to speed up navigation.  We need
 | |
|    * to calculate the widest item entry, then work out how many columns
 | |
|    * of items the window will accommodate and then how many rows there will
 | |
|    * be.  Once the layout is determined the neighbours of each item is
 | |
|    * calculated and the item structures updated.
 | |
|    */
 | |
| int
 | |
| _menui_stitch_items(MENU *menu)
 | |
| {
 | |
| 	int i, row_major;
 | |
| 
 | |
| 	row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
 | |
| 
 | |
| 	if (menu->posted == 1)
 | |
| 		return E_POSTED;
 | |
| 	if (menu->items == NULL)
 | |
| 		return E_BAD_ARGUMENT;
 | |
| 
 | |
| 	menu->item_rows = menu->item_count / menu->cols;
 | |
| 	menu->item_cols = menu->cols;
 | |
| 	if (menu->item_count > (menu->item_rows * menu->item_cols))
 | |
| 		menu->item_rows += 1;
 | |
| 
 | |
| 	_menui_max_item_size(menu);
 | |
| 
 | |
| 	for (i = 0; i < menu->item_count; i++) {
 | |
| 		  /* fill in the row and column value of the item */
 | |
| 		if (row_major) {
 | |
| 			menu->items[i]->row = i / menu->item_cols;
 | |
| 			menu->items[i]->col = i % menu->item_cols;
 | |
| 		} else {
 | |
| 			menu->items[i]->row = i % menu->item_rows;
 | |
| 			menu->items[i]->col = i / menu->item_rows;
 | |
| 		}
 | |
| 
 | |
| 		_menui_calc_neighbours(menu, i);
 | |
| 	}
 | |
| 
 | |
| 	return E_OK;
 | |
| }
 | |
| 
 | |
|   /*
 | |
|    * Calculate the neighbours for an item in menu.
 | |
|    */
 | |
| static void
 | |
| _menui_calc_neighbours(MENU *menu, int item_no)
 | |
| {
 | |
| 	int neighbour, cycle, row_major, edge;
 | |
| 	ITEM *item;
 | |
| 
 | |
| 	row_major = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
 | |
| 	cycle = ((menu->opts & O_NONCYCLIC) != O_NONCYCLIC);
 | |
| 	item = menu->items[item_no];
 | |
| 
 | |
| 	if (menu->item_rows < 2) {
 | |
| 		if (cycle) {
 | |
| 			item->up = item;
 | |
| 			item->down = item;
 | |
| 		} else {
 | |
| 			item->up = NULL;
 | |
| 			item->down = NULL;
 | |
| 		}
 | |
| 	} else {
 | |
| 
 | |
| 		/* up */
 | |
| 		if (menu->item_cols < 2) {
 | |
| 			if (item_no == 0) {
 | |
| 				if (cycle)
 | |
| 					item->up =
 | |
| 					    menu->items[menu->item_count - 1];
 | |
| 				else
 | |
| 					item->up = NULL;
 | |
| 			} else
 | |
| 				item->up = menu->items[item_no - 1];
 | |
| 		} else {
 | |
| 			edge = 0;
 | |
| 			if (row_major) {
 | |
| 				if (item->row == 0) {
 | |
| 					neighbour =
 | |
| 				    	(menu->item_rows - 1) * menu->item_cols
 | |
| 						+ item->col;
 | |
| 					if (neighbour >= menu->item_count)
 | |
| 						neighbour -= menu->item_cols;
 | |
| 					edge = 1;
 | |
| 				} else
 | |
| 					neighbour = item_no - menu->item_cols;
 | |
| 			} else {
 | |
| 				if (item->row == 0) {
 | |
| 					neighbour = menu->item_rows * item->col
 | |
| 						+ menu->item_rows - 1;
 | |
| 					if (neighbour >= menu->item_count)
 | |
| 						neighbour = menu->item_count - 1;
 | |
| 					edge = 1;
 | |
| 				} else
 | |
| 					neighbour = item_no - 1;
 | |
| 			}
 | |
| 
 | |
| 
 | |
| 			item->up = menu->items[neighbour];
 | |
| 			if ((!cycle) && (edge == 1))
 | |
| 				item->up = NULL;
 | |
| 		}
 | |
| 
 | |
| 		/* Down */
 | |
| 		if (menu->item_cols < 2) {
 | |
| 			if (item_no == (menu->item_count - 1)) {
 | |
| 				if (cycle)
 | |
| 					item->down = menu->items[0];
 | |
| 				else
 | |
| 					item->down = NULL;
 | |
| 			} else
 | |
| 				item->down = menu->items[item_no + 1];
 | |
| 		} else {
 | |
| 			edge = 0;
 | |
| 			if (row_major) {
 | |
| 				if (item->row == menu->item_rows - 1) {
 | |
| 					neighbour = item->col;
 | |
| 					edge = 1;
 | |
| 				} else {
 | |
| 					neighbour = item_no + menu->item_cols;
 | |
| 					if (neighbour >= menu->item_count) {
 | |
| 						neighbour = item->col;
 | |
| 						edge = 1;
 | |
| 					}
 | |
| 				}
 | |
| 			} else {
 | |
| 				if (item->row == menu->item_rows - 1) {
 | |
| 					neighbour = item->col * menu->item_rows;
 | |
| 					edge = 1;
 | |
| 				} else {
 | |
| 					neighbour = item_no + 1;
 | |
| 					if (neighbour >= menu->item_count) {
 | |
| 						neighbour = item->col
 | |
| 						    * menu->item_rows;
 | |
| 						edge = 1;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			item->down = menu->items[neighbour];
 | |
| 			if ((!cycle) && (edge == 1))
 | |
| 				item->down = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (menu->item_cols < 2) {
 | |
| 		if (cycle) {
 | |
| 			item->left = item;
 | |
| 			item->right = item;
 | |
| 		} else {
 | |
| 			item->left = NULL;
 | |
| 			item->right = NULL;
 | |
| 		}
 | |
| 	} else {
 | |
| 		/* left */
 | |
| 		if (menu->item_rows < 2) {
 | |
| 			if (item_no == 0) {
 | |
| 				if (cycle)
 | |
| 					item->left =
 | |
| 					    menu->items[menu->item_count - 1];
 | |
| 				else
 | |
| 					item->left = NULL;
 | |
| 			} else
 | |
| 				item->left = menu->items[item_no - 1];
 | |
| 		} else {
 | |
| 			edge = 0;
 | |
| 			if (row_major) {
 | |
| 				if (item->col == 0) {
 | |
| 					neighbour = item_no + menu->cols - 1;
 | |
| 					if (neighbour >= menu->item_count)
 | |
| 						neighbour = menu->item_count - 1;
 | |
| 					edge = 1;
 | |
| 				} else
 | |
| 					neighbour = item_no - 1;
 | |
| 			} else {
 | |
| 				if (item->col == 0) {
 | |
| 					neighbour = menu->item_rows
 | |
| 					    * (menu->item_cols - 1) + item->row;
 | |
| 					if (neighbour >= menu->item_count)
 | |
| 						neighbour -= menu->item_rows;
 | |
| 					edge = 1;
 | |
| 				} else
 | |
| 					neighbour = item_no - menu->item_rows;
 | |
| 			}
 | |
| 
 | |
| 			item->left = menu->items[neighbour];
 | |
| 			if ((!cycle) && (edge == 1))
 | |
| 				item->left = NULL;
 | |
| 		}
 | |
| 
 | |
| 		/* right */
 | |
| 		if (menu->item_rows < 2) {
 | |
| 			if (item_no == menu->item_count - 1) {
 | |
| 				if (cycle)
 | |
| 					item->right = menu->items[0];
 | |
| 				else
 | |
| 					item->right = NULL;
 | |
| 			} else
 | |
| 				item->right = menu->items[item_no + 1];
 | |
| 		} else {
 | |
| 			edge = 0;
 | |
| 			if (row_major) {
 | |
| 				if (item->col == menu->item_cols - 1) {
 | |
| 					neighbour = item_no - menu->item_cols
 | |
| 					    + 1;
 | |
| 					edge = 1;
 | |
| 				} else if (item_no == menu->item_count - 1) {
 | |
| 					neighbour = item->row * menu->item_cols;
 | |
| 					edge = 1;
 | |
| 				} else
 | |
| 					neighbour = item_no + 1;
 | |
| 			} else {
 | |
| 				if (item->col == menu->item_cols - 1) {
 | |
| 					neighbour = item->row;
 | |
| 					edge = 1;
 | |
| 				} else {
 | |
| 					neighbour = item_no + menu->item_rows;
 | |
| 					if (neighbour >= menu->item_count) {
 | |
| 						neighbour = item->row;
 | |
| 						edge = 1;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			item->right = menu->items[neighbour];
 | |
| 			if ((!cycle) && (edge == 1))
 | |
| 				item->right = NULL;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Goto the item pointed to by item and adjust the menu structure
 | |
|  * accordingly.  Call the term and init functions if required.
 | |
|  */
 | |
| int
 | |
| _menui_goto_item(MENU *menu, ITEM *item, int new_top_row)
 | |
| {
 | |
| 	int old_top_row = menu->top_row, old_cur_item = menu->cur_item;
 | |
| 
 | |
| 	  /* If we get a null then the menu is not cyclic so deny request */
 | |
| 	if (item == NULL)
 | |
| 		return E_REQUEST_DENIED;
 | |
| 
 | |
| 	menu->in_init = 1;
 | |
| 	if (menu->top_row != new_top_row) {
 | |
| 		if ((menu->posted == 1) && (menu->menu_term != NULL))
 | |
| 			menu->menu_term(menu);
 | |
| 		menu->top_row = new_top_row;
 | |
| 
 | |
| 		if ((menu->posted == 1) && (menu->menu_init != NULL))
 | |
| 			menu->menu_init(menu);
 | |
| 	}
 | |
| 
 | |
| 	  /* this looks like wasted effort but it can happen.... */
 | |
| 	if (menu->cur_item != item->index) {
 | |
| 
 | |
| 		if ((menu->posted == 1) && (menu->item_term != NULL))
 | |
| 			menu->item_term(menu);
 | |
| 
 | |
| 		menu->cur_item = item->index;
 | |
| 		menu->cur_row = item->row;
 | |
| 		menu->cur_col = item->col;
 | |
| 
 | |
| 		if (menu->posted == 1)
 | |
| 			_menui_redraw_menu(menu, old_top_row, old_cur_item);
 | |
| 
 | |
| 		if ((menu->posted == 1) && (menu->item_init != NULL))
 | |
| 			menu->item_init(menu);
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	menu->in_init = 0;
 | |
| 	return E_OK;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Attempt to match items with the pattern buffer in the direction given
 | |
|  * by iterating over the menu items.  If a match is found return E_OK
 | |
|  * otherwise return E_NO_MATCH
 | |
|  */
 | |
| int
 | |
| _menui_match_items(MENU *menu, int direction, int *item_matched)
 | |
| {
 | |
| 	int i, caseless;
 | |
| 
 | |
| 	caseless = ((menu->opts & O_IGNORECASE) == O_IGNORECASE);
 | |
| 
 | |
| 	i = menu->cur_item;
 | |
| 	if (direction == MATCH_NEXT_FORWARD) {
 | |
| 		if (++i >= menu->item_count) i = 0;
 | |
| 	} else if (direction == MATCH_NEXT_REVERSE) {
 | |
| 		if (--i < 0) i = menu->item_count - 1;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	do {
 | |
| 		if (menu->items[i]->name.length >= menu->plen) {
 | |
| 			  /* no chance if pattern is longer */
 | |
| 			if (caseless) {
 | |
| 				if (strncasecmp(menu->items[i]->name.string,
 | |
| 						menu->pattern,
 | |
| 						(size_t) menu->plen) == 0) {
 | |
| 					*item_matched = i;
 | |
| 					menu->match_len = menu->plen;
 | |
| 					return E_OK;
 | |
| 				}
 | |
| 			} else {
 | |
| 				if (strncmp(menu->items[i]->name.string,
 | |
| 					    menu->pattern,
 | |
| 					    (size_t) menu->plen) == 0) {
 | |
| 					*item_matched = i;
 | |
| 					menu->match_len = menu->plen;
 | |
| 					return E_OK;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ((direction == MATCH_FORWARD) ||
 | |
| 		    (direction == MATCH_NEXT_FORWARD)) {
 | |
| 			if (++i >= menu->item_count) i = 0;
 | |
| 		} else {
 | |
| 			if (--i <= 0) i = menu->item_count - 1;
 | |
| 		}
 | |
| 	} while (i != menu->cur_item);
 | |
| 
 | |
| 	menu->match_len = 0; /* match did not succeed - kill the match len. */
 | |
| 	return E_NO_MATCH;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Attempt to match the pattern buffer against the items.  If c is a
 | |
|  * printable character then add it to the pattern buffer prior to
 | |
|  * performing the match.  Direction determines the direction of matching.
 | |
|  * If the match is successful update the item_matched variable with the
 | |
|  * index of the item that matched the pattern.
 | |
|  */
 | |
| int
 | |
| _menui_match_pattern(MENU *menu, int c, int direction, int *item_matched)
 | |
| {
 | |
| 	if (menu == NULL)
 | |
| 		return E_BAD_ARGUMENT;
 | |
| 	if (menu->items == NULL)
 | |
| 		return E_BAD_ARGUMENT;
 | |
| 	if (*menu->items == NULL)
 | |
| 		return E_BAD_ARGUMENT;
 | |
| 
 | |
| 	if (isprint(c)) {
 | |
| 		  /* add char to buffer - first allocate room for it */
 | |
| 		if ((menu->pattern = (char *)
 | |
| 		     realloc(menu->pattern,
 | |
| 			     menu->plen + sizeof(char) +
 | |
| 			     ((menu->plen > 0)? 0 : 1)))
 | |
| 		    == NULL)
 | |
| 			return E_SYSTEM_ERROR;
 | |
| 		menu->pattern[menu->plen] = c;
 | |
| 		menu->pattern[++menu->plen] = '\0';
 | |
| 
 | |
| 		  /* there is no chance of a match if pattern is longer
 | |
| 		     than all the items */
 | |
| 		if (menu->plen >= menu->max_item_width) {
 | |
| 			menu->pattern[--menu->plen] = '\0';
 | |
| 			return E_NO_MATCH;
 | |
| 		}
 | |
| 
 | |
| 		if (_menui_match_items(menu, direction,
 | |
| 					item_matched) == E_NO_MATCH) {
 | |
| 			menu->pattern[--menu->plen] = '\0';
 | |
| 			return E_NO_MATCH;
 | |
| 		} else
 | |
| 			return E_OK;
 | |
| 	} else {
 | |
| 		if (_menui_match_items(menu, direction,
 | |
| 					item_matched) == E_OK) {
 | |
| 			return E_OK;
 | |
| 		} else {
 | |
| 			return E_NO_MATCH;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Draw an item in the subwindow complete with appropriate highlighting.
 | |
|  */
 | |
| void
 | |
| _menui_draw_item(MENU *menu, int item)
 | |
| {
 | |
| 	int j, pad_len, mark_len;
 | |
| 
 | |
| 	mark_len = max(menu->mark.length, menu->unmark.length);
 | |
| 
 | |
| 	wmove(menu->scrwin,
 | |
| 	      menu->items[item]->row - menu->top_row,
 | |
| 	      menu->items[item]->col * (menu->col_width + 1));
 | |
| 
 | |
| 	if (menu->cur_item == item)
 | |
| 		wattrset(menu->scrwin, menu->fore);
 | |
| 	if ((menu->items[item]->opts & O_SELECTABLE) != O_SELECTABLE)
 | |
| 		wattron(menu->scrwin, menu->grey);
 | |
| 
 | |
| 	  /* deal with the menu mark, if  one is set.
 | |
| 	   * We mark the selected items and write blanks for
 | |
| 	   * all others unless the menu unmark string is set in which
 | |
| 	   * case the unmark string is written.
 | |
| 	   */
 | |
| 	if (menu->items[item]->selected == 1) {
 | |
| 		if (menu->mark.string != NULL) {
 | |
| 			for (j = 0; j < menu->mark.length; j++) {
 | |
| 				waddch(menu->scrwin,
 | |
| 				       menu->mark.string[j]);
 | |
| 			}
 | |
| 		}
 | |
| 		  /* blank any length difference between mark & unmark */
 | |
| 		for (j = menu->mark.length; j < mark_len; j++)
 | |
| 			waddch(menu->scrwin, ' ');
 | |
| 	} else {
 | |
| 		if (menu->unmark.string != NULL) {
 | |
| 			for (j = 0; j < menu->unmark.length; j++) {
 | |
| 				waddch(menu->scrwin,
 | |
| 				       menu->unmark.string[j]);
 | |
| 			}
 | |
| 		}
 | |
| 		  /* blank any length difference between mark & unmark */
 | |
| 		for (j = menu->unmark.length; j < mark_len; j++)
 | |
| 			waddch(menu->scrwin, ' ');
 | |
| 	}
 | |
| 
 | |
| 	  /* add the menu name */
 | |
| 	for (j=0; j < menu->items[item]->name.length; j++)
 | |
| 		waddch(menu->scrwin,
 | |
| 		       menu->items[item]->name.string[j]);
 | |
| 
 | |
| 	pad_len = menu->col_width - menu->items[item]->name.length
 | |
| 		- mark_len - 1;
 | |
| 	if ((menu->opts & O_SHOWDESC) == O_SHOWDESC) {
 | |
| 		pad_len -= menu->items[item]->description.length - 1;
 | |
| 		for (j = 0; j < pad_len; j++)
 | |
| 			waddch(menu->scrwin, menu->pad);
 | |
| 		for (j = 0; j < menu->items[item]->description.length; j++) {
 | |
| 			waddch(menu->scrwin,
 | |
| 			       menu->items[item]->description.string[j]);
 | |
| 		}
 | |
| 	} else {
 | |
| 		for (j = 0; j < pad_len; j++)
 | |
| 			waddch(menu->scrwin, ' ');
 | |
| 	}
 | |
| 	menu->items[item]->visible = 1;
 | |
| 
 | |
| 	  /* kill any special attributes... */
 | |
| 	wattrset(menu->scrwin, menu->back);
 | |
| 
 | |
| 	  /*
 | |
| 	   * Fill in the spacing between items, annoying but it looks
 | |
| 	   * odd if the menu items are inverse because the spacings do not
 | |
| 	   * have the same attributes as the items.
 | |
| 	   */
 | |
| 	if ((menu->items[item]->col > 0) &&
 | |
| 	    (menu->items[item]->col < (menu->item_cols - 1))) {
 | |
| 		wmove(menu->scrwin,
 | |
| 		      menu->items[item]->row - menu->top_row,
 | |
| 		      menu->items[item]->col * (menu->col_width + 1) - 1);
 | |
| 		waddch(menu->scrwin, ' ');
 | |
| 	}
 | |
| 
 | |
| 	  /* and position the cursor nicely */
 | |
| 	pos_menu_cursor(menu);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Draw the menu in the subwindow provided.
 | |
|  */
 | |
| int
 | |
| _menui_draw_menu(MENU *menu)
 | |
| {
 | |
| 	int rowmajor, i, j, k, row = -1, col = -1, stride;
 | |
| 	int incr, cur_row, offset, row_count;
 | |
| 
 | |
| 	rowmajor = ((menu->opts & O_ROWMAJOR) == O_ROWMAJOR);
 | |
| 
 | |
| 	if (rowmajor) {
 | |
| 		stride = 1;
 | |
| 		incr = menu->item_cols;
 | |
| 	} else {
 | |
| 		stride = menu->item_rows;
 | |
| 		incr = 1;
 | |
| 	}
 | |
| 	row_count = 0;
 | |
| 
 | |
| 	for (i = 0;  i < menu->item_count; i += incr) {
 | |
| 		if (menu->items[i]->row == menu->top_row)
 | |
| 			break;
 | |
| 		row_count++;
 | |
| 		for (j = 0; j < menu->item_cols; j++) {
 | |
| 			offset = j * stride + i;
 | |
| 			if (offset >= menu->item_count)
 | |
| 				break; /* done */
 | |
| 			menu->items[offset]->visible = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	wmove(menu->scrwin, 0, 0);
 | |
| 
 | |
| 	menu->col_width = getmaxx(menu->scrwin) / menu->cols;
 | |
| 
 | |
| 	for (cur_row = 0; cur_row < menu->rows; cur_row++) {
 | |
| 		for (j = 0; j < menu->cols; j++) {
 | |
| 			offset = j * stride + i;
 | |
| 			if (offset >= menu->item_count) {
 | |
| 			   /* no more items to draw, write background blanks */
 | |
| 				wattrset(menu->scrwin, menu->back);
 | |
| 				if (row < 0) {
 | |
| 					row = menu->items[menu->item_count - 1]->row;
 | |
| 					col = menu->items[menu->item_count - 1]->col;
 | |
| 				}
 | |
| 
 | |
| 				wmove(menu->scrwin, cur_row,
 | |
| 				      j * (menu->col_width + 1));
 | |
| 				for (k = 0; k < menu->col_width; k++)
 | |
| 					waddch(menu->scrwin, ' ');
 | |
| 			} else {
 | |
| 				_menui_draw_item(menu, offset);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		i += incr;
 | |
| 		row_count++;
 | |
| 	}
 | |
| 
 | |
| 	if (row_count < menu->item_rows) {
 | |
| 		for (cur_row = row_count;  cur_row < menu->item_rows; cur_row++) {
 | |
| 			for (j = 0; j < menu->item_cols; j++) {
 | |
| 				offset = j * stride + i;
 | |
| 				if (offset >= menu->item_count)
 | |
| 					break; /* done */
 | |
| 				menu->items[offset]->visible = 0;
 | |
| 			}
 | |
| 			i += incr;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return E_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Calculate the widest menu item and stash it in the menu struct.
 | |
|  *
 | |
|  */
 | |
| void
 | |
| _menui_max_item_size(MENU *menu)
 | |
| {
 | |
| 	int i, with_desc, width;
 | |
| 
 | |
| 	with_desc = ((menu->opts & O_SHOWDESC) == O_SHOWDESC);
 | |
| 
 | |
| 	for (i = 0; i < menu->item_count; i++) {
 | |
| 		width = menu->items[i]->name.length
 | |
| 			+ max(menu->mark.length, menu->unmark.length);
 | |
| 		if (with_desc)
 | |
| 			width += menu->items[i]->description.length + 1;
 | |
| 
 | |
| 		menu->max_item_width = max(menu->max_item_width, width);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Redraw the menu on the screen.  If the current item has changed then
 | |
|  * unhighlight the old item and highlight the new one.
 | |
|  */
 | |
| static void
 | |
| _menui_redraw_menu(MENU *menu, int old_top_row, int old_cur_item)
 | |
| {
 | |
| 
 | |
| 	if (menu->top_row != old_top_row) {
 | |
| 		  /* top row changed - redo the whole menu
 | |
| 		   * XXXX this could be improved if we had wscrl implemented.
 | |
| 
 | |
| 		   * XXXX we could scroll the window and just fill in the
 | |
| 		   * XXXX changed lines.
 | |
| 		   */
 | |
| 		wclear(menu->scrwin);
 | |
| 		_menui_draw_menu(menu);
 | |
| 	} else {
 | |
| 		if (menu->cur_item != old_cur_item) {
 | |
| 			  /* redo the old item as a normal one. */
 | |
| 			_menui_draw_item(menu, old_cur_item);
 | |
| 		}
 | |
| 		  /* and then redraw the current item */
 | |
| 		_menui_draw_item(menu, menu->cur_item);
 | |
| 	}
 | |
| }
 | 
