2 * Copyright (C) 2013 IBM Corporation
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.
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.
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
18 #if defined(HAVE_CONFIG_H)
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
31 #elif defined HAVE_CURSES_H
34 # error "Curses header file not found."
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
44 # error "Curses form.h not found."
50 #include <talloc/talloc.h>
51 #include <types/types.h>
53 #include <util/util.h>
56 #include "nc-widgets.h"
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)
65 static const char *checkbox_checked_str = "[*]";
66 static const char *checkbox_unchecked_str = "[ ]";
68 static const char *select_selected_str = "(*)";
69 static const char *select_unselected_str = "( )";
76 int n_fields, n_alloc_fields;
77 void (*widget_focus)(struct nc_widget *, void *);
78 void *widget_focus_arg;
81 /* custom validators */
82 FIELDTYPE *ipv4_multi_type;
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 *);
100 struct nc_widget_label {
101 struct nc_widget widget;
105 struct nc_widget_checkbox {
106 struct nc_widget widget;
110 struct nc_widget_textbox {
111 struct nc_widgetset *set;
112 struct nc_widget widget;
115 struct nc_widget_select {
116 struct nc_widget widget;
117 struct select_option {
123 int n_options, selected_option;
124 struct nc_widgetset *set;
125 void (*on_change)(void *, int);
129 struct nc_widget_button {
130 struct nc_widget widget;
131 void (*click)(void *arg);
135 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field);
136 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field);
138 static bool key_is_select(int key)
140 return key == ' ' || key == '\r' || key == '\n' || key == KEY_ENTER;
143 static bool process_key_nop(struct nc_widget *widget __attribute__((unused)),
144 FORM *form __attribute((unused)),
145 int key __attribute__((unused)))
150 static void field_set_visible(FIELD *field, bool visible)
152 int opts = field_opts(field) & ~O_VISIBLE;
155 set_field_opts(field, opts);
158 static void field_move(FIELD *field, int y, int x)
160 move_field(field, y, x);
163 static int label_destructor(void *ptr)
165 struct nc_widget_label *label = ptr;
166 free_field(label->widget.field);
171 struct nc_widget_label *widget_new_label(struct nc_widgetset *set,
172 int y, int x, char *str)
174 struct nc_widget_label *label;
180 label = talloc_zero(set, struct nc_widget_label);
181 label->widget.height = 1;
182 label->widget.width = len;
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;
190 field_opts_off(f, O_ACTIVE);
191 set_field_buffer(f, 0, str);
192 set_field_userptr(f, &label->widget);
194 widgetset_add_field(set, label->widget.field);
195 talloc_set_destructor(label, label_destructor);
200 bool widget_checkbox_get_value(struct nc_widget_checkbox *checkbox)
202 return checkbox->checked;
205 static void checkbox_set_buffer(struct nc_widget_checkbox *checkbox)
208 str = checkbox->checked ? checkbox_checked_str : checkbox_unchecked_str;
209 set_field_buffer(checkbox->widget.field, 0, str);
212 static bool checkbox_process_key(struct nc_widget *widget,
213 FORM *form __attribute__((unused)), int key)
215 struct nc_widget_checkbox *checkbox = to_checkbox(widget);
217 if (!key_is_select(key))
220 checkbox->checked = !checkbox->checked;
221 checkbox_set_buffer(checkbox);
226 static int checkbox_destructor(void *ptr)
228 struct nc_widget_checkbox *checkbox = ptr;
229 free_field(checkbox->widget.field);
233 struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set,
234 int y, int x, bool checked)
236 struct nc_widget_checkbox *checkbox;
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),
251 field_opts_off(f, O_EDIT);
252 set_field_userptr(f, &checkbox->widget);
253 checkbox_set_buffer(checkbox);
255 widgetset_add_field(set, checkbox->widget.field);
256 talloc_set_destructor(checkbox, checkbox_destructor);
261 static char *strip_string(char *str)
267 /* clear trailing space */
268 for (i = len - 1; i >= 0; i--) {
269 if (!isspace(str[i]))
274 /* increment str past leading space */
275 for (i = 0; i < len; i++) {
276 if (str[i] == '\0' || !isspace(str[i]))
283 char *widget_textbox_get_value(struct nc_widget_textbox *textbox)
285 char *str = field_buffer(textbox->widget.field, 0);
286 return str ? strip_string(str) : NULL;
289 static bool textbox_process_key(
290 struct nc_widget *widget __attribute__((unused)),
295 form_driver(form, REQ_BEG_FIELD);
298 form_driver(form, REQ_END_FIELD);
301 form_driver(form, REQ_LEFT_CHAR);
304 form_driver(form, REQ_RIGHT_CHAR);
307 if (form_driver(form, REQ_LEFT_CHAR) != E_OK)
311 form_driver(form, REQ_DEL_CHAR);
314 form_driver(form, key);
321 static int textbox_destructor(void *ptr)
323 struct nc_widget_textbox *textbox = ptr;
324 free_field(textbox->widget.field);
328 struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
329 int y, int x, int len, char *str)
331 struct nc_widget_textbox *textbox;
334 textbox = talloc_zero(set, struct nc_widget_textbox);
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;
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);
350 widgetset_add_field(set, textbox->widget.field);
351 talloc_set_destructor(textbox, textbox_destructor);
356 void widget_textbox_set_fixed_size(struct nc_widget_textbox *textbox)
358 field_opts_on(textbox->widget.field, O_STATIC);
361 void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox,
364 set_field_type(textbox->widget.field, TYPE_INTEGER, 1, min, max);
367 void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox)
369 set_field_type(textbox->widget.field, TYPE_IPV4);
372 static bool check_ipv4_multi_char(int c,
373 const void *arg __attribute__((unused)))
375 return isdigit(c) || c == '.' || c == ' ';
378 static bool check_ipv4_multi_field(FIELD *field,
379 const void *arg __attribute__((unused)))
381 char *buf = field_buffer(field, 0);
385 while (*buf != '\0') {
386 n = sscanf(buf, "%u.%u.%u.%u%n",
387 &ip[0], &ip[1], &ip[2], &ip[3], &len);
391 if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
394 for (buf += len; *buf != '\0'; buf++) {
397 else if (isdigit(*buf))
407 void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox)
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);
414 set_field_type(textbox->widget.field, textbox->set->ipv4_multi_type);
417 static void select_option_change(struct select_option *opt, bool selected)
421 str = selected ? select_selected_str : select_unselected_str;
423 memcpy(opt->str, str, strlen(str));
424 set_field_buffer(opt->field, 0, opt->str);
427 static bool select_process_key(struct nc_widget *w, FORM *form, int key)
429 struct nc_widget_select *select = to_select(w);
430 struct select_option *new_opt, *old_opt;
434 if (!key_is_select(key))
437 field = current_field(form);
440 for (i = 0; i < select->n_options; i++) {
441 if (select->options[i].field == field) {
442 new_opt = &select->options[i];
451 if (new_idx == select->selected_option)
454 old_opt = &select->options[select->selected_option];
456 select_option_change(old_opt, false);
457 select_option_change(new_opt, true);
459 select->selected_option = new_idx;
461 if (select->on_change)
462 select->on_change(select->on_change_arg, new_opt->val);
467 static void select_set_visible(struct nc_widget *widget, bool visible)
469 struct nc_widget_select *select = to_select(widget);
472 for (i = 0; i < select->n_options; i++)
473 field_set_visible(select->options[i].field, visible);
476 static void select_move(struct nc_widget *widget, int y, int x)
478 struct nc_widget_select *select = to_select(widget);
481 for (i = 0; i < select->n_options; i++)
482 field_move(select->options[i].field, y + i, x);
485 static void select_field_focus(struct nc_widget *widget, FIELD *field)
487 struct nc_widget_select *select = to_select(widget);
490 for (i = 0; i < select->n_options; i++) {
491 if (field != select->options[i].field)
498 static int select_destructor(void *ptr)
500 struct nc_widget_select *select = ptr;
503 for (i = 0; i < select->n_options; i++)
504 free_field(select->options[i].field);
509 struct nc_widget_select *widget_new_select(struct nc_widgetset *set,
510 int y, int x, int len)
512 struct nc_widget_select *select;
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;
530 talloc_set_destructor(select, select_destructor);
535 void widget_select_add_option(struct nc_widget_select *select, int value,
536 const char *text, bool selected)
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)
547 select_option_change(&select->options[select->selected_option],
551 select->selected_option = select->n_options;
552 str = select_selected_str;
554 str = select_unselected_str;
556 i = select->n_options++;
557 select->widget.height = select->n_options;
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,
565 select->options[i].field = f = new_field(1, select->size,
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);
573 widgetset_add_field(select->set, f);
576 int widget_select_get_value(struct nc_widget_select *select)
578 if (!select->n_options)
580 return select->options[select->selected_option].val;
583 int widget_select_height(struct nc_widget_select *select)
585 return select->n_options;
588 void widget_select_on_change(struct nc_widget_select *select,
589 void (*on_change)(void *, int), void *arg)
591 select->on_change = on_change;
592 select->on_change_arg = arg;
595 void widget_select_drop_options(struct nc_widget_select *select)
597 struct nc_widgetset *set = select->set;
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);
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;
616 static bool button_process_key(struct nc_widget *widget,
617 FORM *form __attribute__((unused)), int key)
619 struct nc_widget_button *button = to_button(widget);
624 if (!key_is_select(key))
627 button->click(button->arg);
631 static int button_destructor(void *ptr)
633 struct nc_widget_button *button = ptr;
634 free_field(button->widget.field);
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)
642 struct nc_widget_button *button;
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;
659 field_opts_off(f, O_EDIT);
660 set_field_userptr(f, &button->widget);
662 /* center str in a size-char buffer, but don't overrun */
664 len = min(len, size);
665 idx = (size - len) / 2;
667 text = talloc_array(button, char, size + 3);
668 memset(text, ' ', size + 2);
669 memcpy(text + idx + 1, str, len);
671 text[size + 1] = ']';
672 text[size + 2] = '\0';
674 set_field_buffer(f, 0, text);
676 widgetset_add_field(set, button->widget.field);
677 talloc_set_destructor(button, button_destructor);
682 static void widget_focus_change(struct nc_widget *widget, FIELD *field,
685 int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr;
686 set_field_back(field, attr);
689 bool widgetset_process_key(struct nc_widgetset *set, int key)
691 struct nc_widget *widget;
695 field = current_field(set->form);
698 /* handle field change events */
702 req = REQ_PREV_FIELD;
706 req = REQ_NEXT_FIELD;
709 req = REQ_FIRST_FIELD;
712 req = REQ_LAST_FIELD;
716 widget = field_userptr(field);
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);
731 if (!widget->process_key)
734 return widget->process_key(widget, set->form, key);
737 static int widgetset_destructor(void *ptr)
739 struct nc_widgetset *set = ptr;
740 free_form(set->form);
741 if (set->ipv4_multi_type)
742 free_fieldtype(set->ipv4_multi_type);
746 struct nc_widgetset *widgetset_create(void *ctx, WINDOW *main, WINDOW *sub)
748 struct nc_widgetset *set;
750 set = talloc_zero(ctx, struct nc_widgetset);
751 set->n_alloc_fields = 8;
754 set->fields = talloc_array(set, FIELD *, set->n_alloc_fields);
755 talloc_set_destructor(set, widgetset_destructor);
760 void widgetset_set_windows(struct nc_widgetset *set,
761 WINDOW *main, WINDOW *sub)
767 void widgetset_set_widget_focus(struct nc_widgetset *set,
768 widget_focus_cb cb, void *arg)
770 set->widget_focus = cb;
771 set->widget_focus_arg = arg;
774 void widgetset_post(struct nc_widgetset *set)
776 struct nc_widget *widget;
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);
785 if (set->cur_field) {
786 set_current_field(set->form, set->cur_field);
787 field = set->cur_field;
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);
797 void widgetset_unpost(struct nc_widgetset *set)
799 set->cur_field = current_field(set->form);
800 unpost_form(set->form);
801 free_form(set->form);
805 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field)
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);
814 set->fields[set->n_fields - 1] = field;
815 set->fields[set->n_fields] = NULL;
818 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field)
822 for (i = 0; i < set->n_fields; i++) {
823 if (set->fields[i] == field)
827 if (i == set->n_fields)
830 memmove(&set->fields[i], &set->fields[i+i],
831 (set->n_fields - i) * sizeof(set->fields[i]));
835 #define DECLARE_BASEFN(type) \
836 struct nc_widget *widget_ ## type ## _base \
837 (struct nc_widget_ ## type *w) \
838 { return &w->widget; }
840 DECLARE_BASEFN(textbox);
841 DECLARE_BASEFN(checkbox);
842 DECLARE_BASEFN(select);
843 DECLARE_BASEFN(label);
844 DECLARE_BASEFN(button);
846 void widget_set_visible(struct nc_widget *widget, bool visible)
848 if (widget->set_visible)
849 widget->set_visible(widget, visible);
851 field_set_visible(widget->field, visible);
854 void widget_move(struct nc_widget *widget, int y, int x)
857 widget->move(widget, y, x);
859 field_move(widget->field, y, x);
865 int widget_height(struct nc_widget *widget)
867 return widget->height;
870 int widget_width(struct nc_widget *widget)
872 return widget->width;
875 int widget_x(struct nc_widget *widget)
880 int widget_y(struct nc_widget *widget)
885 int widget_focus_y(struct nc_widget *widget)
887 return widget->focus_y;