menubox.c revision 217309
1217309Snwhitehorn/* 2217309Snwhitehorn * $Id: menubox.c,v 1.118 2010/01/17 22:24:11 tom Exp $ 3217309Snwhitehorn * 4217309Snwhitehorn * menubox.c -- implements the menu box 5217309Snwhitehorn * 6217309Snwhitehorn * Copyright 2000-2009,2010 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 30217309Snwhitehornstatic int menu_width, tag_x, item_x; 31217309Snwhitehorn 32217309Snwhitehorntypedef enum { 33217309Snwhitehorn Unselected = 0, 34217309Snwhitehorn Selected, 35217309Snwhitehorn Editing 36217309Snwhitehorn} Mode; 37217309Snwhitehorn 38217309Snwhitehorn#define MIN_HIGH (1 + (5 * MARGIN)) 39217309Snwhitehorn 40217309Snwhitehorn#define INPUT_ROWS 3 /* rows per inputmenu entry */ 41217309Snwhitehorn 42217309Snwhitehorn#define LLEN(n) ((n) * MENUBOX_TAGS) 43217309Snwhitehorn#define ItemName(i) items[LLEN(i)] 44217309Snwhitehorn#define ItemText(i) items[LLEN(i) + 1] 45217309Snwhitehorn#define ItemHelp(i) items[LLEN(i) + 2] 46217309Snwhitehorn 47217309Snwhitehorn#define RowHeight(i) (is_inputmenu ? ((i) * INPUT_ROWS) : ((i) * 1)) 48217309Snwhitehorn#define ItemToRow(i) (is_inputmenu ? ((i) * INPUT_ROWS + 1) : (i)) 49217309Snwhitehorn#define RowToItem(i) (is_inputmenu ? ((i) / INPUT_ROWS + 0) : (i)) 50217309Snwhitehorn 51217309Snwhitehornstatic void 52217309Snwhitehornprint_arrows(WINDOW *win, 53217309Snwhitehorn int box_x, 54217309Snwhitehorn int box_y, 55217309Snwhitehorn int scrollamt, 56217309Snwhitehorn int max_choice, 57217309Snwhitehorn int item_no, 58217309Snwhitehorn int menu_height) 59217309Snwhitehorn{ 60217309Snwhitehorn dlg_draw_scrollbar(win, 61217309Snwhitehorn scrollamt, 62217309Snwhitehorn scrollamt, 63217309Snwhitehorn scrollamt + max_choice, 64217309Snwhitehorn item_no, 65217309Snwhitehorn box_x, 66217309Snwhitehorn box_x + menu_width, 67217309Snwhitehorn box_y, 68217309Snwhitehorn box_y + menu_height + 1, 69217309Snwhitehorn menubox_attr, 70217309Snwhitehorn menubox_border_attr); 71217309Snwhitehorn} 72217309Snwhitehorn 73217309Snwhitehorn/* 74217309Snwhitehorn * Print the tag of a menu-item 75217309Snwhitehorn */ 76217309Snwhitehornstatic void 77217309Snwhitehornprint_tag(WINDOW *win, 78217309Snwhitehorn DIALOG_LISTITEM * item, 79217309Snwhitehorn int choice, 80217309Snwhitehorn Mode selected, 81217309Snwhitehorn bool is_inputmenu) 82217309Snwhitehorn{ 83217309Snwhitehorn int my_x = item_x; 84217309Snwhitehorn int my_y = ItemToRow(choice); 85217309Snwhitehorn int tag_width = (my_x - tag_x - GUTTER); 86217309Snwhitehorn const int *cols; 87217309Snwhitehorn const int *indx; 88217309Snwhitehorn int limit; 89217309Snwhitehorn int prefix; 90217309Snwhitehorn 91217309Snwhitehorn cols = dlg_index_columns(item->name); 92217309Snwhitehorn indx = dlg_index_wchars(item->name); 93217309Snwhitehorn limit = dlg_count_wchars(item->name); 94217309Snwhitehorn prefix = (indx[1] - indx[0]); 95217309Snwhitehorn 96217309Snwhitehorn /* highlight first char of the tag to be special */ 97217309Snwhitehorn (void) wmove(win, my_y, tag_x); 98217309Snwhitehorn wattrset(win, selected ? tag_key_selected_attr : tag_key_attr); 99217309Snwhitehorn if (strlen(item->name) != 0) 100217309Snwhitehorn (void) waddnstr(win, item->name, prefix); 101217309Snwhitehorn /* print rest of the string */ 102217309Snwhitehorn wattrset(win, selected ? tag_selected_attr : tag_attr); 103217309Snwhitehorn if ((int) strlen(item->name) > prefix) { 104217309Snwhitehorn limit = dlg_limit_columns(item->name, tag_width, 1); 105217309Snwhitehorn if (limit > 0) 106217309Snwhitehorn (void) waddnstr(win, item->name + indx[1], indx[limit] - indx[1]); 107217309Snwhitehorn } 108217309Snwhitehorn} 109217309Snwhitehorn 110217309Snwhitehorn/* 111217309Snwhitehorn * Print menu item 112217309Snwhitehorn */ 113217309Snwhitehornstatic void 114217309Snwhitehornprint_item(WINDOW *win, 115217309Snwhitehorn DIALOG_LISTITEM * items, 116217309Snwhitehorn int choice, 117217309Snwhitehorn Mode selected, 118217309Snwhitehorn bool is_inputmenu) 119217309Snwhitehorn{ 120217309Snwhitehorn chtype save = getattrs(win); 121217309Snwhitehorn int n; 122217309Snwhitehorn int my_width = menu_width; 123217309Snwhitehorn int my_x = item_x; 124217309Snwhitehorn int my_y = ItemToRow(choice); 125217309Snwhitehorn chtype attr = A_NORMAL; 126217309Snwhitehorn chtype textchar; 127217309Snwhitehorn chtype bordchar; 128217309Snwhitehorn 129217309Snwhitehorn switch (selected) { 130217309Snwhitehorn default: 131217309Snwhitehorn case Unselected: 132217309Snwhitehorn textchar = item_attr; 133217309Snwhitehorn bordchar = item_attr; 134217309Snwhitehorn break; 135217309Snwhitehorn case Selected: 136217309Snwhitehorn textchar = item_selected_attr; 137217309Snwhitehorn bordchar = item_selected_attr; 138217309Snwhitehorn break; 139217309Snwhitehorn case Editing: 140217309Snwhitehorn textchar = inputbox_attr; 141217309Snwhitehorn bordchar = dialog_attr; 142217309Snwhitehorn break; 143217309Snwhitehorn } 144217309Snwhitehorn 145217309Snwhitehorn /* Clear 'residue' of last item and mark current current item */ 146217309Snwhitehorn if (is_inputmenu) { 147217309Snwhitehorn wattrset(win, (selected != Unselected) ? item_selected_attr : item_attr); 148217309Snwhitehorn for (n = my_y - 1; n < my_y + INPUT_ROWS - 1; n++) { 149217309Snwhitehorn wmove(win, n, 0); 150217309Snwhitehorn wprintw(win, "%*s", my_width, " "); 151217309Snwhitehorn } 152217309Snwhitehorn } else { 153217309Snwhitehorn wattrset(win, menubox_attr); 154217309Snwhitehorn wmove(win, my_y, 0); 155217309Snwhitehorn wprintw(win, "%*s", my_width, " "); 156217309Snwhitehorn } 157217309Snwhitehorn 158217309Snwhitehorn print_tag(win, items, choice, selected, is_inputmenu); 159217309Snwhitehorn 160217309Snwhitehorn /* Draw the input field box (only for inputmenu) */ 161217309Snwhitehorn (void) wmove(win, my_y, my_x); 162217309Snwhitehorn if (is_inputmenu) { 163217309Snwhitehorn my_width -= 1; 164217309Snwhitehorn dlg_draw_box(win, my_y - 1, my_x, INPUT_ROWS, my_width - my_x - tag_x, 165217309Snwhitehorn bordchar, 166217309Snwhitehorn bordchar); 167217309Snwhitehorn my_width -= 1; 168217309Snwhitehorn ++my_x; 169217309Snwhitehorn } 170217309Snwhitehorn 171217309Snwhitehorn /* print actual item */ 172217309Snwhitehorn wmove(win, my_y, my_x); 173217309Snwhitehorn wattrset(win, textchar); 174217309Snwhitehorn dlg_print_text(win, items->text, my_width - my_x, &attr); 175217309Snwhitehorn 176217309Snwhitehorn if (selected) { 177217309Snwhitehorn dlg_item_help(items->help); 178217309Snwhitehorn } 179217309Snwhitehorn wattrset(win, save); 180217309Snwhitehorn} 181217309Snwhitehorn 182217309Snwhitehorn/* 183217309Snwhitehorn * Allow the user to edit the text of a menu entry. 184217309Snwhitehorn */ 185217309Snwhitehornstatic int 186217309Snwhitehorninput_menu_edit(WINDOW *win, 187217309Snwhitehorn DIALOG_LISTITEM * items, 188217309Snwhitehorn int choice, 189217309Snwhitehorn char **resultp) 190217309Snwhitehorn{ 191217309Snwhitehorn chtype save = getattrs(win); 192217309Snwhitehorn char *result; 193217309Snwhitehorn int offset = 0; 194217309Snwhitehorn int key = 0, fkey = 0; 195217309Snwhitehorn int first = TRUE; 196217309Snwhitehorn /* see above */ 197217309Snwhitehorn bool is_inputmenu = TRUE; 198217309Snwhitehorn int y = ItemToRow(choice); 199217309Snwhitehorn int code = TRUE; 200217309Snwhitehorn int max_len = dlg_max_input(MAX((int) strlen(items->text) + 1, MAX_LEN)); 201217309Snwhitehorn 202217309Snwhitehorn result = dlg_malloc(char, (size_t) max_len); 203217309Snwhitehorn assert_ptr(result, "input_menu_edit"); 204217309Snwhitehorn 205217309Snwhitehorn /* original item is used to initialize the input string. */ 206217309Snwhitehorn result[0] = '\0'; 207217309Snwhitehorn strcpy(result, items->text); 208217309Snwhitehorn 209217309Snwhitehorn print_item(win, items, choice, Editing, TRUE); 210217309Snwhitehorn 211217309Snwhitehorn /* taken out of inputbox.c - but somewhat modified */ 212217309Snwhitehorn for (;;) { 213217309Snwhitehorn if (!first) 214217309Snwhitehorn key = dlg_mouse_wgetch(win, &fkey); 215217309Snwhitehorn if (dlg_edit_string(result, &offset, key, fkey, first)) { 216217309Snwhitehorn dlg_show_string(win, result, offset, inputbox_attr, 217217309Snwhitehorn y, item_x + 1, menu_width - item_x - 3, 218217309Snwhitehorn FALSE, first); 219217309Snwhitehorn first = FALSE; 220217309Snwhitehorn } else if (key == ESC || key == TAB) { 221217309Snwhitehorn code = FALSE; 222217309Snwhitehorn break; 223217309Snwhitehorn } else { 224217309Snwhitehorn break; 225217309Snwhitehorn } 226217309Snwhitehorn } 227217309Snwhitehorn print_item(win, items, choice, Selected, TRUE); 228217309Snwhitehorn wattrset(win, save); 229217309Snwhitehorn 230217309Snwhitehorn *resultp = result; 231217309Snwhitehorn return code; 232217309Snwhitehorn} 233217309Snwhitehorn 234217309Snwhitehornstatic int 235217309Snwhitehornhandle_button(int code, DIALOG_LISTITEM * items, int choice) 236217309Snwhitehorn{ 237217309Snwhitehorn switch (code) { 238217309Snwhitehorn case DLG_EXIT_OK: /* FALLTHRU */ 239217309Snwhitehorn case DLG_EXIT_EXTRA: 240217309Snwhitehorn dlg_add_string(items[choice].name); 241217309Snwhitehorn break; 242217309Snwhitehorn case DLG_EXIT_HELP: 243217309Snwhitehorn dlg_add_result("HELP "); 244217309Snwhitehorn if (USE_ITEM_HELP(items[choice].help)) { 245217309Snwhitehorn dlg_add_string(items[choice].help); 246217309Snwhitehorn code = DLG_EXIT_ITEM_HELP; 247217309Snwhitehorn } else { 248217309Snwhitehorn dlg_add_string(items[choice].name); 249217309Snwhitehorn } 250217309Snwhitehorn break; 251217309Snwhitehorn } 252217309Snwhitehorn return code; 253217309Snwhitehorn} 254217309Snwhitehorn 255217309Snwhitehornstatic int 256217309Snwhitehorndlg_renamed_menutext(DIALOG_LISTITEM * items, int current, char *newtext) 257217309Snwhitehorn{ 258217309Snwhitehorn if (dialog_vars.input_result) 259217309Snwhitehorn dialog_vars.input_result[0] = '\0'; 260217309Snwhitehorn dlg_add_result("RENAMED "); 261217309Snwhitehorn dlg_add_string(items[current].name); 262217309Snwhitehorn dlg_add_result(" "); 263217309Snwhitehorn dlg_add_string(newtext); 264217309Snwhitehorn return DLG_EXIT_EXTRA; 265217309Snwhitehorn} 266217309Snwhitehorn 267217309Snwhitehornstatic int 268217309Snwhitehorndlg_dummy_menutext(DIALOG_LISTITEM * items, int current, char *newtext) 269217309Snwhitehorn{ 270217309Snwhitehorn (void) items; 271217309Snwhitehorn (void) current; 272217309Snwhitehorn (void) newtext; 273217309Snwhitehorn return DLG_EXIT_ERROR; 274217309Snwhitehorn} 275217309Snwhitehorn 276217309Snwhitehorn/* 277217309Snwhitehorn * This is an alternate interface to 'menu' which allows the application 278217309Snwhitehorn * to read the list item states back directly without putting them in the 279217309Snwhitehorn * output buffer. 280217309Snwhitehorn */ 281217309Snwhitehornint 282217309Snwhitehorndlg_menu(const char *title, 283217309Snwhitehorn const char *cprompt, 284217309Snwhitehorn int height, 285217309Snwhitehorn int width, 286217309Snwhitehorn int menu_height, 287217309Snwhitehorn int item_no, 288217309Snwhitehorn DIALOG_LISTITEM * items, 289217309Snwhitehorn int *current_item, 290217309Snwhitehorn DIALOG_INPUTMENU rename_menutext) 291217309Snwhitehorn{ 292217309Snwhitehorn /* *INDENT-OFF* */ 293217309Snwhitehorn static DLG_KEYS_BINDING binding[] = { 294217309Snwhitehorn ENTERKEY_BINDINGS, 295217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_NEXT, ' ' ), 296217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ), 297217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), 298217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), 299217309Snwhitehorn DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ), 300217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_NEXT, '+' ), 301217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN ), 302217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_NEXT, CHR_NEXT ), 303217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_PREV, '-' ), 304217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ), 305217309Snwhitehorn DLG_KEYS_DATA( DLGK_ITEM_PREV, CHR_PREVIOUS ), 306217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_FIRST, KEY_HOME ), 307217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_END ), 308217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_LL ), 309217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_NEXT, KEY_NPAGE ), 310217309Snwhitehorn DLG_KEYS_DATA( DLGK_PAGE_PREV, KEY_PPAGE ), 311217309Snwhitehorn END_KEYS_BINDING 312217309Snwhitehorn }; 313217309Snwhitehorn static DLG_KEYS_BINDING binding2[] = { 314217309Snwhitehorn INPUTSTR_BINDINGS, 315217309Snwhitehorn ENTERKEY_BINDINGS, 316217309Snwhitehorn END_KEYS_BINDING 317217309Snwhitehorn }; 318217309Snwhitehorn /* *INDENT-ON* */ 319217309Snwhitehorn 320217309Snwhitehorn#ifdef KEY_RESIZE 321217309Snwhitehorn int old_height = height; 322217309Snwhitehorn int old_width = width; 323217309Snwhitehorn#endif 324217309Snwhitehorn int i, j, x, y, cur_x, cur_y, box_x, box_y; 325217309Snwhitehorn int key = 0, fkey; 326217309Snwhitehorn int button = dialog_state.visit_items ? -1 : dlg_defaultno_button(); 327217309Snwhitehorn int choice = dlg_default_listitem(items); 328217309Snwhitehorn int result = DLG_EXIT_UNKNOWN; 329217309Snwhitehorn int scrollamt = 0; 330217309Snwhitehorn int max_choice, min_width; 331217309Snwhitehorn int found; 332217309Snwhitehorn int use_height, use_width, name_width, text_width; 333217309Snwhitehorn WINDOW *dialog, *menu; 334217309Snwhitehorn char *prompt = dlg_strclone(cprompt); 335217309Snwhitehorn const char **buttons = dlg_ok_labels(); 336217309Snwhitehorn bool is_inputmenu = (rename_menutext == dlg_renamed_menutext); 337217309Snwhitehorn 338217309Snwhitehorn dlg_does_output(); 339217309Snwhitehorn dlg_tab_correct_str(prompt); 340217309Snwhitehorn 341217309Snwhitehorn#ifdef KEY_RESIZE 342217309Snwhitehorn retry: 343217309Snwhitehorn#endif 344217309Snwhitehorn 345217309Snwhitehorn use_height = menu_height; 346217309Snwhitehorn if (use_height == 0) { 347217309Snwhitehorn min_width = dlg_calc_list_width(item_no, items) + 10; 348217309Snwhitehorn /* calculate height without items (4) */ 349217309Snwhitehorn dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, MAX(26, min_width)); 350217309Snwhitehorn dlg_calc_listh(&height, &use_height, item_no); 351217309Snwhitehorn } else { 352217309Snwhitehorn dlg_auto_size(title, prompt, &height, &width, MIN_HIGH + use_height, 26); 353217309Snwhitehorn } 354217309Snwhitehorn dlg_button_layout(buttons, &width); 355217309Snwhitehorn dlg_print_size(height, width); 356217309Snwhitehorn dlg_ctl_size(height, width); 357217309Snwhitehorn 358217309Snwhitehorn x = dlg_box_x_ordinate(width); 359217309Snwhitehorn y = dlg_box_y_ordinate(height); 360217309Snwhitehorn 361217309Snwhitehorn dialog = dlg_new_window(height, width, y, x); 362217309Snwhitehorn dlg_register_window(dialog, "menubox", binding); 363217309Snwhitehorn dlg_register_buttons(dialog, "menubox", buttons); 364217309Snwhitehorn 365217309Snwhitehorn dlg_mouse_setbase(x, y); 366217309Snwhitehorn 367217309Snwhitehorn dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); 368217309Snwhitehorn dlg_draw_bottom_box(dialog); 369217309Snwhitehorn dlg_draw_title(dialog, title); 370217309Snwhitehorn 371217309Snwhitehorn wattrset(dialog, dialog_attr); 372217309Snwhitehorn dlg_print_autowrap(dialog, prompt, height, width); 373217309Snwhitehorn 374217309Snwhitehorn menu_width = width - 6; 375217309Snwhitehorn getyx(dialog, cur_y, cur_x); 376217309Snwhitehorn box_y = cur_y + 1; 377217309Snwhitehorn box_x = (width - menu_width) / 2 - 1; 378217309Snwhitehorn 379217309Snwhitehorn /* 380217309Snwhitehorn * After displaying the prompt, we know how much space we really have. 381217309Snwhitehorn * Limit the list to avoid overwriting the ok-button. 382217309Snwhitehorn */ 383217309Snwhitehorn if (use_height + MIN_HIGH > height - cur_y) 384217309Snwhitehorn use_height = height - MIN_HIGH - cur_y; 385217309Snwhitehorn if (use_height <= 0) 386217309Snwhitehorn use_height = 1; 387217309Snwhitehorn 388217309Snwhitehorn /* Find out maximal number of displayable items at once. */ 389217309Snwhitehorn max_choice = MIN(use_height, 390217309Snwhitehorn RowHeight(item_no)); 391217309Snwhitehorn if (is_inputmenu) 392217309Snwhitehorn max_choice /= INPUT_ROWS; 393217309Snwhitehorn 394217309Snwhitehorn /* create new window for the menu */ 395217309Snwhitehorn menu = dlg_sub_window(dialog, use_height, menu_width, 396217309Snwhitehorn y + box_y + 1, 397217309Snwhitehorn x + box_x + 1); 398217309Snwhitehorn dlg_register_window(menu, "menu", binding2); 399217309Snwhitehorn dlg_register_buttons(menu, "menu", buttons); 400217309Snwhitehorn 401217309Snwhitehorn /* draw a box around the menu items */ 402217309Snwhitehorn dlg_draw_box(dialog, box_y, box_x, use_height + 2, menu_width + 2, 403217309Snwhitehorn menubox_border_attr, menubox_attr); 404217309Snwhitehorn 405217309Snwhitehorn name_width = 0; 406217309Snwhitehorn text_width = 0; 407217309Snwhitehorn 408217309Snwhitehorn /* Find length of longest item to center menu * 409217309Snwhitehorn * only if --menu was given, using --inputmenu * 410217309Snwhitehorn * won't be centered. */ 411217309Snwhitehorn for (i = 0; i < item_no; i++) { 412217309Snwhitehorn name_width = MAX(name_width, dlg_count_columns(items[i].name)); 413217309Snwhitehorn text_width = MAX(text_width, dlg_count_columns(items[i].text)); 414217309Snwhitehorn } 415217309Snwhitehorn 416217309Snwhitehorn /* If the name+text is wider than the list is allowed, then truncate 417217309Snwhitehorn * one or both of them. If the name is no wider than 30% of the list, 418217309Snwhitehorn * leave it intact. 419217309Snwhitehorn * 420217309Snwhitehorn * FIXME: the gutter width and name/list ratio should be configurable. 421217309Snwhitehorn */ 422217309Snwhitehorn use_width = (menu_width - GUTTER); 423217309Snwhitehorn if (text_width + name_width > use_width) { 424217309Snwhitehorn int need = (int) (0.30 * use_width); 425217309Snwhitehorn if (name_width > need) { 426217309Snwhitehorn int want = (int) (use_width 427217309Snwhitehorn * ((double) name_width) 428217309Snwhitehorn / (text_width + name_width)); 429217309Snwhitehorn name_width = (want > need) ? want : need; 430217309Snwhitehorn } 431217309Snwhitehorn text_width = use_width - name_width; 432217309Snwhitehorn } 433217309Snwhitehorn 434217309Snwhitehorn tag_x = (is_inputmenu 435217309Snwhitehorn ? 0 436217309Snwhitehorn : (use_width - text_width - name_width) / 2); 437217309Snwhitehorn item_x = name_width + tag_x + GUTTER; 438217309Snwhitehorn 439217309Snwhitehorn if (choice - scrollamt >= max_choice) { 440217309Snwhitehorn scrollamt = choice - (max_choice - 1); 441217309Snwhitehorn choice = max_choice - 1; 442217309Snwhitehorn } 443217309Snwhitehorn 444217309Snwhitehorn /* Print the menu */ 445217309Snwhitehorn for (i = 0; i < max_choice; i++) { 446217309Snwhitehorn print_item(menu, 447217309Snwhitehorn &items[i + scrollamt], 448217309Snwhitehorn i, 449217309Snwhitehorn (i == choice) ? Selected : Unselected, 450217309Snwhitehorn is_inputmenu); 451217309Snwhitehorn } 452217309Snwhitehorn (void) wnoutrefresh(menu); 453217309Snwhitehorn 454217309Snwhitehorn /* register the new window, along with its borders */ 455217309Snwhitehorn dlg_mouse_mkbigregion(box_y + 1, box_x, use_height + 2, menu_width + 2, 456217309Snwhitehorn KEY_MAX, 1, 1, 1 /* by lines */ ); 457217309Snwhitehorn 458217309Snwhitehorn print_arrows(dialog, box_x, box_y, scrollamt, max_choice, item_no, use_height); 459217309Snwhitehorn 460217309Snwhitehorn dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); 461217309Snwhitehorn 462217309Snwhitehorn while (result == DLG_EXIT_UNKNOWN) { 463217309Snwhitehorn if (button < 0) /* --visit-items */ 464217309Snwhitehorn wmove(dialog, box_y + ItemToRow(choice) + 1, box_x + tag_x + 1); 465217309Snwhitehorn 466217309Snwhitehorn key = dlg_mouse_wgetch(dialog, &fkey); 467217309Snwhitehorn if (dlg_result_key(key, fkey, &result)) 468217309Snwhitehorn break; 469217309Snwhitehorn 470217309Snwhitehorn found = FALSE; 471217309Snwhitehorn if (fkey) { 472217309Snwhitehorn /* 473217309Snwhitehorn * Allow a mouse-click on a box to switch selection to that box. 474217309Snwhitehorn * Handling a button click is a little more complicated, since we 475217309Snwhitehorn * push a KEY_ENTER back onto the input stream so we'll put the 476217309Snwhitehorn * cursor at the right place before handling the "keypress". 477217309Snwhitehorn */ 478217309Snwhitehorn if (key >= DLGK_MOUSE(KEY_MAX)) { 479217309Snwhitehorn key -= DLGK_MOUSE(KEY_MAX); 480217309Snwhitehorn i = RowToItem(key); 481217309Snwhitehorn if (i < max_choice) { 482217309Snwhitehorn found = TRUE; 483217309Snwhitehorn } else { 484217309Snwhitehorn beep(); 485217309Snwhitehorn continue; 486217309Snwhitehorn } 487217309Snwhitehorn } else if (is_DLGK_MOUSE(key) 488217309Snwhitehorn && dlg_ok_buttoncode(key - M_EVENT) >= 0) { 489217309Snwhitehorn button = (key - M_EVENT); 490217309Snwhitehorn ungetch('\n'); 491217309Snwhitehorn continue; 492217309Snwhitehorn } 493217309Snwhitehorn } else { 494217309Snwhitehorn /* 495217309Snwhitehorn * Check if key pressed matches first character of any item tag in 496217309Snwhitehorn * list. If there is more than one match, we will cycle through 497217309Snwhitehorn * each one as the same key is pressed repeatedly. 498217309Snwhitehorn */ 499217309Snwhitehorn if (button < 0 || !dialog_state.visit_items) { 500217309Snwhitehorn for (j = scrollamt + choice + 1; j < item_no; j++) { 501217309Snwhitehorn if (dlg_match_char(dlg_last_getc(), items[j].name)) { 502217309Snwhitehorn found = TRUE; 503217309Snwhitehorn i = j - scrollamt; 504217309Snwhitehorn break; 505217309Snwhitehorn } 506217309Snwhitehorn } 507217309Snwhitehorn if (!found) { 508217309Snwhitehorn for (j = 0; j <= scrollamt + choice; j++) { 509217309Snwhitehorn if (dlg_match_char(dlg_last_getc(), items[j].name)) { 510217309Snwhitehorn found = TRUE; 511217309Snwhitehorn i = j - scrollamt; 512217309Snwhitehorn break; 513217309Snwhitehorn } 514217309Snwhitehorn } 515217309Snwhitehorn } 516217309Snwhitehorn if (found) 517217309Snwhitehorn dlg_flush_getc(); 518217309Snwhitehorn } else if ((j = dlg_char_to_button(key, buttons)) >= 0) { 519217309Snwhitehorn button = j; 520217309Snwhitehorn ungetch('\n'); 521217309Snwhitehorn continue; 522217309Snwhitehorn } 523217309Snwhitehorn 524217309Snwhitehorn /* 525217309Snwhitehorn * A single digit (1-9) positions the selection to that line in the 526217309Snwhitehorn * current screen. 527217309Snwhitehorn */ 528217309Snwhitehorn if (!found 529217309Snwhitehorn && (key <= '9') 530217309Snwhitehorn && (key > '0') 531217309Snwhitehorn && (key - '1' < max_choice)) { 532217309Snwhitehorn found = TRUE; 533217309Snwhitehorn i = key - '1'; 534217309Snwhitehorn } 535217309Snwhitehorn } 536217309Snwhitehorn 537217309Snwhitehorn if (!found && fkey) { 538217309Snwhitehorn found = TRUE; 539217309Snwhitehorn switch (key) { 540217309Snwhitehorn case DLGK_PAGE_FIRST: 541217309Snwhitehorn i = -scrollamt; 542217309Snwhitehorn break; 543217309Snwhitehorn case DLGK_PAGE_LAST: 544217309Snwhitehorn i = item_no - 1 - scrollamt; 545217309Snwhitehorn break; 546217309Snwhitehorn case DLGK_MOUSE(KEY_PPAGE): 547217309Snwhitehorn case DLGK_PAGE_PREV: 548217309Snwhitehorn if (choice) 549217309Snwhitehorn i = 0; 550217309Snwhitehorn else if (scrollamt != 0) 551217309Snwhitehorn i = -MIN(scrollamt, max_choice); 552217309Snwhitehorn else 553217309Snwhitehorn continue; 554217309Snwhitehorn break; 555217309Snwhitehorn case DLGK_MOUSE(KEY_NPAGE): 556217309Snwhitehorn case DLGK_PAGE_NEXT: 557217309Snwhitehorn i = MIN(choice + max_choice, item_no - scrollamt - 1); 558217309Snwhitehorn break; 559217309Snwhitehorn case DLGK_ITEM_PREV: 560217309Snwhitehorn i = choice - 1; 561217309Snwhitehorn if (choice == 0 && scrollamt == 0) 562217309Snwhitehorn continue; 563217309Snwhitehorn break; 564217309Snwhitehorn case DLGK_ITEM_NEXT: 565217309Snwhitehorn i = choice + 1; 566217309Snwhitehorn if (scrollamt + choice >= item_no - 1) 567217309Snwhitehorn continue; 568217309Snwhitehorn break; 569217309Snwhitehorn default: 570217309Snwhitehorn found = FALSE; 571217309Snwhitehorn break; 572217309Snwhitehorn } 573217309Snwhitehorn } 574217309Snwhitehorn 575217309Snwhitehorn if (found) { 576217309Snwhitehorn if (i != choice) { 577217309Snwhitehorn getyx(dialog, cur_y, cur_x); 578217309Snwhitehorn if (i < 0 || i >= max_choice) { 579217309Snwhitehorn#if defined(NCURSES_VERSION_MAJOR) && NCURSES_VERSION_MAJOR < 5 580217309Snwhitehorn /* 581217309Snwhitehorn * Using wscrl to assist ncurses scrolling is not needed 582217309Snwhitehorn * in version 5.x 583217309Snwhitehorn */ 584217309Snwhitehorn if (i == -1) { 585217309Snwhitehorn if (use_height > 1) { 586217309Snwhitehorn /* De-highlight current first item */ 587217309Snwhitehorn print_item(menu, 588217309Snwhitehorn &items[scrollamt], 589217309Snwhitehorn 0, Unselected, is_inputmenu); 590217309Snwhitehorn scrollok(menu, TRUE); 591217309Snwhitehorn wscrl(menu, -RowHeight(1)); 592217309Snwhitehorn scrollok(menu, FALSE); 593217309Snwhitehorn } 594217309Snwhitehorn scrollamt--; 595217309Snwhitehorn print_item(menu, 596217309Snwhitehorn &items[scrollamt], 597217309Snwhitehorn 0, Selected, is_inputmenu); 598217309Snwhitehorn } else if (i == max_choice) { 599217309Snwhitehorn if (use_height > 1) { 600217309Snwhitehorn /* De-highlight current last item before scrolling up */ 601217309Snwhitehorn print_item(menu, 602217309Snwhitehorn &items[scrollamt + max_choice - 1], 603217309Snwhitehorn max_choice - 1, 604217309Snwhitehorn Unselected, 605217309Snwhitehorn is_inputmenu); 606217309Snwhitehorn scrollok(menu, TRUE); 607217309Snwhitehorn wscrl(menu, RowHeight(1)); 608217309Snwhitehorn scrollok(menu, FALSE); 609217309Snwhitehorn } 610217309Snwhitehorn scrollamt++; 611217309Snwhitehorn print_item(menu, 612217309Snwhitehorn &items[scrollamt + max_choice - 1], 613217309Snwhitehorn max_choice - 1, TRUE, 614217309Snwhitehorn is_inputmenu); 615217309Snwhitehorn } else 616217309Snwhitehorn#endif 617217309Snwhitehorn { 618217309Snwhitehorn if (i < 0) { 619217309Snwhitehorn scrollamt += i; 620217309Snwhitehorn choice = 0; 621217309Snwhitehorn } else { 622217309Snwhitehorn choice = max_choice - 1; 623217309Snwhitehorn scrollamt += (i - max_choice + 1); 624217309Snwhitehorn } 625217309Snwhitehorn for (i = 0; i < max_choice; i++) { 626217309Snwhitehorn print_item(menu, 627217309Snwhitehorn &items[scrollamt + i], 628217309Snwhitehorn i, 629217309Snwhitehorn (i == choice) ? Selected : Unselected, 630217309Snwhitehorn is_inputmenu); 631217309Snwhitehorn } 632217309Snwhitehorn } 633217309Snwhitehorn /* Clean bottom lines */ 634217309Snwhitehorn if (is_inputmenu) { 635217309Snwhitehorn int spare_lines, x_count; 636217309Snwhitehorn spare_lines = use_height % INPUT_ROWS; 637217309Snwhitehorn wattrset(menu, menubox_attr); 638217309Snwhitehorn for (; spare_lines; spare_lines--) { 639217309Snwhitehorn wmove(menu, use_height - spare_lines, 0); 640217309Snwhitehorn for (x_count = 0; x_count < menu_width; 641217309Snwhitehorn x_count++) { 642217309Snwhitehorn waddch(menu, ' '); 643217309Snwhitehorn } 644217309Snwhitehorn } 645217309Snwhitehorn } 646217309Snwhitehorn (void) wnoutrefresh(menu); 647217309Snwhitehorn print_arrows(dialog, 648217309Snwhitehorn box_x, box_y, 649217309Snwhitehorn scrollamt, max_choice, item_no, use_height); 650217309Snwhitehorn } else { 651217309Snwhitehorn /* De-highlight current item */ 652217309Snwhitehorn print_item(menu, 653217309Snwhitehorn &items[scrollamt + choice], 654217309Snwhitehorn choice, 655217309Snwhitehorn Unselected, 656217309Snwhitehorn is_inputmenu); 657217309Snwhitehorn /* Highlight new item */ 658217309Snwhitehorn choice = i; 659217309Snwhitehorn print_item(menu, 660217309Snwhitehorn &items[scrollamt + choice], 661217309Snwhitehorn choice, 662217309Snwhitehorn Selected, 663217309Snwhitehorn is_inputmenu); 664217309Snwhitehorn (void) wnoutrefresh(menu); 665217309Snwhitehorn print_arrows(dialog, 666217309Snwhitehorn box_x, box_y, 667217309Snwhitehorn scrollamt, max_choice, item_no, use_height); 668217309Snwhitehorn (void) wmove(dialog, cur_y, cur_x); 669217309Snwhitehorn wrefresh(dialog); 670217309Snwhitehorn } 671217309Snwhitehorn } 672217309Snwhitehorn continue; /* wait for another key press */ 673217309Snwhitehorn } 674217309Snwhitehorn 675217309Snwhitehorn if (fkey) { 676217309Snwhitehorn switch (key) { 677217309Snwhitehorn case DLGK_FIELD_PREV: 678217309Snwhitehorn button = dlg_prev_button(buttons, button); 679217309Snwhitehorn dlg_draw_buttons(dialog, height - 2, 0, buttons, button, 680217309Snwhitehorn FALSE, width); 681217309Snwhitehorn break; 682217309Snwhitehorn case DLGK_FIELD_NEXT: 683217309Snwhitehorn button = dlg_next_button(buttons, button); 684217309Snwhitehorn dlg_draw_buttons(dialog, height - 2, 0, buttons, button, 685217309Snwhitehorn FALSE, width); 686217309Snwhitehorn break; 687217309Snwhitehorn case DLGK_ENTER: 688217309Snwhitehorn result = dlg_ok_buttoncode(button); 689217309Snwhitehorn 690217309Snwhitehorn /* 691217309Snwhitehorn * If dlg_menu() is called from dialog_menu(), we want to 692217309Snwhitehorn * capture the results into dialog_vars.input_result, but not 693217309Snwhitehorn * if dlg_menu() is called directly from an application. We 694217309Snwhitehorn * can check this by testing if rename_menutext is the function 695217309Snwhitehorn * pointer owned by dialog_menu(). It would be nicer to have 696217309Snwhitehorn * this logic inside dialog_menu(), but that cannot be done 697217309Snwhitehorn * since we would lose compatibility for the results reported 698217309Snwhitehorn * after input_menu_edit(). 699217309Snwhitehorn */ 700217309Snwhitehorn if (result == DLG_EXIT_ERROR) { 701217309Snwhitehorn result = DLG_EXIT_UNKNOWN; 702217309Snwhitehorn } else if (is_inputmenu 703217309Snwhitehorn || rename_menutext == dlg_dummy_menutext) { 704217309Snwhitehorn result = handle_button(result, 705217309Snwhitehorn items, 706217309Snwhitehorn scrollamt + choice); 707217309Snwhitehorn } 708217309Snwhitehorn 709217309Snwhitehorn /* 710217309Snwhitehorn * If we have a rename_menutext function, interpret the Extra 711217309Snwhitehorn * button as a request to rename the menu's text. If that 712217309Snwhitehorn * function doesn't return "Unknown", we will exit from this 713217309Snwhitehorn * function. Usually that is done for dialog_menu(), so the 714217309Snwhitehorn * shell script can use the updated value. If it does return 715217309Snwhitehorn * "Unknown", update the list item only. A direct caller of 716217309Snwhitehorn * dlg_menu() can free the renamed value - we cannot. 717217309Snwhitehorn */ 718217309Snwhitehorn if (is_inputmenu && result == DLG_EXIT_EXTRA) { 719217309Snwhitehorn char *tmp; 720217309Snwhitehorn 721217309Snwhitehorn if (input_menu_edit(menu, 722217309Snwhitehorn &items[scrollamt + choice], 723217309Snwhitehorn choice, 724217309Snwhitehorn &tmp)) { 725217309Snwhitehorn result = rename_menutext(items, scrollamt + choice, tmp); 726217309Snwhitehorn if (result == DLG_EXIT_UNKNOWN) { 727217309Snwhitehorn items[scrollamt + choice].text = tmp; 728217309Snwhitehorn } else { 729217309Snwhitehorn free(tmp); 730217309Snwhitehorn } 731217309Snwhitehorn } else { 732217309Snwhitehorn result = DLG_EXIT_UNKNOWN; 733217309Snwhitehorn print_item(menu, 734217309Snwhitehorn &items[scrollamt + choice], 735217309Snwhitehorn choice, 736217309Snwhitehorn Selected, 737217309Snwhitehorn is_inputmenu); 738217309Snwhitehorn (void) wnoutrefresh(menu); 739217309Snwhitehorn free(tmp); 740217309Snwhitehorn } 741217309Snwhitehorn 742217309Snwhitehorn if (result == DLG_EXIT_UNKNOWN) { 743217309Snwhitehorn dlg_draw_buttons(dialog, height - 2, 0, 744217309Snwhitehorn buttons, button, FALSE, width); 745217309Snwhitehorn } 746217309Snwhitehorn } 747217309Snwhitehorn break; 748217309Snwhitehorn#ifdef KEY_RESIZE 749217309Snwhitehorn case KEY_RESIZE: 750217309Snwhitehorn /* reset data */ 751217309Snwhitehorn height = old_height; 752217309Snwhitehorn width = old_width; 753217309Snwhitehorn /* repaint */ 754217309Snwhitehorn dlg_clear(); 755217309Snwhitehorn dlg_del_window(dialog); 756217309Snwhitehorn refresh(); 757217309Snwhitehorn dlg_mouse_free_regions(); 758217309Snwhitehorn goto retry; 759217309Snwhitehorn#endif 760217309Snwhitehorn default: 761217309Snwhitehorn flash(); 762217309Snwhitehorn break; 763217309Snwhitehorn } 764217309Snwhitehorn } 765217309Snwhitehorn } 766217309Snwhitehorn 767217309Snwhitehorn dlg_mouse_free_regions(); 768217309Snwhitehorn dlg_unregister_window(menu); 769217309Snwhitehorn dlg_del_window(dialog); 770217309Snwhitehorn free(prompt); 771217309Snwhitehorn 772217309Snwhitehorn *current_item = scrollamt + choice; 773217309Snwhitehorn return result; 774217309Snwhitehorn} 775217309Snwhitehorn 776217309Snwhitehorn/* 777217309Snwhitehorn * Display a menu for choosing among a number of options 778217309Snwhitehorn */ 779217309Snwhitehornint 780217309Snwhitehorndialog_menu(const char *title, 781217309Snwhitehorn const char *cprompt, 782217309Snwhitehorn int height, 783217309Snwhitehorn int width, 784217309Snwhitehorn int menu_height, 785217309Snwhitehorn int item_no, 786217309Snwhitehorn char **items) 787217309Snwhitehorn{ 788217309Snwhitehorn int result; 789217309Snwhitehorn int choice; 790217309Snwhitehorn int i; 791217309Snwhitehorn DIALOG_LISTITEM *listitems; 792217309Snwhitehorn 793217309Snwhitehorn listitems = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1); 794217309Snwhitehorn assert_ptr(listitems, "dialog_menu"); 795217309Snwhitehorn 796217309Snwhitehorn for (i = 0; i < item_no; ++i) { 797217309Snwhitehorn listitems[i].name = ItemName(i); 798217309Snwhitehorn listitems[i].text = ItemText(i); 799217309Snwhitehorn listitems[i].help = ((dialog_vars.item_help) 800217309Snwhitehorn ? ItemHelp(i) 801217309Snwhitehorn : dlg_strempty()); 802217309Snwhitehorn } 803217309Snwhitehorn dlg_align_columns(&listitems[0].text, sizeof(DIALOG_LISTITEM), item_no); 804217309Snwhitehorn 805217309Snwhitehorn result = dlg_menu(title, 806217309Snwhitehorn cprompt, 807217309Snwhitehorn height, 808217309Snwhitehorn width, 809217309Snwhitehorn menu_height, 810217309Snwhitehorn item_no, 811217309Snwhitehorn listitems, 812217309Snwhitehorn &choice, 813217309Snwhitehorn dialog_vars.input_menu ? dlg_renamed_menutext : dlg_dummy_menutext); 814217309Snwhitehorn 815217309Snwhitehorn dlg_free_columns(&listitems[0].text, sizeof(DIALOG_LISTITEM), item_no); 816217309Snwhitehorn free(listitems); 817217309Snwhitehorn return result; 818217309Snwhitehorn} 819