ui/ncurses: Warn if widget runs over horizontal pad width
[petitboot] / ui / ncurses / nc-widgets.c
index 60f91007ce94f280be21d01f59503cdfb3868d09..f82192939dd968d1dfd4ce2f889cbb382b1d6d8b 100644 (file)
@@ -77,6 +77,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 {
@@ -105,6 +108,7 @@ struct nc_widget_checkbox {
 };
 
 struct nc_widget_textbox {
+       struct nc_widgetset     *set;
        struct nc_widget        widget;
 };
 
@@ -328,6 +332,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;
@@ -348,6 +353,67 @@ 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 select_option_change(struct select_option *opt, bool selected)
 {
        const char *str;
@@ -623,19 +689,26 @@ 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);
 
+       tab = false;
+
        /* handle field change events */
        switch (key) {
        case KEY_BTAB:
+               tab = true;
+               /* fall through */
        case KEY_UP:
                req = REQ_PREV_FIELD;
                break;
        case '\t':
+               tab = true;
+               /* fall through */
        case KEY_DOWN:
                req = REQ_NEXT_FIELD;
                break;
@@ -651,8 +724,18 @@ bool widgetset_process_key(struct nc_widgetset *set, int key)
        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)
@@ -672,6 +755,8 @@ 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;
 }
 
@@ -792,6 +877,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)