blob: ea984ba2df8cb8bc0d17a5e08af3134251814805 [file] [log] [blame]
Nicholas Flintham1e3d3112013-04-10 10:48:38 +01001/*
2 * util.c
3 *
4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
5 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <stdarg.h>
23
24#include "dialog.h"
25
26struct dialog_info dlg;
27
28static void set_mono_theme(void)
29{
30 dlg.screen.atr = A_NORMAL;
31 dlg.shadow.atr = A_NORMAL;
32 dlg.dialog.atr = A_NORMAL;
33 dlg.title.atr = A_BOLD;
34 dlg.border.atr = A_NORMAL;
35 dlg.button_active.atr = A_REVERSE;
36 dlg.button_inactive.atr = A_DIM;
37 dlg.button_key_active.atr = A_REVERSE;
38 dlg.button_key_inactive.atr = A_BOLD;
39 dlg.button_label_active.atr = A_REVERSE;
40 dlg.button_label_inactive.atr = A_NORMAL;
41 dlg.inputbox.atr = A_NORMAL;
42 dlg.inputbox_border.atr = A_NORMAL;
43 dlg.searchbox.atr = A_NORMAL;
44 dlg.searchbox_title.atr = A_BOLD;
45 dlg.searchbox_border.atr = A_NORMAL;
46 dlg.position_indicator.atr = A_BOLD;
47 dlg.menubox.atr = A_NORMAL;
48 dlg.menubox_border.atr = A_NORMAL;
49 dlg.item.atr = A_NORMAL;
50 dlg.item_selected.atr = A_REVERSE;
51 dlg.tag.atr = A_BOLD;
52 dlg.tag_selected.atr = A_REVERSE;
53 dlg.tag_key.atr = A_BOLD;
54 dlg.tag_key_selected.atr = A_REVERSE;
55 dlg.check.atr = A_BOLD;
56 dlg.check_selected.atr = A_REVERSE;
57 dlg.uarrow.atr = A_BOLD;
58 dlg.darrow.atr = A_BOLD;
59}
60
61#define DLG_COLOR(dialog, f, b, h) \
62do { \
63 dlg.dialog.fg = (f); \
64 dlg.dialog.bg = (b); \
65 dlg.dialog.hl = (h); \
66} while (0)
67
68static void set_classic_theme(void)
69{
70 DLG_COLOR(screen, COLOR_CYAN, COLOR_BLUE, true);
71 DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, true);
72 DLG_COLOR(dialog, COLOR_BLACK, COLOR_WHITE, false);
73 DLG_COLOR(title, COLOR_YELLOW, COLOR_WHITE, true);
74 DLG_COLOR(border, COLOR_WHITE, COLOR_WHITE, true);
75 DLG_COLOR(button_active, COLOR_WHITE, COLOR_BLUE, true);
76 DLG_COLOR(button_inactive, COLOR_BLACK, COLOR_WHITE, false);
77 DLG_COLOR(button_key_active, COLOR_WHITE, COLOR_BLUE, true);
78 DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_WHITE, false);
79 DLG_COLOR(button_label_active, COLOR_YELLOW, COLOR_BLUE, true);
80 DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_WHITE, true);
81 DLG_COLOR(inputbox, COLOR_BLACK, COLOR_WHITE, false);
82 DLG_COLOR(inputbox_border, COLOR_BLACK, COLOR_WHITE, false);
83 DLG_COLOR(searchbox, COLOR_BLACK, COLOR_WHITE, false);
84 DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_WHITE, true);
85 DLG_COLOR(searchbox_border, COLOR_WHITE, COLOR_WHITE, true);
86 DLG_COLOR(position_indicator, COLOR_YELLOW, COLOR_WHITE, true);
87 DLG_COLOR(menubox, COLOR_BLACK, COLOR_WHITE, false);
88 DLG_COLOR(menubox_border, COLOR_WHITE, COLOR_WHITE, true);
89 DLG_COLOR(item, COLOR_BLACK, COLOR_WHITE, false);
90 DLG_COLOR(item_selected, COLOR_WHITE, COLOR_BLUE, true);
91 DLG_COLOR(tag, COLOR_YELLOW, COLOR_WHITE, true);
92 DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_BLUE, true);
93 DLG_COLOR(tag_key, COLOR_YELLOW, COLOR_WHITE, true);
94 DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_BLUE, true);
95 DLG_COLOR(check, COLOR_BLACK, COLOR_WHITE, false);
96 DLG_COLOR(check_selected, COLOR_WHITE, COLOR_BLUE, true);
97 DLG_COLOR(uarrow, COLOR_GREEN, COLOR_WHITE, true);
98 DLG_COLOR(darrow, COLOR_GREEN, COLOR_WHITE, true);
99}
100
101static void set_blackbg_theme(void)
102{
103 DLG_COLOR(screen, COLOR_RED, COLOR_BLACK, true);
104 DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false);
105 DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false);
106 DLG_COLOR(title, COLOR_RED, COLOR_BLACK, false);
107 DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true);
108
109 DLG_COLOR(button_active, COLOR_YELLOW, COLOR_RED, false);
110 DLG_COLOR(button_inactive, COLOR_YELLOW, COLOR_BLACK, false);
111 DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_RED, true);
112 DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_BLACK, false);
113 DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_RED, false);
114 DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_BLACK, true);
115
116 DLG_COLOR(inputbox, COLOR_YELLOW, COLOR_BLACK, false);
117 DLG_COLOR(inputbox_border, COLOR_YELLOW, COLOR_BLACK, false);
118
119 DLG_COLOR(searchbox, COLOR_YELLOW, COLOR_BLACK, false);
120 DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_BLACK, true);
121 DLG_COLOR(searchbox_border, COLOR_BLACK, COLOR_BLACK, true);
122
123 DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK, false);
124
125 DLG_COLOR(menubox, COLOR_YELLOW, COLOR_BLACK, false);
126 DLG_COLOR(menubox_border, COLOR_BLACK, COLOR_BLACK, true);
127
128 DLG_COLOR(item, COLOR_WHITE, COLOR_BLACK, false);
129 DLG_COLOR(item_selected, COLOR_WHITE, COLOR_RED, false);
130
131 DLG_COLOR(tag, COLOR_RED, COLOR_BLACK, false);
132 DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_RED, true);
133 DLG_COLOR(tag_key, COLOR_RED, COLOR_BLACK, false);
134 DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED, true);
135
136 DLG_COLOR(check, COLOR_YELLOW, COLOR_BLACK, false);
137 DLG_COLOR(check_selected, COLOR_YELLOW, COLOR_RED, true);
138
139 DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false);
140 DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false);
141}
142
143static void set_bluetitle_theme(void)
144{
145 set_classic_theme();
146 DLG_COLOR(title, COLOR_BLUE, COLOR_WHITE, true);
147 DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_BLUE, true);
148 DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_BLUE, true);
149 DLG_COLOR(searchbox_title, COLOR_BLUE, COLOR_WHITE, true);
150 DLG_COLOR(position_indicator, COLOR_BLUE, COLOR_WHITE, true);
151 DLG_COLOR(tag, COLOR_BLUE, COLOR_WHITE, true);
152 DLG_COLOR(tag_key, COLOR_BLUE, COLOR_WHITE, true);
153
154}
155
156static int set_theme(const char *theme)
157{
158 int use_color = 1;
159 if (!theme)
160 set_bluetitle_theme();
161 else if (strcmp(theme, "classic") == 0)
162 set_classic_theme();
163 else if (strcmp(theme, "bluetitle") == 0)
164 set_bluetitle_theme();
165 else if (strcmp(theme, "blackbg") == 0)
166 set_blackbg_theme();
167 else if (strcmp(theme, "mono") == 0)
168 use_color = 0;
169
170 return use_color;
171}
172
173static void init_one_color(struct dialog_color *color)
174{
175 static int pair = 0;
176
177 pair++;
178 init_pair(pair, color->fg, color->bg);
179 if (color->hl)
180 color->atr = A_BOLD | COLOR_PAIR(pair);
181 else
182 color->atr = COLOR_PAIR(pair);
183}
184
185static void init_dialog_colors(void)
186{
187 init_one_color(&dlg.screen);
188 init_one_color(&dlg.shadow);
189 init_one_color(&dlg.dialog);
190 init_one_color(&dlg.title);
191 init_one_color(&dlg.border);
192 init_one_color(&dlg.button_active);
193 init_one_color(&dlg.button_inactive);
194 init_one_color(&dlg.button_key_active);
195 init_one_color(&dlg.button_key_inactive);
196 init_one_color(&dlg.button_label_active);
197 init_one_color(&dlg.button_label_inactive);
198 init_one_color(&dlg.inputbox);
199 init_one_color(&dlg.inputbox_border);
200 init_one_color(&dlg.searchbox);
201 init_one_color(&dlg.searchbox_title);
202 init_one_color(&dlg.searchbox_border);
203 init_one_color(&dlg.position_indicator);
204 init_one_color(&dlg.menubox);
205 init_one_color(&dlg.menubox_border);
206 init_one_color(&dlg.item);
207 init_one_color(&dlg.item_selected);
208 init_one_color(&dlg.tag);
209 init_one_color(&dlg.tag_selected);
210 init_one_color(&dlg.tag_key);
211 init_one_color(&dlg.tag_key_selected);
212 init_one_color(&dlg.check);
213 init_one_color(&dlg.check_selected);
214 init_one_color(&dlg.uarrow);
215 init_one_color(&dlg.darrow);
216}
217
218static void color_setup(const char *theme)
219{
220 int use_color;
221
222 use_color = set_theme(theme);
223 if (use_color && has_colors()) {
224 start_color();
225 init_dialog_colors();
226 } else
227 set_mono_theme();
228}
229
230void attr_clear(WINDOW * win, int height, int width, chtype attr)
231{
232 int i, j;
233
234 wattrset(win, attr);
235 for (i = 0; i < height; i++) {
236 wmove(win, i, 0);
237 for (j = 0; j < width; j++)
238 waddch(win, ' ');
239 }
240 touchwin(win);
241}
242
243void dialog_clear(void)
244{
245 attr_clear(stdscr, LINES, COLS, dlg.screen.atr);
246
247 if (dlg.backtitle != NULL) {
248 int i;
249
250 wattrset(stdscr, dlg.screen.atr);
251 mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle);
252 wmove(stdscr, 1, 1);
253 for (i = 1; i < COLS - 1; i++)
254 waddch(stdscr, ACS_HLINE);
255 }
256 wnoutrefresh(stdscr);
257}
258
259int init_dialog(const char *backtitle)
260{
261 int height, width;
262
263 initscr();
264 getmaxyx(stdscr, height, width);
265 if (height < 19 || width < 80) {
266 endwin();
267 return -ERRDISPLAYTOOSMALL;
268 }
269
270 dlg.backtitle = backtitle;
271 color_setup(getenv("MENUCONFIG_COLOR"));
272
273 keypad(stdscr, TRUE);
274 cbreak();
275 noecho();
276 dialog_clear();
277
278 return 0;
279}
280
281void set_dialog_backtitle(const char *backtitle)
282{
283 dlg.backtitle = backtitle;
284}
285
286void end_dialog(int x, int y)
287{
288
289 move(y, x);
290 refresh();
291 endwin();
292}
293
294void print_title(WINDOW *dialog, const char *title, int width)
295{
296 if (title) {
297 int tlen = MIN(width - 2, strlen(title));
298 wattrset(dialog, dlg.title.atr);
299 mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' ');
300 mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen);
301 waddch(dialog, ' ');
302 }
303}
304
305void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
306{
307 int newl, cur_x, cur_y;
308 int i, prompt_len, room, wlen;
309 char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
310
311 strcpy(tempstr, prompt);
312
313 prompt_len = strlen(tempstr);
314
315 for (i = 0; i < prompt_len; i++) {
316 if (tempstr[i] == '\n')
317 tempstr[i] = ' ';
318 }
319
320 if (prompt_len <= width - x * 2) {
321 wmove(win, y, (width - prompt_len) / 2);
322 waddstr(win, tempstr);
323 } else {
324 cur_x = x;
325 cur_y = y;
326 newl = 1;
327 word = tempstr;
328 while (word && *word) {
329 sp = strchr(word, ' ');
330 if (sp)
331 *sp++ = 0;
332
333 room = width - cur_x;
334 wlen = strlen(word);
335 if (wlen > room ||
336 (newl && wlen < 4 && sp
337 && wlen + 1 + strlen(sp) > room
338 && (!(sp2 = strchr(sp, ' '))
339 || wlen + 1 + (sp2 - sp) > room))) {
340 cur_y++;
341 cur_x = x;
342 }
343 wmove(win, cur_y, cur_x);
344 waddstr(win, word);
345 getyx(win, cur_y, cur_x);
346 cur_x++;
347 if (sp && *sp == ' ') {
348 cur_x++;
349 while (*++sp == ' ') ;
350 newl = 1;
351 } else
352 newl = 0;
353 word = sp;
354 }
355 }
356}
357
358void print_button(WINDOW * win, const char *label, int y, int x, int selected)
359{
360 int i, temp;
361
362 wmove(win, y, x);
363 wattrset(win, selected ? dlg.button_active.atr
364 : dlg.button_inactive.atr);
365 waddstr(win, "<");
366 temp = strspn(label, " ");
367 label += temp;
368 wattrset(win, selected ? dlg.button_label_active.atr
369 : dlg.button_label_inactive.atr);
370 for (i = 0; i < temp; i++)
371 waddch(win, ' ');
372 wattrset(win, selected ? dlg.button_key_active.atr
373 : dlg.button_key_inactive.atr);
374 waddch(win, label[0]);
375 wattrset(win, selected ? dlg.button_label_active.atr
376 : dlg.button_label_inactive.atr);
377 waddstr(win, (char *)label + 1);
378 wattrset(win, selected ? dlg.button_active.atr
379 : dlg.button_inactive.atr);
380 waddstr(win, ">");
381 wmove(win, y, x + temp + 1);
382}
383
384void
385draw_box(WINDOW * win, int y, int x, int height, int width,
386 chtype box, chtype border)
387{
388 int i, j;
389
390 wattrset(win, 0);
391 for (i = 0; i < height; i++) {
392 wmove(win, y + i, x);
393 for (j = 0; j < width; j++)
394 if (!i && !j)
395 waddch(win, border | ACS_ULCORNER);
396 else if (i == height - 1 && !j)
397 waddch(win, border | ACS_LLCORNER);
398 else if (!i && j == width - 1)
399 waddch(win, box | ACS_URCORNER);
400 else if (i == height - 1 && j == width - 1)
401 waddch(win, box | ACS_LRCORNER);
402 else if (!i)
403 waddch(win, border | ACS_HLINE);
404 else if (i == height - 1)
405 waddch(win, box | ACS_HLINE);
406 else if (!j)
407 waddch(win, border | ACS_VLINE);
408 else if (j == width - 1)
409 waddch(win, box | ACS_VLINE);
410 else
411 waddch(win, box | ' ');
412 }
413}
414
415void draw_shadow(WINDOW * win, int y, int x, int height, int width)
416{
417 int i;
418
419 if (has_colors()) {
420 wattrset(win, dlg.shadow.atr);
421 wmove(win, y + height, x + 2);
422 for (i = 0; i < width; i++)
423 waddch(win, winch(win) & A_CHARTEXT);
424 for (i = y + 1; i < y + height + 1; i++) {
425 wmove(win, i, x + width);
426 waddch(win, winch(win) & A_CHARTEXT);
427 waddch(win, winch(win) & A_CHARTEXT);
428 }
429 wnoutrefresh(win);
430 }
431}
432
433int first_alpha(const char *string, const char *exempt)
434{
435 int i, in_paren = 0, c;
436
437 for (i = 0; i < strlen(string); i++) {
438 c = tolower(string[i]);
439
440 if (strchr("<[(", c))
441 ++in_paren;
442 if (strchr(">])", c) && in_paren > 0)
443 --in_paren;
444
445 if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0)
446 return i;
447 }
448
449 return 0;
450}
451
452int on_key_esc(WINDOW *win)
453{
454 int key;
455 int key2;
456 int key3;
457
458 nodelay(win, TRUE);
459 keypad(win, FALSE);
460 key = wgetch(win);
461 key2 = wgetch(win);
462 do {
463 key3 = wgetch(win);
464 } while (key3 != ERR);
465 nodelay(win, FALSE);
466 keypad(win, TRUE);
467 if (key == KEY_ESC && key2 == ERR)
468 return KEY_ESC;
469 else if (key != ERR && key != KEY_ESC && key2 == ERR)
470 ungetch(key);
471
472 return -1;
473}
474
475int on_key_resize(void)
476{
477 dialog_clear();
478 return KEY_RESIZE;
479}
480
481struct dialog_list *item_cur;
482struct dialog_list item_nil;
483struct dialog_list *item_head;
484
485void item_reset(void)
486{
487 struct dialog_list *p, *next;
488
489 for (p = item_head; p; p = next) {
490 next = p->next;
491 free(p);
492 }
493 item_head = NULL;
494 item_cur = &item_nil;
495}
496
497void item_make(const char *fmt, ...)
498{
499 va_list ap;
500 struct dialog_list *p = malloc(sizeof(*p));
501
502 if (item_head)
503 item_cur->next = p;
504 else
505 item_head = p;
506 item_cur = p;
507 memset(p, 0, sizeof(*p));
508
509 va_start(ap, fmt);
510 vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap);
511 va_end(ap);
512}
513
514void item_add_str(const char *fmt, ...)
515{
516 va_list ap;
517 size_t avail;
518
519 avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str);
520
521 va_start(ap, fmt);
522 vsnprintf(item_cur->node.str + strlen(item_cur->node.str),
523 avail, fmt, ap);
524 item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0';
525 va_end(ap);
526}
527
528void item_set_tag(char tag)
529{
530 item_cur->node.tag = tag;
531}
532void item_set_data(void *ptr)
533{
534 item_cur->node.data = ptr;
535}
536
537void item_set_selected(int val)
538{
539 item_cur->node.selected = val;
540}
541
542int item_activate_selected(void)
543{
544 item_foreach()
545 if (item_is_selected())
546 return 1;
547 return 0;
548}
549
550void *item_data(void)
551{
552 return item_cur->node.data;
553}
554
555char item_tag(void)
556{
557 return item_cur->node.tag;
558}
559
560int item_count(void)
561{
562 int n = 0;
563 struct dialog_list *p;
564
565 for (p = item_head; p; p = p->next)
566 n++;
567 return n;
568}
569
570void item_set(int n)
571{
572 int i = 0;
573 item_foreach()
574 if (i++ == n)
575 return;
576}
577
578int item_n(void)
579{
580 int n = 0;
581 struct dialog_list *p;
582
583 for (p = item_head; p; p = p->next) {
584 if (p == item_cur)
585 return n;
586 n++;
587 }
588 return 0;
589}
590
591const char *item_str(void)
592{
593 return item_cur->node.str;
594}
595
596int item_is_selected(void)
597{
598 return (item_cur->node.selected != 0);
599}
600
601int item_is_tag(char tag)
602{
603 return (item_cur->node.tag == tag);
604}