1 /*
2  *  checklist.c -- implements the checklist box
3  *
4  *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5  *     Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
6  *     Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
7  *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
8  *
9  *  This program is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU General Public License
11  *  as published by the Free Software Foundation; either version 2
12  *  of the License, or (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 
24 #include "dialog.h"
25 
26 static int list_width, check_x, item_x;
27 
28 /*
29  * Print list item
30  */
31 static void print_item(WINDOW * win, int choice, int selected)
32 {
33 	int i;
34 	char *list_item = malloc(list_width + 1);
35 
36 	strncpy(list_item, item_str(), list_width - item_x);
37 	list_item[list_width - item_x] = '\0';
38 
39 	/* Clear 'residue' of last item */
40 	wattrset(win, dlg.menubox.atr);
41 	wmove(win, choice, 0);
42 	for (i = 0; i < list_width; i++)
43 		waddch(win, ' ');
44 
45 	wmove(win, choice, check_x);
46 	wattrset(win, selected ? dlg.check_selected.atr
47 		 : dlg.check.atr);
48 	if (!item_is_tag(':'))
49 		wprintw(win, "(%c)", item_is_tag('X') ? 'X' : ' ');
50 
51 	wattrset(win, selected ? dlg.tag_selected.atr : dlg.tag.atr);
52 	mvwaddch(win, choice, item_x, list_item[0]);
53 	wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
54 	waddstr(win, list_item + 1);
55 	if (selected) {
56 		wmove(win, choice, check_x + 1);
57 		wrefresh(win);
58 	}
59 	free(list_item);
60 }
61 
62 /*
63  * Print the scroll indicators.
64  */
65 static void print_arrows(WINDOW * win, int choice, int item_no, int scroll,
66 	     int y, int x, int height)
67 {
68 	wmove(win, y, x);
69 
70 	if (scroll > 0) {
71 		wattrset(win, dlg.uarrow.atr);
72 		waddch(win, ACS_UARROW);
73 		waddstr(win, "(-)");
74 	} else {
75 		wattrset(win, dlg.menubox.atr);
76 		waddch(win, ACS_HLINE);
77 		waddch(win, ACS_HLINE);
78 		waddch(win, ACS_HLINE);
79 		waddch(win, ACS_HLINE);
80 	}
81 
82 	y = y + height + 1;
83 	wmove(win, y, x);
84 
85 	if ((height < item_no) && (scroll + choice < item_no - 1)) {
86 		wattrset(win, dlg.darrow.atr);
87 		waddch(win, ACS_DARROW);
88 		waddstr(win, "(+)");
89 	} else {
90 		wattrset(win, dlg.menubox_border.atr);
91 		waddch(win, ACS_HLINE);
92 		waddch(win, ACS_HLINE);
93 		waddch(win, ACS_HLINE);
94 		waddch(win, ACS_HLINE);
95 	}
96 }
97 
98 /*
99  *  Display the termination buttons
100  */
101 static void print_buttons(WINDOW * dialog, int height, int width, int selected)
102 {
103 	int x = width / 2 - 11;
104 	int y = height - 2;
105 
106 	print_button(dialog, gettext("Select"), y, x, selected == 0);
107 	print_button(dialog, gettext(" Help "), y, x + 14, selected == 1);
108 
109 	wmove(dialog, y, x + 1 + 14 * selected);
110 	wrefresh(dialog);
111 }
112 
113 /*
114  * Display a dialog box with a list of options that can be turned on or off
115  * in the style of radiolist (only one option turned on at a time).
116  */
117 int dialog_checklist(const char *title, const char *prompt, int height,
118 		     int width, int list_height)
119 {
120 	int i, x, y, box_x, box_y;
121 	int key = 0, button = 0, choice = 0, scroll = 0, max_choice;
122 	WINDOW *dialog, *list;
123 
124 	/* which item to highlight */
125 	item_foreach() {
126 		if (item_is_tag('X'))
127 			choice = item_n();
128 		if (item_is_selected()) {
129 			choice = item_n();
130 			break;
131 		}
132 	}
133 
134 do_resize:
135 	if (getmaxy(stdscr) < (height + CHECKLIST_HEIGTH_MIN))
136 		return -ERRDISPLAYTOOSMALL;
137 	if (getmaxx(stdscr) < (width + CHECKLIST_WIDTH_MIN))
138 		return -ERRDISPLAYTOOSMALL;
139 
140 	max_choice = MIN(list_height, item_count());
141 
142 	/* center dialog box on screen */
143 	x = (getmaxx(stdscr) - width) / 2;
144 	y = (getmaxy(stdscr) - height) / 2;
145 
146 	draw_shadow(stdscr, y, x, height, width);
147 
148 	dialog = newwin(height, width, y, x);
149 	keypad(dialog, TRUE);
150 
151 	draw_box(dialog, 0, 0, height, width,
152 		 dlg.dialog.atr, dlg.border.atr);
153 	wattrset(dialog, dlg.border.atr);
154 	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
155 	for (i = 0; i < width - 2; i++)
156 		waddch(dialog, ACS_HLINE);
157 	wattrset(dialog, dlg.dialog.atr);
158 	waddch(dialog, ACS_RTEE);
159 
160 	print_title(dialog, title, width);
161 
162 	wattrset(dialog, dlg.dialog.atr);
163 	print_autowrap(dialog, prompt, width - 2, 1, 3);
164 
165 	list_width = width - 6;
166 	box_y = height - list_height - 5;
167 	box_x = (width - list_width) / 2 - 1;
168 
169 	/* create new window for the list */
170 	list = subwin(dialog, list_height, list_width, y + box_y + 1,
171 	              x + box_x + 1);
172 
173 	keypad(list, TRUE);
174 
175 	/* draw a box around the list items */
176 	draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2,
177 	         dlg.menubox_border.atr, dlg.menubox.atr);
178 
179 	/* Find length of longest item in order to center checklist */
180 	check_x = 0;
181 	item_foreach()
182 		check_x = MAX(check_x, strlen(item_str()) + 4);
183 	check_x = MIN(check_x, list_width);
184 
185 	check_x = (list_width - check_x) / 2;
186 	item_x = check_x + 4;
187 
188 	if (choice >= list_height) {
189 		scroll = choice - list_height + 1;
190 		choice -= scroll;
191 	}
192 
193 	/* Print the list */
194 	for (i = 0; i < max_choice; i++) {
195 		item_set(scroll + i);
196 		print_item(list, i, i == choice);
197 	}
198 
199 	print_arrows(dialog, choice, item_count(), scroll,
200 		     box_y, box_x + check_x + 5, list_height);
201 
202 	print_buttons(dialog, height, width, 0);
203 
204 	wnoutrefresh(dialog);
205 	wnoutrefresh(list);
206 	doupdate();
207 
208 	while (key != KEY_ESC) {
209 		key = wgetch(dialog);
210 
211 		for (i = 0; i < max_choice; i++) {
212 			item_set(i + scroll);
213 			if (toupper(key) == toupper(item_str()[0]))
214 				break;
215 		}
216 
217 		if (i < max_choice || key == KEY_UP || key == KEY_DOWN ||
218 		    key == '+' || key == '-') {
219 			if (key == KEY_UP || key == '-') {
220 				if (!choice) {
221 					if (!scroll)
222 						continue;
223 					/* Scroll list down */
224 					if (list_height > 1) {
225 						/* De-highlight current first item */
226 						item_set(scroll);
227 						print_item(list, 0, FALSE);
228 						scrollok(list, TRUE);
229 						wscrl(list, -1);
230 						scrollok(list, FALSE);
231 					}
232 					scroll--;
233 					item_set(scroll);
234 					print_item(list, 0, TRUE);
235 					print_arrows(dialog, choice, item_count(),
236 						     scroll, box_y, box_x + check_x + 5, list_height);
237 
238 					wnoutrefresh(dialog);
239 					wrefresh(list);
240 
241 					continue;	/* wait for another key press */
242 				} else
243 					i = choice - 1;
244 			} else if (key == KEY_DOWN || key == '+') {
245 				if (choice == max_choice - 1) {
246 					if (scroll + choice >= item_count() - 1)
247 						continue;
248 					/* Scroll list up */
249 					if (list_height > 1) {
250 						/* De-highlight current last item before scrolling up */
251 						item_set(scroll + max_choice - 1);
252 						print_item(list,
253 							    max_choice - 1,
254 							    FALSE);
255 						scrollok(list, TRUE);
256 						wscrl(list, 1);
257 						scrollok(list, FALSE);
258 					}
259 					scroll++;
260 					item_set(scroll + max_choice - 1);
261 					print_item(list, max_choice - 1, TRUE);
262 
263 					print_arrows(dialog, choice, item_count(),
264 						     scroll, box_y, box_x + check_x + 5, list_height);
265 
266 					wnoutrefresh(dialog);
267 					wrefresh(list);
268 
269 					continue;	/* wait for another key press */
270 				} else
271 					i = choice + 1;
272 			}
273 			if (i != choice) {
274 				/* De-highlight current item */
275 				item_set(scroll + choice);
276 				print_item(list, choice, FALSE);
277 				/* Highlight new item */
278 				choice = i;
279 				item_set(scroll + choice);
280 				print_item(list, choice, TRUE);
281 				wnoutrefresh(dialog);
282 				wrefresh(list);
283 			}
284 			continue;	/* wait for another key press */
285 		}
286 		switch (key) {
287 		case 'H':
288 		case 'h':
289 		case '?':
290 			button = 1;
291 			/* fall-through */
292 		case 'S':
293 		case 's':
294 		case ' ':
295 		case '\n':
296 			item_foreach()
297 				item_set_selected(0);
298 			item_set(scroll + choice);
299 			item_set_selected(1);
300 			delwin(list);
301 			delwin(dialog);
302 			return button;
303 		case TAB:
304 		case KEY_LEFT:
305 		case KEY_RIGHT:
306 			button = ((key == KEY_LEFT ? --button : ++button) < 0)
307 			    ? 1 : (button > 1 ? 0 : button);
308 
309 			print_buttons(dialog, height, width, button);
310 			wrefresh(dialog);
311 			break;
312 		case 'X':
313 		case 'x':
314 			key = KEY_ESC;
315 			break;
316 		case KEY_ESC:
317 			key = on_key_esc(dialog);
318 			break;
319 		case KEY_RESIZE:
320 			delwin(list);
321 			delwin(dialog);
322 			on_key_resize();
323 			goto do_resize;
324 		}
325 
326 		/* Now, update everything... */
327 		doupdate();
328 	}
329 	delwin(list);
330 	delwin(dialog);
331 	return key;		/* ESC pressed */
332 }
333