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 choice, 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] = 0; 74 j = first_alpha(menu_item, "YyNnMmHh"); 75 76 /* Clear 'residue' of last item */ 77 wattrset(win, menubox_attr); 78 wmove(win, choice, 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 ? item_selected_attr : item_attr); 89 mvwaddstr(win, choice, item_x, menu_item); 90 if (hotkey) { 91 wattrset(win, selected ? tag_key_selected_attr : tag_key_attr); 92 mvwaddch(win, choice, item_x + j, menu_item[j]); 93 } 94 if (selected) { 95 wmove(win, choice, item_x + 1); 96 } 97 free(menu_item); 98 wrefresh(win); 99} 100 101#define print_item(index, choice, selected) \ 102do {\ 103 int hotkey = (items[(index) * 2][0] != ':'); \ 104 do_print_item(menu, items[(index) * 2 + 1], choice, selected, hotkey); \ 105} while (0) 106 107/* 108 * Print the scroll indicators. 109 */ 110static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x, 111 int height) 112{ 113 int cur_y, cur_x; 114 115 getyx(win, cur_y, cur_x); 116 117 wmove(win, y, x); 118 119 if (scroll > 0) { 120 wattrset(win, uarrow_attr); 121 waddch(win, ACS_UARROW); 122 waddstr(win, "(-)"); 123 } else { 124 wattrset(win, menubox_attr); 125 waddch(win, ACS_HLINE); 126 waddch(win, ACS_HLINE); 127 waddch(win, ACS_HLINE); 128 waddch(win, ACS_HLINE); 129 } 130 131 y = y + height + 1; 132 wmove(win, y, x); 133 wrefresh(win); 134 135 if ((height < item_no) && (scroll + height < item_no)) { 136 wattrset(win, darrow_attr); 137 waddch(win, ACS_DARROW); 138 waddstr(win, "(+)"); 139 } else { 140 wattrset(win, menubox_border_attr); 141 waddch(win, ACS_HLINE); 142 waddch(win, ACS_HLINE); 143 waddch(win, ACS_HLINE); 144 waddch(win, ACS_HLINE); 145 } 146 147 wmove(win, cur_y, cur_x); 148 wrefresh(win); 149} 150 151/* 152 * Display the termination buttons. 153 */ 154static void print_buttons(WINDOW * win, int height, int width, int selected) 155{ 156 int x = width / 2 - 16; 157 int y = height - 2; 158 159 print_button(win, "Select", y, x, selected == 0); 160 print_button(win, " Exit ", y, x + 12, selected == 1); 161 print_button(win, " Help ", y, x + 24, selected == 2); 162 163 wmove(win, y, x + 1 + 12 * selected); 164 wrefresh(win); 165} 166 167/* scroll up n lines (n may be negative) */ 168static void do_scroll(WINDOW *win, int *scroll, int n) 169{ 170 /* Scroll menu up */ 171 scrollok(win, TRUE); 172 wscrl(win, n); 173 scrollok(win, FALSE); 174 *scroll = *scroll + n; 175 wrefresh(win); 176} 177 178/* 179 * Display a menu for choosing among a number of options 180 */ 181int dialog_menu(const char *title, const char *prompt, int height, int width, 182 int menu_height, const char *current, int item_no, 183 const char *const *items) 184{ 185 int i, j, x, y, box_x, box_y; 186 int key = 0, button = 0, scroll = 0, choice = 0; 187 int first_item = 0, max_choice; 188 WINDOW *dialog, *menu; 189 FILE *f; 190 191 max_choice = MIN(menu_height, item_no); 192 193 /* center dialog box on screen */ 194 x = (COLS - width) / 2; 195 y = (LINES - height) / 2; 196 197 draw_shadow(stdscr, y, x, height, width); 198 199 dialog = newwin(height, width, y, x); 200 keypad(dialog, TRUE); 201 202 draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); 203 wattrset(dialog, border_attr); 204 mvwaddch(dialog, height - 3, 0, ACS_LTEE); 205 for (i = 0; i < width - 2; i++) 206 waddch(dialog, ACS_HLINE); 207 wattrset(dialog, dialog_attr); 208 wbkgdset(dialog, dialog_attr & A_COLOR); 209 waddch(dialog, ACS_RTEE); 210 211 print_title(dialog, title, width); 212 213 wattrset(dialog, dialog_attr); 214 print_autowrap(dialog, prompt, width - 2, 1, 3); 215 216 menu_width = width - 6; 217 box_y = height - menu_height - 5; 218 box_x = (width - menu_width) / 2 - 1; 219 220 /* create new window for the menu */ 221 menu = subwin(dialog, menu_height, menu_width, 222 y + box_y + 1, x + box_x + 1); 223 keypad(menu, TRUE); 224 225 /* draw a box around the menu items */ 226 draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2, 227 menubox_border_attr, menubox_attr); 228 229 item_x = (menu_width - 70) / 2; 230 231 /* Set choice to default item */ 232 for (i = 0; i < item_no; i++) 233 if (strcmp(current, items[i * 2]) == 0) 234 choice = i; 235 236 /* get the scroll info from the temp file */ 237 if ((f = fopen("lxdialog.scrltmp", "r")) != NULL) { 238 if ((fscanf(f, "%d\n", &scroll) == 1) && (scroll <= choice) && 239 (scroll + max_choice > choice) && (scroll >= 0) && 240 (scroll + max_choice <= item_no)) { 241 first_item = scroll; 242 choice = choice - scroll; 243 fclose(f); 244 } else { 245 scroll = 0; 246 remove("lxdialog.scrltmp"); 247 fclose(f); 248 f = NULL; 249 } 250 } 251 if ((choice >= max_choice) || (f == NULL && choice >= max_choice / 2)) { 252 if (choice >= item_no - max_choice / 2) 253 scroll = first_item = item_no - max_choice; 254 else 255 scroll = first_item = choice - max_choice / 2; 256 choice = choice - scroll; 257 } 258 259 /* Print the menu */ 260 for (i = 0; i < max_choice; i++) { 261 print_item(first_item + i, i, i == choice); 262 } 263 264 wnoutrefresh(menu); 265 266 print_arrows(dialog, item_no, scroll, 267 box_y, box_x + item_x + 1, menu_height); 268 269 print_buttons(dialog, height, width, 0); 270 wmove(menu, choice, item_x + 1); 271 wrefresh(menu); 272 273 while (key != ESC) { 274 key = wgetch(menu); 275 276 if (key < 256 && isalpha(key)) 277 key = tolower(key); 278 279 if (strchr("ynmh", key)) 280 i = max_choice; 281 else { 282 for (i = choice + 1; i < max_choice; i++) { 283 j = first_alpha(items[(scroll + i) * 2 + 1], "YyNnMmHh"); 284 if (key == tolower(items[(scroll + i) * 2 + 1][j])) 285 break; 286 } 287 if (i == max_choice) 288 for (i = 0; i < max_choice; i++) { 289 j = first_alpha(items [(scroll + i) * 2 + 1], "YyNnMmHh"); 290 if (key == tolower(items[(scroll + i) * 2 + 1][j])) 291 break; 292 } 293 } 294 295 if (i < max_choice || 296 key == KEY_UP || key == KEY_DOWN || 297 key == '-' || key == '+' || 298 key == KEY_PPAGE || key == KEY_NPAGE) { 299 /* Remove highligt of current item */ 300 print_item(scroll + choice, choice, FALSE); 301 302 if (key == KEY_UP || key == '-') { 303 if (choice < 2 && scroll) { 304 /* Scroll menu down */ 305 do_scroll(menu, &scroll, -1); 306 307 print_item(scroll, 0, FALSE); 308 } else 309 choice = MAX(choice - 1, 0); 310 311 } else if (key == KEY_DOWN || key == '+') { 312 print_item(scroll+choice, choice, FALSE); 313 314 if ((choice > max_choice - 3) && 315 (scroll + max_choice < item_no)) { 316 /* Scroll menu up */ 317 do_scroll(menu, &scroll, 1); 318 319 print_item(scroll+max_choice - 1, 320 max_choice - 1, FALSE); 321 } else 322 choice = MIN(choice + 1, max_choice - 1); 323 324 } else if (key == KEY_PPAGE) { 325 scrollok(menu, TRUE); 326 for (i = 0; (i < max_choice); i++) { 327 if (scroll > 0) { 328 do_scroll(menu, &scroll, -1); 329 print_item(scroll, 0, FALSE); 330 } else { 331 if (choice > 0) 332 choice--; 333 } 334 } 335 336 } else if (key == KEY_NPAGE) { 337 for (i = 0; (i < max_choice); i++) { 338 if (scroll + max_choice < item_no) { 339 do_scroll(menu, &scroll, 1); 340 print_item(scroll+max_choice-1, 341 max_choice - 1, FALSE); 342 } else { 343 if (choice + 1 < max_choice) 344 choice++; 345 } 346 } 347 } else 348 choice = i; 349 350 print_item(scroll + choice, choice, TRUE); 351 352 print_arrows(dialog, item_no, scroll, 353 box_y, box_x + item_x + 1, menu_height); 354 355 wnoutrefresh(dialog); 356 wrefresh(menu); 357 358 continue; /* wait for another key press */ 359 } 360 361 switch (key) { 362 case KEY_LEFT: 363 case TAB: 364 case KEY_RIGHT: 365 button = ((key == KEY_LEFT ? --button : ++button) < 0) 366 ? 2 : (button > 2 ? 0 : button); 367 368 print_buttons(dialog, height, width, button); 369 wrefresh(menu); 370 break; 371 case ' ': 372 case 's': 373 case 'y': 374 case 'n': 375 case 'm': 376 case '/': 377 /* save scroll info */ 378 if ((f = fopen("lxdialog.scrltmp", "w")) != NULL) { 379 fprintf(f, "%d\n", scroll); 380 fclose(f); 381 } 382 delwin(dialog); 383 fprintf(stderr, "%s\n", items[(scroll + choice) * 2]); 384 switch (key) { 385 case 's': 386 return 3; 387 case 'y': 388 return 3; 389 case 'n': 390 return 4; 391 case 'm': 392 return 5; 393 case ' ': 394 return 6; 395 case '/': 396 return 7; 397 } 398 return 0; 399 case 'h': 400 case '?': 401 button = 2; 402 case '\n': 403 delwin(dialog); 404 if (button == 2) 405 fprintf(stderr, "%s \"%s\"\n", 406 items[(scroll + choice) * 2], 407 items[(scroll + choice) * 2 + 1] + 408 first_alpha(items [(scroll + choice) * 2 + 1], "")); 409 else 410 fprintf(stderr, "%s\n", 411 items[(scroll + choice) * 2]); 412 413 remove("lxdialog.scrltmp"); 414 return button; 415 case 'e': 416 case 'x': 417 key = ESC; 418 case ESC: 419 break; 420 } 421 } 422 423 delwin(dialog); 424 remove("lxdialog.scrltmp"); 425 return -1; /* ESC pressed */ 426} 427