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