1 /* 2 * $Id: menubox.c,v 1.122 2011/06/29 09:48:46 tom Exp $ 3 * 4 * menubox.c -- implements the menu box 5 * 6 * Copyright 2000-2010,2011 Thomas E. Dickey 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public Licens, version 2.1e 10 * as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program; if not, write to 19 * Free Software Foundation, Inc. 20 * 51 Franklin St., Fifth Floor 21 * Boston, MA 02110, USA. 22 * 23 * An earlier version of this program lists as authors 24 * Savio Lam (lam836@cs.cuhk.hk) 25 */ 26 27 #include <dialog.h> 28 #include <dlg_keys.h> 29 30 static int menu_width, tag_x, item_x; 31 32 typedef enum { 33 Unselected = 0, 34 Selected, 35 Editing 36 } Mode; 37 38 #define MIN_HIGH (1 + (5 * MARGIN)) 39 40 #define INPUT_ROWS 3 /* rows per inputmenu entry */ 41 42 #define LLEN(n) ((n) * MENUBOX_TAGS) 43 #define ItemName(i) items[LLEN(i)] 44 #define ItemText(i) items[LLEN(i) + 1] 45 #define ItemHelp(i) items[LLEN(i) + 2] 46 47 #define RowHeight(i) (is_inputmenu ? ((i) * INPUT_ROWS) : ((i) * 1)) 48 #define ItemToRow(i) (is_inputmenu ? ((i) * INPUT_ROWS + 1) : (i)) 49 #define RowToItem(i) (is_inputmenu ? ((i) / INPUT_ROWS + 0) : (i)) 50 51 static void 52 print_arrows(WINDOW *win, 53 int box_x, 54 int box_y, 55 int scrollamt, 56 int max_choice, 57 int item_no, 58 int menu_height) 59 { 60 dlg_draw_scrollbar(win, 61 scrollamt, 62 scrollamt, 63 scrollamt + max_choice, 64 item_no, 65 box_x, 66 box_x + menu_width, 67 box_y, 68 box_y + menu_height + 1, 69 menubox_attr, 70 menubox_border_attr); 71 } 72 73 /* 74 * Print the tag of a menu-item 75 */ 76 static void 77 print_tag(WINDOW *win, 78 DIALOG_LISTITEM * item, 79 int choice, 80 Mode selected, 81 bool is_inputmenu) 82 { 83 int my_x = item_x; 84 int my_y = ItemToRow(choice); 85 int tag_width = (my_x - tag_x - GUTTER); 86 const int *cols; 87 const int *indx; 88 int limit; 89 int prefix; 90 91 cols = dlg_index_columns(item->name); 92 indx = dlg_index_wchars(item->name); 93 limit = dlg_count_wchars(item->name); 94 prefix = (indx[1] - indx[0]); 95 96 /* highlight first char of the tag to be special */ 97 (void) wmove(win, my_y, tag_x); 98 wattrset(win, selected ? tag_key_selected_attr : tag_key_attr); 99 if (strlen(item->name) != 0) 100 (void) waddnstr(win, item->name, prefix); 101 /* print rest of the string */ 102 wattrset(win, selected ? tag_selected_attr : tag_attr); 103 if ((int) strlen(item->name) > prefix) { 104 limit = dlg_limit_columns(item->name, tag_width, 1); 105 if (limit > 0) 106 (void) waddnstr(win, item->name + indx[1], indx[limit] - indx[1]); 107 } 108 } 109 110 /* 111 * Print menu item 112 */ 113 static void 114 print_item(WINDOW *win, 115 DIALOG_LISTITEM * items, 116 int choice, 117 Mode selected, 118 bool is_inputmenu) 119 { 120 chtype save = dlg_get_attrs(win); 121 int n; 122 int my_width = menu_width; 123 int my_x = item_x; 124 int my_y = ItemToRow(choice); 125 chtype attr = A_NORMAL; 126 chtype textchar; 127 chtype bordchar; 128 129 switch (selected) { 130 default: 131 case Unselected: 132 textchar = item_attr; 133 bordchar = item_attr; 134 break; 135 case Selected: 136 textchar = item_selected_attr; 137 bordchar = item_selected_attr; 138 break; 139 case Editing: 140 textchar = inputbox_attr; 141 bordchar = dialog_attr; 142 break; 143 } 144 145 /* Clear 'residue' of last item and mark current current item */ 146 if (is_inputmenu) { 147 wattrset(win, (selected != Unselected) ? item_selected_attr : item_attr); 148 for (n = my_y - 1; n < my_y + INPUT_ROWS - 1; n++) { 149 wmove(win, n, 0); 150 wprintw(win, "%*s", my_width, " "); 151 } 152 } else { 153 wattrset(win, menubox_attr); 154 wmove(win, my_y, 0); 155 wprintw(win, "%*s", my_width, " "); 156 } 157 158 print_tag(win, items, choice, selected, is_inputmenu); 159 160 /* Draw the input field box (only for inputmenu) */ 161 (void) wmove(win, my_y, my_x); 162 if (is_inputmenu) { 163 my_width -= 1; 164 dlg_draw_box(win, my_y - 1, my_x, INPUT_ROWS, my_width - my_x - tag_x, 165 bordchar, 166 bordchar); 167 my_width -= 1; 168 ++my_x; 169 } 170 171 /* print actual item */ 172 wmove(win, my_y, my_x); 173 wattrset(win, textchar); 174 dlg_print_text(win, items->text, my_width - my_x, &attr); 175 176 if (selected) { 177 dlg_item_help(items->help); 178 } 179 wattrset(win, save); 180 } 181 182 /* 183 * Allow the user to edit the text of a menu entry. 184 */ 185 static int 186 input_menu_edit(WINDOW *win, 187 DIALOG_LISTITEM * items, 188 int choice, 189 char **resultp) 190 { 191 chtype save = dlg_get_attrs(win); 192 char *result; 193 int offset = 0; 194 int key = 0, fkey = 0; 195 int first = TRUE; 196 /* see above */ 197 bool is_inputmenu = TRUE; 198 int y = ItemToRow(choice); 199 int code = TRUE; 200 int max_len = dlg_max_input(MAX((int) strlen(items->text) + 1, MAX_LEN)); 201 202 result = dlg_malloc(char, (size_t) max_len); 203 assert_ptr(result, "input_menu_edit"); 204 205 /* original item is used to initialize the input string. */ 206 result[0] = '\0'; 207 strcpy(result, items->text); 208 209 print_item(win, items, choice, Editing, TRUE); 210 211 /* taken out of inputbox.c - but somewhat modified */ 212 for (;;) { 213 if (!first) 214 key = dlg_mouse_wgetch(win, &fkey); 215 if (dlg_edit_string(result, &offset, key, fkey, first)) { 216 dlg_show_string(win, result, offset, inputbox_attr, 217 y, item_x + 1, menu_width - item_x - 3, 218 FALSE, first); 219 first = FALSE; 220 } else if (key == ESC || key == TAB) { 221 code = FALSE; 222 break; 223 } else { 224 break; 225 } 226 } 227 print_item(win, items, choice, Selected, TRUE); 228 wattrset(win, save); 229 230 *resultp = result; 231 return code; 232 } 233 234 static int 235 handle_button(int code, DIALOG_LISTITEM * items, int choice) 236 { 237 switch (code) { 238 case DLG_EXIT_OK: /* FALLTHRU */ 239 case DLG_EXIT_EXTRA: 240 dlg_add_string(items[choice].name); 241 break; 242 case DLG_EXIT_HELP: 243 dlg_add_result("HELP "); 244 if (USE_ITEM_HELP(items[choice].help)) { 245 dlg_add_string(items[choice].help); 246 code = DLG_EXIT_ITEM_HELP; 247 } else { 248 dlg_add_string(items[choice].name); 249 } 250 break; 251 } 252 return code; 253 } 254 255 static int 256 dlg_renamed_menutext(DIALOG_LISTITEM * items, int current, char *newtext) 257 { 258 if (dialog_vars.input_result) 259 dialog_vars.input_result[0] = '\0'; 260 dlg_add_result("RENAMED "); 261 dlg_add_string(items[current].name); 262 dlg_add_result(" "); 263 dlg_add_string(newtext); 264 return DLG_EXIT_EXTRA; 265 } 266 267 static int 268 dlg_dummy_menutext(DIALOG_LISTITEM * items, int current, char *newtext) 269 { 270 (void) items; 271 (void) current; 272 (void) newtext; 273 return DLG_EXIT_ERROR; 274 } 275 276 /* 277 * This is an alternate interface to 'menu' which allows the application 278 * to read the list item states back directly without putting them in the 279 * output buffer. 280 */ 281 int 282 dlg_menu(const char *title, 283 const char *cprompt, 284 int height, 285 int width, 286 int menu_height, 287 int item_no, 288 DIALOG_LISTITEM * items, 289 int *current_item, 290 DIALOG_INPUTMENU rename_menutext) 291 { 292 /* *INDENT-OFF* */ 293 static DLG_KEYS_BINDING binding[] = { 294 HELPKEY_BINDINGS, 295 ENTERKEY_BINDINGS, 296 DLG_KEYS_DATA( DLGK_FIELD_NEXT, ' ' ), 297 DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ), 298 DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), 299 DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), 300 DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ), 301 DLG_KEYS_DATA( DLGK_ITEM_NEXT, '+' ), 302 DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN ), 303 DLG_KEYS_DATA( DLGK_ITEM_NEXT, CHR_NEXT ), 304 DLG_KEYS_DATA( DLGK_ITEM_PREV, '-' ), 305 DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ), 306 DLG_KEYS_DATA( DLGK_ITEM_PREV, CHR_PREVIOUS ), 307 DLG_KEYS_DATA( DLGK_PAGE_FIRST, KEY_HOME ), 308 DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_END ), 309 DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_LL ), 310 DLG_KEYS_DATA( DLGK_PAGE_NEXT, KEY_NPAGE ), 311 DLG_KEYS_DATA( DLGK_PAGE_PREV, KEY_PPAGE ), 312 END_KEYS_BINDING 313 }; 314 static DLG_KEYS_BINDING binding2[] = { 315 INPUTSTR_BINDINGS, 316 HELPKEY_BINDINGS, 317 ENTERKEY_BINDINGS, 318 END_KEYS_BINDING 319 }; 320 /* *INDENT-ON* */ 321 322 #ifdef KEY_RESIZE 323 int old_height = height; 324 int old_width = width; 325 #endif 326 int i, j, x, y, cur_x, cur_y, box_x, box_y; 327 int key = 0, fkey; 328 int button = dialog_state.visit_items ? -1 : dlg_defaultno_button(); 329 int choice = dlg_default_listitem(items); 330 int result = DLG_EXIT_UNKNOWN; 331 int scrollamt = 0; 332 int max_choice, min_width; 333 int found; 334 int use_height, use_width, name_width, text_width; 335 WINDOW *dialog, *menu; 336 char *prompt = dlg_strclone(cprompt); 337 const char **buttons = dlg_ok_labels(); 338 bool is_inputmenu = (rename_menutext == dlg_renamed_menutext); 339 340 dlg_does_output(); 341 dlg_tab_correct_str(prompt); 342 343 #ifdef KEY_RESIZE 344 retry: 345 #endif 346 347 use_height = menu_height; 348 if (use_height == 0) { 349 min_width = dlg_calc_list_width(item_no, items) + 10; 350 /* calculate height without items (4) */ 351 dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, MAX(26, min_width)); 352 dlg_calc_listh(&height, &use_height, item_no); 353 } else { 354 dlg_auto_size(title, prompt, &height, &width, MIN_HIGH + use_height, 26); 355 } 356 dlg_button_layout(buttons, &width); 357 dlg_print_size(height, width); 358 dlg_ctl_size(height, width); 359 360 x = dlg_box_x_ordinate(width); 361 y = dlg_box_y_ordinate(height); 362 363 dialog = dlg_new_window(height, width, y, x); 364 dlg_register_window(dialog, "menubox", binding); 365 dlg_register_buttons(dialog, "menubox", buttons); 366 367 dlg_mouse_setbase(x, y); 368 369 dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); 370 dlg_draw_bottom_box(dialog); 371 dlg_draw_title(dialog, title); 372 373 wattrset(dialog, dialog_attr); 374 dlg_print_autowrap(dialog, prompt, height, width); 375 376 menu_width = width - 6; 377 getyx(dialog, cur_y, cur_x); 378 box_y = cur_y + 1; 379 box_x = (width - menu_width) / 2 - 1; 380 381 /* 382 * After displaying the prompt, we know how much space we really have. 383 * Limit the list to avoid overwriting the ok-button. 384 */ 385 if (use_height + MIN_HIGH > height - cur_y) 386 use_height = height - MIN_HIGH - cur_y; 387 if (use_height <= 0) 388 use_height = 1; 389 390 /* Find out maximal number of displayable items at once. */ 391 max_choice = MIN(use_height, 392 RowHeight(item_no)); 393 if (is_inputmenu) 394 max_choice /= INPUT_ROWS; 395 396 /* create new window for the menu */ 397 menu = dlg_sub_window(dialog, use_height, menu_width, 398 y + box_y + 1, 399 x + box_x + 1); 400 dlg_register_window(menu, "menu", binding2); 401 dlg_register_buttons(menu, "menu", buttons); 402 403 /* draw a box around the menu items */ 404 dlg_draw_box(dialog, box_y, box_x, use_height + 2, menu_width + 2, 405 menubox_border_attr, menubox_attr); 406 407 name_width = 0; 408 text_width = 0; 409 410 /* Find length of longest item to center menu * 411 * only if --menu was given, using --inputmenu * 412 * won't be centered. */ 413 for (i = 0; i < item_no; i++) { 414 name_width = MAX(name_width, dlg_count_columns(items[i].name)); 415 text_width = MAX(text_width, dlg_count_columns(items[i].text)); 416 } 417 418 /* If the name+text is wider than the list is allowed, then truncate 419 * one or both of them. If the name is no wider than 30% of the list, 420 * leave it intact. 421 * 422 * FIXME: the gutter width and name/list ratio should be configurable. 423 */ 424 use_width = (menu_width - GUTTER); 425 if (text_width + name_width > use_width) { 426 int need = (int) (0.30 * use_width); 427 if (name_width > need) { 428 int want = (int) (use_width 429 * ((double) name_width) 430 / (text_width + name_width)); 431 name_width = (want > need) ? want : need; 432 } 433 text_width = use_width - name_width; 434 } 435 436 tag_x = (is_inputmenu 437 ? 0 438 : (use_width - text_width - name_width) / 2); 439 item_x = name_width + tag_x + GUTTER; 440 441 if (choice - scrollamt >= max_choice) { 442 scrollamt = choice - (max_choice - 1); 443 choice = max_choice - 1; 444 } 445 446 /* Print the menu */ 447 for (i = 0; i < max_choice; i++) { 448 print_item(menu, 449 &items[i + scrollamt], 450 i, 451 (i == choice) ? Selected : Unselected, 452 is_inputmenu); 453 } 454 (void) wnoutrefresh(menu); 455 456 /* register the new window, along with its borders */ 457 dlg_mouse_mkbigregion(box_y + 1, box_x, use_height + 2, menu_width + 2, 458 KEY_MAX, 1, 1, 1 /* by lines */ ); 459 460 print_arrows(dialog, box_x, box_y, scrollamt, max_choice, item_no, use_height); 461 462 dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); 463 464 while (result == DLG_EXIT_UNKNOWN) { 465 if (button < 0) /* --visit-items */ 466 wmove(dialog, box_y + ItemToRow(choice) + 1, box_x + tag_x + 1); 467 468 key = dlg_mouse_wgetch(dialog, &fkey); 469 if (dlg_result_key(key, fkey, &result)) 470 break; 471 472 found = FALSE; 473 if (fkey) { 474 /* 475 * Allow a mouse-click on a box to switch selection to that box. 476 * Handling a button click is a little more complicated, since we 477 * push a KEY_ENTER back onto the input stream so we'll put the 478 * cursor at the right place before handling the "keypress". 479 */ 480 if (key >= DLGK_MOUSE(KEY_MAX)) { 481 key -= DLGK_MOUSE(KEY_MAX); 482 i = RowToItem(key); 483 if (i < max_choice) { 484 found = TRUE; 485 } else { 486 beep(); 487 continue; 488 } 489 } else if (is_DLGK_MOUSE(key) 490 && dlg_ok_buttoncode(key - M_EVENT) >= 0) { 491 button = (key - M_EVENT); 492 ungetch('\n'); 493 continue; 494 } 495 } else { 496 /* 497 * Check if key pressed matches first character of any item tag in 498 * list. If there is more than one match, we will cycle through 499 * each one as the same key is pressed repeatedly. 500 */ 501 if (button < 0 || !dialog_state.visit_items) { 502 for (j = scrollamt + choice + 1; j < item_no; j++) { 503 if (dlg_match_char(dlg_last_getc(), items[j].name)) { 504 found = TRUE; 505 i = j - scrollamt; 506 break; 507 } 508 } 509 if (!found) { 510 for (j = 0; j <= scrollamt + choice; j++) { 511 if (dlg_match_char(dlg_last_getc(), items[j].name)) { 512 found = TRUE; 513 i = j - scrollamt; 514 break; 515 } 516 } 517 } 518 if (found) 519 dlg_flush_getc(); 520 } else if ((j = dlg_char_to_button(key, buttons)) >= 0) { 521 button = j; 522 ungetch('\n'); 523 continue; 524 } 525 526 /* 527 * A single digit (1-9) positions the selection to that line in the 528 * current screen. 529 */ 530 if (!found 531 && (key <= '9') 532 && (key > '0') 533 && (key - '1' < max_choice)) { 534 found = TRUE; 535 i = key - '1'; 536 } 537 } 538 539 if (!found && fkey) { 540 found = TRUE; 541 switch (key) { 542 case DLGK_PAGE_FIRST: 543 i = -scrollamt; 544 break; 545 case DLGK_PAGE_LAST: 546 i = item_no - 1 - scrollamt; 547 break; 548 case DLGK_MOUSE(KEY_PPAGE): 549 case DLGK_PAGE_PREV: 550 if (choice) 551 i = 0; 552 else if (scrollamt != 0) 553 i = -MIN(scrollamt, max_choice); 554 else 555 continue; 556 break; 557 case DLGK_MOUSE(KEY_NPAGE): 558 case DLGK_PAGE_NEXT: 559 i = MIN(choice + max_choice, item_no - scrollamt - 1); 560 break; 561 case DLGK_ITEM_PREV: 562 i = choice - 1; 563 if (choice == 0 && scrollamt == 0) 564 continue; 565 break; 566 case DLGK_ITEM_NEXT: 567 i = choice + 1; 568 if (scrollamt + choice >= item_no - 1) 569 continue; 570 break; 571 default: 572 found = FALSE; 573 break; 574 } 575 } 576 577 if (found) { 578 if (i != choice) { 579 getyx(dialog, cur_y, cur_x); 580 if (i < 0 || i >= max_choice) { 581 #if defined(NCURSES_VERSION_MAJOR) && NCURSES_VERSION_MAJOR < 5 582 /* 583 * Using wscrl to assist ncurses scrolling is not needed 584 * in version 5.x 585 */ 586 if (i == -1) { 587 if (use_height > 1) { 588 /* De-highlight current first item */ 589 print_item(menu, 590 &items[scrollamt], 591 0, Unselected, is_inputmenu); 592 scrollok(menu, TRUE); 593 wscrl(menu, -RowHeight(1)); 594 scrollok(menu, FALSE); 595 } 596 scrollamt--; 597 print_item(menu, 598 &items[scrollamt], 599 0, Selected, is_inputmenu); 600 } else if (i == max_choice) { 601 if (use_height > 1) { 602 /* De-highlight current last item before scrolling up */ 603 print_item(menu, 604 &items[scrollamt + max_choice - 1], 605 max_choice - 1, 606 Unselected, 607 is_inputmenu); 608 scrollok(menu, TRUE); 609 wscrl(menu, RowHeight(1)); 610 scrollok(menu, FALSE); 611 } 612 scrollamt++; 613 print_item(menu, 614 &items[scrollamt + max_choice - 1], 615 max_choice - 1, TRUE, 616 is_inputmenu); 617 } else 618 #endif 619 { 620 if (i < 0) { 621 scrollamt += i; 622 choice = 0; 623 } else { 624 choice = max_choice - 1; 625 scrollamt += (i - max_choice + 1); 626 } 627 for (i = 0; i < max_choice; i++) { 628 print_item(menu, 629 &items[scrollamt + i], 630 i, 631 (i == choice) ? Selected : Unselected, 632 is_inputmenu); 633 } 634 } 635 /* Clean bottom lines */ 636 if (is_inputmenu) { 637 int spare_lines, x_count; 638 spare_lines = use_height % INPUT_ROWS; 639 wattrset(menu, menubox_attr); 640 for (; spare_lines; spare_lines--) { 641 wmove(menu, use_height - spare_lines, 0); 642 for (x_count = 0; x_count < menu_width; 643 x_count++) { 644 waddch(menu, ' '); 645 } 646 } 647 } 648 (void) wnoutrefresh(menu); 649 print_arrows(dialog, 650 box_x, box_y, 651 scrollamt, max_choice, item_no, use_height); 652 } else { 653 /* De-highlight current item */ 654 print_item(menu, 655 &items[scrollamt + choice], 656 choice, 657 Unselected, 658 is_inputmenu); 659 /* Highlight new item */ 660 choice = i; 661 print_item(menu, 662 &items[scrollamt + choice], 663 choice, 664 Selected, 665 is_inputmenu); 666 (void) wnoutrefresh(menu); 667 print_arrows(dialog, 668 box_x, box_y, 669 scrollamt, max_choice, item_no, use_height); 670 (void) wmove(dialog, cur_y, cur_x); 671 wrefresh(dialog); 672 } 673 } 674 continue; /* wait for another key press */ 675 } 676 677 if (fkey) { 678 switch (key) { 679 case DLGK_FIELD_PREV: 680 button = dlg_prev_button(buttons, button); 681 dlg_draw_buttons(dialog, height - 2, 0, buttons, button, 682 FALSE, width); 683 break; 684 case DLGK_FIELD_NEXT: 685 button = dlg_next_button(buttons, button); 686 dlg_draw_buttons(dialog, height - 2, 0, buttons, button, 687 FALSE, width); 688 break; 689 case DLGK_ENTER: 690 result = dlg_enter_buttoncode(button); 691 692 /* 693 * If dlg_menu() is called from dialog_menu(), we want to 694 * capture the results into dialog_vars.input_result, but not 695 * if dlg_menu() is called directly from an application. We 696 * can check this by testing if rename_menutext is the function 697 * pointer owned by dialog_menu(). It would be nicer to have 698 * this logic inside dialog_menu(), but that cannot be done 699 * since we would lose compatibility for the results reported 700 * after input_menu_edit(). 701 */ 702 if (result == DLG_EXIT_ERROR) { 703 result = DLG_EXIT_UNKNOWN; 704 } else if (is_inputmenu 705 || rename_menutext == dlg_dummy_menutext) { 706 result = handle_button(result, 707 items, 708 scrollamt + choice); 709 } 710 711 /* 712 * If we have a rename_menutext function, interpret the Extra 713 * button as a request to rename the menu's text. If that 714 * function doesn't return "Unknown", we will exit from this 715 * function. Usually that is done for dialog_menu(), so the 716 * shell script can use the updated value. If it does return 717 * "Unknown", update the list item only. A direct caller of 718 * dlg_menu() can free the renamed value - we cannot. 719 */ 720 if (is_inputmenu && result == DLG_EXIT_EXTRA) { 721 char *tmp; 722 723 if (input_menu_edit(menu, 724 &items[scrollamt + choice], 725 choice, 726 &tmp)) { 727 result = rename_menutext(items, scrollamt + choice, tmp); 728 if (result == DLG_EXIT_UNKNOWN) { 729 items[scrollamt + choice].text = tmp; 730 } else { 731 free(tmp); 732 } 733 } else { 734 result = DLG_EXIT_UNKNOWN; 735 print_item(menu, 736 &items[scrollamt + choice], 737 choice, 738 Selected, 739 is_inputmenu); 740 (void) wnoutrefresh(menu); 741 free(tmp); 742 } 743 744 if (result == DLG_EXIT_UNKNOWN) { 745 dlg_draw_buttons(dialog, height - 2, 0, 746 buttons, button, FALSE, width); 747 } 748 } 749 break; 750 #ifdef KEY_RESIZE 751 case KEY_RESIZE: 752 /* reset data */ 753 height = old_height; 754 width = old_width; 755 /* repaint */ 756 dlg_clear(); 757 dlg_del_window(dialog); 758 refresh(); 759 dlg_mouse_free_regions(); 760 goto retry; 761 #endif 762 default: 763 flash(); 764 break; 765 } 766 } 767 } 768 769 dlg_mouse_free_regions(); 770 dlg_unregister_window(menu); 771 dlg_del_window(dialog); 772 free(prompt); 773 774 *current_item = scrollamt + choice; 775 return result; 776 } 777 778 /* 779 * Display a menu for choosing among a number of options 780 */ 781 int 782 dialog_menu(const char *title, 783 const char *cprompt, 784 int height, 785 int width, 786 int menu_height, 787 int item_no, 788 char **items) 789 { 790 int result; 791 int choice; 792 int i; 793 DIALOG_LISTITEM *listitems; 794 795 listitems = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1); 796 assert_ptr(listitems, "dialog_menu"); 797 798 for (i = 0; i < item_no; ++i) { 799 listitems[i].name = ItemName(i); 800 listitems[i].text = ItemText(i); 801 listitems[i].help = ((dialog_vars.item_help) 802 ? ItemHelp(i) 803 : dlg_strempty()); 804 } 805 dlg_align_columns(&listitems[0].text, sizeof(DIALOG_LISTITEM), item_no); 806 807 result = dlg_menu(title, 808 cprompt, 809 height, 810 width, 811 menu_height, 812 item_no, 813 listitems, 814 &choice, 815 dialog_vars.input_menu ? dlg_renamed_menutext : dlg_dummy_menutext); 816 817 dlg_free_columns(&listitems[0].text, sizeof(DIALOG_LISTITEM), item_no); 818 free(listitems); 819 return result; 820 } 821