OSDN Git Service

Replace FSF snail mail address with URLs
[uclinux-h8/uClibc.git] / extra / config / lxdialog / util.c
1 /*
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, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "dialog.h"
22
23 struct dialog_info dlg;
24
25 static void set_mono_theme(void)
26 {
27         dlg.screen.atr = A_NORMAL;
28         dlg.shadow.atr = A_NORMAL;
29         dlg.dialog.atr = A_NORMAL;
30         dlg.title.atr = A_BOLD;
31         dlg.border.atr = A_NORMAL;
32         dlg.button_active.atr = A_REVERSE;
33         dlg.button_inactive.atr = A_DIM;
34         dlg.button_key_active.atr = A_REVERSE;
35         dlg.button_key_inactive.atr = A_BOLD;
36         dlg.button_label_active.atr = A_REVERSE;
37         dlg.button_label_inactive.atr = A_NORMAL;
38         dlg.inputbox.atr = A_NORMAL;
39         dlg.inputbox_border.atr = A_NORMAL;
40         dlg.searchbox.atr = A_NORMAL;
41         dlg.searchbox_title.atr = A_BOLD;
42         dlg.searchbox_border.atr = A_NORMAL;
43         dlg.position_indicator.atr = A_BOLD;
44         dlg.menubox.atr = A_NORMAL;
45         dlg.menubox_border.atr = A_NORMAL;
46         dlg.item.atr = A_NORMAL;
47         dlg.item_selected.atr = A_REVERSE;
48         dlg.tag.atr = A_BOLD;
49         dlg.tag_selected.atr = A_REVERSE;
50         dlg.tag_key.atr = A_BOLD;
51         dlg.tag_key_selected.atr = A_REVERSE;
52         dlg.check.atr = A_BOLD;
53         dlg.check_selected.atr = A_REVERSE;
54         dlg.uarrow.atr = A_BOLD;
55         dlg.darrow.atr = A_BOLD;
56 }
57
58 #define DLG_COLOR(dialog, f, b, h) \
59 do {                               \
60         dlg.dialog.fg = (f);       \
61         dlg.dialog.bg = (b);       \
62         dlg.dialog.hl = (h);       \
63 } while (0)
64
65 static void set_classic_theme(void)
66 {
67         DLG_COLOR(screen,                COLOR_CYAN,   COLOR_BLUE,   true);
68         DLG_COLOR(shadow,                COLOR_BLACK,  COLOR_BLACK,  true);
69         DLG_COLOR(dialog,                COLOR_BLACK,  COLOR_WHITE,  false);
70         DLG_COLOR(title,                 COLOR_YELLOW, COLOR_WHITE,  true);
71         DLG_COLOR(border,                COLOR_WHITE,  COLOR_WHITE,  true);
72         DLG_COLOR(button_active,         COLOR_WHITE,  COLOR_BLUE,   true);
73         DLG_COLOR(button_inactive,       COLOR_BLACK,  COLOR_WHITE,  false);
74         DLG_COLOR(button_key_active,     COLOR_WHITE,  COLOR_BLUE,   true);
75         DLG_COLOR(button_key_inactive,   COLOR_RED,    COLOR_WHITE,  false);
76         DLG_COLOR(button_label_active,   COLOR_YELLOW, COLOR_BLUE,   true);
77         DLG_COLOR(button_label_inactive, COLOR_BLACK,  COLOR_WHITE,  true);
78         DLG_COLOR(inputbox,              COLOR_BLACK,  COLOR_WHITE,  false);
79         DLG_COLOR(inputbox_border,       COLOR_BLACK,  COLOR_WHITE,  false);
80         DLG_COLOR(searchbox,             COLOR_BLACK,  COLOR_WHITE,  false);
81         DLG_COLOR(searchbox_title,       COLOR_YELLOW, COLOR_WHITE,  true);
82         DLG_COLOR(searchbox_border,      COLOR_WHITE,  COLOR_WHITE,  true);
83         DLG_COLOR(position_indicator,    COLOR_YELLOW, COLOR_WHITE,  true);
84         DLG_COLOR(menubox,               COLOR_BLACK,  COLOR_WHITE,  false);
85         DLG_COLOR(menubox_border,        COLOR_WHITE,  COLOR_WHITE,  true);
86         DLG_COLOR(item,                  COLOR_BLACK,  COLOR_WHITE,  false);
87         DLG_COLOR(item_selected,         COLOR_WHITE,  COLOR_BLUE,   true);
88         DLG_COLOR(tag,                   COLOR_YELLOW, COLOR_WHITE,  true);
89         DLG_COLOR(tag_selected,          COLOR_YELLOW, COLOR_BLUE,   true);
90         DLG_COLOR(tag_key,               COLOR_YELLOW, COLOR_WHITE,  true);
91         DLG_COLOR(tag_key_selected,      COLOR_YELLOW, COLOR_BLUE,   true);
92         DLG_COLOR(check,                 COLOR_BLACK,  COLOR_WHITE,  false);
93         DLG_COLOR(check_selected,        COLOR_WHITE,  COLOR_BLUE,   true);
94         DLG_COLOR(uarrow,                COLOR_GREEN,  COLOR_WHITE,  true);
95         DLG_COLOR(darrow,                COLOR_GREEN,  COLOR_WHITE,  true);
96 }
97
98 static void set_blackbg_theme(void)
99 {
100         DLG_COLOR(screen, COLOR_RED,   COLOR_BLACK, true);
101         DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false);
102         DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false);
103         DLG_COLOR(title,  COLOR_RED,   COLOR_BLACK, false);
104         DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true);
105
106         DLG_COLOR(button_active,         COLOR_YELLOW, COLOR_RED,   false);
107         DLG_COLOR(button_inactive,       COLOR_YELLOW, COLOR_BLACK, false);
108         DLG_COLOR(button_key_active,     COLOR_YELLOW, COLOR_RED,   true);
109         DLG_COLOR(button_key_inactive,   COLOR_RED,    COLOR_BLACK, false);
110         DLG_COLOR(button_label_active,   COLOR_WHITE,  COLOR_RED,   false);
111         DLG_COLOR(button_label_inactive, COLOR_BLACK,  COLOR_BLACK, true);
112
113         DLG_COLOR(inputbox,         COLOR_YELLOW, COLOR_BLACK, false);
114         DLG_COLOR(inputbox_border,  COLOR_YELLOW, COLOR_BLACK, false);
115
116         DLG_COLOR(searchbox,        COLOR_YELLOW, COLOR_BLACK, false);
117         DLG_COLOR(searchbox_title,  COLOR_YELLOW, COLOR_BLACK, true);
118         DLG_COLOR(searchbox_border, COLOR_BLACK,  COLOR_BLACK, true);
119
120         DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK,  false);
121
122         DLG_COLOR(menubox,          COLOR_YELLOW, COLOR_BLACK, false);
123         DLG_COLOR(menubox_border,   COLOR_BLACK,  COLOR_BLACK, true);
124
125         DLG_COLOR(item,             COLOR_WHITE, COLOR_BLACK, false);
126         DLG_COLOR(item_selected,    COLOR_WHITE, COLOR_RED,   false);
127
128         DLG_COLOR(tag,              COLOR_RED,    COLOR_BLACK, false);
129         DLG_COLOR(tag_selected,     COLOR_YELLOW, COLOR_RED,   true);
130         DLG_COLOR(tag_key,          COLOR_RED,    COLOR_BLACK, false);
131         DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED,   true);
132
133         DLG_COLOR(check,            COLOR_YELLOW, COLOR_BLACK, false);
134         DLG_COLOR(check_selected,   COLOR_YELLOW, COLOR_RED,   true);
135
136         DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false);
137         DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false);
138 }
139
140 static void set_bluetitle_theme(void)
141 {
142         set_classic_theme();
143         DLG_COLOR(title,               COLOR_BLUE,   COLOR_WHITE, true);
144         DLG_COLOR(button_key_active,   COLOR_YELLOW, COLOR_BLUE,  true);
145         DLG_COLOR(button_label_active, COLOR_WHITE,  COLOR_BLUE,  true);
146         DLG_COLOR(searchbox_title,     COLOR_BLUE,   COLOR_WHITE, true);
147         DLG_COLOR(position_indicator,  COLOR_BLUE,   COLOR_WHITE, true);
148         DLG_COLOR(tag,                 COLOR_BLUE,   COLOR_WHITE, true);
149         DLG_COLOR(tag_key,             COLOR_BLUE,   COLOR_WHITE, true);
150
151 }
152
153 /*
154  * Select color theme
155  */
156 static 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
173 static 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
185 static 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
218 /*
219  * Setup for color display
220  */
221 static void color_setup(const char *theme)
222 {
223         int use_color;
224
225         use_color = set_theme(theme);
226         if (use_color && has_colors()) {
227                 start_color();
228                 init_dialog_colors();
229         } else
230                 set_mono_theme();
231 }
232
233 /*
234  * Set window to attribute 'attr'
235  */
236 void attr_clear(WINDOW * win, int height, int width, chtype attr)
237 {
238         int i, j;
239
240         wattrset(win, attr);
241         for (i = 0; i < height; i++) {
242                 wmove(win, i, 0);
243                 for (j = 0; j < width; j++)
244                         waddch(win, ' ');
245         }
246         touchwin(win);
247 }
248
249 void dialog_clear(void)
250 {
251         attr_clear(stdscr, LINES, COLS, dlg.screen.atr);
252         /* Display background title if it exists ... - SLH */
253         if (dlg.backtitle != NULL) {
254                 int i;
255
256                 wattrset(stdscr, dlg.screen.atr);
257                 mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle);
258                 wmove(stdscr, 1, 1);
259                 for (i = 1; i < COLS - 1; i++)
260                         waddch(stdscr, ACS_HLINE);
261         }
262         wnoutrefresh(stdscr);
263 }
264
265 /*
266  * Do some initialization for dialog
267  */
268 int init_dialog(const char *backtitle)
269 {
270         int height, width;
271
272         initscr();              /* Init curses */
273         getmaxyx(stdscr, height, width);
274         if (height < 19 || width < 80) {
275                 endwin();
276                 return -ERRDISPLAYTOOSMALL;
277         }
278
279         dlg.backtitle = backtitle;
280         color_setup(getenv("MENUCONFIG_COLOR"));
281
282         keypad(stdscr, TRUE);
283         cbreak();
284         noecho();
285         dialog_clear();
286
287         return 0;
288 }
289
290 void set_dialog_backtitle(const char *backtitle)
291 {
292         dlg.backtitle = backtitle;
293 }
294
295 /*
296  * End using dialog functions.
297  */
298 void end_dialog(int x, int y)
299 {
300         /* move cursor back to original position */
301         move(y, x);
302         refresh();
303         endwin();
304 }
305
306 /* Print the title of the dialog. Center the title and truncate
307  * tile if wider than dialog (- 2 chars).
308  **/
309 void print_title(WINDOW *dialog, const char *title, int width)
310 {
311         if (title) {
312                 int tlen = MIN(width - 2, strlen(title));
313                 wattrset(dialog, dlg.title.atr);
314                 mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' ');
315                 mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen);
316                 waddch(dialog, ' ');
317         }
318 }
319
320 /*
321  * Print a string of text in a window, automatically wrap around to the
322  * next line if the string is too long to fit on one line. Newline
323  * characters '\n' are replaced by spaces.  We start on a new line
324  * if there is no room for at least 4 nonblanks following a double-space.
325  */
326 void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
327 {
328         int newl, cur_x, cur_y;
329         int i, prompt_len, room, wlen;
330         char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
331
332         strcpy(tempstr, prompt);
333
334         prompt_len = strlen(tempstr);
335
336         /*
337          * Remove newlines
338          */
339         for (i = 0; i < prompt_len; i++) {
340                 if (tempstr[i] == '\n')
341                         tempstr[i] = ' ';
342         }
343
344         if (prompt_len <= width - x * 2) {      /* If prompt is short */
345                 wmove(win, y, (width - prompt_len) / 2);
346                 waddstr(win, tempstr);
347         } else {
348                 cur_x = x;
349                 cur_y = y;
350                 newl = 1;
351                 word = tempstr;
352                 while (word && *word) {
353                         sp = strchr(word, ' ');
354                         if (sp)
355                                 *sp++ = 0;
356
357                         /* Wrap to next line if either the word does not fit,
358                            or it is the first word of a new sentence, and it is
359                            short, and the next word does not fit. */
360                         room = width - cur_x;
361                         wlen = strlen(word);
362                         if (wlen > room ||
363                             (newl && wlen < 4 && sp
364                              && wlen + 1 + strlen(sp) > room
365                              && (!(sp2 = strchr(sp, ' '))
366                                  || wlen + 1 + (sp2 - sp) > room))) {
367                                 cur_y++;
368                                 cur_x = x;
369                         }
370                         wmove(win, cur_y, cur_x);
371                         waddstr(win, word);
372                         getyx(win, cur_y, cur_x);
373                         cur_x++;
374                         if (sp && *sp == ' ') {
375                                 cur_x++;        /* double space */
376                                 while (*++sp == ' ') ;
377                                 newl = 1;
378                         } else
379                                 newl = 0;
380                         word = sp;
381                 }
382         }
383 }
384
385 /*
386  * Print a button
387  */
388 void print_button(WINDOW * win, const char *label, int y, int x, int selected)
389 {
390         int i, temp;
391
392         wmove(win, y, x);
393         wattrset(win, selected ? dlg.button_active.atr
394                  : dlg.button_inactive.atr);
395         waddstr(win, "<");
396         temp = strspn(label, " ");
397         label += temp;
398         wattrset(win, selected ? dlg.button_label_active.atr
399                  : dlg.button_label_inactive.atr);
400         for (i = 0; i < temp; i++)
401                 waddch(win, ' ');
402         wattrset(win, selected ? dlg.button_key_active.atr
403                  : dlg.button_key_inactive.atr);
404         waddch(win, label[0]);
405         wattrset(win, selected ? dlg.button_label_active.atr
406                  : dlg.button_label_inactive.atr);
407         waddstr(win, (char *)label + 1);
408         wattrset(win, selected ? dlg.button_active.atr
409                  : dlg.button_inactive.atr);
410         waddstr(win, ">");
411         wmove(win, y, x + temp + 1);
412 }
413
414 /*
415  * Draw a rectangular box with line drawing characters
416  */
417 void
418 draw_box(WINDOW * win, int y, int x, int height, int width,
419          chtype box, chtype border)
420 {
421         int i, j;
422
423         wattrset(win, 0);
424         for (i = 0; i < height; i++) {
425                 wmove(win, y + i, x);
426                 for (j = 0; j < width; j++)
427                         if (!i && !j)
428                                 waddch(win, border | ACS_ULCORNER);
429                         else if (i == height - 1 && !j)
430                                 waddch(win, border | ACS_LLCORNER);
431                         else if (!i && j == width - 1)
432                                 waddch(win, box | ACS_URCORNER);
433                         else if (i == height - 1 && j == width - 1)
434                                 waddch(win, box | ACS_LRCORNER);
435                         else if (!i)
436                                 waddch(win, border | ACS_HLINE);
437                         else if (i == height - 1)
438                                 waddch(win, box | ACS_HLINE);
439                         else if (!j)
440                                 waddch(win, border | ACS_VLINE);
441                         else if (j == width - 1)
442                                 waddch(win, box | ACS_VLINE);
443                         else
444                                 waddch(win, box | ' ');
445         }
446 }
447
448 /*
449  * Draw shadows along the right and bottom edge to give a more 3D look
450  * to the boxes
451  */
452 void draw_shadow(WINDOW * win, int y, int x, int height, int width)
453 {
454         int i;
455
456         if (has_colors()) {     /* Whether terminal supports color? */
457                 wattrset(win, dlg.shadow.atr);
458                 wmove(win, y + height, x + 2);
459                 for (i = 0; i < width; i++)
460                         waddch(win, winch(win) & A_CHARTEXT);
461                 for (i = y + 1; i < y + height + 1; i++) {
462                         wmove(win, i, x + width);
463                         waddch(win, winch(win) & A_CHARTEXT);
464                         waddch(win, winch(win) & A_CHARTEXT);
465                 }
466                 wnoutrefresh(win);
467         }
468 }
469
470 /*
471  *  Return the position of the first alphabetic character in a string.
472  */
473 int first_alpha(const char *string, const char *exempt)
474 {
475         int i, in_paren = 0, c;
476
477         for (i = 0; i < strlen(string); i++) {
478                 c = tolower(string[i]);
479
480                 if (strchr("<[(", c))
481                         ++in_paren;
482                 if (strchr(">])", c) && in_paren > 0)
483                         --in_paren;
484
485                 if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0)
486                         return i;
487         }
488
489         return 0;
490 }
491
492 /*
493  * ncurses uses ESC to detect escaped char sequences. This resutl in
494  * a small timeout before ESC is actually delivered to the application.
495  * lxdialog suggest <ESC> <ESC> which is correctly translated to two
496  * times esc. But then we need to ignore the second esc to avoid stepping
497  * out one menu too much. Filter away all escaped key sequences since
498  * keypad(FALSE) turn off ncurses support for escape sequences - and thats
499  * needed to make notimeout() do as expected.
500  */
501 int on_key_esc(WINDOW *win)
502 {
503         int key;
504         int key2;
505         int key3;
506
507         nodelay(win, TRUE);
508         keypad(win, FALSE);
509         key = wgetch(win);
510         key2 = wgetch(win);
511         do {
512                 key3 = wgetch(win);
513         } while (key3 != ERR);
514         nodelay(win, FALSE);
515         keypad(win, TRUE);
516         if (key == KEY_ESC && key2 == ERR)
517                 return KEY_ESC;
518         else if (key != ERR && key != KEY_ESC && key2 == ERR)
519                 ungetch(key);
520
521         return -1;
522 }
523
524 /* redraw screen in new size */
525 int on_key_resize(void)
526 {
527         dialog_clear();
528         return KEY_RESIZE;
529 }
530
531 struct dialog_list *item_cur;
532 struct dialog_list item_nil;
533 struct dialog_list *item_head;
534
535 void item_reset(void)
536 {
537         struct dialog_list *p, *next;
538
539         for (p = item_head; p; p = next) {
540                 next = p->next;
541                 free(p);
542         }
543         item_head = NULL;
544         item_cur = &item_nil;
545 }
546
547 void item_make(const char *fmt, ...)
548 {
549         va_list ap;
550         struct dialog_list *p = malloc(sizeof(*p));
551
552         if (item_head)
553                 item_cur->next = p;
554         else
555                 item_head = p;
556         item_cur = p;
557         memset(p, 0, sizeof(*p));
558
559         va_start(ap, fmt);
560         vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap);
561         va_end(ap);
562 }
563
564 void item_add_str(const char *fmt, ...)
565 {
566         va_list ap;
567         size_t avail;
568
569         avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str);
570
571         va_start(ap, fmt);
572         vsnprintf(item_cur->node.str + strlen(item_cur->node.str),
573                   avail, fmt, ap);
574         item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0';
575         va_end(ap);
576 }
577
578 void item_set_tag(char tag)
579 {
580         item_cur->node.tag = tag;
581 }
582 void item_set_data(void *ptr)
583 {
584         item_cur->node.data = ptr;
585 }
586
587 void item_set_selected(int val)
588 {
589         item_cur->node.selected = val;
590 }
591
592 int item_activate_selected(void)
593 {
594         item_foreach()
595                 if (item_is_selected())
596                         return 1;
597         return 0;
598 }
599
600 void *item_data(void)
601 {
602         return item_cur->node.data;
603 }
604
605 char item_tag(void)
606 {
607         return item_cur->node.tag;
608 }
609
610 int item_count(void)
611 {
612         int n = 0;
613         struct dialog_list *p;
614
615         for (p = item_head; p; p = p->next)
616                 n++;
617         return n;
618 }
619
620 void item_set(int n)
621 {
622         int i = 0;
623         item_foreach()
624                 if (i++ == n)
625                         return;
626 }
627
628 int item_n(void)
629 {
630         int n = 0;
631         struct dialog_list *p;
632
633         for (p = item_head; p; p = p->next) {
634                 if (p == item_cur)
635                         return n;
636                 n++;
637         }
638         return 0;
639 }
640
641 const char *item_str(void)
642 {
643         return item_cur->node.str;
644 }
645
646 int item_is_selected(void)
647 {
648         return (item_cur->node.selected != 0);
649 }
650
651 int item_is_tag(char tag)
652 {
653         return (item_cur->node.tag == tag);
654 }