kernel - Improve basic entropy collector
[dragonfly.git] / contrib / dialog / buildlist.c
blob362b75fd6e67bf3ef08f2df1ee460ef983348a0e
1 /*
2 * $Id: buildlist.c,v 1.56 2012/12/31 00:38:57 tom Exp $
4 * buildlist.c -- implements the buildlist dialog
6 * Copyright 2012 Thomas E. Dickey
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License, version 2.1
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to
19 * Free Software Foundation, Inc.
20 * 51 Franklin St., Fifth Floor
21 * Boston, MA 02110, USA.
24 #include <dialog.h>
25 #include <dlg_keys.h>
28 * Visually like menubox, but two columns.
31 #define sLEFT (-2)
32 #define sRIGHT (-1)
34 #define KEY_TOGGLE ' '
35 #define KEY_LEFTCOL '^'
36 #define KEY_RIGHTCOL '$'
38 #define MIN_HIGH (1 + (5 * MARGIN))
40 typedef struct {
41 WINDOW *win;
42 int box_y;
43 int box_x;
44 int top_index;
45 int cur_index;
46 } MY_DATA;
48 typedef struct {
49 DIALOG_LISTITEM *items;
50 int base_y; /* base for mouse coordinates */
51 int base_x;
52 int use_height; /* actual size of column box */
53 int use_width;
54 int item_no;
55 int check_x;
56 int item_x;
57 MY_DATA list[2];
58 } ALL_DATA;
61 * Print list item. The 'selected' parameter is true if 'choice' is the
62 * current item. That one is colored differently from the other items.
64 static void
65 print_item(ALL_DATA * data,
66 WINDOW *win,
67 DIALOG_LISTITEM * item,
68 int choice,
69 int selected)
71 chtype save = dlg_get_attrs(win);
72 int i;
73 bool both = (!dialog_vars.no_tags && !dialog_vars.no_items);
74 bool first = TRUE;
75 int climit = (data->item_x - data->check_x - 1);
76 const char *show = (dialog_vars.no_items
77 ? item->name
78 : item->text);
80 /* Clear 'residue' of last item */
81 (void) wattrset(win, menubox_attr);
82 (void) wmove(win, choice, 0);
83 for (i = 0; i < getmaxx(win); i++)
84 (void) waddch(win, ' ');
86 (void) wmove(win, choice, data->check_x);
87 (void) wattrset(win, menubox_attr);
89 if (both) {
90 dlg_print_listitem(win, item->name, climit, first, selected);
91 (void) waddch(win, ' ');
92 first = FALSE;
95 (void) wmove(win, choice, data->item_x);
96 climit = (getmaxx(win) - data->item_x + 1);
97 dlg_print_listitem(win, show, climit, first, selected);
99 if (selected) {
100 dlg_item_help(item->help);
102 (void) wattrset(win, save);
106 * Prints either the left (unselected) or right (selected) list.
108 static void
109 print_1_list(ALL_DATA * data,
110 int choice,
111 int selected)
113 MY_DATA *moi = data->list + selected;
114 WINDOW *win = moi->win;
115 int i, j;
116 int last = 0;
117 int max_rows = getmaxy(win);
119 for (i = j = 0; j < max_rows; i++) {
120 int ii = i + moi->top_index;
121 if (ii >= data->item_no) {
122 break;
123 } else if (!(selected ^ (data->items[ii].state != 0))) {
124 print_item(data,
125 win,
126 &data->items[ii],
127 j, ii == choice);
128 last = ++j;
131 if (wmove(win, last, 0) != ERR)
132 wclrtobot(win);
133 (void) wnoutrefresh(win);
137 * Return the previous item from the list, staying in the same column. If no
138 * further movement is possible, return the same choice as given.
140 static int
141 prev_item(ALL_DATA * data, int choice, int selected)
143 int result = choice;
144 int n;
146 for (n = choice - 1; n >= 0; --n) {
147 if ((data->items[n].state != 0) == selected) {
148 result = n;
149 break;
152 return result;
156 * Return true if the given choice is on the first page in the current column.
158 static bool
159 stop_prev(ALL_DATA * data, int choice, int selected)
161 return (prev_item(data, choice, selected) == choice);
164 static bool
165 check_hotkey(DIALOG_LISTITEM * items, int choice, int selected)
167 bool result = FALSE;
169 if ((items[choice].state != 0) == selected) {
170 if (dlg_match_char(dlg_last_getc(),
171 (dialog_vars.no_tags
172 ? items[choice].text
173 : items[choice].name))) {
174 result = TRUE;
177 return result;
181 * Return the next item from the list, staying in the same column. If no
182 * further movement is possible, return the same choice as given.
184 static int
185 next_item(ALL_DATA * data, int choice, int selected)
187 int result = choice;
188 int n;
190 for (n = choice + 1; n < data->item_no; ++n) {
191 if ((data->items[n].state != 0) == selected) {
192 result = n;
193 break;
196 dlg_trace_msg("next_item(%d) ->%d\n", choice, result);
197 return result;
201 * Translate a choice from items[] to a row-number in an unbounded column,
202 * starting at zero.
204 static int
205 index2row(ALL_DATA * data, int choice, int selected)
207 int result = -1;
208 int n;
209 for (n = 0; n < data->item_no; ++n) {
210 if ((data->items[n].state != 0) == selected) {
211 ++result;
213 if (n == choice)
214 break;
216 return result;
220 * Return the first choice from items[] for the given column.
222 static int
223 first_item(ALL_DATA * data, int selected)
225 int result = -1;
226 int n;
228 for (n = 0; n < data->item_no; ++n) {
229 if ((data->items[n].state != 0) == selected) {
230 result = n;
231 break;
234 return result;
238 * Return the last choice from items[] for the given column.
240 static int
241 last_item(ALL_DATA * data, int selected)
243 int result = -1;
244 int n;
246 for (n = data->item_no - 1; n >= 0; --n) {
247 if ((data->items[n].state != 0) == selected) {
248 result = n;
249 break;
252 return result;
256 * Convert a row-number back to an item number, i.e., index into items[].
258 static int
259 row2index(ALL_DATA * data, int row, int selected)
261 int result = -1;
262 int n;
263 for (n = 0; n < data->item_no; ++n) {
264 if ((data->items[n].state != 0) == selected) {
265 if (row-- <= 0) {
266 result = n;
267 break;
271 return result;
274 static int
275 skip_rows(ALL_DATA * data, int row, int skip, int selected)
277 int choice = row2index(data, row, selected);
278 int result = row;
279 int n;
280 if (skip > 0) {
281 for (n = choice + 1; n < data->item_no; ++n) {
282 if ((data->items[n].state != 0) == selected) {
283 ++result;
284 if (--skip <= 0)
285 break;
288 } else if (skip < 0) {
289 for (n = choice - 1; n >= 0; --n) {
290 if ((data->items[n].state != 0) == selected) {
291 --result;
292 if (++skip >= 0)
293 break;
297 return result;
301 * Find the closest item in the given column starting with the given choice.
303 static int
304 closest_item(ALL_DATA * data, int choice, int selected)
306 int prev = choice;
307 int next = choice;
308 int result = choice;
309 int n;
311 for (n = choice; n >= 0; --n) {
312 if ((data->items[n].state != 0) == selected) {
313 prev = n;
314 break;
317 for (n = choice; n < data->item_no; ++n) {
318 if ((data->items[n].state != 0) == selected) {
319 next = n;
320 break;
323 if (prev != choice) {
324 result = prev;
325 if (next != choice) {
326 if ((choice - prev) > (next - choice)) {
327 result = next;
330 } else if (next != choice) {
331 result = next;
333 return result;
336 static void
337 print_both(ALL_DATA * data,
338 int choice)
340 int selected;
341 int cur_y, cur_x;
342 WINDOW *dialog = wgetparent(data->list[0].win);
344 getyx(dialog, cur_y, cur_x);
345 for (selected = 0; selected < 2; ++selected) {
346 MY_DATA *moi = data->list + selected;
347 WINDOW *win = moi->win;
348 int thumb_top = index2row(data, moi->top_index, selected);
349 int thumb_max = index2row(data, -1, selected);
350 int thumb_end = thumb_top + getmaxy(win);
352 print_1_list(data, choice, selected);
354 dlg_mouse_setcode(selected * KEY_MAX);
355 dlg_draw_scrollbar(dialog,
356 (long) (moi->top_index),
357 (long) (thumb_top),
358 (long) MIN(thumb_end, thumb_max),
359 (long) thumb_max,
360 moi->box_x + data->check_x,
361 moi->box_x + getmaxx(win),
362 moi->box_y,
363 moi->box_y + getmaxy(win) + 1,
364 menubox_border2_attr,
365 menubox_border_attr);
367 (void) wmove(dialog, cur_y, cur_x);
368 dlg_mouse_setcode(0);
371 static void
372 set_top_item(ALL_DATA * data, int value, int selected)
374 if (value != data->list[selected].top_index) {
375 dlg_trace_msg("set top of %s column to %d\n",
376 selected ? "right" : "left",
377 value);
378 data->list[selected].top_index = value;
383 * Adjust the top-index as needed to ensure that it and the given item are
384 * visible.
386 static void
387 fix_top_item(ALL_DATA * data, int cur_item, int selected)
389 int top_item = data->list[selected].top_index;
390 int cur_row = index2row(data, cur_item, selected);
391 int top_row = index2row(data, top_item, selected);
393 if (cur_row < top_row) {
394 top_item = cur_item;
395 } else if ((cur_row - top_row) > data->use_height) {
396 top_item = row2index(data, cur_row + 1 - data->use_height, selected);
398 if (cur_row < data->use_height) {
399 top_item = row2index(data, 0, selected);
401 dlg_trace_msg("fix_top_item(cur_item %d, selected %d) ->top_item %d\n",
402 cur_item, selected, top_item);
403 set_top_item(data, top_item, selected);
407 * This is an alternate interface to 'buildlist' which allows the application
408 * to read the list item states back directly without putting them in the
409 * output buffer.
412 dlg_buildlist(const char *title,
413 const char *cprompt,
414 int height,
415 int width,
416 int list_height,
417 int item_no,
418 DIALOG_LISTITEM * items,
419 const char *states,
420 int order_mode,
421 int *current_item)
423 /* *INDENT-OFF* */
424 static DLG_KEYS_BINDING binding[] = {
425 HELPKEY_BINDINGS,
426 ENTERKEY_BINDINGS,
427 DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ),
428 DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
429 DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
430 DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_LEFT ),
431 DLG_KEYS_DATA( DLGK_ITEM_FIRST, KEY_HOME ),
432 DLG_KEYS_DATA( DLGK_ITEM_LAST, KEY_END ),
433 DLG_KEYS_DATA( DLGK_ITEM_LAST, KEY_LL ),
434 DLG_KEYS_DATA( DLGK_ITEM_NEXT, '+' ),
435 DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN ),
436 DLG_KEYS_DATA( DLGK_ITEM_NEXT, CHR_NEXT ),
437 DLG_KEYS_DATA( DLGK_ITEM_PREV, '-' ),
438 DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ),
439 DLG_KEYS_DATA( DLGK_ITEM_PREV, CHR_PREVIOUS ),
440 DLG_KEYS_DATA( DLGK_PAGE_NEXT, KEY_NPAGE ),
441 DLG_KEYS_DATA( DLGK_PAGE_NEXT, DLGK_MOUSE(KEY_NPAGE) ),
442 DLG_KEYS_DATA( DLGK_PAGE_NEXT, DLGK_MOUSE(KEY_NPAGE+KEY_MAX) ),
443 DLG_KEYS_DATA( DLGK_PAGE_PREV, KEY_PPAGE ),
444 DLG_KEYS_DATA( DLGK_PAGE_PREV, DLGK_MOUSE(KEY_PPAGE) ),
445 DLG_KEYS_DATA( DLGK_PAGE_PREV, DLGK_MOUSE(KEY_PPAGE+KEY_MAX) ),
446 DLG_KEYS_DATA( DLGK_GRID_LEFT, KEY_LEFTCOL ),
447 DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHTCOL ),
448 END_KEYS_BINDING
450 /* *INDENT-ON* */
452 #ifdef KEY_RESIZE
453 int old_height = height;
454 int old_width = width;
455 #endif
456 ALL_DATA all;
457 MY_DATA *data = all.list;
458 int i, j, k, key2, found, x, y, cur_x, cur_y;
459 int key = 0, fkey;
460 bool save_visit = dialog_state.visit_items;
461 int button;
462 int cur_item;
463 int was_mouse;
464 int name_width, text_width, full_width, list_width;
465 int result = DLG_EXIT_UNKNOWN;
466 int num_states;
467 bool first = TRUE;
468 WINDOW *dialog;
469 char *prompt = dlg_strclone(cprompt);
470 const char **buttons = dlg_ok_labels();
471 const char *widget_name = "buildlist";
473 (void) order_mode;
476 * Unlike other uses of --visit-items, we have two windows to visit.
478 if (dialog_state.visit_cols)
479 dialog_state.visit_cols = 2;
481 memset(&all, 0, sizeof(all));
482 all.items = items;
483 all.item_no = item_no;
485 if (dialog_vars.default_item != 0) {
486 cur_item = dlg_default_listitem(items);
487 } else {
488 if ((cur_item = first_item(&all, 0)) < 0)
489 cur_item = first_item(&all, 1);
491 button = (dialog_state.visit_items
492 ? (items[cur_item].state ? sRIGHT : sLEFT)
493 : dlg_default_button());
495 dlg_does_output();
496 dlg_tab_correct_str(prompt);
498 #ifdef KEY_RESIZE
499 retry:
500 #endif
502 all.use_height = list_height;
503 all.use_width = (2 * (dlg_calc_list_width(item_no, items)
505 + 2 * MARGIN)
506 + 1);
507 all.use_width = MAX(26, all.use_width);
508 if (all.use_height == 0) {
509 /* calculate height without items (4) */
510 dlg_auto_size(title, prompt, &height, &width, MIN_HIGH, all.use_width);
511 dlg_calc_listh(&height, &all.use_height, item_no);
512 } else {
513 dlg_auto_size(title, prompt,
514 &height, &width,
515 MIN_HIGH + all.use_height, all.use_width);
517 dlg_button_layout(buttons, &width);
518 dlg_print_size(height, width);
519 dlg_ctl_size(height, width);
521 /* we need at least two states */
522 if (states == 0 || strlen(states) < 2)
523 states = " *";
524 num_states = (int) strlen(states);
526 x = dlg_box_x_ordinate(width);
527 y = dlg_box_y_ordinate(height);
529 dialog = dlg_new_window(height, width, y, x);
530 dlg_register_window(dialog, widget_name, binding);
531 dlg_register_buttons(dialog, widget_name, buttons);
533 dlg_mouse_setbase(all.base_x = x, all.base_y = y);
535 dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
536 dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
537 dlg_draw_title(dialog, title);
539 (void) wattrset(dialog, dialog_attr);
540 dlg_print_autowrap(dialog, prompt, height, width);
542 list_width = (width - 6 * MARGIN - 2) / 2;
543 getyx(dialog, cur_y, cur_x);
544 data[0].box_y = cur_y + 1;
545 data[0].box_x = MARGIN + 1;
546 data[1].box_y = cur_y + 1;
547 data[1].box_x = data[0].box_x + 1 + 2 * MARGIN + list_width;
550 * After displaying the prompt, we know how much space we really have.
551 * Limit the list to avoid overwriting the ok-button.
553 if (all.use_height + MIN_HIGH > height - cur_y)
554 all.use_height = height - MIN_HIGH - cur_y;
555 if (all.use_height <= 0)
556 all.use_height = 1;
558 for (k = 0; k < 2; ++k) {
559 /* create new window for the list */
560 data[k].win = dlg_sub_window(dialog, all.use_height, list_width,
561 y + data[k].box_y + 1,
562 x + data[k].box_x + 1);
564 /* draw a box around the list items */
565 dlg_draw_box(dialog, data[k].box_y, data[k].box_x,
566 all.use_height + 2 * MARGIN,
567 list_width + 2 * MARGIN,
568 menubox_border_attr, menubox_border2_attr);
571 text_width = 0;
572 name_width = 0;
573 /* Find length of longest item to center buildlist */
574 for (i = 0; i < item_no; i++) {
575 text_width = MAX(text_width, dlg_count_columns(items[i].text));
576 name_width = MAX(name_width, dlg_count_columns(items[i].name));
579 /* If the name+text is wider than the list is allowed, then truncate
580 * one or both of them. If the name is no wider than 1/4 of the list,
581 * leave it intact.
583 all.use_width = (list_width - 6 * MARGIN);
584 if (dialog_vars.no_tags && !dialog_vars.no_items) {
585 full_width = MIN(all.use_width, text_width);
586 } else if (dialog_vars.no_items) {
587 full_width = MIN(all.use_width, name_width);
588 } else {
589 if (text_width >= 0
590 && name_width >= 0
591 && all.use_width > 0
592 && text_width + name_width > all.use_width) {
593 int need = (int) (0.25 * all.use_width);
594 if (name_width > need) {
595 int want = (int) (all.use_width * ((double) name_width) /
596 (text_width + name_width));
597 name_width = (want > need) ? want : need;
599 text_width = all.use_width - name_width;
601 full_width = text_width + name_width;
604 all.check_x = (all.use_width - full_width) / 2;
605 all.item_x = ((dialog_vars.no_tags
607 : (dialog_vars.no_items
609 : (name_width + 2)))
610 + all.check_x);
612 /* ensure we are scrolled to show the current choice */
613 j = MIN(all.use_height, item_no);
614 for (i = 0; i < 2; ++i) {
615 int top_item = 0;
616 if ((items[cur_item].state != 0) == i) {
617 top_item = cur_item - j + 1;
618 if (top_item < 0)
619 top_item = 0;
620 set_top_item(&all, top_item, i);
621 } else {
622 set_top_item(&all, 0, i);
626 /* register the new window, along with its borders */
627 for (i = 0; i < 2; ++i) {
628 dlg_mouse_mkbigregion(data[i].box_y + 1,
629 data[i].box_x,
630 all.use_height,
631 list_width + 2,
632 2 * KEY_MAX + (i * (1 + all.use_height)),
633 1, 1, 1 /* by lines */ );
636 dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
638 while (result == DLG_EXIT_UNKNOWN) {
639 int which = (items[cur_item].state != 0);
640 MY_DATA *moi = data + which;
641 int at_top = index2row(&all, moi->top_index, which);
642 int at_end = index2row(&all, -1, which);
643 int at_bot = skip_rows(&all, at_top, all.use_height, which);
645 dlg_trace_msg("\t** state %d:%d top %d (%d:%d:%d) %d\n",
646 cur_item, item_no - 1,
647 moi->top_index,
648 at_top, at_bot, at_end,
649 which);
651 if (first) {
652 print_both(&all, cur_item);
653 dlg_trace_win(dialog);
654 first = FALSE;
657 if (button < 0) { /* --visit-items */
658 int cur_row = index2row(&all, cur_item, which);
659 cur_y = (data[which].box_y
660 + cur_row
661 + 1);
662 if (at_top > 0)
663 cur_y -= at_top;
664 cur_x = (data[which].box_x
665 + all.check_x + 1);
666 dlg_trace_msg("\t...visit row %d (%d,%d)\n", cur_row, cur_y, cur_x);
667 wmove(dialog, cur_y, cur_x);
670 key = dlg_mouse_wgetch(dialog, &fkey);
671 if (dlg_result_key(key, fkey, &result))
672 break;
674 was_mouse = (fkey && is_DLGK_MOUSE(key));
675 if (was_mouse)
676 key -= M_EVENT;
678 if (!was_mouse) {
680 } else if (key >= 2 * KEY_MAX) {
681 i = (key - 2 * KEY_MAX) % (1 + all.use_height);
682 j = (key - 2 * KEY_MAX) / (1 + all.use_height);
683 k = row2index(&all, i + at_top, j);
684 dlg_trace_msg("MOUSE column %d, row %d ->item %d\n", j, i, k);
685 if (k >= 0 && j < 2) {
686 if (j != which) {
688 * Mouse click was in the other column.
690 moi = data + j;
691 fix_top_item(&all, k, j);
693 which = j;
694 at_top = index2row(&all, moi->top_index, which);
695 at_bot = skip_rows(&all, at_top, all.use_height, which);
696 cur_item = k;
697 print_both(&all, cur_item);
698 key = KEY_TOGGLE; /* force the selected item to toggle */
699 } else {
700 beep();
701 continue;
703 fkey = FALSE;
704 } else if (key >= KEY_MIN) {
705 if (key > KEY_MAX) {
706 if (which == 0) {
707 key = KEY_RIGHTCOL; /* switch to right-column */
708 fkey = FALSE;
709 } else {
710 key -= KEY_MAX;
712 } else {
713 if (which == 1) {
714 key = KEY_LEFTCOL; /* switch to left-column */
715 fkey = FALSE;
718 key = dlg_lookup_key(dialog, key, &fkey);
722 * A space toggles the item status. Normally we put the cursor on
723 * the next available item in the same column. But if there are no
724 * more items in the column, move the cursor to the other column.
726 if (key == KEY_TOGGLE) {
727 int new_choice;
728 int new_state = items[cur_item].state + 1;
730 if ((new_choice = next_item(&all, cur_item, which)) == cur_item) {
731 new_choice = prev_item(&all, cur_item, which);
733 dlg_trace_msg("cur_item %d, new_choice:%d\n", cur_item, new_choice);
734 if (new_state >= num_states)
735 new_state = 0;
737 items[cur_item].state = new_state;
738 if (cur_item == moi->top_index) {
739 set_top_item(&all, new_choice, which);
742 if (new_choice >= 0) {
743 fix_top_item(&all, cur_item, !which);
744 cur_item = new_choice;
746 print_both(&all, cur_item);
747 dlg_trace_win(dialog);
748 continue; /* wait for another key press */
752 * Check if key pressed matches first character of any item tag in
753 * list. If there is more than one match, we will cycle through
754 * each one as the same key is pressed repeatedly.
756 found = FALSE;
757 if (!fkey) {
758 if (button < 0 || !dialog_state.visit_items) {
759 for (j = cur_item + 1; j < item_no; j++) {
760 if (check_hotkey(items, j, which)) {
761 found = TRUE;
762 i = j;
763 break;
766 if (!found) {
767 for (j = 0; j <= cur_item; j++) {
768 if (check_hotkey(items, j, which)) {
769 found = TRUE;
770 i = j;
771 break;
775 if (found)
776 dlg_flush_getc();
777 } else if ((j = dlg_char_to_button(key, buttons)) >= 0) {
778 button = j;
779 ungetch('\n');
780 continue;
785 * A single digit (1-9) positions the selection to that line in the
786 * current screen.
788 if (!found
789 && (key <= '9')
790 && (key > '0')
791 && (key - '1' < at_bot)) {
792 found = TRUE;
793 i = key - '1';
796 if (!found && fkey) {
797 switch (key) {
798 case DLGK_FIELD_PREV:
799 if ((button == sRIGHT) && dialog_state.visit_items) {
800 key = DLGK_GRID_LEFT;
801 button = sLEFT;
802 } else {
803 button = dlg_prev_button(buttons, button);
804 dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
805 FALSE, width);
806 if (button == sRIGHT) {
807 key = DLGK_GRID_RIGHT;
808 } else {
809 continue;
812 break;
813 case DLGK_FIELD_NEXT:
814 if ((button == sLEFT) && dialog_state.visit_items) {
815 key = DLGK_GRID_RIGHT;
816 button = sRIGHT;
817 } else {
818 button = dlg_next_button(buttons, button);
819 dlg_draw_buttons(dialog, height - 2, 0, buttons, button,
820 FALSE, width);
821 if (button == sLEFT) {
822 key = DLGK_GRID_LEFT;
823 } else {
824 continue;
827 break;
831 if (!found && fkey) {
832 i = cur_item;
833 found = TRUE;
834 switch (key) {
835 case DLGK_GRID_LEFT:
836 i = closest_item(&all, cur_item, 0);
837 fix_top_item(&all, i, 0);
838 break;
839 case DLGK_GRID_RIGHT:
840 i = closest_item(&all, cur_item, 1);
841 fix_top_item(&all, i, 1);
842 break;
843 case DLGK_PAGE_PREV:
844 if (cur_item > moi->top_index) {
845 i = moi->top_index;
846 } else if (moi->top_index != 0) {
847 int temp = at_top;
848 if ((temp -= all.use_height) < 0)
849 temp = 0;
850 i = row2index(&all, temp, which);
852 break;
853 case DLGK_PAGE_NEXT:
854 if ((at_end - at_bot) < all.use_height) {
855 i = next_item(&all,
856 row2index(&all, at_end, which),
857 which);
858 } else {
859 i = next_item(&all,
860 row2index(&all, at_bot, which),
861 which);
862 at_top = at_bot;
863 set_top_item(&all,
864 next_item(&all,
865 row2index(&all, at_top, which),
866 which),
867 which);
868 at_bot = skip_rows(&all, at_top, all.use_height, which);
869 at_bot = MIN(at_bot, at_end);
871 break;
872 case DLGK_ITEM_FIRST:
873 i = first_item(&all, which);
874 break;
875 case DLGK_ITEM_LAST:
876 i = last_item(&all, which);
877 break;
878 case DLGK_ITEM_PREV:
879 i = prev_item(&all, cur_item, which);
880 if (stop_prev(&all, cur_item, which))
881 continue;
882 break;
883 case DLGK_ITEM_NEXT:
884 i = next_item(&all, cur_item, which);
885 break;
886 default:
887 found = FALSE;
888 break;
892 if (found) {
893 if (i != cur_item) {
894 int now_at = index2row(&all, i, which);
895 int oops = item_no;
896 int old_item;
898 dlg_trace_msg("<--CHOICE %d\n", i);
899 dlg_trace_msg("<--topITM %d\n", moi->top_index);
900 dlg_trace_msg("<--now_at %d\n", now_at);
901 dlg_trace_msg("<--at_top %d\n", at_top);
902 dlg_trace_msg("<--at_bot %d\n", at_bot);
904 if (now_at >= at_bot) {
905 while (now_at >= at_bot) {
906 if ((at_bot - at_top) >= all.use_height) {
907 set_top_item(&all,
908 next_item(&all, moi->top_index, which),
909 which);
911 at_top = index2row(&all, moi->top_index, which);
912 at_bot = skip_rows(&all, at_top, all.use_height, which);
914 dlg_trace_msg("...at_bot %d (now %d vs %d)\n",
915 at_bot, now_at, at_end);
916 dlg_trace_msg("...topITM %d\n", moi->top_index);
917 dlg_trace_msg("...at_top %d (diff %d)\n", at_top,
918 at_bot - at_top);
920 if (at_bot >= at_end) {
922 * If we bumped into the end, move the top-item
923 * down by one line so that we can display the
924 * last item in the list.
926 if ((at_bot - at_top) > all.use_height) {
927 set_top_item(&all,
928 next_item(&all, moi->top_index, which),
929 which);
930 } else if (at_top > 0 &&
931 (at_bot - at_top) >= all.use_height) {
932 set_top_item(&all,
933 next_item(&all, moi->top_index, which),
934 which);
936 break;
938 if (--oops < 0) {
939 dlg_trace_msg("OOPS-forward\n");
940 break;
943 } else if (now_at < at_top) {
944 while (now_at < at_top) {
945 old_item = moi->top_index;
946 set_top_item(&all,
947 prev_item(&all, moi->top_index, which),
948 which);
949 at_top = index2row(&all, moi->top_index, which);
951 dlg_trace_msg("...at_top %d (now %d)\n", at_top, now_at);
952 dlg_trace_msg("...topITM %d\n", moi->top_index);
954 if (moi->top_index >= old_item)
955 break;
956 if (at_top <= now_at)
957 break;
958 if (--oops < 0) {
959 dlg_trace_msg("OOPS-backward\n");
960 break;
964 dlg_trace_msg("-->now_at %d\n", now_at);
965 cur_item = i;
966 print_both(&all, cur_item);
968 dlg_trace_win(dialog);
969 continue; /* wait for another key press */
972 if (fkey) {
973 switch (key) {
974 case DLGK_ENTER:
975 result = dlg_enter_buttoncode(button);
976 break;
977 #ifdef KEY_RESIZE
978 case KEY_RESIZE:
979 /* reset data */
980 height = old_height;
981 width = old_width;
982 /* repaint */
983 dlg_clear();
984 dlg_del_window(dialog);
985 refresh();
986 dlg_mouse_free_regions();
987 goto retry;
988 #endif
989 default:
990 if (was_mouse) {
991 if ((key2 = dlg_ok_buttoncode(key)) >= 0) {
992 result = key2;
993 break;
995 beep();
998 } else {
999 beep();
1003 dialog_state.visit_cols = save_visit;
1004 dlg_del_window(dialog);
1005 dlg_mouse_free_regions();
1006 free(prompt);
1007 *current_item = cur_item;
1008 return result;
1012 * Display a dialog box with a list of options that can be turned on or off
1015 dialog_buildlist(const char *title,
1016 const char *cprompt,
1017 int height,
1018 int width,
1019 int list_height,
1020 int item_no,
1021 char **items,
1022 int order_mode)
1024 int result;
1025 int i, j;
1026 DIALOG_LISTITEM *listitems;
1027 bool separate_output = dialog_vars.separate_output;
1028 bool show_status = FALSE;
1029 int current = 0;
1031 listitems = dlg_calloc(DIALOG_LISTITEM, (size_t) item_no + 1);
1032 assert_ptr(listitems, "dialog_buildlist");
1034 for (i = j = 0; i < item_no; ++i) {
1035 listitems[i].name = items[j++];
1036 listitems[i].text = (dialog_vars.no_items
1037 ? dlg_strempty()
1038 : items[j++]);
1039 listitems[i].state = !dlg_strcmp(items[j++], "on");
1040 listitems[i].help = ((dialog_vars.item_help)
1041 ? items[j++]
1042 : dlg_strempty());
1044 dlg_align_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
1046 result = dlg_buildlist(title,
1047 cprompt,
1048 height,
1049 width,
1050 list_height,
1051 item_no,
1052 listitems,
1053 NULL,
1054 order_mode,
1055 &current);
1057 switch (result) {
1058 case DLG_EXIT_OK: /* FALLTHRU */
1059 case DLG_EXIT_EXTRA:
1060 show_status = TRUE;
1061 break;
1062 case DLG_EXIT_HELP:
1063 dlg_add_result("HELP ");
1064 show_status = dialog_vars.help_status;
1065 if (USE_ITEM_HELP(listitems[current].help)) {
1066 if (show_status) {
1067 if (separate_output) {
1068 dlg_add_string(listitems[current].help);
1069 dlg_add_separator();
1070 } else {
1071 dlg_add_quoted(listitems[current].help);
1073 } else {
1074 dlg_add_string(listitems[current].help);
1076 result = DLG_EXIT_ITEM_HELP;
1077 } else {
1078 if (show_status) {
1079 if (separate_output) {
1080 dlg_add_string(listitems[current].name);
1081 dlg_add_separator();
1082 } else {
1083 dlg_add_quoted(listitems[current].name);
1085 } else {
1086 dlg_add_string(listitems[current].name);
1089 break;
1092 if (show_status) {
1093 for (i = 0; i < item_no; i++) {
1094 if (listitems[i].state) {
1095 if (separate_output) {
1096 dlg_add_string(listitems[i].name);
1097 dlg_add_separator();
1098 } else {
1099 if (dlg_need_separator())
1100 dlg_add_separator();
1101 dlg_add_quoted(listitems[i].name);
1107 dlg_free_columns(&listitems[0].text, (int) sizeof(DIALOG_LISTITEM), item_no);
1108 free(listitems);
1109 return result;