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
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>
57 #include "nc-widgets.h"
61 #define to_checkbox(w) container_of(w, struct nc_widget_checkbox, widget)
62 #define to_textbox(w) container_of(w, struct nc_widget_textbox, widget)
63 #define to_button(w) container_of(w, struct nc_widget_button, widget)
64 #define to_select(w) container_of(w, struct nc_widget_select, widget)
66 static const char *checkbox_checked_str = "[*]";
67 static const char *checkbox_unchecked_str = "[ ]";
69 static const char *select_selected_str = "(*)";
70 static const char *select_unselected_str = "( )";
77 int n_fields, n_alloc_fields;
78 void (*widget_focus)(struct nc_widget *, void *);
79 void *widget_focus_arg;
85 bool (*process_key)(struct nc_widget *, FORM *, int);
86 void (*set_visible)(struct nc_widget *, bool);
87 void (*move)(struct nc_widget *, int, int);
88 void (*field_focus)(struct nc_widget *, FIELD *);
98 struct nc_widget_label {
99 struct nc_widget widget;
103 struct nc_widget_checkbox {
104 struct nc_widget widget;
108 struct nc_widget_textbox {
109 struct nc_widget widget;
112 struct nc_widget_select {
113 struct nc_widget widget;
114 struct select_option {
120 int n_options, selected_option;
121 struct nc_widgetset *set;
122 void (*on_change)(void *, int);
126 struct nc_widget_button {
127 struct nc_widget widget;
128 void (*click)(void *arg);
132 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field);
133 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field);
135 static bool key_is_select(int key)
137 return key == ' ' || key == '\r' || key == '\n' || key == KEY_ENTER;
140 static bool process_key_nop(struct nc_widget *widget __attribute__((unused)),
141 FORM *form __attribute((unused)),
142 int key __attribute__((unused)))
147 static void field_set_visible(FIELD *field, bool visible)
149 int opts = field_opts(field) & ~O_VISIBLE;
152 set_field_opts(field, opts);
155 static void field_move(FIELD *field, int y, int x)
157 move_field(field, y, x);
160 static int label_destructor(void *ptr)
162 struct nc_widget_label *label = ptr;
163 free_field(label->widget.field);
168 struct nc_widget_label *widget_new_label(struct nc_widgetset *set,
169 int y, int x, char *str)
171 struct nc_widget_label *label;
177 label = talloc_zero(set, struct nc_widget_label);
178 label->widget.height = 1;
179 label->widget.width = len;
182 label->widget.process_key = process_key_nop;
183 label->widget.field = f = new_field(1, len, y, x, 0, 0);
184 label->widget.focussed_attr = A_NORMAL;
185 label->widget.unfocussed_attr = A_NORMAL;
187 field_opts_off(f, O_ACTIVE);
188 set_field_buffer(f, 0, str);
189 set_field_userptr(f, &label->widget);
191 widgetset_add_field(set, label->widget.field);
192 talloc_set_destructor(label, label_destructor);
197 bool widget_checkbox_get_value(struct nc_widget_checkbox *checkbox)
199 return checkbox->checked;
202 static void checkbox_set_buffer(struct nc_widget_checkbox *checkbox)
205 str = checkbox->checked ? checkbox_checked_str : checkbox_unchecked_str;
206 set_field_buffer(checkbox->widget.field, 0, str);
209 static bool checkbox_process_key(struct nc_widget *widget,
210 FORM *form __attribute__((unused)), int key)
212 struct nc_widget_checkbox *checkbox = to_checkbox(widget);
214 if (!key_is_select(key))
217 checkbox->checked = !checkbox->checked;
218 checkbox_set_buffer(checkbox);
223 static int checkbox_destructor(void *ptr)
225 struct nc_widget_checkbox *checkbox = ptr;
226 free_field(checkbox->widget.field);
230 struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set,
231 int y, int x, bool checked)
233 struct nc_widget_checkbox *checkbox;
236 checkbox = talloc_zero(set, struct nc_widget_checkbox);
237 checkbox->checked = checked;
238 checkbox->widget.height = 1;
239 checkbox->widget.width = strlen(checkbox_checked_str);
240 checkbox->widget.x = x;
241 checkbox->widget.y = y;
242 checkbox->widget.process_key = checkbox_process_key;
243 checkbox->widget.focussed_attr = A_REVERSE;
244 checkbox->widget.unfocussed_attr = A_NORMAL;
245 checkbox->widget.field = f = new_field(1, strlen(checkbox_checked_str),
248 field_opts_off(f, O_EDIT);
249 set_field_userptr(f, &checkbox->widget);
250 checkbox_set_buffer(checkbox);
252 widgetset_add_field(set, checkbox->widget.field);
253 talloc_set_destructor(checkbox, checkbox_destructor);
258 static char *strip_string(char *str)
264 /* clear trailing space */
265 for (i = len - 1; i >= 0; i--) {
266 if (!isspace(str[i]))
271 /* increment str past leading space */
272 for (i = 0; i < len; i++) {
273 if (str[i] == '\0' || !isspace(str[i]))
280 char *widget_textbox_get_value(struct nc_widget_textbox *textbox)
282 char *str = field_buffer(textbox->widget.field, 0);
283 return str ? strip_string(str) : NULL;
286 static bool textbox_process_key(
287 struct nc_widget *widget __attribute__((unused)),
292 form_driver(form, REQ_BEG_FIELD);
295 form_driver(form, REQ_END_FIELD);
298 form_driver(form, REQ_LEFT_CHAR);
301 form_driver(form, REQ_RIGHT_CHAR);
304 if (form_driver(form, REQ_LEFT_CHAR) != E_OK)
308 form_driver(form, REQ_DEL_CHAR);
311 form_driver(form, key);
318 static int textbox_destructor(void *ptr)
320 struct nc_widget_textbox *textbox = ptr;
321 free_field(textbox->widget.field);
325 struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set,
326 int y, int x, int len, char *str)
328 struct nc_widget_textbox *textbox;
331 textbox = talloc_zero(set, struct nc_widget_textbox);
332 textbox->widget.height = 1;
333 textbox->widget.width = len;
334 textbox->widget.x = x;
335 textbox->widget.y = y;
336 textbox->widget.process_key = textbox_process_key;
337 textbox->widget.field = f = new_field(1, len, y, x, 0, 0);
338 textbox->widget.focussed_attr = A_REVERSE;
339 textbox->widget.unfocussed_attr = A_UNDERLINE;
341 field_opts_off(f, O_STATIC | O_WRAP | O_BLANK);
342 set_field_buffer(f, 0, str);
343 set_field_back(f, textbox->widget.unfocussed_attr);
344 set_field_userptr(f, &textbox->widget);
346 widgetset_add_field(set, textbox->widget.field);
347 talloc_set_destructor(textbox, textbox_destructor);
352 static void select_option_change(struct select_option *opt, bool selected)
356 str = selected ? select_selected_str : select_unselected_str;
358 memcpy(opt->str, str, strlen(str));
359 set_field_buffer(opt->field, 0, opt->str);
362 static bool select_process_key(struct nc_widget *w, FORM *form, int key)
364 struct nc_widget_select *select = to_select(w);
365 struct select_option *new_opt, *old_opt;
369 if (!key_is_select(key))
372 field = current_field(form);
375 for (i = 0; i < select->n_options; i++) {
376 if (select->options[i].field == field) {
377 new_opt = &select->options[i];
386 if (new_idx == select->selected_option)
389 old_opt = &select->options[select->selected_option];
391 select_option_change(old_opt, false);
392 select_option_change(new_opt, true);
394 select->selected_option = new_idx;
396 if (select->on_change)
397 select->on_change(select->on_change_arg, new_opt->val);
402 static void select_set_visible(struct nc_widget *widget, bool visible)
404 struct nc_widget_select *select = to_select(widget);
407 for (i = 0; i < select->n_options; i++)
408 field_set_visible(select->options[i].field, visible);
411 static void select_move(struct nc_widget *widget, int y, int x)
413 struct nc_widget_select *select = to_select(widget);
416 for (i = 0; i < select->n_options; i++)
417 field_move(select->options[i].field, y + i, x);
420 static void select_field_focus(struct nc_widget *widget, FIELD *field)
422 struct nc_widget_select *select = to_select(widget);
425 for (i = 0; i < select->n_options; i++) {
426 if (field != select->options[i].field)
433 static int select_destructor(void *ptr)
435 struct nc_widget_select *select = ptr;
438 for (i = 0; i < select->n_options; i++)
439 free_field(select->options[i].field);
444 struct nc_widget_select *widget_new_select(struct nc_widgetset *set,
445 int y, int x, int len)
447 struct nc_widget_select *select;
449 select = talloc_zero(set, struct nc_widget_select);
450 select->widget.width = len;
451 select->widget.height = 0;
452 select->widget.x = x;
453 select->widget.y = y;
454 select->widget.process_key = select_process_key;
455 select->widget.set_visible = select_set_visible;
456 select->widget.move = select_move;
457 select->widget.field_focus = select_field_focus;
458 select->widget.focussed_attr = A_REVERSE;
459 select->widget.unfocussed_attr = A_NORMAL;
465 talloc_set_destructor(select, select_destructor);
470 void widget_select_add_option(struct nc_widget_select *select, int value,
471 const char *text, bool selected)
477 /* if we never see an option with selected set, we want the first
478 * one to be selected */
479 if (select->n_options == 0)
482 select_option_change(&select->options[select->selected_option],
486 select->selected_option = select->n_options;
487 str = select_selected_str;
489 str = select_unselected_str;
491 i = select->n_options++;
492 select->widget.height = select->n_options;
494 select->options = talloc_realloc(select, select->options,
495 struct select_option, i + 2);
496 select->options[i].val = value;
497 select->options[i].str = talloc_asprintf(select->options,
500 select->options[i].field = f = new_field(1, select->size,
504 field_opts_off(f, O_WRAP | O_EDIT);
505 set_field_userptr(f, &select->widget);
506 set_field_buffer(f, 0, select->options[i].str);
508 widgetset_add_field(select->set, f);
511 int widget_select_get_value(struct nc_widget_select *select)
513 if (!select->n_options)
515 return select->options[select->selected_option].val;
518 int widget_select_height(struct nc_widget_select *select)
520 return select->n_options;
523 void widget_select_on_change(struct nc_widget_select *select,
524 void (*on_change)(void *, int), void *arg)
526 select->on_change = on_change;
527 select->on_change_arg = arg;
530 void widget_select_drop_options(struct nc_widget_select *select)
532 struct nc_widgetset *set = select->set;
535 for (i = 0; i < select->n_options; i++) {
536 FIELD *field = select->options[i].field;
537 widgetset_remove_field(set, field);
538 if (field == set->cur_field)
539 set->cur_field = NULL;
540 free_field(select->options[i].field);
543 talloc_free(select->options);
544 select->options = NULL;
545 select->n_options = 0;
546 select->widget.height = 0;
547 select->widget.focus_y = 0;
551 static bool button_process_key(struct nc_widget *widget,
552 FORM *form __attribute__((unused)), int key)
554 struct nc_widget_button *button = to_button(widget);
559 if (!key_is_select(key))
562 button->click(button->arg);
566 static int button_destructor(void *ptr)
568 struct nc_widget_button *button = ptr;
569 free_field(button->widget.field);
573 struct nc_widget_button *widget_new_button(struct nc_widgetset *set,
574 int y, int x, int size, const char *str,
575 void (*click)(void *), void *arg)
577 struct nc_widget_button *button;
582 button = talloc_zero(set, struct nc_widget_button);
583 button->widget.height = 1;
584 button->widget.width = size;
585 button->widget.x = x;
586 button->widget.y = y;
587 button->widget.field = f = new_field(1, size + 2, y, x, 0, 0);
588 button->widget.process_key = button_process_key;
589 button->widget.focussed_attr = A_REVERSE;
590 button->widget.unfocussed_attr = A_NORMAL;
591 button->click = click;
594 field_opts_off(f, O_EDIT);
595 set_field_userptr(f, &button->widget);
597 /* center str in a size-char buffer, but don't overrun */
599 len = min(len, size);
600 idx = (size - len) / 2;
602 text = talloc_array(button, char, size + 3);
603 memset(text, ' ', size + 2);
604 memcpy(text + idx + 1, str, len);
606 text[size + 1] = ']';
607 text[size + 2] = '\0';
609 set_field_buffer(f, 0, text);
611 widgetset_add_field(set, button->widget.field);
612 talloc_set_destructor(button, button_destructor);
617 static void widget_focus_change(struct nc_widget *widget, FIELD *field,
620 int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr;
621 set_field_back(field, attr);
624 bool widgetset_process_key(struct nc_widgetset *set, int key)
626 struct nc_widget *widget;
630 field = current_field(set->form);
633 /* handle field change events */
637 req = REQ_PREV_FIELD;
641 req = REQ_NEXT_FIELD;
644 req = REQ_FIRST_FIELD;
647 req = REQ_LAST_FIELD;
651 widget = field_userptr(field);
653 widget_focus_change(widget, field, false);
654 form_driver(set->form, req);
655 form_driver(set->form, REQ_END_FIELD);
656 field = current_field(set->form);
657 widget = field_userptr(field);
658 widget_focus_change(widget, field, true);
659 if (widget->field_focus)
660 widget->field_focus(widget, field);
661 if (set->widget_focus)
662 set->widget_focus(widget, set->widget_focus_arg);
666 if (!widget->process_key)
669 return widget->process_key(widget, set->form, key);
672 static int widgetset_destructor(void *ptr)
674 struct nc_widgetset *set = ptr;
675 free_form(set->form);
679 struct nc_widgetset *widgetset_create(void *ctx, WINDOW *main, WINDOW *sub)
681 struct nc_widgetset *set;
683 set = talloc_zero(ctx, struct nc_widgetset);
684 set->n_alloc_fields = 8;
687 set->fields = talloc_array(set, FIELD *, set->n_alloc_fields);
688 talloc_set_destructor(set, widgetset_destructor);
693 void widgetset_set_windows(struct nc_widgetset *set,
694 WINDOW *main, WINDOW *sub)
700 void widgetset_set_widget_focus(struct nc_widgetset *set,
701 widget_focus_cb cb, void *arg)
703 set->widget_focus = cb;
704 set->widget_focus_arg = arg;
707 void widgetset_post(struct nc_widgetset *set)
709 struct nc_widget *widget;
712 set->form = new_form(set->fields);
713 set_form_win(set->form, set->mainwin);
714 set_form_sub(set->form, set->subwin);
715 post_form(set->form);
716 form_driver(set->form, REQ_END_FIELD);
718 if (set->cur_field) {
719 set_current_field(set->form, set->cur_field);
720 field = set->cur_field;
723 field = current_field(set->form);
724 widget = field_userptr(field);
725 widget_focus_change(widget, field, true);
726 if (set->widget_focus)
727 set->widget_focus(widget, set->widget_focus_arg);
730 void widgetset_unpost(struct nc_widgetset *set)
732 set->cur_field = current_field(set->form);
733 unpost_form(set->form);
734 free_form(set->form);
738 static void widgetset_add_field(struct nc_widgetset *set, FIELD *field)
740 if (set->n_fields == set->n_alloc_fields - 1) {
741 set->n_alloc_fields *= 2;
742 set->fields = talloc_realloc(set, set->fields,
743 FIELD *, set->n_alloc_fields);
747 set->fields[set->n_fields - 1] = field;
748 set->fields[set->n_fields] = NULL;
751 static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field)
755 for (i = 0; i < set->n_fields; i++) {
756 if (set->fields[i] == field)
760 if (i == set->n_fields)
763 memmove(&set->fields[i], &set->fields[i+i],
764 (set->n_fields - i) * sizeof(set->fields[i]));
768 #define DECLARE_BASEFN(type) \
769 struct nc_widget *widget_ ## type ## _base \
770 (struct nc_widget_ ## type *w) \
771 { return &w->widget; }
773 DECLARE_BASEFN(textbox);
774 DECLARE_BASEFN(checkbox);
775 DECLARE_BASEFN(select);
776 DECLARE_BASEFN(label);
777 DECLARE_BASEFN(button);
779 void widget_set_visible(struct nc_widget *widget, bool visible)
781 if (widget->set_visible)
782 widget->set_visible(widget, visible);
784 field_set_visible(widget->field, visible);
787 void widget_move(struct nc_widget *widget, int y, int x)
790 widget->move(widget, y, x);
792 field_move(widget->field, y, x);
798 int widget_height(struct nc_widget *widget)
800 return widget->height;
803 int widget_width(struct nc_widget *widget)
805 return widget->width;
808 int widget_x(struct nc_widget *widget)
813 int widget_y(struct nc_widget *widget)
818 int widget_focus_y(struct nc_widget *widget)
820 return widget->focus_y;