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