]> git.ozlabs.org Git - petitboot/blob - ui/ncurses/nc-widgets.c
ui/ncurses: Add help to system configuration screen
[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_validator_integer(struct nc_widget_textbox *textbox,
357                 long min, long max)
358 {
359         set_field_type(textbox->widget.field, TYPE_INTEGER, 1, min, max);
360 }
361
362 void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox)
363 {
364         set_field_type(textbox->widget.field, TYPE_IPV4);
365 }
366
367 static bool check_ipv4_multi_char(int c,
368                 const void *arg __attribute__((unused)))
369 {
370         return isdigit(c) || c == '.' || c == ' ';
371 }
372
373 static bool check_ipv4_multi_field(FIELD *field,
374                 const void *arg __attribute__((unused)))
375 {
376         char *buf = field_buffer(field, 0);
377         unsigned int ip[4];
378         int n, len;
379
380         while (*buf != '\0') {
381                 n = sscanf(buf, "%u.%u.%u.%u%n",
382                                 &ip[0], &ip[1], &ip[2], &ip[3], &len);
383                 if (n != 4)
384                         return false;
385
386                 if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
387                         return false;
388
389                 for (buf += len; *buf != '\0'; buf++) {
390                         if (isspace(*buf))
391                                 continue;
392                         else if (isdigit(*buf))
393                                 break;
394                         else
395                                 return false;
396                 }
397         }
398
399         return true;
400 }
401
402 void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox)
403 {
404         if (!textbox->set->ipv4_multi_type) {
405                 textbox->set->ipv4_multi_type = new_fieldtype(
406                                 check_ipv4_multi_field,
407                                 check_ipv4_multi_char);
408         }
409         set_field_type(textbox->widget.field, textbox->set->ipv4_multi_type);
410 }
411
412 static void select_option_change(struct select_option *opt, bool selected)
413 {
414         const char *str;
415
416         str = selected ? select_selected_str : select_unselected_str;
417
418         memcpy(opt->str, str, strlen(str));
419         set_field_buffer(opt->field, 0, opt->str);
420 }
421
422 static bool select_process_key(struct nc_widget *w, FORM *form, int key)
423 {
424         struct nc_widget_select *select = to_select(w);
425         struct select_option *new_opt, *old_opt;
426         int i, new_idx;
427         FIELD *field;
428
429         if (!key_is_select(key))
430                 return false;
431
432         field = current_field(form);
433         new_opt = NULL;
434
435         for (i = 0; i < select->n_options; i++) {
436                 if (select->options[i].field == field) {
437                         new_opt = &select->options[i];
438                         new_idx = i;
439                         break;
440                 }
441         }
442
443         if (!new_opt)
444                 return true;
445
446         if (new_idx == select->selected_option)
447                 return true;
448
449         old_opt = &select->options[select->selected_option];
450
451         select_option_change(old_opt, false);
452         select_option_change(new_opt, true);
453
454         select->selected_option = new_idx;
455
456         if (select->on_change)
457                 select->on_change(select->on_change_arg, new_opt->val);
458
459         return true;
460 }
461
462 static void select_set_visible(struct nc_widget *widget, bool visible)
463 {
464         struct nc_widget_select *select = to_select(widget);
465         int i;
466
467         for (i = 0; i < select->n_options; i++)
468                 field_set_visible(select->options[i].field, visible);
469 }
470
471 static void select_move(struct nc_widget *widget, int y, int x)
472 {
473         struct nc_widget_select *select = to_select(widget);
474         int i;
475
476         for (i = 0; i < select->n_options; i++)
477                 field_move(select->options[i].field, y + i, x);
478 }
479
480 static void select_field_focus(struct nc_widget *widget, FIELD *field)
481 {
482         struct nc_widget_select *select = to_select(widget);
483         int i;
484
485         for (i = 0; i < select->n_options; i++) {
486                 if (field != select->options[i].field)
487                         continue;
488                 widget->focus_y = i;
489                 return;
490         }
491 }
492
493 static int select_destructor(void *ptr)
494 {
495         struct nc_widget_select *select = ptr;
496         int i;
497
498         for (i = 0; i < select->n_options; i++)
499                 free_field(select->options[i].field);
500
501         return 0;
502 }
503
504 struct nc_widget_select *widget_new_select(struct nc_widgetset *set,
505                 int y, int x, int len)
506 {
507         struct nc_widget_select *select;
508
509         select = talloc_zero(set, struct nc_widget_select);
510         select->widget.width = len;
511         select->widget.height = 0;
512         select->widget.x = x;
513         select->widget.y = y;
514         select->widget.process_key = select_process_key;
515         select->widget.set_visible = select_set_visible;
516         select->widget.move = select_move;
517         select->widget.field_focus = select_field_focus;
518         select->widget.focussed_attr = A_REVERSE;
519         select->widget.unfocussed_attr = A_NORMAL;
520         select->top = y;
521         select->left = x;
522         select->size = len;
523         select->set = set;
524
525         talloc_set_destructor(select, select_destructor);
526
527         return select;
528 }
529
530 void widget_select_add_option(struct nc_widget_select *select, int value,
531                 const char *text, bool selected)
532 {
533         const char *str;
534         FIELD *f;
535         int i;
536
537         /* if we never see an option with selected set, we want the first
538          * one to be selected */
539         if (select->n_options == 0)
540                 selected = true;
541         else if (selected)
542                 select_option_change(&select->options[select->selected_option],
543                                         false);
544
545         if (selected) {
546                 select->selected_option = select->n_options;
547                 str = select_selected_str;
548         } else
549                 str = select_unselected_str;
550
551         i = select->n_options++;
552         select->widget.height = select->n_options;
553
554         select->options = talloc_realloc(select, select->options,
555                                 struct select_option, i + 2);
556         select->options[i].val = value;
557         select->options[i].str = talloc_asprintf(select->options,
558                                         "%s %s", str, text);
559
560         select->options[i].field = f = new_field(1, select->size,
561                                                 select->top + i,
562                                                 select->left, 0, 0);
563
564         field_opts_off(f, O_WRAP | O_EDIT);
565         set_field_userptr(f, &select->widget);
566         set_field_buffer(f, 0, select->options[i].str);
567
568         widgetset_add_field(select->set, f);
569 }
570
571 int widget_select_get_value(struct nc_widget_select *select)
572 {
573         if (!select->n_options)
574                 return -1;
575         return select->options[select->selected_option].val;
576 }
577
578 int widget_select_height(struct nc_widget_select *select)
579 {
580         return select->n_options;
581 }
582
583 void widget_select_on_change(struct nc_widget_select *select,
584                 void (*on_change)(void *, int), void *arg)
585 {
586         select->on_change = on_change;
587         select->on_change_arg = arg;
588 }
589
590 void widget_select_drop_options(struct nc_widget_select *select)
591 {
592         struct nc_widgetset *set = select->set;
593         int i;
594
595         for (i = 0; i < select->n_options; i++) {
596                 FIELD *field = select->options[i].field;
597                 widgetset_remove_field(set, field);
598                 if (field == set->cur_field)
599                         set->cur_field = NULL;
600                 free_field(select->options[i].field);
601         }
602
603         talloc_free(select->options);
604         select->options = NULL;
605         select->n_options = 0;
606         select->widget.height = 0;
607         select->widget.focus_y = 0;
608
609 }
610
611 static bool button_process_key(struct nc_widget *widget,
612                 FORM *form __attribute__((unused)), int key)
613 {
614         struct nc_widget_button *button = to_button(widget);
615
616         if (!button->click)
617                 return false;
618
619         if (!key_is_select(key))
620                 return false;
621
622         button->click(button->arg);
623         return true;
624 }
625
626 static int button_destructor(void *ptr)
627 {
628         struct nc_widget_button *button = ptr;
629         free_field(button->widget.field);
630         return 0;
631 }
632
633 struct nc_widget_button *widget_new_button(struct nc_widgetset *set,
634                 int y, int x, int size, const char *str,
635                 void (*click)(void *), void *arg)
636 {
637         struct nc_widget_button *button;
638         char *text;
639         FIELD *f;
640         int idx, len;
641
642         button = talloc_zero(set, struct nc_widget_button);
643         button->widget.height = 1;
644         button->widget.width = size;
645         button->widget.x = x;
646         button->widget.y = y;
647         button->widget.field = f = new_field(1, size + 2, y, x, 0, 0);
648         button->widget.process_key = button_process_key;
649         button->widget.focussed_attr = A_REVERSE;
650         button->widget.unfocussed_attr = A_NORMAL;
651         button->click = click;
652         button->arg = arg;
653
654         field_opts_off(f, O_EDIT);
655         set_field_userptr(f, &button->widget);
656
657         /* center str in a size-char buffer, but don't overrun */
658         len = strlen(str);
659         len = min(len, size);
660         idx = (size - len) / 2;
661
662         text = talloc_array(button, char, size + 3);
663         memset(text, ' ', size + 2);
664         memcpy(text + idx + 1, str, len);
665         text[0] = '[';
666         text[size + 1] = ']';
667         text[size + 2] = '\0';
668
669         set_field_buffer(f, 0, text);
670
671         widgetset_add_field(set, button->widget.field);
672         talloc_set_destructor(button, button_destructor);
673
674         return button;
675 }
676
677 static void widget_focus_change(struct nc_widget *widget, FIELD *field,
678                 bool focussed)
679 {
680         int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr;
681         set_field_back(field, attr);
682 }
683
684 bool widgetset_process_key(struct nc_widgetset *set, int key)
685 {
686         struct nc_widget *widget;
687         FIELD *field;
688         int req = 0;
689
690         field = current_field(set->form);
691         assert(field);
692
693         /* handle field change events */
694         switch (key) {
695         case KEY_BTAB:
696         case KEY_UP:
697                 req = REQ_PREV_FIELD;
698                 break;
699         case '\t':
700         case KEY_DOWN:
701                 req = REQ_NEXT_FIELD;
702                 break;
703         case KEY_PPAGE:
704                 req = REQ_FIRST_FIELD;
705                 break;
706         case KEY_NPAGE:
707                 req = REQ_LAST_FIELD;
708                 break;
709         }
710
711         widget = field_userptr(field);
712         if (req) {
713                 widget_focus_change(widget, field, false);
714                 form_driver(set->form, req);
715                 form_driver(set->form, REQ_END_FIELD);
716                 field = current_field(set->form);
717                 widget = field_userptr(field);
718                 widget_focus_change(widget, field, true);
719                 if (widget->field_focus)
720                         widget->field_focus(widget, field);
721                 if (set->widget_focus)
722                         set->widget_focus(widget, set->widget_focus_arg);
723                 return true;
724         }
725
726         if (!widget->process_key)
727                 return false;
728
729         return widget->process_key(widget, set->form, key);
730 }
731
732 static int widgetset_destructor(void *ptr)
733 {
734         struct nc_widgetset *set = ptr;
735         free_form(set->form);
736         if (set->ipv4_multi_type)
737                 free_fieldtype(set->ipv4_multi_type);
738         return 0;
739 }
740
741 struct nc_widgetset *widgetset_create(void *ctx, WINDOW *main, WINDOW *sub)
742 {
743         struct nc_widgetset *set;
744
745         set = talloc_zero(ctx, struct nc_widgetset);
746         set->n_alloc_fields = 8;
747         set->mainwin = main;
748         set->subwin = sub;
749         set->fields = talloc_array(set, FIELD *, set->n_alloc_fields);
750         talloc_set_destructor(set, widgetset_destructor);
751
752         return set;
753 }
754
755 void widgetset_set_windows(struct nc_widgetset *set,
756                 WINDOW *main, WINDOW *sub)
757 {
758         set->mainwin = main;
759         set->subwin = sub;
760 }
761
762 void widgetset_set_widget_focus(struct nc_widgetset *set,
763                 widget_focus_cb cb, void *arg)
764 {
765         set->widget_focus = cb;
766         set->widget_focus_arg = arg;
767 }
768
769 void widgetset_post(struct nc_widgetset *set)
770 {
771         struct nc_widget *widget;
772         FIELD *field;
773
774         set->form = new_form(set->fields);
775         set_form_win(set->form, set->mainwin);
776         set_form_sub(set->form, set->subwin);
777         post_form(set->form);
778         form_driver(set->form, REQ_END_FIELD);
779
780         if (set->cur_field) {
781                 set_current_field(set->form, set->cur_field);
782                 field = set->cur_field;
783         }
784
785         field = current_field(set->form);
786         widget = field_userptr(field);
787         widget_focus_change(widget, field, true);
788         if (set->widget_focus)
789                 set->widget_focus(widget, set->widget_focus_arg);
790 }
791
792 void widgetset_unpost(struct nc_widgetset *set)
793 {
794         set->cur_field = current_field(set->form);
795         unpost_form(set->form);
796         free_form(set->form);
797         set->form = NULL;
798 }
799
800 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field)
801 {
802         if (set->n_fields == set->n_alloc_fields - 1) {
803                 set->n_alloc_fields *= 2;
804                 set->fields = talloc_realloc(set, set->fields,
805                                 FIELD *, set->n_alloc_fields);
806         }
807
808         set->n_fields++;
809         set->fields[set->n_fields - 1] = field;
810         set->fields[set->n_fields] = NULL;
811 }
812
813 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field)
814 {
815         int i;
816
817         for (i = 0; i < set->n_fields; i++) {
818                 if (set->fields[i] == field)
819                         break;
820         }
821
822         if (i == set->n_fields)
823                 return;
824
825         memmove(&set->fields[i], &set->fields[i+i],
826                         (set->n_fields - i) * sizeof(set->fields[i]));
827         set->n_fields--;
828 }
829
830 #define DECLARE_BASEFN(type) \
831         struct nc_widget *widget_ ## type ## _base              \
832                 (struct nc_widget_ ## type *w)                  \
833         { return &w->widget; }
834
835 DECLARE_BASEFN(textbox);
836 DECLARE_BASEFN(checkbox);
837 DECLARE_BASEFN(select);
838 DECLARE_BASEFN(label);
839 DECLARE_BASEFN(button);
840
841 void widget_set_visible(struct nc_widget *widget, bool visible)
842 {
843         if (widget->set_visible)
844                 widget->set_visible(widget, visible);
845         else
846                 field_set_visible(widget->field, visible);
847 }
848
849 void widget_move(struct nc_widget *widget, int y, int x)
850 {
851         if (widget->move)
852                 widget->move(widget, y, x);
853         else
854                 field_move(widget->field, y, x);
855
856         widget->x = x;
857         widget->y = y;
858 }
859
860 int widget_height(struct nc_widget *widget)
861 {
862         return widget->height;
863 }
864
865 int widget_width(struct nc_widget *widget)
866 {
867         return widget->width;
868 }
869
870 int widget_x(struct nc_widget *widget)
871 {
872         return widget->x;
873 }
874
875 int widget_y(struct nc_widget *widget)
876 {
877         return widget->y;
878 }
879
880 int widget_focus_y(struct nc_widget *widget)
881 {
882         return widget->focus_y;
883 }
884