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