1217309Snwhitehorn/* 2255852Sdteske * $Id: menubox.c,v 1.148 2013/09/02 17:15:13 tom Exp $ 3217309Snwhitehorn * 4217309Snwhitehorn * menubox.c -- implements the menu box 5217309Snwhitehorn * 6255852Sdteske * Copyright 2000-2012,2013 Thomas E. Dickey 7217309Snwhitehorn * 8217309Snwhitehorn * This program is free software; you can redistribute it and/or modify 9217309Snwhitehorn * it under the terms of the GNU Lesser General Public Licens, version 2.1e 10217309Snwhitehorn * as published by the Free Software Foundation. 11217309Snwhitehorn * 12217309Snwhitehorn * This program is distributed in the hope that it will be useful, but 13217309Snwhitehorn * WITHOUT ANY WARRANTY; without even the implied warranty of 14217309Snwhitehorn * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15217309Snwhitehorn * Lesser General Public License for more details. 16217309Snwhitehorn * 17217309Snwhitehorn * You should have received a copy of the GNU Lesser General Public 18217309Snwhitehorn * License along with this program; if not, write to 19217309Snwhitehorn * Free Software Foundation, Inc. 20217309Snwhitehorn * 51 Franklin St., Fifth Floor 21217309Snwhitehorn * Boston, MA 02110, USA. 22217309Snwhitehorn * 23217309Snwhitehorn * An earlier version of this program lists as authors 24217309Snwhitehorn * Savio Lam (lam836@cs.cuhk.hk) 25217309Snwhitehorn */ 26217309Snwhitehorn 27217309Snwhitehorn#include <dialog.h> 28217309Snwhitehorn#include <dlg_keys.h> 29217309Snwhitehorn 30217309Snwhitehorntypedef enum { 31217309Snwhitehorn Unselected = 0, 32217309Snwhitehorn Selected, 33217309Snwhitehorn Editing 34217309Snwhitehorn} Mode; 35217309Snwhitehorn 36251843Sbapttypedef struct { 37251843Sbapt /* the outer-window */ 38251843Sbapt WINDOW *dialog; 39251843Sbapt int box_y; 40251843Sbapt int box_x; 41251843Sbapt int tag_x; 42251843Sbapt int item_x; 43251843Sbapt int menu_height; 44251843Sbapt int menu_width; 45251843Sbapt /* the inner-window */ 46251843Sbapt WINDOW *menu; 47251843Sbapt DIALOG_LISTITEM *items; 48251843Sbapt int item_no; 49251843Sbapt} ALL_DATA; 50251843Sbapt 51217309Snwhitehorn#define MIN_HIGH (1 + (5 * MARGIN)) 52217309Snwhitehorn 53217309Snwhitehorn#define INPUT_ROWS 3 /* rows per inputmenu entry */ 54217309Snwhitehorn 55217309Snwhitehorn#define RowHeight(i) (is_inputmenu ? ((i) * INPUT_ROWS) : ((i) * 1)) 56217309Snwhitehorn#define ItemToRow(i) (is_inputmenu ? ((i) * INPUT_ROWS + 1) : (i)) 57217309Snwhitehorn#define RowToItem(i) (is_inputmenu ? ((i) / INPUT_ROWS + 0) : (i)) 58217309Snwhitehorn 59217309Snwhitehorn/* 60217309Snwhitehorn * Print menu item 61217309Snwhitehorn */ 62217309Snwhitehornstatic void 63251843Sbaptprint_item(ALL_DATA * data, 64251843Sbapt WINDOW *win, 65251843Sbapt DIALOG_LISTITEM * item, 66217309Snwhitehorn int choice, 67217309Snwhitehorn Mode selected, 68217309Snwhitehorn bool is_inputmenu) 69217309Snwhitehorn{ 70220749Snwhitehorn chtype save = dlg_get_attrs(win); 71217309Snwhitehorn int n; 72251843Sbapt int climit = (data->item_x - data->tag_x - GUTTER); 73251843Sbapt int my_width = data->menu_width; 74251843Sbapt int my_x = data->item_x; 75217309Snwhitehorn int my_y = ItemToRow(choice); 76251843Sbapt bool both = (!dialog_vars.no_tags && !dialog_vars.no_items); 77251843Sbapt bool first = TRUE; 78217309Snwhitehorn chtype bordchar; 79251843Sbapt const char *show = (dialog_vars.no_items 80251843Sbapt ? item->name 81251843Sbapt : item->text); 82217309Snwhitehorn 83217309Snwhitehorn switch (selected) { 84217309Snwhitehorn default: 85217309Snwhitehorn case Unselected: 86217309Snwhitehorn bordchar = item_attr; 87217309Snwhitehorn break; 88217309Snwhitehorn case Selected: 89217309Snwhitehorn bordchar = item_selected_attr; 90217309Snwhitehorn break; 91217309Snwhitehorn case Editing: 92217309Snwhitehorn bordchar = dialog_attr; 93217309Snwhitehorn break; 94217309Snwhitehorn } 95217309Snwhitehorn 96217309Snwhitehorn /* Clear 'residue' of last item and mark current current item */ 97217309Snwhitehorn if (is_inputmenu) { 98251843Sbapt (void) wattrset(win, (selected != Unselected) ? item_selected_attr : item_attr); 99217309Snwhitehorn for (n = my_y - 1; n < my_y + INPUT_ROWS - 1; n++) { 100217309Snwhitehorn wmove(win, n, 0); 101217309Snwhitehorn wprintw(win, "%*s", my_width, " "); 102217309Snwhitehorn } 103217309Snwhitehorn } else { 104251843Sbapt (void) wattrset(win, menubox_attr); 105217309Snwhitehorn wmove(win, my_y, 0); 106217309Snwhitehorn wprintw(win, "%*s", my_width, " "); 107217309Snwhitehorn } 108217309Snwhitehorn 109251843Sbapt /* highlight first char of the tag to be special */ 110251843Sbapt if (both) { 111251843Sbapt (void) wmove(win, my_y, data->tag_x); 112251843Sbapt dlg_print_listitem(win, item->name, climit, first, selected); 113251843Sbapt first = FALSE; 114251843Sbapt } 115217309Snwhitehorn 116217309Snwhitehorn /* Draw the input field box (only for inputmenu) */ 117217309Snwhitehorn (void) wmove(win, my_y, my_x); 118217309Snwhitehorn if (is_inputmenu) { 119217309Snwhitehorn my_width -= 1; 120251843Sbapt dlg_draw_box(win, my_y - 1, my_x, INPUT_ROWS, my_width - my_x - data->tag_x, 121217309Snwhitehorn bordchar, 122217309Snwhitehorn bordchar); 123217309Snwhitehorn my_width -= 1; 124217309Snwhitehorn ++my_x; 125217309Snwhitehorn } 126217309Snwhitehorn 127217309Snwhitehorn /* print actual item */ 128217309Snwhitehorn wmove(win, my_y, my_x); 129251843Sbapt dlg_print_listitem(win, show, my_width - my_x, first, selected); 130217309Snwhitehorn 131217309Snwhitehorn if (selected) { 132251843Sbapt dlg_item_help(item->help); 133217309Snwhitehorn } 134251843Sbapt (void) wattrset(win, save); 135217309Snwhitehorn} 136217309Snwhitehorn 137217309Snwhitehorn/* 138217309Snwhitehorn * Allow the user to edit the text of a menu entry. 139217309Snwhitehorn */ 140217309Snwhitehornstatic int 141251843Sbaptinput_menu_edit(ALL_DATA * data, 142217309Snwhitehorn DIALOG_LISTITEM * items, 143217309Snwhitehorn int choice, 144217309Snwhitehorn char **resultp) 145217309Snwhitehorn{ 146251843Sbapt chtype save = dlg_get_attrs(data->menu); 147217309Snwhitehorn char *result; 148217309Snwhitehorn int offset = 0; 149217309Snwhitehorn int key = 0, fkey = 0; 150217309Snwhitehorn int first = TRUE; 151217309Snwhitehorn /* see above */ 152217309Snwhitehorn bool is_inputmenu = TRUE; 153217309Snwhitehorn int y = ItemToRow(choice); 154217309Snwhitehorn int code = TRUE; 155217309Snwhitehorn int max_len = dlg_max_input(MAX((int) strlen(items->text) + 1, MAX_LEN)); 156217309Snwhitehorn 157217309Snwhitehorn result = dlg_malloc(char, (size_t) max_len); 158217309Snwhitehorn assert_ptr(result, "input_menu_edit"); 159217309Snwhitehorn 160217309Snwhitehorn /* original item is used to initialize the input string. */ 161217309Snwhitehorn result[0] = '\0'; 162217309Snwhitehorn strcpy(result, items->text); 163217309Snwhitehorn 164251843Sbapt print_item(data, data->menu, items, choice, Editing, TRUE); 165217309Snwhitehorn 166217309Snwhitehorn /* taken out of inputbox.c - but somewhat modified */ 167217309Snwhitehorn for (;;) { 168217309Snwhitehorn if (!first) 169251843Sbapt key = dlg_mouse_wgetch(data->menu, &fkey); 170217309Snwhitehorn if (dlg_edit_string(result, &offset, key, fkey, first)) { 171251843Sbapt dlg_show_string(data->menu, result, offset, inputbox_attr, 172251843Sbapt y, 173251843Sbapt data->item_x + 1, 174251843Sbapt data->menu_width - data->item_x - 3, 175217309Snwhitehorn FALSE, first); 176217309Snwhitehorn first = FALSE; 177217309Snwhitehorn } else if (key == ESC || key == TAB) { 178217309Snwhitehorn code = FALSE; 179217309Snwhitehorn break; 180217309Snwhitehorn } else { 181217309Snwhitehorn break; 182217309Snwhitehorn } 183217309Snwhitehorn } 184251843Sbapt print_item(data, data->menu, items, choice, Selected, TRUE); 185251843Sbapt (void) wattrset(data->menu, save); 186217309Snwhitehorn 187217309Snwhitehorn *resultp = result; 188217309Snwhitehorn return code; 189217309Snwhitehorn} 190217309Snwhitehorn 191217309Snwhitehornstatic int 192217309Snwhitehornhandle_button(int code, DIALOG_LISTITEM * items, int choice) 193217309Snwhitehorn{ 194255852Sdteske char *help_result; 195255852Sdteske 196217309Snwhitehorn switch (code) { 197217309Snwhitehorn case DLG_EXIT_OK: /* FALLTHRU */ 198217309Snwhitehorn case DLG_EXIT_EXTRA: 199217309Snwhitehorn dlg_add_string(items[choice].name); 200217309Snwhitehorn break; 201217309Snwhitehorn case DLG_EXIT_HELP: 202255852Sdteske dlg_add_help_listitem(&code, &help_result, &items[choice]); 203255852Sdteske dlg_add_string(help_result); 204217309Snwhitehorn break; 205217309Snwhitehorn } 206217309Snwhitehorn return code; 207217309Snwhitehorn} 208217309Snwhitehorn 209251843Sbaptint 210217309Snwhitehorndlg_renamed_menutext(DIALOG_LISTITEM * items, int current, char *newtext) 211217309Snwhitehorn{ 212217309Snwhitehorn if (dialog_vars.input_result) 213217309Snwhitehorn dialog_vars.input_result[0] = '\0'; 214217309Snwhitehorn dlg_add_result("RENAMED "); 215217309Snwhitehorn dlg_add_string(items[current].name); 216217309Snwhitehorn dlg_add_result(" "); 217217309Snwhitehorn dlg_add_string(newtext); 218217309Snwhitehorn return DLG_EXIT_EXTRA; 219217309Snwhitehorn} 220217309Snwhitehorn 221251843Sbaptint 222217309Snwhitehorndlg_dummy_menutext(DIALOG_LISTITEM * items, int current, char *newtext) 223217309Snwhitehorn{ 224217309Snwhitehorn (void) items; 225217309Snwhitehorn (void) current; 226217309Snwhitehorn (void) newtext; 227217309Snwhitehorn return DLG_EXIT_ERROR; 228217309Snwhitehorn} 229217309Snwhitehorn 230251843Sbaptstatic void 231251843Sbaptprint_menu(ALL_DATA * data, int choice, int scrollamt, int max_choice, bool is_inputmenu) 232251843Sbapt{ 233251843Sbapt int i; 234251843Sbapt 235251843Sbapt for (i = 0; i < max_choice; i++) { 236251843Sbapt print_item(data, 237251843Sbapt data->menu, 238251843Sbapt &data->items[i + scrollamt], 239251843Sbapt i, 240251843Sbapt (i == choice) ? Selected : Unselected, 241251843Sbapt is_inputmenu); 242251843Sbapt } 243251843Sbapt 244251843Sbapt /* Clean bottom lines */ 245251843Sbapt if (is_inputmenu) { 246251843Sbapt int spare_lines, x_count; 247251843Sbapt spare_lines = data->menu_height % INPUT_ROWS; 248251843Sbapt (void) wattrset(data->menu, menubox_attr); 249251843Sbapt for (; spare_lines; spare_lines--) { 250251843Sbapt wmove(data->menu, data->menu_height - spare_lines, 0); 251251843Sbapt for (x_count = 0; x_count < data->menu_width; 252251843Sbapt x_count++) { 253251843Sbapt waddch(data->menu, ' '); 254251843Sbapt } 255251843Sbapt } 256251843Sbapt } 257251843Sbapt 258251843Sbapt (void) wnoutrefresh(data->menu); 259251843Sbapt 260251843Sbapt dlg_draw_scrollbar(data->dialog, 261251843Sbapt scrollamt, 262251843Sbapt scrollamt, 263251843Sbapt scrollamt + max_choice, 264251843Sbapt data->item_no, 265251843Sbapt data->box_x, 266251843Sbapt data->box_x + data->menu_width, 267251843Sbapt data->box_y, 268251843Sbapt data->box_y + data->menu_height + 1, 269251843Sbapt menubox_border2_attr, 270251843Sbapt menubox_border_attr); 271251843Sbapt} 272251843Sbapt 273251843Sbaptstatic bool 274251843Sbaptcheck_hotkey(DIALOG_LISTITEM * items, int choice) 275251843Sbapt{ 276251843Sbapt bool result = FALSE; 277251843Sbapt 278251843Sbapt if (dlg_match_char(dlg_last_getc(), 279251843Sbapt (dialog_vars.no_tags 280251843Sbapt ? items[choice].text 281251843Sbapt : items[choice].name))) { 282251843Sbapt result = TRUE; 283251843Sbapt } 284251843Sbapt return result; 285251843Sbapt} 286251843Sbapt 287217309Snwhitehorn/* 288217309Snwhitehorn * This is an alternate interface to 'menu' which allows the application 289217309Snwhitehorn * to read the list item states back directly without putting them in the 290217309Snwhitehorn * output buffer. 291217309Snwhitehorn */ 292217309Snwhitehornint 293217309Snwhitehorndlg_menu(const char *title, 294217309Snwhitehorn const char *cprompt, 295217309Snwhitehorn int height, 296217309Snwhitehorn int width, 297217309Snwhitehorn int menu_height, 298217309Snwhitehorn int item_no, 299217309Snwhitehorn DIALOG_LISTITEM * items, 300217309Snwhitehorn int *current_item, 301217309Snwhitehorn DIALOG_INPUTMENU rename_menutext) 302217309Snwhitehorn{ 303217309Snwhitehorn /* *INDENT-OFF* */ 304217309Snwhitehorn static DLG_KEYS_BINDING binding[] = { 305224014Snwhitehorn HELPKEY_BINDINGS, 306217309Snwhitehorn ENTERKEY_BINDINGS, 307217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_NEXT, ' ' ), 308217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ), 309217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), 310217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), 311217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ), 312217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_NEXT, '+' ), 313217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN ), 314217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_NEXT, CHR_NEXT ), 315217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_PREV, '-' ), 316217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ), 317217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_PREV, CHR_PREVIOUS ), 318217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_FIRST, KEY_HOME ), 319217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_END ), 320217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_LL ), 321217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_NEXT, KEY_NPAGE ), 322217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_PREV, KEY_PPAGE ), 323217309Snwhitehorn END_KEYS_BINDING 324217309Snwhitehorn }; 325217309Snwhitehorn static DLG_KEYS_BINDING binding2[] = { 326217309Snwhitehorn INPUTSTR_BINDINGS, 327224014Snwhitehorn HELPKEY_BINDINGS, 328217309Snwhitehorn ENTERKEY_BINDINGS, 329217309Snwhitehorn END_KEYS_BINDING 330217309Snwhitehorn }; 331217309Snwhitehorn /* *INDENT-ON* */ 332217309Snwhitehorn 333217309Snwhitehorn#ifdef KEY_RESIZE 334217309Snwhitehorn int old_height = height; 335217309Snwhitehorn int old_width = width; 336217309Snwhitehorn#endif 337251843Sbapt ALL_DATA all; 338251843Sbapt int i, j, x, y, cur_x, cur_y; 339217309Snwhitehorn int key = 0, fkey; 340251843Sbapt int button = dialog_state.visit_items ? -1 : dlg_default_button(); 341217309Snwhitehorn int choice = dlg_default_listitem(items); 342217309Snwhitehorn int result = DLG_EXIT_UNKNOWN; 343217309Snwhitehorn int scrollamt = 0; 344251843Sbapt int max_choice; 345217309Snwhitehorn int found; 346251843Sbapt int use_width, name_width, text_width, list_width; 347217309Snwhitehorn WINDOW *dialog, *menu; 348217309Snwhitehorn char *prompt = dlg_strclone(cprompt); 349217309Snwhitehorn const char **buttons = dlg_ok_labels(); 350251843Sbapt bool is_inputmenu = ((rename_menutext != 0) 351251843Sbapt && (rename_menutext != dlg_dummy_menutext)); 352217309Snwhitehorn 353251843Sbapt all.items = items; 354251843Sbapt all.item_no = item_no; 355251843Sbapt 356217309Snwhitehorn dlg_does_output(); 357217309Snwhitehorn dlg_tab_correct_str(prompt); 358217309Snwhitehorn 359217309Snwhitehorn#ifdef KEY_RESIZE 360217309Snwhitehorn retry: 361217309Snwhitehorn#endif 362217309Snwhitehorn 363251843Sbapt all.menu_height = menu_height; 364251843Sbapt use_width = dlg_calc_list_width(item_no, items) + 10; 365251843Sbapt use_width = MAX(26, use_width); 366251843Sbapt if (all.menu_height == 0) { 367217309Snwhitehorn /* calculate height without items (4) */ 368251843Sbapt dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, use_width); 369251843Sbapt dlg_calc_listh(&height, &all.menu_height, item_no); 370217309Snwhitehorn } else { 371251843Sbapt dlg_auto_size(title, prompt, 372251843Sbapt &height, &width, 373251843Sbapt MIN_HIGH + all.menu_height, use_width); 374217309Snwhitehorn } 375217309Snwhitehorn dlg_button_layout(buttons, &width); 376217309Snwhitehorn dlg_print_size(height, width); 377217309Snwhitehorn dlg_ctl_size(height, width); 378217309Snwhitehorn 379217309Snwhitehorn x = dlg_box_x_ordinate(width); 380217309Snwhitehorn y = dlg_box_y_ordinate(height); 381217309Snwhitehorn 382217309Snwhitehorn dialog = dlg_new_window(height, width, y, x); 383251843Sbapt all.dialog = dialog; 384251843Sbapt 385217309Snwhitehorn dlg_register_window(dialog, "menubox", binding); 386217309Snwhitehorn dlg_register_buttons(dialog, "menubox", buttons); 387217309Snwhitehorn 388217309Snwhitehorn dlg_mouse_setbase(x, y); 389217309Snwhitehorn 390251843Sbapt dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr); 391251843Sbapt dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr); 392217309Snwhitehorn dlg_draw_title(dialog, title); 393217309Snwhitehorn 394251843Sbapt (void) wattrset(dialog, dialog_attr); 395217309Snwhitehorn dlg_print_autowrap(dialog, prompt, height, width); 396217309Snwhitehorn 397251843Sbapt all.menu_width = width - 6; 398217309Snwhitehorn getyx(dialog, cur_y, cur_x); 399251843Sbapt all.box_y = cur_y + 1; 400251843Sbapt all.box_x = (width - all.menu_width) / 2 - 1; 401217309Snwhitehorn 402217309Snwhitehorn /* 403217309Snwhitehorn * After displaying the prompt, we know how much space we really have. 404217309Snwhitehorn * Limit the list to avoid overwriting the ok-button. 405217309Snwhitehorn */ 406251843Sbapt if (all.menu_height + MIN_HIGH > height - cur_y) 407251843Sbapt all.menu_height = height - MIN_HIGH - cur_y; 408251843Sbapt if (all.menu_height <= 0) 409251843Sbapt all.menu_height = 1; 410217309Snwhitehorn 411217309Snwhitehorn /* Find out maximal number of displayable items at once. */ 412251843Sbapt max_choice = MIN(all.menu_height, 413217309Snwhitehorn RowHeight(item_no)); 414217309Snwhitehorn if (is_inputmenu) 415217309Snwhitehorn max_choice /= INPUT_ROWS; 416217309Snwhitehorn 417217309Snwhitehorn /* create new window for the menu */ 418251843Sbapt menu = dlg_sub_window(dialog, all.menu_height, all.menu_width, 419251843Sbapt y + all.box_y + 1, 420251843Sbapt x + all.box_x + 1); 421251843Sbapt all.menu = menu; 422251843Sbapt 423217309Snwhitehorn dlg_register_window(menu, "menu", binding2); 424217309Snwhitehorn dlg_register_buttons(menu, "menu", buttons); 425217309Snwhitehorn 426217309Snwhitehorn /* draw a box around the menu items */ 427251843Sbapt dlg_draw_box(dialog, 428251843Sbapt all.box_y, all.box_x, 429251843Sbapt all.menu_height + 2, all.menu_width + 2, 430251843Sbapt menubox_border_attr, menubox_border2_attr); 431217309Snwhitehorn 432217309Snwhitehorn name_width = 0; 433217309Snwhitehorn text_width = 0; 434217309Snwhitehorn 435217309Snwhitehorn /* Find length of longest item to center menu * 436217309Snwhitehorn * only if --menu was given, using --inputmenu * 437217309Snwhitehorn * won't be centered. */ 438217309Snwhitehorn for (i = 0; i < item_no; i++) { 439217309Snwhitehorn name_width = MAX(name_width, dlg_count_columns(items[i].name)); 440217309Snwhitehorn text_width = MAX(text_width, dlg_count_columns(items[i].text)); 441217309Snwhitehorn } 442217309Snwhitehorn 443217309Snwhitehorn /* If the name+text is wider than the list is allowed, then truncate 444217309Snwhitehorn * one or both of them. If the name is no wider than 30% of the list, 445217309Snwhitehorn * leave it intact. 446217309Snwhitehorn * 447217309Snwhitehorn * FIXME: the gutter width and name/list ratio should be configurable. 448217309Snwhitehorn */ 449251843Sbapt use_width = (all.menu_width - GUTTER); 450251843Sbapt if (dialog_vars.no_tags) { 451251843Sbapt list_width = MIN(use_width, text_width); 452251843Sbapt } else if (dialog_vars.no_items) { 453251843Sbapt list_width = MIN(use_width, name_width); 454251843Sbapt } else { 455251843Sbapt if (text_width >= 0 456251843Sbapt && name_width >= 0 457251843Sbapt && use_width > 0 458251843Sbapt && text_width + name_width > use_width) { 459251843Sbapt int need = (int) (0.30 * use_width); 460251843Sbapt if (name_width > need) { 461251843Sbapt int want = (int) (use_width 462251843Sbapt * ((double) name_width) 463251843Sbapt / (text_width + name_width)); 464251843Sbapt name_width = (want > need) ? want : need; 465251843Sbapt } 466251843Sbapt text_width = use_width - name_width; 467217309Snwhitehorn } 468251843Sbapt list_width = (text_width + name_width); 469217309Snwhitehorn } 470217309Snwhitehorn 471251843Sbapt all.tag_x = (is_inputmenu 472251843Sbapt ? 0 473251843Sbapt : (use_width - list_width) / 2); 474251843Sbapt all.item_x = ((dialog_vars.no_tags 475251843Sbapt ? 0 476251843Sbapt : (dialog_vars.no_items 477251843Sbapt ? 0 478251843Sbapt : (GUTTER + name_width))) 479251843Sbapt + all.tag_x); 480217309Snwhitehorn 481217309Snwhitehorn if (choice - scrollamt >= max_choice) { 482217309Snwhitehorn scrollamt = choice - (max_choice - 1); 483217309Snwhitehorn choice = max_choice - 1; 484217309Snwhitehorn } 485217309Snwhitehorn 486251843Sbapt print_menu(&all, choice, scrollamt, max_choice, is_inputmenu); 487217309Snwhitehorn 488217309Snwhitehorn /* register the new window, along with its borders */ 489251843Sbapt dlg_mouse_mkbigregion(all.box_y + 1, all.box_x, 490251843Sbapt all.menu_height + 2, all.menu_width + 2, 491217309Snwhitehorn KEY_MAX, 1, 1, 1 /* by lines */ ); 492217309Snwhitehorn 493217309Snwhitehorn dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); 494217309Snwhitehorn 495251843Sbapt dlg_trace_win(dialog); 496217309Snwhitehorn while (result == DLG_EXIT_UNKNOWN) { 497217309Snwhitehorn if (button < 0) /* --visit-items */ 498251843Sbapt wmove(dialog, 499251843Sbapt all.box_y + ItemToRow(choice) + 1, 500251843Sbapt all.box_x + all.tag_x + 1); 501217309Snwhitehorn 502217309Snwhitehorn key = dlg_mouse_wgetch(dialog, &fkey); 503217309Snwhitehorn if (dlg_result_key(key, fkey, &result)) 504217309Snwhitehorn break; 505217309Snwhitehorn 506217309Snwhitehorn found = FALSE; 507217309Snwhitehorn if (fkey) { 508217309Snwhitehorn /* 509217309Snwhitehorn * Allow a mouse-click on a box to switch selection to that box. 510217309Snwhitehorn * Handling a button click is a little more complicated, since we 511217309Snwhitehorn * push a KEY_ENTER back onto the input stream so we'll put the 512217309Snwhitehorn * cursor at the right place before handling the "keypress". 513217309Snwhitehorn */ 514217309Snwhitehorn if (key >= DLGK_MOUSE(KEY_MAX)) { 515217309Snwhitehorn key -= DLGK_MOUSE(KEY_MAX); 516217309Snwhitehorn i = RowToItem(key); 517217309Snwhitehorn if (i < max_choice) { 518217309Snwhitehorn found = TRUE; 519217309Snwhitehorn } else { 520217309Snwhitehorn beep(); 521217309Snwhitehorn continue; 522217309Snwhitehorn } 523217309Snwhitehorn } else if (is_DLGK_MOUSE(key) 524217309Snwhitehorn && dlg_ok_buttoncode(key - M_EVENT) >= 0) { 525217309Snwhitehorn button = (key - M_EVENT); 526217309Snwhitehorn ungetch('\n'); 527217309Snwhitehorn continue; 528217309Snwhitehorn } 529217309Snwhitehorn } else { 530217309Snwhitehorn /* 531217309Snwhitehorn * Check if key pressed matches first character of any item tag in 532217309Snwhitehorn * list. If there is more than one match, we will cycle through 533217309Snwhitehorn * each one as the same key is pressed repeatedly. 534217309Snwhitehorn */ 535217309Snwhitehorn if (button < 0 || !dialog_state.visit_items) { 536217309Snwhitehorn for (j = scrollamt + choice + 1; j < item_no; j++) { 537251843Sbapt if (check_hotkey(items, j)) { 538217309Snwhitehorn found = TRUE; 539217309Snwhitehorn i = j - scrollamt; 540217309Snwhitehorn break; 541217309Snwhitehorn } 542217309Snwhitehorn } 543217309Snwhitehorn if (!found) { 544217309Snwhitehorn for (j = 0; j <= scrollamt + choice; j++) { 545251843Sbapt if (check_hotkey(items, j)) { 546217309Snwhitehorn found = TRUE; 547217309Snwhitehorn i = j - scrollamt; 548217309Snwhitehorn break; 549217309Snwhitehorn } 550217309Snwhitehorn } 551217309Snwhitehorn } 552217309Snwhitehorn if (found) 553217309Snwhitehorn dlg_flush_getc(); 554217309Snwhitehorn } else if ((j = dlg_char_to_button(key, buttons)) >= 0) { 555217309Snwhitehorn button = j; 556217309Snwhitehorn ungetch('\n'); 557217309Snwhitehorn continue; 558217309Snwhitehorn } 559217309Snwhitehorn 560217309Snwhitehorn /* 561217309Snwhitehorn * A single digit (1-9) positions the selection to that line in the 562217309Snwhitehorn * current screen. 563217309Snwhitehorn */ 564217309Snwhitehorn if (!found 565217309Snwhitehorn && (key <= '9') 566217309Snwhitehorn && (key > '0') 567217309Snwhitehorn && (key - '1' < max_choice)) { 568217309Snwhitehorn found = TRUE; 569217309Snwhitehorn i = key - '1'; 570217309Snwhitehorn } 571217309Snwhitehorn } 572217309Snwhitehorn 573217309Snwhitehorn if (!found && fkey) { 574217309Snwhitehorn found = TRUE; 575217309Snwhitehorn switch (key) { 576217309Snwhitehorn case DLGK_PAGE_FIRST: 577217309Snwhitehorn i = -scrollamt; 578217309Snwhitehorn break; 579217309Snwhitehorn case DLGK_PAGE_LAST: 580217309Snwhitehorn i = item_no - 1 - scrollamt; 581217309Snwhitehorn break; 582217309Snwhitehorn case DLGK_MOUSE(KEY_PPAGE): 583217309Snwhitehorn case DLGK_PAGE_PREV: 584217309Snwhitehorn if (choice) 585217309Snwhitehorn i = 0; 586217309Snwhitehorn else if (scrollamt != 0) 587217309Snwhitehorn i = -MIN(scrollamt, max_choice); 588217309Snwhitehorn else 589217309Snwhitehorn continue; 590217309Snwhitehorn break; 591217309Snwhitehorn case DLGK_MOUSE(KEY_NPAGE): 592217309Snwhitehorn case DLGK_PAGE_NEXT: 593217309Snwhitehorn i = MIN(choice + max_choice, item_no - scrollamt - 1); 594217309Snwhitehorn break; 595217309Snwhitehorn case DLGK_ITEM_PREV: 596217309Snwhitehorn i = choice - 1; 597217309Snwhitehorn if (choice == 0 && scrollamt == 0) 598217309Snwhitehorn continue; 599217309Snwhitehorn break; 600217309Snwhitehorn case DLGK_ITEM_NEXT: 601217309Snwhitehorn i = choice + 1; 602217309Snwhitehorn if (scrollamt + choice >= item_no - 1) 603217309Snwhitehorn continue; 604217309Snwhitehorn break; 605217309Snwhitehorn default: 606217309Snwhitehorn found = FALSE; 607217309Snwhitehorn break; 608217309Snwhitehorn } 609217309Snwhitehorn } 610217309Snwhitehorn 611217309Snwhitehorn if (found) { 612217309Snwhitehorn if (i != choice) { 613217309Snwhitehorn getyx(dialog, cur_y, cur_x); 614217309Snwhitehorn if (i < 0 || i >= max_choice) { 615251843Sbapt if (i < 0) { 616251843Sbapt scrollamt += i; 617251843Sbapt choice = 0; 618251843Sbapt } else { 619251843Sbapt choice = max_choice - 1; 620251843Sbapt scrollamt += (i - max_choice + 1); 621217309Snwhitehorn } 622251843Sbapt print_menu(&all, choice, scrollamt, max_choice, is_inputmenu); 623217309Snwhitehorn } else { 624217309Snwhitehorn choice = i; 625251843Sbapt print_menu(&all, choice, scrollamt, max_choice, is_inputmenu); 626217309Snwhitehorn (void) wmove(dialog, cur_y, cur_x); 627217309Snwhitehorn wrefresh(dialog); 628217309Snwhitehorn } 629217309Snwhitehorn } 630217309Snwhitehorn continue; /* wait for another key press */ 631217309Snwhitehorn } 632217309Snwhitehorn 633217309Snwhitehorn if (fkey) { 634217309Snwhitehorn switch (key) { 635217309Snwhitehorn case DLGK_FIELD_PREV: 636217309Snwhitehorn button = dlg_prev_button(buttons, button); 637217309Snwhitehorn dlg_draw_buttons(dialog, height - 2, 0, buttons, button, 638217309Snwhitehorn FALSE, width); 639217309Snwhitehorn break; 640217309Snwhitehorn case DLGK_FIELD_NEXT: 641217309Snwhitehorn button = dlg_next_button(buttons, button); 642217309Snwhitehorn dlg_draw_buttons(dialog, height - 2, 0, buttons, button, 643217309Snwhitehorn FALSE, width); 644217309Snwhitehorn break; 645217309Snwhitehorn case DLGK_ENTER: 646251843Sbapt if (is_inputmenu) 647251843Sbapt result = dlg_ok_buttoncode(button); 648251843Sbapt else 649251843Sbapt result = dlg_enter_buttoncode(button); 650217309Snwhitehorn 651217309Snwhitehorn /* 652217309Snwhitehorn * If dlg_menu() is called from dialog_menu(), we want to 653251843Sbapt * capture the results into dialog_vars.input_result. 654217309Snwhitehorn */ 655217309Snwhitehorn if (result == DLG_EXIT_ERROR) { 656217309Snwhitehorn result = DLG_EXIT_UNKNOWN; 657217309Snwhitehorn } else if (is_inputmenu 658217309Snwhitehorn || rename_menutext == dlg_dummy_menutext) { 659217309Snwhitehorn result = handle_button(result, 660217309Snwhitehorn items, 661217309Snwhitehorn scrollamt + choice); 662217309Snwhitehorn } 663217309Snwhitehorn 664217309Snwhitehorn /* 665217309Snwhitehorn * If we have a rename_menutext function, interpret the Extra 666217309Snwhitehorn * button as a request to rename the menu's text. If that 667217309Snwhitehorn * function doesn't return "Unknown", we will exit from this 668217309Snwhitehorn * function. Usually that is done for dialog_menu(), so the 669217309Snwhitehorn * shell script can use the updated value. If it does return 670217309Snwhitehorn * "Unknown", update the list item only. A direct caller of 671217309Snwhitehorn * dlg_menu() can free the renamed value - we cannot. 672217309Snwhitehorn */ 673217309Snwhitehorn if (is_inputmenu && result == DLG_EXIT_EXTRA) { 674217309Snwhitehorn char *tmp; 675217309Snwhitehorn 676251843Sbapt if (input_menu_edit(&all, 677217309Snwhitehorn &items[scrollamt + choice], 678217309Snwhitehorn choice, 679217309Snwhitehorn &tmp)) { 680217309Snwhitehorn result = rename_menutext(items, scrollamt + choice, tmp); 681217309Snwhitehorn if (result == DLG_EXIT_UNKNOWN) { 682217309Snwhitehorn items[scrollamt + choice].text = tmp; 683217309Snwhitehorn } else { 684217309Snwhitehorn free(tmp); 685217309Snwhitehorn } 686217309Snwhitehorn } else { 687217309Snwhitehorn result = DLG_EXIT_UNKNOWN; 688251843Sbapt print_item(&all, 689251843Sbapt menu, 690217309Snwhitehorn &items[scrollamt + choice], 691217309Snwhitehorn choice, 692217309Snwhitehorn Selected, 693217309Snwhitehorn is_inputmenu); 694217309Snwhitehorn (void) wnoutrefresh(menu); 695217309Snwhitehorn free(tmp); 696217309Snwhitehorn } 697217309Snwhitehorn 698217309Snwhitehorn if (result == DLG_EXIT_UNKNOWN) { 699217309Snwhitehorn dlg_draw_buttons(dialog, height - 2, 0, 700217309Snwhitehorn buttons, button, FALSE, width); 701217309Snwhitehorn } 702217309Snwhitehorn } 703217309Snwhitehorn break; 704217309Snwhitehorn#ifdef KEY_RESIZE 705217309Snwhitehorn case KEY_RESIZE: 706217309Snwhitehorn /* reset data */ 707217309Snwhitehorn height = old_height; 708217309Snwhitehorn width = old_width; 709217309Snwhitehorn /* repaint */ 710217309Snwhitehorn dlg_clear(); 711217309Snwhitehorn dlg_del_window(dialog); 712217309Snwhitehorn refresh(); 713217309Snwhitehorn dlg_mouse_free_regions(); 714217309Snwhitehorn goto retry; 715217309Snwhitehorn#endif 716217309Snwhitehorn default: 717217309Snwhitehorn flash(); 718217309Snwhitehorn break; 719217309Snwhitehorn } 720217309Snwhitehorn } 721217309Snwhitehorn } 722217309Snwhitehorn 723217309Snwhitehorn dlg_mouse_free_regions(); 724217309Snwhitehorn dlg_unregister_window(menu); 725217309Snwhitehorn dlg_del_window(dialog); 726217309Snwhitehorn free(prompt); 727217309Snwhitehorn 728217309Snwhitehorn *current_item = scrollamt + choice; 729217309Snwhitehorn return result; 730217309Snwhitehorn} 731217309Snwhitehorn 732217309Snwhitehorn/* 733217309Snwhitehorn * Display a menu for choosing among a number of options 734217309Snwhitehorn */ 735217309Snwhitehornint 736217309Snwhitehorndialog_menu(const char *title, 737217309Snwhitehorn const char *cprompt, 738217309Snwhitehorn int height, 739217309Snwhitehorn int width, 740217309Snwhitehorn int menu_height, 741217309Snwhitehorn int item_no, 742217309Snwhitehorn char **items) 743217309Snwhitehorn{ 744217309Snwhitehorn int result; 745217309Snwhitehorn int choice; 746251843Sbapt int i, j; 747217309Snwhitehorn DIALOG_LISTITEM *listitems; 748217309Snwhitehorn 749217309Snwhitehorn listitems = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1); 750217309Snwhitehorn assert_ptr(listitems, "dialog_menu"); 751217309Snwhitehorn 752251843Sbapt for (i = j = 0; i < item_no; ++i) { 753251843Sbapt listitems[i].name = items[j++]; 754251843Sbapt listitems[i].text = (dialog_vars.no_items 755251843Sbapt ? dlg_strempty() 756251843Sbapt : items[j++]); 757217309Snwhitehorn listitems[i].help = ((dialog_vars.item_help) 758251843Sbapt ? items[j++] 759217309Snwhitehorn : dlg_strempty()); 760217309Snwhitehorn } 761217309Snwhitehorn dlg_align_columns(&listitems[0].text, sizeof(DIALOG_LISTITEM), item_no); 762217309Snwhitehorn 763217309Snwhitehorn result = dlg_menu(title, 764217309Snwhitehorn cprompt, 765217309Snwhitehorn height, 766217309Snwhitehorn width, 767217309Snwhitehorn menu_height, 768217309Snwhitehorn item_no, 769217309Snwhitehorn listitems, 770217309Snwhitehorn &choice, 771251843Sbapt (dialog_vars.input_menu 772251843Sbapt ? dlg_renamed_menutext 773251843Sbapt : dlg_dummy_menutext)); 774217309Snwhitehorn 775217309Snwhitehorn dlg_free_columns(&listitems[0].text, sizeof(DIALOG_LISTITEM), item_no); 776217309Snwhitehorn free(listitems); 777217309Snwhitehorn return result; 778217309Snwhitehorn} 779