1217309Snwhitehorn/*
2255852Sdteske *  $Id: checklist.c,v 1.153 2013/09/02 17:01:02 tom Exp $
3217309Snwhitehorn *
4217309Snwhitehorn *  checklist.c -- implements the checklist box
5217309Snwhitehorn *
6251843Sbapt *  Copyright 2000-2012,2013	Thomas E. Dickey
7217309Snwhitehorn *
8217309Snwhitehorn *  This program is free software; you can redistribute it and/or modify
9217309Snwhitehorn *  it under the terms of the GNU Lesser General Public 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
32217309Snwhitehorn#define MIN_HIGH  (1 + (5 * MARGIN))
33217309Snwhitehorn
34251843Sbapttypedef struct {
35251843Sbapt    /* the outer-window */
36251843Sbapt    WINDOW *dialog;
37251843Sbapt    int box_y;
38251843Sbapt    int box_x;
39251843Sbapt    int check_x;
40251843Sbapt    int item_x;
41251843Sbapt    int checkflag;
42251843Sbapt    int use_height;
43251843Sbapt    int use_width;
44251843Sbapt    /* the inner-window */
45251843Sbapt    WINDOW *list;
46251843Sbapt    DIALOG_LISTITEM *items;
47251843Sbapt    int item_no;
48251843Sbapt    const char *states;
49251843Sbapt} ALL_DATA;
50217309Snwhitehorn
51217309Snwhitehorn/*
52217309Snwhitehorn * Print list item.  The 'selected' parameter is true if 'choice' is the
53217309Snwhitehorn * current item.  That one is colored differently from the other items.
54217309Snwhitehorn */
55217309Snwhitehornstatic void
56251843Sbaptprint_item(ALL_DATA * data,
57251843Sbapt	   WINDOW *win,
58217309Snwhitehorn	   DIALOG_LISTITEM * item,
59217309Snwhitehorn	   const char *states,
60217309Snwhitehorn	   int choice,
61217309Snwhitehorn	   int selected)
62217309Snwhitehorn{
63220749Snwhitehorn    chtype save = dlg_get_attrs(win);
64217309Snwhitehorn    int i;
65251843Sbapt    bool both = (!dialog_vars.no_tags && !dialog_vars.no_items);
66251843Sbapt    bool first = TRUE;
67251843Sbapt    int climit = (getmaxx(win) - data->check_x + 1);
68251843Sbapt    const char *show = (dialog_vars.no_items
69251843Sbapt			? item->name
70251843Sbapt			: item->text);
71217309Snwhitehorn
72217309Snwhitehorn    /* Clear 'residue' of last item */
73251843Sbapt    (void) wattrset(win, menubox_attr);
74217309Snwhitehorn    (void) wmove(win, choice, 0);
75251843Sbapt    for (i = 0; i < data->use_width; i++)
76217309Snwhitehorn	(void) waddch(win, ' ');
77217309Snwhitehorn
78251843Sbapt    (void) wmove(win, choice, data->check_x);
79251843Sbapt    (void) wattrset(win, selected ? check_selected_attr : check_attr);
80217309Snwhitehorn    (void) wprintw(win,
81251843Sbapt		   (data->checkflag == FLAG_CHECK) ? "[%c]" : "(%c)",
82217309Snwhitehorn		   states[item->state]);
83251843Sbapt    (void) wattrset(win, menubox_attr);
84217309Snwhitehorn    (void) waddch(win, ' ');
85217309Snwhitehorn
86251843Sbapt    if (both) {
87251843Sbapt	dlg_print_listitem(win, item->name, climit, first, selected);
88251843Sbapt	first = FALSE;
89251843Sbapt    }
90217309Snwhitehorn
91251843Sbapt    (void) wmove(win, choice, data->item_x);
92251843Sbapt    dlg_print_listitem(win, show, climit, first, selected);
93217309Snwhitehorn
94251843Sbapt    if (selected) {
95251843Sbapt	dlg_item_help(item->help);
96217309Snwhitehorn    }
97251843Sbapt    (void) wattrset(win, save);
98251843Sbapt}
99217309Snwhitehorn
100251843Sbaptstatic void
101251843Sbaptprint_list(ALL_DATA * data, int choice, int scrollamt, int max_choice)
102251843Sbapt{
103251843Sbapt    int i;
104251843Sbapt    int cur_y, cur_x;
105217309Snwhitehorn
106251843Sbapt    getyx(data->dialog, cur_y, cur_x);
107251843Sbapt    for (i = 0; i < max_choice; i++) {
108251843Sbapt	print_item(data,
109251843Sbapt		   data->list,
110251843Sbapt		   &data->items[i + scrollamt],
111251843Sbapt		   data->states,
112251843Sbapt		   i, i == choice);
113217309Snwhitehorn    }
114251843Sbapt    (void) wnoutrefresh(data->list);
115217309Snwhitehorn
116251843Sbapt    dlg_draw_scrollbar(data->dialog,
117251843Sbapt		       (long) (scrollamt),
118251843Sbapt		       (long) (scrollamt),
119251843Sbapt		       (long) (scrollamt + max_choice),
120251843Sbapt		       (long) (data->item_no),
121251843Sbapt		       data->box_x + data->check_x,
122251843Sbapt		       data->box_x + data->use_width,
123251843Sbapt		       data->box_y,
124251843Sbapt		       data->box_y + data->use_height + 1,
125251843Sbapt		       menubox_border2_attr,
126251843Sbapt		       menubox_border_attr);
127251843Sbapt
128251843Sbapt    (void) wmove(data->dialog, cur_y, cur_x);
129251843Sbapt}
130251843Sbapt
131251843Sbaptstatic bool
132251843Sbaptcheck_hotkey(DIALOG_LISTITEM * items, int choice)
133251843Sbapt{
134251843Sbapt    bool result = FALSE;
135251843Sbapt
136251843Sbapt    if (dlg_match_char(dlg_last_getc(),
137251843Sbapt		       (dialog_vars.no_tags
138251843Sbapt			? items[choice].text
139251843Sbapt			: items[choice].name))) {
140251843Sbapt	result = TRUE;
141217309Snwhitehorn    }
142251843Sbapt    return result;
143217309Snwhitehorn}
144217309Snwhitehorn
145217309Snwhitehorn/*
146217309Snwhitehorn * This is an alternate interface to 'checklist' which allows the application
147217309Snwhitehorn * to read the list item states back directly without putting them in the
148217309Snwhitehorn * output buffer.  It also provides for more than two states over which the
149217309Snwhitehorn * check/radio box can display.
150217309Snwhitehorn */
151217309Snwhitehornint
152217309Snwhitehorndlg_checklist(const char *title,
153217309Snwhitehorn	      const char *cprompt,
154217309Snwhitehorn	      int height,
155217309Snwhitehorn	      int width,
156217309Snwhitehorn	      int list_height,
157217309Snwhitehorn	      int item_no,
158217309Snwhitehorn	      DIALOG_LISTITEM * items,
159217309Snwhitehorn	      const char *states,
160217309Snwhitehorn	      int flag,
161217309Snwhitehorn	      int *current_item)
162217309Snwhitehorn{
163217309Snwhitehorn    /* *INDENT-OFF* */
164217309Snwhitehorn    static DLG_KEYS_BINDING binding[] = {
165224014Snwhitehorn	HELPKEY_BINDINGS,
166217309Snwhitehorn	ENTERKEY_BINDINGS,
167217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
168217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
169217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
170217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
171217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_FIRST, KEY_HOME ),
172217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_END ),
173217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_LL ),
174217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	'+' ),
175217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	KEY_DOWN ),
176217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  CHR_NEXT ),
177217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,	'-' ),
178217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,	KEY_UP ),
179217309Snwhitehorn	DLG_KEYS_DATA( DLGK_ITEM_PREV,  CHR_PREVIOUS ),
180217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ),
181217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE) ),
182217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ),
183217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE) ),
184217309Snwhitehorn	END_KEYS_BINDING
185217309Snwhitehorn    };
186217309Snwhitehorn    /* *INDENT-ON* */
187217309Snwhitehorn
188217309Snwhitehorn#ifdef KEY_RESIZE
189217309Snwhitehorn    int old_height = height;
190217309Snwhitehorn    int old_width = width;
191217309Snwhitehorn#endif
192251843Sbapt    ALL_DATA all;
193251843Sbapt    int i, j, key2, found, x, y, cur_x, cur_y;
194217309Snwhitehorn    int key = 0, fkey;
195251843Sbapt    int button = dialog_state.visit_items ? -1 : dlg_default_button();
196217309Snwhitehorn    int choice = dlg_default_listitem(items);
197217309Snwhitehorn    int scrollamt = 0;
198217309Snwhitehorn    int max_choice;
199217309Snwhitehorn    int was_mouse;
200251843Sbapt    int use_width, list_width, name_width, text_width;
201217309Snwhitehorn    int result = DLG_EXIT_UNKNOWN;
202217309Snwhitehorn    int num_states;
203251843Sbapt    WINDOW *dialog;
204217309Snwhitehorn    char *prompt = dlg_strclone(cprompt);
205217309Snwhitehorn    const char **buttons = dlg_ok_labels();
206251843Sbapt    const char *widget_name;
207217309Snwhitehorn
208251843Sbapt    memset(&all, 0, sizeof(all));
209251843Sbapt    all.items = items;
210251843Sbapt    all.item_no = item_no;
211251843Sbapt
212217309Snwhitehorn    dlg_does_output();
213217309Snwhitehorn    dlg_tab_correct_str(prompt);
214217309Snwhitehorn
215224014Snwhitehorn    /*
216224014Snwhitehorn     * If this is a radiobutton list, ensure that no more than one item is
217224014Snwhitehorn     * selected initially.  Allow none to be selected, since some users may
218224014Snwhitehorn     * wish to provide this flavor.
219224014Snwhitehorn     */
220224014Snwhitehorn    if (flag == FLAG_RADIO) {
221224014Snwhitehorn	bool first = TRUE;
222224014Snwhitehorn
223224014Snwhitehorn	for (i = 0; i < item_no; i++) {
224224014Snwhitehorn	    if (items[i].state) {
225224014Snwhitehorn		if (first) {
226224014Snwhitehorn		    first = FALSE;
227224014Snwhitehorn		} else {
228224014Snwhitehorn		    items[i].state = 0;
229224014Snwhitehorn		}
230224014Snwhitehorn	    }
231224014Snwhitehorn	}
232251843Sbapt	widget_name = "radiolist";
233251843Sbapt    } else {
234251843Sbapt	widget_name = "checklist";
235224014Snwhitehorn    }
236217309Snwhitehorn#ifdef KEY_RESIZE
237217309Snwhitehorn  retry:
238217309Snwhitehorn#endif
239217309Snwhitehorn
240251843Sbapt    all.use_height = list_height;
241251843Sbapt    use_width = dlg_calc_list_width(item_no, items) + 10;
242251843Sbapt    use_width = MAX(26, use_width);
243251843Sbapt    if (all.use_height == 0) {
244217309Snwhitehorn	/* calculate height without items (4) */
245251843Sbapt	dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, use_width);
246251843Sbapt	dlg_calc_listh(&height, &all.use_height, item_no);
247217309Snwhitehorn    } else {
248251843Sbapt	dlg_auto_size(title, prompt,
249251843Sbapt		      &height, &width,
250251843Sbapt		      MIN_HIGH + all.use_height, use_width);
251217309Snwhitehorn    }
252217309Snwhitehorn    dlg_button_layout(buttons, &width);
253217309Snwhitehorn    dlg_print_size(height, width);
254217309Snwhitehorn    dlg_ctl_size(height, width);
255217309Snwhitehorn
256217309Snwhitehorn    /* we need at least two states */
257217309Snwhitehorn    if (states == 0 || strlen(states) < 2)
258217309Snwhitehorn	states = " *";
259217309Snwhitehorn    num_states = (int) strlen(states);
260251843Sbapt    all.states = states;
261217309Snwhitehorn
262251843Sbapt    all.checkflag = flag;
263217309Snwhitehorn
264217309Snwhitehorn    x = dlg_box_x_ordinate(width);
265217309Snwhitehorn    y = dlg_box_y_ordinate(height);
266217309Snwhitehorn
267217309Snwhitehorn    dialog = dlg_new_window(height, width, y, x);
268251843Sbapt    all.dialog = dialog;
269251843Sbapt    dlg_register_window(dialog, widget_name, binding);
270251843Sbapt    dlg_register_buttons(dialog, widget_name, buttons);
271217309Snwhitehorn
272217309Snwhitehorn    dlg_mouse_setbase(x, y);
273217309Snwhitehorn
274251843Sbapt    dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
275251843Sbapt    dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
276217309Snwhitehorn    dlg_draw_title(dialog, title);
277217309Snwhitehorn
278251843Sbapt    (void) wattrset(dialog, dialog_attr);
279217309Snwhitehorn    dlg_print_autowrap(dialog, prompt, height, width);
280217309Snwhitehorn
281251843Sbapt    all.use_width = width - 6;
282217309Snwhitehorn    getyx(dialog, cur_y, cur_x);
283251843Sbapt    all.box_y = cur_y + 1;
284251843Sbapt    all.box_x = (width - all.use_width) / 2 - 1;
285217309Snwhitehorn
286217309Snwhitehorn    /*
287217309Snwhitehorn     * After displaying the prompt, we know how much space we really have.
288217309Snwhitehorn     * Limit the list to avoid overwriting the ok-button.
289217309Snwhitehorn     */
290251843Sbapt    if (all.use_height + MIN_HIGH > height - cur_y)
291251843Sbapt	all.use_height = height - MIN_HIGH - cur_y;
292251843Sbapt    if (all.use_height <= 0)
293251843Sbapt	all.use_height = 1;
294217309Snwhitehorn
295251843Sbapt    max_choice = MIN(all.use_height, item_no);
296251843Sbapt    max_choice = MAX(max_choice, 1);
297217309Snwhitehorn
298217309Snwhitehorn    /* create new window for the list */
299251843Sbapt    all.list = dlg_sub_window(dialog, all.use_height, all.use_width,
300251843Sbapt			      y + all.box_y + 1, x + all.box_x + 1);
301217309Snwhitehorn
302217309Snwhitehorn    /* draw a box around the list items */
303251843Sbapt    dlg_draw_box(dialog, all.box_y, all.box_x,
304251843Sbapt		 all.use_height + 2 * MARGIN,
305251843Sbapt		 all.use_width + 2 * MARGIN,
306251843Sbapt		 menubox_border_attr, menubox_border2_attr);
307217309Snwhitehorn
308217309Snwhitehorn    text_width = 0;
309217309Snwhitehorn    name_width = 0;
310217309Snwhitehorn    /* Find length of longest item to center checklist */
311217309Snwhitehorn    for (i = 0; i < item_no; i++) {
312217309Snwhitehorn	text_width = MAX(text_width, dlg_count_columns(items[i].text));
313217309Snwhitehorn	name_width = MAX(name_width, dlg_count_columns(items[i].name));
314217309Snwhitehorn    }
315217309Snwhitehorn
316217309Snwhitehorn    /* If the name+text is wider than the list is allowed, then truncate
317217309Snwhitehorn     * one or both of them.  If the name is no wider than 1/4 of the list,
318217309Snwhitehorn     * leave it intact.
319217309Snwhitehorn     */
320251843Sbapt    use_width = (all.use_width - 6);
321251843Sbapt    if (dialog_vars.no_tags) {
322251843Sbapt	list_width = MIN(all.use_width, text_width);
323251843Sbapt    } else if (dialog_vars.no_items) {
324251843Sbapt	list_width = MIN(all.use_width, name_width);
325251843Sbapt    } else {
326251843Sbapt	if (text_width >= 0
327251843Sbapt	    && name_width >= 0
328251843Sbapt	    && use_width > 0
329251843Sbapt	    && text_width + name_width > use_width) {
330251843Sbapt	    int need = (int) (0.25 * use_width);
331251843Sbapt	    if (name_width > need) {
332251843Sbapt		int want = (int) (use_width * ((double) name_width) /
333251843Sbapt				  (text_width + name_width));
334251843Sbapt		name_width = (want > need) ? want : need;
335251843Sbapt	    }
336251843Sbapt	    text_width = use_width - name_width;
337217309Snwhitehorn	}
338251843Sbapt	list_width = (text_width + name_width);
339217309Snwhitehorn    }
340217309Snwhitehorn
341251843Sbapt    all.check_x = (use_width - list_width) / 2;
342251843Sbapt    all.item_x = ((dialog_vars.no_tags
343251843Sbapt		   ? 0
344251843Sbapt		   : (dialog_vars.no_items
345251843Sbapt		      ? 0
346251843Sbapt		      : (2 + name_width)))
347251843Sbapt		  + all.check_x + 4);
348217309Snwhitehorn
349217309Snwhitehorn    /* ensure we are scrolled to show the current choice */
350251843Sbapt    scrollamt = MIN(scrollamt, max_choice + item_no - 1);
351251843Sbapt    if (choice >= (max_choice + scrollamt - 1)) {
352251843Sbapt	scrollamt = MAX(0, choice - max_choice + 1);
353217309Snwhitehorn	choice = max_choice - 1;
354217309Snwhitehorn    }
355251843Sbapt    print_list(&all, choice, scrollamt, max_choice);
356217309Snwhitehorn
357217309Snwhitehorn    /* register the new window, along with its borders */
358251843Sbapt    dlg_mouse_mkbigregion(all.box_y + 1, all.box_x,
359251843Sbapt			  all.use_height, all.use_width + 2,
360217309Snwhitehorn			  KEY_MAX, 1, 1, 1 /* by lines */ );
361217309Snwhitehorn
362217309Snwhitehorn    dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
363217309Snwhitehorn
364251843Sbapt    dlg_trace_win(dialog);
365217309Snwhitehorn    while (result == DLG_EXIT_UNKNOWN) {
366217309Snwhitehorn	if (button < 0)		/* --visit-items */
367251843Sbapt	    wmove(dialog, all.box_y + choice + 1, all.box_x + all.check_x + 2);
368217309Snwhitehorn
369217309Snwhitehorn	key = dlg_mouse_wgetch(dialog, &fkey);
370217309Snwhitehorn	if (dlg_result_key(key, fkey, &result))
371217309Snwhitehorn	    break;
372217309Snwhitehorn
373217309Snwhitehorn	was_mouse = (fkey && is_DLGK_MOUSE(key));
374217309Snwhitehorn	if (was_mouse)
375217309Snwhitehorn	    key -= M_EVENT;
376217309Snwhitehorn
377217309Snwhitehorn	if (was_mouse && (key >= KEY_MAX)) {
378217309Snwhitehorn	    getyx(dialog, cur_y, cur_x);
379217309Snwhitehorn	    i = (key - KEY_MAX);
380217309Snwhitehorn	    if (i < max_choice) {
381217309Snwhitehorn		choice = (key - KEY_MAX);
382251843Sbapt		print_list(&all, choice, scrollamt, max_choice);
383217309Snwhitehorn
384217309Snwhitehorn		key = ' ';	/* force the selected item to toggle */
385217309Snwhitehorn	    } else {
386217309Snwhitehorn		beep();
387217309Snwhitehorn		continue;
388217309Snwhitehorn	    }
389217309Snwhitehorn	    fkey = FALSE;
390217309Snwhitehorn	} else if (was_mouse && key >= KEY_MIN) {
391217309Snwhitehorn	    key = dlg_lookup_key(dialog, key, &fkey);
392217309Snwhitehorn	}
393217309Snwhitehorn
394217309Snwhitehorn	/*
395217309Snwhitehorn	 * A space toggles the item status.  We handle either a checklist
396217309Snwhitehorn	 * (any number of items can be selected) or radio list (zero or one
397217309Snwhitehorn	 * items can be selected).
398217309Snwhitehorn	 */
399217309Snwhitehorn	if (key == ' ') {
400217309Snwhitehorn	    int current = scrollamt + choice;
401217309Snwhitehorn	    int next = items[current].state + 1;
402217309Snwhitehorn
403217309Snwhitehorn	    if (next >= num_states)
404217309Snwhitehorn		next = 0;
405217309Snwhitehorn
406217309Snwhitehorn	    if (flag == FLAG_CHECK) {	/* checklist? */
407251843Sbapt		getyx(dialog, cur_y, cur_x);
408217309Snwhitehorn		items[current].state = next;
409251843Sbapt		print_item(&all, all.list,
410217309Snwhitehorn			   &items[scrollamt + choice],
411217309Snwhitehorn			   states,
412217309Snwhitehorn			   choice, TRUE);
413251843Sbapt		(void) wnoutrefresh(all.list);
414251843Sbapt		(void) wmove(dialog, cur_y, cur_x);
415217309Snwhitehorn	    } else {		/* radiolist */
416217309Snwhitehorn		for (i = 0; i < item_no; i++) {
417217309Snwhitehorn		    if (i != current) {
418217309Snwhitehorn			items[i].state = 0;
419217309Snwhitehorn		    }
420217309Snwhitehorn		}
421217309Snwhitehorn		if (items[current].state) {
422251843Sbapt		    getyx(dialog, cur_y, cur_x);
423217309Snwhitehorn		    items[current].state = next ? next : 1;
424251843Sbapt		    print_item(&all, all.list,
425217309Snwhitehorn			       &items[current],
426217309Snwhitehorn			       states,
427217309Snwhitehorn			       choice, TRUE);
428251843Sbapt		    (void) wnoutrefresh(all.list);
429251843Sbapt		    (void) wmove(dialog, cur_y, cur_x);
430217309Snwhitehorn		} else {
431217309Snwhitehorn		    items[current].state = 1;
432251843Sbapt		    print_list(&all, choice, scrollamt, max_choice);
433217309Snwhitehorn		}
434217309Snwhitehorn	    }
435217309Snwhitehorn	    continue;		/* wait for another key press */
436217309Snwhitehorn	}
437217309Snwhitehorn
438217309Snwhitehorn	/*
439217309Snwhitehorn	 * Check if key pressed matches first character of any item tag in
440217309Snwhitehorn	 * list.  If there is more than one match, we will cycle through
441217309Snwhitehorn	 * each one as the same key is pressed repeatedly.
442217309Snwhitehorn	 */
443217309Snwhitehorn	found = FALSE;
444217309Snwhitehorn	if (!fkey) {
445217309Snwhitehorn	    if (button < 0 || !dialog_state.visit_items) {
446217309Snwhitehorn		for (j = scrollamt + choice + 1; j < item_no; j++) {
447251843Sbapt		    if (check_hotkey(items, j)) {
448217309Snwhitehorn			found = TRUE;
449217309Snwhitehorn			i = j - scrollamt;
450217309Snwhitehorn			break;
451217309Snwhitehorn		    }
452217309Snwhitehorn		}
453217309Snwhitehorn		if (!found) {
454217309Snwhitehorn		    for (j = 0; j <= scrollamt + choice; j++) {
455251843Sbapt			if (check_hotkey(items, j)) {
456217309Snwhitehorn			    found = TRUE;
457217309Snwhitehorn			    i = j - scrollamt;
458217309Snwhitehorn			    break;
459217309Snwhitehorn			}
460217309Snwhitehorn		    }
461217309Snwhitehorn		}
462217309Snwhitehorn		if (found)
463217309Snwhitehorn		    dlg_flush_getc();
464217309Snwhitehorn	    } else if ((j = dlg_char_to_button(key, buttons)) >= 0) {
465217309Snwhitehorn		button = j;
466217309Snwhitehorn		ungetch('\n');
467217309Snwhitehorn		continue;
468217309Snwhitehorn	    }
469217309Snwhitehorn	}
470217309Snwhitehorn
471217309Snwhitehorn	/*
472217309Snwhitehorn	 * A single digit (1-9) positions the selection to that line in the
473217309Snwhitehorn	 * current screen.
474217309Snwhitehorn	 */
475217309Snwhitehorn	if (!found
476217309Snwhitehorn	    && (key <= '9')
477217309Snwhitehorn	    && (key > '0')
478217309Snwhitehorn	    && (key - '1' < max_choice)) {
479217309Snwhitehorn	    found = TRUE;
480217309Snwhitehorn	    i = key - '1';
481217309Snwhitehorn	}
482217309Snwhitehorn
483217309Snwhitehorn	if (!found) {
484217309Snwhitehorn	    if (fkey) {
485217309Snwhitehorn		found = TRUE;
486217309Snwhitehorn		switch (key) {
487217309Snwhitehorn		case DLGK_ITEM_FIRST:
488217309Snwhitehorn		    i = -scrollamt;
489217309Snwhitehorn		    break;
490217309Snwhitehorn		case DLGK_ITEM_LAST:
491217309Snwhitehorn		    i = item_no - 1 - scrollamt;
492217309Snwhitehorn		    break;
493217309Snwhitehorn		case DLGK_PAGE_PREV:
494217309Snwhitehorn		    if (choice)
495217309Snwhitehorn			i = 0;
496217309Snwhitehorn		    else if (scrollamt != 0)
497217309Snwhitehorn			i = -MIN(scrollamt, max_choice);
498217309Snwhitehorn		    else
499217309Snwhitehorn			continue;
500217309Snwhitehorn		    break;
501217309Snwhitehorn		case DLGK_PAGE_NEXT:
502217309Snwhitehorn		    i = MIN(choice + max_choice, item_no - scrollamt - 1);
503217309Snwhitehorn		    break;
504217309Snwhitehorn		case DLGK_ITEM_PREV:
505217309Snwhitehorn		    i = choice - 1;
506217309Snwhitehorn		    if (choice == 0 && scrollamt == 0)
507217309Snwhitehorn			continue;
508217309Snwhitehorn		    break;
509217309Snwhitehorn		case DLGK_ITEM_NEXT:
510217309Snwhitehorn		    i = choice + 1;
511217309Snwhitehorn		    if (scrollamt + choice >= item_no - 1)
512217309Snwhitehorn			continue;
513217309Snwhitehorn		    break;
514217309Snwhitehorn		default:
515217309Snwhitehorn		    found = FALSE;
516217309Snwhitehorn		    break;
517217309Snwhitehorn		}
518217309Snwhitehorn	    }
519217309Snwhitehorn	}
520217309Snwhitehorn
521217309Snwhitehorn	if (found) {
522217309Snwhitehorn	    if (i != choice) {
523217309Snwhitehorn		getyx(dialog, cur_y, cur_x);
524217309Snwhitehorn		if (i < 0 || i >= max_choice) {
525251843Sbapt		    if (i < 0) {
526251843Sbapt			scrollamt += i;
527251843Sbapt			choice = 0;
528251843Sbapt		    } else {
529251843Sbapt			choice = max_choice - 1;
530251843Sbapt			scrollamt += (i - max_choice + 1);
531217309Snwhitehorn		    }
532251843Sbapt		    print_list(&all, choice, scrollamt, max_choice);
533217309Snwhitehorn		} else {
534217309Snwhitehorn		    choice = i;
535251843Sbapt		    print_list(&all, choice, scrollamt, max_choice);
536217309Snwhitehorn		}
537217309Snwhitehorn	    }
538217309Snwhitehorn	    continue;		/* wait for another key press */
539217309Snwhitehorn	}
540217309Snwhitehorn
541217309Snwhitehorn	if (fkey) {
542217309Snwhitehorn	    switch (key) {
543217309Snwhitehorn	    case DLGK_ENTER:
544224014Snwhitehorn		result = dlg_enter_buttoncode(button);
545217309Snwhitehorn		break;
546217309Snwhitehorn	    case DLGK_FIELD_PREV:
547217309Snwhitehorn		button = dlg_prev_button(buttons, button);
548217309Snwhitehorn		dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
549217309Snwhitehorn				 FALSE, width);
550217309Snwhitehorn		break;
551217309Snwhitehorn	    case DLGK_FIELD_NEXT:
552217309Snwhitehorn		button = dlg_next_button(buttons, button);
553217309Snwhitehorn		dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
554217309Snwhitehorn				 FALSE, width);
555217309Snwhitehorn		break;
556217309Snwhitehorn#ifdef KEY_RESIZE
557217309Snwhitehorn	    case KEY_RESIZE:
558217309Snwhitehorn		/* reset data */
559217309Snwhitehorn		height = old_height;
560217309Snwhitehorn		width = old_width;
561217309Snwhitehorn		/* repaint */
562217309Snwhitehorn		dlg_clear();
563217309Snwhitehorn		dlg_del_window(dialog);
564217309Snwhitehorn		refresh();
565217309Snwhitehorn		dlg_mouse_free_regions();
566217309Snwhitehorn		goto retry;
567217309Snwhitehorn#endif
568217309Snwhitehorn	    default:
569217309Snwhitehorn		if (was_mouse) {
570217309Snwhitehorn		    if ((key2 = dlg_ok_buttoncode(key)) >= 0) {
571217309Snwhitehorn			result = key2;
572217309Snwhitehorn			break;
573217309Snwhitehorn		    }
574217309Snwhitehorn		    beep();
575217309Snwhitehorn		}
576217309Snwhitehorn	    }
577217309Snwhitehorn	} else {
578217309Snwhitehorn	    beep();
579217309Snwhitehorn	}
580217309Snwhitehorn    }
581217309Snwhitehorn
582217309Snwhitehorn    dlg_del_window(dialog);
583217309Snwhitehorn    dlg_mouse_free_regions();
584217309Snwhitehorn    free(prompt);
585217309Snwhitehorn    *current_item = (scrollamt + choice);
586217309Snwhitehorn    return result;
587217309Snwhitehorn}
588217309Snwhitehorn
589217309Snwhitehorn/*
590217309Snwhitehorn * Display a dialog box with a list of options that can be turned on or off
591217309Snwhitehorn * The `flag' parameter is used to select between radiolist and checklist.
592217309Snwhitehorn */
593217309Snwhitehornint
594217309Snwhitehorndialog_checklist(const char *title,
595217309Snwhitehorn		 const char *cprompt,
596217309Snwhitehorn		 int height,
597217309Snwhitehorn		 int width,
598217309Snwhitehorn		 int list_height,
599217309Snwhitehorn		 int item_no,
600217309Snwhitehorn		 char **items,
601217309Snwhitehorn		 int flag)
602217309Snwhitehorn{
603217309Snwhitehorn    int result;
604251843Sbapt    int i, j;
605217309Snwhitehorn    DIALOG_LISTITEM *listitems;
606217309Snwhitehorn    bool separate_output = ((flag == FLAG_CHECK)
607217309Snwhitehorn			    && (dialog_vars.separate_output));
608217309Snwhitehorn    bool show_status = FALSE;
609217309Snwhitehorn    int current = 0;
610255852Sdteske    char *help_result;
611217309Snwhitehorn
612217309Snwhitehorn    listitems = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1);
613217309Snwhitehorn    assert_ptr(listitems, "dialog_checklist");
614217309Snwhitehorn
615251843Sbapt    for (i = j = 0; i < item_no; ++i) {
616251843Sbapt	listitems[i].name = items[j++];
617251843Sbapt	listitems[i].text = (dialog_vars.no_items
618251843Sbapt			     ? dlg_strempty()
619251843Sbapt			     : items[j++]);
620251843Sbapt	listitems[i].state = !dlg_strcmp(items[j++], "on");
621217309Snwhitehorn	listitems[i].help = ((dialog_vars.item_help)
622251843Sbapt			     ? items[j++]
623217309Snwhitehorn			     : dlg_strempty());
624217309Snwhitehorn    }
625220749Snwhitehorn    dlg_align_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
626217309Snwhitehorn
627217309Snwhitehorn    result = dlg_checklist(title,
628217309Snwhitehorn			   cprompt,
629217309Snwhitehorn			   height,
630217309Snwhitehorn			   width,
631217309Snwhitehorn			   list_height,
632217309Snwhitehorn			   item_no,
633217309Snwhitehorn			   listitems,
634217309Snwhitehorn			   NULL,
635217309Snwhitehorn			   flag,
636217309Snwhitehorn			   &current);
637217309Snwhitehorn
638217309Snwhitehorn    switch (result) {
639217309Snwhitehorn    case DLG_EXIT_OK:		/* FALLTHRU */
640217309Snwhitehorn    case DLG_EXIT_EXTRA:
641217309Snwhitehorn	show_status = TRUE;
642217309Snwhitehorn	break;
643217309Snwhitehorn    case DLG_EXIT_HELP:
644255852Sdteske	dlg_add_help_listitem(&result, &help_result, &listitems[current]);
645255852Sdteske	if ((show_status = dialog_vars.help_status)) {
646255852Sdteske	    if (separate_output) {
647255852Sdteske		dlg_add_string(help_result);
648255852Sdteske		dlg_add_separator();
649217309Snwhitehorn	    } else {
650255852Sdteske		dlg_add_quoted(help_result);
651217309Snwhitehorn	    }
652217309Snwhitehorn	} else {
653255852Sdteske	    dlg_add_string(help_result);
654217309Snwhitehorn	}
655217309Snwhitehorn	break;
656217309Snwhitehorn    }
657217309Snwhitehorn
658217309Snwhitehorn    if (show_status) {
659217309Snwhitehorn	for (i = 0; i < item_no; i++) {
660217309Snwhitehorn	    if (listitems[i].state) {
661217309Snwhitehorn		if (separate_output) {
662217309Snwhitehorn		    dlg_add_string(listitems[i].name);
663217309Snwhitehorn		    dlg_add_separator();
664217309Snwhitehorn		} else {
665217309Snwhitehorn		    if (dlg_need_separator())
666217309Snwhitehorn			dlg_add_separator();
667251843Sbapt		    if (flag == FLAG_CHECK)
668251843Sbapt			dlg_add_quoted(listitems[i].name);
669251843Sbapt		    else
670251843Sbapt			dlg_add_string(listitems[i].name);
671217309Snwhitehorn		}
672217309Snwhitehorn	    }
673217309Snwhitehorn	}
674251843Sbapt	dlg_add_last_key(separate_output);
675217309Snwhitehorn    }
676217309Snwhitehorn
677220749Snwhitehorn    dlg_free_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
678217309Snwhitehorn    free(listitems);
679217309Snwhitehorn    return result;
680217309Snwhitehorn}
681