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