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