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_validator_integer(struct nc_widget_textbox *textbox,
359 set_field_type(textbox->widget.field, TYPE_INTEGER, 1, min, max);
362 void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox)
364 set_field_type(textbox->widget.field, TYPE_IPV4);
367 static bool check_ipv4_multi_char(int c,
368 const void *arg __attribute__((unused)))
370 return isdigit(c) || c == '.' || c == ' ';
373 static bool check_ipv4_multi_field(FIELD *field,
374 const void *arg __attribute__((unused)))
376 char *buf = field_buffer(field, 0);
380 while (*buf != '\0') {
381 n = sscanf(buf, "%u.%u.%u.%u%n",
382 &ip[0], &ip[1], &ip[2], &ip[3], &len);
386 if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255)
389 for (buf += len; *buf != '\0'; buf++) {
392 else if (isdigit(*buf))
402 void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox)
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);
409 set_field_type(textbox->widget.field, textbox->set->ipv4_multi_type);
412 static void select_option_change(struct select_option *opt, bool selected)
416 str = selected ? select_selected_str : select_unselected_str;
418 memcpy(opt->str, str, strlen(str));
419 set_field_buffer(opt->field, 0, opt->str);
422 static bool select_process_key(struct nc_widget *w, FORM *form, int key)
424 struct nc_widget_select *select = to_select(w);
425 struct select_option *new_opt, *old_opt;
429 if (!key_is_select(key))
432 field = current_field(form);
435 for (i = 0; i < select->n_options; i++) {
436 if (select->options[i].field == field) {
437 new_opt = &select->options[i];
446 if (new_idx == select->selected_option)
449 old_opt = &select->options[select->selected_option];
451 select_option_change(old_opt, false);
452 select_option_change(new_opt, true);
454 select->selected_option = new_idx;
456 if (select->on_change)
457 select->on_change(select->on_change_arg, new_opt->val);
462 static void select_set_visible(struct nc_widget *widget, bool visible)
464 struct nc_widget_select *select = to_select(widget);
467 for (i = 0; i < select->n_options; i++)
468 field_set_visible(select->options[i].field, visible);
471 static void select_move(struct nc_widget *widget, int y, int x)
473 struct nc_widget_select *select = to_select(widget);
476 for (i = 0; i < select->n_options; i++)
477 field_move(select->options[i].field, y + i, x);
480 static void select_field_focus(struct nc_widget *widget, FIELD *field)
482 struct nc_widget_select *select = to_select(widget);
485 for (i = 0; i < select->n_options; i++) {
486 if (field != select->options[i].field)
493 static int select_destructor(void *ptr)
495 struct nc_widget_select *select = ptr;
498 for (i = 0; i < select->n_options; i++)
499 free_field(select->options[i].field);
504 struct nc_widget_select *widget_new_select(struct nc_widgetset *set,
505 int y, int x, int len)
507 struct nc_widget_select *select;
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;
525 talloc_set_destructor(select, select_destructor);
530 void widget_select_add_option(struct nc_widget_select *select, int value,
531 const char *text, bool selected)
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)
542 select_option_change(&select->options[select->selected_option],
546 select->selected_option = select->n_options;
547 str = select_selected_str;
549 str = select_unselected_str;
551 i = select->n_options++;
552 select->widget.height = select->n_options;
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,
560 select->options[i].field = f = new_field(1, select->size,
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);
568 widgetset_add_field(select->set, f);
571 int widget_select_get_value(struct nc_widget_select *select)
573 if (!select->n_options)
575 return select->options[select->selected_option].val;
578 int widget_select_height(struct nc_widget_select *select)
580 return select->n_options;
583 void widget_select_on_change(struct nc_widget_select *select,
584 void (*on_change)(void *, int), void *arg)
586 select->on_change = on_change;
587 select->on_change_arg = arg;
590 void widget_select_drop_options(struct nc_widget_select *select)
592 struct nc_widgetset *set = select->set;
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);
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;
611 static bool button_process_key(struct nc_widget *widget,
612 FORM *form __attribute__((unused)), int key)
614 struct nc_widget_button *button = to_button(widget);
619 if (!key_is_select(key))
622 button->click(button->arg);
626 static int button_destructor(void *ptr)
628 struct nc_widget_button *button = ptr;
629 free_field(button->widget.field);
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)
637 struct nc_widget_button *button;
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;
654 field_opts_off(f, O_EDIT);
655 set_field_userptr(f, &button->widget);
657 /* center str in a size-char buffer, but don't overrun */
659 len = min(len, size);
660 idx = (size - len) / 2;
662 text = talloc_array(button, char, size + 3);
663 memset(text, ' ', size + 2);
664 memcpy(text + idx + 1, str, len);
666 text[size + 1] = ']';
667 text[size + 2] = '\0';
669 set_field_buffer(f, 0, text);
671 widgetset_add_field(set, button->widget.field);
672 talloc_set_destructor(button, button_destructor);
677 static void widget_focus_change(struct nc_widget *widget, FIELD *field,
680 int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr;
681 set_field_back(field, attr);
684 bool widgetset_process_key(struct nc_widgetset *set, int key)
686 struct nc_widget *widget;
690 field = current_field(set->form);
693 /* handle field change events */
697 req = REQ_PREV_FIELD;
701 req = REQ_NEXT_FIELD;
704 req = REQ_FIRST_FIELD;
707 req = REQ_LAST_FIELD;
711 widget = field_userptr(field);
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);
726 if (!widget->process_key)
729 return widget->process_key(widget, set->form, key);
732 static int widgetset_destructor(void *ptr)
734 struct nc_widgetset *set = ptr;
735 free_form(set->form);
736 if (set->ipv4_multi_type)
737 free_fieldtype(set->ipv4_multi_type);
741 struct nc_widgetset *widgetset_create(void *ctx, WINDOW *main, WINDOW *sub)
743 struct nc_widgetset *set;
745 set = talloc_zero(ctx, struct nc_widgetset);
746 set->n_alloc_fields = 8;
749 set->fields = talloc_array(set, FIELD *, set->n_alloc_fields);
750 talloc_set_destructor(set, widgetset_destructor);
755 void widgetset_set_windows(struct nc_widgetset *set,
756 WINDOW *main, WINDOW *sub)
762 void widgetset_set_widget_focus(struct nc_widgetset *set,
763 widget_focus_cb cb, void *arg)
765 set->widget_focus = cb;
766 set->widget_focus_arg = arg;
769 void widgetset_post(struct nc_widgetset *set)
771 struct nc_widget *widget;
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);
780 if (set->cur_field) {
781 set_current_field(set->form, set->cur_field);
782 field = set->cur_field;
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);
792 void widgetset_unpost(struct nc_widgetset *set)
794 set->cur_field = current_field(set->form);
795 unpost_form(set->form);
796 free_form(set->form);
800 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field)
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);
809 set->fields[set->n_fields - 1] = field;
810 set->fields[set->n_fields] = NULL;
813 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field)
817 for (i = 0; i < set->n_fields; i++) {
818 if (set->fields[i] == field)
822 if (i == set->n_fields)
825 memmove(&set->fields[i], &set->fields[i+i],
826 (set->n_fields - i) * sizeof(set->fields[i]));
830 #define DECLARE_BASEFN(type) \
831 struct nc_widget *widget_ ## type ## _base \
832 (struct nc_widget_ ## type *w) \
833 { return &w->widget; }
835 DECLARE_BASEFN(textbox);
836 DECLARE_BASEFN(checkbox);
837 DECLARE_BASEFN(select);
838 DECLARE_BASEFN(label);
839 DECLARE_BASEFN(button);
841 void widget_set_visible(struct nc_widget *widget, bool visible)
843 if (widget->set_visible)
844 widget->set_visible(widget, visible);
846 field_set_visible(widget->field, visible);
849 void widget_move(struct nc_widget *widget, int y, int x)
852 widget->move(widget, y, x);
854 field_move(widget->field, y, x);
860 int widget_height(struct nc_widget *widget)
862 return widget->height;
865 int widget_width(struct nc_widget *widget)
867 return widget->width;
870 int widget_x(struct nc_widget *widget)
875 int widget_y(struct nc_widget *widget)
880 int widget_focus_y(struct nc_widget *widget)
882 return widget->focus_y;