1 /* 2 * $Id: menubox.c,v 1.118 2010/01/17 22:24:11 tom Exp $ 3 * 4 * menubox.c -- implements the menu box 5 * 6 * Copyright 2000-2009,2010 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 = getattrs(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 = getattrs(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 ENTERKEY_BINDINGS, 295 DLG_KEYS_DATA( DLGK_FIELD_NEXT, ' ' ), 296 DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ), 297 DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), 298 DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), 299 DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ), 300 DLG_KEYS_DATA( DLGK_ITEM_NEXT, '+' ), 301 DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN ), 302 DLG_KEYS_DATA( DLGK_ITEM_NEXT, CHR_NEXT ), 303 DLG_KEYS_DATA( DLGK_ITEM_PREV, '-' ), 304 DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ), 305 DLG_KEYS_DATA( DLGK_ITEM_PREV, CHR_PREVIOUS ), 306 DLG_KEYS_DATA( DLGK_PAGE_FIRST, KEY_HOME ), 307 DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_END ), 308 DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_LL ), 309 DLG_KEYS_DATA( DLGK_PAGE_NEXT, KEY_NPAGE ), 310 DLG_KEYS_DATA( DLGK_PAGE_PREV, KEY_PPAGE ), 311 END_KEYS_BINDING 312 }; 313 static DLG_KEYS_BINDING binding2[] = { 314 INPUTSTR_BINDINGS, 315 ENTERKEY_BINDINGS, 316 END_KEYS_BINDING 317 }; 318 /* *INDENT-ON* */ 319 320 #ifdef KEY_RESIZE 321 int old_height = height; 322 int old_width = width; 323 #endif 324 int i, j, x, y, cur_x, cur_y, box_x, box_y; 325 int key = 0, fkey; 326 int button = dialog_state.visit_items ? -1 : dlg_defaultno_button(); 327 int choice = dlg_default_listitem(items); 328 int result = DLG_EXIT_UNKNOWN; 329 int scrollamt = 0; 330 int max_choice, min_width; 331 int found; 332 int use_height, use_width, name_width, text_width; 333 WINDOW *dialog, *menu; 334 char *prompt = dlg_strclone(cprompt); 335 const char **buttons = dlg_ok_labels(); 336 bool is_inputmenu = (rename_menutext == dlg_renamed_menutext); 337 338 dlg_does_output(); 339 dlg_tab_correct_str(prompt); 340 341 #ifdef KEY_RESIZE 342 retry: 343 #endif 344 345 use_height = menu_height; 346 if (use_height == 0) { 347 min_width = dlg_calc_list_width(item_no, items) + 10; 348 /* calculate height without items (4) */ 349 dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, MAX(26, min_width)); 350 dlg_calc_listh(&height, &use_height, item_no); 351 } else { 352 dlg_auto_size(title, prompt, &height, &width, MIN_HIGH + use_height, 26); 353 } 354 dlg_button_layout(buttons, &width); 355 dlg_print_size(height, width); 356 dlg_ctl_size(height, width); 357 358 x = dlg_box_x_ordinate(width); 359 y = dlg_box_y_ordinate(height); 360 361 dialog = dlg_new_window(height, width, y, x); 362 dlg_register_window(dialog, "menubox", binding); 363 dlg_register_buttons(dialog, "menubox", buttons); 364 365 dlg_mouse_setbase(x, y); 366 367 dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); 368 dlg_draw_bottom_box(dialog); 369 dlg_draw_title(dialog, title); 370 371 wattrset(dialog, dialog_attr); 372 dlg_print_autowrap(dialog, prompt, height, width); 373 374 menu_width = width - 6; 375 getyx(dialog, cur_y, cur_x); 376 box_y = cur_y + 1; 377 box_x = (width - menu_width) / 2 - 1; 378 379 /* 380 * After displaying the prompt, we know how much space we really have. 381 * Limit the list to avoid overwriting the ok-button. 382 */ 383 if (use_height + MIN_HIGH > height - cur_y) 384 use_height = height - MIN_HIGH - cur_y; 385 if (use_height <= 0) 386 use_height = 1; 387 388 /* Find out maximal number of displayable items at once. */ 389 max_choice = MIN(use_height, 390 RowHeight(item_no)); 391 if (is_inputmenu) 392 max_choice /= INPUT_ROWS; 393 394 /* create new window for the menu */ 395 menu = dlg_sub_window(dialog, use_height, menu_width, 396 y + box_y + 1, 397 x + box_x + 1); 398 dlg_register_window(menu, "menu", binding2); 399 dlg_register_buttons(menu, "menu", buttons); 400 401 /* draw a box around the menu items */ 402 dlg_draw_box(dialog, box_y, box_x, use_height + 2, menu_width + 2, 403 menubox_border_attr, menubox_attr); 404 405 name_width = 0; 406 text_width = 0; 407 408 /* Find length of longest item to center menu * 409 * only if --menu was given, using --inputmenu * 410 * won't be centered. */ 411 for (i = 0; i < item_no; i++) { 412 name_width = MAX(name_width, dlg_count_columns(items[i].name)); 413 text_width = MAX(text_width, dlg_count_columns(items[i].text)); 414 } 415 416 /* If the name+text is wider than the list is allowed, then truncate 417 * one or both of them. If the name is no wider than 30% of the list, 418 * leave it intact. 419 * 420 * FIXME: the gutter width and name/list ratio should be configurable. 421 */ 422 use_width = (menu_width - GUTTER); 423 if (text_width + name_width > use_width) { 424 int need = (int) (0.30 * use_width); 425 if (name_width > need) { 426 int want = (int) (use_width 427 * ((double) name_width) 428 / (text_width + name_width)); 429 name_width = (want > need) ? want : need; 430 } 431 text_width = use_width - name_width; 432 } 433 434 tag_x = (is_inputmenu 435 ? 0 436 : (use_width - text_width - name_width) / 2); 437 item_x = name_width + tag_x + GUTTER; 438 439 if (choice - scrollamt >= max_choice) { 440 scrollamt = choice - (max_choice - 1); 441 choice = max_choice - 1; 442 } 443 444 /* Print the menu */ 445 for (i = 0; i < max_choice; i++) { 446 print_item(menu, 447 &items[i + scrollamt], 448 i, 449 (i == choice) ? Selected : Unselected, 450 is_inputmenu); 451 } 452 (void) wnoutrefresh(menu); 453 454 /* register the new window, along with its borders */ 455 dlg_mouse_mkbigregion(box_y + 1, box_x, use_height + 2, menu_width + 2, 456 KEY_MAX, 1, 1, 1 /* by lines */ ); 457 458 print_arrows(dialog, box_x, box_y, scrollamt, max_choice, item_no, use_height); 459 460 dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); 461 462 while (result == DLG_EXIT_UNKNOWN) { 463 if (button < 0) /* --visit-items */ 464 wmove(dialog, box_y + ItemToRow(choice) + 1, box_x + tag_x + 1); 465 466 key = dlg_mouse_wgetch(dialog, &fkey); 467 if (dlg_result_key(key, fkey, &result)) 468 break; 469 470 found = FALSE; 471 if (fkey) { 472 /* 473 * Allow a mouse-click on a box to switch selection to that box. 474 * Handling a button click is a little more complicated, since we 475 * push a KEY_ENTER back onto the input stream so we'll put the 476 * cursor at the right place before handling the "keypress". 477 */ 478 if (key >= DLGK_MOUSE(KEY_MAX)) { 479 key -= DLGK_MOUSE(KEY_MAX); 480 i = RowToItem(key); 481 if (i < max_choice) { 482 found = TRUE; 483 } else { 484 beep(); 485 continue; 486 } 487 } else if (is_DLGK_MOUSE(key) 488 && dlg_ok_buttoncode(key - M_EVENT) >= 0) { 489 button = (key - M_EVENT); 490 ungetch('\n'); 491 continue; 492 } 493 } else { 494 /* 495 * Check if key pressed matches first character of any item tag in 496 * list. If there is more than one match, we will cycle through 497 * each one as the same key is pressed repeatedly. 498 */ 499 if (button < 0 || !dialog_state.visit_items) { 500 for (j = scrollamt + choice + 1; j < item_no; j++) { 501 if (dlg_match_char(dlg_last_getc(), items[j].name)) { 502 found = TRUE; 503 i = j - scrollamt; 504 break; 505 } 506 } 507 if (!found) { 508 for (j = 0; j <= scrollamt + choice; j++) { 509 if (dlg_match_char(dlg_last_getc(), items[j].name)) { 510 found = TRUE; 511 i = j - scrollamt; 512 break; 513 } 514 } 515 } 516 if (found) 517 dlg_flush_getc(); 518 } else if ((j = dlg_char_to_button(key, buttons)) >= 0) { 519 button = j; 520 ungetch('\n'); 521 continue; 522 } 523 524 /* 525 * A single digit (1-9) positions the selection to that line in the 526 * current screen. 527 */ 528 if (!found 529 && (key <= '9') 530 && (key > '0') 531 && (key - '1' < max_choice)) { 532 found = TRUE; 533 i = key - '1'; 534 } 535 } 536 537 if (!found && fkey) { 538 found = TRUE; 539 switch (key) { 540 case DLGK_PAGE_FIRST: 541 i = -scrollamt; 542 break; 543 case DLGK_PAGE_LAST: 544 i = item_no - 1 - scrollamt; 545 break; 546 case DLGK_MOUSE(KEY_PPAGE): 547 case DLGK_PAGE_PREV: 548 if (choice) 549 i = 0; 550 else if (scrollamt != 0) 551 i = -MIN(scrollamt, max_choice); 552 else 553 continue; 554 break; 555 case DLGK_MOUSE(KEY_NPAGE): 556 case DLGK_PAGE_NEXT: 557 i = MIN(choice + max_choice, item_no - scrollamt - 1); 558 break; 559 case DLGK_ITEM_PREV: 560 i = choice - 1; 561 if (choice == 0 && scrollamt == 0) 562 continue; 563 break; 564 case DLGK_ITEM_NEXT: 565 i = choice + 1; 566 if (scrollamt + choice >= item_no - 1) 567 continue; 568 break; 569 default: 570 found = FALSE; 571 break; 572 } 573 } 574 575 if (found) { 576 if (i != choice) { 577 getyx(dialog, cur_y, cur_x); 578 if (i < 0 || i >= max_choice) { 579 #if defined(NCURSES_VERSION_MAJOR) && NCURSES_VERSION_MAJOR < 5 580 /* 581 * Using wscrl to assist ncurses scrolling is not needed 582 * in version 5.x 583 */ 584 if (i == -1) { 585 if (use_height > 1) { 586 /* De-highlight current first item */ 587 print_item(menu, 588 &items[scrollamt], 589 0, Unselected, is_inputmenu); 590 scrollok(menu, TRUE); 591 wscrl(menu, -RowHeight(1)); 592 scrollok(menu, FALSE); 593 } 594 scrollamt--; 595 print_item(menu, 596 &items[scrollamt], 597 0, Selected, is_inputmenu); 598 } else if (i == max_choice) { 599 if (use_height > 1) { 600 /* De-highlight current last item before scrolling up */ 601 print_item(menu, 602 &items[scrollamt + max_choice - 1], 603 max_choice - 1, 604 Unselected, 605 is_inputmenu); 606 scrollok(menu, TRUE); 607 wscrl(menu, RowHeight(1)); 608 scrollok(menu, FALSE); 609 } 610 scrollamt++; 611 print_item(menu, 612 &items[scrollamt + max_choice - 1], 613 max_choice - 1, TRUE, 614 is_inputmenu); 615 } else 616 #endif 617 { 618 if (i < 0) { 619 scrollamt += i; 620 choice = 0; 621 } else { 622 choice = max_choice - 1; 623 scrollamt += (i - max_choice + 1); 624 } 625 for (i = 0; i < max_choice; i++) { 626 print_item(menu, 627 &items[scrollamt + i], 628 i, 629 (i == choice) ? Selected : Unselected, 630 is_inputmenu); 631 } 632 } 633 /* Clean bottom lines */ 634 if (is_inputmenu) { 635 int spare_lines, x_count; 636 spare_lines = use_height % INPUT_ROWS; 637 wattrset(menu, menubox_attr); 638 for (; spare_lines; spare_lines--) { 639 wmove(menu, use_height - spare_lines, 0); 640 for (x_count = 0; x_count < menu_width; 641 x_count++) { 642 waddch(menu, ' '); 643 } 644 } 645 } 646 (void) wnoutrefresh(menu); 647 print_arrows(dialog, 648 box_x, box_y, 649 scrollamt, max_choice, item_no, use_height); 650 } else { 651 /* De-highlight current item */ 652 print_item(menu, 653 &items[scrollamt + choice], 654 choice, 655 Unselected, 656 is_inputmenu); 657 /* Highlight new item */ 658 choice = i; 659 print_item(menu, 660 &items[scrollamt + choice], 661 choice, 662 Selected, 663 is_inputmenu); 664 (void) wnoutrefresh(menu); 665 print_arrows(dialog, 666 box_x, box_y, 667 scrollamt, max_choice, item_no, use_height); 668 (void) wmove(dialog, cur_y, cur_x); 669 wrefresh(dialog); 670 } 671 } 672 continue; /* wait for another key press */ 673 } 674 675 if (fkey) { 676 switch (key) { 677 case DLGK_FIELD_PREV: 678 button = dlg_prev_button(buttons, button); 679 dlg_draw_buttons(dialog, height - 2, 0, buttons, button, 680 FALSE, width); 681 break; 682 case DLGK_FIELD_NEXT: 683 button = dlg_next_button(buttons, button); 684 dlg_draw_buttons(dialog, height - 2, 0, buttons, button, 685 FALSE, width); 686 break; 687 case DLGK_ENTER: 688 result = dlg_ok_buttoncode(button); 689 690 /* 691 * If dlg_menu() is called from dialog_menu(), we want to 692 * capture the results into dialog_vars.input_result, but not 693 * if dlg_menu() is called directly from an application. We 694 * can check this by testing if rename_menutext is the function 695 * pointer owned by dialog_menu(). It would be nicer to have 696 * this logic inside dialog_menu(), but that cannot be done 697 * since we would lose compatibility for the results reported 698 * after input_menu_edit(). 699 */ 700 if (result == DLG_EXIT_ERROR) { 701 result = DLG_EXIT_UNKNOWN; 702 } else if (is_inputmenu 703 || rename_menutext == dlg_dummy_menutext) { 704 result = handle_button(result, 705 items, 706 scrollamt + choice); 707 } 708 709 /* 710 * If we have a rename_menutext function, interpret the Extra 711 * button as a request to rename the menu's text. If that 712 * function doesn't return "Unknown", we will exit from this 713 * function. Usually that is done for dialog_menu(), so the 714 * shell script can use the updated value. If it does return 715 * "Unknown", update the list item only. A direct caller of 716 * dlg_menu() can free the renamed value - we cannot. 717 */ 718 if (is_inputmenu && result == DLG_EXIT_EXTRA) { 719 char *tmp; 720 721 if (input_menu_edit(menu, 722 &items[scrollamt + choice], 723 choice, 724 &tmp)) { 725 result = rename_menutext(items, scrollamt + choice, tmp); 726 if (result == DLG_EXIT_UNKNOWN) { 727 items[scrollamt + choice].text = tmp; 728 } else { 729 free(tmp); 730 } 731 } else { 732 result = DLG_EXIT_UNKNOWN; 733 print_item(menu, 734 &items[scrollamt + choice], 735 choice, 736 Selected, 737 is_inputmenu); 738 (void) wnoutrefresh(menu); 739 free(tmp); 740 } 741 742 if (result == DLG_EXIT_UNKNOWN) { 743 dlg_draw_buttons(dialog, height - 2, 0, 744 buttons, button, FALSE, width); 745 } 746 } 747 break; 748 #ifdef KEY_RESIZE 749 case KEY_RESIZE: 750 /* reset data */ 751 height = old_height; 752 width = old_width; 753 /* repaint */ 754 dlg_clear(); 755 dlg_del_window(dialog); 756 refresh(); 757 dlg_mouse_free_regions(); 758 goto retry; 759 #endif 760 default: 761 flash(); 762 break; 763 } 764 } 765 } 766 767 dlg_mouse_free_regions(); 768 dlg_unregister_window(menu); 769 dlg_del_window(dialog); 770 free(prompt); 771 772 *current_item = scrollamt + choice; 773 return result; 774 } 775 776 /* 777 * Display a menu for choosing among a number of options 778 */ 779 int 780 dialog_menu(const char *title, 781 const char *cprompt, 782 int height, 783 int width, 784 int menu_height, 785 int item_no, 786 char **items) 787 { 788 int result; 789 int choice; 790 int i; 791 DIALOG_LISTITEM *listitems; 792 793 listitems = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1); 794 assert_ptr(listitems, "dialog_menu"); 795 796 for (i = 0; i < item_no; ++i) { 797 listitems[i].name = ItemName(i); 798 listitems[i].text = ItemText(i); 799 listitems[i].help = ((dialog_vars.item_help) 800 ? ItemHelp(i) 801 : dlg_strempty()); 802 } 803 dlg_align_columns(&listitems[0].text, sizeof(DIALOG_LISTITEM), item_no); 804 805 result = dlg_menu(title, 806 cprompt, 807 height, 808 width, 809 menu_height, 810 item_no, 811 listitems, 812 &choice, 813 dialog_vars.input_menu ? dlg_renamed_menutext : dlg_dummy_menutext); 814 815 dlg_free_columns(&listitems[0].text, sizeof(DIALOG_LISTITEM), item_no); 816 free(listitems); 817 return result; 818 } 819