1251839Sbapt/*
2255852Sdteske *  $Id: buildlist.c,v 1.59 2013/09/02 17:01:02 tom Exp $
3251839Sbapt *
4251839Sbapt *  buildlist.c -- implements the buildlist dialog
5251839Sbapt *
6251839Sbapt *  Copyright 2012,2013	Thomas E. Dickey
7251839Sbapt *
8251839Sbapt *  This program is free software; you can redistribute it and/or modify
9251839Sbapt *  it under the terms of the GNU Lesser General Public License, version 2.1
10251839Sbapt *  as published by the Free Software Foundation.
11251839Sbapt *
12251839Sbapt *  This program is distributed in the hope that it will be useful, but
13251839Sbapt *  WITHOUT ANY WARRANTY; without even the implied warranty of
14251839Sbapt *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15251839Sbapt *  Lesser General Public License for more details.
16251839Sbapt *
17251839Sbapt *  You should have received a copy of the GNU Lesser General Public
18251839Sbapt *  License along with this program; if not, write to
19251839Sbapt *	Free Software Foundation, Inc.
20251839Sbapt *	51 Franklin St., Fifth Floor
21251839Sbapt *	Boston, MA 02110, USA.
22251839Sbapt */
23251839Sbapt
24251839Sbapt#include <dialog.h>
25251839Sbapt#include <dlg_keys.h>
26251839Sbapt
27251839Sbapt/*
28251839Sbapt * Visually like menubox, but two columns.
29251839Sbapt */
30251839Sbapt
31251839Sbapt#define sLEFT         (-2)
32251839Sbapt#define sRIGHT        (-1)
33251839Sbapt
34251839Sbapt#define KEY_TOGGLE    ' '
35251839Sbapt#define KEY_LEFTCOL   '^'
36251839Sbapt#define KEY_RIGHTCOL  '$'
37251839Sbapt
38251839Sbapt#define MIN_HIGH  (1 + (5 * MARGIN))
39251839Sbapt
40251839Sbapttypedef struct {
41251839Sbapt    WINDOW *win;
42251839Sbapt    int box_y;
43251839Sbapt    int box_x;
44251839Sbapt    int top_index;
45251839Sbapt    int cur_index;
46251839Sbapt} MY_DATA;
47251839Sbapt
48251839Sbapttypedef struct {
49251839Sbapt    DIALOG_LISTITEM *items;
50251839Sbapt    int base_y;			/* base for mouse coordinates */
51251839Sbapt    int base_x;
52251839Sbapt    int use_height;		/* actual size of column box */
53251839Sbapt    int use_width;
54251839Sbapt    int item_no;
55251839Sbapt    int check_x;
56251839Sbapt    int item_x;
57251839Sbapt    MY_DATA list[2];
58251839Sbapt} ALL_DATA;
59251839Sbapt
60251839Sbapt/*
61251839Sbapt * Print list item.  The 'selected' parameter is true if 'choice' is the
62251839Sbapt * current item.  That one is colored differently from the other items.
63251839Sbapt */
64251839Sbaptstatic void
65251839Sbaptprint_item(ALL_DATA * data,
66251839Sbapt	   WINDOW *win,
67251839Sbapt	   DIALOG_LISTITEM * item,
68251839Sbapt	   int choice,
69251839Sbapt	   int selected)
70251839Sbapt{
71251839Sbapt    chtype save = dlg_get_attrs(win);
72251839Sbapt    int i;
73251839Sbapt    bool both = (!dialog_vars.no_tags && !dialog_vars.no_items);
74251839Sbapt    bool first = TRUE;
75251839Sbapt    int climit = (data->item_x - data->check_x - 1);
76251839Sbapt    const char *show = (dialog_vars.no_items
77251839Sbapt			? item->name
78251839Sbapt			: item->text);
79251839Sbapt
80251839Sbapt    /* Clear 'residue' of last item */
81251839Sbapt    (void) wattrset(win, menubox_attr);
82251839Sbapt    (void) wmove(win, choice, 0);
83251839Sbapt    for (i = 0; i < getmaxx(win); i++)
84251839Sbapt	(void) waddch(win, ' ');
85251839Sbapt
86251839Sbapt    (void) wmove(win, choice, data->check_x);
87251839Sbapt    (void) wattrset(win, menubox_attr);
88251839Sbapt
89251839Sbapt    if (both) {
90251839Sbapt	dlg_print_listitem(win, item->name, climit, first, selected);
91251839Sbapt	(void) waddch(win, ' ');
92251839Sbapt	first = FALSE;
93251839Sbapt    }
94251839Sbapt
95251839Sbapt    (void) wmove(win, choice, data->item_x);
96251839Sbapt    climit = (getmaxx(win) - data->item_x + 1);
97251839Sbapt    dlg_print_listitem(win, show, climit, first, selected);
98251839Sbapt
99251839Sbapt    if (selected) {
100251839Sbapt	dlg_item_help(item->help);
101251839Sbapt    }
102251839Sbapt    (void) wattrset(win, save);
103251839Sbapt}
104251839Sbapt
105251839Sbapt/*
106251839Sbapt * Prints either the left (unselected) or right (selected) list.
107251839Sbapt */
108251839Sbaptstatic void
109251839Sbaptprint_1_list(ALL_DATA * data,
110251839Sbapt	     int choice,
111251839Sbapt	     int selected)
112251839Sbapt{
113251839Sbapt    MY_DATA *moi = data->list + selected;
114251839Sbapt    WINDOW *win = moi->win;
115251839Sbapt    int i, j;
116251839Sbapt    int last = 0;
117251839Sbapt    int max_rows = getmaxy(win);
118251839Sbapt
119251839Sbapt    for (i = j = 0; j < max_rows; i++) {
120251839Sbapt	int ii = i + moi->top_index;
121251839Sbapt	if (ii >= data->item_no) {
122251839Sbapt	    break;
123251839Sbapt	} else if (!(selected ^ (data->items[ii].state != 0))) {
124251839Sbapt	    print_item(data,
125251839Sbapt		       win,
126251839Sbapt		       &data->items[ii],
127251839Sbapt		       j, ii == choice);
128251839Sbapt	    last = ++j;
129251839Sbapt	}
130251839Sbapt    }
131251839Sbapt    if (wmove(win, last, 0) != ERR)
132251839Sbapt	wclrtobot(win);
133251839Sbapt    (void) wnoutrefresh(win);
134251839Sbapt}
135251839Sbapt
136251839Sbapt/*
137251839Sbapt * Return the previous item from the list, staying in the same column.  If no
138251839Sbapt * further movement is possible, return the same choice as given.
139251839Sbapt */
140251839Sbaptstatic int
141251839Sbaptprev_item(ALL_DATA * data, int choice, int selected)
142251839Sbapt{
143251839Sbapt    int result = choice;
144251839Sbapt    int n;
145251839Sbapt
146251839Sbapt    for (n = choice - 1; n >= 0; --n) {
147251839Sbapt	if ((data->items[n].state != 0) == selected) {
148251839Sbapt	    result = n;
149251839Sbapt	    break;
150251839Sbapt	}
151251839Sbapt    }
152251839Sbapt    return result;
153251839Sbapt}
154251839Sbapt
155251839Sbapt/*
156251839Sbapt * Return true if the given choice is on the first page in the current column.
157251839Sbapt */
158251839Sbaptstatic bool
159251839Sbaptstop_prev(ALL_DATA * data, int choice, int selected)
160251839Sbapt{
161251839Sbapt    return (prev_item(data, choice, selected) == choice);
162251839Sbapt}
163251839Sbapt
164251839Sbaptstatic bool
165251839Sbaptcheck_hotkey(DIALOG_LISTITEM * items, int choice, int selected)
166251839Sbapt{
167251839Sbapt    bool result = FALSE;
168251839Sbapt
169251839Sbapt    if ((items[choice].state != 0) == selected) {
170251839Sbapt	if (dlg_match_char(dlg_last_getc(),
171251839Sbapt			   (dialog_vars.no_tags
172251839Sbapt			    ? items[choice].text
173251839Sbapt			    : items[choice].name))) {
174251839Sbapt	    result = TRUE;
175251839Sbapt	}
176251839Sbapt    }
177251839Sbapt    return result;
178251839Sbapt}
179251839Sbapt
180251839Sbapt/*
181251839Sbapt * Return the next item from the list, staying in the same column.  If no
182251839Sbapt * further movement is possible, return the same choice as given.
183251839Sbapt */
184251839Sbaptstatic int
185251839Sbaptnext_item(ALL_DATA * data, int choice, int selected)
186251839Sbapt{
187251839Sbapt    int result = choice;
188251839Sbapt    int n;
189251839Sbapt
190251839Sbapt    for (n = choice + 1; n < data->item_no; ++n) {
191251839Sbapt	if ((data->items[n].state != 0) == selected) {
192251839Sbapt	    result = n;
193251839Sbapt	    break;
194251839Sbapt	}
195251839Sbapt    }
196251839Sbapt    dlg_trace_msg("next_item(%d) ->%d\n", choice, result);
197251839Sbapt    return result;
198251839Sbapt}
199251839Sbapt
200251839Sbapt/*
201251839Sbapt * Translate a choice from items[] to a row-number in an unbounded column,
202251839Sbapt * starting at zero.
203251839Sbapt */
204251839Sbaptstatic int
205251839Sbaptindex2row(ALL_DATA * data, int choice, int selected)
206251839Sbapt{
207251839Sbapt    int result = -1;
208251839Sbapt    int n;
209251839Sbapt    for (n = 0; n < data->item_no; ++n) {
210251839Sbapt	if ((data->items[n].state != 0) == selected) {
211251839Sbapt	    ++result;
212251839Sbapt	}
213251839Sbapt	if (n == choice)
214251839Sbapt	    break;
215251839Sbapt    }
216251839Sbapt    return result;
217251839Sbapt}
218251839Sbapt
219251839Sbapt/*
220251839Sbapt * Return the first choice from items[] for the given column.
221251839Sbapt */
222251839Sbaptstatic int
223251839Sbaptfirst_item(ALL_DATA * data, int selected)
224251839Sbapt{
225251839Sbapt    int result = -1;
226251839Sbapt    int n;
227251839Sbapt
228251839Sbapt    for (n = 0; n < data->item_no; ++n) {
229251839Sbapt	if ((data->items[n].state != 0) == selected) {
230251839Sbapt	    result = n;
231251839Sbapt	    break;
232251839Sbapt	}
233251839Sbapt    }
234251839Sbapt    return result;
235251839Sbapt}
236251839Sbapt
237251839Sbapt/*
238251839Sbapt * Return the last choice from items[] for the given column.
239251839Sbapt */
240251839Sbaptstatic int
241251839Sbaptlast_item(ALL_DATA * data, int selected)
242251839Sbapt{
243251839Sbapt    int result = -1;
244251839Sbapt    int n;
245251839Sbapt
246251839Sbapt    for (n = data->item_no - 1; n >= 0; --n) {
247251839Sbapt	if ((data->items[n].state != 0) == selected) {
248251839Sbapt	    result = n;
249251839Sbapt	    break;
250251839Sbapt	}
251251839Sbapt    }
252251839Sbapt    return result;
253251839Sbapt}
254251839Sbapt
255251839Sbapt/*
256251839Sbapt * Convert a row-number back to an item number, i.e., index into items[].
257251839Sbapt */
258251839Sbaptstatic int
259251839Sbaptrow2index(ALL_DATA * data, int row, int selected)
260251839Sbapt{
261251839Sbapt    int result = -1;
262251839Sbapt    int n;
263251839Sbapt    for (n = 0; n < data->item_no; ++n) {
264251839Sbapt	if ((data->items[n].state != 0) == selected) {
265251839Sbapt	    if (row-- <= 0) {
266251839Sbapt		result = n;
267251839Sbapt		break;
268251839Sbapt	    }
269251839Sbapt	}
270251839Sbapt    }
271251839Sbapt    return result;
272251839Sbapt}
273251839Sbapt
274251839Sbaptstatic int
275251839Sbaptskip_rows(ALL_DATA * data, int row, int skip, int selected)
276251839Sbapt{
277251839Sbapt    int choice = row2index(data, row, selected);
278251839Sbapt    int result = row;
279251839Sbapt    int n;
280251839Sbapt    if (skip > 0) {
281251839Sbapt	for (n = choice + 1; n < data->item_no; ++n) {
282251839Sbapt	    if ((data->items[n].state != 0) == selected) {
283251839Sbapt		++result;
284251839Sbapt		if (--skip <= 0)
285251839Sbapt		    break;
286251839Sbapt	    }
287251839Sbapt	}
288251839Sbapt    } else if (skip < 0) {
289251839Sbapt	for (n = choice - 1; n >= 0; --n) {
290251839Sbapt	    if ((data->items[n].state != 0) == selected) {
291251839Sbapt		--result;
292251839Sbapt		if (++skip >= 0)
293251839Sbapt		    break;
294251839Sbapt	    }
295251839Sbapt	}
296251839Sbapt    }
297251839Sbapt    return result;
298251839Sbapt}
299251839Sbapt
300251839Sbapt/*
301251839Sbapt * Find the closest item in the given column starting with the given choice.
302251839Sbapt */
303251839Sbaptstatic int
304251839Sbaptclosest_item(ALL_DATA * data, int choice, int selected)
305251839Sbapt{
306251839Sbapt    int prev = choice;
307251839Sbapt    int next = choice;
308251839Sbapt    int result = choice;
309251839Sbapt    int n;
310251839Sbapt
311251839Sbapt    for (n = choice; n >= 0; --n) {
312251839Sbapt	if ((data->items[n].state != 0) == selected) {
313251839Sbapt	    prev = n;
314251839Sbapt	    break;
315251839Sbapt	}
316251839Sbapt    }
317251839Sbapt    for (n = choice; n < data->item_no; ++n) {
318251839Sbapt	if ((data->items[n].state != 0) == selected) {
319251839Sbapt	    next = n;
320251839Sbapt	    break;
321251839Sbapt	}
322251839Sbapt    }
323251839Sbapt    if (prev != choice) {
324251839Sbapt	result = prev;
325251839Sbapt	if (next != choice) {
326251839Sbapt	    if ((choice - prev) > (next - choice)) {
327251839Sbapt		result = next;
328251839Sbapt	    }
329251839Sbapt	}
330251839Sbapt    } else if (next != choice) {
331251839Sbapt	result = next;
332251839Sbapt    }
333251839Sbapt    return result;
334251839Sbapt}
335251839Sbapt
336251839Sbaptstatic void
337251839Sbaptprint_both(ALL_DATA * data,
338251839Sbapt	   int choice)
339251839Sbapt{
340251839Sbapt    int selected;
341251839Sbapt    int cur_y, cur_x;
342251839Sbapt    WINDOW *dialog = wgetparent(data->list[0].win);
343251839Sbapt
344251839Sbapt    getyx(dialog, cur_y, cur_x);
345251839Sbapt    for (selected = 0; selected < 2; ++selected) {
346251839Sbapt	MY_DATA *moi = data->list + selected;
347251839Sbapt	WINDOW *win = moi->win;
348251839Sbapt	int thumb_top = index2row(data, moi->top_index, selected);
349251839Sbapt	int thumb_max = index2row(data, -1, selected);
350251839Sbapt	int thumb_end = thumb_top + getmaxy(win);
351251839Sbapt
352251839Sbapt	print_1_list(data, choice, selected);
353251839Sbapt
354251839Sbapt	dlg_mouse_setcode(selected * KEY_MAX);
355251839Sbapt	dlg_draw_scrollbar(dialog,
356251839Sbapt			   (long) (moi->top_index),
357251839Sbapt			   (long) (thumb_top),
358251839Sbapt			   (long) MIN(thumb_end, thumb_max),
359251839Sbapt			   (long) thumb_max,
360251839Sbapt			   moi->box_x + data->check_x,
361251839Sbapt			   moi->box_x + getmaxx(win),
362251839Sbapt			   moi->box_y,
363251839Sbapt			   moi->box_y + getmaxy(win) + 1,
364251839Sbapt			   menubox_border2_attr,
365251839Sbapt			   menubox_border_attr);
366251839Sbapt    }
367251839Sbapt    (void) wmove(dialog, cur_y, cur_x);
368251839Sbapt    dlg_mouse_setcode(0);
369251839Sbapt}
370251839Sbapt
371251839Sbaptstatic void
372251839Sbaptset_top_item(ALL_DATA * data, int value, int selected)
373251839Sbapt{
374251839Sbapt    if (value != data->list[selected].top_index) {
375251839Sbapt	dlg_trace_msg("set top of %s column to %d\n",
376251839Sbapt		      selected ? "right" : "left",
377251839Sbapt		      value);
378251839Sbapt	data->list[selected].top_index = value;
379251839Sbapt    }
380251839Sbapt}
381251839Sbapt
382251839Sbapt/*
383251839Sbapt * Adjust the top-index as needed to ensure that it and the given item are
384251839Sbapt * visible.
385251839Sbapt */
386251839Sbaptstatic void
387251839Sbaptfix_top_item(ALL_DATA * data, int cur_item, int selected)
388251839Sbapt{
389251839Sbapt    int top_item = data->list[selected].top_index;
390251839Sbapt    int cur_row = index2row(data, cur_item, selected);
391251839Sbapt    int top_row = index2row(data, top_item, selected);
392251839Sbapt
393251839Sbapt    if (cur_row < top_row) {
394251839Sbapt	top_item = cur_item;
395251839Sbapt    } else if ((cur_row - top_row) > data->use_height) {
396251839Sbapt	top_item = row2index(data, cur_row + 1 - data->use_height, selected);
397251839Sbapt    }
398251839Sbapt    if (cur_row < data->use_height) {
399251839Sbapt	top_item = row2index(data, 0, selected);
400251839Sbapt    }
401251839Sbapt    dlg_trace_msg("fix_top_item(cur_item %d, selected %d) ->top_item %d\n",
402251839Sbapt		  cur_item, selected, top_item);
403251839Sbapt    set_top_item(data, top_item, selected);
404251839Sbapt}
405251839Sbapt
406251839Sbapt/*
407251839Sbapt * This is an alternate interface to 'buildlist' which allows the application
408251839Sbapt * to read the list item states back directly without putting them in the
409251839Sbapt * output buffer.
410251839Sbapt */
411251839Sbaptint
412251839Sbaptdlg_buildlist(const char *title,
413251839Sbapt	      const char *cprompt,
414251839Sbapt	      int height,
415251839Sbapt	      int width,
416251839Sbapt	      int list_height,
417251839Sbapt	      int item_no,
418251839Sbapt	      DIALOG_LISTITEM * items,
419251839Sbapt	      const char *states,
420251839Sbapt	      int order_mode,
421251839Sbapt	      int *current_item)
422251839Sbapt{
423251839Sbapt    /* *INDENT-OFF* */
424251839Sbapt    static DLG_KEYS_BINDING binding[] = {
425251839Sbapt	HELPKEY_BINDINGS,
426251839Sbapt	ENTERKEY_BINDINGS,
427251839Sbapt	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
428251839Sbapt	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
429251839Sbapt	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
430251839Sbapt	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
431251839Sbapt	DLG_KEYS_DATA( DLGK_ITEM_FIRST, KEY_HOME ),
432251839Sbapt	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_END ),
433251839Sbapt	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_LL ),
434251839Sbapt	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	'+' ),
435251839Sbapt	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	KEY_DOWN ),
436251839Sbapt	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  CHR_NEXT ),
437251839Sbapt	DLG_KEYS_DATA( DLGK_ITEM_PREV,	'-' ),
438251839Sbapt	DLG_KEYS_DATA( DLGK_ITEM_PREV,	KEY_UP ),
439251839Sbapt	DLG_KEYS_DATA( DLGK_ITEM_PREV,  CHR_PREVIOUS ),
440251839Sbapt	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ),
441251839Sbapt	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE) ),
442251839Sbapt	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE+KEY_MAX) ),
443251839Sbapt	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ),
444251839Sbapt	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE) ),
445251839Sbapt	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE+KEY_MAX) ),
446251839Sbapt	DLG_KEYS_DATA( DLGK_GRID_LEFT,	KEY_LEFTCOL ),
447251839Sbapt	DLG_KEYS_DATA( DLGK_GRID_RIGHT,	KEY_RIGHTCOL ),
448251839Sbapt	END_KEYS_BINDING
449251839Sbapt    };
450251839Sbapt    /* *INDENT-ON* */
451251839Sbapt
452251839Sbapt#ifdef KEY_RESIZE
453251839Sbapt    int old_height = height;
454251839Sbapt    int old_width = width;
455251839Sbapt#endif
456251839Sbapt    ALL_DATA all;
457251839Sbapt    MY_DATA *data = all.list;
458251839Sbapt    int i, j, k, key2, found, x, y, cur_x, cur_y;
459251839Sbapt    int key = 0, fkey;
460251839Sbapt    bool save_visit = dialog_state.visit_items;
461251839Sbapt    int button;
462251839Sbapt    int cur_item;
463251839Sbapt    int was_mouse;
464251839Sbapt    int name_width, text_width, full_width, list_width;
465251839Sbapt    int result = DLG_EXIT_UNKNOWN;
466251839Sbapt    int num_states;
467251839Sbapt    bool first = TRUE;
468251839Sbapt    WINDOW *dialog;
469251839Sbapt    char *prompt = dlg_strclone(cprompt);
470251839Sbapt    const char **buttons = dlg_ok_labels();
471251839Sbapt    const char *widget_name = "buildlist";
472251839Sbapt
473251839Sbapt    (void) order_mode;
474251839Sbapt
475251839Sbapt    /*
476251839Sbapt     * Unlike other uses of --visit-items, we have two windows to visit.
477251839Sbapt     */
478251839Sbapt    if (dialog_state.visit_cols)
479251839Sbapt	dialog_state.visit_cols = 2;
480251839Sbapt
481251839Sbapt    memset(&all, 0, sizeof(all));
482251839Sbapt    all.items = items;
483251839Sbapt    all.item_no = item_no;
484251839Sbapt
485251839Sbapt    if (dialog_vars.default_item != 0) {
486251839Sbapt	cur_item = dlg_default_listitem(items);
487251839Sbapt    } else {
488251839Sbapt	if ((cur_item = first_item(&all, 0)) < 0)
489251839Sbapt	    cur_item = first_item(&all, 1);
490251839Sbapt    }
491251839Sbapt    button = (dialog_state.visit_items
492251839Sbapt	      ? (items[cur_item].state ? sRIGHT : sLEFT)
493251839Sbapt	      : dlg_default_button());
494251839Sbapt
495251839Sbapt    dlg_does_output();
496251839Sbapt    dlg_tab_correct_str(prompt);
497251839Sbapt
498251839Sbapt#ifdef KEY_RESIZE
499251839Sbapt  retry:
500251839Sbapt#endif
501251839Sbapt
502251839Sbapt    all.use_height = list_height;
503251839Sbapt    all.use_width = (2 * (dlg_calc_list_width(item_no, items)
504251839Sbapt			  + 4
505251839Sbapt			  + 2 * MARGIN)
506251839Sbapt		     + 1);
507251839Sbapt    all.use_width = MAX(26, all.use_width);
508251839Sbapt    if (all.use_height == 0) {
509251839Sbapt	/* calculate height without items (4) */
510251839Sbapt	dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, all.use_width);
511251839Sbapt	dlg_calc_listh(&height, &all.use_height, item_no);
512251839Sbapt    } else {
513251839Sbapt	dlg_auto_size(title, prompt,
514251839Sbapt		      &height, &width,
515251839Sbapt		      MIN_HIGH + all.use_height, all.use_width);
516251839Sbapt    }
517251839Sbapt    dlg_button_layout(buttons, &width);
518251839Sbapt    dlg_print_size(height, width);
519251839Sbapt    dlg_ctl_size(height, width);
520251839Sbapt
521251839Sbapt    /* we need at least two states */
522251839Sbapt    if (states == 0 || strlen(states) < 2)
523251839Sbapt	states = " *";
524251839Sbapt    num_states = (int) strlen(states);
525251839Sbapt
526251839Sbapt    x = dlg_box_x_ordinate(width);
527251839Sbapt    y = dlg_box_y_ordinate(height);
528251839Sbapt
529251839Sbapt    dialog = dlg_new_window(height, width, y, x);
530251839Sbapt    dlg_register_window(dialog, widget_name, binding);
531251839Sbapt    dlg_register_buttons(dialog, widget_name, buttons);
532251839Sbapt
533251839Sbapt    dlg_mouse_setbase(all.base_x = x, all.base_y = y);
534251839Sbapt
535251839Sbapt    dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
536251839Sbapt    dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
537251839Sbapt    dlg_draw_title(dialog, title);
538251839Sbapt
539251839Sbapt    (void) wattrset(dialog, dialog_attr);
540251839Sbapt    dlg_print_autowrap(dialog, prompt, height, width);
541251839Sbapt
542251839Sbapt    list_width = (width - 6 * MARGIN - 2) / 2;
543251839Sbapt    getyx(dialog, cur_y, cur_x);
544251839Sbapt    data[0].box_y = cur_y + 1;
545251839Sbapt    data[0].box_x = MARGIN + 1;
546251839Sbapt    data[1].box_y = cur_y + 1;
547251839Sbapt    data[1].box_x = data[0].box_x + 1 + 2 * MARGIN + list_width;
548251839Sbapt
549251839Sbapt    /*
550251839Sbapt     * After displaying the prompt, we know how much space we really have.
551251839Sbapt     * Limit the list to avoid overwriting the ok-button.
552251839Sbapt     */
553251839Sbapt    if (all.use_height + MIN_HIGH > height - cur_y)
554251839Sbapt	all.use_height = height - MIN_HIGH - cur_y;
555251839Sbapt    if (all.use_height <= 0)
556251839Sbapt	all.use_height = 1;
557251839Sbapt
558251839Sbapt    for (k = 0; k < 2; ++k) {
559251839Sbapt	/* create new window for the list */
560251839Sbapt	data[k].win = dlg_sub_window(dialog, all.use_height, list_width,
561251839Sbapt				     y + data[k].box_y + 1,
562251839Sbapt				     x + data[k].box_x + 1);
563251839Sbapt
564251839Sbapt	/* draw a box around the list items */
565251839Sbapt	dlg_draw_box(dialog, data[k].box_y, data[k].box_x,
566251839Sbapt		     all.use_height + 2 * MARGIN,
567251839Sbapt		     list_width + 2 * MARGIN,
568251839Sbapt		     menubox_border_attr, menubox_border2_attr);
569251839Sbapt    }
570251839Sbapt
571251839Sbapt    text_width = 0;
572251839Sbapt    name_width = 0;
573251839Sbapt    /* Find length of longest item to center buildlist */
574251839Sbapt    for (i = 0; i < item_no; i++) {
575251839Sbapt	text_width = MAX(text_width, dlg_count_columns(items[i].text));
576251839Sbapt	name_width = MAX(name_width, dlg_count_columns(items[i].name));
577251839Sbapt    }
578251839Sbapt
579251839Sbapt    /* If the name+text is wider than the list is allowed, then truncate
580251839Sbapt     * one or both of them.  If the name is no wider than 1/4 of the list,
581251839Sbapt     * leave it intact.
582251839Sbapt     */
583251839Sbapt    all.use_width = (list_width - 6 * MARGIN);
584251839Sbapt    if (dialog_vars.no_tags && !dialog_vars.no_items) {
585251839Sbapt	full_width = MIN(all.use_width, text_width);
586251839Sbapt    } else if (dialog_vars.no_items) {
587251839Sbapt	full_width = MIN(all.use_width, name_width);
588251839Sbapt    } else {
589251839Sbapt	if (text_width >= 0
590251839Sbapt	    && name_width >= 0
591251839Sbapt	    && all.use_width > 0
592251839Sbapt	    && text_width + name_width > all.use_width) {
593251839Sbapt	    int need = (int) (0.25 * all.use_width);
594251839Sbapt	    if (name_width > need) {
595251839Sbapt		int want = (int) (all.use_width * ((double) name_width) /
596251839Sbapt				  (text_width + name_width));
597251839Sbapt		name_width = (want > need) ? want : need;
598251839Sbapt	    }
599251839Sbapt	    text_width = all.use_width - name_width;
600251839Sbapt	}
601251839Sbapt	full_width = text_width + name_width;
602251839Sbapt    }
603251839Sbapt
604251839Sbapt    all.check_x = (all.use_width - full_width) / 2;
605251839Sbapt    all.item_x = ((dialog_vars.no_tags
606251839Sbapt		   ? 0
607251839Sbapt		   : (dialog_vars.no_items
608251839Sbapt		      ? 0
609251839Sbapt		      : (name_width + 2)))
610251839Sbapt		  + all.check_x);
611251839Sbapt
612251839Sbapt    /* ensure we are scrolled to show the current choice */
613251839Sbapt    j = MIN(all.use_height, item_no);
614251839Sbapt    for (i = 0; i < 2; ++i) {
615251839Sbapt	int top_item = 0;
616251839Sbapt	if ((items[cur_item].state != 0) == i) {
617251839Sbapt	    top_item = cur_item - j + 1;
618251839Sbapt	    if (top_item < 0)
619251839Sbapt		top_item = 0;
620251839Sbapt	    set_top_item(&all, top_item, i);
621251839Sbapt	} else {
622251839Sbapt	    set_top_item(&all, 0, i);
623251839Sbapt	}
624251839Sbapt    }
625251839Sbapt
626251839Sbapt    /* register the new window, along with its borders */
627251839Sbapt    for (i = 0; i < 2; ++i) {
628251839Sbapt	dlg_mouse_mkbigregion(data[i].box_y + 1,
629251839Sbapt			      data[i].box_x,
630251839Sbapt			      all.use_height,
631251839Sbapt			      list_width + 2,
632251839Sbapt			      2 * KEY_MAX + (i * (1 + all.use_height)),
633251839Sbapt			      1, 1, 1 /* by lines */ );
634251839Sbapt    }
635251839Sbapt
636251839Sbapt    dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
637251839Sbapt
638251839Sbapt    while (result == DLG_EXIT_UNKNOWN) {
639251839Sbapt	int which = (items[cur_item].state != 0);
640251839Sbapt	MY_DATA *moi = data + which;
641251839Sbapt	int at_top = index2row(&all, moi->top_index, which);
642251839Sbapt	int at_end = index2row(&all, -1, which);
643251839Sbapt	int at_bot = skip_rows(&all, at_top, all.use_height, which);
644251839Sbapt
645251839Sbapt	dlg_trace_msg("\t** state %d:%d top %d (%d:%d:%d) %d\n",
646251839Sbapt		      cur_item, item_no - 1,
647251839Sbapt		      moi->top_index,
648251839Sbapt		      at_top, at_bot, at_end,
649251839Sbapt		      which);
650251839Sbapt
651251839Sbapt	if (first) {
652251839Sbapt	    print_both(&all, cur_item);
653251839Sbapt	    dlg_trace_win(dialog);
654251839Sbapt	    first = FALSE;
655251839Sbapt	}
656251839Sbapt
657251839Sbapt	if (button < 0) {	/* --visit-items */
658251839Sbapt	    int cur_row = index2row(&all, cur_item, which);
659251839Sbapt	    cur_y = (data[which].box_y
660251839Sbapt		     + cur_row
661251839Sbapt		     + 1);
662251839Sbapt	    if (at_top > 0)
663251839Sbapt		cur_y -= at_top;
664251839Sbapt	    cur_x = (data[which].box_x
665251839Sbapt		     + all.check_x + 1);
666251839Sbapt	    dlg_trace_msg("\t...visit row %d (%d,%d)\n", cur_row, cur_y, cur_x);
667251839Sbapt	    wmove(dialog, cur_y, cur_x);
668251839Sbapt	}
669251839Sbapt
670251839Sbapt	key = dlg_mouse_wgetch(dialog, &fkey);
671251839Sbapt	if (dlg_result_key(key, fkey, &result))
672251839Sbapt	    break;
673251839Sbapt
674251839Sbapt	was_mouse = (fkey && is_DLGK_MOUSE(key));
675251839Sbapt	if (was_mouse)
676251839Sbapt	    key -= M_EVENT;
677251839Sbapt
678251839Sbapt	if (!was_mouse) {
679251839Sbapt	    ;
680251839Sbapt	} else if (key >= 2 * KEY_MAX) {
681251839Sbapt	    i = (key - 2 * KEY_MAX) % (1 + all.use_height);
682251839Sbapt	    j = (key - 2 * KEY_MAX) / (1 + all.use_height);
683251839Sbapt	    k = row2index(&all, i + at_top, j);
684251839Sbapt	    dlg_trace_msg("MOUSE column %d, row %d ->item %d\n", j, i, k);
685251839Sbapt	    if (k >= 0 && j < 2) {
686251839Sbapt		if (j != which) {
687251839Sbapt		    /*
688251839Sbapt		     * Mouse click was in the other column.
689251839Sbapt		     */
690251839Sbapt		    moi = data + j;
691251839Sbapt		    fix_top_item(&all, k, j);
692251839Sbapt		}
693251839Sbapt		which = j;
694251839Sbapt		at_top = index2row(&all, moi->top_index, which);
695251839Sbapt		at_bot = skip_rows(&all, at_top, all.use_height, which);
696251839Sbapt		cur_item = k;
697251839Sbapt		print_both(&all, cur_item);
698251839Sbapt		key = KEY_TOGGLE;	/* force the selected item to toggle */
699251839Sbapt	    } else {
700251839Sbapt		beep();
701251839Sbapt		continue;
702251839Sbapt	    }
703251839Sbapt	    fkey = FALSE;
704251839Sbapt	} else if (key >= KEY_MIN) {
705251839Sbapt	    if (key > KEY_MAX) {
706251839Sbapt		if (which == 0) {
707251839Sbapt		    key = KEY_RIGHTCOL;		/* switch to right-column */
708251839Sbapt		    fkey = FALSE;
709251839Sbapt		} else {
710251839Sbapt		    key -= KEY_MAX;
711251839Sbapt		}
712251839Sbapt	    } else {
713251839Sbapt		if (which == 1) {
714251839Sbapt		    key = KEY_LEFTCOL;	/* switch to left-column */
715251839Sbapt		    fkey = FALSE;
716251839Sbapt		}
717251839Sbapt	    }
718251839Sbapt	    key = dlg_lookup_key(dialog, key, &fkey);
719251839Sbapt	}
720251839Sbapt
721251839Sbapt	/*
722251839Sbapt	 * A space toggles the item status.  Normally we put the cursor on
723251839Sbapt	 * the next available item in the same column.  But if there are no
724251839Sbapt	 * more items in the column, move the cursor to the other column.
725251839Sbapt	 */
726251839Sbapt	if (key == KEY_TOGGLE) {
727251839Sbapt	    int new_choice;
728251839Sbapt	    int new_state = items[cur_item].state + 1;
729251839Sbapt
730251839Sbapt	    if ((new_choice = next_item(&all, cur_item, which)) == cur_item) {
731251839Sbapt		new_choice = prev_item(&all, cur_item, which);
732251839Sbapt	    }
733251839Sbapt	    dlg_trace_msg("cur_item %d, new_choice:%d\n", cur_item, new_choice);
734251839Sbapt	    if (new_state >= num_states)
735251839Sbapt		new_state = 0;
736251839Sbapt
737251839Sbapt	    items[cur_item].state = new_state;
738251839Sbapt	    if (cur_item == moi->top_index) {
739251839Sbapt		set_top_item(&all, new_choice, which);
740251839Sbapt	    }
741251839Sbapt
742251839Sbapt	    if (new_choice >= 0) {
743251839Sbapt		fix_top_item(&all, cur_item, !which);
744251839Sbapt		cur_item = new_choice;
745251839Sbapt	    }
746251839Sbapt	    print_both(&all, cur_item);
747251839Sbapt	    dlg_trace_win(dialog);
748251839Sbapt	    continue;		/* wait for another key press */
749251839Sbapt	}
750251839Sbapt
751251839Sbapt	/*
752251839Sbapt	 * Check if key pressed matches first character of any item tag in
753251839Sbapt	 * list.  If there is more than one match, we will cycle through
754251839Sbapt	 * each one as the same key is pressed repeatedly.
755251839Sbapt	 */
756251839Sbapt	found = FALSE;
757251839Sbapt	if (!fkey) {
758251839Sbapt	    if (button < 0 || !dialog_state.visit_items) {
759251839Sbapt		for (j = cur_item + 1; j < item_no; j++) {
760251839Sbapt		    if (check_hotkey(items, j, which)) {
761251839Sbapt			found = TRUE;
762251839Sbapt			i = j;
763251839Sbapt			break;
764251839Sbapt		    }
765251839Sbapt		}
766251839Sbapt		if (!found) {
767251839Sbapt		    for (j = 0; j <= cur_item; j++) {
768251839Sbapt			if (check_hotkey(items, j, which)) {
769251839Sbapt			    found = TRUE;
770251839Sbapt			    i = j;
771251839Sbapt			    break;
772251839Sbapt			}
773251839Sbapt		    }
774251839Sbapt		}
775251839Sbapt		if (found)
776251839Sbapt		    dlg_flush_getc();
777251839Sbapt	    } else if ((j = dlg_char_to_button(key, buttons)) >= 0) {
778251839Sbapt		button = j;
779251839Sbapt		ungetch('\n');
780251839Sbapt		continue;
781251839Sbapt	    }
782251839Sbapt	}
783251839Sbapt
784251839Sbapt	/*
785251839Sbapt	 * A single digit (1-9) positions the selection to that line in the
786251839Sbapt	 * current screen.
787251839Sbapt	 */
788251839Sbapt	if (!found
789251839Sbapt	    && (key <= '9')
790251839Sbapt	    && (key > '0')
791251839Sbapt	    && (key - '1' < at_bot)) {
792251839Sbapt	    found = TRUE;
793251839Sbapt	    i = key - '1';
794251839Sbapt	}
795251839Sbapt
796251839Sbapt	if (!found && fkey) {
797251839Sbapt	    switch (key) {
798251839Sbapt	    case DLGK_FIELD_PREV:
799251839Sbapt		if ((button == sRIGHT) && dialog_state.visit_items) {
800251839Sbapt		    key = DLGK_GRID_LEFT;
801251839Sbapt		    button = sLEFT;
802251839Sbapt		} else {
803251839Sbapt		    button = dlg_prev_button(buttons, button);
804251839Sbapt		    dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
805251839Sbapt				     FALSE, width);
806251839Sbapt		    if (button == sRIGHT) {
807251839Sbapt			key = DLGK_GRID_RIGHT;
808251839Sbapt		    } else {
809251839Sbapt			continue;
810251839Sbapt		    }
811251839Sbapt		}
812251839Sbapt		break;
813251839Sbapt	    case DLGK_FIELD_NEXT:
814251839Sbapt		if ((button == sLEFT) && dialog_state.visit_items) {
815251839Sbapt		    key = DLGK_GRID_RIGHT;
816251839Sbapt		    button = sRIGHT;
817251839Sbapt		} else {
818251839Sbapt		    button = dlg_next_button(buttons, button);
819251839Sbapt		    dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
820251839Sbapt				     FALSE, width);
821251839Sbapt		    if (button == sLEFT) {
822251839Sbapt			key = DLGK_GRID_LEFT;
823251839Sbapt		    } else {
824251839Sbapt			continue;
825251839Sbapt		    }
826251839Sbapt		}
827251839Sbapt		break;
828251839Sbapt	    }
829251839Sbapt	}
830251839Sbapt
831251839Sbapt	if (!found && fkey) {
832251839Sbapt	    i = cur_item;
833251839Sbapt	    found = TRUE;
834251839Sbapt	    switch (key) {
835251839Sbapt	    case DLGK_GRID_LEFT:
836251839Sbapt		i = closest_item(&all, cur_item, 0);
837251839Sbapt		fix_top_item(&all, i, 0);
838251839Sbapt		break;
839251839Sbapt	    case DLGK_GRID_RIGHT:
840251839Sbapt		i = closest_item(&all, cur_item, 1);
841251839Sbapt		fix_top_item(&all, i, 1);
842251839Sbapt		break;
843251839Sbapt	    case DLGK_PAGE_PREV:
844251839Sbapt		if (cur_item > moi->top_index) {
845251839Sbapt		    i = moi->top_index;
846251839Sbapt		} else if (moi->top_index != 0) {
847251839Sbapt		    int temp = at_top;
848251839Sbapt		    if ((temp -= all.use_height) < 0)
849251839Sbapt			temp = 0;
850251839Sbapt		    i = row2index(&all, temp, which);
851251839Sbapt		}
852251839Sbapt		break;
853251839Sbapt	    case DLGK_PAGE_NEXT:
854251839Sbapt		if ((at_end - at_bot) < all.use_height) {
855251839Sbapt		    i = next_item(&all,
856251839Sbapt				  row2index(&all, at_end, which),
857251839Sbapt				  which);
858251839Sbapt		} else {
859251839Sbapt		    i = next_item(&all,
860251839Sbapt				  row2index(&all, at_bot, which),
861251839Sbapt				  which);
862251839Sbapt		    at_top = at_bot;
863251839Sbapt		    set_top_item(&all,
864251839Sbapt				 next_item(&all,
865251839Sbapt					   row2index(&all, at_top, which),
866251839Sbapt					   which),
867251839Sbapt				 which);
868251839Sbapt		    at_bot = skip_rows(&all, at_top, all.use_height, which);
869251839Sbapt		    at_bot = MIN(at_bot, at_end);
870251839Sbapt		}
871251839Sbapt		break;
872251839Sbapt	    case DLGK_ITEM_FIRST:
873251839Sbapt		i = first_item(&all, which);
874251839Sbapt		break;
875251839Sbapt	    case DLGK_ITEM_LAST:
876251839Sbapt		i = last_item(&all, which);
877251839Sbapt		break;
878251839Sbapt	    case DLGK_ITEM_PREV:
879251839Sbapt		i = prev_item(&all, cur_item, which);
880251839Sbapt		if (stop_prev(&all, cur_item, which))
881251839Sbapt		    continue;
882251839Sbapt		break;
883251839Sbapt	    case DLGK_ITEM_NEXT:
884251839Sbapt		i = next_item(&all, cur_item, which);
885251839Sbapt		break;
886251839Sbapt	    default:
887251839Sbapt		found = FALSE;
888251839Sbapt		break;
889251839Sbapt	    }
890251839Sbapt	}
891251839Sbapt
892251839Sbapt	if (found) {
893251839Sbapt	    if (i != cur_item) {
894251839Sbapt		int now_at = index2row(&all, i, which);
895251839Sbapt		int oops = item_no;
896251839Sbapt		int old_item;
897251839Sbapt
898251839Sbapt		dlg_trace_msg("<--CHOICE %d\n", i);
899251839Sbapt		dlg_trace_msg("<--topITM %d\n", moi->top_index);
900251839Sbapt		dlg_trace_msg("<--now_at %d\n", now_at);
901251839Sbapt		dlg_trace_msg("<--at_top %d\n", at_top);
902251839Sbapt		dlg_trace_msg("<--at_bot %d\n", at_bot);
903251839Sbapt
904251839Sbapt		if (now_at >= at_bot) {
905251839Sbapt		    while (now_at >= at_bot) {
906251839Sbapt			if ((at_bot - at_top) >= all.use_height) {
907251839Sbapt			    set_top_item(&all,
908251839Sbapt					 next_item(&all, moi->top_index, which),
909251839Sbapt					 which);
910251839Sbapt			}
911251839Sbapt			at_top = index2row(&all, moi->top_index, which);
912251839Sbapt			at_bot = skip_rows(&all, at_top, all.use_height, which);
913251839Sbapt
914251839Sbapt			dlg_trace_msg("...at_bot %d (now %d vs %d)\n",
915251839Sbapt				      at_bot, now_at, at_end);
916251839Sbapt			dlg_trace_msg("...topITM %d\n", moi->top_index);
917251839Sbapt			dlg_trace_msg("...at_top %d (diff %d)\n", at_top,
918251839Sbapt				      at_bot - at_top);
919251839Sbapt
920251839Sbapt			if (at_bot >= at_end) {
921251839Sbapt			    /*
922251839Sbapt			     * If we bumped into the end, move the top-item
923251839Sbapt			     * down by one line so that we can display the
924251839Sbapt			     * last item in the list.
925251839Sbapt			     */
926251839Sbapt			    if ((at_bot - at_top) > all.use_height) {
927251839Sbapt				set_top_item(&all,
928251839Sbapt					     next_item(&all, moi->top_index, which),
929251839Sbapt					     which);
930251839Sbapt			    } else if (at_top > 0 &&
931251839Sbapt				       (at_bot - at_top) >= all.use_height) {
932251839Sbapt				set_top_item(&all,
933251839Sbapt					     next_item(&all, moi->top_index, which),
934251839Sbapt					     which);
935251839Sbapt			    }
936251839Sbapt			    break;
937251839Sbapt			}
938251839Sbapt			if (--oops < 0) {
939251839Sbapt			    dlg_trace_msg("OOPS-forward\n");
940251839Sbapt			    break;
941251839Sbapt			}
942251839Sbapt		    }
943251839Sbapt		} else if (now_at < at_top) {
944251839Sbapt		    while (now_at < at_top) {
945251839Sbapt			old_item = moi->top_index;
946251839Sbapt			set_top_item(&all,
947251839Sbapt				     prev_item(&all, moi->top_index, which),
948251839Sbapt				     which);
949251839Sbapt			at_top = index2row(&all, moi->top_index, which);
950251839Sbapt
951251839Sbapt			dlg_trace_msg("...at_top %d (now %d)\n", at_top, now_at);
952251839Sbapt			dlg_trace_msg("...topITM %d\n", moi->top_index);
953251839Sbapt
954251839Sbapt			if (moi->top_index >= old_item)
955251839Sbapt			    break;
956251839Sbapt			if (at_top <= now_at)
957251839Sbapt			    break;
958251839Sbapt			if (--oops < 0) {
959251839Sbapt			    dlg_trace_msg("OOPS-backward\n");
960251839Sbapt			    break;
961251839Sbapt			}
962251839Sbapt		    }
963251839Sbapt		}
964251839Sbapt		dlg_trace_msg("-->now_at %d\n", now_at);
965251839Sbapt		cur_item = i;
966251839Sbapt		print_both(&all, cur_item);
967251839Sbapt	    }
968251839Sbapt	    dlg_trace_win(dialog);
969251839Sbapt	    continue;		/* wait for another key press */
970251839Sbapt	}
971251839Sbapt
972251839Sbapt	if (fkey) {
973251839Sbapt	    switch (key) {
974251839Sbapt	    case DLGK_ENTER:
975251839Sbapt		result = dlg_enter_buttoncode(button);
976251839Sbapt		break;
977251839Sbapt#ifdef KEY_RESIZE
978251839Sbapt	    case KEY_RESIZE:
979251839Sbapt		/* reset data */
980251839Sbapt		height = old_height;
981251839Sbapt		width = old_width;
982251839Sbapt		/* repaint */
983251839Sbapt		dlg_clear();
984251839Sbapt		dlg_del_window(dialog);
985251839Sbapt		refresh();
986251839Sbapt		dlg_mouse_free_regions();
987251839Sbapt		goto retry;
988251839Sbapt#endif
989251839Sbapt	    default:
990251839Sbapt		if (was_mouse) {
991251839Sbapt		    if ((key2 = dlg_ok_buttoncode(key)) >= 0) {
992251839Sbapt			result = key2;
993251839Sbapt			break;
994251839Sbapt		    }
995251839Sbapt		    beep();
996251839Sbapt		}
997251839Sbapt	    }
998251839Sbapt	} else {
999251839Sbapt	    beep();
1000251839Sbapt	}
1001251839Sbapt    }
1002251839Sbapt
1003251839Sbapt    dialog_state.visit_cols = save_visit;
1004251839Sbapt    dlg_del_window(dialog);
1005251839Sbapt    dlg_mouse_free_regions();
1006251839Sbapt    free(prompt);
1007251839Sbapt    *current_item = cur_item;
1008251839Sbapt    return result;
1009251839Sbapt}
1010251839Sbapt
1011251839Sbapt/*
1012251839Sbapt * Display a dialog box with a list of options that can be turned on or off
1013251839Sbapt */
1014251839Sbaptint
1015251839Sbaptdialog_buildlist(const char *title,
1016251839Sbapt		 const char *cprompt,
1017251839Sbapt		 int height,
1018251839Sbapt		 int width,
1019251839Sbapt		 int list_height,
1020251839Sbapt		 int item_no,
1021251839Sbapt		 char **items,
1022251839Sbapt		 int order_mode)
1023251839Sbapt{
1024251839Sbapt    int result;
1025251839Sbapt    int i, j;
1026251839Sbapt    DIALOG_LISTITEM *listitems;
1027251839Sbapt    bool separate_output = dialog_vars.separate_output;
1028251839Sbapt    bool show_status = FALSE;
1029251839Sbapt    int current = 0;
1030255852Sdteske    char *help_result;
1031251839Sbapt
1032251839Sbapt    listitems = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1);
1033251839Sbapt    assert_ptr(listitems, "dialog_buildlist");
1034251839Sbapt
1035251839Sbapt    for (i = j = 0; i < item_no; ++i) {
1036251839Sbapt	listitems[i].name = items[j++];
1037251839Sbapt	listitems[i].text = (dialog_vars.no_items
1038251839Sbapt			     ? dlg_strempty()
1039251839Sbapt			     : items[j++]);
1040251839Sbapt	listitems[i].state = !dlg_strcmp(items[j++], "on");
1041251839Sbapt	listitems[i].help = ((dialog_vars.item_help)
1042251839Sbapt			     ? items[j++]
1043251839Sbapt			     : dlg_strempty());
1044251839Sbapt    }
1045251839Sbapt    dlg_align_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
1046251839Sbapt
1047251839Sbapt    result = dlg_buildlist(title,
1048251839Sbapt			   cprompt,
1049251839Sbapt			   height,
1050251839Sbapt			   width,
1051251839Sbapt			   list_height,
1052251839Sbapt			   item_no,
1053251839Sbapt			   listitems,
1054251839Sbapt			   NULL,
1055251839Sbapt			   order_mode,
1056251839Sbapt			   &current);
1057251839Sbapt
1058251839Sbapt    switch (result) {
1059251839Sbapt    case DLG_EXIT_OK:		/* FALLTHRU */
1060251839Sbapt    case DLG_EXIT_EXTRA:
1061251839Sbapt	show_status = TRUE;
1062251839Sbapt	break;
1063251839Sbapt    case DLG_EXIT_HELP:
1064255852Sdteske	dlg_add_help_listitem(&result, &help_result, &listitems[current]);
1065255852Sdteske	if ((show_status = dialog_vars.help_status)) {
1066255852Sdteske	    if (separate_output) {
1067255852Sdteske		dlg_add_string(help_result);
1068255852Sdteske		dlg_add_separator();
1069251839Sbapt	    } else {
1070255852Sdteske		dlg_add_quoted(help_result);
1071251839Sbapt	    }
1072251839Sbapt	} else {
1073255852Sdteske	    dlg_add_string(help_result);
1074251839Sbapt	}
1075251839Sbapt	break;
1076251839Sbapt    }
1077251839Sbapt
1078251839Sbapt    if (show_status) {
1079251839Sbapt	for (i = 0; i < item_no; i++) {
1080251839Sbapt	    if (listitems[i].state) {
1081251839Sbapt		if (separate_output) {
1082251839Sbapt		    dlg_add_string(listitems[i].name);
1083251839Sbapt		    dlg_add_separator();
1084251839Sbapt		} else {
1085251839Sbapt		    if (dlg_need_separator())
1086251839Sbapt			dlg_add_separator();
1087251839Sbapt		    dlg_add_quoted(listitems[i].name);
1088251839Sbapt		}
1089251839Sbapt	    }
1090251839Sbapt	}
1091251839Sbapt	dlg_add_last_key(-1);
1092251839Sbapt    }
1093251839Sbapt
1094251839Sbapt    dlg_free_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
1095251839Sbapt    free(listitems);
1096251839Sbapt    return result;
1097251839Sbapt}
1098