]> git.ozlabs.org Git - petitboot/blob - ui/ncurses/nc-widgets.c
bd78927781f2c6dc830dd514bd5d97a9128b5c1f
[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         /* custom validators */
82         FIELDTYPE *ipv4_multi_type;
83 };
84
85 struct nc_widget {
86         FIELD   *field;
87         bool    (*process_key)(struct nc_widget *, FORM *, int);
88         void    (*set_visible)(struct nc_widget *, bool);
89         void    (*move)(struct nc_widget *, int, int);
90         void    (*field_focus)(struct nc_widget *, FIELD *);
91         int     focussed_attr;
92         int     unfocussed_attr;
93         int     height;
94         int     width;
95         int     focus_y;
96         int     x;
97         int     y;
98 };
99
100 struct nc_widget_label {
101         struct nc_widget        widget;
102         const char              *text;
103 };
104
105 struct nc_widget_checkbox {
106         struct nc_widget        widget;
107         bool                    checked;
108 };
109
110 struct nc_widget_textbox {
111         struct nc_widgetset     *set;
112         struct nc_widget        widget;
113 };
114
115 struct nc_widget_select {
116         struct nc_widget        widget;
117         struct select_option {
118                 char            *str;
119                 int             val;
120                 FIELD           *field;
121         } *options;
122         int                     top, left, size;
123         int                     n_options, selected_option;
124         struct nc_widgetset     *set;
125         void                    (*on_change)(void *, int);
126         void                    *on_change_arg;
127 };
128
129 struct nc_widget_button {
130         struct nc_widget        widget;
131         void                    (*click)(void *arg);
132         void                    *arg;
133 };
134
135 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field);
136 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field);
137
138 static bool key_is_select(int key)
139 {
140         return key == ' ' || key == '\r' || key == '\n' || key == KEY_ENTER;
141 }
142
143 static bool process_key_nop(struct nc_widget *widget __attribute__((unused)),
144                 FORM *form __attribute((unused)),
145                 int key __attribute__((unused)))
146 {
147         return false;
148 }
149
150 static void field_set_visible(FIELD *field, bool visible)
151 {
152         int opts = field_opts(field) & ~O_VISIBLE;
153         if (visible)
154                 opts |= O_VISIBLE;
155         set_field_opts(field, opts);
156 }
157
158 static void field_move(FIELD *field, int y, int x)
159 {
160         move_field(field, y, x);
161 }
162
163 static int label_destructor(void *ptr)
164 {
165         struct nc_widget_label *label = ptr;
166         free_field(label->widget.field);
167         return 0;
168 }
169
170
171 struct nc_widget_label *widget_new_label(struct nc_widgetset *set,
172                 int y, int x, char *str)
173 {
174         struct nc_widget_label *label;
175         FIELD *f;
176         int len;
177
178         len = strlen(str);
179
180         label = talloc_zero(set, struct nc_widget_label);
181         label->widget.height = 1;
182         label->widget.width = len;
183         label->widget.x = x;
184         label->widget.y = y;
185         label->widget.process_key = process_key_nop;
186         label->widget.field = f = new_field(1, len, y, x, 0, 0);
187         label->widget.focussed_attr = A_NORMAL;
188         label->widget.unfocussed_attr = A_NORMAL;
189
190         field_opts_off(f, O_ACTIVE);
191         set_field_buffer(f, 0, str);
192         set_field_userptr(f, &label->widget);
193
194         widgetset_add_field(set, label->widget.field);
195         talloc_set_destructor(label, label_destructor);
196
197         return label;
198 }
199
200 bool widget_checkbox_get_value(struct nc_widget_checkbox *checkbox)
201 {
202         return checkbox->checked;
203 }
204
205 static void checkbox_set_buffer(struct nc_widget_checkbox *checkbox)
206 {
207         const char *str;
208         str = checkbox->checked ? checkbox_checked_str : checkbox_unchecked_str;
209         set_field_buffer(checkbox->widget.field, 0, str);
210 }
211
212 static bool checkbox_process_key(struct nc_widget *widget,
213                 FORM *form __attribute__((unused)), int key)
214 {
215         struct nc_widget_checkbox *checkbox = to_checkbox(widget);
216
217         if (!key_is_select(key))
218                 return false;
219
220         checkbox->checked = !checkbox->checked;
221         checkbox_set_buffer(checkbox);
222
223         return true;
224 }
225
226 static int checkbox_destructor(void *ptr)
227 {
228         struct nc_widget_checkbox *checkbox = ptr;
229         free_field(checkbox->widget.field);
230         return 0;
231 }
232
233 struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set,
234                 int y, int x, bool checked)
235 {
236         struct nc_widget_checkbox *checkbox;
237         FIELD *f;
238
239         checkbox = talloc_zero(set, struct nc_widget_checkbox);
240         checkbox->checked = checked;
241         checkbox->widget.height = 1;
242         checkbox->widget.width = strlen(checkbox_checked_str);
243         checkbox->widget.x = x;
244         checkbox->widget.y = y;
245         checkbox->widget.process_key = checkbox_process_key;
246         checkbox->widget.focussed_attr = A_REVERSE;
247         checkbox->widget.unfocussed_attr = A_NORMAL;
248         checkbox->widget.field = f = new_field(1, strlen(checkbox_checked_str),
249                         y, x, 0, 0);
250
251         field_opts_off(f, O_EDIT);
252         set_field_userptr(f, &checkbox->widget);
253         checkbox_set_buffer(checkbox);
254
255         widgetset_add_field(set, checkbox->widget.field);
256         talloc_set_destructor(checkbox, checkbox_destructor);
257
258         return checkbox;
259 }
260
261 static char *strip_string(char *str)
262 {
263         int len, i;
264
265         len = strlen(str);
266
267         /* clear trailing space */
268         for (i = len - 1; i >= 0; i--) {
269                 if (!isspace(str[i]))
270                         break;
271                 str[i] = '\0';
272         }
273
274         /* increment str past leading space */
275         for (i = 0; i < len; i++) {
276                 if (str[i] == '\0' || !isspace(str[i]))
277                         break;
278         }
279
280         return str + i;
281 }
282
283 char *widget_textbox_get_value(struct nc_widget_textbox *textbox)
284 {
285         char *str = field_buffer(textbox->widget.field, 0);
286         return str ? strip_string(str) : NULL;
287 }
288
289 static bool textbox_process_key(
290                 struct nc_widget *widget __attribute__((unused)),
291                 FORM *form, int key)
292 {
293         switch (key) {
294         case KEY_HOME:
295                 form_driver(form, REQ_BEG_FIELD);
296                 break;
297         case KEY_END:
298                 form_driver(form, REQ_END_FIELD);
299                 break;
300         case KEY_LEFT:
301                 form_driver(form, REQ_LEFT_CHAR);
302                 break;
303         case KEY_RIGHT:
304                 form_driver(form, REQ_RIGHT_CHAR);
305                 break;
306         case KEY_BACKSPACE:
307                 if (form_driver(form, REQ_LEFT_CHAR) != E_OK)
308                         break;
309                 /* fall through */
310         case KEY_DC:
311                 form_driver(form, REQ_DEL_CHAR);
312                 break;
313         default:
314                 form_driver(form, key);
315                 break;
316         }
317
318         return true;
319 }
320
321 static int textbox_destructor(void *ptr)
322 {
323         struct nc_widget_textbox *textbox = ptr;
324         free_field(textbox->widget.field);
325         return 0;
326 }
327
328 struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
329                 int y, int x, int len, char *str)
330 {
331         struct nc_widget_textbox *textbox;
332         FIELD *f;
333
334         textbox = talloc_zero(set, struct nc_widget_textbox);
335         textbox->set = set;
336         textbox->widget.height = 1;
337         textbox->widget.width = len;
338         textbox->widget.x = x;
339         textbox->widget.y = y;
340         textbox->widget.process_key = textbox_process_key;
341         textbox->widget.field = f = new_field(1, len, y, x, 0, 0);
342         textbox->widget.focussed_attr = A_REVERSE;
343         textbox->widget.unfocussed_attr = A_UNDERLINE;
344
345         field_opts_off(f, O_STATIC | O_WRAP | O_BLANK);
346         set_field_buffer(f, 0, str);
347         set_field_back(f, textbox->widget.unfocussed_attr);
348         set_field_userptr(f, &textbox->widget);
349
350         widgetset_add_field(set, textbox->widget.field);
351         talloc_set_destructor(textbox, textbox_destructor);
352
353         return textbox;
354 }
355
356 void widget_textbox_set_fixed_size(struct nc_widget_textbox *textbox)
357 {
358         field_opts_on(textbox->widget.field, O_STATIC);
359 }
360
361 void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox,
362                 long min, long max)
363 {
364         set_field_type(textbox->widget.field, TYPE_INTEGER, 1, min, max);
365 }
366
367 void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox)
368 {
369         set_field_type(textbox->widget.field, TYPE_IPV4);
370 }
371
372 static bool check_ipv4_multi_char(int c,
373                 const void *arg __attribute__((unused)))
374 {
375         return isdigit(c) || c == '.' || c == ' ';
376 }
377
378 static bool check_ipv4_multi_field(FIELD *field,
379                 const void *arg __attribute__((unused)))
380 {
381         char *buf = field_buffer(field, 0);
382         unsigned int ip[4];
383         int n, len;
384
385         while (*buf != '\0') {
386                 n = sscanf(buf, "%u.%u.%u.%u%n",
387                                 &ip[0], &ip[1], &ip[2], &ip[3], &len);
388                 if (n != 4)
389                         return false;
390
391                 if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
392                         return false;
393
394                 for (buf += len; *buf != '\0'; buf++) {
395                         if (isspace(*buf))
396                                 continue;
397                         else if (isdigit(*buf))
398                                 break;
399                         else
400                                 return false;
401                 }
402         }
403
404         return true;
405 }
406
407 void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox)
408 {
409         if (!textbox->set->ipv4_multi_type) {
410                 textbox->set->ipv4_multi_type = new_fieldtype(
411                                 check_ipv4_multi_field,
412                                 check_ipv4_multi_char);
413         }
414         set_field_type(textbox->widget.field, textbox->set->ipv4_multi_type);
415 }
416
417 static void select_option_change(struct select_option *opt, bool selected)
418 {
419         const char *str;
420
421         str = selected ? select_selected_str : select_unselected_str;
422
423         memcpy(opt->str, str, strlen(str));
424         set_field_buffer(opt->field, 0, opt->str);
425 }
426
427 static bool select_process_key(struct nc_widget *w, FORM *form, int key)
428 {
429         struct nc_widget_select *select = to_select(w);
430         struct select_option *new_opt, *old_opt;
431         int i, new_idx;
432         FIELD *field;
433
434         if (!key_is_select(key))
435                 return false;
436
437         field = current_field(form);
438         new_opt = NULL;
439
440         for (i = 0; i < select->n_options; i++) {
441                 if (select->options[i].field == field) {
442                         new_opt = &select->options[i];
443                         new_idx = i;
444                         break;
445                 }
446         }
447
448         if (!new_opt)
449                 return true;
450
451         if (new_idx == select->selected_option)
452                 return true;
453
454         old_opt = &select->options[select->selected_option];
455
456         select_option_change(old_opt, false);
457         select_option_change(new_opt, true);
458
459         select->selected_option = new_idx;
460
461         if (select->on_change)
462                 select->on_change(select->on_change_arg, new_opt->val);
463
464         return true;
465 }
466
467 static void select_set_visible(struct nc_widget *widget, bool visible)
468 {
469         struct nc_widget_select *select = to_select(widget);
470         int i;
471
472         for (i = 0; i < select->n_options; i++)
473                 field_set_visible(select->options[i].field, visible);
474 }
475
476 static void select_move(struct nc_widget *widget, int y, int x)
477 {
478         struct nc_widget_select *select = to_select(widget);
479         int i;
480
481         for (i = 0; i < select->n_options; i++)
482                 field_move(select->options[i].field, y + i, x);
483 }
484
485 static void select_field_focus(struct nc_widget *widget, FIELD *field)
486 {
487         struct nc_widget_select *select = to_select(widget);
488         int i;
489
490         for (i = 0; i < select->n_options; i++) {
491                 if (field != select->options[i].field)
492                         continue;
493                 widget->focus_y = i;
494                 return;
495         }
496 }
497
498 static int select_destructor(void *ptr)
499 {
500         struct nc_widget_select *select = ptr;
501         int i;
502
503         for (i = 0; i < select->n_options; i++)
504                 free_field(select->options[i].field);
505
506         return 0;
507 }
508
509 struct nc_widget_select *widget_new_select(struct nc_widgetset *set,
510                 int y, int x, int len)
511 {
512         struct nc_widget_select *select;
513
514         select = talloc_zero(set, struct nc_widget_select);
515         select->widget.width = len;
516         select->widget.height = 0;
517         select->widget.x = x;
518         select->widget.y = y;
519         select->widget.process_key = select_process_key;
520         select->widget.set_visible = select_set_visible;
521         select->widget.move = select_move;
522         select->widget.field_focus = select_field_focus;
523         select->widget.focussed_attr = A_REVERSE;
524         select->widget.unfocussed_attr = A_NORMAL;
525         select->top = y;
526         select->left = x;
527         select->size = len;
528         select->set = set;
529
530         talloc_set_destructor(select, select_destructor);
531
532         return select;
533 }
534
535 void widget_select_add_option(struct nc_widget_select *select, int value,
536                 const char *text, bool selected)
537 {
538         const char *str;
539         FIELD *f;
540         int i;
541
542         /* if we never see an option with selected set, we want the first
543          * one to be selected */
544         if (select->n_options == 0)
545                 selected = true;
546         else if (selected)
547                 select_option_change(&select->options[select->selected_option],
548                                         false);
549
550         if (selected) {
551                 select->selected_option = select->n_options;
552                 str = select_selected_str;
553         } else
554                 str = select_unselected_str;
555
556         i = select->n_options++;
557         select->widget.height = select->n_options;
558
559         select->options = talloc_realloc(select, select->options,
560                                 struct select_option, i + 2);
561         select->options[i].val = value;
562         select->options[i].str = talloc_asprintf(select->options,
563                                         "%s %s", str, text);
564
565         select->options[i].field = f = new_field(1, select->size,
566                                                 select->top + i,
567                                                 select->left, 0, 0);
568
569         field_opts_off(f, O_WRAP | O_EDIT);
570         set_field_userptr(f, &select->widget);
571         set_field_buffer(f, 0, select->options[i].str);
572
573         widgetset_add_field(select->set, f);
574 }
575
576 int widget_select_get_value(struct nc_widget_select *select)
577 {
578         if (!select->n_options)
579                 return -1;
580         return select->options[select->selected_option].val;
581 }
582
583 int widget_select_height(struct nc_widget_select *select)
584 {
585         return select->n_options;
586 }
587
588 void widget_select_on_change(struct nc_widget_select *select,
589                 void (*on_change)(void *, int), void *arg)
590 {
591         select->on_change = on_change;
592         select->on_change_arg = arg;
593 }
594
595 void widget_select_drop_options(struct nc_widget_select *select)
596 {
597         struct nc_widgetset *set = select->set;
598         int i;
599
600         for (i = 0; i < select->n_options; i++) {
601                 FIELD *field = select->options[i].field;
602                 widgetset_remove_field(set, field);
603                 if (field == set->cur_field)
604                         set->cur_field = NULL;
605                 free_field(select->options[i].field);
606         }
607
608         talloc_free(select->options);
609         select->options = NULL;
610         select->n_options = 0;
611         select->widget.height = 0;
612         select->widget.focus_y = 0;
613
614 }
615
616 static bool button_process_key(struct nc_widget *widget,
617                 FORM *form __attribute__((unused)), int key)
618 {
619         struct nc_widget_button *button = to_button(widget);
620
621         if (!button->click)
622                 return false;
623
624         if (!key_is_select(key))
625                 return false;
626
627         button->click(button->arg);
628         return true;
629 }
630
631 static int button_destructor(void *ptr)
632 {
633         struct nc_widget_button *button = ptr;
634         free_field(button->widget.field);
635         return 0;
636 }
637
638 struct nc_widget_button *widget_new_button(struct nc_widgetset *set,
639                 int y, int x, int size, const char *str,
640                 void (*click)(void *), void *arg)
641 {
642         struct nc_widget_button *button;
643         char *text;
644         FIELD *f;
645         int idx, len;
646
647         button = talloc_zero(set, struct nc_widget_button);
648         button->widget.height = 1;
649         button->widget.width = size;
650         button->widget.x = x;
651         button->widget.y = y;
652         button->widget.field = f = new_field(1, size + 2, y, x, 0, 0);
653         button->widget.process_key = button_process_key;
654         button->widget.focussed_attr = A_REVERSE;
655         button->widget.unfocussed_attr = A_NORMAL;
656         button->click = click;
657         button->arg = arg;
658
659         field_opts_off(f, O_EDIT);
660         set_field_userptr(f, &button->widget);
661
662         /* center str in a size-char buffer, but don't overrun */
663         len = strlen(str);
664         len = min(len, size);
665         idx = (size - len) / 2;
666
667         text = talloc_array(button, char, size + 3);
668         memset(text, ' ', size + 2);
669         memcpy(text + idx + 1, str, len);
670         text[0] = '[';
671         text[size + 1] = ']';
672         text[size + 2] = '\0';
673
674         set_field_buffer(f, 0, text);
675
676         widgetset_add_field(set, button->widget.field);
677         talloc_set_destructor(button, button_destructor);
678
679         return button;
680 }
681
682 static void widget_focus_change(struct nc_widget *widget, FIELD *field,
683                 bool focussed)
684 {
685         int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr;
686         set_field_back(field, attr);
687 }
688
689 bool widgetset_process_key(struct nc_widgetset *set, int key)
690 {
691         struct nc_widget *widget;
692         FIELD *field;
693         int req = 0;
694
695         field = current_field(set->form);
696         assert(field);
697
698         /* handle field change events */
699         switch (key) {
700         case KEY_BTAB:
701         case KEY_UP:
702                 req = REQ_PREV_FIELD;
703                 break;
704         case '\t':
705         case KEY_DOWN:
706                 req = REQ_NEXT_FIELD;
707                 break;
708         case KEY_PPAGE:
709                 req = REQ_FIRST_FIELD;
710                 break;
711         case KEY_NPAGE:
712                 req = REQ_LAST_FIELD;
713                 break;
714         }
715
716         widget = field_userptr(field);
717         if (req) {
718                 widget_focus_change(widget, field, false);
719                 form_driver(set->form, req);
720                 form_driver(set->form, REQ_END_FIELD);
721                 field = current_field(set->form);
722                 widget = field_userptr(field);
723                 widget_focus_change(widget, field, true);
724                 if (widget->field_focus)
725                         widget->field_focus(widget, field);
726                 if (set->widget_focus)
727                         set->widget_focus(widget, set->widget_focus_arg);
728                 return true;
729         }
730
731         if (!widget->process_key)
732                 return false;
733
734         return widget->process_key(widget, set->form, key);
735 }
736
737 static int widgetset_destructor(void *ptr)
738 {
739         struct nc_widgetset *set = ptr;
740         free_form(set->form);
741         if (set->ipv4_multi_type)
742                 free_fieldtype(set->ipv4_multi_type);
743         return 0;
744 }
745
746 struct nc_widgetset *widgetset_create(void *ctx, WINDOW *main, WINDOW *sub)
747 {
748         struct nc_widgetset *set;
749
750         set = talloc_zero(ctx, struct nc_widgetset);
751         set->n_alloc_fields = 8;
752         set->mainwin = main;
753         set->subwin = sub;
754         set->fields = talloc_array(set, FIELD *, set->n_alloc_fields);
755         talloc_set_destructor(set, widgetset_destructor);
756
757         return set;
758 }
759
760 void widgetset_set_windows(struct nc_widgetset *set,
761                 WINDOW *main, WINDOW *sub)
762 {
763         set->mainwin = main;
764         set->subwin = sub;
765 }
766
767 void widgetset_set_widget_focus(struct nc_widgetset *set,
768                 widget_focus_cb cb, void *arg)
769 {
770         set->widget_focus = cb;
771         set->widget_focus_arg = arg;
772 }
773
774 void widgetset_post(struct nc_widgetset *set)
775 {
776         struct nc_widget *widget;
777         FIELD *field;
778
779         set->form = new_form(set->fields);
780         set_form_win(set->form, set->mainwin);
781         set_form_sub(set->form, set->subwin);
782         post_form(set->form);
783         form_driver(set->form, REQ_END_FIELD);
784
785         if (set->cur_field) {
786                 set_current_field(set->form, set->cur_field);
787                 field = set->cur_field;
788         }
789
790         field = current_field(set->form);
791         widget = field_userptr(field);
792         widget_focus_change(widget, field, true);
793         if (set->widget_focus)
794                 set->widget_focus(widget, set->widget_focus_arg);
795 }
796
797 void widgetset_unpost(struct nc_widgetset *set)
798 {
799         set->cur_field = current_field(set->form);
800         unpost_form(set->form);
801         free_form(set->form);
802         set->form = NULL;
803 }
804
805 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field)
806 {
807         if (set->n_fields == set->n_alloc_fields - 1) {
808                 set->n_alloc_fields *= 2;
809                 set->fields = talloc_realloc(set, set->fields,
810                                 FIELD *, set->n_alloc_fields);
811         }
812
813         set->n_fields++;
814         set->fields[set->n_fields - 1] = field;
815         set->fields[set->n_fields] = NULL;
816 }
817
818 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field)
819 {
820         int i;
821
822         for (i = 0; i < set->n_fields; i++) {
823                 if (set->fields[i] == field)
824                         break;
825         }
826
827         if (i == set->n_fields)
828                 return;
829
830         memmove(&set->fields[i], &set->fields[i+i],
831                         (set->n_fields - i) * sizeof(set->fields[i]));
832         set->n_fields--;
833 }
834
835 #define DECLARE_BASEFN(type) \
836         struct nc_widget *widget_ ## type ## _base              \
837                 (struct nc_widget_ ## type *w)                  \
838         { return &w->widget; }
839
840 DECLARE_BASEFN(textbox);
841 DECLARE_BASEFN(checkbox);
842 DECLARE_BASEFN(select);
843 DECLARE_BASEFN(label);
844 DECLARE_BASEFN(button);
845
846 void widget_set_visible(struct nc_widget *widget, bool visible)
847 {
848         if (widget->set_visible)
849                 widget->set_visible(widget, visible);
850         else
851                 field_set_visible(widget->field, visible);
852 }
853
854 void widget_move(struct nc_widget *widget, int y, int x)
855 {
856         if (widget->move)
857                 widget->move(widget, y, x);
858         else
859                 field_move(widget->field, y, x);
860
861         widget->x = x;
862         widget->y = y;
863 }
864
865 int widget_height(struct nc_widget *widget)
866 {
867         return widget->height;
868 }
869
870 int widget_width(struct nc_widget *widget)
871 {
872         return widget->width;
873 }
874
875 int widget_x(struct nc_widget *widget)
876 {
877         return widget->x;
878 }
879
880 int widget_y(struct nc_widget *widget)
881 {
882         return widget->y;
883 }
884
885 int widget_focus_y(struct nc_widget *widget)
886 {
887         return widget->focus_y;
888 }
889