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 61static int menu_width, item_x; 62 63/* 64 * Print menu item 65 */ 66static 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) \ 103do { \ 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 */ 111static 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 */ 155static void print_buttons(WINDOW * win, int height, int width, int selected) 156{ 157 int x = width / 2 - 28; 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 print_button(win, gettext(" Save "), y, x + 36, selected == 3); 164 print_button(win, gettext(" Load "), y, x + 48, selected == 4); 165 166 wmove(win, y, x + 1 + 12 * selected); 167 wrefresh(win); 168} 169 170/* scroll up n lines (n may be negative) */ 171static void do_scroll(WINDOW *win, int *scroll, int n) 172{ 173 /* Scroll menu up */ 174 scrollok(win, TRUE); 175 wscrl(win, n); 176 scrollok(win, FALSE); 177 *scroll = *scroll + n; 178 wrefresh(win); 179} 180 181/* 182 * Display a menu for choosing among a number of options 183 */ 184int dialog_menu(const char *title, const char *prompt, 185 const void *selected, int *s_scroll) 186{ 187 int i, j, x, y, box_x, box_y; 188 int height, width, menu_height; 189 int key = 0, button = 0, scroll = 0, choice = 0; 190 int first_item = 0, max_choice; 191 WINDOW *dialog, *menu; 192 193do_resize: 194 height = getmaxy(stdscr); 195 width = getmaxx(stdscr); 196 if (height < MENUBOX_HEIGTH_MIN || width < MENUBOX_WIDTH_MIN) 197 return -ERRDISPLAYTOOSMALL; 198 199 height -= 4; 200 width -= 5; 201 menu_height = height - 10; 202 203 max_choice = MIN(menu_height, item_count()); 204 205 /* center dialog box on screen */ 206 x = (getmaxx(stdscr) - width) / 2; 207 y = (getmaxy(stdscr) - height) / 2; 208 209 draw_shadow(stdscr, y, x, height, width); 210 211 dialog = newwin(height, width, y, x); 212 keypad(dialog, TRUE); 213 214 draw_box(dialog, 0, 0, height, width, 215 dlg.dialog.atr, dlg.border.atr); 216 wattrset(dialog, dlg.border.atr); 217 mvwaddch(dialog, height - 3, 0, ACS_LTEE); 218 for (i = 0; i < width - 2; i++) 219 waddch(dialog, ACS_HLINE); 220 wattrset(dialog, dlg.dialog.atr); 221 wbkgdset(dialog, dlg.dialog.atr & A_COLOR); 222 waddch(dialog, ACS_RTEE); 223 224 print_title(dialog, title, width); 225 226 wattrset(dialog, dlg.dialog.atr); 227 print_autowrap(dialog, prompt, width - 2, 1, 3); 228 229 menu_width = width - 6; 230 box_y = height - menu_height - 5; 231 box_x = (width - menu_width) / 2 - 1; 232 233 /* create new window for the menu */ 234 menu = subwin(dialog, menu_height, menu_width, 235 y + box_y + 1, x + box_x + 1); 236 keypad(menu, TRUE); 237 238 /* draw a box around the menu items */ 239 draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2, 240 dlg.menubox_border.atr, dlg.menubox.atr); 241 242 if (menu_width >= 80) 243 item_x = (menu_width - 70) / 2; 244 else 245 item_x = 4; 246 247 /* Set choice to default item */ 248 item_foreach() 249 if (selected && (selected == item_data())) 250 choice = item_n(); 251 /* get the saved scroll info */ 252 scroll = *s_scroll; 253 if ((scroll <= choice) && (scroll + max_choice > choice) && 254 (scroll >= 0) && (scroll + max_choice <= item_count())) { 255 first_item = scroll; 256 choice = choice - scroll; 257 } else { 258 scroll = 0; 259 } 260 if ((choice >= max_choice)) { 261 if (choice >= item_count() - max_choice / 2) 262 scroll = first_item = item_count() - max_choice; 263 else 264 scroll = first_item = choice - max_choice / 2; 265 choice = choice - scroll; 266 } 267 268 /* Print the menu */ 269 for (i = 0; i < max_choice; i++) { 270 print_item(first_item + i, i, i == choice); 271 } 272 273 wnoutrefresh(menu); 274 275 print_arrows(dialog, item_count(), scroll, 276 box_y, box_x + item_x + 1, menu_height); 277 278 print_buttons(dialog, height, width, 0); 279 wmove(menu, choice, item_x + 1); 280 wrefresh(menu); 281 282 while (key != KEY_ESC) { 283 key = wgetch(menu); 284 285 if (key < 256 && isalpha(key)) 286 key = tolower(key); 287 288 if (strchr("ynmh", key)) 289 i = max_choice; 290 else { 291 for (i = choice + 1; i < max_choice; i++) { 292 item_set(scroll + i); 293 j = first_alpha(item_str(), "YyNnMmHh"); 294 if (key == tolower(item_str()[j])) 295 break; 296 } 297 if (i == max_choice) 298 for (i = 0; i < max_choice; i++) { 299 item_set(scroll + i); 300 j = first_alpha(item_str(), "YyNnMmHh"); 301 if (key == tolower(item_str()[j])) 302 break; 303 } 304 } 305 306 if (item_count() != 0 && 307 (i < max_choice || 308 key == KEY_UP || key == KEY_DOWN || 309 key == '-' || key == '+' || 310 key == KEY_PPAGE || key == KEY_NPAGE)) { 311 /* Remove highligt of current item */ 312 print_item(scroll + choice, choice, FALSE); 313 314 if (key == KEY_UP || key == '-') { 315 if (choice < 2 && scroll) { 316 /* Scroll menu down */ 317 do_scroll(menu, &scroll, -1); 318 319 print_item(scroll, 0, FALSE); 320 } else 321 choice = MAX(choice - 1, 0); 322 323 } else if (key == KEY_DOWN || key == '+') { 324 print_item(scroll+choice, choice, FALSE); 325 326 if ((choice > max_choice - 3) && 327 (scroll + max_choice < item_count())) { 328 /* Scroll menu up */ 329 do_scroll(menu, &scroll, 1); 330 331 print_item(scroll+max_choice - 1, 332 max_choice - 1, FALSE); 333 } else 334 choice = MIN(choice + 1, max_choice - 1); 335 336 } else if (key == KEY_PPAGE) { 337 scrollok(menu, TRUE); 338 for (i = 0; (i < max_choice); i++) { 339 if (scroll > 0) { 340 do_scroll(menu, &scroll, -1); 341 print_item(scroll, 0, FALSE); 342 } else { 343 if (choice > 0) 344 choice--; 345 } 346 } 347 348 } else if (key == KEY_NPAGE) { 349 for (i = 0; (i < max_choice); i++) { 350 if (scroll + max_choice < item_count()) { 351 do_scroll(menu, &scroll, 1); 352 print_item(scroll+max_choice-1, 353 max_choice - 1, FALSE); 354 } else { 355 if (choice + 1 < max_choice) 356 choice++; 357 } 358 } 359 } else 360 choice = i; 361 362 print_item(scroll + choice, choice, TRUE); 363 364 print_arrows(dialog, item_count(), scroll, 365 box_y, box_x + item_x + 1, menu_height); 366 367 wnoutrefresh(dialog); 368 wrefresh(menu); 369 370 continue; /* wait for another key press */ 371 } 372 373 switch (key) { 374 case KEY_LEFT: 375 case TAB: 376 case KEY_RIGHT: 377 button = ((key == KEY_LEFT ? --button : ++button) < 0) 378 ? 4 : (button > 4 ? 0 : button); 379 380 print_buttons(dialog, height, width, button); 381 wrefresh(menu); 382 break; 383 case ' ': 384 case 's': 385 case 'y': 386 case 'n': 387 case 'm': 388 case '/': 389 case 'h': 390 case '?': 391 case 'z': 392 case '\n': 393 /* save scroll info */ 394 *s_scroll = scroll; 395 delwin(menu); 396 delwin(dialog); 397 item_set(scroll + choice); 398 item_set_selected(1); 399 switch (key) { 400 case 'h': 401 case '?': 402 return 2; 403 case 's': 404 case 'y': 405 return 5; 406 case 'n': 407 return 6; 408 case 'm': 409 return 7; 410 case ' ': 411 return 8; 412 case '/': 413 return 9; 414 case 'z': 415 return 10; 416 case '\n': 417 return button; 418 } 419 return 0; 420 case 'e': 421 case 'x': 422 key = KEY_ESC; 423 break; 424 case KEY_ESC: 425 key = on_key_esc(menu); 426 break; 427 case KEY_RESIZE: 428 on_key_resize(); 429 delwin(menu); 430 delwin(dialog); 431 goto do_resize; 432 } 433 } 434 delwin(menu); 435 delwin(dialog); 436 return key; /* ESC pressed */ 437} 438