]> git.ozlabs.org Git - petitboot/blob - ui/ncurses/nc-widgets.c
configure: Use AC_GNU_SOURCE
[petitboot] / ui / ncurses / nc-widgets.c
1 /*
2  *  Copyright (C) 2013 IBM Corporation
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #if defined(HAVE_CONFIG_H)
19 #include "config.h"
20 #endif
21
22 #include <linux/input.h> /* This must be included before ncurses.h */
23 #if defined HAVE_NCURSESW_CURSES_H
24 #  include <ncursesw/curses.h>
25 #elif defined HAVE_NCURSESW_H
26 #  include <ncursesw.h>
27 #elif defined HAVE_NCURSES_CURSES_H
28 #  include <ncurses/curses.h>
29 #elif defined HAVE_NCURSES_H
30 #  include <ncurses.h>
31 #elif defined HAVE_CURSES_H
32 #  include <curses.h>
33 #else
34 #  error "Curses header file not found."
35 #endif
36
37 #if defined HAVE_NCURSESW_FORM_H
38 #  include <ncursesw/form.h>
39 #elif defined HAVE_NCURSES_FORM_H
40 #  include <ncurses/form.h>
41 #elif defined HAVE_FORM_H
42 #  include <form.h>
43 #else
44 #  error "Curses form.h not found."
45 #endif
46
47 #include <string.h>
48 #include <ctype.h>
49
50 #include <talloc/talloc.h>
51 #include <types/types.h>
52 #include <log/log.h>
53 #include <util/util.h>
54
55 #include "nc-cui.h"
56 #include "nc-widgets.h"
57
58 #undef move
59
60 #define to_checkbox(w) container_of(w, struct nc_widget_checkbox, widget)
61 #define to_textbox(w) container_of(w, struct nc_widget_textbox, widget)
62 #define to_button(w) container_of(w, struct nc_widget_button, widget)
63 #define to_select(w) container_of(w, struct nc_widget_select, widget)
64
65 static const char *checkbox_checked_str = "[*]";
66 static const char *checkbox_unchecked_str = "[ ]";
67
68 static const char *select_selected_str = "(*)";
69 static const char *select_unselected_str = "( )";
70
71 struct nc_widgetset {
72         WINDOW  *mainwin;
73         WINDOW  *subwin;
74         FORM    *form;
75         FIELD   **fields;
76         int     n_fields, n_alloc_fields;
77         void    (*widget_focus)(struct nc_widget *, void *);
78         void    *widget_focus_arg;
79         FIELD   *cur_field;
80 };
81
82 struct nc_widget {
83         FIELD   *field;
84         bool    (*process_key)(struct nc_widget *, FORM *, int);
85         void    (*set_visible)(struct nc_widget *, bool);
86         void    (*move)(struct nc_widget *, int, int);
87         void    (*field_focus)(struct nc_widget *, FIELD *);
88         int     focussed_attr;
89         int     unfocussed_attr;
90         int     height;
91         int     width;
92         int     focus_y;
93         int     x;
94         int     y;
95 };
96
97 struct nc_widget_label {
98         struct nc_widget        widget;
99         const char              *text;
100 };
101
102 struct nc_widget_checkbox {
103         struct nc_widget        widget;
104         bool                    checked;
105 };
106
107 struct nc_widget_textbox {
108         struct nc_widget        widget;
109 };
110
111 struct nc_widget_select {
112         struct nc_widget        widget;
113         struct select_option {
114                 char            *str;
115                 int             val;
116                 FIELD           *field;
117         } *options;
118         int                     top, left, size;
119         int                     n_options, selected_option;
120         struct nc_widgetset     *set;
121         void                    (*on_change)(void *, int);
122         void                    *on_change_arg;
123 };
124
125 struct nc_widget_button {
126         struct nc_widget        widget;
127         void                    (*click)(void *arg);
128         void                    *arg;
129 };
130
131 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field);
132 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field);
133
134 static bool key_is_select(int key)
135 {
136         return key == ' ' || key == '\r' || key == '\n' || key == KEY_ENTER;
137 }
138
139 static bool process_key_nop(struct nc_widget *widget __attribute__((unused)),
140                 FORM *form __attribute((unused)),
141                 int key __attribute__((unused)))
142 {
143         return false;
144 }
145
146 static void field_set_visible(FIELD *field, bool visible)
147 {
148         int opts = field_opts(field) & ~O_VISIBLE;
149         if (visible)
150                 opts |= O_VISIBLE;
151         set_field_opts(field, opts);
152 }
153
154 static void field_move(FIELD *field, int y, int x)
155 {
156         move_field(field, y, x);
157 }
158
159 static int label_destructor(void *ptr)
160 {
161         struct nc_widget_label *label = ptr;
162         free_field(label->widget.field);
163         return 0;
164 }
165
166
167 struct nc_widget_label *widget_new_label(struct nc_widgetset *set,
168                 int y, int x, char *str)
169 {
170         struct nc_widget_label *label;
171         FIELD *f;
172         int len;
173
174         len = strlen(str);
175
176         label = talloc_zero(set, struct nc_widget_label);
177         label->widget.height = 1;
178         label->widget.width = len;
179         label->widget.x = x;
180         label->widget.y = y;
181         label->widget.process_key = process_key_nop;
182         label->widget.field = f = new_field(1, len, y, x, 0, 0);
183         label->widget.focussed_attr = A_NORMAL;
184         label->widget.unfocussed_attr = A_NORMAL;
185
186         field_opts_off(f, O_ACTIVE);
187         set_field_buffer(f, 0, str);
188         set_field_userptr(f, &label->widget);
189
190         widgetset_add_field(set, label->widget.field);
191         talloc_set_destructor(label, label_destructor);
192
193         return label;
194 }
195
196 bool widget_checkbox_get_value(struct nc_widget_checkbox *checkbox)
197 {
198         return checkbox->checked;
199 }
200
201 static void checkbox_set_buffer(struct nc_widget_checkbox *checkbox)
202 {
203         const char *str;
204         str = checkbox->checked ? checkbox_checked_str : checkbox_unchecked_str;
205         set_field_buffer(checkbox->widget.field, 0, str);
206 }
207
208 static bool checkbox_process_key(struct nc_widget *widget,
209                 FORM *form __attribute__((unused)), int key)
210 {
211         struct nc_widget_checkbox *checkbox = to_checkbox(widget);
212
213         if (!key_is_select(key))
214                 return false;
215
216         checkbox->checked = !checkbox->checked;
217         checkbox_set_buffer(checkbox);
218
219         return true;
220 }
221
222 static int checkbox_destructor(void *ptr)
223 {
224         struct nc_widget_checkbox *checkbox = ptr;
225         free_field(checkbox->widget.field);
226         return 0;
227 }
228
229 struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set,
230                 int y, int x, bool checked)
231 {
232         struct nc_widget_checkbox *checkbox;
233         FIELD *f;
234
235         checkbox = talloc_zero(set, struct nc_widget_checkbox);
236         checkbox->checked = checked;
237         checkbox->widget.height = 1;
238         checkbox->widget.width = strlen(checkbox_checked_str);
239         checkbox->widget.x = x;
240         checkbox->widget.y = y;
241         checkbox->widget.process_key = checkbox_process_key;
242         checkbox->widget.focussed_attr = A_REVERSE;
243         checkbox->widget.unfocussed_attr = A_NORMAL;
244         checkbox->widget.field = f = new_field(1, strlen(checkbox_checked_str),
245                         y, x, 0, 0);
246
247         field_opts_off(f, O_EDIT);
248         set_field_userptr(f, &checkbox->widget);
249         checkbox_set_buffer(checkbox);
250
251         widgetset_add_field(set, checkbox->widget.field);
252         talloc_set_destructor(checkbox, checkbox_destructor);
253
254         return checkbox;
255 }
256
257 static char *strip_string(char *str)
258 {
259         int len, i;
260
261         len = strlen(str);
262
263         /* clear trailing space */
264         for (i = len - 1; i >= 0; i--) {
265                 if (!isspace(str[i]))
266                         break;
267                 str[i] = '\0';
268         }
269
270         /* increment str past leading space */
271         for (i = 0; i < len; i++) {
272                 if (str[i] == '\0' || !isspace(str[i]))
273                         break;
274         }
275
276         return str + i;
277 }
278
279 char *widget_textbox_get_value(struct nc_widget_textbox *textbox)
280 {
281         char *str = field_buffer(textbox->widget.field, 0);
282         return str ? strip_string(str) : NULL;
283 }
284
285 static bool textbox_process_key(
286                 struct nc_widget *widget __attribute__((unused)),
287                 FORM *form, int key)
288 {
289         switch (key) {
290         case KEY_HOME:
291                 form_driver(form, REQ_BEG_FIELD);
292                 break;
293         case KEY_END:
294                 form_driver(form, REQ_END_FIELD);
295                 break;
296         case KEY_LEFT:
297                 form_driver(form, REQ_LEFT_CHAR);
298                 break;
299         case KEY_RIGHT:
300                 form_driver(form, REQ_RIGHT_CHAR);
301                 break;
302         case KEY_BACKSPACE:
303                 if (form_driver(form, REQ_LEFT_CHAR) != E_OK)
304                         break;
305                 /* fall through */
306         case KEY_DC:
307                 form_driver(form, REQ_DEL_CHAR);
308                 break;
309         default:
310                 form_driver(form, key);
311                 break;
312         }
313
314         return true;
315 }
316
317 static int textbox_destructor(void *ptr)
318 {
319         struct nc_widget_textbox *textbox = ptr;
320         free_field(textbox->widget.field);
321         return 0;
322 }
323
324 struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
325                 int y, int x, int len, char *str)
326 {
327         struct nc_widget_textbox *textbox;
328         FIELD *f;
329
330         textbox = talloc_zero(set, struct nc_widget_textbox);
331         textbox->widget.height = 1;
332         textbox->widget.width = len;
333         textbox->widget.x = x;
334         textbox->widget.y = y;
335         textbox->widget.process_key = textbox_process_key;
336         textbox->widget.field = f = new_field(1, len, y, x, 0, 0);
337         textbox->widget.focussed_attr = A_REVERSE;
338         textbox->widget.unfocussed_attr = A_UNDERLINE;
339
340         field_opts_off(f, O_STATIC | O_WRAP | O_BLANK);
341         set_field_buffer(f, 0, str);
342         set_field_back(f, textbox->widget.unfocussed_attr);
343         set_field_userptr(f, &textbox->widget);
344
345         widgetset_add_field(set, textbox->widget.field);
346         talloc_set_destructor(textbox, textbox_destructor);
347
348         return textbox;
349 }
350
351 static void select_option_change(struct select_option *opt, bool selected)
352 {
353         const char *str;
354
355         str = selected ? select_selected_str : select_unselected_str;
356
357         memcpy(opt->str, str, strlen(str));
358         set_field_buffer(opt->field, 0, opt->str);
359 }
360
361 static bool select_process_key(struct nc_widget *w, FORM *form, int key)
362 {
363         struct nc_widget_select *select = to_select(w);
364         struct select_option *new_opt, *old_opt;
365         int i, new_idx;
366         FIELD *field;
367
368         if (!key_is_select(key))
369                 return false;
370
371         field = current_field(form);
372         new_opt = NULL;
373
374         for (i = 0; i < select->n_options; i++) {
375                 if (select->options[i].field == field) {
376                         new_opt = &select->options[i];
377                         new_idx = i;
378                         break;
379                 }
380         }
381
382         if (!new_opt)
383                 return true;
384
385         if (new_idx == select->selected_option)
386                 return true;
387
388         old_opt = &select->options[select->selected_option];
389
390         select_option_change(old_opt, false);
391         select_option_change(new_opt, true);
392
393         select->selected_option = new_idx;
394
395         if (select->on_change)
396                 select->on_change(select->on_change_arg, new_opt->val);
397
398         return true;
399 }
400
401 static void select_set_visible(struct nc_widget *widget, bool visible)
402 {
403         struct nc_widget_select *select = to_select(widget);
404         int i;
405
406         for (i = 0; i < select->n_options; i++)
407                 field_set_visible(select->options[i].field, visible);
408 }
409
410 static void select_move(struct nc_widget *widget, int y, int x)
411 {
412         struct nc_widget_select *select = to_select(widget);
413         int i;
414
415         for (i = 0; i < select->n_options; i++)
416                 field_move(select->options[i].field, y + i, x);
417 }
418
419 static void select_field_focus(struct nc_widget *widget, FIELD *field)
420 {
421         struct nc_widget_select *select = to_select(widget);
422         int i;
423
424         for (i = 0; i < select->n_options; i++) {
425                 if (field != select->options[i].field)
426                         continue;
427                 widget->focus_y = i;
428                 return;
429         }
430 }
431
432 static int select_destructor(void *ptr)
433 {
434         struct nc_widget_select *select = ptr;
435         int i;
436
437         for (i = 0; i < select->n_options; i++)
438                 free_field(select->options[i].field);
439
440         return 0;
441 }
442
443 struct nc_widget_select *widget_new_select(struct nc_widgetset *set,
444                 int y, int x, int len)
445 {
446         struct nc_widget_select *select;
447
448         select = talloc_zero(set, struct nc_widget_select);
449         select->widget.width = len;
450         select->widget.height = 0;
451         select->widget.x = x;
452         select->widget.y = y;
453         select->widget.process_key = select_process_key;
454         select->widget.set_visible = select_set_visible;
455         select->widget.move = select_move;
456         select->widget.field_focus = select_field_focus;
457         select->widget.focussed_attr = A_REVERSE;
458         select->widget.unfocussed_attr = A_NORMAL;
459         select->top = y;
460         select->left = x;
461         select->size = len;
462         select->set = set;
463
464         talloc_set_destructor(select, select_destructor);
465
466         return select;
467 }
468
469 void widget_select_add_option(struct nc_widget_select *select, int value,
470                 const char *text, bool selected)
471 {
472         const char *str;
473         FIELD *f;
474         int i;
475
476         /* if we never see an option with selected set, we want the first
477          * one to be selected */
478         if (select->n_options == 0)
479                 selected = true;
480         else if (selected)
481                 select_option_change(&select->options[select->selected_option],
482                                         false);
483
484         if (selected) {
485                 select->selected_option = select->n_options;
486                 str = select_selected_str;
487         } else
488                 str = select_unselected_str;
489
490         i = select->n_options++;
491         select->widget.height = select->n_options;
492
493         select->options = talloc_realloc(select, select->options,
494                                 struct select_option, i + 2);
495         select->options[i].val = value;
496         select->options[i].str = talloc_asprintf(select->options,
497                                         "%s %s", str, text);
498
499         select->options[i].field = f = new_field(1, select->size,
500                                                 select->top + i,
501                                                 select->left, 0, 0);
502
503         field_opts_off(f, O_WRAP | O_EDIT);
504         set_field_userptr(f, &select->widget);
505         set_field_buffer(f, 0, select->options[i].str);
506
507         widgetset_add_field(select->set, f);
508 }
509
510 int widget_select_get_value(struct nc_widget_select *select)
511 {
512         if (!select->n_options)
513                 return -1;
514         return select->options[select->selected_option].val;
515 }
516
517 int widget_select_height(struct nc_widget_select *select)
518 {
519         return select->n_options;
520 }
521
522 void widget_select_on_change(struct nc_widget_select *select,
523                 void (*on_change)(void *, int), void *arg)
524 {
525         select->on_change = on_change;
526         select->on_change_arg = arg;
527 }
528
529 void widget_select_drop_options(struct nc_widget_select *select)
530 {
531         struct nc_widgetset *set = select->set;
532         int i;
533
534         for (i = 0; i < select->n_options; i++) {
535                 FIELD *field = select->options[i].field;
536                 widgetset_remove_field(set, field);
537                 if (field == set->cur_field)
538                         set->cur_field = NULL;
539                 free_field(select->options[i].field);
540         }
541
542         talloc_free(select->options);
543         select->options = NULL;
544         select->n_options = 0;
545         select->widget.height = 0;
546         select->widget.focus_y = 0;
547
548 }
549
550 static bool button_process_key(struct nc_widget *widget,
551                 FORM *form __attribute__((unused)), int key)
552 {
553         struct nc_widget_button *button = to_button(widget);
554
555         if (!button->click)
556                 return false;
557
558         if (!key_is_select(key))
559                 return false;
560
561         button->click(button->arg);
562         return true;
563 }
564
565 static int button_destructor(void *ptr)
566 {
567         struct nc_widget_button *button = ptr;
568         free_field(button->widget.field);
569         return 0;
570 }
571
572 struct nc_widget_button *widget_new_button(struct nc_widgetset *set,
573                 int y, int x, int size, const char *str,
574                 void (*click)(void *), void *arg)
575 {
576         struct nc_widget_button *button;
577         char *text;
578         FIELD *f;
579         int idx, len;
580
581         button = talloc_zero(set, struct nc_widget_button);
582         button->widget.height = 1;
583         button->widget.width = size;
584         button->widget.x = x;
585         button->widget.y = y;
586         button->widget.field = f = new_field(1, size + 2, y, x, 0, 0);
587         button->widget.process_key = button_process_key;
588         button->widget.focussed_attr = A_REVERSE;
589         button->widget.unfocussed_attr = A_NORMAL;
590         button->click = click;
591         button->arg = arg;
592
593         field_opts_off(f, O_EDIT);
594         set_field_userptr(f, &button->widget);
595
596         /* center str in a size-char buffer, but don't overrun */
597         len = strlen(str);
598         len = min(len, size);
599         idx = (size - len) / 2;
600
601         text = talloc_array(button, char, size + 3);
602         memset(text, ' ', size + 2);
603         memcpy(text + idx + 1, str, len);
604         text[0] = '[';
605         text[size + 1] = ']';
606         text[size + 2] = '\0';
607
608         set_field_buffer(f, 0, text);
609
610         widgetset_add_field(set, button->widget.field);
611         talloc_set_destructor(button, button_destructor);
612
613         return button;
614 }
615
616 static void widget_focus_change(struct nc_widget *widget, FIELD *field,
617                 bool focussed)
618 {
619         int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr;
620         set_field_back(field, attr);
621 }
622
623 bool widgetset_process_key(struct nc_widgetset *set, int key)
624 {
625         struct nc_widget *widget;
626         FIELD *field;
627         int req = 0;
628
629         field = current_field(set->form);
630         assert(field);
631
632         /* handle field change events */
633         switch (key) {
634         case KEY_BTAB:
635         case KEY_UP:
636                 req = REQ_PREV_FIELD;
637                 break;
638         case '\t':
639         case KEY_DOWN:
640                 req = REQ_NEXT_FIELD;
641                 break;
642         case KEY_PPAGE:
643                 req = REQ_FIRST_FIELD;
644                 break;
645         case KEY_NPAGE:
646                 req = REQ_LAST_FIELD;
647                 break;
648         }
649
650         widget = field_userptr(field);
651         if (req) {
652                 widget_focus_change(widget, field, false);
653                 form_driver(set->form, req);
654                 form_driver(set->form, REQ_END_FIELD);
655                 field = current_field(set->form);
656                 widget = field_userptr(field);
657                 widget_focus_change(widget, field, true);
658                 if (widget->field_focus)
659                         widget->field_focus(widget, field);
660                 if (set->widget_focus)
661                         set->widget_focus(widget, set->widget_focus_arg);
662                 return true;
663         }
664
665         if (!widget->process_key)
666                 return false;
667
668         return widget->process_key(widget, set->form, key);
669 }
670
671 static int widgetset_destructor(void *ptr)
672 {
673         struct nc_widgetset *set = ptr;
674         free_form(set->form);
675         return 0;
676 }
677
678 struct nc_widgetset *widgetset_create(void *ctx, WINDOW *main, WINDOW *sub)
679 {
680         struct nc_widgetset *set;
681
682         set = talloc_zero(ctx, struct nc_widgetset);
683         set->n_alloc_fields = 8;
684         set->mainwin = main;
685         set->subwin = sub;
686         set->fields = talloc_array(set, FIELD *, set->n_alloc_fields);
687         talloc_set_destructor(set, widgetset_destructor);
688
689         return set;
690 }
691
692 void widgetset_set_windows(struct nc_widgetset *set,
693                 WINDOW *main, WINDOW *sub)
694 {
695         set->mainwin = main;
696         set->subwin = sub;
697 }
698
699 void widgetset_set_widget_focus(struct nc_widgetset *set,
700                 widget_focus_cb cb, void *arg)
701 {
702         set->widget_focus = cb;
703         set->widget_focus_arg = arg;
704 }
705
706 void widgetset_post(struct nc_widgetset *set)
707 {
708         struct nc_widget *widget;
709         FIELD *field;
710
711         set->form = new_form(set->fields);
712         set_form_win(set->form, set->mainwin);
713         set_form_sub(set->form, set->subwin);
714         post_form(set->form);
715         form_driver(set->form, REQ_END_FIELD);
716
717         if (set->cur_field) {
718                 set_current_field(set->form, set->cur_field);
719                 field = set->cur_field;
720         }
721
722         field = current_field(set->form);
723         widget = field_userptr(field);
724         widget_focus_change(widget, field, true);
725         if (set->widget_focus)
726                 set->widget_focus(widget, set->widget_focus_arg);
727 }
728
729 void widgetset_unpost(struct nc_widgetset *set)
730 {
731         set->cur_field = current_field(set->form);
732         unpost_form(set->form);
733         free_form(set->form);
734         set->form = NULL;
735 }
736
737 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field)
738 {
739         if (set->n_fields == set->n_alloc_fields - 1) {
740                 set->n_alloc_fields *= 2;
741                 set->fields = talloc_realloc(set, set->fields,
742                                 FIELD *, set->n_alloc_fields);
743         }
744
745         set->n_fields++;
746         set->fields[set->n_fields - 1] = field;
747         set->fields[set->n_fields] = NULL;
748 }
749
750 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field)
751 {
752         int i;
753
754         for (i = 0; i < set->n_fields; i++) {
755                 if (set->fields[i] == field)
756                         break;
757         }
758
759         if (i == set->n_fields)
760                 return;
761
762         memmove(&set->fields[i], &set->fields[i+i],
763                         (set->n_fields - i) * sizeof(set->fields[i]));
764         set->n_fields--;
765 }
766
767 #define DECLARE_BASEFN(type) \
768         struct nc_widget *widget_ ## type ## _base              \
769                 (struct nc_widget_ ## type *w)                  \
770         { return &w->widget; }
771
772 DECLARE_BASEFN(textbox);
773 DECLARE_BASEFN(checkbox);
774 DECLARE_BASEFN(select);
775 DECLARE_BASEFN(label);
776 DECLARE_BASEFN(button);
777
778 void widget_set_visible(struct nc_widget *widget, bool visible)
779 {
780         if (widget->set_visible)
781                 widget->set_visible(widget, visible);
782         else
783                 field_set_visible(widget->field, visible);
784 }
785
786 void widget_move(struct nc_widget *widget, int y, int x)
787 {
788         if (widget->move)
789                 widget->move(widget, y, x);
790         else
791                 field_move(widget->field, y, x);
792
793         widget->x = x;
794         widget->y = y;
795 }
796
797 int widget_height(struct nc_widget *widget)
798 {
799         return widget->height;
800 }
801
802 int widget_width(struct nc_widget *widget)
803 {
804         return widget->width;
805 }
806
807 int widget_x(struct nc_widget *widget)
808 {
809         return widget->x;
810 }
811
812 int widget_y(struct nc_widget *widget)
813 {
814         return widget->y;
815 }
816
817 int widget_focus_y(struct nc_widget *widget)
818 {
819         return widget->focus_y;
820 }
821