* 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 <linux/input.h> /* This must be included before ncurses.h */
#if defined HAVE_NCURSESW_CURSES_H
#include <types/types.h>
#include <log/log.h>
#include <util/util.h>
+#include <i18n/i18n.h>
+#include <fold/fold.h>
-#include "config.h"
#include "nc-cui.h"
#include "nc-widgets.h"
#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 = "[ ]";
void (*widget_focus)(struct nc_widget *, void *);
void *widget_focus_arg;
FIELD *cur_field;
+
+ /* custom validators */
+ FIELDTYPE *ipv4_multi_type;
};
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;
};
};
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;
};
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 key_is_minus(int key)
+{
+ return key == 055;
+}
+
+static bool key_is_left(int key)
+{
+ return key == KEY_LEFT;
+}
+
+static bool key_is_right(int key)
+{
+ return key == KEY_RIGHT;
+}
static bool process_key_nop(struct nc_widget *widget __attribute__((unused)),
FORM *form __attribute((unused)),
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);
{
struct nc_widget_checkbox *checkbox = to_checkbox(widget);
- if (key != ' ')
+ if (!key_is_select(key))
return false;
checkbox->checked = !checkbox->checked;
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;
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;
+
+ /* 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])) {
+ 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_is_minus(key) && !key_is_left(key) && !key_is_right(key))
+ 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_is_minus(key))
+ subset_delete_active(subset, opt_idx);
+
+ if (key_is_left(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_is_right(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;
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;
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)
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;
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;
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,
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)
{
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)
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;
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);
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;
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);
+ 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;
}
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;
{
struct nc_widgetset *set = ptr;
free_form(set->form);
+ if (set->ipv4_multi_type)
+ free_fieldtype(set->ipv4_multi_type);
return 0;
}
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)
{
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);
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) \
DECLARE_BASEFN(textbox);
DECLARE_BASEFN(checkbox);
+DECLARE_BASEFN(subset);
DECLARE_BASEFN(select);
DECLARE_BASEFN(label);
DECLARE_BASEFN(button);
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)
return widget->y;
}
+int widget_focus_y(struct nc_widget *widget)
+{
+ return widget->focus_y;
+}
+