kconfig/lxdialog/menubox.c
branch1.2
changeset 731 65614732cfe7
child 943 1cca90ce0481
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/kconfig/lxdialog/menubox.c	Sat Jul 26 15:14:48 2008 +0000
     1.3 @@ -0,0 +1,434 @@
     1.4 +/*
     1.5 + *  menubox.c -- implements the menu box
     1.6 + *
     1.7 + *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
     1.8 + *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
     1.9 + *
    1.10 + *  This program is free software; you can redistribute it and/or
    1.11 + *  modify it under the terms of the GNU General Public License
    1.12 + *  as published by the Free Software Foundation; either version 2
    1.13 + *  of the License, or (at your option) any later version.
    1.14 + *
    1.15 + *  This program is distributed in the hope that it will be useful,
    1.16 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.17 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.18 + *  GNU General Public License for more details.
    1.19 + *
    1.20 + *  You should have received a copy of the GNU General Public License
    1.21 + *  along with this program; if not, write to the Free Software
    1.22 + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    1.23 + */
    1.24 +
    1.25 +/*
    1.26 + *  Changes by Clifford Wolf (god@clifford.at)
    1.27 + *
    1.28 + *  [ 1998-06-13 ]
    1.29 + *
    1.30 + *    *)  A bugfix for the Page-Down problem
    1.31 + *
    1.32 + *    *)  Formerly when I used Page Down and Page Up, the cursor would be set 
    1.33 + *        to the first position in the menu box.  Now lxdialog is a bit
    1.34 + *        smarter and works more like other menu systems (just have a look at
    1.35 + *        it).
    1.36 + *
    1.37 + *    *)  Formerly if I selected something my scrolling would be broken because
    1.38 + *        lxdialog is re-invoked by the Menuconfig shell script, can't
    1.39 + *        remember the last scrolling position, and just sets it so that the
    1.40 + *        cursor is at the bottom of the box.  Now it writes the temporary file
    1.41 + *        lxdialog.scrltmp which contains this information. The file is
    1.42 + *        deleted by lxdialog if the user leaves a submenu or enters a new
    1.43 + *        one, but it would be nice if Menuconfig could make another "rm -f"
    1.44 + *        just to be sure.  Just try it out - you will recognise a difference!
    1.45 + *
    1.46 + *  [ 1998-06-14 ]
    1.47 + *
    1.48 + *    *)  Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
    1.49 + *        and menus change their size on the fly.
    1.50 + *
    1.51 + *    *)  If for some reason the last scrolling position is not saved by
    1.52 + *        lxdialog, it sets the scrolling so that the selected item is in the
    1.53 + *        middle of the menu box, not at the bottom.
    1.54 + *
    1.55 + * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
    1.56 + * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
    1.57 + * This fixes a bug in Menuconfig where using ' ' to descend into menus
    1.58 + * would leave mis-synchronized lxdialog.scrltmp files lying around,
    1.59 + * fscanf would read in 'scroll', and eventually that value would get used.
    1.60 + */
    1.61 +
    1.62 +#include "dialog.h"
    1.63 +
    1.64 +static int menu_width, item_x;
    1.65 +
    1.66 +/*
    1.67 + * Print menu item
    1.68 + */
    1.69 +static void do_print_item(WINDOW * win, const char *item, int line_y,
    1.70 +                          int selected, int hotkey)
    1.71 +{
    1.72 +	int j;
    1.73 +	char *menu_item = malloc(menu_width + 1);
    1.74 +
    1.75 +	strncpy(menu_item, item, menu_width - item_x);
    1.76 +	menu_item[menu_width - item_x] = '\0';
    1.77 +	j = first_alpha(menu_item, "YyNnMmHh");
    1.78 +
    1.79 +	/* Clear 'residue' of last item */
    1.80 +	wattrset(win, dlg.menubox.atr);
    1.81 +	wmove(win, line_y, 0);
    1.82 +#if OLD_NCURSES
    1.83 +	{
    1.84 +		int i;
    1.85 +		for (i = 0; i < menu_width; i++)
    1.86 +			waddch(win, ' ');
    1.87 +	}
    1.88 +#else
    1.89 +	wclrtoeol(win);
    1.90 +#endif
    1.91 +	wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
    1.92 +	mvwaddstr(win, line_y, item_x, menu_item);
    1.93 +	if (hotkey) {
    1.94 +		wattrset(win, selected ? dlg.tag_key_selected.atr
    1.95 +			 : dlg.tag_key.atr);
    1.96 +		mvwaddch(win, line_y, item_x + j, menu_item[j]);
    1.97 +	}
    1.98 +	if (selected) {
    1.99 +		wmove(win, line_y, item_x + 1);
   1.100 +	}
   1.101 +	free(menu_item);
   1.102 +	wrefresh(win);
   1.103 +}
   1.104 +
   1.105 +#define print_item(index, choice, selected)				\
   1.106 +do {									\
   1.107 +	item_set(index);						\
   1.108 +	do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \
   1.109 +} while (0)
   1.110 +
   1.111 +/*
   1.112 + * Print the scroll indicators.
   1.113 + */
   1.114 +static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x,
   1.115 +			 int height)
   1.116 +{
   1.117 +	int cur_y, cur_x;
   1.118 +
   1.119 +	getyx(win, cur_y, cur_x);
   1.120 +
   1.121 +	wmove(win, y, x);
   1.122 +
   1.123 +	if (scroll > 0) {
   1.124 +		wattrset(win, dlg.uarrow.atr);
   1.125 +		waddch(win, ACS_UARROW);
   1.126 +		waddstr(win, "(-)");
   1.127 +	} else {
   1.128 +		wattrset(win, dlg.menubox.atr);
   1.129 +		waddch(win, ACS_HLINE);
   1.130 +		waddch(win, ACS_HLINE);
   1.131 +		waddch(win, ACS_HLINE);
   1.132 +		waddch(win, ACS_HLINE);
   1.133 +	}
   1.134 +
   1.135 +	y = y + height + 1;
   1.136 +	wmove(win, y, x);
   1.137 +	wrefresh(win);
   1.138 +
   1.139 +	if ((height < item_no) && (scroll + height < item_no)) {
   1.140 +		wattrset(win, dlg.darrow.atr);
   1.141 +		waddch(win, ACS_DARROW);
   1.142 +		waddstr(win, "(+)");
   1.143 +	} else {
   1.144 +		wattrset(win, dlg.menubox_border.atr);
   1.145 +		waddch(win, ACS_HLINE);
   1.146 +		waddch(win, ACS_HLINE);
   1.147 +		waddch(win, ACS_HLINE);
   1.148 +		waddch(win, ACS_HLINE);
   1.149 +	}
   1.150 +
   1.151 +	wmove(win, cur_y, cur_x);
   1.152 +	wrefresh(win);
   1.153 +}
   1.154 +
   1.155 +/*
   1.156 + * Display the termination buttons.
   1.157 + */
   1.158 +static void print_buttons(WINDOW * win, int height, int width, int selected)
   1.159 +{
   1.160 +	int x = width / 2 - 16;
   1.161 +	int y = height - 2;
   1.162 +
   1.163 +	print_button(win, "Select", y, x, selected == 0);
   1.164 +	print_button(win, " Exit ", y, x + 12, selected == 1);
   1.165 +	print_button(win, " Help ", y, x + 24, selected == 2);
   1.166 +
   1.167 +	wmove(win, y, x + 1 + 12 * selected);
   1.168 +	wrefresh(win);
   1.169 +}
   1.170 +
   1.171 +/* scroll up n lines (n may be negative) */
   1.172 +static void do_scroll(WINDOW *win, int *scroll, int n)
   1.173 +{
   1.174 +	/* Scroll menu up */
   1.175 +	scrollok(win, TRUE);
   1.176 +	wscrl(win, n);
   1.177 +	scrollok(win, FALSE);
   1.178 +	*scroll = *scroll + n;
   1.179 +	wrefresh(win);
   1.180 +}
   1.181 +
   1.182 +/*
   1.183 + * Display a menu for choosing among a number of options
   1.184 + */
   1.185 +int dialog_menu(const char *title, const char *prompt,
   1.186 +                const void *selected, int *s_scroll)
   1.187 +{
   1.188 +	int i, j, x, y, box_x, box_y;
   1.189 +	int height, width, menu_height;
   1.190 +	int key = 0, button = 0, scroll = 0, choice = 0;
   1.191 +	int first_item =  0, max_choice;
   1.192 +	WINDOW *dialog, *menu;
   1.193 +
   1.194 +do_resize:
   1.195 +	height = getmaxy(stdscr);
   1.196 +	width = getmaxx(stdscr);
   1.197 +	if (height < 15 || width < 65)
   1.198 +		return -ERRDISPLAYTOOSMALL;
   1.199 +
   1.200 +	height -= 4;
   1.201 +	width  -= 5;
   1.202 +	menu_height = height - 10;
   1.203 +
   1.204 +	max_choice = MIN(menu_height, item_count());
   1.205 +
   1.206 +	/* center dialog box on screen */
   1.207 +	x = (COLS - width) / 2;
   1.208 +	y = (LINES - height) / 2;
   1.209 +
   1.210 +	draw_shadow(stdscr, y, x, height, width);
   1.211 +
   1.212 +	dialog = newwin(height, width, y, x);
   1.213 +	keypad(dialog, TRUE);
   1.214 +
   1.215 +	draw_box(dialog, 0, 0, height, width,
   1.216 +		 dlg.dialog.atr, dlg.border.atr);
   1.217 +	wattrset(dialog, dlg.border.atr);
   1.218 +	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
   1.219 +	for (i = 0; i < width - 2; i++)
   1.220 +		waddch(dialog, ACS_HLINE);
   1.221 +	wattrset(dialog, dlg.dialog.atr);
   1.222 +	wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
   1.223 +	waddch(dialog, ACS_RTEE);
   1.224 +
   1.225 +	print_title(dialog, title, width);
   1.226 +
   1.227 +	wattrset(dialog, dlg.dialog.atr);
   1.228 +	print_autowrap(dialog, prompt, width - 2, 1, 3);
   1.229 +
   1.230 +	menu_width = width - 6;
   1.231 +	box_y = height - menu_height - 5;
   1.232 +	box_x = (width - menu_width) / 2 - 1;
   1.233 +
   1.234 +	/* create new window for the menu */
   1.235 +	menu = subwin(dialog, menu_height, menu_width,
   1.236 +		      y + box_y + 1, x + box_x + 1);
   1.237 +	keypad(menu, TRUE);
   1.238 +
   1.239 +	/* draw a box around the menu items */
   1.240 +	draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2,
   1.241 +		 dlg.menubox_border.atr, dlg.menubox.atr);
   1.242 +
   1.243 +	if (menu_width >= 80)
   1.244 +		item_x = (menu_width - 70) / 2;
   1.245 +	else
   1.246 +		item_x = 4;
   1.247 +
   1.248 +	/* Set choice to default item */
   1.249 +	item_foreach()
   1.250 +		if (selected && (selected == item_data()))
   1.251 +			choice = item_n();
   1.252 +	/* get the saved scroll info */
   1.253 +	scroll = *s_scroll;
   1.254 +	if ((scroll <= choice) && (scroll + max_choice > choice) &&
   1.255 +	   (scroll >= 0) && (scroll + max_choice <= item_count())) {
   1.256 +		first_item = scroll;
   1.257 +		choice = choice - scroll;
   1.258 +	} else {
   1.259 +		scroll = 0;
   1.260 +	}
   1.261 +	if ((choice >= max_choice)) {
   1.262 +		if (choice >= item_count() - max_choice / 2)
   1.263 +			scroll = first_item = item_count() - max_choice;
   1.264 +		else
   1.265 +			scroll = first_item = choice - max_choice / 2;
   1.266 +		choice = choice - scroll;
   1.267 +	}
   1.268 +
   1.269 +	/* Print the menu */
   1.270 +	for (i = 0; i < max_choice; i++) {
   1.271 +		print_item(first_item + i, i, i == choice);
   1.272 +	}
   1.273 +
   1.274 +	wnoutrefresh(menu);
   1.275 +
   1.276 +	print_arrows(dialog, item_count(), scroll,
   1.277 +		     box_y, box_x + item_x + 1, menu_height);
   1.278 +
   1.279 +	print_buttons(dialog, height, width, 0);
   1.280 +	wmove(menu, choice, item_x + 1);
   1.281 +	wrefresh(menu);
   1.282 +
   1.283 +	while (key != KEY_ESC) {
   1.284 +		key = wgetch(menu);
   1.285 +
   1.286 +		if (key < 256 && isalpha(key))
   1.287 +			key = tolower(key);
   1.288 +
   1.289 +		if (strchr("ynmh", key))
   1.290 +			i = max_choice;
   1.291 +		else {
   1.292 +			for (i = choice + 1; i < max_choice; i++) {
   1.293 +				item_set(scroll + i);
   1.294 +				j = first_alpha(item_str(), "YyNnMmHh");
   1.295 +				if (key == tolower(item_str()[j]))
   1.296 +					break;
   1.297 +			}
   1.298 +			if (i == max_choice)
   1.299 +				for (i = 0; i < max_choice; i++) {
   1.300 +					item_set(scroll + i);
   1.301 +					j = first_alpha(item_str(), "YyNnMmHh");
   1.302 +					if (key == tolower(item_str()[j]))
   1.303 +						break;
   1.304 +				}
   1.305 +		}
   1.306 +
   1.307 +		if (i < max_choice ||
   1.308 +		    key == KEY_UP || key == KEY_DOWN ||
   1.309 +		    key == '-' || key == '+' ||
   1.310 +		    key == KEY_PPAGE || key == KEY_NPAGE) {
   1.311 +			/* Remove highligt of current item */
   1.312 +			print_item(scroll + choice, choice, FALSE);
   1.313 +
   1.314 +			if (key == KEY_UP || key == '-') {
   1.315 +				if (choice < 2 && scroll) {
   1.316 +					/* Scroll menu down */
   1.317 +					do_scroll(menu, &scroll, -1);
   1.318 +
   1.319 +					print_item(scroll, 0, FALSE);
   1.320 +				} else
   1.321 +					choice = MAX(choice - 1, 0);
   1.322 +
   1.323 +			} else if (key == KEY_DOWN || key == '+') {
   1.324 +				print_item(scroll+choice, choice, FALSE);
   1.325 +
   1.326 +				if ((choice > max_choice - 3) &&
   1.327 +				    (scroll + max_choice < item_count())) {
   1.328 +					/* Scroll menu up */
   1.329 +					do_scroll(menu, &scroll, 1);
   1.330 +
   1.331 +					print_item(scroll+max_choice - 1,
   1.332 +						   max_choice - 1, FALSE);
   1.333 +				} else
   1.334 +					choice = MIN(choice + 1, max_choice - 1);
   1.335 +
   1.336 +			} else if (key == KEY_PPAGE) {
   1.337 +				scrollok(menu, TRUE);
   1.338 +				for (i = 0; (i < max_choice); i++) {
   1.339 +					if (scroll > 0) {
   1.340 +						do_scroll(menu, &scroll, -1);
   1.341 +						print_item(scroll, 0, FALSE);
   1.342 +					} else {
   1.343 +						if (choice > 0)
   1.344 +							choice--;
   1.345 +					}
   1.346 +				}
   1.347 +
   1.348 +			} else if (key == KEY_NPAGE) {
   1.349 +				for (i = 0; (i < max_choice); i++) {
   1.350 +					if (scroll + max_choice < item_count()) {
   1.351 +						do_scroll(menu, &scroll, 1);
   1.352 +						print_item(scroll+max_choice-1,
   1.353 +							   max_choice - 1, FALSE);
   1.354 +					} else {
   1.355 +						if (choice + 1 < max_choice)
   1.356 +							choice++;
   1.357 +					}
   1.358 +				}
   1.359 +			} else
   1.360 +				choice = i;
   1.361 +
   1.362 +			print_item(scroll + choice, choice, TRUE);
   1.363 +
   1.364 +			print_arrows(dialog, item_count(), scroll,
   1.365 +				     box_y, box_x + item_x + 1, menu_height);
   1.366 +
   1.367 +			wnoutrefresh(dialog);
   1.368 +			wrefresh(menu);
   1.369 +
   1.370 +			continue;	/* wait for another key press */
   1.371 +		}
   1.372 +
   1.373 +		switch (key) {
   1.374 +		case KEY_LEFT:
   1.375 +		case TAB:
   1.376 +		case KEY_RIGHT:
   1.377 +			button = ((key == KEY_LEFT ? --button : ++button) < 0)
   1.378 +			    ? 2 : (button > 2 ? 0 : button);
   1.379 +
   1.380 +			print_buttons(dialog, height, width, button);
   1.381 +			wrefresh(menu);
   1.382 +			break;
   1.383 +		case ' ':
   1.384 +		case 's':
   1.385 +		case 'y':
   1.386 +		case 'n':
   1.387 +		case 'm':
   1.388 +		case '/':
   1.389 +			/* save scroll info */
   1.390 +			*s_scroll = scroll;
   1.391 +			delwin(menu);
   1.392 +			delwin(dialog);
   1.393 +			item_set(scroll + choice);
   1.394 +			item_set_selected(1);
   1.395 +			switch (key) {
   1.396 +			case 's':
   1.397 +				return 3;
   1.398 +			case 'y':
   1.399 +				return 3;
   1.400 +			case 'n':
   1.401 +				return 4;
   1.402 +			case 'm':
   1.403 +				return 5;
   1.404 +			case ' ':
   1.405 +				return 6;
   1.406 +			case '/':
   1.407 +				return 7;
   1.408 +			}
   1.409 +			return 0;
   1.410 +		case 'h':
   1.411 +		case '?':
   1.412 +			button = 2;
   1.413 +		case '\n':
   1.414 +			*s_scroll = scroll;
   1.415 +			delwin(menu);
   1.416 +			delwin(dialog);
   1.417 +			item_set(scroll + choice);
   1.418 +			item_set_selected(1);
   1.419 +			return button;
   1.420 +		case 'e':
   1.421 +		case 'x':
   1.422 +			key = KEY_ESC;
   1.423 +			break;
   1.424 +		case KEY_ESC:
   1.425 +			key = on_key_esc(menu);
   1.426 +			break;
   1.427 +		case KEY_RESIZE:
   1.428 +			on_key_resize();
   1.429 +			delwin(menu);
   1.430 +			delwin(dialog);
   1.431 +			goto do_resize;
   1.432 +		}
   1.433 +	}
   1.434 +	delwin(menu);
   1.435 +	delwin(dialog);
   1.436 +	return key;		/* ESC pressed */
   1.437 +}