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 #define ITEM_IDENT 1 /* Indent of menu entries. Fixed for all menus */ 62 static int menu_width; 63 64 /* 65 * Print menu item 66 */ 67 static void do_print_item(WINDOW * win, const char *item, int choice, 68 int selected, int hotkey) 69 { 70 int j; 71 char *menu_item = malloc(menu_width + 1); 72 73 strncpy(menu_item, item, menu_width - ITEM_IDENT); 74 menu_item[menu_width] = 0; 75 j = first_alpha(menu_item, "YyNnMmHh"); 76 77 /* Clear 'residue' of last item */ 78 wattrset(win, menubox_attr); 79 wmove(win, choice, 0); 80 #if OLD_NCURSES 81 { 82 int i; 83 for (i = 0; i < menu_width; i++) 84 waddch(win, ' '); 85 } 86 #else 87 wclrtoeol(win); 88 #endif 89 wattrset(win, selected ? item_selected_attr : item_attr); 90 mvwaddstr(win, choice, ITEM_IDENT, menu_item); 91 if (hotkey) { 92 wattrset(win, selected ? tag_key_selected_attr : tag_key_attr); 93 mvwaddch(win, choice, ITEM_IDENT + j, menu_item[j]); 94 } 95 if (selected) { 96 wmove(win, choice, ITEM_IDENT + 1); 97 } 98 free(menu_item); 99 wrefresh(win); 100 } 101 102 #define print_item(index, choice, selected) \ 103 do {\ 104 int hotkey = (items[(index) * 2][0] != ':'); \ 105 do_print_item(menu, items[(index) * 2 + 1], choice, selected, hotkey); \ 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, uarrow_attr); 122 waddch(win, ACS_UARROW); 123 waddstr(win, "(-)"); 124 } else { 125 wattrset(win, menubox_attr); 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, darrow_attr); 138 waddch(win, ACS_DARROW); 139 waddstr(win, "(+)"); 140 } else { 141 wattrset(win, menubox_border_attr); 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, "Select", y, x, selected == 0); 161 print_button(win, " Exit ", y, x + 12, selected == 1); 162 print_button(win, " 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, int height, int width, 183 int menu_height, const char *current, int item_no, 184 const char *const *items) 185 { 186 int i, j, x, y, box_x, box_y; 187 int key = 0, button = 0, scroll = 0, choice = 0; 188 int first_item = 0, max_choice; 189 WINDOW *dialog, *menu; 190 FILE *f; 191 192 max_choice = MIN(menu_height, item_no); 193 194 /* center dialog box on screen */ 195 x = (COLS - width) / 2; 196 y = (LINES - height) / 2; 197 198 draw_shadow(stdscr, y, x, height, width); 199 200 dialog = newwin(height, width, y, x); 201 keypad(dialog, TRUE); 202 203 draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); 204 wattrset(dialog, border_attr); 205 mvwaddch(dialog, height - 3, 0, ACS_LTEE); 206 for (i = 0; i < width - 2; i++) 207 waddch(dialog, ACS_HLINE); 208 wattrset(dialog, dialog_attr); 209 wbkgdset(dialog, dialog_attr & A_COLOR); 210 waddch(dialog, ACS_RTEE); 211 212 print_title(dialog, title, width); 213 214 wattrset(dialog, dialog_attr); 215 print_autowrap(dialog, prompt, width - 2, 1, 3); 216 217 menu_width = width - 6; 218 box_y = height - menu_height - 5; 219 box_x = (width - menu_width) / 2 - 1; 220 221 /* create new window for the menu */ 222 menu = subwin(dialog, menu_height, menu_width, 223 y + box_y + 1, x + box_x + 1); 224 keypad(menu, TRUE); 225 226 /* draw a box around the menu items */ 227 draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2, 228 menubox_border_attr, menubox_attr); 229 230 /* Set choice to default item */ 231 for (i = 0; i < item_no; i++) 232 if (strcmp(current, items[i * 2]) == 0) 233 choice = i; 234 235 /* get the scroll info from the temp file */ 236 if ((f = fopen("lxdialog.scrltmp", "r")) != NULL) { 237 if ((fscanf(f, "%d\n", &scroll) == 1) && (scroll <= choice) && 238 (scroll + max_choice > choice) && (scroll >= 0) && 239 (scroll + max_choice <= item_no)) { 240 first_item = scroll; 241 choice = choice - scroll; 242 fclose(f); 243 } else { 244 scroll = 0; 245 remove("lxdialog.scrltmp"); 246 fclose(f); 247 f = NULL; 248 } 249 } 250 if ((choice >= max_choice) || (f == NULL && choice >= max_choice / 2)) { 251 if (choice >= item_no - max_choice / 2) 252 scroll = first_item = item_no - max_choice; 253 else 254 scroll = first_item = choice - max_choice / 2; 255 choice = choice - scroll; 256 } 257 258 /* Print the menu */ 259 for (i = 0; i < max_choice; i++) { 260 print_item(first_item + i, i, i == choice); 261 } 262 263 wnoutrefresh(menu); 264 265 print_arrows(dialog, item_no, scroll, 266 box_y, box_x + ITEM_IDENT + 1, menu_height); 267 268 print_buttons(dialog, height, width, 0); 269 wmove(menu, choice, ITEM_IDENT + 1); 270 wrefresh(menu); 271 272 while (key != ESC) { 273 key = wgetch(menu); 274 275 if (key < 256 && isalpha(key)) 276 key = tolower(key); 277 278 if (strchr("ynmh", key)) 279 i = max_choice; 280 else { 281 for (i = choice + 1; i < max_choice; i++) { 282 j = first_alpha(items[(scroll + i) * 2 + 1], "YyNnMmHh"); 283 if (key == tolower(items[(scroll + i) * 2 + 1][j])) 284 break; 285 } 286 if (i == max_choice) 287 for (i = 0; i < max_choice; i++) { 288 j = first_alpha(items [(scroll + i) * 2 + 1], "YyNnMmHh"); 289 if (key == tolower(items[(scroll + i) * 2 + 1][j])) 290 break; 291 } 292 } 293 294 if (i < max_choice || 295 key == KEY_UP || key == KEY_DOWN || 296 key == '-' || key == '+' || 297 key == KEY_PPAGE || key == KEY_NPAGE) { 298 /* Remove highligt of current item */ 299 print_item(scroll + choice, choice, FALSE); 300 301 if (key == KEY_UP || key == '-') { 302 if (choice < 2 && scroll) { 303 /* Scroll menu down */ 304 do_scroll(menu, &scroll, -1); 305 306 print_item(scroll, 0, FALSE); 307 } else 308 choice = MAX(choice - 1, 0); 309 310 } else if (key == KEY_DOWN || key == '+') { 311 print_item(scroll+choice, choice, FALSE); 312 313 if ((choice > max_choice - 3) && 314 (scroll + max_choice < item_no)) { 315 /* Scroll menu up */ 316 do_scroll(menu, &scroll, 1); 317 318 print_item(scroll+max_choice - 1, 319 max_choice - 1, FALSE); 320 } else 321 choice = MIN(choice + 1, max_choice - 1); 322 323 } else if (key == KEY_PPAGE) { 324 scrollok(menu, TRUE); 325 for (i = 0; (i < max_choice); i++) { 326 if (scroll > 0) { 327 do_scroll(menu, &scroll, -1); 328 print_item(scroll, 0, FALSE); 329 } else { 330 if (choice > 0) 331 choice--; 332 } 333 } 334 335 } else if (key == KEY_NPAGE) { 336 for (i = 0; (i < max_choice); i++) { 337 if (scroll + max_choice < item_no) { 338 do_scroll(menu, &scroll, 1); 339 print_item(scroll+max_choice-1, 340 max_choice - 1, FALSE); 341 } else { 342 if (choice + 1 < max_choice) 343 choice++; 344 } 345 } 346 } else 347 choice = i; 348 349 print_item(scroll + choice, choice, TRUE); 350 351 print_arrows(dialog, item_no, scroll, 352 box_y, box_x + ITEM_IDENT + 1, menu_height); 353 354 wnoutrefresh(dialog); 355 wrefresh(menu); 356 357 continue; /* wait for another key press */ 358 } 359 360 switch (key) { 361 case KEY_LEFT: 362 case TAB: 363 case KEY_RIGHT: 364 button = ((key == KEY_LEFT ? --button : ++button) < 0) 365 ? 2 : (button > 2 ? 0 : button); 366 367 print_buttons(dialog, height, width, button); 368 wrefresh(menu); 369 break; 370 case ' ': 371 case 's': 372 case 'y': 373 case 'n': 374 case 'm': 375 case '/': 376 /* save scroll info */ 377 if ((f = fopen("lxdialog.scrltmp", "w")) != NULL) { 378 fprintf(f, "%d\n", scroll); 379 fclose(f); 380 } 381 delwin(dialog); 382 fprintf(stderr, "%s\n", items[(scroll + choice) * 2]); 383 switch (key) { 384 case 's': 385 return 3; 386 case 'y': 387 return 3; 388 case 'n': 389 return 4; 390 case 'm': 391 return 5; 392 case ' ': 393 return 6; 394 case '/': 395 return 7; 396 } 397 return 0; 398 case 'h': 399 case '?': 400 button = 2; 401 case '\n': 402 delwin(dialog); 403 if (button == 2) 404 fprintf(stderr, "%s \"%s\"\n", 405 items[(scroll + choice) * 2], 406 items[(scroll + choice) * 2 + 1] + 407 first_alpha(items [(scroll + choice) * 2 + 1], "")); 408 else 409 fprintf(stderr, "%s\n", 410 items[(scroll + choice) * 2]); 411 412 remove("lxdialog.scrltmp"); 413 return button; 414 case 'e': 415 case 'x': 416 key = ESC; 417 case ESC: 418 break; 419 } 420 } 421 422 delwin(dialog); 423 remove("lxdialog.scrltmp"); 424 return -1; /* ESC pressed */ 425 } 426