X-Git-Url: http://git.ozlabs.org/?p=petitboot;a=blobdiff_plain;f=ui%2Fncurses%2Fnc-widgets.c;h=7dc2df3f1d1fc2138c001b7511bac6bfc0e8041a;hp=98ea6be269774bf8186f7baedc1b597d0aa079be;hb=297d2f0cc9c0a233fadf42dacc08708ad3909f77;hpb=37c9aee3ffc02a299d94867df9df8132b09fc611 diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c index 98ea6be..7dc2df3 100644 --- a/ui/ncurses/nc-widgets.c +++ b/ui/ncurses/nc-widgets.c @@ -15,9 +15,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define _GNU_SOURCE - +#if defined(HAVE_CONFIG_H) #include "config.h" +#endif #include /* This must be included before ncurses.h */ #if defined HAVE_NCURSESW_CURSES_H @@ -51,8 +51,9 @@ #include #include #include +#include +#include -#include "config.h" #include "nc-cui.h" #include "nc-widgets.h" @@ -62,6 +63,7 @@ #define to_textbox(w) container_of(w, struct nc_widget_textbox, widget) #define to_button(w) container_of(w, struct nc_widget_button, widget) #define to_select(w) container_of(w, struct nc_widget_select, widget) +#define to_subset(w) container_of(w, struct nc_widget_subset, widget) static const char *checkbox_checked_str = "[*]"; static const char *checkbox_unchecked_str = "[ ]"; @@ -78,6 +80,9 @@ struct nc_widgetset { void (*widget_focus)(struct nc_widget *, void *); void *widget_focus_arg; FIELD *cur_field; + + /* custom validators */ + FIELDTYPE *ipv4_multi_type; }; struct nc_widget { @@ -85,10 +90,12 @@ struct nc_widget { bool (*process_key)(struct nc_widget *, FORM *, int); void (*set_visible)(struct nc_widget *, bool); void (*move)(struct nc_widget *, int, int); + void (*field_focus)(struct nc_widget *, FIELD *); int focussed_attr; int unfocussed_attr; int height; int width; + int focus_y; int x; int y; }; @@ -104,15 +111,35 @@ struct nc_widget_checkbox { }; struct nc_widget_textbox { + struct nc_widgetset *set; struct nc_widget widget; }; +struct nc_widget_subset { + struct nc_widget widget; + int *active; + int n_active; + struct subset_option { + char *str; + int val; + FIELD *field; + } *options; + int n_options; + int top, left, size; + struct nc_widgetset *set; + void (*on_change)(void *, int); + void *on_change_arg; + void (*screen_cb)(void *, + struct nc_widget_subset *, int); +}; + struct nc_widget_select { struct nc_widget widget; struct select_option { char *str; int val; FIELD *field; + int lines; } *options; int top, left, size; int n_options, selected_option; @@ -128,6 +155,12 @@ struct nc_widget_button { }; static void widgetset_add_field(struct nc_widgetset *set, FIELD *field); +static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field); + +static bool key_is_select(int key) +{ + return key == ' ' || key == '\r' || key == '\n' || key == KEY_ENTER; +} static bool process_key_nop(struct nc_widget *widget __attribute__((unused)), FORM *form __attribute((unused)), @@ -144,6 +177,11 @@ static void field_set_visible(FIELD *field, bool visible) set_field_opts(field, opts); } +static bool field_visible(FIELD *field) +{ + return (field_opts(field) & O_VISIBLE) == O_VISIBLE; +} + static void field_move(FIELD *field, int y, int x) { move_field(field, y, x); @@ -203,7 +241,7 @@ static bool checkbox_process_key(struct nc_widget *widget, { struct nc_widget_checkbox *checkbox = to_checkbox(widget); - if (key != ' ') + if (!key_is_select(key)) return false; checkbox->checked = !checkbox->checked; @@ -299,6 +337,14 @@ static bool textbox_process_key( case KEY_DC: form_driver(form, REQ_DEL_CHAR); break; + case '\t': + case KEY_BTAB: + case KEY_UP: + case KEY_DOWN: + case KEY_PPAGE: + case KEY_NPAGE: + /* Don't catch navigational keys */ + return false; default: form_driver(form, key); break; @@ -321,6 +367,7 @@ struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set, FIELD *f; textbox = talloc_zero(set, struct nc_widget_textbox); + textbox->set = set; textbox->widget.height = 1; textbox->widget.width = len; textbox->widget.x = x; @@ -341,6 +388,394 @@ struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set, return textbox; } +void widget_textbox_set_fixed_size(struct nc_widget_textbox *textbox) +{ + field_opts_on(textbox->widget.field, O_STATIC); +} + +void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox, + long min, long max) +{ + set_field_type(textbox->widget.field, TYPE_INTEGER, 1, min, max); +} + +void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox) +{ + set_field_type(textbox->widget.field, TYPE_IPV4); +} + +static bool check_ipv4_multi_char(int c, + const void *arg __attribute__((unused))) +{ + return isdigit(c) || c == '.' || c == ' '; +} + +static bool check_ipv4_multi_field(FIELD *field, + const void *arg __attribute__((unused))) +{ + char *buf = field_buffer(field, 0); + unsigned int ip[4]; + int n, len; + + while (*buf != '\0') { + n = sscanf(buf, "%u.%u.%u.%u%n", + &ip[0], &ip[1], &ip[2], &ip[3], &len); + if (n != 4) + return false; + + if (ip[0] > 255 || ip[1] > 255 || ip[2] > 255 || ip[3] > 255) + return false; + + for (buf += len; *buf != '\0'; buf++) { + if (isspace(*buf)) + continue; + else if (isdigit(*buf)) + break; + else + return false; + } + } + + return true; +} + +void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox) +{ + if (!textbox->set->ipv4_multi_type) { + textbox->set->ipv4_multi_type = new_fieldtype( + check_ipv4_multi_field, + check_ipv4_multi_char); + } + set_field_type(textbox->widget.field, textbox->set->ipv4_multi_type); +} + +static void subset_update_order(struct nc_widget_subset *subset) +{ + char *str; + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + str = talloc_asprintf(subset, "(%d) %s", + i, subset->options[val].str); + set_field_buffer(subset->options[val].field, 0, + str); + talloc_free(str); + } +} + +static void widget_focus_change(struct nc_widget *widget, FIELD *field, + bool focussed); + +static void subset_delete_active(struct nc_widget_subset *subset, int idx) +{ + bool last = idx == (subset->n_active - 1); + struct nc_widgetset *set = subset->set; + struct nc_widget *widget; + size_t rem; + int i, val; + uint32_t opts; + + /* Shift field focus to nearest active option or next visible field */ + if (subset->n_active > 1) { + if (last) + val = subset->active[idx - 1]; + else + val = subset->active[idx + 1]; + set->cur_field = subset->options[val].field; + } else { + for (i = 0; i < set->n_fields; i++) + if (field_visible(set->fields[i])) { + opts = field_opts(set->fields[i]); + if ((opts & O_ACTIVE) == O_ACTIVE) { + set->cur_field = set->fields[i]; + break; + } + } + } + + set_current_field(set->form, set->cur_field); + widget = field_userptr(set->cur_field); + widget_focus_change(widget, set->cur_field, true); + if (set->widget_focus) + set->widget_focus(widget, set->widget_focus_arg); + + /* Update active array */ + rem = sizeof(int) * (subset->n_active - idx - 1); + val = subset->active[idx]; + field_set_visible(subset->options[val].field, false); + if (rem) + memmove(&subset->active[idx], &subset->active[idx + 1], rem); + subset->n_active--; + subset->active = talloc_realloc(subset, subset->active, + int, subset->n_active); + + subset->widget.height = subset->n_active; +} + +static bool subset_process_key(struct nc_widget *w, FORM *form, int key) +{ + struct nc_widget_subset *subset = to_subset(w); + int i, val, opt_idx = -1; + FIELD *field; + + if (key != '-' && key != '+' && key != KEY_DC && key != KEY_BACKSPACE) + return false; + + field = current_field(form); + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + if (subset->options[val].field == field) { + opt_idx = i; + break; + } + } + + if (opt_idx < 0) + return false; + + if (key == KEY_DC || key == KEY_BACKSPACE) + subset_delete_active(subset, opt_idx); + + if (key == '-') { + if (opt_idx == 0) + return true; + + val = subset->active[opt_idx]; + subset->active[opt_idx] = subset->active[opt_idx - 1]; + subset->active[opt_idx - 1] = val; + } + + if (key == '+') { + if (opt_idx >= subset->n_active - 1) + return true; + + val = subset->active[opt_idx]; + subset->active[opt_idx] = subset->active[opt_idx + 1]; + subset->active[opt_idx + 1] = val; + } + + subset_update_order(subset); + + if (subset->on_change) + subset->on_change(subset->on_change_arg, 0); + + return true; +} + +static void subset_set_visible(struct nc_widget *widget, bool visible) +{ + struct nc_widget_subset *subset = to_subset(widget); + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + field_set_visible(subset->options[val].field, visible); + } +} + +static void subset_move(struct nc_widget *widget, int y, int x) +{ + struct nc_widget_subset *subset = to_subset(widget); + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + field_move(subset->options[val].field, y + i , x); + } +} + +static void subset_field_focus(struct nc_widget *widget, FIELD *field) +{ + struct nc_widget_subset *subset = to_subset(widget); + int i, val; + + for (i = 0; i < subset->n_active; i++) { + val = subset->active[i]; + if (field == subset->options[val].field) { + widget->focus_y = i; + return; + } + } +} + +static int subset_destructor(void *ptr) +{ + struct nc_widget_subset *subset = ptr; + int i; + + for (i = 0; i < subset->n_options; i++) + free_field(subset->options[i].field); + + return 0; +} + +struct nc_widget_subset *widget_new_subset(struct nc_widgetset *set, + int y, int x, int len, void *screen_cb) +{ + struct nc_widget_subset *subset; + + subset = talloc_zero(set, struct nc_widget_subset); + subset->widget.width = len; + subset->widget.height = 0; + subset->widget.x = x; + subset->widget.y = y; + subset->widget.process_key = subset_process_key; + subset->widget.set_visible = subset_set_visible; + subset->widget.move = subset_move; + subset->widget.field_focus = subset_field_focus; + subset->widget.focussed_attr = A_REVERSE; + subset->widget.unfocussed_attr = A_NORMAL; + subset->top = y; + subset->left = x; + subset->size = len; + subset->set = set; + subset->n_active = subset->n_options = 0; + subset->active = NULL; + subset->options = NULL; + subset->screen_cb = screen_cb; + + talloc_set_destructor(subset, subset_destructor); + + return subset; +} + +void widget_subset_add_option(struct nc_widget_subset *subset, const char *text) +{ + FIELD *f; + int i; + + i = subset->n_options++; + subset->options = talloc_realloc(subset, subset->options, + struct subset_option, i + 1); + + subset->options[i].str = talloc_strdup(subset->options, text); + + subset->options[i].field = f = new_field(1, subset->size, subset->top + i, + subset->left, 0, 0); + + field_opts_off(f, O_WRAP | O_EDIT); + set_field_userptr(f, &subset->widget); + set_field_buffer(f, 0, subset->options[i].str); + field_set_visible(f, false); + widgetset_add_field(subset->set, f); +} + +void widget_subset_make_active(struct nc_widget_subset *subset, int idx) +{ + int i; + + for (i = 0; i < subset->n_active; i++) + if (subset->active[i] == idx) { + pb_debug("%s: Index %d already active\n", __func__, idx); + return; + } + + i = subset->n_active++; + subset->widget.height = subset->n_active; + subset->active = talloc_realloc(subset, subset->active, + int, i + 1); + subset->active[i] = idx; + + subset_update_order(subset); +} + +int widget_subset_get_order(void *ctx, unsigned int **order, + struct nc_widget_subset *subset) +{ + unsigned int *buf = talloc_array(ctx, unsigned int, subset->n_active); + int i; + + for (i = 0; i < subset->n_active; i++) + buf[i] = subset->active[i]; + + *order = buf; + return i; +} + +void widget_subset_show_inactive(struct nc_widget_subset *subset, + struct nc_widget_select *select) +{ + bool active = false, first = true; + int i, j; + + for (i = 0; i < subset->n_options; i++) { + active = false; + for (j = 0; j < subset->n_active; j++) + if (subset->active[j] == i) + active = true; + + if (active) + continue; + + widget_select_add_option(select, i, + subset->options[i].str, first); + if (first) + first = false; + } +} + +int widget_subset_n_inactive(struct nc_widget_subset *subset) +{ + return subset->n_options - subset->n_active; +} + +int widget_subset_height(struct nc_widget_subset *subset) +{ + return subset->n_active; +} + +void widget_subset_on_change(struct nc_widget_subset *subset, + void (*on_change)(void *, int), void *arg) +{ + subset->on_change = on_change; + subset->on_change_arg = arg; +} + +void widget_subset_drop_options(struct nc_widget_subset *subset) +{ + struct nc_widgetset *set = subset->set; + int i; + + for (i = 0; i < subset->n_options; i++) { + FIELD *field = subset->options[i].field; + widgetset_remove_field(set, field); + if (field == set->cur_field) + set->cur_field = NULL; + free_field(subset->options[i].field); + } + + talloc_free(subset->options); + talloc_free(subset->active); + subset->options = NULL; + subset->active = NULL; + subset->n_options = 0; + subset->n_active = 0; + subset->widget.height = 0; + subset->widget.focus_y = 0; +} + +void widget_subset_clear_active(struct nc_widget_subset *subset) +{ + int i; + + for (i = 0; i < subset->n_options; i++) + field_set_visible(subset->options[i].field, false); + + talloc_free(subset->active); + subset->active = NULL; + subset->n_active = 0; + subset->widget.height = 0; + subset->widget.focus_y = 0; +} + +void widget_subset_callback(void *arg, + struct nc_widget_subset *subset, int idx) +{ + subset->screen_cb(arg, subset, idx); +} + static void select_option_change(struct select_option *opt, bool selected) { const char *str; @@ -358,13 +793,8 @@ static bool select_process_key(struct nc_widget *w, FORM *form, int key) int i, new_idx; FIELD *field; - switch (key) { - case ' ': - case KEY_ENTER: - break; - default: + if (!key_is_select(key)) return false; - } field = current_field(form); new_opt = NULL; @@ -408,10 +838,27 @@ static void select_set_visible(struct nc_widget *widget, bool visible) static void select_move(struct nc_widget *widget, int y, int x) { struct nc_widget_select *select = to_select(widget); - int i; + int i, cur = 0; - for (i = 0; i < select->n_options; i++) - field_move(select->options[i].field, y + i, x); + for (i = 0; i < select->n_options; i++) { + field_move(select->options[i].field, y + cur, x); + cur += select->options[i].lines; + } +} + +static void select_field_focus(struct nc_widget *widget, FIELD *field) +{ + struct nc_widget_select *select = to_select(widget); + int i, cur = 0; + + for (i = 0; i < select->n_options; i++) { + if (field != select->options[i].field) { + cur += select->options[i].lines; + continue; + } + widget->focus_y = cur; + return; + } } static int select_destructor(void *ptr) @@ -438,6 +885,7 @@ struct nc_widget_select *widget_new_select(struct nc_widgetset *set, select->widget.process_key = select_process_key; select->widget.set_visible = select_set_visible; select->widget.move = select_move; + select->widget.field_focus = select_field_focus; select->widget.focussed_attr = A_REVERSE; select->widget.unfocussed_attr = A_NORMAL; select->top = y; @@ -450,10 +898,47 @@ struct nc_widget_select *widget_new_select(struct nc_widgetset *set, return select; } +static int widget_select_fold_cb(void *arg, const char *buf, int len) +{ + struct nc_widget_select *select = arg; + char *line, *newstr, *padbuf = NULL; + int i, pad; + + if (!len) + return 0; + + line = talloc_strndup(select->options, buf, len); + + i = select->n_options - 1; + pad = max(0, select->widget.width - strncols(line)); + + if (pad) { + padbuf = talloc_array(select->options, char, pad + 1); + memset(padbuf, ' ', pad); + padbuf[pad] = '\0'; + } + + if (select->options[i].str) + newstr = talloc_asprintf_append(select->options[i].str, + "%s%s", line, + pad ? padbuf : ""); + else + newstr = talloc_asprintf(select->options, "%s%s", line, + pad ? padbuf : ""); + + select->options[i].str = newstr; + select->options[i].lines++; + + talloc_free(padbuf); + talloc_free(line); + return 0; +} + void widget_select_add_option(struct nc_widget_select *select, int value, const char *text, bool selected) { const char *str; + char *full_text; FIELD *f; int i; @@ -472,33 +957,41 @@ void widget_select_add_option(struct nc_widget_select *select, int value, str = select_unselected_str; i = select->n_options++; - select->widget.height = select->n_options; - select->options = talloc_realloc(select, select->options, struct select_option, i + 2); select->options[i].val = value; - select->options[i].str = talloc_asprintf(select->options, - "%s %s", str, text); + select->options[i].lines = 0; + select->options[i].str = NULL; - select->options[i].field = f = new_field(1, select->size, - select->top + i, - select->left, 0, 0); + full_text = talloc_asprintf(select->options, "%s %s", str, text); + fold_text(full_text, select->widget.width, + widget_select_fold_cb, select); - field_opts_off(f, O_WRAP | O_EDIT); + select->options[i].field = f = new_field(select->options[i].lines, + select->size, + select->top + select->widget.height, + select->left, 0, 0); + + select->widget.height += select->options[i].lines; + + field_opts_off(f, O_EDIT); set_field_userptr(f, &select->widget); set_field_buffer(f, 0, select->options[i].str); widgetset_add_field(select->set, f); + talloc_free(full_text); } int widget_select_get_value(struct nc_widget_select *select) { + if (!select->n_options) + return -1; return select->options[select->selected_option].val; } int widget_select_height(struct nc_widget_select *select) { - return select->n_options; + return select->widget.height; } void widget_select_on_change(struct nc_widget_select *select, @@ -508,6 +1001,27 @@ void widget_select_on_change(struct nc_widget_select *select, select->on_change_arg = arg; } +void widget_select_drop_options(struct nc_widget_select *select) +{ + struct nc_widgetset *set = select->set; + int i; + + for (i = 0; i < select->n_options; i++) { + FIELD *field = select->options[i].field; + widgetset_remove_field(set, field); + if (field == set->cur_field) + set->cur_field = NULL; + free_field(select->options[i].field); + } + + talloc_free(select->options); + select->options = NULL; + select->n_options = 0; + select->widget.height = 0; + select->widget.focus_y = 0; + +} + static bool button_process_key(struct nc_widget *widget, FORM *form __attribute__((unused)), int key) { @@ -516,15 +1030,11 @@ static bool button_process_key(struct nc_widget *widget, if (!button->click) return false; - switch (key) { - case ' ': - case '\r': - case '\n': - button->click(button->arg); - return true; - } + if (!key_is_select(key)) + return false; - return false; + button->click(button->arg); + return true; } static int button_destructor(void *ptr) @@ -539,16 +1049,18 @@ struct nc_widget_button *widget_new_button(struct nc_widgetset *set, void (*click)(void *), void *arg) { struct nc_widget_button *button; + int idx, len, pad1, pad2, bufsz; char *text; FIELD *f; - int idx, len; + + int field_size = size + 2; button = talloc_zero(set, struct nc_widget_button); button->widget.height = 1; - button->widget.width = size; + button->widget.width = field_size; button->widget.x = x; button->widget.y = y; - button->widget.field = f = new_field(1, size + 2, y, x, 0, 0); + button->widget.field = f = new_field(1, field_size, y, x, 0, 0); button->widget.process_key = button_process_key; button->widget.focussed_attr = A_REVERSE; button->widget.unfocussed_attr = A_NORMAL; @@ -558,17 +1070,28 @@ struct nc_widget_button *widget_new_button(struct nc_widgetset *set, field_opts_off(f, O_EDIT); set_field_userptr(f, &button->widget); - /* center str in a size-char buffer, but don't overrun */ - len = strlen(str); - len = min(len, size); - idx = (size - len) / 2; + /* Center str in the field. This depends on the number of columns used + * by the string, not the number of chars in str */ + len = strncols(str); + if (len <= size) { + idx = (field_size - len) / 2; + } else { + idx = 1; + pb_log("Warning: '%s' %d columns wide " + "but button is %d columns wide\n", + str, len, size); + } + + pad1 = max(idx - 1, 0); + pad2 = max(size - len - pad1, 0); + bufsz = 1 + pad1 + strlen(str) + pad2 + 2; - text = talloc_array(button, char, size + 3); - memset(text, ' ', size + 2); - memcpy(text + idx + 1, str, len); + text = talloc_array(button, char, bufsz); + memset(text, ' ', bufsz); + memcpy(text + idx, str, strlen(str)); text[0] = '['; - text[size + 1] = ']'; - text[size + 2] = '\0'; + text[bufsz - 2] = ']'; + text[bufsz - 1] = '\0'; set_field_buffer(f, 0, text); @@ -578,7 +1101,7 @@ struct nc_widget_button *widget_new_button(struct nc_widgetset *set, return button; } -static void widget_focus_change(struct nc_widget *widget, FIELD *field, +void widget_focus_change(struct nc_widget *widget, FIELD *field, bool focussed) { int attr = focussed ? widget->focussed_attr : widget->unfocussed_attr; @@ -588,53 +1111,78 @@ static void widget_focus_change(struct nc_widget *widget, FIELD *field, bool widgetset_process_key(struct nc_widgetset *set, int key) { struct nc_widget *widget; - FIELD *field; + FIELD *field, *tmp; int req = 0; + bool tab; field = current_field(set->form); assert(field); + widget = field_userptr(field); + + if (widget->process_key) + if (widget->process_key(widget, set->form, key)) + return true; + + tab = false; + /* handle field change events */ switch (key) { case KEY_BTAB: + tab = true; + /* fall through */ case KEY_UP: - req = REQ_PREV_FIELD; + case KEY_LEFT: + req = REQ_SPREV_FIELD; break; case '\t': + tab = true; + /* fall through */ case KEY_DOWN: - req = REQ_NEXT_FIELD; + case KEY_RIGHT: + req = REQ_SNEXT_FIELD; break; case KEY_PPAGE: - req = REQ_FIRST_FIELD; + req = REQ_SFIRST_FIELD; break; case KEY_NPAGE: - req = REQ_LAST_FIELD; + req = REQ_SLAST_FIELD; break; } - widget = field_userptr(field); if (req) { widget_focus_change(widget, field, false); form_driver(set->form, req); - form_driver(set->form, REQ_END_FIELD); + + /* if we're doing a tabbed-field-change, skip until we + * see the next widget */ + tmp = field; field = current_field(set->form); + + for (; tab && tmp != field && field_userptr(field) == widget;) { + form_driver(set->form, req); + field = current_field(set->form); + } + + form_driver(set->form, REQ_END_FIELD); widget = field_userptr(field); widget_focus_change(widget, field, true); + if (widget->field_focus) + widget->field_focus(widget, field); if (set->widget_focus) set->widget_focus(widget, set->widget_focus_arg); return true; } - if (!widget->process_key) - return false; - - return widget->process_key(widget, set->form, key); + return false; } static int widgetset_destructor(void *ptr) { struct nc_widgetset *set = ptr; free_form(set->form); + if (set->ipv4_multi_type) + free_fieldtype(set->ipv4_multi_type); return 0; } @@ -652,6 +1200,13 @@ struct nc_widgetset *widgetset_create(void *ctx, WINDOW *main, WINDOW *sub) return set; } +void widgetset_set_windows(struct nc_widgetset *set, + WINDOW *main, WINDOW *sub) +{ + set->mainwin = main; + set->subwin = sub; +} + void widgetset_set_widget_focus(struct nc_widgetset *set, widget_focus_cb cb, void *arg) { @@ -670,10 +1225,8 @@ void widgetset_post(struct nc_widgetset *set) post_form(set->form); form_driver(set->form, REQ_END_FIELD); - if (set->cur_field) { + if (set->cur_field) set_current_field(set->form, set->cur_field); - field = set->cur_field; - } field = current_field(set->form); widget = field_userptr(field); @@ -703,6 +1256,23 @@ static void widgetset_add_field(struct nc_widgetset *set, FIELD *field) set->fields[set->n_fields] = NULL; } +static void widgetset_remove_field(struct nc_widgetset *set, FIELD *field) +{ + int i; + + for (i = 0; i < set->n_fields; i++) { + if (set->fields[i] == field) + break; + } + + if (i == set->n_fields) + return; + + memmove(&set->fields[i], &set->fields[i+i], + (set->n_fields - i) * sizeof(set->fields[i])); + set->n_fields--; +} + #define DECLARE_BASEFN(type) \ struct nc_widget *widget_ ## type ## _base \ (struct nc_widget_ ## type *w) \ @@ -710,6 +1280,7 @@ static void widgetset_add_field(struct nc_widgetset *set, FIELD *field) DECLARE_BASEFN(textbox); DECLARE_BASEFN(checkbox); +DECLARE_BASEFN(subset); DECLARE_BASEFN(select); DECLARE_BASEFN(label); DECLARE_BASEFN(button); @@ -731,6 +1302,10 @@ void widget_move(struct nc_widget *widget, int y, int x) widget->x = x; widget->y = y; + + if (x + widget->width > COLS) + pb_debug("%s: Widget at %d,%d runs over pad! (%d)", __func__, + y, x, x + widget->width); } int widget_height(struct nc_widget *widget) @@ -753,3 +1328,8 @@ int widget_y(struct nc_widget *widget) return widget->y; } +int widget_focus_y(struct nc_widget *widget) +{ + return widget->focus_y; +} +