xref: /freebsd-src/contrib/dialog/buildlist.c (revision a96ef4501919d7ac08e94e98dc34b0bdd744802b)
12a3e3873SBaptiste Daroussin /*
2*a96ef450SBaptiste Daroussin  *  $Id: buildlist.c,v 1.94 2020/11/23 00:37:17 tom Exp $
32a3e3873SBaptiste Daroussin  *
42a3e3873SBaptiste Daroussin  *  buildlist.c -- implements the buildlist dialog
52a3e3873SBaptiste Daroussin  *
6*a96ef450SBaptiste Daroussin  *  Copyright 2012-2019,2020	Thomas E. Dickey
72a3e3873SBaptiste Daroussin  *
82a3e3873SBaptiste Daroussin  *  This program is free software; you can redistribute it and/or modify
92a3e3873SBaptiste Daroussin  *  it under the terms of the GNU Lesser General Public License, version 2.1
102a3e3873SBaptiste Daroussin  *  as published by the Free Software Foundation.
112a3e3873SBaptiste Daroussin  *
122a3e3873SBaptiste Daroussin  *  This program is distributed in the hope that it will be useful, but
132a3e3873SBaptiste Daroussin  *  WITHOUT ANY WARRANTY; without even the implied warranty of
142a3e3873SBaptiste Daroussin  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
152a3e3873SBaptiste Daroussin  *  Lesser General Public License for more details.
162a3e3873SBaptiste Daroussin  *
172a3e3873SBaptiste Daroussin  *  You should have received a copy of the GNU Lesser General Public
182a3e3873SBaptiste Daroussin  *  License along with this program; if not, write to
192a3e3873SBaptiste Daroussin  *	Free Software Foundation, Inc.
202a3e3873SBaptiste Daroussin  *	51 Franklin St., Fifth Floor
212a3e3873SBaptiste Daroussin  *	Boston, MA 02110, USA.
222a3e3873SBaptiste Daroussin  */
232a3e3873SBaptiste Daroussin 
24*a96ef450SBaptiste Daroussin #include <dlg_internals.h>
252a3e3873SBaptiste Daroussin #include <dlg_keys.h>
262a3e3873SBaptiste Daroussin 
272a3e3873SBaptiste Daroussin /*
282a3e3873SBaptiste Daroussin  * Visually like menubox, but two columns.
292a3e3873SBaptiste Daroussin  */
302a3e3873SBaptiste Daroussin 
312a3e3873SBaptiste Daroussin #define sLEFT         (-2)
322a3e3873SBaptiste Daroussin #define sRIGHT        (-1)
332a3e3873SBaptiste Daroussin 
342a3e3873SBaptiste Daroussin #define KEY_LEFTCOL   '^'
352a3e3873SBaptiste Daroussin #define KEY_RIGHTCOL  '$'
362a3e3873SBaptiste Daroussin 
372a3e3873SBaptiste Daroussin #define MIN_HIGH  (1 + (5 * MARGIN))
382a3e3873SBaptiste Daroussin 
392a3e3873SBaptiste Daroussin typedef struct {
402a3e3873SBaptiste Daroussin     WINDOW *win;
412a3e3873SBaptiste Daroussin     int box_y;
422a3e3873SBaptiste Daroussin     int box_x;
432a3e3873SBaptiste Daroussin     int top_index;
442a3e3873SBaptiste Daroussin     int cur_index;
45f4f33ea0SBaptiste Daroussin     DIALOG_LISTITEM **ip;	/* pointers to items in this list */
462a3e3873SBaptiste Daroussin } MY_DATA;
472a3e3873SBaptiste Daroussin 
48f4f33ea0SBaptiste Daroussin #if 0
49f4f33ea0SBaptiste Daroussin #define TRACE(p)    dlg_trace_msg p
50f4f33ea0SBaptiste Daroussin #else
51f4f33ea0SBaptiste Daroussin #define TRACE(p)		/* nothing */
52f4f33ea0SBaptiste Daroussin #endif
53f4f33ea0SBaptiste Daroussin 
54f4f33ea0SBaptiste Daroussin #define okIndex(all,index) ((index) >= 0 && (index) < (all)->item_no)
55f4f33ea0SBaptiste Daroussin 
56f4f33ea0SBaptiste Daroussin #define myItem(p,n) ((p)->ip)[n]
57f4f33ea0SBaptiste Daroussin #define mySide(n)   ((n)?"right":"left")
58f4f33ea0SBaptiste Daroussin 
592a3e3873SBaptiste Daroussin typedef struct {
60f4f33ea0SBaptiste Daroussin     DIALOG_LISTITEM *items;	/* all items in the widget */
612a3e3873SBaptiste Daroussin     int base_y;			/* base for mouse coordinates */
622a3e3873SBaptiste Daroussin     int base_x;
632a3e3873SBaptiste Daroussin     int use_height;		/* actual size of column box */
642a3e3873SBaptiste Daroussin     int use_width;
652a3e3873SBaptiste Daroussin     int item_no;
662a3e3873SBaptiste Daroussin     int check_x;
672a3e3873SBaptiste Daroussin     int item_x;
682a3e3873SBaptiste Daroussin     MY_DATA list[2];
692a3e3873SBaptiste Daroussin } ALL_DATA;
702a3e3873SBaptiste Daroussin 
712a3e3873SBaptiste Daroussin /*
72f4f33ea0SBaptiste Daroussin  * Translate a choice from items[] to a row-number in an unbounded column,
73f4f33ea0SBaptiste Daroussin  * starting at zero.
74f4f33ea0SBaptiste Daroussin  */
75f4f33ea0SBaptiste Daroussin static int
index2row(ALL_DATA * all,int choice,int selected)76f4f33ea0SBaptiste Daroussin index2row(ALL_DATA * all, int choice, int selected)
77f4f33ea0SBaptiste Daroussin {
78f4f33ea0SBaptiste Daroussin     MY_DATA *data = all->list + selected;
79f4f33ea0SBaptiste Daroussin     int result = -1;
80f4f33ea0SBaptiste Daroussin 
81f4f33ea0SBaptiste Daroussin     if (okIndex(all, choice)) {
82*a96ef450SBaptiste Daroussin 	int row;
83*a96ef450SBaptiste Daroussin 
84f4f33ea0SBaptiste Daroussin 	for (row = 0; row < all->item_no; ++row) {
85f4f33ea0SBaptiste Daroussin 	    TRACE(("!... choice %d: %p vs row %d: %p\n",
86f4f33ea0SBaptiste Daroussin 		   choice, all->items + choice,
87f4f33ea0SBaptiste Daroussin 		   row, myItem(data, row)));
88f4f33ea0SBaptiste Daroussin 	    if (myItem(data, row) == all->items + choice) {
89f4f33ea0SBaptiste Daroussin 		result = row;
90f4f33ea0SBaptiste Daroussin 		break;
91f4f33ea0SBaptiste Daroussin 	    }
92f4f33ea0SBaptiste Daroussin 	}
93f4f33ea0SBaptiste Daroussin     }
94f4f33ea0SBaptiste Daroussin     TRACE(("! index2row(choice %d, %s) = %d\n", choice, mySide(selected), result));
95f4f33ea0SBaptiste Daroussin     return result;
96f4f33ea0SBaptiste Daroussin }
97f4f33ea0SBaptiste Daroussin 
98f4f33ea0SBaptiste Daroussin /*
99f4f33ea0SBaptiste Daroussin  * Convert a row-number back to an item number, i.e., index into items[].
100f4f33ea0SBaptiste Daroussin  */
101f4f33ea0SBaptiste Daroussin static int
row2index(ALL_DATA * all,int row,int selected)102f4f33ea0SBaptiste Daroussin row2index(ALL_DATA * all, int row, int selected)
103f4f33ea0SBaptiste Daroussin {
104f4f33ea0SBaptiste Daroussin     MY_DATA *data = all->list + selected;
105f4f33ea0SBaptiste Daroussin     int result = -1;
106f4f33ea0SBaptiste Daroussin     int n;
107f4f33ea0SBaptiste Daroussin     for (n = 0; n < all->item_no; ++n) {
108f4f33ea0SBaptiste Daroussin 	TRACE(("!... row %d: %p vs choice %d: %p\n",
109f4f33ea0SBaptiste Daroussin 	       row, myItem(data, row),
110f4f33ea0SBaptiste Daroussin 	       n, all->items + n));
111f4f33ea0SBaptiste Daroussin 	if (myItem(data, row) == all->items + n) {
112f4f33ea0SBaptiste Daroussin 	    result = n;
113f4f33ea0SBaptiste Daroussin 	    break;
114f4f33ea0SBaptiste Daroussin 	}
115f4f33ea0SBaptiste Daroussin     }
116f4f33ea0SBaptiste Daroussin     TRACE(("! row2index(row %d, %s) = %d\n", row, mySide(selected), result));
117f4f33ea0SBaptiste Daroussin     return result;
118f4f33ea0SBaptiste Daroussin }
119f4f33ea0SBaptiste Daroussin 
120f4f33ea0SBaptiste Daroussin /*
1212a3e3873SBaptiste Daroussin  * Print list item.  The 'selected' parameter is true if 'choice' is the
1222a3e3873SBaptiste Daroussin  * current item.  That one is colored differently from the other items.
1232a3e3873SBaptiste Daroussin  */
1242a3e3873SBaptiste Daroussin static void
print_item(ALL_DATA * all,WINDOW * win,DIALOG_LISTITEM * item,int row,int selected)125f4f33ea0SBaptiste Daroussin print_item(ALL_DATA * all,
1262a3e3873SBaptiste Daroussin 	   WINDOW *win,
1272a3e3873SBaptiste Daroussin 	   DIALOG_LISTITEM * item,
128f4f33ea0SBaptiste Daroussin 	   int row,
1292a3e3873SBaptiste Daroussin 	   int selected)
1302a3e3873SBaptiste Daroussin {
1312a3e3873SBaptiste Daroussin     chtype save = dlg_get_attrs(win);
1322a3e3873SBaptiste Daroussin     int i;
1332a3e3873SBaptiste Daroussin     bool both = (!dialog_vars.no_tags && !dialog_vars.no_items);
1342a3e3873SBaptiste Daroussin     bool first = TRUE;
135f4f33ea0SBaptiste Daroussin     int climit = (all->item_x - all->check_x - 1);
1362a3e3873SBaptiste Daroussin     const char *show = (dialog_vars.no_items
1372a3e3873SBaptiste Daroussin 			? item->name
1382a3e3873SBaptiste Daroussin 			: item->text);
1392a3e3873SBaptiste Daroussin 
1402a3e3873SBaptiste Daroussin     /* Clear 'residue' of last item */
141f4f33ea0SBaptiste Daroussin     dlg_attrset(win, menubox_attr);
142f4f33ea0SBaptiste Daroussin     (void) wmove(win, row, 0);
1432a3e3873SBaptiste Daroussin     for (i = 0; i < getmaxx(win); i++)
1442a3e3873SBaptiste Daroussin 	(void) waddch(win, ' ');
1452a3e3873SBaptiste Daroussin 
146f4f33ea0SBaptiste Daroussin     (void) wmove(win, row, all->check_x);
147f4f33ea0SBaptiste Daroussin     dlg_attrset(win, menubox_attr);
1482a3e3873SBaptiste Daroussin 
1492a3e3873SBaptiste Daroussin     if (both) {
1502a3e3873SBaptiste Daroussin 	dlg_print_listitem(win, item->name, climit, first, selected);
1512a3e3873SBaptiste Daroussin 	(void) waddch(win, ' ');
1522a3e3873SBaptiste Daroussin 	first = FALSE;
1532a3e3873SBaptiste Daroussin     }
1542a3e3873SBaptiste Daroussin 
155f4f33ea0SBaptiste Daroussin     (void) wmove(win, row, all->item_x);
156f4f33ea0SBaptiste Daroussin     climit = (getmaxx(win) - all->item_x + 1);
1572a3e3873SBaptiste Daroussin     dlg_print_listitem(win, show, climit, first, selected);
1582a3e3873SBaptiste Daroussin 
1592a3e3873SBaptiste Daroussin     if (selected) {
1602a3e3873SBaptiste Daroussin 	dlg_item_help(item->help);
1612a3e3873SBaptiste Daroussin     }
162f4f33ea0SBaptiste Daroussin     dlg_attrset(win, save);
1632a3e3873SBaptiste Daroussin }
1642a3e3873SBaptiste Daroussin 
1652a3e3873SBaptiste Daroussin /*
1662a3e3873SBaptiste Daroussin  * Prints either the left (unselected) or right (selected) list.
1672a3e3873SBaptiste Daroussin  */
1682a3e3873SBaptiste Daroussin static void
print_1_list(ALL_DATA * all,int choice,int selected)169f4f33ea0SBaptiste Daroussin print_1_list(ALL_DATA * all,
1702a3e3873SBaptiste Daroussin 	     int choice,
1712a3e3873SBaptiste Daroussin 	     int selected)
1722a3e3873SBaptiste Daroussin {
173f4f33ea0SBaptiste Daroussin     MY_DATA *data = all->list + selected;
174f4f33ea0SBaptiste Daroussin     DIALOG_LISTITEM *target = (okIndex(all, choice)
175f4f33ea0SBaptiste Daroussin 			       ? all->items + choice
176f4f33ea0SBaptiste Daroussin 			       : 0);
177f4f33ea0SBaptiste Daroussin     WINDOW *win = data->win;
1782a3e3873SBaptiste Daroussin     int i, j;
1792a3e3873SBaptiste Daroussin     int last = 0;
180f4f33ea0SBaptiste Daroussin     int top_row = index2row(all, data->top_index, selected);
1812a3e3873SBaptiste Daroussin     int max_rows = getmaxy(win);
1822a3e3873SBaptiste Daroussin 
183f4f33ea0SBaptiste Daroussin     TRACE(("! print_1_list %d %s, top %d\n", choice, mySide(selected), top_row));
1842a3e3873SBaptiste Daroussin     for (i = j = 0; j < max_rows; i++) {
185f4f33ea0SBaptiste Daroussin 	int ii = i + top_row;
186f4f33ea0SBaptiste Daroussin 	if (ii < 0) {
187f4f33ea0SBaptiste Daroussin 	    continue;
188f4f33ea0SBaptiste Daroussin 	} else if (myItem(data, ii)) {
189f4f33ea0SBaptiste Daroussin 	    print_item(all,
1902a3e3873SBaptiste Daroussin 		       win,
191f4f33ea0SBaptiste Daroussin 		       myItem(data, ii),
192f4f33ea0SBaptiste Daroussin 		       j, myItem(data, ii) == target);
1932a3e3873SBaptiste Daroussin 	    last = ++j;
194f4f33ea0SBaptiste Daroussin 	} else {
195f4f33ea0SBaptiste Daroussin 	    break;
1962a3e3873SBaptiste Daroussin 	}
1972a3e3873SBaptiste Daroussin     }
198f4f33ea0SBaptiste Daroussin     if (wmove(win, last, 0) != ERR) {
199f4f33ea0SBaptiste Daroussin 	while (waddch(win, ' ') != ERR) {
200f4f33ea0SBaptiste Daroussin 	    ;
201f4f33ea0SBaptiste Daroussin 	}
202f4f33ea0SBaptiste Daroussin     }
2032a3e3873SBaptiste Daroussin     (void) wnoutrefresh(win);
2042a3e3873SBaptiste Daroussin }
2052a3e3873SBaptiste Daroussin 
2062a3e3873SBaptiste Daroussin /*
2072a3e3873SBaptiste Daroussin  * Return the previous item from the list, staying in the same column.  If no
2082a3e3873SBaptiste Daroussin  * further movement is possible, return the same choice as given.
2092a3e3873SBaptiste Daroussin  */
2102a3e3873SBaptiste Daroussin static int
prev_item(ALL_DATA * all,int choice,int selected)211f4f33ea0SBaptiste Daroussin prev_item(ALL_DATA * all, int choice, int selected)
2122a3e3873SBaptiste Daroussin {
2132a3e3873SBaptiste Daroussin     int result = choice;
214f4f33ea0SBaptiste Daroussin     int row = index2row(all, choice, selected);
215f4f33ea0SBaptiste Daroussin     if (row > 0) {
216f4f33ea0SBaptiste Daroussin 	row--;
217f4f33ea0SBaptiste Daroussin 	result = row2index(all, row, selected);
2182a3e3873SBaptiste Daroussin     }
219f4f33ea0SBaptiste Daroussin     TRACE(("! prev_item choice %d, %s = %d\n", choice, mySide(selected), result));
2202a3e3873SBaptiste Daroussin     return result;
2212a3e3873SBaptiste Daroussin }
2222a3e3873SBaptiste Daroussin 
2232a3e3873SBaptiste Daroussin /*
2242a3e3873SBaptiste Daroussin  * Return true if the given choice is on the first page in the current column.
2252a3e3873SBaptiste Daroussin  */
2262a3e3873SBaptiste Daroussin static bool
stop_prev(ALL_DATA * all,int choice,int selected)227f4f33ea0SBaptiste Daroussin stop_prev(ALL_DATA * all, int choice, int selected)
2282a3e3873SBaptiste Daroussin {
229f4f33ea0SBaptiste Daroussin     return (prev_item(all, choice, selected) == choice);
2302a3e3873SBaptiste Daroussin }
2312a3e3873SBaptiste Daroussin 
2322a3e3873SBaptiste Daroussin static bool
check_hotkey(DIALOG_LISTITEM * items,int choice,int selected)2332a3e3873SBaptiste Daroussin check_hotkey(DIALOG_LISTITEM * items, int choice, int selected)
2342a3e3873SBaptiste Daroussin {
2352a3e3873SBaptiste Daroussin     bool result = FALSE;
2362a3e3873SBaptiste Daroussin 
2372a3e3873SBaptiste Daroussin     if ((items[choice].state != 0) == selected) {
2382a3e3873SBaptiste Daroussin 	if (dlg_match_char(dlg_last_getc(),
2392a3e3873SBaptiste Daroussin 			   (dialog_vars.no_tags
2402a3e3873SBaptiste Daroussin 			    ? items[choice].text
2412a3e3873SBaptiste Daroussin 			    : items[choice].name))) {
2422a3e3873SBaptiste Daroussin 	    result = TRUE;
2432a3e3873SBaptiste Daroussin 	}
2442a3e3873SBaptiste Daroussin     }
2452a3e3873SBaptiste Daroussin     return result;
2462a3e3873SBaptiste Daroussin }
2472a3e3873SBaptiste Daroussin 
2482a3e3873SBaptiste Daroussin /*
2492a3e3873SBaptiste Daroussin  * Return the next item from the list, staying in the same column.  If no
2502a3e3873SBaptiste Daroussin  * further movement is possible, return the same choice as given.
2512a3e3873SBaptiste Daroussin  */
2522a3e3873SBaptiste Daroussin static int
next_item(ALL_DATA * all,int choice,int selected)253f4f33ea0SBaptiste Daroussin next_item(ALL_DATA * all, int choice, int selected)
2542a3e3873SBaptiste Daroussin {
255f4f33ea0SBaptiste Daroussin     MY_DATA *data = all->list + selected;
2562a3e3873SBaptiste Daroussin     int result = choice;
257f4f33ea0SBaptiste Daroussin     int row = index2row(all, choice, selected);
258f4f33ea0SBaptiste Daroussin     TRACE(("! given item %d, testing next-item on row %d\n", choice, row + 1));
259f4f33ea0SBaptiste Daroussin     if (myItem(data, row + 1)) {
260f4f33ea0SBaptiste Daroussin 	result = row2index(all, row + 1, selected);
2612a3e3873SBaptiste Daroussin     }
262f4f33ea0SBaptiste Daroussin     TRACE(("! next_item(%d, %s) ->%d\n", choice, mySide(selected), result));
2632a3e3873SBaptiste Daroussin     return result;
2642a3e3873SBaptiste Daroussin }
2652a3e3873SBaptiste Daroussin 
2662a3e3873SBaptiste Daroussin /*
2672a3e3873SBaptiste Daroussin  * Return the first choice from items[] for the given column.
2682a3e3873SBaptiste Daroussin  */
2692a3e3873SBaptiste Daroussin static int
first_item(ALL_DATA * all,int selected)270f4f33ea0SBaptiste Daroussin first_item(ALL_DATA * all, int selected)
2712a3e3873SBaptiste Daroussin {
272f4f33ea0SBaptiste Daroussin     MY_DATA *data = all->list + selected;
2732a3e3873SBaptiste Daroussin     int result = -1;
2742a3e3873SBaptiste Daroussin 
275f4f33ea0SBaptiste Daroussin     if (myItem(data, 0) != 0) {
276*a96ef450SBaptiste Daroussin 	int n;
277*a96ef450SBaptiste Daroussin 
278f4f33ea0SBaptiste Daroussin 	for (n = 0; n < all->item_no; ++n) {
279f4f33ea0SBaptiste Daroussin 	    if (myItem(data, 0) == &all->items[n]) {
2802a3e3873SBaptiste Daroussin 		result = n;
2812a3e3873SBaptiste Daroussin 		break;
2822a3e3873SBaptiste Daroussin 	    }
2832a3e3873SBaptiste Daroussin 	}
284f4f33ea0SBaptiste Daroussin     }
285f4f33ea0SBaptiste Daroussin     TRACE(("! first_item %s = %d\n", mySide(selected), result));
2862a3e3873SBaptiste Daroussin     return result;
2872a3e3873SBaptiste Daroussin }
2882a3e3873SBaptiste Daroussin 
2892a3e3873SBaptiste Daroussin /*
2902a3e3873SBaptiste Daroussin  * Return the last choice from items[] for the given column.
2912a3e3873SBaptiste Daroussin  */
2922a3e3873SBaptiste Daroussin static int
last_item(ALL_DATA * all,int selected)293f4f33ea0SBaptiste Daroussin last_item(ALL_DATA * all, int selected)
2942a3e3873SBaptiste Daroussin {
295f4f33ea0SBaptiste Daroussin     MY_DATA *data = all->list + selected;
2962a3e3873SBaptiste Daroussin     int result = -1;
2972a3e3873SBaptiste Daroussin     int n;
2982a3e3873SBaptiste Daroussin 
299f4f33ea0SBaptiste Daroussin     for (n = 0; myItem(data, n) != 0; ++n) {
3002a3e3873SBaptiste Daroussin 	result = n;
3012a3e3873SBaptiste Daroussin     }
302f4f33ea0SBaptiste Daroussin     if (result >= 0) {
303f4f33ea0SBaptiste Daroussin 	result = row2index(all, result, selected);
3042a3e3873SBaptiste Daroussin     }
305f4f33ea0SBaptiste Daroussin     TRACE(("! last_item %s = %d\n", mySide(selected), result));
3062a3e3873SBaptiste Daroussin     return result;
3072a3e3873SBaptiste Daroussin }
3082a3e3873SBaptiste Daroussin 
3092a3e3873SBaptiste Daroussin static int
skip_rows(ALL_DATA * all,int row,int skip,int selected)310f4f33ea0SBaptiste Daroussin skip_rows(ALL_DATA * all, int row, int skip, int selected)
3112a3e3873SBaptiste Daroussin {
312f4f33ea0SBaptiste Daroussin     MY_DATA *data = all->list + selected;
3132a3e3873SBaptiste Daroussin     int result = row;
314f4f33ea0SBaptiste Daroussin 
3152a3e3873SBaptiste Daroussin     if (skip > 0) {
316*a96ef450SBaptiste Daroussin 	int n;
317*a96ef450SBaptiste Daroussin 
318f4f33ea0SBaptiste Daroussin 	for (n = row + 1; (n < all->item_no) && (n <= row + skip); ++n) {
319f4f33ea0SBaptiste Daroussin 	    if (myItem(data, n) == 0)
3202a3e3873SBaptiste Daroussin 		break;
321f4f33ea0SBaptiste Daroussin 	    result = n;
3222a3e3873SBaptiste Daroussin 	}
3232a3e3873SBaptiste Daroussin     } else if (skip < 0) {
324f4f33ea0SBaptiste Daroussin 	result -= skip;
325f4f33ea0SBaptiste Daroussin 	if (result < 0)
326f4f33ea0SBaptiste Daroussin 	    result = 0;
3272a3e3873SBaptiste Daroussin     }
328f4f33ea0SBaptiste Daroussin     TRACE(("! skip_rows row %d, skip %d, %s = %d\n",
329f4f33ea0SBaptiste Daroussin 	   row, skip, mySide(selected), result));
3302a3e3873SBaptiste Daroussin     return result;
3312a3e3873SBaptiste Daroussin }
3322a3e3873SBaptiste Daroussin 
3332a3e3873SBaptiste Daroussin /*
3342a3e3873SBaptiste Daroussin  * Find the closest item in the given column starting with the given choice.
3352a3e3873SBaptiste Daroussin  */
3362a3e3873SBaptiste Daroussin static int
closest_item(ALL_DATA * all,int choice,int selected)337f4f33ea0SBaptiste Daroussin closest_item(ALL_DATA * all, int choice, int selected)
3382a3e3873SBaptiste Daroussin {
3392a3e3873SBaptiste Daroussin     int prev = choice;
3402a3e3873SBaptiste Daroussin     int next = choice;
3412a3e3873SBaptiste Daroussin     int result = choice;
3422a3e3873SBaptiste Daroussin     int n;
3432a3e3873SBaptiste Daroussin 
3442a3e3873SBaptiste Daroussin     for (n = choice; n >= 0; --n) {
345f4f33ea0SBaptiste Daroussin 	if ((all->items[n].state != 0) == selected) {
3462a3e3873SBaptiste Daroussin 	    prev = n;
3472a3e3873SBaptiste Daroussin 	    break;
3482a3e3873SBaptiste Daroussin 	}
3492a3e3873SBaptiste Daroussin     }
350f4f33ea0SBaptiste Daroussin     for (n = choice; n < all->item_no; ++n) {
351f4f33ea0SBaptiste Daroussin 	if ((all->items[n].state != 0) == selected) {
3522a3e3873SBaptiste Daroussin 	    next = n;
3532a3e3873SBaptiste Daroussin 	    break;
3542a3e3873SBaptiste Daroussin 	}
3552a3e3873SBaptiste Daroussin     }
3562a3e3873SBaptiste Daroussin     if (prev != choice) {
3572a3e3873SBaptiste Daroussin 	result = prev;
3582a3e3873SBaptiste Daroussin 	if (next != choice) {
3592a3e3873SBaptiste Daroussin 	    if ((choice - prev) > (next - choice)) {
3602a3e3873SBaptiste Daroussin 		result = next;
3612a3e3873SBaptiste Daroussin 	    }
3622a3e3873SBaptiste Daroussin 	}
3632a3e3873SBaptiste Daroussin     } else if (next != choice) {
3642a3e3873SBaptiste Daroussin 	result = next;
3652a3e3873SBaptiste Daroussin     }
366f4f33ea0SBaptiste Daroussin     TRACE(("! XXX closest item choice %d, %s = %d\n",
367f4f33ea0SBaptiste Daroussin 	   choice, mySide(selected), result));
3682a3e3873SBaptiste Daroussin     return result;
3692a3e3873SBaptiste Daroussin }
3702a3e3873SBaptiste Daroussin 
3712a3e3873SBaptiste Daroussin static void
print_both(ALL_DATA * all,int choice)372f4f33ea0SBaptiste Daroussin print_both(ALL_DATA * all,
3732a3e3873SBaptiste Daroussin 	   int choice)
3742a3e3873SBaptiste Daroussin {
3752a3e3873SBaptiste Daroussin     int selected;
3762a3e3873SBaptiste Daroussin     int cur_y, cur_x;
377f4f33ea0SBaptiste Daroussin     WINDOW *dialog = wgetparent(all->list[0].win);
3782a3e3873SBaptiste Daroussin 
379f4f33ea0SBaptiste Daroussin     TRACE(("! print_both %d\n", choice));
3802a3e3873SBaptiste Daroussin     getyx(dialog, cur_y, cur_x);
3812a3e3873SBaptiste Daroussin     for (selected = 0; selected < 2; ++selected) {
382f4f33ea0SBaptiste Daroussin 	MY_DATA *data = all->list + selected;
383f4f33ea0SBaptiste Daroussin 	WINDOW *win = data->win;
384f4f33ea0SBaptiste Daroussin 	int thumb_top = index2row(all, data->top_index, selected);
385f4f33ea0SBaptiste Daroussin 	int thumb_max = index2row(all, -1, selected);
3862a3e3873SBaptiste Daroussin 	int thumb_end = thumb_top + getmaxy(win);
3872a3e3873SBaptiste Daroussin 
388f4f33ea0SBaptiste Daroussin 	print_1_list(all, choice, selected);
3892a3e3873SBaptiste Daroussin 
3902a3e3873SBaptiste Daroussin 	dlg_mouse_setcode(selected * KEY_MAX);
3912a3e3873SBaptiste Daroussin 	dlg_draw_scrollbar(dialog,
392f4f33ea0SBaptiste Daroussin 			   (long) (data->top_index),
3932a3e3873SBaptiste Daroussin 			   (long) (thumb_top),
3942a3e3873SBaptiste Daroussin 			   (long) MIN(thumb_end, thumb_max),
3952a3e3873SBaptiste Daroussin 			   (long) thumb_max,
396f4f33ea0SBaptiste Daroussin 			   data->box_x + all->check_x,
397f4f33ea0SBaptiste Daroussin 			   data->box_x + getmaxx(win),
398f4f33ea0SBaptiste Daroussin 			   data->box_y,
399f4f33ea0SBaptiste Daroussin 			   data->box_y + getmaxy(win) + 1,
4002a3e3873SBaptiste Daroussin 			   menubox_border2_attr,
4012a3e3873SBaptiste Daroussin 			   menubox_border_attr);
4022a3e3873SBaptiste Daroussin     }
4032a3e3873SBaptiste Daroussin     (void) wmove(dialog, cur_y, cur_x);
4042a3e3873SBaptiste Daroussin     dlg_mouse_setcode(0);
4052a3e3873SBaptiste Daroussin }
4062a3e3873SBaptiste Daroussin 
4072a3e3873SBaptiste Daroussin static void
set_top_item(ALL_DATA * all,int choice,int selected)408f4f33ea0SBaptiste Daroussin set_top_item(ALL_DATA * all, int choice, int selected)
4092a3e3873SBaptiste Daroussin {
410f4f33ea0SBaptiste Daroussin     if (choice != all->list[selected].top_index) {
411f4f33ea0SBaptiste Daroussin 	DLG_TRACE(("# set top of %s column to %d\n",
412f4f33ea0SBaptiste Daroussin 		   mySide(selected),
413f4f33ea0SBaptiste Daroussin 		   choice));
414f4f33ea0SBaptiste Daroussin 	all->list[selected].top_index = choice;
4152a3e3873SBaptiste Daroussin     }
4162a3e3873SBaptiste Daroussin }
4172a3e3873SBaptiste Daroussin 
4182a3e3873SBaptiste Daroussin /*
4192a3e3873SBaptiste Daroussin  * Adjust the top-index as needed to ensure that it and the given item are
4202a3e3873SBaptiste Daroussin  * visible.
4212a3e3873SBaptiste Daroussin  */
4222a3e3873SBaptiste Daroussin static void
fix_top_item(ALL_DATA * all,int cur_item,int selected)423f4f33ea0SBaptiste Daroussin fix_top_item(ALL_DATA * all, int cur_item, int selected)
4242a3e3873SBaptiste Daroussin {
425f4f33ea0SBaptiste Daroussin     int top_item = all->list[selected].top_index;
426f4f33ea0SBaptiste Daroussin     int cur_row = index2row(all, cur_item, selected);
427f4f33ea0SBaptiste Daroussin     int top_row = index2row(all, top_item, selected);
4282a3e3873SBaptiste Daroussin 
4292a3e3873SBaptiste Daroussin     if (cur_row < top_row) {
4302a3e3873SBaptiste Daroussin 	top_item = cur_item;
431f4f33ea0SBaptiste Daroussin     } else if ((cur_row - top_row) >= all->use_height) {
432f4f33ea0SBaptiste Daroussin 	top_item = row2index(all, cur_row + 1 - all->use_height, selected);
4332a3e3873SBaptiste Daroussin     }
434f4f33ea0SBaptiste Daroussin     if (cur_row < all->use_height) {
435f4f33ea0SBaptiste Daroussin 	top_item = row2index(all, 0, selected);
4362a3e3873SBaptiste Daroussin     }
437f4f33ea0SBaptiste Daroussin     DLG_TRACE(("# fix_top_item(cur_item %d, %s) ->top_item %d\n",
438f4f33ea0SBaptiste Daroussin 	       cur_item, mySide(selected), top_item));
439f4f33ea0SBaptiste Daroussin     set_top_item(all, top_item, selected);
440f4f33ea0SBaptiste Daroussin }
441f4f33ea0SBaptiste Daroussin 
442f4f33ea0SBaptiste Daroussin static void
append_right_side(ALL_DATA * all,int choice)443f4f33ea0SBaptiste Daroussin append_right_side(ALL_DATA * all, int choice)
444f4f33ea0SBaptiste Daroussin {
445f4f33ea0SBaptiste Daroussin     MY_DATA *data = &all->list[1];
446f4f33ea0SBaptiste Daroussin     int j;
447f4f33ea0SBaptiste Daroussin     for (j = 0; j < all->item_no; ++j) {
448f4f33ea0SBaptiste Daroussin 	if (myItem(data, j) == 0) {
449f4f33ea0SBaptiste Daroussin 	    myItem(data, j) = &all->items[choice];
450f4f33ea0SBaptiste Daroussin 	    break;
451f4f33ea0SBaptiste Daroussin 	}
452f4f33ea0SBaptiste Daroussin     }
453f4f33ea0SBaptiste Daroussin }
454f4f33ea0SBaptiste Daroussin 
455f4f33ea0SBaptiste Daroussin static void
amend_right_side(ALL_DATA * all,int choice)456f4f33ea0SBaptiste Daroussin amend_right_side(ALL_DATA * all, int choice)
457f4f33ea0SBaptiste Daroussin {
458f4f33ea0SBaptiste Daroussin     MY_DATA *data = &all->list[1];
459f4f33ea0SBaptiste Daroussin     int j, k;
460f4f33ea0SBaptiste Daroussin     for (j = 0; j < all->item_no; ++j) {
461f4f33ea0SBaptiste Daroussin 	if (myItem(data, j) == &all->items[choice]) {
462f4f33ea0SBaptiste Daroussin 	    for (k = j; k < all->item_no; ++k) {
463f4f33ea0SBaptiste Daroussin 		if ((myItem(data, k) = myItem(data, k + 1)) == 0)
464f4f33ea0SBaptiste Daroussin 		    break;
465f4f33ea0SBaptiste Daroussin 	    }
466f4f33ea0SBaptiste Daroussin 	    break;
467f4f33ea0SBaptiste Daroussin 	}
468f4f33ea0SBaptiste Daroussin     }
469f4f33ea0SBaptiste Daroussin }
470f4f33ea0SBaptiste Daroussin 
471f4f33ea0SBaptiste Daroussin static void
fill_one_side(ALL_DATA * all,int selected)472f4f33ea0SBaptiste Daroussin fill_one_side(ALL_DATA * all, int selected)
473f4f33ea0SBaptiste Daroussin {
474f4f33ea0SBaptiste Daroussin     int i, j;
475f4f33ea0SBaptiste Daroussin     MY_DATA *data = all->list + selected;
476f4f33ea0SBaptiste Daroussin 
477f4f33ea0SBaptiste Daroussin     for (i = j = 0; j < all->item_no; ++j) {
478f4f33ea0SBaptiste Daroussin 	myItem(data, i) = 0;
479f4f33ea0SBaptiste Daroussin 	if ((all->items[j].state != 0) == selected) {
480f4f33ea0SBaptiste Daroussin 	    myItem(data, i) = all->items + j;
481f4f33ea0SBaptiste Daroussin 	    TRACE(("! %s item[%d] %p = all[%d] %p\n",
482f4f33ea0SBaptiste Daroussin 		   mySide(selected),
483f4f33ea0SBaptiste Daroussin 		   i, myItem(data, i),
484f4f33ea0SBaptiste Daroussin 		   j, all->items + j));
485f4f33ea0SBaptiste Daroussin 	    ++i;
486f4f33ea0SBaptiste Daroussin 	}
487f4f33ea0SBaptiste Daroussin     }
488f4f33ea0SBaptiste Daroussin     myItem(data, i) = 0;
489f4f33ea0SBaptiste Daroussin }
490f4f33ea0SBaptiste Daroussin 
491f4f33ea0SBaptiste Daroussin static void
fill_both_sides(ALL_DATA * all)492f4f33ea0SBaptiste Daroussin fill_both_sides(ALL_DATA * all)
493f4f33ea0SBaptiste Daroussin {
494f4f33ea0SBaptiste Daroussin     int k;
495f4f33ea0SBaptiste Daroussin 
496f4f33ea0SBaptiste Daroussin     for (k = 0; k < 2; ++k) {
497f4f33ea0SBaptiste Daroussin 	fill_one_side(all, k);
498f4f33ea0SBaptiste Daroussin     }
4992a3e3873SBaptiste Daroussin }
5002a3e3873SBaptiste Daroussin 
5012a3e3873SBaptiste Daroussin /*
5022a3e3873SBaptiste Daroussin  * This is an alternate interface to 'buildlist' which allows the application
5032a3e3873SBaptiste Daroussin  * to read the list item states back directly without putting them in the
5042a3e3873SBaptiste Daroussin  * output buffer.
5052a3e3873SBaptiste Daroussin  */
5062a3e3873SBaptiste Daroussin int
dlg_buildlist(const char * title,const char * cprompt,int height,int width,int list_height,int item_no,DIALOG_LISTITEM * items,const char * states,int order_mode,int * current_item)5072a3e3873SBaptiste Daroussin dlg_buildlist(const char *title,
5082a3e3873SBaptiste Daroussin 	      const char *cprompt,
5092a3e3873SBaptiste Daroussin 	      int height,
5102a3e3873SBaptiste Daroussin 	      int width,
5112a3e3873SBaptiste Daroussin 	      int list_height,
5122a3e3873SBaptiste Daroussin 	      int item_no,
5132a3e3873SBaptiste Daroussin 	      DIALOG_LISTITEM * items,
5142a3e3873SBaptiste Daroussin 	      const char *states,
5152a3e3873SBaptiste Daroussin 	      int order_mode,
5162a3e3873SBaptiste Daroussin 	      int *current_item)
5172a3e3873SBaptiste Daroussin {
518f4f33ea0SBaptiste Daroussin #define THIS_FUNC "dlg_buildlist"
5192a3e3873SBaptiste Daroussin     /* *INDENT-OFF* */
5202a3e3873SBaptiste Daroussin     static DLG_KEYS_BINDING binding[] = {
5212a3e3873SBaptiste Daroussin 	HELPKEY_BINDINGS,
5222a3e3873SBaptiste Daroussin 	ENTERKEY_BINDINGS,
5232a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
5242a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
5252a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
5262a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
5272a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_ITEM_FIRST, KEY_HOME ),
5282a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_END ),
5292a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_ITEM_LAST,	KEY_LL ),
5302a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	'+' ),
5312a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_ITEM_NEXT,	KEY_DOWN ),
5322a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_ITEM_NEXT,  CHR_NEXT ),
5332a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_ITEM_PREV,	'-' ),
5342a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_ITEM_PREV,	KEY_UP ),
5352a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_ITEM_PREV,  CHR_PREVIOUS ),
5362a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ),
5372a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE) ),
5382a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE+KEY_MAX) ),
5392a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ),
5402a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE) ),
5412a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE+KEY_MAX) ),
5422a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_GRID_LEFT,	KEY_LEFTCOL ),
5432a3e3873SBaptiste Daroussin 	DLG_KEYS_DATA( DLGK_GRID_RIGHT,	KEY_RIGHTCOL ),
544f4f33ea0SBaptiste Daroussin 	TOGGLEKEY_BINDINGS,
5452a3e3873SBaptiste Daroussin 	END_KEYS_BINDING
5462a3e3873SBaptiste Daroussin     };
5472a3e3873SBaptiste Daroussin     /* *INDENT-ON* */
5482a3e3873SBaptiste Daroussin 
5492a3e3873SBaptiste Daroussin #ifdef KEY_RESIZE
5502a3e3873SBaptiste Daroussin     int old_height = height;
5512a3e3873SBaptiste Daroussin     int old_width = width;
5522a3e3873SBaptiste Daroussin #endif
5532a3e3873SBaptiste Daroussin     ALL_DATA all;
5542a3e3873SBaptiste Daroussin     MY_DATA *data = all.list;
5552a3e3873SBaptiste Daroussin     int i, j, k, key2, found, x, y, cur_x, cur_y;
556*a96ef450SBaptiste Daroussin     int key, fkey;
5572a3e3873SBaptiste Daroussin     bool save_visit = dialog_state.visit_items;
5582a3e3873SBaptiste Daroussin     int button;
5592a3e3873SBaptiste Daroussin     int cur_item;
5602a3e3873SBaptiste Daroussin     int name_width, text_width, full_width, list_width;
5612a3e3873SBaptiste Daroussin     int result = DLG_EXIT_UNKNOWN;
5622a3e3873SBaptiste Daroussin     int num_states;
5632a3e3873SBaptiste Daroussin     bool first = TRUE;
5642a3e3873SBaptiste Daroussin     WINDOW *dialog;
565f4f33ea0SBaptiste Daroussin     char *prompt;
5662a3e3873SBaptiste Daroussin     const char **buttons = dlg_ok_labels();
5672a3e3873SBaptiste Daroussin     const char *widget_name = "buildlist";
5682a3e3873SBaptiste Daroussin 
569f4f33ea0SBaptiste Daroussin     dialog_state.plain_buttons = TRUE;
5702a3e3873SBaptiste Daroussin 
5712a3e3873SBaptiste Daroussin     /*
5722a3e3873SBaptiste Daroussin      * Unlike other uses of --visit-items, we have two windows to visit.
5732a3e3873SBaptiste Daroussin      */
5742a3e3873SBaptiste Daroussin     if (dialog_state.visit_cols)
5752a3e3873SBaptiste Daroussin 	dialog_state.visit_cols = 2;
5762a3e3873SBaptiste Daroussin 
5772a3e3873SBaptiste Daroussin     memset(&all, 0, sizeof(all));
5782a3e3873SBaptiste Daroussin     all.items = items;
5792a3e3873SBaptiste Daroussin     all.item_no = item_no;
580f4f33ea0SBaptiste Daroussin     for (k = 0; k < 2; ++k) {
581f4f33ea0SBaptiste Daroussin 	data[k].ip = dlg_calloc(DIALOG_LISTITEM *, (item_no + 2));
582f4f33ea0SBaptiste Daroussin     }
583f4f33ea0SBaptiste Daroussin     fill_both_sides(&all);
5842a3e3873SBaptiste Daroussin 
5852a3e3873SBaptiste Daroussin     if (dialog_vars.default_item != 0) {
5862a3e3873SBaptiste Daroussin 	cur_item = dlg_default_listitem(items);
5872a3e3873SBaptiste Daroussin     } else {
5882a3e3873SBaptiste Daroussin 	if ((cur_item = first_item(&all, 0)) < 0)
5892a3e3873SBaptiste Daroussin 	    cur_item = first_item(&all, 1);
5902a3e3873SBaptiste Daroussin     }
5912a3e3873SBaptiste Daroussin     button = (dialog_state.visit_items
5922a3e3873SBaptiste Daroussin 	      ? (items[cur_item].state ? sRIGHT : sLEFT)
5932a3e3873SBaptiste Daroussin 	      : dlg_default_button());
5942a3e3873SBaptiste Daroussin 
5952a3e3873SBaptiste Daroussin     dlg_does_output();
5962a3e3873SBaptiste Daroussin 
5972a3e3873SBaptiste Daroussin #ifdef KEY_RESIZE
5982a3e3873SBaptiste Daroussin   retry:
5992a3e3873SBaptiste Daroussin #endif
6002a3e3873SBaptiste Daroussin 
601f4f33ea0SBaptiste Daroussin     prompt = dlg_strclone(cprompt);
602f4f33ea0SBaptiste Daroussin     dlg_tab_correct_str(prompt);
603f4f33ea0SBaptiste Daroussin 
6042a3e3873SBaptiste Daroussin     all.use_height = list_height;
6052a3e3873SBaptiste Daroussin     all.use_width = (2 * (dlg_calc_list_width(item_no, items)
6062a3e3873SBaptiste Daroussin 			  + 4
6072a3e3873SBaptiste Daroussin 			  + 2 * MARGIN)
6082a3e3873SBaptiste Daroussin 		     + 1);
6092a3e3873SBaptiste Daroussin     all.use_width = MAX(26, all.use_width);
6102a3e3873SBaptiste Daroussin     if (all.use_height == 0) {
6112a3e3873SBaptiste Daroussin 	/* calculate height without items (4) */
6122a3e3873SBaptiste Daroussin 	dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, all.use_width);
6132a3e3873SBaptiste Daroussin 	dlg_calc_listh(&height, &all.use_height, item_no);
6142a3e3873SBaptiste Daroussin     } else {
6152a3e3873SBaptiste Daroussin 	dlg_auto_size(title, prompt,
6162a3e3873SBaptiste Daroussin 		      &height, &width,
6172a3e3873SBaptiste Daroussin 		      MIN_HIGH + all.use_height, all.use_width);
6182a3e3873SBaptiste Daroussin     }
6192a3e3873SBaptiste Daroussin     dlg_button_layout(buttons, &width);
6202a3e3873SBaptiste Daroussin     dlg_print_size(height, width);
6212a3e3873SBaptiste Daroussin     dlg_ctl_size(height, width);
6222a3e3873SBaptiste Daroussin 
6232a3e3873SBaptiste Daroussin     /* we need at least two states */
6242a3e3873SBaptiste Daroussin     if (states == 0 || strlen(states) < 2)
6252a3e3873SBaptiste Daroussin 	states = " *";
6262a3e3873SBaptiste Daroussin     num_states = (int) strlen(states);
6272a3e3873SBaptiste Daroussin 
6282a3e3873SBaptiste Daroussin     x = dlg_box_x_ordinate(width);
6292a3e3873SBaptiste Daroussin     y = dlg_box_y_ordinate(height);
6302a3e3873SBaptiste Daroussin 
6312a3e3873SBaptiste Daroussin     dialog = dlg_new_window(height, width, y, x);
6322a3e3873SBaptiste Daroussin     dlg_register_window(dialog, widget_name, binding);
6332a3e3873SBaptiste Daroussin     dlg_register_buttons(dialog, widget_name, buttons);
6342a3e3873SBaptiste Daroussin 
6352a3e3873SBaptiste Daroussin     dlg_mouse_setbase(all.base_x = x, all.base_y = y);
6362a3e3873SBaptiste Daroussin 
6372a3e3873SBaptiste Daroussin     dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
6382a3e3873SBaptiste Daroussin     dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
6392a3e3873SBaptiste Daroussin     dlg_draw_title(dialog, title);
6402a3e3873SBaptiste Daroussin 
641f4f33ea0SBaptiste Daroussin     dlg_attrset(dialog, dialog_attr);
6422a3e3873SBaptiste Daroussin     dlg_print_autowrap(dialog, prompt, height, width);
6432a3e3873SBaptiste Daroussin 
6442a3e3873SBaptiste Daroussin     list_width = (width - 6 * MARGIN - 2) / 2;
6452a3e3873SBaptiste Daroussin     getyx(dialog, cur_y, cur_x);
6462a3e3873SBaptiste Daroussin     data[0].box_y = cur_y + 1;
6472a3e3873SBaptiste Daroussin     data[0].box_x = MARGIN + 1;
6482a3e3873SBaptiste Daroussin     data[1].box_y = cur_y + 1;
6492a3e3873SBaptiste Daroussin     data[1].box_x = data[0].box_x + 1 + 2 * MARGIN + list_width;
6502a3e3873SBaptiste Daroussin 
6512a3e3873SBaptiste Daroussin     /*
6522a3e3873SBaptiste Daroussin      * After displaying the prompt, we know how much space we really have.
6532a3e3873SBaptiste Daroussin      * Limit the list to avoid overwriting the ok-button.
6542a3e3873SBaptiste Daroussin      */
6552a3e3873SBaptiste Daroussin     all.use_height = height - MIN_HIGH - cur_y;
6562a3e3873SBaptiste Daroussin     if (all.use_height <= 0)
6572a3e3873SBaptiste Daroussin 	all.use_height = 1;
6582a3e3873SBaptiste Daroussin 
6592a3e3873SBaptiste Daroussin     for (k = 0; k < 2; ++k) {
6602a3e3873SBaptiste Daroussin 	/* create new window for the list */
6612a3e3873SBaptiste Daroussin 	data[k].win = dlg_sub_window(dialog, all.use_height, list_width,
6622a3e3873SBaptiste Daroussin 				     y + data[k].box_y + 1,
6632a3e3873SBaptiste Daroussin 				     x + data[k].box_x + 1);
6642a3e3873SBaptiste Daroussin 
6652a3e3873SBaptiste Daroussin 	/* draw a box around the list items */
6662a3e3873SBaptiste Daroussin 	dlg_draw_box(dialog, data[k].box_y, data[k].box_x,
6672a3e3873SBaptiste Daroussin 		     all.use_height + 2 * MARGIN,
6682a3e3873SBaptiste Daroussin 		     list_width + 2 * MARGIN,
6692a3e3873SBaptiste Daroussin 		     menubox_border_attr, menubox_border2_attr);
6702a3e3873SBaptiste Daroussin     }
6712a3e3873SBaptiste Daroussin 
6722a3e3873SBaptiste Daroussin     text_width = 0;
6732a3e3873SBaptiste Daroussin     name_width = 0;
6742a3e3873SBaptiste Daroussin     /* Find length of longest item to center buildlist */
6752a3e3873SBaptiste Daroussin     for (i = 0; i < item_no; i++) {
6762a3e3873SBaptiste Daroussin 	text_width = MAX(text_width, dlg_count_columns(items[i].text));
6772a3e3873SBaptiste Daroussin 	name_width = MAX(name_width, dlg_count_columns(items[i].name));
6782a3e3873SBaptiste Daroussin     }
6792a3e3873SBaptiste Daroussin 
6802a3e3873SBaptiste Daroussin     /* If the name+text is wider than the list is allowed, then truncate
6812a3e3873SBaptiste Daroussin      * one or both of them.  If the name is no wider than 1/4 of the list,
6822a3e3873SBaptiste Daroussin      * leave it intact.
6832a3e3873SBaptiste Daroussin      */
6842a3e3873SBaptiste Daroussin     all.use_width = (list_width - 6 * MARGIN);
6852a3e3873SBaptiste Daroussin     if (dialog_vars.no_tags && !dialog_vars.no_items) {
6862a3e3873SBaptiste Daroussin 	full_width = MIN(all.use_width, text_width);
6872a3e3873SBaptiste Daroussin     } else if (dialog_vars.no_items) {
6882a3e3873SBaptiste Daroussin 	full_width = MIN(all.use_width, name_width);
6892a3e3873SBaptiste Daroussin     } else {
6902a3e3873SBaptiste Daroussin 	if (text_width >= 0
6912a3e3873SBaptiste Daroussin 	    && name_width >= 0
6922a3e3873SBaptiste Daroussin 	    && all.use_width > 0
6932a3e3873SBaptiste Daroussin 	    && text_width + name_width > all.use_width) {
6942a3e3873SBaptiste Daroussin 	    int need = (int) (0.25 * all.use_width);
6952a3e3873SBaptiste Daroussin 	    if (name_width > need) {
6962a3e3873SBaptiste Daroussin 		int want = (int) (all.use_width * ((double) name_width) /
6972a3e3873SBaptiste Daroussin 				  (text_width + name_width));
6982a3e3873SBaptiste Daroussin 		name_width = (want > need) ? want : need;
6992a3e3873SBaptiste Daroussin 	    }
7002a3e3873SBaptiste Daroussin 	    text_width = all.use_width - name_width;
7012a3e3873SBaptiste Daroussin 	}
7022a3e3873SBaptiste Daroussin 	full_width = text_width + name_width;
7032a3e3873SBaptiste Daroussin     }
7042a3e3873SBaptiste Daroussin 
7052a3e3873SBaptiste Daroussin     all.check_x = (all.use_width - full_width) / 2;
7062a3e3873SBaptiste Daroussin     all.item_x = ((dialog_vars.no_tags
7072a3e3873SBaptiste Daroussin 		   ? 0
7082a3e3873SBaptiste Daroussin 		   : (dialog_vars.no_items
7092a3e3873SBaptiste Daroussin 		      ? 0
7102a3e3873SBaptiste Daroussin 		      : (name_width + 2)))
7112a3e3873SBaptiste Daroussin 		  + all.check_x);
7122a3e3873SBaptiste Daroussin 
7132a3e3873SBaptiste Daroussin     /* ensure we are scrolled to show the current choice */
7142a3e3873SBaptiste Daroussin     j = MIN(all.use_height, item_no);
7152a3e3873SBaptiste Daroussin     for (i = 0; i < 2; ++i) {
7162a3e3873SBaptiste Daroussin 	if ((items[cur_item].state != 0) == i) {
717f4f33ea0SBaptiste Daroussin 	    int top_item = cur_item - j + 1;
7182a3e3873SBaptiste Daroussin 	    if (top_item < 0)
7192a3e3873SBaptiste Daroussin 		top_item = 0;
720f4f33ea0SBaptiste Daroussin 	    while ((items[top_item].state != 0) != i)
721f4f33ea0SBaptiste Daroussin 		++top_item;
7222a3e3873SBaptiste Daroussin 	    set_top_item(&all, top_item, i);
7232a3e3873SBaptiste Daroussin 	} else {
7242a3e3873SBaptiste Daroussin 	    set_top_item(&all, 0, i);
7252a3e3873SBaptiste Daroussin 	}
7262a3e3873SBaptiste Daroussin     }
7272a3e3873SBaptiste Daroussin 
7282a3e3873SBaptiste Daroussin     /* register the new window, along with its borders */
7292a3e3873SBaptiste Daroussin     for (i = 0; i < 2; ++i) {
7302a3e3873SBaptiste Daroussin 	dlg_mouse_mkbigregion(data[i].box_y + 1,
7312a3e3873SBaptiste Daroussin 			      data[i].box_x,
7322a3e3873SBaptiste Daroussin 			      all.use_height,
7332a3e3873SBaptiste Daroussin 			      list_width + 2,
7342a3e3873SBaptiste Daroussin 			      2 * KEY_MAX + (i * (1 + all.use_height)),
7352a3e3873SBaptiste Daroussin 			      1, 1, 1 /* by lines */ );
7362a3e3873SBaptiste Daroussin     }
7372a3e3873SBaptiste Daroussin 
7382a3e3873SBaptiste Daroussin     dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
7392a3e3873SBaptiste Daroussin 
7402a3e3873SBaptiste Daroussin     while (result == DLG_EXIT_UNKNOWN) {
7412a3e3873SBaptiste Daroussin 	int which = (items[cur_item].state != 0);
7422a3e3873SBaptiste Daroussin 	MY_DATA *moi = data + which;
7432a3e3873SBaptiste Daroussin 	int at_top = index2row(&all, moi->top_index, which);
7442a3e3873SBaptiste Daroussin 	int at_end = index2row(&all, -1, which);
7452a3e3873SBaptiste Daroussin 	int at_bot = skip_rows(&all, at_top, all.use_height, which);
746*a96ef450SBaptiste Daroussin 	int was_mouse;
7472a3e3873SBaptiste Daroussin 
748f4f33ea0SBaptiste Daroussin 	DLG_TRACE(("# ** state %d:%d top %d (%d:%d:%d) %s\n",
7492a3e3873SBaptiste Daroussin 		   cur_item, item_no - 1,
7502a3e3873SBaptiste Daroussin 		   moi->top_index,
7512a3e3873SBaptiste Daroussin 		   at_top, at_bot, at_end,
752f4f33ea0SBaptiste Daroussin 		   mySide(which)));
7532a3e3873SBaptiste Daroussin 
7542a3e3873SBaptiste Daroussin 	if (first) {
7552a3e3873SBaptiste Daroussin 	    print_both(&all, cur_item);
7562a3e3873SBaptiste Daroussin 	    dlg_trace_win(dialog);
7572a3e3873SBaptiste Daroussin 	    first = FALSE;
7582a3e3873SBaptiste Daroussin 	}
7592a3e3873SBaptiste Daroussin 
7602a3e3873SBaptiste Daroussin 	if (button < 0) {	/* --visit-items */
7612a3e3873SBaptiste Daroussin 	    int cur_row = index2row(&all, cur_item, which);
7622a3e3873SBaptiste Daroussin 	    cur_y = (data[which].box_y
7632a3e3873SBaptiste Daroussin 		     + cur_row
7642a3e3873SBaptiste Daroussin 		     + 1);
7652a3e3873SBaptiste Daroussin 	    if (at_top > 0)
7662a3e3873SBaptiste Daroussin 		cur_y -= at_top;
7672a3e3873SBaptiste Daroussin 	    cur_x = (data[which].box_x
7682a3e3873SBaptiste Daroussin 		     + all.check_x + 1);
769f4f33ea0SBaptiste Daroussin 	    DLG_TRACE(("# ...visit row %d (%d,%d)\n", cur_row, cur_y, cur_x));
7702a3e3873SBaptiste Daroussin 	    wmove(dialog, cur_y, cur_x);
7712a3e3873SBaptiste Daroussin 	}
7722a3e3873SBaptiste Daroussin 
7732a3e3873SBaptiste Daroussin 	key = dlg_mouse_wgetch(dialog, &fkey);
774*a96ef450SBaptiste Daroussin 	if (dlg_result_key(key, fkey, &result)) {
775*a96ef450SBaptiste Daroussin 	    if (!dlg_button_key(result, &button, &key, &fkey))
7762a3e3873SBaptiste Daroussin 		break;
777*a96ef450SBaptiste Daroussin 	}
7782a3e3873SBaptiste Daroussin 
7792a3e3873SBaptiste Daroussin 	was_mouse = (fkey && is_DLGK_MOUSE(key));
7802a3e3873SBaptiste Daroussin 	if (was_mouse)
7812a3e3873SBaptiste Daroussin 	    key -= M_EVENT;
7822a3e3873SBaptiste Daroussin 
7832a3e3873SBaptiste Daroussin 	if (!was_mouse) {
7842a3e3873SBaptiste Daroussin 	    ;
7852a3e3873SBaptiste Daroussin 	} else if (key >= 2 * KEY_MAX) {
7862a3e3873SBaptiste Daroussin 	    i = (key - 2 * KEY_MAX) % (1 + all.use_height);
7872a3e3873SBaptiste Daroussin 	    j = (key - 2 * KEY_MAX) / (1 + all.use_height);
7882a3e3873SBaptiste Daroussin 	    k = row2index(&all, i + at_top, j);
789f4f33ea0SBaptiste Daroussin 	    DLG_TRACE(("# MOUSE column %d, row %d ->item %d\n", j, i, k));
7902a3e3873SBaptiste Daroussin 	    if (k >= 0 && j < 2) {
7912a3e3873SBaptiste Daroussin 		if (j != which) {
7922a3e3873SBaptiste Daroussin 		    /*
7932a3e3873SBaptiste Daroussin 		     * Mouse click was in the other column.
7942a3e3873SBaptiste Daroussin 		     */
7952a3e3873SBaptiste Daroussin 		    moi = data + j;
7962a3e3873SBaptiste Daroussin 		    fix_top_item(&all, k, j);
7972a3e3873SBaptiste Daroussin 		}
7982a3e3873SBaptiste Daroussin 		which = j;
7992a3e3873SBaptiste Daroussin 		at_top = index2row(&all, moi->top_index, which);
8002a3e3873SBaptiste Daroussin 		at_bot = skip_rows(&all, at_top, all.use_height, which);
8012a3e3873SBaptiste Daroussin 		cur_item = k;
8022a3e3873SBaptiste Daroussin 		print_both(&all, cur_item);
803f4f33ea0SBaptiste Daroussin 		key = DLGK_TOGGLE;	/* force the selected item to toggle */
8042a3e3873SBaptiste Daroussin 	    } else {
8052a3e3873SBaptiste Daroussin 		beep();
8062a3e3873SBaptiste Daroussin 		continue;
8072a3e3873SBaptiste Daroussin 	    }
8082a3e3873SBaptiste Daroussin 	    fkey = FALSE;
8092a3e3873SBaptiste Daroussin 	} else if (key >= KEY_MIN) {
8102a3e3873SBaptiste Daroussin 	    if (key > KEY_MAX) {
8112a3e3873SBaptiste Daroussin 		if (which == 0) {
8122a3e3873SBaptiste Daroussin 		    key = KEY_RIGHTCOL;		/* switch to right-column */
8132a3e3873SBaptiste Daroussin 		    fkey = FALSE;
8142a3e3873SBaptiste Daroussin 		} else {
8152a3e3873SBaptiste Daroussin 		    key -= KEY_MAX;
8162a3e3873SBaptiste Daroussin 		}
8172a3e3873SBaptiste Daroussin 	    } else {
8182a3e3873SBaptiste Daroussin 		if (which == 1) {
8192a3e3873SBaptiste Daroussin 		    key = KEY_LEFTCOL;	/* switch to left-column */
8202a3e3873SBaptiste Daroussin 		    fkey = FALSE;
8212a3e3873SBaptiste Daroussin 		}
8222a3e3873SBaptiste Daroussin 	    }
8232a3e3873SBaptiste Daroussin 	    key = dlg_lookup_key(dialog, key, &fkey);
8242a3e3873SBaptiste Daroussin 	}
8252a3e3873SBaptiste Daroussin 
8262a3e3873SBaptiste Daroussin 	/*
8272a3e3873SBaptiste Daroussin 	 * A space toggles the item status.  Normally we put the cursor on
8282a3e3873SBaptiste Daroussin 	 * the next available item in the same column.  But if there are no
8292a3e3873SBaptiste Daroussin 	 * more items in the column, move the cursor to the other column.
8302a3e3873SBaptiste Daroussin 	 */
831f4f33ea0SBaptiste Daroussin 	if (key == DLGK_TOGGLE) {
8322a3e3873SBaptiste Daroussin 	    int new_choice;
8332a3e3873SBaptiste Daroussin 	    int new_state = items[cur_item].state + 1;
8342a3e3873SBaptiste Daroussin 
8352a3e3873SBaptiste Daroussin 	    if ((new_choice = next_item(&all, cur_item, which)) == cur_item) {
8362a3e3873SBaptiste Daroussin 		new_choice = prev_item(&all, cur_item, which);
8372a3e3873SBaptiste Daroussin 	    }
838f4f33ea0SBaptiste Daroussin 	    DLG_TRACE(("# cur_item %d, new_choice:%d\n", cur_item, new_choice));
839f4f33ea0SBaptiste Daroussin 	    /* FIXME - how to test and handle multiple states? */
8402a3e3873SBaptiste Daroussin 	    if (new_state >= num_states)
8412a3e3873SBaptiste Daroussin 		new_state = 0;
8422a3e3873SBaptiste Daroussin 
8432a3e3873SBaptiste Daroussin 	    items[cur_item].state = new_state;
844f4f33ea0SBaptiste Daroussin 	    if (order_mode) {
845f4f33ea0SBaptiste Daroussin 		fill_one_side(&all, 0);
846f4f33ea0SBaptiste Daroussin 		if (new_state) {
847f4f33ea0SBaptiste Daroussin 		    append_right_side(&all, cur_item);
848f4f33ea0SBaptiste Daroussin 		} else {
849f4f33ea0SBaptiste Daroussin 		    amend_right_side(&all, cur_item);
850f4f33ea0SBaptiste Daroussin 		}
851f4f33ea0SBaptiste Daroussin 	    } else {
852f4f33ea0SBaptiste Daroussin 		fill_both_sides(&all);
853f4f33ea0SBaptiste Daroussin 	    }
8542a3e3873SBaptiste Daroussin 	    if (cur_item == moi->top_index) {
8552a3e3873SBaptiste Daroussin 		set_top_item(&all, new_choice, which);
8562a3e3873SBaptiste Daroussin 	    }
8572a3e3873SBaptiste Daroussin 
8582a3e3873SBaptiste Daroussin 	    if (new_choice >= 0) {
8592a3e3873SBaptiste Daroussin 		fix_top_item(&all, cur_item, !which);
8602a3e3873SBaptiste Daroussin 		cur_item = new_choice;
8612a3e3873SBaptiste Daroussin 	    }
8622a3e3873SBaptiste Daroussin 	    print_both(&all, cur_item);
8632a3e3873SBaptiste Daroussin 	    dlg_trace_win(dialog);
8642a3e3873SBaptiste Daroussin 	    continue;		/* wait for another key press */
8652a3e3873SBaptiste Daroussin 	}
8662a3e3873SBaptiste Daroussin 
8672a3e3873SBaptiste Daroussin 	/*
8682a3e3873SBaptiste Daroussin 	 * Check if key pressed matches first character of any item tag in
8692a3e3873SBaptiste Daroussin 	 * list.  If there is more than one match, we will cycle through
8702a3e3873SBaptiste Daroussin 	 * each one as the same key is pressed repeatedly.
8712a3e3873SBaptiste Daroussin 	 */
8722a3e3873SBaptiste Daroussin 	found = FALSE;
8732a3e3873SBaptiste Daroussin 	if (!fkey) {
8742a3e3873SBaptiste Daroussin 	    if (button < 0 || !dialog_state.visit_items) {
8752a3e3873SBaptiste Daroussin 		for (j = cur_item + 1; j < item_no; j++) {
8762a3e3873SBaptiste Daroussin 		    if (check_hotkey(items, j, which)) {
8772a3e3873SBaptiste Daroussin 			found = TRUE;
8782a3e3873SBaptiste Daroussin 			i = j;
8792a3e3873SBaptiste Daroussin 			break;
8802a3e3873SBaptiste Daroussin 		    }
8812a3e3873SBaptiste Daroussin 		}
8822a3e3873SBaptiste Daroussin 		if (!found) {
8832a3e3873SBaptiste Daroussin 		    for (j = 0; j <= cur_item; j++) {
8842a3e3873SBaptiste Daroussin 			if (check_hotkey(items, j, which)) {
8852a3e3873SBaptiste Daroussin 			    found = TRUE;
8862a3e3873SBaptiste Daroussin 			    i = j;
8872a3e3873SBaptiste Daroussin 			    break;
8882a3e3873SBaptiste Daroussin 			}
8892a3e3873SBaptiste Daroussin 		    }
8902a3e3873SBaptiste Daroussin 		}
8912a3e3873SBaptiste Daroussin 		if (found)
8922a3e3873SBaptiste Daroussin 		    dlg_flush_getc();
8932a3e3873SBaptiste Daroussin 	    } else if ((j = dlg_char_to_button(key, buttons)) >= 0) {
8942a3e3873SBaptiste Daroussin 		button = j;
8952a3e3873SBaptiste Daroussin 		ungetch('\n');
8962a3e3873SBaptiste Daroussin 		continue;
8972a3e3873SBaptiste Daroussin 	    }
8982a3e3873SBaptiste Daroussin 	}
8992a3e3873SBaptiste Daroussin 
9002a3e3873SBaptiste Daroussin 	/*
9012a3e3873SBaptiste Daroussin 	 * A single digit (1-9) positions the selection to that line in the
9022a3e3873SBaptiste Daroussin 	 * current screen.
9032a3e3873SBaptiste Daroussin 	 */
9042a3e3873SBaptiste Daroussin 	if (!found
9052a3e3873SBaptiste Daroussin 	    && (key <= '9')
9062a3e3873SBaptiste Daroussin 	    && (key > '0')
9072a3e3873SBaptiste Daroussin 	    && (key - '1' < at_bot)) {
9082a3e3873SBaptiste Daroussin 	    found = TRUE;
9092a3e3873SBaptiste Daroussin 	    i = key - '1';
9102a3e3873SBaptiste Daroussin 	}
9112a3e3873SBaptiste Daroussin 
9122a3e3873SBaptiste Daroussin 	if (!found && fkey) {
9132a3e3873SBaptiste Daroussin 	    switch (key) {
9142a3e3873SBaptiste Daroussin 	    case DLGK_FIELD_PREV:
9152a3e3873SBaptiste Daroussin 		if ((button == sRIGHT) && dialog_state.visit_items) {
9162a3e3873SBaptiste Daroussin 		    key = DLGK_GRID_LEFT;
9172a3e3873SBaptiste Daroussin 		    button = sLEFT;
9182a3e3873SBaptiste Daroussin 		} else {
9192a3e3873SBaptiste Daroussin 		    button = dlg_prev_button(buttons, button);
9202a3e3873SBaptiste Daroussin 		    dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
9212a3e3873SBaptiste Daroussin 				     FALSE, width);
9222a3e3873SBaptiste Daroussin 		    if (button == sRIGHT) {
9232a3e3873SBaptiste Daroussin 			key = DLGK_GRID_RIGHT;
9242a3e3873SBaptiste Daroussin 		    } else {
9252a3e3873SBaptiste Daroussin 			continue;
9262a3e3873SBaptiste Daroussin 		    }
9272a3e3873SBaptiste Daroussin 		}
9282a3e3873SBaptiste Daroussin 		break;
9292a3e3873SBaptiste Daroussin 	    case DLGK_FIELD_NEXT:
9302a3e3873SBaptiste Daroussin 		if ((button == sLEFT) && dialog_state.visit_items) {
9312a3e3873SBaptiste Daroussin 		    key = DLGK_GRID_RIGHT;
9322a3e3873SBaptiste Daroussin 		    button = sRIGHT;
9332a3e3873SBaptiste Daroussin 		} else {
9342a3e3873SBaptiste Daroussin 		    button = dlg_next_button(buttons, button);
9352a3e3873SBaptiste Daroussin 		    dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
9362a3e3873SBaptiste Daroussin 				     FALSE, width);
9372a3e3873SBaptiste Daroussin 		    if (button == sLEFT) {
9382a3e3873SBaptiste Daroussin 			key = DLGK_GRID_LEFT;
9392a3e3873SBaptiste Daroussin 		    } else {
9402a3e3873SBaptiste Daroussin 			continue;
9412a3e3873SBaptiste Daroussin 		    }
9422a3e3873SBaptiste Daroussin 		}
9432a3e3873SBaptiste Daroussin 		break;
9442a3e3873SBaptiste Daroussin 	    }
9452a3e3873SBaptiste Daroussin 	}
9462a3e3873SBaptiste Daroussin 
9472a3e3873SBaptiste Daroussin 	if (!found && fkey) {
9482a3e3873SBaptiste Daroussin 	    i = cur_item;
9492a3e3873SBaptiste Daroussin 	    found = TRUE;
9502a3e3873SBaptiste Daroussin 	    switch (key) {
9512a3e3873SBaptiste Daroussin 	    case DLGK_GRID_LEFT:
9522a3e3873SBaptiste Daroussin 		i = closest_item(&all, cur_item, 0);
9532a3e3873SBaptiste Daroussin 		fix_top_item(&all, i, 0);
9542a3e3873SBaptiste Daroussin 		break;
9552a3e3873SBaptiste Daroussin 	    case DLGK_GRID_RIGHT:
956f4f33ea0SBaptiste Daroussin 		if (order_mode) {
957f4f33ea0SBaptiste Daroussin 		    i = last_item(&all, 1);
958f4f33ea0SBaptiste Daroussin 		} else {
9592a3e3873SBaptiste Daroussin 		    i = closest_item(&all, cur_item, 1);
960f4f33ea0SBaptiste Daroussin 		}
9612a3e3873SBaptiste Daroussin 		fix_top_item(&all, i, 1);
9622a3e3873SBaptiste Daroussin 		break;
9632a3e3873SBaptiste Daroussin 	    case DLGK_PAGE_PREV:
9642a3e3873SBaptiste Daroussin 		if (cur_item > moi->top_index) {
9652a3e3873SBaptiste Daroussin 		    i = moi->top_index;
9662a3e3873SBaptiste Daroussin 		} else if (moi->top_index != 0) {
9672a3e3873SBaptiste Daroussin 		    int temp = at_top;
9682a3e3873SBaptiste Daroussin 		    if ((temp -= all.use_height) < 0)
9692a3e3873SBaptiste Daroussin 			temp = 0;
9702a3e3873SBaptiste Daroussin 		    i = row2index(&all, temp, which);
9712a3e3873SBaptiste Daroussin 		}
9722a3e3873SBaptiste Daroussin 		break;
9732a3e3873SBaptiste Daroussin 	    case DLGK_PAGE_NEXT:
9742a3e3873SBaptiste Daroussin 		if ((at_end - at_bot) < all.use_height) {
9752a3e3873SBaptiste Daroussin 		    i = next_item(&all,
9762a3e3873SBaptiste Daroussin 				  row2index(&all, at_end, which),
9772a3e3873SBaptiste Daroussin 				  which);
9782a3e3873SBaptiste Daroussin 		} else {
9792a3e3873SBaptiste Daroussin 		    i = next_item(&all,
9802a3e3873SBaptiste Daroussin 				  row2index(&all, at_bot, which),
9812a3e3873SBaptiste Daroussin 				  which);
9822a3e3873SBaptiste Daroussin 		    at_top = at_bot;
9832a3e3873SBaptiste Daroussin 		    set_top_item(&all,
9842a3e3873SBaptiste Daroussin 				 next_item(&all,
9852a3e3873SBaptiste Daroussin 					   row2index(&all, at_top, which),
9862a3e3873SBaptiste Daroussin 					   which),
9872a3e3873SBaptiste Daroussin 				 which);
9882a3e3873SBaptiste Daroussin 		    at_bot = skip_rows(&all, at_top, all.use_height, which);
9892a3e3873SBaptiste Daroussin 		    at_bot = MIN(at_bot, at_end);
9902a3e3873SBaptiste Daroussin 		}
9912a3e3873SBaptiste Daroussin 		break;
9922a3e3873SBaptiste Daroussin 	    case DLGK_ITEM_FIRST:
9932a3e3873SBaptiste Daroussin 		i = first_item(&all, which);
9942a3e3873SBaptiste Daroussin 		break;
9952a3e3873SBaptiste Daroussin 	    case DLGK_ITEM_LAST:
9962a3e3873SBaptiste Daroussin 		i = last_item(&all, which);
9972a3e3873SBaptiste Daroussin 		break;
9982a3e3873SBaptiste Daroussin 	    case DLGK_ITEM_PREV:
9992a3e3873SBaptiste Daroussin 		i = prev_item(&all, cur_item, which);
10002a3e3873SBaptiste Daroussin 		if (stop_prev(&all, cur_item, which))
10012a3e3873SBaptiste Daroussin 		    continue;
10022a3e3873SBaptiste Daroussin 		break;
10032a3e3873SBaptiste Daroussin 	    case DLGK_ITEM_NEXT:
10042a3e3873SBaptiste Daroussin 		i = next_item(&all, cur_item, which);
10052a3e3873SBaptiste Daroussin 		break;
10062a3e3873SBaptiste Daroussin 	    default:
10072a3e3873SBaptiste Daroussin 		found = FALSE;
10082a3e3873SBaptiste Daroussin 		break;
10092a3e3873SBaptiste Daroussin 	    }
10102a3e3873SBaptiste Daroussin 	}
10112a3e3873SBaptiste Daroussin 
10122a3e3873SBaptiste Daroussin 	if (found) {
10132a3e3873SBaptiste Daroussin 	    if (i != cur_item) {
10142a3e3873SBaptiste Daroussin 		int now_at = index2row(&all, i, which);
10152a3e3873SBaptiste Daroussin 		int oops = item_no;
10162a3e3873SBaptiste Daroussin 		int old_item;
10172a3e3873SBaptiste Daroussin 
1018f4f33ea0SBaptiste Daroussin 		DLG_TRACE(("# <--CHOICE %d\n", i));
1019f4f33ea0SBaptiste Daroussin 		DLG_TRACE(("# <--topITM %d\n", moi->top_index));
1020f4f33ea0SBaptiste Daroussin 		DLG_TRACE(("# <--now_at %d\n", now_at));
1021f4f33ea0SBaptiste Daroussin 		DLG_TRACE(("# <--at_top %d\n", at_top));
1022f4f33ea0SBaptiste Daroussin 		DLG_TRACE(("# <--at_bot %d\n", at_bot));
10232a3e3873SBaptiste Daroussin 
10242a3e3873SBaptiste Daroussin 		if (now_at >= at_bot) {
10252a3e3873SBaptiste Daroussin 		    while (now_at >= at_bot) {
10262a3e3873SBaptiste Daroussin 			if ((at_bot - at_top) >= all.use_height) {
10272a3e3873SBaptiste Daroussin 			    set_top_item(&all,
10282a3e3873SBaptiste Daroussin 					 next_item(&all, moi->top_index, which),
10292a3e3873SBaptiste Daroussin 					 which);
10302a3e3873SBaptiste Daroussin 			}
10312a3e3873SBaptiste Daroussin 			at_top = index2row(&all, moi->top_index, which);
10322a3e3873SBaptiste Daroussin 			at_bot = skip_rows(&all, at_top, all.use_height, which);
10332a3e3873SBaptiste Daroussin 
1034f4f33ea0SBaptiste Daroussin 			DLG_TRACE(("# ...at_bot %d (now %d vs %d)\n",
1035f4f33ea0SBaptiste Daroussin 				   at_bot, now_at, at_end));
1036f4f33ea0SBaptiste Daroussin 			DLG_TRACE(("# ...topITM %d\n", moi->top_index));
1037f4f33ea0SBaptiste Daroussin 			DLG_TRACE(("# ...at_top %d (diff %d)\n", at_top,
1038f4f33ea0SBaptiste Daroussin 				   at_bot - at_top));
10392a3e3873SBaptiste Daroussin 
10402a3e3873SBaptiste Daroussin 			if (at_bot >= at_end) {
10412a3e3873SBaptiste Daroussin 			    /*
10422a3e3873SBaptiste Daroussin 			     * If we bumped into the end, move the top-item
10432a3e3873SBaptiste Daroussin 			     * down by one line so that we can display the
10442a3e3873SBaptiste Daroussin 			     * last item in the list.
10452a3e3873SBaptiste Daroussin 			     */
10462a3e3873SBaptiste Daroussin 			    if ((at_bot - at_top) > all.use_height) {
10472a3e3873SBaptiste Daroussin 				set_top_item(&all,
10482a3e3873SBaptiste Daroussin 					     next_item(&all, moi->top_index, which),
10492a3e3873SBaptiste Daroussin 					     which);
10502a3e3873SBaptiste Daroussin 			    } else if (at_top > 0 &&
10512a3e3873SBaptiste Daroussin 				       (at_bot - at_top) >= all.use_height) {
10522a3e3873SBaptiste Daroussin 				set_top_item(&all,
10532a3e3873SBaptiste Daroussin 					     next_item(&all, moi->top_index, which),
10542a3e3873SBaptiste Daroussin 					     which);
10552a3e3873SBaptiste Daroussin 			    }
10562a3e3873SBaptiste Daroussin 			    break;
10572a3e3873SBaptiste Daroussin 			}
10582a3e3873SBaptiste Daroussin 			if (--oops < 0) {
1059f4f33ea0SBaptiste Daroussin 			    DLG_TRACE(("# OOPS-forward\n"));
10602a3e3873SBaptiste Daroussin 			    break;
10612a3e3873SBaptiste Daroussin 			}
10622a3e3873SBaptiste Daroussin 		    }
10632a3e3873SBaptiste Daroussin 		} else if (now_at < at_top) {
10642a3e3873SBaptiste Daroussin 		    while (now_at < at_top) {
10652a3e3873SBaptiste Daroussin 			old_item = moi->top_index;
10662a3e3873SBaptiste Daroussin 			set_top_item(&all,
10672a3e3873SBaptiste Daroussin 				     prev_item(&all, moi->top_index, which),
10682a3e3873SBaptiste Daroussin 				     which);
10692a3e3873SBaptiste Daroussin 			at_top = index2row(&all, moi->top_index, which);
10702a3e3873SBaptiste Daroussin 
1071f4f33ea0SBaptiste Daroussin 			DLG_TRACE(("# ...at_top %d (now %d)\n", at_top, now_at));
1072f4f33ea0SBaptiste Daroussin 			DLG_TRACE(("# ...topITM %d\n", moi->top_index));
10732a3e3873SBaptiste Daroussin 
10742a3e3873SBaptiste Daroussin 			if (moi->top_index >= old_item)
10752a3e3873SBaptiste Daroussin 			    break;
10762a3e3873SBaptiste Daroussin 			if (at_top <= now_at)
10772a3e3873SBaptiste Daroussin 			    break;
10782a3e3873SBaptiste Daroussin 			if (--oops < 0) {
1079f4f33ea0SBaptiste Daroussin 			    DLG_TRACE(("# OOPS-backward\n"));
10802a3e3873SBaptiste Daroussin 			    break;
10812a3e3873SBaptiste Daroussin 			}
10822a3e3873SBaptiste Daroussin 		    }
10832a3e3873SBaptiste Daroussin 		}
1084f4f33ea0SBaptiste Daroussin 		DLG_TRACE(("# -->now_at %d\n", now_at));
10852a3e3873SBaptiste Daroussin 		cur_item = i;
10862a3e3873SBaptiste Daroussin 		print_both(&all, cur_item);
10872a3e3873SBaptiste Daroussin 	    }
10882a3e3873SBaptiste Daroussin 	    dlg_trace_win(dialog);
10892a3e3873SBaptiste Daroussin 	    continue;		/* wait for another key press */
10902a3e3873SBaptiste Daroussin 	}
10912a3e3873SBaptiste Daroussin 
10922a3e3873SBaptiste Daroussin 	if (fkey) {
10932a3e3873SBaptiste Daroussin 	    switch (key) {
10942a3e3873SBaptiste Daroussin 	    case DLGK_ENTER:
10952a3e3873SBaptiste Daroussin 		result = dlg_enter_buttoncode(button);
10962a3e3873SBaptiste Daroussin 		break;
1097*a96ef450SBaptiste Daroussin 	    case DLGK_LEAVE:
1098*a96ef450SBaptiste Daroussin 		result = dlg_ok_buttoncode(button);
1099*a96ef450SBaptiste Daroussin 		break;
11002a3e3873SBaptiste Daroussin #ifdef KEY_RESIZE
11012a3e3873SBaptiste Daroussin 	    case KEY_RESIZE:
1102f4f33ea0SBaptiste Daroussin 		dlg_will_resize(dialog);
11032a3e3873SBaptiste Daroussin 		/* reset data */
11042a3e3873SBaptiste Daroussin 		height = old_height;
11052a3e3873SBaptiste Daroussin 		width = old_width;
1106f4f33ea0SBaptiste Daroussin 		free(prompt);
1107*a96ef450SBaptiste Daroussin 		_dlg_resize_cleanup(dialog);
1108f4f33ea0SBaptiste Daroussin 		/* repaint */
1109f4f33ea0SBaptiste Daroussin 		first = TRUE;
11102a3e3873SBaptiste Daroussin 		goto retry;
11112a3e3873SBaptiste Daroussin #endif
11122a3e3873SBaptiste Daroussin 	    default:
11132a3e3873SBaptiste Daroussin 		if (was_mouse) {
11142a3e3873SBaptiste Daroussin 		    if ((key2 = dlg_ok_buttoncode(key)) >= 0) {
11152a3e3873SBaptiste Daroussin 			result = key2;
11162a3e3873SBaptiste Daroussin 			break;
11172a3e3873SBaptiste Daroussin 		    }
11182a3e3873SBaptiste Daroussin 		    beep();
11192a3e3873SBaptiste Daroussin 		}
11202a3e3873SBaptiste Daroussin 	    }
1121*a96ef450SBaptiste Daroussin 	} else if (key > 0) {
11222a3e3873SBaptiste Daroussin 	    beep();
11232a3e3873SBaptiste Daroussin 	}
11242a3e3873SBaptiste Daroussin     }
11252a3e3873SBaptiste Daroussin 
1126f4f33ea0SBaptiste Daroussin     /*
1127f4f33ea0SBaptiste Daroussin      * If told to re-order the list, update it to reflect the current display:
1128f4f33ea0SBaptiste Daroussin      * a) The left-side will be at the beginning, without gaps.
1129f4f33ea0SBaptiste Daroussin      * b) The right-side will follow, in display-order.
1130f4f33ea0SBaptiste Daroussin      */
1131f4f33ea0SBaptiste Daroussin     if (order_mode) {
1132f4f33ea0SBaptiste Daroussin 	DIALOG_LISTITEM *redo;
1133f4f33ea0SBaptiste Daroussin 	int row;
1134f4f33ea0SBaptiste Daroussin 	int choice;
1135f4f33ea0SBaptiste Daroussin 	int new_item = cur_item;
1136f4f33ea0SBaptiste Daroussin 
1137f4f33ea0SBaptiste Daroussin 	redo = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1);
1138f4f33ea0SBaptiste Daroussin 	assert_ptr(redo, THIS_FUNC);
1139f4f33ea0SBaptiste Daroussin 
1140f4f33ea0SBaptiste Daroussin 	j = 0;
1141f4f33ea0SBaptiste Daroussin 	for (k = 0; k < 2; ++k) {
1142f4f33ea0SBaptiste Daroussin 	    for (row = 0; row < item_no; ++row) {
1143f4f33ea0SBaptiste Daroussin 		if (myItem(all.list + k, row) == 0)
1144f4f33ea0SBaptiste Daroussin 		    break;
1145f4f33ea0SBaptiste Daroussin 		choice = row2index(&all, row, k);
1146f4f33ea0SBaptiste Daroussin 		if (choice == cur_item)
1147f4f33ea0SBaptiste Daroussin 		    new_item = j;
1148f4f33ea0SBaptiste Daroussin 		redo[j++] = items[choice];
1149f4f33ea0SBaptiste Daroussin 	    }
1150f4f33ea0SBaptiste Daroussin 	}
1151f4f33ea0SBaptiste Daroussin 
1152f4f33ea0SBaptiste Daroussin 	cur_item = new_item;
1153f4f33ea0SBaptiste Daroussin 	memcpy(items, redo, sizeof(DIALOG_LISTITEM) * (size_t) (item_no + 1));
1154f4f33ea0SBaptiste Daroussin 
1155f4f33ea0SBaptiste Daroussin 	free(redo);
1156f4f33ea0SBaptiste Daroussin     }
1157f4f33ea0SBaptiste Daroussin 
1158f4f33ea0SBaptiste Daroussin     for (k = 0; k < 2; ++k) {
1159f4f33ea0SBaptiste Daroussin 	free(data[k].ip);
1160f4f33ea0SBaptiste Daroussin     }
1161f4f33ea0SBaptiste Daroussin 
11622a3e3873SBaptiste Daroussin     dialog_state.visit_cols = save_visit;
11632a3e3873SBaptiste Daroussin     dlg_del_window(dialog);
11642a3e3873SBaptiste Daroussin     dlg_mouse_free_regions();
11652a3e3873SBaptiste Daroussin     free(prompt);
1166f4f33ea0SBaptiste Daroussin 
11672a3e3873SBaptiste Daroussin     *current_item = cur_item;
11682a3e3873SBaptiste Daroussin     return result;
1169f4f33ea0SBaptiste Daroussin #undef THIS_FUNC
11702a3e3873SBaptiste Daroussin }
11712a3e3873SBaptiste Daroussin 
11722a3e3873SBaptiste Daroussin /*
11732a3e3873SBaptiste Daroussin  * Display a dialog box with a list of options that can be turned on or off
11742a3e3873SBaptiste Daroussin  */
11752a3e3873SBaptiste Daroussin int
dialog_buildlist(const char * title,const char * cprompt,int height,int width,int list_height,int item_no,char ** items,int order_mode)11762a3e3873SBaptiste Daroussin dialog_buildlist(const char *title,
11772a3e3873SBaptiste Daroussin 		 const char *cprompt,
11782a3e3873SBaptiste Daroussin 		 int height,
11792a3e3873SBaptiste Daroussin 		 int width,
11802a3e3873SBaptiste Daroussin 		 int list_height,
11812a3e3873SBaptiste Daroussin 		 int item_no,
11822a3e3873SBaptiste Daroussin 		 char **items,
11832a3e3873SBaptiste Daroussin 		 int order_mode)
11842a3e3873SBaptiste Daroussin {
1185f4f33ea0SBaptiste Daroussin #define THIS_FUNC "dialog_buildlist"
11862a3e3873SBaptiste Daroussin     int result;
11872a3e3873SBaptiste Daroussin     int i, j;
11882a3e3873SBaptiste Daroussin     DIALOG_LISTITEM *listitems;
11892a3e3873SBaptiste Daroussin     bool separate_output = dialog_vars.separate_output;
11902a3e3873SBaptiste Daroussin     bool show_status = FALSE;
11912a3e3873SBaptiste Daroussin     int current = 0;
1192febdb468SDevin Teske     char *help_result;
11932a3e3873SBaptiste Daroussin 
1194f4f33ea0SBaptiste Daroussin     DLG_TRACE(("# buildlist args:\n"));
1195f4f33ea0SBaptiste Daroussin     DLG_TRACE2S("title", title);
1196f4f33ea0SBaptiste Daroussin     DLG_TRACE2S("message", cprompt);
1197f4f33ea0SBaptiste Daroussin     DLG_TRACE2N("height", height);
1198f4f33ea0SBaptiste Daroussin     DLG_TRACE2N("width", width);
1199f4f33ea0SBaptiste Daroussin     DLG_TRACE2N("lheight", list_height);
1200f4f33ea0SBaptiste Daroussin     DLG_TRACE2N("llength", item_no);
1201f4f33ea0SBaptiste Daroussin     /* FIXME dump the items[][] too */
1202f4f33ea0SBaptiste Daroussin     DLG_TRACE2N("order", order_mode != 0);
1203f4f33ea0SBaptiste Daroussin 
12042a3e3873SBaptiste Daroussin     listitems = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1);
1205f4f33ea0SBaptiste Daroussin     assert_ptr(listitems, THIS_FUNC);
12062a3e3873SBaptiste Daroussin 
12072a3e3873SBaptiste Daroussin     for (i = j = 0; i < item_no; ++i) {
12082a3e3873SBaptiste Daroussin 	listitems[i].name = items[j++];
12092a3e3873SBaptiste Daroussin 	listitems[i].text = (dialog_vars.no_items
12102a3e3873SBaptiste Daroussin 			     ? dlg_strempty()
12112a3e3873SBaptiste Daroussin 			     : items[j++]);
12122a3e3873SBaptiste Daroussin 	listitems[i].state = !dlg_strcmp(items[j++], "on");
12132a3e3873SBaptiste Daroussin 	listitems[i].help = ((dialog_vars.item_help)
12142a3e3873SBaptiste Daroussin 			     ? items[j++]
12152a3e3873SBaptiste Daroussin 			     : dlg_strempty());
12162a3e3873SBaptiste Daroussin     }
12172a3e3873SBaptiste Daroussin     dlg_align_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
12182a3e3873SBaptiste Daroussin 
12192a3e3873SBaptiste Daroussin     result = dlg_buildlist(title,
12202a3e3873SBaptiste Daroussin 			   cprompt,
12212a3e3873SBaptiste Daroussin 			   height,
12222a3e3873SBaptiste Daroussin 			   width,
12232a3e3873SBaptiste Daroussin 			   list_height,
12242a3e3873SBaptiste Daroussin 			   item_no,
12252a3e3873SBaptiste Daroussin 			   listitems,
12262a3e3873SBaptiste Daroussin 			   NULL,
12272a3e3873SBaptiste Daroussin 			   order_mode,
12282a3e3873SBaptiste Daroussin 			   &current);
12292a3e3873SBaptiste Daroussin 
12302a3e3873SBaptiste Daroussin     switch (result) {
12312a3e3873SBaptiste Daroussin     case DLG_EXIT_OK:		/* FALLTHRU */
12322a3e3873SBaptiste Daroussin     case DLG_EXIT_EXTRA:
12332a3e3873SBaptiste Daroussin 	show_status = TRUE;
12342a3e3873SBaptiste Daroussin 	break;
12352a3e3873SBaptiste Daroussin     case DLG_EXIT_HELP:
1236febdb468SDevin Teske 	dlg_add_help_listitem(&result, &help_result, &listitems[current]);
1237febdb468SDevin Teske 	if ((show_status = dialog_vars.help_status)) {
12382a3e3873SBaptiste Daroussin 	    if (separate_output) {
1239febdb468SDevin Teske 		dlg_add_string(help_result);
12402a3e3873SBaptiste Daroussin 	    } else {
1241febdb468SDevin Teske 		dlg_add_quoted(help_result);
12422a3e3873SBaptiste Daroussin 	    }
12432a3e3873SBaptiste Daroussin 	} else {
1244febdb468SDevin Teske 	    dlg_add_string(help_result);
12452a3e3873SBaptiste Daroussin 	}
12462a3e3873SBaptiste Daroussin 	break;
12472a3e3873SBaptiste Daroussin     }
12482a3e3873SBaptiste Daroussin 
12492a3e3873SBaptiste Daroussin     if (show_status) {
12502a3e3873SBaptiste Daroussin 	for (i = 0; i < item_no; i++) {
12512a3e3873SBaptiste Daroussin 	    if (listitems[i].state) {
12522a3e3873SBaptiste Daroussin 		if (dlg_need_separator())
12532a3e3873SBaptiste Daroussin 		    dlg_add_separator();
1254*a96ef450SBaptiste Daroussin 		if (separate_output) {
1255*a96ef450SBaptiste Daroussin 		    dlg_add_string(listitems[i].name);
1256*a96ef450SBaptiste Daroussin 		} else {
12572a3e3873SBaptiste Daroussin 		    dlg_add_quoted(listitems[i].name);
12582a3e3873SBaptiste Daroussin 		}
12592a3e3873SBaptiste Daroussin 	    }
12602a3e3873SBaptiste Daroussin 	}
1261*a96ef450SBaptiste Daroussin 	AddLastKey();
12622a3e3873SBaptiste Daroussin     }
12632a3e3873SBaptiste Daroussin 
12642a3e3873SBaptiste Daroussin     dlg_free_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
12652a3e3873SBaptiste Daroussin     free(listitems);
12662a3e3873SBaptiste Daroussin     return result;
1267f4f33ea0SBaptiste Daroussin #undef THIS_FUNC
12682a3e3873SBaptiste Daroussin }
1269