menubox.c 10.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 *  menubox.c -- implements the menu box
 *
 *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
 *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 *  Changes by Clifford Wolf (god@clifford.at)
 *
 *  [ 1998-06-13 ]
 *
 *    *)  A bugfix for the Page-Down problem
 *
W
Wang YanQing 已提交
29
 *    *)  Formerly when I used Page Down and Page Up, the cursor would be set
L
Linus Torvalds 已提交
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
 *        to the first position in the menu box.  Now lxdialog is a bit
 *        smarter and works more like other menu systems (just have a look at
 *        it).
 *
 *    *)  Formerly if I selected something my scrolling would be broken because
 *        lxdialog is re-invoked by the Menuconfig shell script, can't
 *        remember the last scrolling position, and just sets it so that the
 *        cursor is at the bottom of the box.  Now it writes the temporary file
 *        lxdialog.scrltmp which contains this information. The file is
 *        deleted by lxdialog if the user leaves a submenu or enters a new
 *        one, but it would be nice if Menuconfig could make another "rm -f"
 *        just to be sure.  Just try it out - you will recognise a difference!
 *
 *  [ 1998-06-14 ]
 *
 *    *)  Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
 *        and menus change their size on the fly.
 *
 *    *)  If for some reason the last scrolling position is not saved by
 *        lxdialog, it sets the scrolling so that the selected item is in the
 *        middle of the menu box, not at the bottom.
 *
 * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
 * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
 * This fixes a bug in Menuconfig where using ' ' to descend into menus
 * would leave mis-synchronized lxdialog.scrltmp files lying around,
 * fscanf would read in 'scroll', and eventually that value would get used.
 */

#include "dialog.h"

R
Roman Zippel 已提交
61
static int menu_width, item_x;
L
Linus Torvalds 已提交
62 63 64 65

/*
 * Print menu item
 */
66
static void do_print_item(WINDOW * win, const char *item, int line_y,
67
                          int selected, int hotkey)
L
Linus Torvalds 已提交
68
{
S
Sam Ravnborg 已提交
69
	int j;
70
	char *menu_item = malloc(menu_width + 1);
L
Linus Torvalds 已提交
71

R
Roman Zippel 已提交
72
	strncpy(menu_item, item, menu_width - item_x);
73
	menu_item[menu_width - item_x] = '\0';
S
Sam Ravnborg 已提交
74
	j = first_alpha(menu_item, "YyNnMmHh");
L
Linus Torvalds 已提交
75

S
Sam Ravnborg 已提交
76
	/* Clear 'residue' of last item */
77
	wattrset(win, dlg.menubox.atr);
78
	wmove(win, line_y, 0);
L
Linus Torvalds 已提交
79
#if OLD_NCURSES
S
Sam Ravnborg 已提交
80 81 82 83 84
	{
		int i;
		for (i = 0; i < menu_width; i++)
			waddch(win, ' ');
	}
L
Linus Torvalds 已提交
85
#else
S
Sam Ravnborg 已提交
86
	wclrtoeol(win);
L
Linus Torvalds 已提交
87
#endif
88
	wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
89
	mvwaddstr(win, line_y, item_x, menu_item);
S
Sam Ravnborg 已提交
90
	if (hotkey) {
91 92
		wattrset(win, selected ? dlg.tag_key_selected.atr
			 : dlg.tag_key.atr);
93
		mvwaddch(win, line_y, item_x + j, menu_item[j]);
S
Sam Ravnborg 已提交
94 95
	}
	if (selected) {
96
		wmove(win, line_y, item_x + 1);
S
Sam Ravnborg 已提交
97
	}
98
	free(menu_item);
99
	wrefresh(win);
L
Linus Torvalds 已提交
100 101
}

102 103 104 105
#define print_item(index, choice, selected)				\
do {									\
	item_set(index);						\
	do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \
106 107
} while (0)

L
Linus Torvalds 已提交
108 109 110
/*
 * Print the scroll indicators.
 */
S
Sam Ravnborg 已提交
111 112
static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x,
			 int height)
L
Linus Torvalds 已提交
113
{
S
Sam Ravnborg 已提交
114 115 116 117 118 119 120
	int cur_y, cur_x;

	getyx(win, cur_y, cur_x);

	wmove(win, y, x);

	if (scroll > 0) {
121
		wattrset(win, dlg.uarrow.atr);
S
Sam Ravnborg 已提交
122 123 124
		waddch(win, ACS_UARROW);
		waddstr(win, "(-)");
	} else {
125
		wattrset(win, dlg.menubox.atr);
S
Sam Ravnborg 已提交
126 127 128 129 130 131 132 133
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
	}

	y = y + height + 1;
	wmove(win, y, x);
134
	wrefresh(win);
S
Sam Ravnborg 已提交
135 136

	if ((height < item_no) && (scroll + height < item_no)) {
137
		wattrset(win, dlg.darrow.atr);
S
Sam Ravnborg 已提交
138 139 140
		waddch(win, ACS_DARROW);
		waddstr(win, "(+)");
	} else {
141
		wattrset(win, dlg.menubox_border.atr);
S
Sam Ravnborg 已提交
142 143 144 145 146 147 148
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
		waddch(win, ACS_HLINE);
	}

	wmove(win, cur_y, cur_x);
149
	wrefresh(win);
L
Linus Torvalds 已提交
150 151 152 153 154
}

/*
 * Display the termination buttons.
 */
S
Sam Ravnborg 已提交
155
static void print_buttons(WINDOW * win, int height, int width, int selected)
L
Linus Torvalds 已提交
156
{
W
Wang YanQing 已提交
157
	int x = width / 2 - 28;
S
Sam Ravnborg 已提交
158
	int y = height - 2;
L
Linus Torvalds 已提交
159

160 161 162
	print_button(win, gettext("Select"), y, x, selected == 0);
	print_button(win, gettext(" Exit "), y, x + 12, selected == 1);
	print_button(win, gettext(" Help "), y, x + 24, selected == 2);
W
Wang YanQing 已提交
163 164
	print_button(win, gettext(" Save "), y, x + 36, selected == 3);
	print_button(win, gettext(" Load "), y, x + 48, selected == 4);
L
Linus Torvalds 已提交
165

S
Sam Ravnborg 已提交
166 167
	wmove(win, y, x + 1 + 12 * selected);
	wrefresh(win);
L
Linus Torvalds 已提交
168 169
}

170 171 172 173 174 175 176 177 178 179 180
/* scroll up n lines (n may be negative) */
static void do_scroll(WINDOW *win, int *scroll, int n)
{
	/* Scroll menu up */
	scrollok(win, TRUE);
	wscrl(win, n);
	scrollok(win, FALSE);
	*scroll = *scroll + n;
	wrefresh(win);
}

L
Linus Torvalds 已提交
181 182 183
/*
 * Display a menu for choosing among a number of options
 */
S
Sam Ravnborg 已提交
184 185
int dialog_menu(const char *title, const char *prompt,
                const void *selected, int *s_scroll)
L
Linus Torvalds 已提交
186
{
S
Sam Ravnborg 已提交
187
	int i, j, x, y, box_x, box_y;
S
Sam Ravnborg 已提交
188
	int height, width, menu_height;
189 190
	int key = 0, button = 0, scroll = 0, choice = 0;
	int first_item =  0, max_choice;
S
Sam Ravnborg 已提交
191 192
	WINDOW *dialog, *menu;

S
Sam Ravnborg 已提交
193 194 195 196 197 198 199 200 201 202
do_resize:
	height = getmaxy(stdscr);
	width = getmaxx(stdscr);
	if (height < 15 || width < 65)
		return -ERRDISPLAYTOOSMALL;

	height -= 4;
	width  -= 5;
	menu_height = height - 10;

203
	max_choice = MIN(menu_height, item_count());
S
Sam Ravnborg 已提交
204 205 206 207 208 209 210 211 212 213

	/* center dialog box on screen */
	x = (COLS - width) / 2;
	y = (LINES - height) / 2;

	draw_shadow(stdscr, y, x, height, width);

	dialog = newwin(height, width, y, x);
	keypad(dialog, TRUE);

214 215 216
	draw_box(dialog, 0, 0, height, width,
		 dlg.dialog.atr, dlg.border.atr);
	wattrset(dialog, dlg.border.atr);
S
Sam Ravnborg 已提交
217 218 219
	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
	for (i = 0; i < width - 2; i++)
		waddch(dialog, ACS_HLINE);
220 221
	wattrset(dialog, dlg.dialog.atr);
	wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
S
Sam Ravnborg 已提交
222 223
	waddch(dialog, ACS_RTEE);

224
	print_title(dialog, title, width);
S
Sam Ravnborg 已提交
225

226
	wattrset(dialog, dlg.dialog.atr);
S
Sam Ravnborg 已提交
227 228 229 230 231 232 233 234 235 236 237 238 239
	print_autowrap(dialog, prompt, width - 2, 1, 3);

	menu_width = width - 6;
	box_y = height - menu_height - 5;
	box_x = (width - menu_width) / 2 - 1;

	/* create new window for the menu */
	menu = subwin(dialog, menu_height, menu_width,
		      y + box_y + 1, x + box_x + 1);
	keypad(menu, TRUE);

	/* draw a box around the menu items */
	draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2,
240
		 dlg.menubox_border.atr, dlg.menubox.atr);
S
Sam Ravnborg 已提交
241

S
Sam Ravnborg 已提交
242 243 244 245
	if (menu_width >= 80)
		item_x = (menu_width - 70) / 2;
	else
		item_x = 4;
R
Roman Zippel 已提交
246

247
	/* Set choice to default item */
248 249 250 251 252 253 254 255 256 257 258
	item_foreach()
		if (selected && (selected == item_data()))
			choice = item_n();
	/* get the saved scroll info */
	scroll = *s_scroll;
	if ((scroll <= choice) && (scroll + max_choice > choice) &&
	   (scroll >= 0) && (scroll + max_choice <= item_count())) {
		first_item = scroll;
		choice = choice - scroll;
	} else {
		scroll = 0;
L
Linus Torvalds 已提交
259
	}
260 261 262
	if ((choice >= max_choice)) {
		if (choice >= item_count() - max_choice / 2)
			scroll = first_item = item_count() - max_choice;
S
Sam Ravnborg 已提交
263 264 265 266
		else
			scroll = first_item = choice - max_choice / 2;
		choice = choice - scroll;
	}
L
Linus Torvalds 已提交
267

S
Sam Ravnborg 已提交
268 269
	/* Print the menu */
	for (i = 0; i < max_choice; i++) {
270
		print_item(first_item + i, i, i == choice);
S
Sam Ravnborg 已提交
271 272 273 274
	}

	wnoutrefresh(menu);

275
	print_arrows(dialog, item_count(), scroll,
R
Roman Zippel 已提交
276
		     box_y, box_x + item_x + 1, menu_height);
S
Sam Ravnborg 已提交
277 278

	print_buttons(dialog, height, width, 0);
R
Roman Zippel 已提交
279
	wmove(menu, choice, item_x + 1);
S
Sam Ravnborg 已提交
280 281
	wrefresh(menu);

282
	while (key != KEY_ESC) {
S
Sam Ravnborg 已提交
283 284 285 286 287 288 289 290 291
		key = wgetch(menu);

		if (key < 256 && isalpha(key))
			key = tolower(key);

		if (strchr("ynmh", key))
			i = max_choice;
		else {
			for (i = choice + 1; i < max_choice; i++) {
292 293 294
				item_set(scroll + i);
				j = first_alpha(item_str(), "YyNnMmHh");
				if (key == tolower(item_str()[j]))
S
Sam Ravnborg 已提交
295 296 297 298
					break;
			}
			if (i == max_choice)
				for (i = 0; i < max_choice; i++) {
299 300 301
					item_set(scroll + i);
					j = first_alpha(item_str(), "YyNnMmHh");
					if (key == tolower(item_str()[j]))
S
Sam Ravnborg 已提交
302 303 304 305 306 307 308 309
						break;
				}
		}

		if (i < max_choice ||
		    key == KEY_UP || key == KEY_DOWN ||
		    key == '-' || key == '+' ||
		    key == KEY_PPAGE || key == KEY_NPAGE) {
310
			/* Remove highligt of current item */
311
			print_item(scroll + choice, choice, FALSE);
S
Sam Ravnborg 已提交
312 313 314 315

			if (key == KEY_UP || key == '-') {
				if (choice < 2 && scroll) {
					/* Scroll menu down */
316
					do_scroll(menu, &scroll, -1);
S
Sam Ravnborg 已提交
317

318
					print_item(scroll, 0, FALSE);
S
Sam Ravnborg 已提交
319 320 321 322
				} else
					choice = MAX(choice - 1, 0);

			} else if (key == KEY_DOWN || key == '+') {
323
				print_item(scroll+choice, choice, FALSE);
S
Sam Ravnborg 已提交
324 325

				if ((choice > max_choice - 3) &&
326
				    (scroll + max_choice < item_count())) {
S
Sam Ravnborg 已提交
327
					/* Scroll menu up */
328
					do_scroll(menu, &scroll, 1);
S
Sam Ravnborg 已提交
329

330 331
					print_item(scroll+max_choice - 1,
						   max_choice - 1, FALSE);
S
Sam Ravnborg 已提交
332
				} else
S
Sam Ravnborg 已提交
333
					choice = MIN(choice + 1, max_choice - 1);
S
Sam Ravnborg 已提交
334 335 336 337 338

			} else if (key == KEY_PPAGE) {
				scrollok(menu, TRUE);
				for (i = 0; (i < max_choice); i++) {
					if (scroll > 0) {
339
						do_scroll(menu, &scroll, -1);
340
						print_item(scroll, 0, FALSE);
S
Sam Ravnborg 已提交
341 342 343 344 345 346 347 348
					} else {
						if (choice > 0)
							choice--;
					}
				}

			} else if (key == KEY_NPAGE) {
				for (i = 0; (i < max_choice); i++) {
349
					if (scroll + max_choice < item_count()) {
350
						do_scroll(menu, &scroll, 1);
351 352
						print_item(scroll+max_choice-1,
							   max_choice - 1, FALSE);
S
Sam Ravnborg 已提交
353 354 355 356 357 358 359 360
					} else {
						if (choice + 1 < max_choice)
							choice++;
					}
				}
			} else
				choice = i;

361
			print_item(scroll + choice, choice, TRUE);
S
Sam Ravnborg 已提交
362

363
			print_arrows(dialog, item_count(), scroll,
R
Roman Zippel 已提交
364
				     box_y, box_x + item_x + 1, menu_height);
S
Sam Ravnborg 已提交
365 366 367 368 369 370 371 372 373 374 375 376

			wnoutrefresh(dialog);
			wrefresh(menu);

			continue;	/* wait for another key press */
		}

		switch (key) {
		case KEY_LEFT:
		case TAB:
		case KEY_RIGHT:
			button = ((key == KEY_LEFT ? --button : ++button) < 0)
W
Wang YanQing 已提交
377
			    ? 4 : (button > 4 ? 0 : button);
S
Sam Ravnborg 已提交
378 379 380 381 382 383 384 385 386 387

			print_buttons(dialog, height, width, button);
			wrefresh(menu);
			break;
		case ' ':
		case 's':
		case 'y':
		case 'n':
		case 'm':
		case '/':
388 389 390 391
		case 'h':
		case '?':
		case 'z':
		case '\n':
S
Sam Ravnborg 已提交
392
			/* save scroll info */
393 394
			*s_scroll = scroll;
			delwin(menu);
S
Sam Ravnborg 已提交
395
			delwin(dialog);
396 397
			item_set(scroll + choice);
			item_set_selected(1);
S
Sam Ravnborg 已提交
398
			switch (key) {
399 400 401
			case 'h':
			case '?':
				return 2;
S
Sam Ravnborg 已提交
402 403
			case 's':
			case 'y':
W
Wang YanQing 已提交
404
				return 5;
S
Sam Ravnborg 已提交
405
			case 'n':
W
Wang YanQing 已提交
406
				return 6;
S
Sam Ravnborg 已提交
407
			case 'm':
W
Wang YanQing 已提交
408
				return 7;
S
Sam Ravnborg 已提交
409
			case ' ':
W
Wang YanQing 已提交
410
				return 8;
S
Sam Ravnborg 已提交
411
			case '/':
W
Wang YanQing 已提交
412
				return 9;
413
			case 'z':
W
Wang YanQing 已提交
414
				return 10;
415 416
			case '\n':
				return button;
S
Sam Ravnborg 已提交
417 418 419 420
			}
			return 0;
		case 'e':
		case 'x':
421 422 423 424
			key = KEY_ESC;
			break;
		case KEY_ESC:
			key = on_key_esc(menu);
S
Sam Ravnborg 已提交
425
			break;
S
Sam Ravnborg 已提交
426 427 428 429 430
		case KEY_RESIZE:
			on_key_resize();
			delwin(menu);
			delwin(dialog);
			goto do_resize;
S
Sam Ravnborg 已提交
431
		}
L
Linus Torvalds 已提交
432
	}
433
	delwin(menu);
S
Sam Ravnborg 已提交
434
	delwin(dialog);
435
	return key;		/* ESC pressed */
L
Linus Torvalds 已提交
436
}