]> git.ozlabs.org Git - petitboot/blob - ui/ncurses/nc-widgets.c
discover: pxe: Avoid dereferencing null pointer
[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 #include <i18n/i18n.h>
55 #include <fold/fold.h>
56 #include <url/url.h>
57
58 #include "nc-cui.h"
59 #include "nc-widgets.h"
60
61 #undef move
62
63 #define to_checkbox(w) container_of(w, struct nc_widget_checkbox, widget)
64 #define to_textbox(w) container_of(w, struct nc_widget_textbox, widget)
65 #define to_button(w) container_of(w, struct nc_widget_button, widget)
66 #define to_select(w) container_of(w, struct nc_widget_select, widget)
67 #define to_subset(w) container_of(w, struct nc_widget_subset, widget)
68
69 static const char *checkbox_checked_str = "[*]";
70 static const char *checkbox_unchecked_str = "[ ]";
71
72 static const char *select_selected_str = "(*)";
73 static const char *select_unselected_str = "( )";
74
75 struct nc_widgetset {
76         WINDOW  *mainwin;
77         WINDOW  *subwin;
78         FORM    *form;
79         FIELD   **fields;
80         int     n_fields, n_alloc_fields;
81         void    (*widget_focus)(struct nc_widget *, void *);
82         void    *widget_focus_arg;
83         FIELD   *cur_field;
84
85         /* custom validators */
86         FIELDTYPE *ipv4_multi_type;
87         FIELDTYPE *url_type;
88 };
89
90 struct nc_widget {
91         FIELD   *field;
92         bool    (*process_key)(struct nc_widget *, FORM *, int);
93         void    (*set_visible)(struct nc_widget *, bool);
94         void    (*move)(struct nc_widget *, int, int);
95         void    (*field_focus)(struct nc_widget *, FIELD *);
96         int     focussed_attr;
97         int     unfocussed_attr;
98         int     height;
99         int     width;
100         int     focus_y;
101         int     x;
102         int     y;
103 };
104
105 struct nc_widget_label {
106         struct nc_widget        widget;
107         const char              *text;
108 };
109
110 struct nc_widget_checkbox {
111         struct nc_widget        widget;
112         bool                    checked;
113 };
114
115 struct nc_widget_textbox {
116         struct nc_widgetset     *set;
117         struct nc_widget        widget;
118 };
119
120 struct nc_widget_subset {
121         struct nc_widget        widget;
122         int                     *active;
123         int                     n_active;
124         struct subset_option {
125                 char            *str;
126                 int             val;
127                 FIELD           *field;
128         } *options;
129         int                     n_options;
130         int                     top, left, size;
131         struct nc_widgetset     *set;
132         void                    (*on_change)(void *, int);
133         void                    *on_change_arg;
134         void                    (*screen_cb)(void *,
135                                         struct nc_widget_subset *, int);
136 };
137
138 struct nc_widget_select {
139         struct nc_widget        widget;
140         struct select_option {
141                 char            *str;
142                 int             val;
143                 FIELD           *field;
144                 int             lines;
145         } *options;
146         int                     top, left, size;
147         int                     n_options, selected_option;
148         struct nc_widgetset     *set;
149         void                    (*on_change)(void *, int);
150         void                    *on_change_arg;
151 };
152
153 struct nc_widget_button {
154         struct nc_widget        widget;
155         void                    (*click)(void *arg);
156         void                    *arg;
157 };
158
159 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field);
160 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field);
161
162 static bool key_is_select(int key)
163 {
164         return key == ' ' || key == '\r' || key == '\n' || key == KEY_ENTER;
165 }
166
167 static bool process_key_nop(struct nc_widget *widget __attribute__((unused)),
168                 FORM *form __attribute((unused)),
169                 int key __attribute__((unused)))
170 {
171         return false;
172 }
173
174 static void field_set_visible(FIELD *field, bool visible)
175 {
176         int opts = field_opts(field) & ~O_VISIBLE;
177         if (visible)
178                 opts |= O_VISIBLE;
179         set_field_opts(field, opts);
180 }
181
182 static bool field_visible(FIELD *field)
183 {
184         return (field_opts(field) & O_VISIBLE) == O_VISIBLE;
185 }
186
187 static void field_move(FIELD *field, int y, int x)
188 {
189         move_field(field, y, x);
190 }
191
192 static int label_destructor(void *ptr)
193 {
194         struct nc_widget_label *label = ptr;
195         free_field(label->widget.field);
196         return 0;
197 }
198
199
200 struct nc_widget_label *widget_new_label(struct nc_widgetset *set,
201                 int y, int x, char *str)
202 {
203         struct nc_widget_label *label;
204         FIELD *f;
205         int len;
206
207         len = strlen(str);
208
209         label = talloc_zero(set, struct nc_widget_label);
210         label->widget.height = 1;
211         label->widget.width = len;
212         label->widget.x = x;
213         label->widget.y = y;
214         label->widget.process_key = process_key_nop;
215         label->widget.field = f = new_field(1, len, y, x, 0, 0);
216         label->widget.focussed_attr = A_NORMAL;
217         label->widget.unfocussed_attr = A_NORMAL;
218
219         field_opts_off(f, O_ACTIVE);
220         set_field_buffer(f, 0, str);
221         set_field_userptr(f, &label->widget);
222
223         widgetset_add_field(set, label->widget.field);
224         talloc_set_destructor(label, label_destructor);
225
226         return label;
227 }
228
229 bool widget_checkbox_get_value(struct nc_widget_checkbox *checkbox)
230 {
231         return checkbox->checked;
232 }
233
234 static void checkbox_set_buffer(struct nc_widget_checkbox *checkbox)
235 {
236         const char *str;
237         str = checkbox->checked ? checkbox_checked_str : checkbox_unchecked_str;
238         set_field_buffer(checkbox->widget.field, 0, str);
239 }
240
241 static bool checkbox_process_key(struct nc_widget *widget,
242                 FORM *form __attribute__((unused)), int key)
243 {
244         struct nc_widget_checkbox *checkbox = to_checkbox(widget);
245
246         if (!key_is_select(key))
247                 return false;
248
249         checkbox->checked = !checkbox->checked;
250         checkbox_set_buffer(checkbox);
251
252         return true;
253 }
254
255 static int checkbox_destructor(void *ptr)
256 {
257         struct nc_widget_checkbox *checkbox = ptr;
258         free_field(checkbox->widget.field);
259         return 0;
260 }
261
262 struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set,
263                 int y, int x, bool checked)
264 {
265         struct nc_widget_checkbox *checkbox;
266         FIELD *f;
267
268         checkbox = talloc_zero(set, struct nc_widget_checkbox);
269         checkbox->checked = checked;
270         checkbox->widget.height = 1;
271         checkbox->widget.width = strlen(checkbox_checked_str);
272         checkbox->widget.x = x;
273         checkbox->widget.y = y;
274         checkbox->widget.process_key = checkbox_process_key;
275         checkbox->widget.focussed_attr = A_REVERSE;
276         checkbox->widget.unfocussed_attr = A_NORMAL;
277         checkbox->widget.field = f = new_field(1, strlen(checkbox_checked_str),
278                         y, x, 0, 0);
279
280         field_opts_off(f, O_EDIT);
281         set_field_userptr(f, &checkbox->widget);
282         checkbox_set_buffer(checkbox);
283
284         widgetset_add_field(set, checkbox->widget.field);
285         talloc_set_destructor(checkbox, checkbox_destructor);
286
287         return checkbox;
288 }
289
290 static char *strip_string(char *str)
291 {
292         int len, i;
293
294         len = strlen(str);
295
296         /* clear trailing space */
297         for (i = len - 1; i >= 0; i--) {
298                 if (!isspace(str[i]))
299                         break;
300                 str[i] = '\0';
301         }
302
303         /* increment str past leading space */
304         for (i = 0; i < len; i++) {
305                 if (str[i] == '\0' || !isspace(str[i]))
306                         break;
307         }
308
309         return str + i;
310 }
311
312 char *widget_textbox_get_value(struct nc_widget_textbox *textbox)
313 {
314         char *str = field_buffer(textbox->widget.field, 0);
315         return str ? strip_string(str) : NULL;
316 }
317
318 static bool textbox_process_key(
319                 struct nc_widget *widget __attribute__((unused)),
320                 FORM *form, int key)
321 {
322         switch (key) {
323         case KEY_HOME:
324                 form_driver(form, REQ_BEG_FIELD);
325                 break;
326         case KEY_END:
327                 form_driver(form, REQ_END_FIELD);
328                 break;
329         case KEY_LEFT:
330                 form_driver(form, REQ_LEFT_CHAR);
331                 break;
332         case KEY_RIGHT:
333                 form_driver(form, REQ_RIGHT_CHAR);
334                 break;
335         case KEY_BACKSPACE:
336                 if (form_driver(form, REQ_LEFT_CHAR) != E_OK)
337                         break;
338                 /* fall through */
339         case KEY_DC:
340                 form_driver(form, REQ_DEL_CHAR);
341                 break;
342         case '\t':
343         case KEY_BTAB:
344         case KEY_UP:
345         case KEY_DOWN:
346         case KEY_PPAGE:
347         case KEY_NPAGE:
348                 /* Don't catch navigational keys */
349                 return false;
350         default:
351                 form_driver(form, key);
352                 break;
353         }
354
355         return true;
356 }
357
358 static int textbox_destructor(void *ptr)
359 {
360         struct nc_widget_textbox *textbox = ptr;
361         free_field(textbox->widget.field);
362         return 0;
363 }
364
365 struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
366                 int y, int x, int len, char *str)
367 {
368         struct nc_widget_textbox *textbox;
369         FIELD *f;
370
371         textbox = talloc_zero(set, struct nc_widget_textbox);
372         textbox->set = set;
373         textbox->widget.height = 1;
374         textbox->widget.width = len;
375         textbox->widget.x = x;
376         textbox->widget.y = y;
377         textbox->widget.process_key = textbox_process_key;
378         textbox->widget.field = f = new_field(1, len, y, x, 0, 0);
379         textbox->widget.focussed_attr = A_REVERSE;
380         textbox->widget.unfocussed_attr = A_UNDERLINE;
381
382         field_opts_off(f, O_STATIC | O_WRAP | O_BLANK);
383         set_field_buffer(f, 0, str);
384         set_field_back(f, textbox->widget.unfocussed_attr);
385         set_field_userptr(f, &textbox->widget);
386
387         widgetset_add_field(set, textbox->widget.field);
388         talloc_set_destructor(textbox, textbox_destructor);
389
390         return textbox;
391 }
392
393 void widget_textbox_set_fixed_size(struct nc_widget_textbox *textbox)
394 {
395         field_opts_on(textbox->widget.field, O_STATIC);
396 }
397
398 void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox,
399                 long min, long max)
400 {
401         set_field_type(textbox->widget.field, TYPE_INTEGER, 1, min, max);
402 }
403
404 static bool check_url_field(FIELD *field,
405                 const void *arg __attribute__((unused)))
406 {
407         return is_url(field_buffer(field, 0));
408 }
409
410 void widget_textbox_set_validator_url(struct nc_widget_textbox *textbox)
411 {
412         if (!textbox->set->url_type)
413                 textbox->set->url_type = new_fieldtype(check_url_field, NULL);
414
415         set_field_type(textbox->widget.field, textbox->set->url_type);
416 }
417
418 void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox)
419 {
420         set_field_type(textbox->widget.field, TYPE_IPV4);
421 }
422
423 static bool check_ipv4_multi_char(int c,
424                 const void *arg __attribute__((unused)))
425 {
426         return isdigit(c) || c == '.' || c == ' ';
427 }
428
429 static bool check_ipv4_multi_field(FIELD *field,
430                 const void *arg __attribute__((unused)))
431 {
432         char *buf = field_buffer(field, 0);
433         unsigned int ip[4];
434         int n, len;
435
436         while (*buf != '\0') {
437                 n = sscanf(buf, "%u.%u.%u.%u%n",
438                                 &ip[0], &ip[1], &ip[2], &ip[3], &len);
439                 if (n != 4)
440                         return false;
441
442                 if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
443                         return false;
444
445                 for (buf += len; *buf != '\0'; buf++) {
446                         if (isspace(*buf))
447                                 continue;
448                         else if (isdigit(*buf))
449                                 break;
450                         else
451                                 return false;
452                 }
453         }
454
455         return true;
456 }
457
458 void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox)
459 {
460         if (!textbox->set->ipv4_multi_type) {
461                 textbox->set->ipv4_multi_type = new_fieldtype(
462                                 check_ipv4_multi_field,
463                                 check_ipv4_multi_char);
464         }
465         set_field_type(textbox->widget.field, textbox->set->ipv4_multi_type);
466 }
467
468 static void subset_update_order(struct nc_widget_subset *subset)
469 {
470         char *str;
471         int i, val;
472
473         for (i = 0; i < subset->n_active; i++) {
474                 val = subset->active[i];
475                 str = talloc_asprintf(subset, "(%d) %s",
476                                       i, subset->options[val].str);
477                 set_field_buffer(subset->options[val].field, 0,
478                                  str);
479                 talloc_free(str);
480         }
481 }
482
483 static void widget_focus_change(struct nc_widget *widget, FIELD *field,
484                 bool focussed);
485
486 static void subset_delete_active(struct nc_widget_subset *subset, int idx)
487 {
488         bool last = idx == (subset->n_active - 1);
489         struct nc_widgetset *set = subset->set;
490         struct nc_widget *widget;
491         size_t rem;
492         int i, val;
493         uint32_t opts;
494
495         /* Shift field focus to nearest active option or next visible field */
496         if (subset->n_active > 1) {
497                 if (last)
498                         val = subset->active[idx - 1];
499                 else
500                         val = subset->active[idx + 1];
501                 set->cur_field = subset->options[val].field;
502         } else {
503                 for (i = 0; i < set->n_fields; i++)
504                         if (field_visible(set->fields[i])) {
505                                 opts = field_opts(set->fields[i]);
506                                 if ((opts & O_ACTIVE) == O_ACTIVE) {
507                                         set->cur_field = set->fields[i];
508                                         break;
509                                 }
510                         }
511         }
512
513         set_current_field(set->form, set->cur_field);
514         widget = field_userptr(set->cur_field);
515         widget_focus_change(widget, set->cur_field, true);
516         if (set->widget_focus)
517                 set->widget_focus(widget, set->widget_focus_arg);
518
519         /* Update active array */
520         rem = sizeof(int) * (subset->n_active - idx - 1);
521         val = subset->active[idx];
522         field_set_visible(subset->options[val].field, false);
523         if (rem)
524                 memmove(&subset->active[idx], &subset->active[idx + 1], rem);
525         subset->n_active--;
526         subset->active = talloc_realloc(subset, subset->active,
527                                          int, subset->n_active);
528
529         subset->widget.height = subset->n_active;
530 }
531
532 static bool subset_process_key(struct nc_widget *w, FORM *form, int key)
533 {
534         struct nc_widget_subset *subset = to_subset(w);
535         int i, val, opt_idx = -1;
536         FIELD *field;
537
538         if (key != '-' && key != '+' && key != KEY_DC && key != KEY_BACKSPACE)
539                 return false;
540
541         field = current_field(form);
542
543         for (i = 0; i < subset->n_active; i++) {
544                 val = subset->active[i];
545                 if (subset->options[val].field == field) {
546                         opt_idx = i;
547                         break;
548                 }
549         }
550
551         if (opt_idx < 0)
552                 return false;
553
554         if (key == KEY_DC || key == KEY_BACKSPACE)
555                 subset_delete_active(subset, opt_idx);
556
557         if (key == '-') {
558                 if (opt_idx == 0)
559                         return true;
560
561                 val = subset->active[opt_idx];
562                 subset->active[opt_idx] = subset->active[opt_idx - 1];
563                 subset->active[opt_idx - 1] = val;
564         }
565
566         if (key == '+') {
567                 if (opt_idx >= subset->n_active - 1)
568                         return true;
569
570                 val = subset->active[opt_idx];
571                 subset->active[opt_idx] = subset->active[opt_idx + 1];
572                 subset->active[opt_idx + 1] = val;
573         }
574
575         subset_update_order(subset);
576
577         if (subset->on_change)
578                 subset->on_change(subset->on_change_arg, 0);
579
580         return true;
581 }
582
583 static void subset_set_visible(struct nc_widget *widget, bool visible)
584 {
585         struct nc_widget_subset *subset = to_subset(widget);
586         int i, val;
587
588         for (i = 0; i < subset->n_active; i++) {
589                 val = subset->active[i];
590                 field_set_visible(subset->options[val].field, visible);
591         }
592 }
593
594 static void subset_move(struct nc_widget *widget, int y, int x)
595 {
596         struct nc_widget_subset *subset = to_subset(widget);
597         int i, val;
598
599         for (i = 0; i < subset->n_active; i++) {
600                 val = subset->active[i];
601                 field_move(subset->options[val].field, y + i , x);
602         }
603 }
604
605 static void subset_field_focus(struct nc_widget *widget, FIELD *field)
606 {
607         struct nc_widget_subset *subset = to_subset(widget);
608         int i, val;
609
610         for (i = 0; i < subset->n_active; i++) {
611                 val = subset->active[i];
612                 if (field == subset->options[val].field) {
613                         widget->focus_y = i;
614                         return;
615                 }
616         }
617 }
618
619 static int subset_destructor(void *ptr)
620 {
621         struct nc_widget_subset *subset = ptr;
622         int i;
623
624         for (i = 0; i < subset->n_options; i++)
625                 free_field(subset->options[i].field);
626
627         return 0;
628 }
629
630 struct nc_widget_subset *widget_new_subset(struct nc_widgetset *set,
631                 int y, int x, int len, void *screen_cb)
632 {
633         struct nc_widget_subset *subset;
634
635         subset = talloc_zero(set, struct nc_widget_subset);
636         subset->widget.width = len;
637         subset->widget.height = 0;
638         subset->widget.x = x;
639         subset->widget.y = y;
640         subset->widget.process_key = subset_process_key;
641         subset->widget.set_visible = subset_set_visible;
642         subset->widget.move = subset_move;
643         subset->widget.field_focus = subset_field_focus;
644         subset->widget.focussed_attr = A_REVERSE;
645         subset->widget.unfocussed_attr = A_NORMAL;
646         subset->top = y;
647         subset->left = x;
648         subset->size = len;
649         subset->set = set;
650         subset->n_active = subset->n_options = 0;
651         subset->active = NULL;
652         subset->options = NULL;
653         subset->screen_cb = screen_cb;
654
655         talloc_set_destructor(subset, subset_destructor);
656
657         return subset;
658 }
659
660 void widget_subset_add_option(struct nc_widget_subset *subset, const char *text)
661 {
662         FIELD *f;
663         int i;
664
665         i = subset->n_options++;
666         subset->options = talloc_realloc(subset, subset->options,
667                                          struct subset_option, i + 1);
668
669         subset->options[i].str = talloc_strdup(subset->options, text);
670
671         subset->options[i].field = f = new_field(1, subset->size, subset->top + i,
672                                                  subset->left, 0, 0);
673
674         field_opts_off(f, O_WRAP | O_EDIT);
675         set_field_userptr(f, &subset->widget);
676         set_field_buffer(f, 0, subset->options[i].str);
677         field_set_visible(f, false);
678         widgetset_add_field(subset->set, f);
679 }
680
681 void widget_subset_make_active(struct nc_widget_subset *subset, int idx)
682 {
683         int i;
684
685         for (i = 0; i < subset->n_active; i++)
686                 if (subset->active[i] == idx) {
687                         pb_debug("%s: Index %d already active\n", __func__, idx);
688                         return;
689                 }
690
691         i = subset->n_active++;
692         subset->widget.height = subset->n_active;
693         subset->active = talloc_realloc(subset, subset->active,
694                                         int, i + 1);
695         subset->active[i] = idx;
696
697         subset_update_order(subset);
698 }
699
700 int widget_subset_get_order(void *ctx, unsigned int **order,
701                 struct nc_widget_subset *subset)
702 {
703         unsigned int *buf = talloc_array(ctx, unsigned int, subset->n_active);
704         int i;
705
706         for (i = 0; i < subset->n_active; i++)
707                 buf[i] = subset->active[i];
708
709         *order = buf;
710         return i;
711 }
712
713 void widget_subset_show_inactive(struct nc_widget_subset *subset,
714                 struct nc_widget_select *select)
715 {
716         bool active = false, first = true;
717         int i, j;
718
719         for (i = 0; i < subset->n_options; i++) {
720                 active = false;
721                 for (j = 0; j < subset->n_active; j++)
722                         if (subset->active[j] == i)
723                                 active = true;
724
725                 if (active)
726                         continue;
727
728                 widget_select_add_option(select, i,
729                                          subset->options[i].str, first);
730                 if (first)
731                         first = false;
732         }
733 }
734
735 int widget_subset_n_inactive(struct nc_widget_subset *subset)
736 {
737         return subset->n_options - subset->n_active;
738 }
739
740 int widget_subset_height(struct nc_widget_subset *subset)
741 {
742         return subset->n_active;
743 }
744
745 void widget_subset_on_change(struct nc_widget_subset *subset,
746                 void (*on_change)(void *, int), void *arg)
747 {
748         subset->on_change = on_change;
749         subset->on_change_arg = arg;
750 }
751
752 void widget_subset_drop_options(struct nc_widget_subset *subset)
753 {
754         struct nc_widgetset *set = subset->set;
755         int i;
756
757         for (i = 0; i < subset->n_options; i++) {
758                 FIELD *field = subset->options[i].field;
759                 widgetset_remove_field(set, field);
760                 if (field == set->cur_field)
761                         set->cur_field = NULL;
762                 free_field(subset->options[i].field);
763         }
764
765         talloc_free(subset->options);
766         talloc_free(subset->active);
767         subset->options = NULL;
768         subset->active = NULL;
769         subset->n_options = 0;
770         subset->n_active = 0;
771         subset->widget.height = 0;
772         subset->widget.focus_y = 0;
773 }
774
775 void widget_subset_clear_active(struct nc_widget_subset *subset)
776 {
777         int i;
778
779         for (i = 0; i < subset->n_options; i++)
780                 field_set_visible(subset->options[i].field, false);
781
782         talloc_free(subset->active);
783         subset->active = NULL;
784         subset->n_active = 0;
785         subset->widget.height = 0;
786         subset->widget.focus_y = 0;
787 }
788
789 void widget_subset_callback(void *arg,
790                 struct nc_widget_subset *subset, int idx)
791 {
792         subset->screen_cb(arg, subset, idx);
793 }
794
795 static void select_option_change(struct select_option *opt, bool selected)
796 {
797         const char *str;
798
799         str = selected ? select_selected_str : select_unselected_str;
800
801         memcpy(opt->str, str, strlen(str));
802         set_field_buffer(opt->field, 0, opt->str);
803 }
804
805 static bool select_process_key(struct nc_widget *w, FORM *form, int key)
806 {
807         struct nc_widget_select *select = to_select(w);
808         struct select_option *new_opt, *old_opt;
809         int i, new_idx;
810         FIELD *field;
811
812         if (!key_is_select(key))
813                 return false;
814
815         field = current_field(form);
816         new_opt = NULL;
817
818         for (i = 0; i < select->n_options; i++) {
819                 if (select->options[i].field == field) {
820                         new_opt = &select->options[i];
821                         new_idx = i;
822                         break;
823                 }
824         }
825
826         if (!new_opt)
827                 return true;
828
829         if (new_idx == select->selected_option)
830                 return true;
831
832         old_opt = &select->options[select->selected_option];
833
834         select_option_change(old_opt, false);
835         select_option_change(new_opt, true);
836
837         select->selected_option = new_idx;
838
839         if (select->on_change)
840                 select->on_change(select->on_change_arg, new_opt->val);
841
842         return true;
843 }
844
845 static void select_set_visible(struct nc_widget *widget, bool visible)
846 {
847         struct nc_widget_select *select = to_select(widget);
848         int i;
849
850         for (i = 0; i < select->n_options; i++)
851                 field_set_visible(select->options[i].field, visible);
852 }
853
854 static void select_move(struct nc_widget *widget, int y, int x)
855 {
856         struct nc_widget_select *select = to_select(widget);
857         int i, cur = 0;
858
859         for (i = 0; i < select->n_options; i++) {
860                 field_move(select->options[i].field, y + cur, x);
861                 cur += select->options[i].lines;
862         }
863 }
864
865 static void select_field_focus(struct nc_widget *widget, FIELD *field)
866 {
867         struct nc_widget_select *select = to_select(widget);
868         int i, cur = 0;
869
870         for (i = 0; i < select->n_options; i++) {
871                 if (field != select->options[i].field) {
872                         cur += select->options[i].lines;
873                         continue;
874                 }
875                 widget->focus_y = cur;
876                 return;
877         }
878 }
879
880 static int select_destructor(void *ptr)
881 {
882         struct nc_widget_select *select = ptr;
883         int i;
884
885         for (i = 0; i < select->n_options; i++)
886                 free_field(select->options[i].field);
887
888         return 0;
889 }
890
891 struct nc_widget_select *widget_new_select(struct nc_widgetset *set,
892                 int y, int x, int len)
893 {
894         struct nc_widget_select *select;
895
896         select = talloc_zero(set, struct nc_widget_select);
897         select->widget.width = len;
898         select->widget.height = 0;
899         select->widget.x = x;
900         select->widget.y = y;
901         select->widget.process_key = select_process_key;
902         select->widget.set_visible = select_set_visible;
903         select->widget.move = select_move;
904         select->widget.field_focus = select_field_focus;
905         select->widget.focussed_attr = A_REVERSE;
906         select->widget.unfocussed_attr = A_NORMAL;
907         select->top = y;
908         select->left = x;
909         select->size = len;
910         select->set = set;
911
912         talloc_set_destructor(select, select_destructor);
913
914         return select;
915 }
916
917 static int widget_select_fold_cb(void *arg, const char *buf, int len)
918 {
919         struct nc_widget_select *select = arg;
920         char *line, *newstr, *padbuf = NULL;
921         int i, pad;
922
923         if (!len)
924                 return 0;
925
926         line = talloc_strndup(select->options, buf, len);
927
928         i = select->n_options - 1;
929         pad = max(0, select->widget.width - strncols(line));
930
931         if (pad) {
932                 padbuf = talloc_array(select->options, char, pad + 1);
933                 memset(padbuf, ' ', pad);
934                 padbuf[pad] = '\0';
935         }
936
937         if (select->options[i].str)
938                 newstr = talloc_asprintf_append(select->options[i].str,
939                                                          "%s%s", line,
940                                                          pad ? padbuf : "");
941         else
942                 newstr = talloc_asprintf(select->options, "%s%s", line,
943                                                          pad ? padbuf : "");
944
945         select->options[i].str = newstr;
946         select->options[i].lines++;
947
948         talloc_free(padbuf);
949         talloc_free(line);
950         return 0;
951 }
952
953 void widget_select_add_option(struct nc_widget_select *select, int value,
954                 const char *text, bool selected)
955 {
956         const char *str;
957         char *full_text;
958         FIELD *f;
959         int i;
960
961         /* if we never see an option with selected set, we want the first
962          * one to be selected */
963         if (select->n_options == 0)
964                 selected = true;
965         else if (selected)
966                 select_option_change(&select->options[select->selected_option],
967                                         false);
968
969         if (selected) {
970                 select->selected_option = select->n_options;
971                 str = select_selected_str;
972         } else
973                 str = select_unselected_str;
974
975         i = select->n_options++;
976         select->options = talloc_realloc(select, select->options,
977                                 struct select_option, i + 2);
978         select->options[i].val = value;
979         select->options[i].lines = 0;
980         select->options[i].str = NULL;
981
982         full_text = talloc_asprintf(select->options, "%s %s", str, text);
983         fold_text(full_text, select->widget.width,
984                   widget_select_fold_cb, select);
985
986         select->options[i].field = f = new_field(select->options[i].lines,
987                                         select->size,
988                                         select->top + select->widget.height,
989                                         select->left, 0, 0);
990
991         select->widget.height += select->options[i].lines;
992
993         field_opts_off(f, O_EDIT);
994         set_field_userptr(f, &select->widget);
995         set_field_buffer(f, 0, select->options[i].str);
996
997         widgetset_add_field(select->set, f);
998         talloc_free(full_text);
999 }
1000
1001 int widget_select_get_value(struct nc_widget_select *select)
1002 {
1003         if (!select->n_options)
1004                 return -1;
1005         return select->options[select->selected_option].val;
1006 }
1007
1008 int widget_select_height(struct nc_widget_select *select)
1009 {
1010         return select->widget.height;
1011 }
1012
1013 void widget_select_on_change(struct nc_widget_select *select,
1014                 void (*on_change)(void *, int), void *arg)
1015 {
1016         select->on_change = on_change;
1017         select->on_change_arg = arg;
1018 }
1019
1020 void widget_select_drop_options(struct nc_widget_select *select)
1021 {
1022         struct nc_widgetset *set = select->set;
1023         int i;
1024
1025         for (i = 0; i < select->n_options; i++) {
1026                 FIELD *field = select->options[i].field;
1027                 widgetset_remove_field(set, field);
1028                 if (field == set->cur_field)
1029                         set->cur_field = NULL;
1030                 free_field(select->options[i].field);
1031         }
1032
1033         talloc_free(select->options);
1034         select->options = NULL;
1035         select->n_options = 0;
1036         select->widget.height = 0;
1037         select->widget.focus_y = 0;
1038
1039 }
1040
1041 static bool button_process_key(struct nc_widget *widget,
1042                 FORM *form __attribute__((unused)), int key)
1043 {
1044         struct nc_widget_button *button = to_button(widget);
1045
1046         if (!button->click)
1047                 return false;
1048
1049         if (!key_is_select(key))
1050                 return false;
1051
1052         button->click(button->arg);
1053         return true;
1054 }
1055
1056 static int button_destructor(void *ptr)
1057 {
1058         struct nc_widget_button *button = ptr;
1059         free_field(button->widget.field);
1060         return 0;
1061 }
1062
1063 struct nc_widget_button *widget_new_button(struct nc_widgetset *set,
1064                 int y, int x, int size, const char *str,
1065                 void (*click)(void *), void *arg)
1066 {
1067         struct nc_widget_button *button;
1068         int idx, len, pad1, pad2, bufsz;
1069         char *text;
1070         FIELD *f;
1071
1072         int field_size = size + 2;
1073
1074         button = talloc_zero(set, struct nc_widget_button);
1075         button->widget.height = 1;
1076         button->widget.width = field_size;
1077         button->widget.x = x;
1078         button->widget.y = y;
1079         button->widget.field = f = new_field(1, field_size, y, x, 0, 0);
1080         button->widget.process_key = button_process_key;
1081         button->widget.focussed_attr = A_REVERSE;
1082         button->widget.unfocussed_attr = A_NORMAL;
1083         button->click = click;
1084         button->arg = arg;
1085
1086         field_opts_off(f, O_EDIT);
1087         set_field_userptr(f, &button->widget);
1088
1089         /* Center str in the field. This depends on the number of columns used
1090          * by the string, not the number of chars in str */
1091         len = strncols(str);
1092         if (len <= size) {
1093                 idx = (field_size - len) / 2;
1094         } else {
1095                 idx = 1;
1096                 pb_log("Warning: '%s' %d columns wide "
1097                        "but button is %d columns wide\n",
1098                        str, len, size);
1099         }
1100
1101         pad1 = max(idx - 1, 0);
1102         pad2 = max(size - len - pad1, 0);
1103         bufsz = 1 + pad1 + strlen(str) + pad2 + 2;
1104
1105         text = talloc_array(button, char, bufsz);
1106         memset(text, ' ', bufsz);
1107         memcpy(text + idx, str, strlen(str));
1108         text[0] = '[';
1109         text[bufsz - 2] = ']';
1110         text[bufsz - 1] = '\0';
1111
1112         set_field_buffer(f, 0, text);
1113
1114         widgetset_add_field(set, button->widget.field);
1115         talloc_set_destructor(button, button_destructor);
1116
1117         return button;
1118 }
1119
1120 void widget_focus_change(struct nc_widget *widget, FIELD *field,
1121                 bool focussed)
1122 {
1123         int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr;
1124         set_field_back(field, attr);
1125 }
1126
1127 bool widgetset_process_key(struct nc_widgetset *set, int key)
1128 {
1129         struct nc_widget *widget;
1130         FIELD *field, *tmp;
1131         int req = 0;
1132         bool tab;
1133
1134         field = current_field(set->form);
1135         assert(field);
1136
1137         widget = field_userptr(field);
1138
1139         if (widget->process_key)
1140                 if (widget->process_key(widget, set->form, key))
1141                         return true;
1142
1143         tab = false;
1144
1145         /* handle field change events */
1146         switch (key) {
1147         case KEY_BTAB:
1148                 tab = true;
1149                 /* fall through */
1150         case KEY_UP:
1151                 req = REQ_SPREV_FIELD;
1152                 break;
1153         case '\t':
1154                 tab = true;
1155                 /* fall through */
1156         case KEY_DOWN:
1157                 req = REQ_SNEXT_FIELD;
1158                 break;
1159         case KEY_PPAGE:
1160                 req = REQ_SFIRST_FIELD;
1161                 break;
1162         case KEY_NPAGE:
1163                 req = REQ_SLAST_FIELD;
1164                 break;
1165         case KEY_LEFT:
1166                 req = REQ_LEFT_FIELD;
1167                 break;
1168         case KEY_RIGHT:
1169                 req = REQ_RIGHT_FIELD;
1170                 break;
1171         }
1172
1173         if (req) {
1174                 widget_focus_change(widget, field, false);
1175                 form_driver(set->form, req);
1176
1177                 /* if we're doing a tabbed-field-change, skip until we
1178                  * see the next widget */
1179                 tmp = field;
1180                 field = current_field(set->form);
1181
1182                 for (; tab && tmp != field && field_userptr(field) == widget;) {
1183                         form_driver(set->form, req);
1184                         field = current_field(set->form);
1185                 }
1186
1187                 form_driver(set->form, REQ_END_FIELD);
1188                 widget = field_userptr(field);
1189                 widget_focus_change(widget, field, true);
1190                 if (widget->field_focus)
1191                         widget->field_focus(widget, field);
1192                 if (set->widget_focus)
1193                         set->widget_focus(widget, set->widget_focus_arg);
1194                 return true;
1195         }
1196
1197         return false;
1198 }
1199
1200 static int widgetset_destructor(void *ptr)
1201 {
1202         struct nc_widgetset *set = ptr;
1203         free_form(set->form);
1204         if (set->ipv4_multi_type)
1205                 free_fieldtype(set->ipv4_multi_type);
1206         return 0;
1207 }
1208
1209 struct nc_widgetset *widgetset_create(void *ctx, WINDOW *main, WINDOW *sub)
1210 {
1211         struct nc_widgetset *set;
1212
1213         set = talloc_zero(ctx, struct nc_widgetset);
1214         set->n_alloc_fields = 8;
1215         set->mainwin = main;
1216         set->subwin = sub;
1217         set->fields = talloc_array(set, FIELD *, set->n_alloc_fields);
1218         talloc_set_destructor(set, widgetset_destructor);
1219
1220         return set;
1221 }
1222
1223 void widgetset_set_windows(struct nc_widgetset *set,
1224                 WINDOW *main, WINDOW *sub)
1225 {
1226         set->mainwin = main;
1227         set->subwin = sub;
1228 }
1229
1230 void widgetset_set_widget_focus(struct nc_widgetset *set,
1231                 widget_focus_cb cb, void *arg)
1232 {
1233         set->widget_focus = cb;
1234         set->widget_focus_arg = arg;
1235 }
1236
1237 void widgetset_post(struct nc_widgetset *set)
1238 {
1239         struct nc_widget *widget;
1240         FIELD *field;
1241
1242         set->form = new_form(set->fields);
1243         set_form_win(set->form, set->mainwin);
1244         set_form_sub(set->form, set->subwin);
1245         post_form(set->form);
1246         form_driver(set->form, REQ_END_FIELD);
1247
1248         if (set->cur_field)
1249                 set_current_field(set->form, set->cur_field);
1250
1251         field = current_field(set->form);
1252         widget = field_userptr(field);
1253         widget_focus_change(widget, field, true);
1254         if (set->widget_focus)
1255                 set->widget_focus(widget, set->widget_focus_arg);
1256 }
1257
1258 void widgetset_unpost(struct nc_widgetset *set)
1259 {
1260         set->cur_field = current_field(set->form);
1261         unpost_form(set->form);
1262         free_form(set->form);
1263         set->form = NULL;
1264 }
1265
1266 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field)
1267 {
1268         if (set->n_fields == set->n_alloc_fields - 1) {
1269                 set->n_alloc_fields *= 2;
1270                 set->fields = talloc_realloc(set, set->fields,
1271                                 FIELD *, set->n_alloc_fields);
1272         }
1273
1274         set->n_fields++;
1275         set->fields[set->n_fields - 1] = field;
1276         set->fields[set->n_fields] = NULL;
1277 }
1278
1279 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field)
1280 {
1281         int i;
1282
1283         for (i = 0; i < set->n_fields; i++) {
1284                 if (set->fields[i] == field)
1285                         break;
1286         }
1287
1288         if (i == set->n_fields)
1289                 return;
1290
1291         memmove(&set->fields[i], &set->fields[i+i],
1292                         (set->n_fields - i) * sizeof(set->fields[i]));
1293         set->n_fields--;
1294 }
1295
1296 #define DECLARE_BASEFN(type) \
1297         struct nc_widget *widget_ ## type ## _base              \
1298                 (struct nc_widget_ ## type *w)                  \
1299         { return &w->widget; }
1300
1301 DECLARE_BASEFN(textbox);
1302 DECLARE_BASEFN(checkbox);
1303 DECLARE_BASEFN(subset);
1304 DECLARE_BASEFN(select);
1305 DECLARE_BASEFN(label);
1306 DECLARE_BASEFN(button);
1307
1308 void widget_set_visible(struct nc_widget *widget, bool visible)
1309 {
1310         if (widget->set_visible)
1311                 widget->set_visible(widget, visible);
1312         else
1313                 field_set_visible(widget->field, visible);
1314 }
1315
1316 void widget_move(struct nc_widget *widget, int y, int x)
1317 {
1318         if (widget->move)
1319                 widget->move(widget, y, x);
1320         else
1321                 field_move(widget->field, y, x);
1322
1323         widget->x = x;
1324         widget->y = y;
1325
1326         if (x + widget->width > COLS)
1327                 pb_debug("%s: Widget at %d,%d runs over pad! (%d)", __func__,
1328                        y, x, x + widget->width);
1329 }
1330
1331 int widget_height(struct nc_widget *widget)
1332 {
1333         return widget->height;
1334 }
1335
1336 int widget_width(struct nc_widget *widget)
1337 {
1338         return widget->width;
1339 }
1340
1341 int widget_x(struct nc_widget *widget)
1342 {
1343         return widget->x;
1344 }
1345
1346 int widget_y(struct nc_widget *widget)
1347 {
1348         return widget->y;
1349 }
1350
1351 int widget_focus_y(struct nc_widget *widget)
1352 {
1353         return widget->focus_y;
1354 }
1355