kconfig/lxdialog/menubox.c
author Arnaud Lacombe <lacombar@gmail.com>
Tue Aug 03 06:17:51 2010 +0200 (2010-08-03)
changeset 2064 f5ebe8c429dc
parent 1 eeea35fbf182
child 2448 a103abae1560
permissions -rw-r--r--
libc/uClibc: add uClibc 0.9.30.3

This version has been released a couple of month ago, but it never reached
crosstool-ng tree. This may be linked to the fact that the current 0.9.30.2,
once patched, has nothing much different from 0.9.30.3, released.

I'm not including any patch with this upgrade, on purpose.

Signed-off-by: Arnaud Lacombe <lacombar@gmail.com>
     1 /*
     2  *  menubox.c -- implements the menu box
     3  *
     4  *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
     5  *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
     6  *
     7  *  This program is free software; you can redistribute it and/or
     8  *  modify it under the terms of the GNU General Public License
     9  *  as published by the Free Software Foundation; either version 2
    10  *  of the License, or (at your option) any later version.
    11  *
    12  *  This program is distributed in the hope that it will be useful,
    13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  *  GNU General Public License for more details.
    16  *
    17  *  You should have received a copy of the GNU General Public License
    18  *  along with this program; if not, write to the Free Software
    19  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    20  */
    21 
    22 /*
    23  *  Changes by Clifford Wolf (god@clifford.at)
    24  *
    25  *  [ 1998-06-13 ]
    26  *
    27  *    *)  A bugfix for the Page-Down problem
    28  *
    29  *    *)  Formerly when I used Page Down and Page Up, the cursor would be set 
    30  *        to the first position in the menu box.  Now lxdialog is a bit
    31  *        smarter and works more like other menu systems (just have a look at
    32  *        it).
    33  *
    34  *    *)  Formerly if I selected something my scrolling would be broken because
    35  *        lxdialog is re-invoked by the Menuconfig shell script, can't
    36  *        remember the last scrolling position, and just sets it so that the
    37  *        cursor is at the bottom of the box.  Now it writes the temporary file
    38  *        lxdialog.scrltmp which contains this information. The file is
    39  *        deleted by lxdialog if the user leaves a submenu or enters a new
    40  *        one, but it would be nice if Menuconfig could make another "rm -f"
    41  *        just to be sure.  Just try it out - you will recognise a difference!
    42  *
    43  *  [ 1998-06-14 ]
    44  *
    45  *    *)  Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
    46  *        and menus change their size on the fly.
    47  *
    48  *    *)  If for some reason the last scrolling position is not saved by
    49  *        lxdialog, it sets the scrolling so that the selected item is in the
    50  *        middle of the menu box, not at the bottom.
    51  *
    52  * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
    53  * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
    54  * This fixes a bug in Menuconfig where using ' ' to descend into menus
    55  * would leave mis-synchronized lxdialog.scrltmp files lying around,
    56  * fscanf would read in 'scroll', and eventually that value would get used.
    57  */
    58 
    59 #include "dialog.h"
    60 
    61 static int menu_width, item_x;
    62 
    63 /*
    64  * Print menu item
    65  */
    66 static void do_print_item(WINDOW * win, const char *item, int line_y,
    67                           int selected, int hotkey)
    68 {
    69 	int j;
    70 	char *menu_item = malloc(menu_width + 1);
    71 
    72 	strncpy(menu_item, item, menu_width - item_x);
    73 	menu_item[menu_width - item_x] = '\0';
    74 	j = first_alpha(menu_item, "YyNnMmHh");
    75 
    76 	/* Clear 'residue' of last item */
    77 	wattrset(win, dlg.menubox.atr);
    78 	wmove(win, line_y, 0);
    79 #if OLD_NCURSES
    80 	{
    81 		int i;
    82 		for (i = 0; i < menu_width; i++)
    83 			waddch(win, ' ');
    84 	}
    85 #else
    86 	wclrtoeol(win);
    87 #endif
    88 	wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
    89 	mvwaddstr(win, line_y, item_x, menu_item);
    90 	if (hotkey) {
    91 		wattrset(win, selected ? dlg.tag_key_selected.atr
    92 			 : dlg.tag_key.atr);
    93 		mvwaddch(win, line_y, item_x + j, menu_item[j]);
    94 	}
    95 	if (selected) {
    96 		wmove(win, line_y, item_x + 1);
    97 	}
    98 	free(menu_item);
    99 	wrefresh(win);
   100 }
   101 
   102 #define print_item(index, choice, selected)				\
   103 do {									\
   104 	item_set(index);						\
   105 	do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \
   106 } while (0)
   107 
   108 /*
   109  * Print the scroll indicators.
   110  */
   111 static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x,
   112 			 int height)
   113 {
   114 	int cur_y, cur_x;
   115 
   116 	getyx(win, cur_y, cur_x);
   117 
   118 	wmove(win, y, x);
   119 
   120 	if (scroll > 0) {
   121 		wattrset(win, dlg.uarrow.atr);
   122 		waddch(win, ACS_UARROW);
   123 		waddstr(win, "(-)");
   124 	} else {
   125 		wattrset(win, dlg.menubox.atr);
   126 		waddch(win, ACS_HLINE);
   127 		waddch(win, ACS_HLINE);
   128 		waddch(win, ACS_HLINE);
   129 		waddch(win, ACS_HLINE);
   130 	}
   131 
   132 	y = y + height + 1;
   133 	wmove(win, y, x);
   134 	wrefresh(win);
   135 
   136 	if ((height < item_no) && (scroll + height < item_no)) {
   137 		wattrset(win, dlg.darrow.atr);
   138 		waddch(win, ACS_DARROW);
   139 		waddstr(win, "(+)");
   140 	} else {
   141 		wattrset(win, dlg.menubox_border.atr);
   142 		waddch(win, ACS_HLINE);
   143 		waddch(win, ACS_HLINE);
   144 		waddch(win, ACS_HLINE);
   145 		waddch(win, ACS_HLINE);
   146 	}
   147 
   148 	wmove(win, cur_y, cur_x);
   149 	wrefresh(win);
   150 }
   151 
   152 /*
   153  * Display the termination buttons.
   154  */
   155 static void print_buttons(WINDOW * win, int height, int width, int selected)
   156 {
   157 	int x = width / 2 - 16;
   158 	int y = height - 2;
   159 
   160 	print_button(win, gettext("Select"), y, x, selected == 0);
   161 	print_button(win, gettext(" Exit "), y, x + 12, selected == 1);
   162 	print_button(win, gettext(" Help "), y, x + 24, selected == 2);
   163 
   164 	wmove(win, y, x + 1 + 12 * selected);
   165 	wrefresh(win);
   166 }
   167 
   168 /* scroll up n lines (n may be negative) */
   169 static void do_scroll(WINDOW *win, int *scroll, int n)
   170 {
   171 	/* Scroll menu up */
   172 	scrollok(win, TRUE);
   173 	wscrl(win, n);
   174 	scrollok(win, FALSE);
   175 	*scroll = *scroll + n;
   176 	wrefresh(win);
   177 }
   178 
   179 /*
   180  * Display a menu for choosing among a number of options
   181  */
   182 int dialog_menu(const char *title, const char *prompt,
   183                 const void *selected, int *s_scroll)
   184 {
   185 	int i, j, x, y, box_x, box_y;
   186 	int height, width, menu_height;
   187 	int key = 0, button = 0, scroll = 0, choice = 0;
   188 	int first_item =  0, max_choice;
   189 	WINDOW *dialog, *menu;
   190 
   191 do_resize:
   192 	height = getmaxy(stdscr);
   193 	width = getmaxx(stdscr);
   194 	if (height < 15 || width < 65)
   195 		return -ERRDISPLAYTOOSMALL;
   196 
   197 	height -= 4;
   198 	width  -= 5;
   199 	menu_height = height - 10;
   200 
   201 	max_choice = MIN(menu_height, item_count());
   202 
   203 	/* center dialog box on screen */
   204 	x = (COLS - width) / 2;
   205 	y = (LINES - height) / 2;
   206 
   207 	draw_shadow(stdscr, y, x, height, width);
   208 
   209 	dialog = newwin(height, width, y, x);
   210 	keypad(dialog, TRUE);
   211 
   212 	draw_box(dialog, 0, 0, height, width,
   213 		 dlg.dialog.atr, dlg.border.atr);
   214 	wattrset(dialog, dlg.border.atr);
   215 	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
   216 	for (i = 0; i < width - 2; i++)
   217 		waddch(dialog, ACS_HLINE);
   218 	wattrset(dialog, dlg.dialog.atr);
   219 	wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
   220 	waddch(dialog, ACS_RTEE);
   221 
   222 	print_title(dialog, title, width);
   223 
   224 	wattrset(dialog, dlg.dialog.atr);
   225 	print_autowrap(dialog, prompt, width - 2, 1, 3);
   226 
   227 	menu_width = width - 6;
   228 	box_y = height - menu_height - 5;
   229 	box_x = (width - menu_width) / 2 - 1;
   230 
   231 	/* create new window for the menu */
   232 	menu = subwin(dialog, menu_height, menu_width,
   233 		      y + box_y + 1, x + box_x + 1);
   234 	keypad(menu, TRUE);
   235 
   236 	/* draw a box around the menu items */
   237 	draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2,
   238 		 dlg.menubox_border.atr, dlg.menubox.atr);
   239 
   240 	if (menu_width >= 80)
   241 		item_x = (menu_width - 70) / 2;
   242 	else
   243 		item_x = 4;
   244 
   245 	/* Set choice to default item */
   246 	item_foreach()
   247 		if (selected && (selected == item_data()))
   248 			choice = item_n();
   249 	/* get the saved scroll info */
   250 	scroll = *s_scroll;
   251 	if ((scroll <= choice) && (scroll + max_choice > choice) &&
   252 	   (scroll >= 0) && (scroll + max_choice <= item_count())) {
   253 		first_item = scroll;
   254 		choice = choice - scroll;
   255 	} else {
   256 		scroll = 0;
   257 	}
   258 	if ((choice >= max_choice)) {
   259 		if (choice >= item_count() - max_choice / 2)
   260 			scroll = first_item = item_count() - max_choice;
   261 		else
   262 			scroll = first_item = choice - max_choice / 2;
   263 		choice = choice - scroll;
   264 	}
   265 
   266 	/* Print the menu */
   267 	for (i = 0; i < max_choice; i++) {
   268 		print_item(first_item + i, i, i == choice);
   269 	}
   270 
   271 	wnoutrefresh(menu);
   272 
   273 	print_arrows(dialog, item_count(), scroll,
   274 		     box_y, box_x + item_x + 1, menu_height);
   275 
   276 	print_buttons(dialog, height, width, 0);
   277 	wmove(menu, choice, item_x + 1);
   278 	wrefresh(menu);
   279 
   280 	while (key != KEY_ESC) {
   281 		key = wgetch(menu);
   282 
   283 		if (key < 256 && isalpha(key))
   284 			key = tolower(key);
   285 
   286 		if (strchr("ynmh", key))
   287 			i = max_choice;
   288 		else {
   289 			for (i = choice + 1; i < max_choice; i++) {
   290 				item_set(scroll + i);
   291 				j = first_alpha(item_str(), "YyNnMmHh");
   292 				if (key == tolower(item_str()[j]))
   293 					break;
   294 			}
   295 			if (i == max_choice)
   296 				for (i = 0; i < max_choice; i++) {
   297 					item_set(scroll + i);
   298 					j = first_alpha(item_str(), "YyNnMmHh");
   299 					if (key == tolower(item_str()[j]))
   300 						break;
   301 				}
   302 		}
   303 
   304 		if (i < max_choice ||
   305 		    key == KEY_UP || key == KEY_DOWN ||
   306 		    key == '-' || key == '+' ||
   307 		    key == KEY_PPAGE || key == KEY_NPAGE) {
   308 			/* Remove highligt of current item */
   309 			print_item(scroll + choice, choice, FALSE);
   310 
   311 			if (key == KEY_UP || key == '-') {
   312 				if (choice < 2 && scroll) {
   313 					/* Scroll menu down */
   314 					do_scroll(menu, &scroll, -1);
   315 
   316 					print_item(scroll, 0, FALSE);
   317 				} else
   318 					choice = MAX(choice - 1, 0);
   319 
   320 			} else if (key == KEY_DOWN || key == '+') {
   321 				print_item(scroll+choice, choice, FALSE);
   322 
   323 				if ((choice > max_choice - 3) &&
   324 				    (scroll + max_choice < item_count())) {
   325 					/* Scroll menu up */
   326 					do_scroll(menu, &scroll, 1);
   327 
   328 					print_item(scroll+max_choice - 1,
   329 						   max_choice - 1, FALSE);
   330 				} else
   331 					choice = MIN(choice + 1, max_choice - 1);
   332 
   333 			} else if (key == KEY_PPAGE) {
   334 				scrollok(menu, TRUE);
   335 				for (i = 0; (i < max_choice); i++) {
   336 					if (scroll > 0) {
   337 						do_scroll(menu, &scroll, -1);
   338 						print_item(scroll, 0, FALSE);
   339 					} else {
   340 						if (choice > 0)
   341 							choice--;
   342 					}
   343 				}
   344 
   345 			} else if (key == KEY_NPAGE) {
   346 				for (i = 0; (i < max_choice); i++) {
   347 					if (scroll + max_choice < item_count()) {
   348 						do_scroll(menu, &scroll, 1);
   349 						print_item(scroll+max_choice-1,
   350 							   max_choice - 1, FALSE);
   351 					} else {
   352 						if (choice + 1 < max_choice)
   353 							choice++;
   354 					}
   355 				}
   356 			} else
   357 				choice = i;
   358 
   359 			print_item(scroll + choice, choice, TRUE);
   360 
   361 			print_arrows(dialog, item_count(), scroll,
   362 				     box_y, box_x + item_x + 1, menu_height);
   363 
   364 			wnoutrefresh(dialog);
   365 			wrefresh(menu);
   366 
   367 			continue;	/* wait for another key press */
   368 		}
   369 
   370 		switch (key) {
   371 		case KEY_LEFT:
   372 		case TAB:
   373 		case KEY_RIGHT:
   374 			button = ((key == KEY_LEFT ? --button : ++button) < 0)
   375 			    ? 2 : (button > 2 ? 0 : button);
   376 
   377 			print_buttons(dialog, height, width, button);
   378 			wrefresh(menu);
   379 			break;
   380 		case ' ':
   381 		case 's':
   382 		case 'y':
   383 		case 'n':
   384 		case 'm':
   385 		case '/':
   386 			/* save scroll info */
   387 			*s_scroll = scroll;
   388 			delwin(menu);
   389 			delwin(dialog);
   390 			item_set(scroll + choice);
   391 			item_set_selected(1);
   392 			switch (key) {
   393 			case 's':
   394 				return 3;
   395 			case 'y':
   396 				return 3;
   397 			case 'n':
   398 				return 4;
   399 			case 'm':
   400 				return 5;
   401 			case ' ':
   402 				return 6;
   403 			case '/':
   404 				return 7;
   405 			}
   406 			return 0;
   407 		case 'h':
   408 		case '?':
   409 			button = 2;
   410 		case '\n':
   411 			*s_scroll = scroll;
   412 			delwin(menu);
   413 			delwin(dialog);
   414 			item_set(scroll + choice);
   415 			item_set_selected(1);
   416 			return button;
   417 		case 'e':
   418 		case 'x':
   419 			key = KEY_ESC;
   420 			break;
   421 		case KEY_ESC:
   422 			key = on_key_esc(menu);
   423 			break;
   424 		case KEY_RESIZE:
   425 			on_key_resize();
   426 			delwin(menu);
   427 			delwin(dialog);
   428 			goto do_resize;
   429 		}
   430 	}
   431 	delwin(menu);
   432 	delwin(dialog);
   433 	return key;		/* ESC pressed */
   434 }