checklist.c revision 220749
1217309Snwhitehorn/*
2220749Snwhitehorn *  $Id: checklist.c,v 1.124 2011/01/19 00:27:53 tom Exp $
3217309Snwhitehorn *
4217309Snwhitehorn *  checklist.c -- implements the checklist box
5217309Snwhitehorn *
6220749Snwhitehorn *  Copyright 2000-2010,2011	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 License, version 2.1
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 *	Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
26217309Snwhitehorn *	Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
27217309Snwhitehorn */
28217309Snwhitehorn
29217309Snwhitehorn#include <dialog.h>
30217309Snwhitehorn#include <dlg_keys.h>
31217309Snwhitehorn
32217309Snwhitehornstatic int list_width, check_x, item_x, checkflag;
33217309Snwhitehorn
34217309Snwhitehorn#define MIN_HIGH  (1 + (5 * MARGIN))
35217309Snwhitehorn
36217309Snwhitehorn#define LLEN(n) ((n) * CHECKBOX_TAGS)
37217309Snwhitehorn#define ItemData(i)    &items[LLEN(i)]
38217309Snwhitehorn#define ItemName(i)    items[LLEN(i)]
39217309Snwhitehorn#define ItemText(i)    items[LLEN(i) + 1]
40217309Snwhitehorn#define ItemStatus(i)  items[LLEN(i) + 2]
41217309Snwhitehorn#define ItemHelp(i)    items[LLEN(i) + 3]
42217309Snwhitehorn
43217309Snwhitehornstatic void
44217309Snwhitehornprint_arrows(WINDOW *win,
45217309Snwhitehorn	     int box_x,
46217309Snwhitehorn	     int box_y,
47217309Snwhitehorn	     int scrollamt,
48217309Snwhitehorn	     int choice,
49217309Snwhitehorn	     int item_no,
50217309Snwhitehorn	     int list_height)
51217309Snwhitehorn{
52217309Snwhitehorn    dlg_draw_scrollbar(win,
53220749Snwhitehorn		       (long) (scrollamt),
54220749Snwhitehorn		       (long) (scrollamt),
55220749Snwhitehorn		       (long) (scrollamt + choice),
56220749Snwhitehorn		       (long) (item_no),
57217309Snwhitehorn		       box_x + check_x,
58217309Snwhitehorn		       box_x + list_width,
59217309Snwhitehorn		       box_y,
60217309Snwhitehorn		       box_y + list_height + 1,
61217309Snwhitehorn		       menubox_attr,
62217309Snwhitehorn		       menubox_border_attr);
63217309Snwhitehorn}
64217309Snwhitehorn
65217309Snwhitehorn/*
66217309Snwhitehorn * Print list item.  The 'selected' parameter is true if 'choice' is the
67217309Snwhitehorn * current item.  That one is colored differently from the other items.
68217309Snwhitehorn */
69217309Snwhitehornstatic void
70217309Snwhitehornprint_item(WINDOW *win,
71217309Snwhitehorn	   DIALOG_LISTITEM * item,
72217309Snwhitehorn	   const char *states,
73217309Snwhitehorn	   int choice,
74217309Snwhitehorn	   int selected)
75217309Snwhitehorn{
76220749Snwhitehorn    chtype save = dlg_get_attrs(win);
77217309Snwhitehorn    int i;
78217309Snwhitehorn    chtype attr = A_NORMAL;
79217309Snwhitehorn    const int *cols;
80217309Snwhitehorn    const int *indx;
81217309Snwhitehorn    int limit;
82217309Snwhitehorn
83217309Snwhitehorn    /* Clear 'residue' of last item */
84217309Snwhitehorn    wattrset(win, menubox_attr);
85217309Snwhitehorn    (void) wmove(win, choice, 0);
86217309Snwhitehorn    for (i = 0; i < list_width; i++)
87217309Snwhitehorn	(void) waddch(win, ' ');
88217309Snwhitehorn
89217309Snwhitehorn    (void) wmove(win, choice, check_x);
90217309Snwhitehorn    wattrset(win, selected ? check_selected_attr : check_attr);
91217309Snwhitehorn    (void) wprintw(win,
92217309Snwhitehorn		   (checkflag == FLAG_CHECK) ? "[%c]" : "(%c)",
93217309Snwhitehorn		   states[item->state]);
94217309Snwhitehorn    wattrset(win, menubox_attr);
95217309Snwhitehorn    (void) waddch(win, ' ');
96217309Snwhitehorn
97217309Snwhitehorn    if (strlen(item->name) != 0) {
98217309Snwhitehorn
99217309Snwhitehorn	indx = dlg_index_wchars(item->name);
100217309Snwhitehorn
101217309Snwhitehorn	wattrset(win, selected ? tag_key_selected_attr : tag_key_attr);
102217309Snwhitehorn	(void) waddnstr(win, item->name, indx[1]);
103217309Snwhitehorn
104217309Snwhitehorn	if ((int) strlen(item->name) > indx[1]) {
105217309Snwhitehorn	    limit = dlg_limit_columns(item->name, (item_x - check_x - 6), 1);
106217309Snwhitehorn	    if (limit > 1) {
107217309Snwhitehorn		wattrset(win, selected ? tag_selected_attr : tag_attr);
108217309Snwhitehorn		(void) waddnstr(win,
109217309Snwhitehorn				item->name + indx[1],
110217309Snwhitehorn				indx[limit] - indx[1]);
111217309Snwhitehorn	    }
112217309Snwhitehorn	}
113217309Snwhitehorn    }
114217309Snwhitehorn
115217309Snwhitehorn    if (strlen(item->text) != 0) {
116217309Snwhitehorn	cols = dlg_index_columns(item->text);
117217309Snwhitehorn	limit = dlg_limit_columns(item->text, (getmaxx(win) - item_x + 1), 0);
118217309Snwhitehorn
119217309Snwhitehorn	if (limit > 0) {
120217309Snwhitehorn	    (void) wmove(win, choice, item_x);
121217309Snwhitehorn	    wattrset(win, selected ? item_selected_attr : item_attr);
122217309Snwhitehorn	    dlg_print_text(win, item->text, cols[limit], &attr);
123217309Snwhitehorn	}
124217309Snwhitehorn    }
125217309Snwhitehorn
126217309Snwhitehorn    if (selected) {
127217309Snwhitehorn	dlg_item_help(item->help);
128217309Snwhitehorn    }
129217309Snwhitehorn    wattrset(win, save);
130217309Snwhitehorn}
131217309Snwhitehorn
132217309Snwhitehorn/*
133217309Snwhitehorn * This is an alternate interface to 'checklist' which allows the application
134217309Snwhitehorn * to read the list item states back directly without putting them in the
135217309Snwhitehorn * output buffer.  It also provides for more than two states over which the
136217309Snwhitehorn * check/radio box can display.
137217309Snwhitehorn */
138217309Snwhitehornint
139217309Snwhitehorndlg_checklist(const char *title,
140217309Snwhitehorn	      const char *cprompt,
141217309Snwhitehorn	      int height,
142217309Snwhitehorn	      int width,
143217309Snwhitehorn	      int list_height,
144217309Snwhitehorn	      int item_no,
145217309Snwhitehorn	      DIALOG_LISTITEM * items,
146217309Snwhitehorn	      const char *states,
147217309Snwhitehorn	      int flag,
148217309Snwhitehorn	      int *current_item)
149217309Snwhitehorn{
150217309Snwhitehorn    /* *INDENT-OFF* */
151217309Snwhitehorn    static DLG_KEYS_BINDING binding[] = {
152217309Snwhitehorn	ENTERKEY_BINDINGS,
153217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
154217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
155217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
156217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
157217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_FIRST, KEY_HOME ),
158217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_END ),
159217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_LL ),
160217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	'+' ),
161217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	KEY_DOWN ),
162217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  CHR_NEXT ),
163217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,	'-' ),
164217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,	KEY_UP ),
165217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,  CHR_PREVIOUS ),
166217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ),
167217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE) ),
168217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ),
169217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE) ),
170217309Snwhitehorn	END_KEYS_BINDING
171217309Snwhitehorn    };
172217309Snwhitehorn    /* *INDENT-ON* */
173217309Snwhitehorn
174217309Snwhitehorn#ifdef KEY_RESIZE
175217309Snwhitehorn    int old_height = height;
176217309Snwhitehorn    int old_width = width;
177217309Snwhitehorn#endif
178217309Snwhitehorn    int i, j, key2, found, x, y, cur_x, cur_y, box_x, box_y;
179217309Snwhitehorn    int key = 0, fkey;
180217309Snwhitehorn    int button = dialog_state.visit_items ? -1 : dlg_defaultno_button();
181217309Snwhitehorn    int choice = dlg_default_listitem(items);
182217309Snwhitehorn    int scrollamt = 0;
183217309Snwhitehorn    int max_choice;
184217309Snwhitehorn    int was_mouse;
185217309Snwhitehorn    int use_height;
186217309Snwhitehorn    int use_width, name_width, text_width;
187217309Snwhitehorn    int result = DLG_EXIT_UNKNOWN;
188217309Snwhitehorn    int num_states;
189217309Snwhitehorn    WINDOW *dialog, *list;
190217309Snwhitehorn    char *prompt = dlg_strclone(cprompt);
191217309Snwhitehorn    const char **buttons = dlg_ok_labels();
192217309Snwhitehorn
193217309Snwhitehorn    dlg_does_output();
194217309Snwhitehorn    dlg_tab_correct_str(prompt);
195217309Snwhitehorn
196217309Snwhitehorn#ifdef KEY_RESIZE
197217309Snwhitehorn  retry:
198217309Snwhitehorn#endif
199217309Snwhitehorn
200217309Snwhitehorn    use_height = list_height;
201217309Snwhitehorn    if (use_height == 0) {
202217309Snwhitehorn	use_width = dlg_calc_list_width(item_no, items) + 10;
203217309Snwhitehorn	/* calculate height without items (4) */
204217309Snwhitehorn	dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, MAX(26, use_width));
205217309Snwhitehorn	dlg_calc_listh(&height, &use_height, item_no);
206217309Snwhitehorn    } else {
207217309Snwhitehorn	dlg_auto_size(title, prompt, &height, &width, MIN_HIGH + use_height, 26);
208217309Snwhitehorn    }
209217309Snwhitehorn    dlg_button_layout(buttons, &width);
210217309Snwhitehorn    dlg_print_size(height, width);
211217309Snwhitehorn    dlg_ctl_size(height, width);
212217309Snwhitehorn
213217309Snwhitehorn    /* we need at least two states */
214217309Snwhitehorn    if (states == 0 || strlen(states) < 2)
215217309Snwhitehorn	states = " *";
216217309Snwhitehorn    num_states = (int) strlen(states);
217217309Snwhitehorn
218217309Snwhitehorn    checkflag = flag;
219217309Snwhitehorn
220217309Snwhitehorn    x = dlg_box_x_ordinate(width);
221217309Snwhitehorn    y = dlg_box_y_ordinate(height);
222217309Snwhitehorn
223217309Snwhitehorn    dialog = dlg_new_window(height, width, y, x);
224217309Snwhitehorn    dlg_register_window(dialog, "checklist", binding);
225217309Snwhitehorn    dlg_register_buttons(dialog, "checklist", buttons);
226217309Snwhitehorn
227217309Snwhitehorn    dlg_mouse_setbase(x, y);
228217309Snwhitehorn
229217309Snwhitehorn    dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
230217309Snwhitehorn    dlg_draw_bottom_box(dialog);
231217309Snwhitehorn    dlg_draw_title(dialog, title);
232217309Snwhitehorn
233217309Snwhitehorn    wattrset(dialog, dialog_attr);
234217309Snwhitehorn    dlg_print_autowrap(dialog, prompt, height, width);
235217309Snwhitehorn
236217309Snwhitehorn    list_width = width - 6;
237217309Snwhitehorn    getyx(dialog, cur_y, cur_x);
238217309Snwhitehorn    box_y = cur_y + 1;
239217309Snwhitehorn    box_x = (width - list_width) / 2 - 1;
240217309Snwhitehorn
241217309Snwhitehorn    /*
242217309Snwhitehorn     * After displaying the prompt, we know how much space we really have.
243217309Snwhitehorn     * Limit the list to avoid overwriting the ok-button.
244217309Snwhitehorn     */
245217309Snwhitehorn    if (use_height + MIN_HIGH > height - cur_y)
246217309Snwhitehorn	use_height = height - MIN_HIGH - cur_y;
247217309Snwhitehorn    if (use_height <= 0)
248217309Snwhitehorn	use_height = 1;
249217309Snwhitehorn
250217309Snwhitehorn    max_choice = MIN(use_height, item_no);
251217309Snwhitehorn
252217309Snwhitehorn    /* create new window for the list */
253217309Snwhitehorn    list = dlg_sub_window(dialog, use_height, list_width,
254217309Snwhitehorn			  y + box_y + 1, x + box_x + 1);
255217309Snwhitehorn
256217309Snwhitehorn    /* draw a box around the list items */
257217309Snwhitehorn    dlg_draw_box(dialog, box_y, box_x,
258217309Snwhitehorn		 use_height + 2 * MARGIN,
259217309Snwhitehorn		 list_width + 2 * MARGIN,
260217309Snwhitehorn		 menubox_border_attr, menubox_attr);
261217309Snwhitehorn
262217309Snwhitehorn    text_width = 0;
263217309Snwhitehorn    name_width = 0;
264217309Snwhitehorn    /* Find length of longest item to center checklist */
265217309Snwhitehorn    for (i = 0; i < item_no; i++) {
266217309Snwhitehorn	text_width = MAX(text_width, dlg_count_columns(items[i].text));
267217309Snwhitehorn	name_width = MAX(name_width, dlg_count_columns(items[i].name));
268217309Snwhitehorn    }
269217309Snwhitehorn
270217309Snwhitehorn    /* If the name+text is wider than the list is allowed, then truncate
271217309Snwhitehorn     * one or both of them.  If the name is no wider than 1/4 of the list,
272217309Snwhitehorn     * leave it intact.
273217309Snwhitehorn     */
274217309Snwhitehorn    use_width = (list_width - 6);
275217309Snwhitehorn    if (text_width + name_width > use_width) {
276217309Snwhitehorn	int need = (int) (0.25 * use_width);
277217309Snwhitehorn	if (name_width > need) {
278217309Snwhitehorn	    int want = (int) (use_width * ((double) name_width) /
279217309Snwhitehorn			      (text_width + name_width));
280217309Snwhitehorn	    name_width = (want > need) ? want : need;
281217309Snwhitehorn	}
282217309Snwhitehorn	text_width = use_width - name_width;
283217309Snwhitehorn    }
284217309Snwhitehorn
285217309Snwhitehorn    check_x = (use_width - (text_width + name_width)) / 2;
286217309Snwhitehorn    item_x = name_width + check_x + 6;
287217309Snwhitehorn
288217309Snwhitehorn    /* ensure we are scrolled to show the current choice */
289217309Snwhitehorn    if (choice >= (max_choice + scrollamt)) {
290217309Snwhitehorn	scrollamt = choice - max_choice + 1;
291217309Snwhitehorn	choice = max_choice - 1;
292217309Snwhitehorn    }
293217309Snwhitehorn    /* Print the list */
294217309Snwhitehorn    for (i = 0; i < max_choice; i++)
295217309Snwhitehorn	print_item(list,
296217309Snwhitehorn		   &items[i + scrollamt],
297217309Snwhitehorn		   states,
298217309Snwhitehorn		   i, i == choice);
299217309Snwhitehorn    (void) wnoutrefresh(list);
300217309Snwhitehorn
301217309Snwhitehorn    /* register the new window, along with its borders */
302217309Snwhitehorn    dlg_mouse_mkbigregion(box_y + 1, box_x, use_height, list_width + 2,
303217309Snwhitehorn			  KEY_MAX, 1, 1, 1 /* by lines */ );
304217309Snwhitehorn
305217309Snwhitehorn    print_arrows(dialog,
306217309Snwhitehorn		 box_x, box_y,
307217309Snwhitehorn		 scrollamt, max_choice, item_no, use_height);
308217309Snwhitehorn
309217309Snwhitehorn    dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
310217309Snwhitehorn
311217309Snwhitehorn    while (result == DLG_EXIT_UNKNOWN) {
312217309Snwhitehorn	if (button < 0)		/* --visit-items */
313217309Snwhitehorn	    wmove(dialog, box_y + choice + 1, box_x + check_x + 2);
314217309Snwhitehorn
315217309Snwhitehorn	key = dlg_mouse_wgetch(dialog, &fkey);
316217309Snwhitehorn	if (dlg_result_key(key, fkey, &result))
317217309Snwhitehorn	    break;
318217309Snwhitehorn
319217309Snwhitehorn	was_mouse = (fkey && is_DLGK_MOUSE(key));
320217309Snwhitehorn	if (was_mouse)
321217309Snwhitehorn	    key -= M_EVENT;
322217309Snwhitehorn
323217309Snwhitehorn	if (was_mouse && (key >= KEY_MAX)) {
324217309Snwhitehorn	    getyx(dialog, cur_y, cur_x);
325217309Snwhitehorn	    i = (key - KEY_MAX);
326217309Snwhitehorn	    if (i < max_choice) {
327217309Snwhitehorn		/* De-highlight current item */
328217309Snwhitehorn		print_item(list,
329217309Snwhitehorn			   &items[scrollamt + choice],
330217309Snwhitehorn			   states,
331217309Snwhitehorn			   choice, FALSE);
332217309Snwhitehorn		/* Highlight new item */
333217309Snwhitehorn		choice = (key - KEY_MAX);
334217309Snwhitehorn		print_item(list,
335217309Snwhitehorn			   &items[scrollamt + choice],
336217309Snwhitehorn			   states,
337217309Snwhitehorn			   choice, TRUE);
338217309Snwhitehorn		(void) wnoutrefresh(list);
339217309Snwhitehorn		(void) wmove(dialog, cur_y, cur_x);
340217309Snwhitehorn
341217309Snwhitehorn		key = ' ';	/* force the selected item to toggle */
342217309Snwhitehorn	    } else {
343217309Snwhitehorn		beep();
344217309Snwhitehorn		continue;
345217309Snwhitehorn	    }
346217309Snwhitehorn	    fkey = FALSE;
347217309Snwhitehorn	} else if (was_mouse && key >= KEY_MIN) {
348217309Snwhitehorn	    key = dlg_lookup_key(dialog, key, &fkey);
349217309Snwhitehorn	}
350217309Snwhitehorn
351217309Snwhitehorn	/*
352217309Snwhitehorn	 * A space toggles the item status.  We handle either a checklist
353217309Snwhitehorn	 * (any number of items can be selected) or radio list (zero or one
354217309Snwhitehorn	 * items can be selected).
355217309Snwhitehorn	 */
356217309Snwhitehorn	if (key == ' ') {
357217309Snwhitehorn	    int current = scrollamt + choice;
358217309Snwhitehorn	    int next = items[current].state + 1;
359217309Snwhitehorn
360217309Snwhitehorn	    if (next >= num_states)
361217309Snwhitehorn		next = 0;
362217309Snwhitehorn
363217309Snwhitehorn	    getyx(dialog, cur_y, cur_x);
364217309Snwhitehorn	    if (flag == FLAG_CHECK) {	/* checklist? */
365217309Snwhitehorn		items[current].state = next;
366217309Snwhitehorn		print_item(list,
367217309Snwhitehorn			   &items[scrollamt + choice],
368217309Snwhitehorn			   states,
369217309Snwhitehorn			   choice, TRUE);
370217309Snwhitehorn	    } else {		/* radiolist */
371217309Snwhitehorn		for (i = 0; i < item_no; i++) {
372217309Snwhitehorn		    if (i != current) {
373217309Snwhitehorn			items[i].state = 0;
374217309Snwhitehorn		    }
375217309Snwhitehorn		}
376217309Snwhitehorn		if (items[current].state) {
377217309Snwhitehorn		    items[current].state = next ? next : 1;
378217309Snwhitehorn		    print_item(list,
379217309Snwhitehorn			       &items[current],
380217309Snwhitehorn			       states,
381217309Snwhitehorn			       choice, TRUE);
382217309Snwhitehorn		} else {
383217309Snwhitehorn		    items[current].state = 1;
384217309Snwhitehorn		    for (i = 0; i < max_choice; i++)
385217309Snwhitehorn			print_item(list,
386217309Snwhitehorn				   &items[scrollamt + i],
387217309Snwhitehorn				   states,
388217309Snwhitehorn				   i, i == choice);
389217309Snwhitehorn		}
390217309Snwhitehorn	    }
391217309Snwhitehorn	    (void) wnoutrefresh(list);
392217309Snwhitehorn	    (void) wmove(dialog, cur_y, cur_x);
393217309Snwhitehorn	    wrefresh(dialog);
394217309Snwhitehorn	    continue;		/* wait for another key press */
395217309Snwhitehorn	}
396217309Snwhitehorn
397217309Snwhitehorn	/*
398217309Snwhitehorn	 * Check if key pressed matches first character of any item tag in
399217309Snwhitehorn	 * list.  If there is more than one match, we will cycle through
400217309Snwhitehorn	 * each one as the same key is pressed repeatedly.
401217309Snwhitehorn	 */
402217309Snwhitehorn	found = FALSE;
403217309Snwhitehorn	if (!fkey) {
404217309Snwhitehorn	    if (button < 0 || !dialog_state.visit_items) {
405217309Snwhitehorn		for (j = scrollamt + choice + 1; j < item_no; j++) {
406217309Snwhitehorn		    if (dlg_match_char(dlg_last_getc(), items[j].name)) {
407217309Snwhitehorn			found = TRUE;
408217309Snwhitehorn			i = j - scrollamt;
409217309Snwhitehorn			break;
410217309Snwhitehorn		    }
411217309Snwhitehorn		}
412217309Snwhitehorn		if (!found) {
413217309Snwhitehorn		    for (j = 0; j <= scrollamt + choice; j++) {
414217309Snwhitehorn			if (dlg_match_char(dlg_last_getc(), items[j].name)) {
415217309Snwhitehorn			    found = TRUE;
416217309Snwhitehorn			    i = j - scrollamt;
417217309Snwhitehorn			    break;
418217309Snwhitehorn			}
419217309Snwhitehorn		    }
420217309Snwhitehorn		}
421217309Snwhitehorn		if (found)
422217309Snwhitehorn		    dlg_flush_getc();
423217309Snwhitehorn	    } else if ((j = dlg_char_to_button(key, buttons)) >= 0) {
424217309Snwhitehorn		button = j;
425217309Snwhitehorn		ungetch('\n');
426217309Snwhitehorn		continue;
427217309Snwhitehorn	    }
428217309Snwhitehorn	}
429217309Snwhitehorn
430217309Snwhitehorn	/*
431217309Snwhitehorn	 * A single digit (1-9) positions the selection to that line in the
432217309Snwhitehorn	 * current screen.
433217309Snwhitehorn	 */
434217309Snwhitehorn	if (!found
435217309Snwhitehorn	    && (key <= '9')
436217309Snwhitehorn	    && (key > '0')
437217309Snwhitehorn	    && (key - '1' < max_choice)) {
438217309Snwhitehorn	    found = TRUE;
439217309Snwhitehorn	    i = key - '1';
440217309Snwhitehorn	}
441217309Snwhitehorn
442217309Snwhitehorn	if (!found) {
443217309Snwhitehorn	    if (fkey) {
444217309Snwhitehorn		found = TRUE;
445217309Snwhitehorn		switch (key) {
446217309Snwhitehorn		case DLGK_ITEM_FIRST:
447217309Snwhitehorn		    i = -scrollamt;
448217309Snwhitehorn		    break;
449217309Snwhitehorn		case DLGK_ITEM_LAST:
450217309Snwhitehorn		    i = item_no - 1 - scrollamt;
451217309Snwhitehorn		    break;
452217309Snwhitehorn		case DLGK_PAGE_PREV:
453217309Snwhitehorn		    if (choice)
454217309Snwhitehorn			i = 0;
455217309Snwhitehorn		    else if (scrollamt != 0)
456217309Snwhitehorn			i = -MIN(scrollamt, max_choice);
457217309Snwhitehorn		    else
458217309Snwhitehorn			continue;
459217309Snwhitehorn		    break;
460217309Snwhitehorn		case DLGK_PAGE_NEXT:
461217309Snwhitehorn		    i = MIN(choice + max_choice, item_no - scrollamt - 1);
462217309Snwhitehorn		    break;
463217309Snwhitehorn		case DLGK_ITEM_PREV:
464217309Snwhitehorn		    i = choice - 1;
465217309Snwhitehorn		    if (choice == 0 && scrollamt == 0)
466217309Snwhitehorn			continue;
467217309Snwhitehorn		    break;
468217309Snwhitehorn		case DLGK_ITEM_NEXT:
469217309Snwhitehorn		    i = choice + 1;
470217309Snwhitehorn		    if (scrollamt + choice >= item_no - 1)
471217309Snwhitehorn			continue;
472217309Snwhitehorn		    break;
473217309Snwhitehorn		default:
474217309Snwhitehorn		    found = FALSE;
475217309Snwhitehorn		    break;
476217309Snwhitehorn		}
477217309Snwhitehorn	    }
478217309Snwhitehorn	}
479217309Snwhitehorn
480217309Snwhitehorn	if (found) {
481217309Snwhitehorn	    if (i != choice) {
482217309Snwhitehorn		getyx(dialog, cur_y, cur_x);
483217309Snwhitehorn		if (i < 0 || i >= max_choice) {
484217309Snwhitehorn#if defined(NCURSES_VERSION_MAJOR) && NCURSES_VERSION_MAJOR < 5
485217309Snwhitehorn		    /*
486217309Snwhitehorn		     * Using wscrl to assist ncurses scrolling is not needed
487217309Snwhitehorn		     * in version 5.x
488217309Snwhitehorn		     */
489217309Snwhitehorn		    if (i == -1) {
490217309Snwhitehorn			if (use_height > 1) {
491217309Snwhitehorn			    /* De-highlight current first item */
492217309Snwhitehorn			    print_item(list,
493217309Snwhitehorn				       &items[scrollamt],
494217309Snwhitehorn				       states,
495217309Snwhitehorn				       0, FALSE);
496217309Snwhitehorn			    scrollok(list, TRUE);
497217309Snwhitehorn			    wscrl(list, -1);
498217309Snwhitehorn			    scrollok(list, FALSE);
499217309Snwhitehorn			}
500217309Snwhitehorn			scrollamt--;
501217309Snwhitehorn			print_item(list,
502217309Snwhitehorn				   &items[scrollamt],
503217309Snwhitehorn				   states,
504217309Snwhitehorn				   0, TRUE);
505217309Snwhitehorn		    } else if (i == max_choice) {
506217309Snwhitehorn			if (use_height > 1) {
507217309Snwhitehorn			    /* De-highlight current last item before scrolling up */
508217309Snwhitehorn			    print_item(list,
509217309Snwhitehorn				       &items[scrollamt + max_choice - 1],
510217309Snwhitehorn				       states,
511217309Snwhitehorn				       max_choice - 1, FALSE);
512217309Snwhitehorn			    scrollok(list, TRUE);
513217309Snwhitehorn			    wscrl(list, 1);
514217309Snwhitehorn			    scrollok(list, FALSE);
515217309Snwhitehorn			}
516217309Snwhitehorn			scrollamt++;
517217309Snwhitehorn			print_item(list,
518217309Snwhitehorn				   &items[scrollamt + max_choice - 1],
519217309Snwhitehorn				   states,
520217309Snwhitehorn				   max_choice - 1, TRUE);
521217309Snwhitehorn		    } else
522217309Snwhitehorn#endif
523217309Snwhitehorn		    {
524217309Snwhitehorn			if (i < 0) {
525217309Snwhitehorn			    scrollamt += i;
526217309Snwhitehorn			    choice = 0;
527217309Snwhitehorn			} else {
528217309Snwhitehorn			    choice = max_choice - 1;
529217309Snwhitehorn			    scrollamt += (i - max_choice + 1);
530217309Snwhitehorn			}
531217309Snwhitehorn			for (i = 0; i < max_choice; i++) {
532217309Snwhitehorn			    print_item(list,
533217309Snwhitehorn				       &items[scrollamt + i],
534217309Snwhitehorn				       states,
535217309Snwhitehorn				       i, i == choice);
536217309Snwhitehorn			}
537217309Snwhitehorn		    }
538217309Snwhitehorn		    (void) wnoutrefresh(list);
539217309Snwhitehorn		    print_arrows(dialog,
540217309Snwhitehorn				 box_x, box_y,
541217309Snwhitehorn				 scrollamt, max_choice, item_no, use_height);
542217309Snwhitehorn		} else {
543217309Snwhitehorn		    /* De-highlight current item */
544217309Snwhitehorn		    print_item(list,
545217309Snwhitehorn			       &items[scrollamt + choice],
546217309Snwhitehorn			       states,
547217309Snwhitehorn			       choice, FALSE);
548217309Snwhitehorn		    /* Highlight new item */
549217309Snwhitehorn		    choice = i;
550217309Snwhitehorn		    print_item(list,
551217309Snwhitehorn			       &items[scrollamt + choice],
552217309Snwhitehorn			       states,
553217309Snwhitehorn			       choice, TRUE);
554217309Snwhitehorn		    (void) wnoutrefresh(list);
555217309Snwhitehorn		    print_arrows(dialog,
556217309Snwhitehorn				 box_x, box_y,
557217309Snwhitehorn				 scrollamt, max_choice, item_no, use_height);
558217309Snwhitehorn		    (void) wmove(dialog, cur_y, cur_x);
559217309Snwhitehorn		    wrefresh(dialog);
560217309Snwhitehorn		}
561217309Snwhitehorn	    }
562217309Snwhitehorn	    continue;		/* wait for another key press */
563217309Snwhitehorn	}
564217309Snwhitehorn
565217309Snwhitehorn	if (fkey) {
566217309Snwhitehorn	    switch (key) {
567217309Snwhitehorn	    case DLGK_ENTER:
568217309Snwhitehorn		result = dlg_ok_buttoncode(button);
569217309Snwhitehorn		break;
570217309Snwhitehorn	    case DLGK_FIELD_PREV:
571217309Snwhitehorn		button = dlg_prev_button(buttons, button);
572217309Snwhitehorn		dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
573217309Snwhitehorn				 FALSE, width);
574217309Snwhitehorn		break;
575217309Snwhitehorn	    case DLGK_FIELD_NEXT:
576217309Snwhitehorn		button = dlg_next_button(buttons, button);
577217309Snwhitehorn		dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
578217309Snwhitehorn				 FALSE, width);
579217309Snwhitehorn		break;
580217309Snwhitehorn#ifdef KEY_RESIZE
581217309Snwhitehorn	    case KEY_RESIZE:
582217309Snwhitehorn		/* reset data */
583217309Snwhitehorn		height = old_height;
584217309Snwhitehorn		width = old_width;
585217309Snwhitehorn		/* repaint */
586217309Snwhitehorn		dlg_clear();
587217309Snwhitehorn		dlg_del_window(dialog);
588217309Snwhitehorn		refresh();
589217309Snwhitehorn		dlg_mouse_free_regions();
590217309Snwhitehorn		goto retry;
591217309Snwhitehorn#endif
592217309Snwhitehorn	    default:
593217309Snwhitehorn		if (was_mouse) {
594217309Snwhitehorn		    if ((key2 = dlg_ok_buttoncode(key)) >= 0) {
595217309Snwhitehorn			result = key2;
596217309Snwhitehorn			break;
597217309Snwhitehorn		    }
598217309Snwhitehorn		    beep();
599217309Snwhitehorn		}
600217309Snwhitehorn	    }
601217309Snwhitehorn	} else {
602217309Snwhitehorn	    beep();
603217309Snwhitehorn	}
604217309Snwhitehorn    }
605217309Snwhitehorn
606217309Snwhitehorn    dlg_del_window(dialog);
607217309Snwhitehorn    dlg_mouse_free_regions();
608217309Snwhitehorn    free(prompt);
609217309Snwhitehorn    *current_item = (scrollamt + choice);
610217309Snwhitehorn    return result;
611217309Snwhitehorn}
612217309Snwhitehorn
613217309Snwhitehorn/*
614217309Snwhitehorn * Display a dialog box with a list of options that can be turned on or off
615217309Snwhitehorn * The `flag' parameter is used to select between radiolist and checklist.
616217309Snwhitehorn */
617217309Snwhitehornint
618217309Snwhitehorndialog_checklist(const char *title,
619217309Snwhitehorn		 const char *cprompt,
620217309Snwhitehorn		 int height,
621217309Snwhitehorn		 int width,
622217309Snwhitehorn		 int list_height,
623217309Snwhitehorn		 int item_no,
624217309Snwhitehorn		 char **items,
625217309Snwhitehorn		 int flag)
626217309Snwhitehorn{
627217309Snwhitehorn    int result;
628217309Snwhitehorn    int i;
629217309Snwhitehorn    DIALOG_LISTITEM *listitems;
630217309Snwhitehorn    bool separate_output = ((flag == FLAG_CHECK)
631217309Snwhitehorn			    && (dialog_vars.separate_output));
632217309Snwhitehorn    bool show_status = FALSE;
633217309Snwhitehorn    int current = 0;
634217309Snwhitehorn
635217309Snwhitehorn    listitems = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1);
636217309Snwhitehorn    assert_ptr(listitems, "dialog_checklist");
637217309Snwhitehorn
638217309Snwhitehorn    for (i = 0; i < item_no; ++i) {
639217309Snwhitehorn	listitems[i].name = ItemName(i);
640217309Snwhitehorn	listitems[i].text = ItemText(i);
641217309Snwhitehorn	listitems[i].help = ((dialog_vars.item_help)
642217309Snwhitehorn			     ? ItemHelp(i)
643217309Snwhitehorn			     : dlg_strempty());
644217309Snwhitehorn	listitems[i].state = !dlg_strcmp(ItemStatus(i), "on");
645217309Snwhitehorn    }
646220749Snwhitehorn    dlg_align_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
647217309Snwhitehorn
648217309Snwhitehorn    result = dlg_checklist(title,
649217309Snwhitehorn			   cprompt,
650217309Snwhitehorn			   height,
651217309Snwhitehorn			   width,
652217309Snwhitehorn			   list_height,
653217309Snwhitehorn			   item_no,
654217309Snwhitehorn			   listitems,
655217309Snwhitehorn			   NULL,
656217309Snwhitehorn			   flag,
657217309Snwhitehorn			   &current);
658217309Snwhitehorn
659217309Snwhitehorn    switch (result) {
660217309Snwhitehorn    case DLG_EXIT_OK:		/* FALLTHRU */
661217309Snwhitehorn    case DLG_EXIT_EXTRA:
662217309Snwhitehorn	show_status = TRUE;
663217309Snwhitehorn	break;
664217309Snwhitehorn    case DLG_EXIT_HELP:
665217309Snwhitehorn	dlg_add_result("HELP ");
666217309Snwhitehorn	show_status = dialog_vars.help_status;
667217309Snwhitehorn	if (USE_ITEM_HELP(listitems[current].help)) {
668217309Snwhitehorn	    if (show_status) {
669217309Snwhitehorn		if (separate_output) {
670217309Snwhitehorn		    dlg_add_string(listitems[current].help);
671217309Snwhitehorn		    dlg_add_separator();
672217309Snwhitehorn		} else {
673217309Snwhitehorn		    dlg_add_quoted(listitems[current].help);
674217309Snwhitehorn		}
675217309Snwhitehorn	    } else {
676217309Snwhitehorn		dlg_add_string(listitems[current].help);
677217309Snwhitehorn	    }
678217309Snwhitehorn	    result = DLG_EXIT_ITEM_HELP;
679217309Snwhitehorn	} else {
680217309Snwhitehorn	    if (show_status) {
681217309Snwhitehorn		if (separate_output) {
682217309Snwhitehorn		    dlg_add_string(listitems[current].name);
683217309Snwhitehorn		    dlg_add_separator();
684217309Snwhitehorn		} else {
685217309Snwhitehorn		    dlg_add_quoted(listitems[current].name);
686217309Snwhitehorn		}
687217309Snwhitehorn	    } else {
688217309Snwhitehorn		dlg_add_string(listitems[current].name);
689217309Snwhitehorn	    }
690217309Snwhitehorn	}
691217309Snwhitehorn	break;
692217309Snwhitehorn    }
693217309Snwhitehorn
694217309Snwhitehorn    if (show_status) {
695217309Snwhitehorn	for (i = 0; i < item_no; i++) {
696217309Snwhitehorn	    if (listitems[i].state) {
697217309Snwhitehorn		if (separate_output) {
698217309Snwhitehorn		    dlg_add_string(listitems[i].name);
699217309Snwhitehorn		    dlg_add_separator();
700217309Snwhitehorn		} else {
701217309Snwhitehorn		    if (dlg_need_separator())
702217309Snwhitehorn			dlg_add_separator();
703217309Snwhitehorn		    dlg_add_string(listitems[i].name);
704217309Snwhitehorn		}
705217309Snwhitehorn	    }
706217309Snwhitehorn	}
707217309Snwhitehorn    }
708217309Snwhitehorn
709220749Snwhitehorn    dlg_free_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
710217309Snwhitehorn    free(listitems);
711217309Snwhitehorn    return result;
712217309Snwhitehorn}
713