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."
47 #include <arpa/inet.h>
52 #include <talloc/talloc.h>
53 #include <types/types.h>
55 #include <util/util.h>
56 #include <i18n/i18n.h>
57 #include <fold/fold.h>
61 #include "nc-widgets.h"
65 #define to_checkbox(w) container_of(w, struct nc_widget_checkbox, widget)
66 #define to_textbox(w) container_of(w, struct nc_widget_textbox, widget)
67 #define to_button(w) container_of(w, struct nc_widget_button, widget)
68 #define to_select(w) container_of(w, struct nc_widget_select, widget)
69 #define to_subset(w) container_of(w, struct nc_widget_subset, widget)
71 static const char *checkbox_checked_str = "[*]";
72 static const char *checkbox_unchecked_str = "[ ]";
74 static const char *select_selected_str = "(*)";
75 static const char *select_unselected_str = "( )";
82 int n_fields, n_alloc_fields;
83 void (*widget_focus)(struct nc_widget *, void *);
84 void *widget_focus_arg;
87 /* custom validators */
88 FIELDTYPE *ip_multi_type;
95 bool (*process_key)(struct nc_widget *, FORM *, int);
96 void (*set_visible)(struct nc_widget *, bool);
97 void (*move)(struct nc_widget *, int, int);
98 void (*field_focus)(struct nc_widget *, FIELD *);
108 struct nc_widget_label {
109 struct nc_widget widget;
113 struct nc_widget_checkbox {
114 struct nc_widget widget;
118 struct nc_widget_textbox {
119 struct nc_widgetset *set;
120 struct nc_widget widget;
123 struct nc_widget_subset {
124 struct nc_widget widget;
127 struct subset_option {
134 struct nc_widgetset *set;
135 void (*on_change)(void *, int);
137 void (*screen_cb)(void *,
138 struct nc_widget_subset *, int);
141 struct nc_widget_select {
142 struct nc_widget widget;
143 struct select_option {
150 int n_options, selected_option;
151 struct nc_widgetset *set;
152 void (*on_change)(void *, int);
156 struct nc_widget_button {
157 struct nc_widget widget;
158 void (*click)(void *arg);
162 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field);
163 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field);
165 static bool key_is_select(int key)
167 return key == ' ' || key == '\r' || key == '\n' || key == KEY_ENTER;
170 static bool process_key_nop(struct nc_widget *widget __attribute__((unused)),
171 FORM *form __attribute((unused)),
172 int key __attribute__((unused)))
177 static void field_set_visible(FIELD *field, bool visible)
179 int opts = field_opts(field) & ~O_VISIBLE;
182 set_field_opts(field, opts);
185 static bool field_visible(FIELD *field)
187 return (field_opts(field) & O_VISIBLE) == O_VISIBLE;
190 static void field_move(FIELD *field, int y, int x)
192 move_field(field, y, x);
195 static int label_destructor(void *ptr)
197 struct nc_widget_label *label = ptr;
198 free_field(label->widget.field);
203 struct nc_widget_label *widget_new_label(struct nc_widgetset *set,
204 int y, int x, char *str)
206 struct nc_widget_label *label;
212 label = talloc_zero(set, struct nc_widget_label);
213 label->widget.height = 1;
214 label->widget.width = len;
217 label->widget.process_key = process_key_nop;
218 label->widget.field = f = new_field(1, len, y, x, 0, 0);
219 label->widget.focussed_attr = A_NORMAL;
220 label->widget.unfocussed_attr = A_NORMAL;
222 field_opts_off(f, O_ACTIVE);
223 set_field_buffer(f, 0, str);
224 set_field_userptr(f, &label->widget);
226 widgetset_add_field(set, label->widget.field);
227 talloc_set_destructor(label, label_destructor);
232 bool widget_checkbox_get_value(struct nc_widget_checkbox *checkbox)
234 return checkbox->checked;
237 static void checkbox_set_buffer(struct nc_widget_checkbox *checkbox)
240 str = checkbox->checked ? checkbox_checked_str : checkbox_unchecked_str;
241 set_field_buffer(checkbox->widget.field, 0, str);
244 static bool checkbox_process_key(struct nc_widget *widget,
245 FORM *form __attribute__((unused)), int key)
247 struct nc_widget_checkbox *checkbox = to_checkbox(widget);
249 if (!key_is_select(key))
252 checkbox->checked = !checkbox->checked;
253 checkbox_set_buffer(checkbox);
258 static int checkbox_destructor(void *ptr)
260 struct nc_widget_checkbox *checkbox = ptr;
261 free_field(checkbox->widget.field);
265 struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set,
266 int y, int x, bool checked)
268 struct nc_widget_checkbox *checkbox;
271 checkbox = talloc_zero(set, struct nc_widget_checkbox);
272 checkbox->checked = checked;
273 checkbox->widget.height = 1;
274 checkbox->widget.width = strlen(checkbox_checked_str);
275 checkbox->widget.x = x;
276 checkbox->widget.y = y;
277 checkbox->widget.process_key = checkbox_process_key;
278 checkbox->widget.focussed_attr = A_REVERSE;
279 checkbox->widget.unfocussed_attr = A_NORMAL;
280 checkbox->widget.field = f = new_field(1, strlen(checkbox_checked_str),
283 field_opts_off(f, O_EDIT);
284 set_field_userptr(f, &checkbox->widget);
285 checkbox_set_buffer(checkbox);
287 widgetset_add_field(set, checkbox->widget.field);
288 talloc_set_destructor(checkbox, checkbox_destructor);
293 static char *strip_string(char *str)
299 /* clear trailing space */
300 for (i = len - 1; i >= 0; i--) {
301 if (!isspace(str[i]))
306 /* increment str past leading space */
307 for (i = 0; i < len; i++) {
308 if (str[i] == '\0' || !isspace(str[i]))
315 char *widget_textbox_get_value(struct nc_widget_textbox *textbox)
317 char *str = field_buffer(textbox->widget.field, 0);
318 return str ? strip_string(str) : NULL;
321 static bool textbox_process_key(
322 struct nc_widget *widget __attribute__((unused)),
327 form_driver(form, REQ_BEG_FIELD);
330 form_driver(form, REQ_END_FIELD);
333 form_driver(form, REQ_LEFT_CHAR);
336 form_driver(form, REQ_RIGHT_CHAR);
339 if (form_driver(form, REQ_LEFT_CHAR) != E_OK)
343 form_driver(form, REQ_DEL_CHAR);
351 /* Don't catch navigational keys */
354 form_driver(form, key);
361 static int textbox_destructor(void *ptr)
363 struct nc_widget_textbox *textbox = ptr;
364 free_field(textbox->widget.field);
368 struct nc_widget_textbox *widget_new_textbox_hidden(struct nc_widgetset *set,
369 int y, int x, int len, char *str, bool hide_input)
371 struct nc_widget_textbox *textbox;
374 textbox = talloc_zero(set, struct nc_widget_textbox);
376 textbox->widget.height = 1;
377 textbox->widget.width = len;
378 textbox->widget.x = x;
379 textbox->widget.y = y;
380 textbox->widget.process_key = textbox_process_key;
381 textbox->widget.field = f = new_field(1, len, y, x, 0, 0);
382 textbox->widget.focussed_attr = A_REVERSE;
383 textbox->widget.unfocussed_attr = A_UNDERLINE;
385 field_opts_off(f, O_STATIC | O_WRAP | O_BLANK);
387 field_opts_off(f, O_PUBLIC);
388 set_field_buffer(f, 0, str);
389 set_field_back(f, textbox->widget.unfocussed_attr);
390 set_field_userptr(f, &textbox->widget);
392 widgetset_add_field(set, textbox->widget.field);
393 talloc_set_destructor(textbox, textbox_destructor);
398 struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
399 int y, int x, int len, char *str)
401 return widget_new_textbox_hidden(set, y, x, len, str, false);
404 void widget_textbox_set_fixed_size(struct nc_widget_textbox *textbox)
406 field_opts_on(textbox->widget.field, O_STATIC);
409 void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox,
412 set_field_type(textbox->widget.field, TYPE_INTEGER, 1, min, max);
415 static bool check_url_field(FIELD *field,
416 const void *arg __attribute__((unused)))
418 return is_url(field_buffer(field, 0));
421 void widget_textbox_set_validator_url(struct nc_widget_textbox *textbox)
423 if (!textbox->set->url_type)
424 textbox->set->url_type = new_fieldtype(check_url_field, NULL);
426 set_field_type(textbox->widget.field, textbox->set->url_type);
429 static bool check_ip_field(FIELD *field,
430 const void *arg __attribute__((unused)))
435 str = strip_string(field_buffer(field, 0));
437 rc = addr_scheme(str);
439 return (rc == AF_INET || rc == AF_INET6);
443 static bool check_ipv6_multi_char(int c)
445 return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||
446 c == ':' || c == ' ';
449 static bool check_ipv4_multi_char(int c)
451 return isdigit(c) || c == '.' || c == ' ';
454 static bool check_ip_multi_field(FIELD *field,
455 const void *arg __attribute__((unused)))
457 char *buf, *tok, *saveptr;
461 /* Use strdup directly since we can't easily reach a talloc parent */
462 buf = strdup(strip_string(field_buffer(field, 0)));
468 tok = strtok_r(buf, " ", &saveptr);
469 if (!tok || *tok == '\0')
473 type = addr_scheme(tok);
474 if (!(type == AF_INET || type == AF_INET6))
477 tok = strtok_r(NULL, " ", &saveptr);
486 static bool check_ip_multi_char(int c, const void *arg __attribute__((unused)))
488 return (check_ipv4_multi_char(c) || check_ipv6_multi_char(c));
491 void widget_textbox_set_validator_ip(struct nc_widget_textbox *textbox)
493 if (!textbox->set->ip_type) {
494 textbox->set->ip_type = new_fieldtype(check_ip_field, NULL);
496 set_field_type(textbox->widget.field, textbox->set->ip_type);
500 * In a perfect world we would use link_fieldtype() but segfaults do not
501 * enhance the user experience.
503 void widget_textbox_set_validator_ip_multi(struct nc_widget_textbox *textbox)
505 if (!textbox->set->ip_multi_type) {
506 textbox->set->ip_multi_type = new_fieldtype(
507 check_ip_multi_field,
508 check_ip_multi_char);
510 set_field_type(textbox->widget.field, textbox->set->ip_multi_type);
513 static void subset_update_order(struct nc_widget_subset *subset)
518 for (i = 0; i < subset->n_active; i++) {
519 val = subset->active[i];
520 str = talloc_asprintf(subset, "(%d) %s",
521 i, subset->options[val].str);
522 set_field_buffer(subset->options[val].field, 0,
528 static void widget_focus_change(struct nc_widget *widget, FIELD *field,
531 static void subset_delete_active(struct nc_widget_subset *subset, int idx)
533 bool last = idx == (subset->n_active - 1);
534 struct nc_widgetset *set = subset->set;
535 struct nc_widget *widget;
540 /* Shift field focus to nearest active option or next visible field */
541 if (subset->n_active > 1) {
543 val = subset->active[idx - 1];
545 val = subset->active[idx + 1];
546 set->cur_field = subset->options[val].field;
548 for (i = 0; i < set->n_fields; i++)
549 if (field_visible(set->fields[i])) {
550 opts = field_opts(set->fields[i]);
551 if ((opts & O_ACTIVE) == O_ACTIVE) {
552 set->cur_field = set->fields[i];
558 set_current_field(set->form, set->cur_field);
559 widget = field_userptr(set->cur_field);
560 widget_focus_change(widget, set->cur_field, true);
561 if (set->widget_focus)
562 set->widget_focus(widget, set->widget_focus_arg);
564 /* Update active array */
565 rem = sizeof(int) * (subset->n_active - idx - 1);
566 val = subset->active[idx];
567 field_set_visible(subset->options[val].field, false);
569 memmove(&subset->active[idx], &subset->active[idx + 1], rem);
571 subset->active = talloc_realloc(subset, subset->active,
572 int, subset->n_active);
574 subset->widget.height = subset->n_active;
577 static bool subset_process_key(struct nc_widget *w, FORM *form, int key)
579 struct nc_widget_subset *subset = to_subset(w);
580 int i, val, opt_idx = -1;
583 if (key != '-' && key != '+' && key != KEY_DC && key != KEY_BACKSPACE)
586 field = current_field(form);
588 for (i = 0; i < subset->n_active; i++) {
589 val = subset->active[i];
590 if (subset->options[val].field == field) {
599 if (key == KEY_DC || key == KEY_BACKSPACE)
600 subset_delete_active(subset, opt_idx);
606 val = subset->active[opt_idx];
607 subset->active[opt_idx] = subset->active[opt_idx - 1];
608 subset->active[opt_idx - 1] = val;
612 if (opt_idx >= subset->n_active - 1)
615 val = subset->active[opt_idx];
616 subset->active[opt_idx] = subset->active[opt_idx + 1];
617 subset->active[opt_idx + 1] = val;
620 subset_update_order(subset);
622 if (subset->on_change)
623 subset->on_change(subset->on_change_arg, 0);
628 static void subset_set_visible(struct nc_widget *widget, bool visible)
630 struct nc_widget_subset *subset = to_subset(widget);
633 for (i = 0; i < subset->n_active; i++) {
634 val = subset->active[i];
635 field_set_visible(subset->options[val].field, visible);
639 static void subset_move(struct nc_widget *widget, int y, int x)
641 struct nc_widget_subset *subset = to_subset(widget);
644 for (i = 0; i < subset->n_active; i++) {
645 val = subset->active[i];
646 field_move(subset->options[val].field, y + i , x);
650 static void subset_field_focus(struct nc_widget *widget, FIELD *field)
652 struct nc_widget_subset *subset = to_subset(widget);
655 for (i = 0; i < subset->n_active; i++) {
656 val = subset->active[i];
657 if (field == subset->options[val].field) {
664 static int subset_destructor(void *ptr)
666 struct nc_widget_subset *subset = ptr;
669 for (i = 0; i < subset->n_options; i++)
670 free_field(subset->options[i].field);
675 struct nc_widget_subset *widget_new_subset(struct nc_widgetset *set,
676 int y, int x, int len, void *screen_cb)
678 struct nc_widget_subset *subset;
680 subset = talloc_zero(set, struct nc_widget_subset);
681 subset->widget.width = len;
682 subset->widget.height = 0;
683 subset->widget.x = x;
684 subset->widget.y = y;
685 subset->widget.process_key = subset_process_key;
686 subset->widget.set_visible = subset_set_visible;
687 subset->widget.move = subset_move;
688 subset->widget.field_focus = subset_field_focus;
689 subset->widget.focussed_attr = A_REVERSE;
690 subset->widget.unfocussed_attr = A_NORMAL;
695 subset->n_active = subset->n_options = 0;
696 subset->active = NULL;
697 subset->options = NULL;
698 subset->screen_cb = screen_cb;
700 talloc_set_destructor(subset, subset_destructor);
705 void widget_subset_add_option(struct nc_widget_subset *subset, const char *text)
710 i = subset->n_options++;
711 subset->options = talloc_realloc(subset, subset->options,
712 struct subset_option, i + 1);
714 subset->options[i].str = talloc_strdup(subset->options, text);
716 subset->options[i].field = f = new_field(1, subset->size, subset->top + i,
719 field_opts_off(f, O_WRAP | O_EDIT);
720 set_field_userptr(f, &subset->widget);
721 set_field_buffer(f, 0, subset->options[i].str);
722 field_set_visible(f, false);
723 widgetset_add_field(subset->set, f);
726 void widget_subset_make_active(struct nc_widget_subset *subset, int idx)
730 for (i = 0; i < subset->n_active; i++)
731 if (subset->active[i] == idx) {
732 pb_debug("%s: Index %d already active\n", __func__, idx);
736 i = subset->n_active++;
737 subset->widget.height = subset->n_active;
738 subset->active = talloc_realloc(subset, subset->active,
740 subset->active[i] = idx;
742 subset_update_order(subset);
745 int widget_subset_get_order(void *ctx, unsigned int **order,
746 struct nc_widget_subset *subset)
748 unsigned int *buf = talloc_array(ctx, unsigned int, subset->n_active);
751 for (i = 0; i < subset->n_active; i++)
752 buf[i] = subset->active[i];
758 void widget_subset_show_inactive(struct nc_widget_subset *subset,
759 struct nc_widget_select *select)
761 bool active = false, first = true;
764 for (i = 0; i < subset->n_options; i++) {
766 for (j = 0; j < subset->n_active; j++)
767 if (subset->active[j] == i)
773 widget_select_add_option(select, i,
774 subset->options[i].str, first);
780 int widget_subset_n_inactive(struct nc_widget_subset *subset)
782 return subset->n_options - subset->n_active;
785 int widget_subset_height(struct nc_widget_subset *subset)
787 return subset->n_active;
790 void widget_subset_on_change(struct nc_widget_subset *subset,
791 void (*on_change)(void *, int), void *arg)
793 subset->on_change = on_change;
794 subset->on_change_arg = arg;
797 void widget_subset_drop_options(struct nc_widget_subset *subset)
799 struct nc_widgetset *set = subset->set;
802 for (i = 0; i < subset->n_options; i++) {
803 FIELD *field = subset->options[i].field;
804 widgetset_remove_field(set, field);
805 if (field == set->cur_field)
806 set->cur_field = NULL;
807 free_field(subset->options[i].field);
810 talloc_free(subset->options);
811 talloc_free(subset->active);
812 subset->options = NULL;
813 subset->active = NULL;
814 subset->n_options = 0;
815 subset->n_active = 0;
816 subset->widget.height = 0;
817 subset->widget.focus_y = 0;
820 void widget_subset_clear_active(struct nc_widget_subset *subset)
824 for (i = 0; i < subset->n_options; i++)
825 field_set_visible(subset->options[i].field, false);
827 talloc_free(subset->active);
828 subset->active = NULL;
829 subset->n_active = 0;
830 subset->widget.height = 0;
831 subset->widget.focus_y = 0;
834 void widget_subset_callback(void *arg,
835 struct nc_widget_subset *subset, int idx)
837 subset->screen_cb(arg, subset, idx);
840 static void select_option_change(struct select_option *opt, bool selected)
844 str = selected ? select_selected_str : select_unselected_str;
846 memcpy(opt->str, str, strlen(str));
847 set_field_buffer(opt->field, 0, opt->str);
850 static bool select_process_key(struct nc_widget *w, FORM *form, int key)
852 struct nc_widget_select *select = to_select(w);
853 struct select_option *new_opt, *old_opt;
857 if (!key_is_select(key))
860 field = current_field(form);
863 for (i = 0; i < select->n_options; i++) {
864 if (select->options[i].field == field) {
865 new_opt = &select->options[i];
874 if (new_idx == select->selected_option)
877 old_opt = &select->options[select->selected_option];
879 select_option_change(old_opt, false);
880 select_option_change(new_opt, true);
882 select->selected_option = new_idx;
884 if (select->on_change)
885 select->on_change(select->on_change_arg, new_opt->val);
890 static void select_set_visible(struct nc_widget *widget, bool visible)
892 struct nc_widget_select *select = to_select(widget);
895 for (i = 0; i < select->n_options; i++)
896 field_set_visible(select->options[i].field, visible);
899 static void select_move(struct nc_widget *widget, int y, int x)
901 struct nc_widget_select *select = to_select(widget);
904 for (i = 0; i < select->n_options; i++) {
905 field_move(select->options[i].field, y + cur, x);
906 cur += select->options[i].lines;
910 static void select_field_focus(struct nc_widget *widget, FIELD *field)
912 struct nc_widget_select *select = to_select(widget);
915 for (i = 0; i < select->n_options; i++) {
916 if (field != select->options[i].field) {
917 cur += select->options[i].lines;
920 widget->focus_y = cur;
925 static int select_destructor(void *ptr)
927 struct nc_widget_select *select = ptr;
930 for (i = 0; i < select->n_options; i++)
931 free_field(select->options[i].field);
936 struct nc_widget_select *widget_new_select(struct nc_widgetset *set,
937 int y, int x, int len)
939 struct nc_widget_select *select;
941 select = talloc_zero(set, struct nc_widget_select);
942 select->widget.width = len;
943 select->widget.height = 0;
944 select->widget.x = x;
945 select->widget.y = y;
946 select->widget.process_key = select_process_key;
947 select->widget.set_visible = select_set_visible;
948 select->widget.move = select_move;
949 select->widget.field_focus = select_field_focus;
950 select->widget.focussed_attr = A_REVERSE;
951 select->widget.unfocussed_attr = A_NORMAL;
957 talloc_set_destructor(select, select_destructor);
962 static int widget_select_fold_cb(void *arg, const char *buf, int len)
964 struct nc_widget_select *select = arg;
965 char *line, *newstr, *padbuf = NULL;
971 line = talloc_strndup(select->options, buf, len);
973 i = select->n_options - 1;
974 pad = max(0, select->widget.width - strncols(line));
977 padbuf = talloc_array(select->options, char, pad + 1);
978 memset(padbuf, ' ', pad);
982 if (select->options[i].str)
983 newstr = talloc_asprintf_append(select->options[i].str,
987 newstr = talloc_asprintf(select->options, "%s%s", line,
990 select->options[i].str = newstr;
991 select->options[i].lines++;
998 void widget_select_add_option(struct nc_widget_select *select, int value,
999 const char *text, bool selected)
1006 /* if we never see an option with selected set, we want the first
1007 * one to be selected */
1008 if (select->n_options == 0)
1011 select_option_change(&select->options[select->selected_option],
1015 select->selected_option = select->n_options;
1016 str = select_selected_str;
1018 str = select_unselected_str;
1020 i = select->n_options++;
1021 select->options = talloc_realloc(select, select->options,
1022 struct select_option, i + 2);
1023 select->options[i].val = value;
1024 select->options[i].lines = 0;
1025 select->options[i].str = NULL;
1027 full_text = talloc_asprintf(select->options, "%s %s", str, text);
1028 fold_text(full_text, select->widget.width,
1029 widget_select_fold_cb, select);
1031 select->options[i].field = f = new_field(select->options[i].lines,
1033 select->top + select->widget.height,
1034 select->left, 0, 0);
1036 select->widget.height += select->options[i].lines;
1038 field_opts_off(f, O_EDIT);
1039 set_field_userptr(f, &select->widget);
1040 set_field_buffer(f, 0, select->options[i].str);
1042 widgetset_add_field(select->set, f);
1043 talloc_free(full_text);
1046 int widget_select_get_value(struct nc_widget_select *select)
1048 if (!select->n_options)
1050 return select->options[select->selected_option].val;
1053 int widget_select_height(struct nc_widget_select *select)
1055 return select->widget.height;
1058 void widget_select_on_change(struct nc_widget_select *select,
1059 void (*on_change)(void *, int), void *arg)
1061 select->on_change = on_change;
1062 select->on_change_arg = arg;
1065 void widget_select_drop_options(struct nc_widget_select *select)
1067 struct nc_widgetset *set = select->set;
1070 for (i = 0; i < select->n_options; i++) {
1071 FIELD *field = select->options[i].field;
1072 widgetset_remove_field(set, field);
1073 if (field == set->cur_field)
1074 set->cur_field = NULL;
1075 free_field(select->options[i].field);
1078 talloc_free(select->options);
1079 select->options = NULL;
1080 select->n_options = 0;
1081 select->widget.height = 0;
1082 select->widget.focus_y = 0;
1086 static bool button_process_key(struct nc_widget *widget,
1087 FORM *form __attribute__((unused)), int key)
1089 struct nc_widget_button *button = to_button(widget);
1094 if (!key_is_select(key))
1097 button->click(button->arg);
1101 static int button_destructor(void *ptr)
1103 struct nc_widget_button *button = ptr;
1104 free_field(button->widget.field);
1108 struct nc_widget_button *widget_new_button(struct nc_widgetset *set,
1109 int y, int x, int size, const char *str,
1110 void (*click)(void *), void *arg)
1112 struct nc_widget_button *button;
1113 int idx, len, pad1, pad2, bufsz;
1117 int field_size = size + 2;
1119 button = talloc_zero(set, struct nc_widget_button);
1120 button->widget.height = 1;
1121 button->widget.width = field_size;
1122 button->widget.x = x;
1123 button->widget.y = y;
1124 button->widget.field = f = new_field(1, field_size, y, x, 0, 0);
1125 button->widget.process_key = button_process_key;
1126 button->widget.focussed_attr = A_REVERSE;
1127 button->widget.unfocussed_attr = A_NORMAL;
1128 button->click = click;
1131 field_opts_off(f, O_EDIT);
1132 set_field_userptr(f, &button->widget);
1134 /* Center str in the field. This depends on the number of columns used
1135 * by the string, not the number of chars in str */
1136 len = strncols(str);
1138 idx = (field_size - len) / 2;
1141 pb_log("Warning: '%s' %d columns wide "
1142 "but button is %d columns wide\n",
1146 pad1 = max(idx - 1, 0);
1147 pad2 = max(size - len - pad1, 0);
1148 bufsz = 1 + pad1 + strlen(str) + pad2 + 2;
1150 text = talloc_array(button, char, bufsz);
1151 memset(text, ' ', bufsz);
1152 memcpy(text + idx, str, strlen(str));
1154 text[bufsz - 2] = ']';
1155 text[bufsz - 1] = '\0';
1157 set_field_buffer(f, 0, text);
1159 widgetset_add_field(set, button->widget.field);
1160 talloc_set_destructor(button, button_destructor);
1165 void widget_focus_change(struct nc_widget *widget, FIELD *field,
1168 int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr;
1169 set_field_back(field, attr);
1172 bool widgetset_process_key(struct nc_widgetset *set, int key)
1174 struct nc_widget *widget;
1179 field = current_field(set->form);
1182 widget = field_userptr(field);
1184 if (widget->process_key)
1185 if (widget->process_key(widget, set->form, key))
1190 /* handle field change events */
1196 req = REQ_SPREV_FIELD;
1202 req = REQ_SNEXT_FIELD;
1205 req = REQ_SFIRST_FIELD;
1208 req = REQ_SLAST_FIELD;
1211 req = REQ_LEFT_FIELD;
1214 req = REQ_RIGHT_FIELD;
1219 widget_focus_change(widget, field, false);
1220 form_driver(set->form, req);
1222 /* if we're doing a tabbed-field-change, skip until we
1223 * see the next widget */
1225 field = current_field(set->form);
1227 for (; tab && tmp != field && field_userptr(field) == widget;) {
1228 form_driver(set->form, req);
1229 field = current_field(set->form);
1232 form_driver(set->form, REQ_END_FIELD);
1233 widget = field_userptr(field);
1234 widget_focus_change(widget, field, true);
1235 if (widget->field_focus)
1236 widget->field_focus(widget, field);
1237 if (set->widget_focus)
1238 set->widget_focus(widget, set->widget_focus_arg);
1245 static int widgetset_destructor(void *ptr)
1247 struct nc_widgetset *set = ptr;
1248 free_form(set->form);
1250 free_fieldtype(set->ip_type);
1251 if (set->ip_multi_type)
1252 free_fieldtype(set->ip_multi_type);
1254 free_fieldtype(set->url_type);
1258 struct nc_widgetset *widgetset_create(void *ctx, WINDOW *main, WINDOW *sub)
1260 struct nc_widgetset *set;
1262 set = talloc_zero(ctx, struct nc_widgetset);
1263 set->n_alloc_fields = 8;
1264 set->mainwin = main;
1266 set->fields = talloc_array(set, FIELD *, set->n_alloc_fields);
1267 talloc_set_destructor(set, widgetset_destructor);
1272 void widgetset_set_windows(struct nc_widgetset *set,
1273 WINDOW *main, WINDOW *sub)
1275 set->mainwin = main;
1279 void widgetset_set_widget_focus(struct nc_widgetset *set,
1280 widget_focus_cb cb, void *arg)
1282 set->widget_focus = cb;
1283 set->widget_focus_arg = arg;
1286 void widgetset_post(struct nc_widgetset *set)
1288 struct nc_widget *widget;
1291 set->form = new_form(set->fields);
1292 set_form_win(set->form, set->mainwin);
1293 set_form_sub(set->form, set->subwin);
1294 post_form(set->form);
1295 form_driver(set->form, REQ_END_FIELD);
1298 set_current_field(set->form, set->cur_field);
1300 field = current_field(set->form);
1301 widget = field_userptr(field);
1302 widget_focus_change(widget, field, true);
1303 if (set->widget_focus)
1304 set->widget_focus(widget, set->widget_focus_arg);
1307 void widgetset_unpost(struct nc_widgetset *set)
1309 set->cur_field = current_field(set->form);
1310 unpost_form(set->form);
1311 free_form(set->form);
1315 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field)
1317 if (set->n_fields == set->n_alloc_fields - 1) {
1318 set->n_alloc_fields *= 2;
1319 set->fields = talloc_realloc(set, set->fields,
1320 FIELD *, set->n_alloc_fields);
1324 set->fields[set->n_fields - 1] = field;
1325 set->fields[set->n_fields] = NULL;
1328 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field)
1332 for (i = 0; i < set->n_fields; i++) {
1333 if (set->fields[i] == field)
1337 if (i == set->n_fields)
1340 memmove(&set->fields[i], &set->fields[i+i],
1341 (set->n_fields - i) * sizeof(set->fields[i]));
1345 #define DECLARE_BASEFN(type) \
1346 struct nc_widget *widget_ ## type ## _base \
1347 (struct nc_widget_ ## type *w) \
1348 { return &w->widget; }
1350 DECLARE_BASEFN(textbox);
1351 DECLARE_BASEFN(checkbox);
1352 DECLARE_BASEFN(subset);
1353 DECLARE_BASEFN(select);
1354 DECLARE_BASEFN(label);
1355 DECLARE_BASEFN(button);
1357 void widget_set_visible(struct nc_widget *widget, bool visible)
1359 if (widget->set_visible)
1360 widget->set_visible(widget, visible);
1362 field_set_visible(widget->field, visible);
1365 void widget_move(struct nc_widget *widget, int y, int x)
1368 widget->move(widget, y, x);
1370 field_move(widget->field, y, x);
1375 if (x + widget->width > COLS)
1376 pb_debug("%s: Widget at %d,%d runs over pad! (%d)", __func__,
1377 y, x, x + widget->width);
1380 int widget_height(struct nc_widget *widget)
1382 return widget->height;
1385 int widget_width(struct nc_widget *widget)
1387 return widget->width;
1390 int widget_x(struct nc_widget *widget)
1395 int widget_y(struct nc_widget *widget)
1400 int widget_focus_y(struct nc_widget *widget)
1402 return widget->focus_y;