diff options
Diffstat (limited to 'ui/ncurses')
-rw-r--r-- | ui/ncurses/Makefile.am | 4 | ||||
-rw-r--r-- | ui/ncurses/nc-config-help.c | 27 | ||||
-rw-r--r-- | ui/ncurses/nc-config.c | 370 | ||||
-rw-r--r-- | ui/ncurses/nc-cui.c | 64 | ||||
-rw-r--r-- | ui/ncurses/nc-cui.h | 3 | ||||
-rw-r--r-- | ui/ncurses/nc-scr.h | 1 | ||||
-rw-r--r-- | ui/ncurses/nc-subset.c | 289 | ||||
-rw-r--r-- | ui/ncurses/nc-subset.h | 36 | ||||
-rw-r--r-- | ui/ncurses/nc-widgets.c | 373 | ||||
-rw-r--r-- | ui/ncurses/nc-widgets.h | 19 |
10 files changed, 1056 insertions, 130 deletions
diff --git a/ui/ncurses/Makefile.am b/ui/ncurses/Makefile.am index 9a4e1e4..265ae69 100644 --- a/ui/ncurses/Makefile.am +++ b/ui/ncurses/Makefile.am @@ -47,7 +47,9 @@ ui_ncurses_libpbnc_la_SOURCES = \ ui/ncurses/nc-widgets.h \ ui/ncurses/nc-add-url.c \ ui/ncurses/nc-add-url.h \ - ui/ncurses/nc-add-url-help.c + ui/ncurses/nc-add-url-help.c \ + ui/ncurses/nc-subset.c \ + ui/ncurses/nc-subset.h sbin_PROGRAMS += ui/ncurses/petitboot-nc diff --git a/ui/ncurses/nc-config-help.c b/ui/ncurses/nc-config-help.c index c977d1a..22ced3c 100644 --- a/ui/ncurses/nc-config-help.c +++ b/ui/ncurses/nc-config-help.c @@ -1,23 +1,26 @@ #include "nc-helpscreen.h" struct help_text config_help_text = define_help_text("\ -Autoboot: There are three possible options for automatic-boot hehaviour:\n" +Autoboot: Specify which devices to autoboot from.\n" "\n" -"Don't autoboot: boot options will be listed in the petitboot menu, but none \ -will be booted automatically. User interaction will be required to continue \ -past the petitboot menu. Use this option if you want the machine to wait for \ -an explicit boot selection, or want to interact with petitboot before \ -booting the system\n" +"By selecting the 'Add Device' button new devices can be added to the autoboot \ +list, either by UUID, MAC address, or device type. Once added to the boot \ +order, the priority of devices can be changed with the 'left' and 'right' keys \ +Devices can be individually removed from the boot order with the minus key. \ +Use this option if you have multiple operating system images installed.\n" "\n" -"Autoboot from any disk/network device: any boot option that is marked as a \ -default (by bootloader configuration) will be booted automatically after a \ +"To autoboot from any device, select the 'Clear & Boot Any' button. \ +In this case, any boot option that is marked as a default \ +(by bootloader configuration) will be booted automatically after a \ timeout. Use this option if you want to quickly boot your system without \ changing any boot option settings. This is the typical configuration.\n" "\n" -"Only autoboot from a specific disk/network device: only boot options \ -from a single device (specifed here) will be booted automatically after a \ -timeout. Use this option if you have multiple operating system images \ -installed.\n" +"To disable autoboot, select the 'Clear' button, which will clear the boot \ +order. \ +With autoboot disabled, user interaction will be required to continue past \ +the petitboot menu. Use this option if you want the machine to wait for an \ +explicit boot selection, or want to interact with petitboot before booting \ +the system\n" "\n" "Timeout: Specify the length of time, in seconds, that the main menu will be \ displayed before the default boot option is started. This option is only \ diff --git a/ui/ncurses/nc-config.c b/ui/ncurses/nc-config.c index c45df34..911559d 100644 --- a/ui/ncurses/nc-config.c +++ b/ui/ncurses/nc-config.c @@ -33,16 +33,10 @@ #include "nc-config.h" #include "nc-widgets.h" -#define N_FIELDS 26 +#define N_FIELDS 32 extern struct help_text config_help_text; -enum autoboot_type { - AUTOBOOT_ANY, - AUTOBOOT_ONE, - AUTOBOOT_DISABLED, -}; - enum net_conf_type { NET_CONF_TYPE_DHCP_ALL, NET_CONF_TYPE_DHCP_ONE, @@ -57,7 +51,9 @@ struct config_screen { bool exit; bool show_help; + bool show_subset; bool need_redraw; + void (*on_exit)(struct cui *); int scroll_y; @@ -67,16 +63,25 @@ struct config_screen { int network_config_y; enum net_conf_type net_conf_type; - enum autoboot_type autoboot_type; + + bool autoboot_enabled; + bool ipmi_override; struct { - struct nc_widget_select *autoboot_f; - struct nc_widget_label *autoboot_l; - struct nc_widget_select *boot_device_f; + struct nc_widget_label *boot_order_l; + struct nc_widget_subset *boot_order_f; + struct nc_widget_label *boot_empty_l; + struct nc_widget_button *boot_add_b; + struct nc_widget_button *boot_none_b; + struct nc_widget_button *boot_any_b; struct nc_widget_textbox *timeout_f; struct nc_widget_label *timeout_l; struct nc_widget_label *timeout_help_l; + struct nc_widget_label *ipmi_type_l; + struct nc_widget_label *ipmi_clear_l; + struct nc_widget_checkbox *ipmi_clear_cb; + struct nc_widget_label *network_l; struct nc_widget_select *network_f; @@ -151,7 +156,7 @@ static void config_screen_process_key(struct nc_scr *scr, int key) cui_show_help(screen->cui, _("System Configuration"), &config_help_text); - } else if (handled) { + } else if (handled && !screen->show_subset) { pad_refresh(screen); } } @@ -165,6 +170,7 @@ static void config_screen_resize(struct nc_scr *scr) static int config_screen_post(struct nc_scr *scr) { struct config_screen *screen = config_screen_from_scr(scr); + screen->show_subset = false; widgetset_post(screen->widgetset); nc_scr_frame_draw(scr); if (screen->need_redraw) { @@ -193,40 +199,48 @@ static int screen_process_form(struct config_screen *screen) const struct system_info *sysinfo = screen->cui->sysinfo; enum net_conf_type net_conf_type; struct interface_config *iface; - char *str, *end, *uuid; + char *str, *end; struct config *config; - int rc, idx; + int i, n_boot_opts, rc, idx; + unsigned int *order; + char mac[20]; config = config_copy(screen, screen->cui->config); - screen->autoboot_type = - widget_select_get_value(screen->widgets.autoboot_f); - - config->autoboot_enabled = screen->autoboot_type != AUTOBOOT_DISABLED; - - uuid = NULL; - if (screen->autoboot_type == AUTOBOOT_ONE) { - char mac[20]; - - /* if idx is -1 here, we have an unknown UUID selected. - * Otherwise, it's a blockdev index (idx <= n_blockdevs) or an - * interface index. - */ - idx = widget_select_get_value(screen->widgets.boot_device_f); - if (idx >= (int)sysinfo->n_blockdevs) { - struct interface_info *info = sysinfo-> - interfaces[idx - sysinfo->n_blockdevs]; - mac_str(info->hwaddr, info->hwaddr_size, - mac, sizeof(mac)); - uuid = mac; - } else if (idx != -1) { - uuid = sysinfo->blockdevs[idx]->uuid; + talloc_free(config->autoboot_opts); + config->n_autoboot_opts = 0; + + n_boot_opts = widget_subset_get_order(config, &order, + screen->widgets.boot_order_f); + + config->autoboot_enabled = n_boot_opts > 0; + + config->n_autoboot_opts = n_boot_opts; + config->autoboot_opts = talloc_array(config, struct autoboot_option, + n_boot_opts); + + for (i = 0; i < n_boot_opts; i++) { + if (order[i] < sysinfo->n_blockdevs) { + /* disk uuid */ + config->autoboot_opts[i].boot_type = BOOT_DEVICE_UUID; + config->autoboot_opts[i].uuid = talloc_strdup(config, + sysinfo->blockdevs[order[i]]->uuid); + } else if(order[i] < (sysinfo->n_blockdevs + sysinfo->n_interfaces)) { + /* net uuid */ + order[i] -= sysinfo->n_blockdevs; + config->autoboot_opts[i].boot_type = BOOT_DEVICE_UUID; + mac_str(sysinfo->interfaces[order[i]]->hwaddr, + sysinfo->interfaces[order[i]]->hwaddr_size, + mac, sizeof(mac)); + config->autoboot_opts[i].uuid = talloc_strdup(config, mac); + } else { + /* device type */ + order[i] -= (sysinfo->n_blockdevs + sysinfo->n_interfaces); + config->autoboot_opts[i].boot_type = BOOT_DEVICE_TYPE; + config->autoboot_opts[i].type = order[i]; } } - talloc_free(config->boot_device); - config->boot_device = uuid ? talloc_strdup(config, uuid) : NULL; - str = widget_textbox_get_value(screen->widgets.timeout_f); if (str) { unsigned long x; @@ -236,6 +250,11 @@ static int screen_process_form(struct config_screen *screen) config->autoboot_timeout_sec = x; } + if (screen->ipmi_override) + if (widget_checkbox_get_value(screen->widgets.ipmi_clear_cb)) + config->ipmi_bootdev = IPMI_BOOTDEV_INVALID; + + net_conf_type = widget_select_get_value(screen->widgets.network_f); /* if we don't have any network interfaces, prevent per-interface @@ -367,37 +386,75 @@ static void config_screen_layout_widgets(struct config_screen *screen) help_x = screen->field_x + 2 + widget_width(widget_textbox_base(screen->widgets.dns_f)); - y += layout_pair(screen, y, screen->widgets.autoboot_l, - widget_select_base(screen->widgets.autoboot_f)); + y += 1; + + wl = widget_label_base(screen->widgets.boot_order_l); + widget_set_visible(wl, true); + widget_move(wl, y, screen->label_x); - wf = widget_select_base(screen->widgets.boot_device_f); - if (screen->autoboot_type == AUTOBOOT_ONE) { + wf = widget_subset_base(screen->widgets.boot_order_f); + widget_move(wf, y, screen->field_x); + wl = widget_label_base(screen->widgets.boot_empty_l); + widget_move(wl, y, screen->field_x); + + if (widget_subset_height(screen->widgets.boot_order_f)) { widget_set_visible(wf, true); - widget_move(wf, y, screen->field_x + 3); + widget_set_visible(wl, false); y += widget_height(wf); } else { + widget_set_visible(wl, true); widget_set_visible(wf, false); + y += 1; } y += 1; + widget_move(widget_button_base(screen->widgets.boot_add_b), + y, screen->field_x); + widget_move(widget_button_base(screen->widgets.boot_any_b), + y, screen->field_x + 12); + widget_move(widget_button_base(screen->widgets.boot_none_b), + y, screen->field_x + 30); + + wf = widget_button_base(screen->widgets.boot_add_b); + if (widget_subset_n_inactive(screen->widgets.boot_order_f)) + widget_set_visible(wf, true); + else + widget_set_visible(wf, false); + + y += 2; + wf = widget_textbox_base(screen->widgets.timeout_f); wl = widget_label_base(screen->widgets.timeout_l); wh = widget_label_base(screen->widgets.timeout_help_l); - if (screen->autoboot_type != AUTOBOOT_DISABLED) { - widget_set_visible(wl, true); - widget_set_visible(wf, true); - widget_set_visible(wh, true); + widget_set_visible(wl, screen->autoboot_enabled); + widget_set_visible(wf, screen->autoboot_enabled); + widget_set_visible(wh, screen->autoboot_enabled); + if (screen->autoboot_enabled) { + widget_set_visible(wh, screen->autoboot_enabled); widget_move(wl, y, screen->label_x); widget_move(wf, y, screen->field_x); widget_move(wh, y, screen->field_x + widget_width(wf) + 1); y += 2; - } else { - widget_set_visible(wl, false); - widget_set_visible(wf, false); - widget_set_visible(wh, false); } + if (screen->ipmi_override) { + wl = widget_label_base(screen->widgets.ipmi_type_l); + widget_set_visible(wl, true); + widget_move(wl, y, screen->label_x); + y += 1; + + wl = widget_label_base(screen->widgets.ipmi_clear_l); + wf = widget_checkbox_base(screen->widgets.ipmi_clear_cb); + widget_set_visible(wl, true); + widget_set_visible(wf, true); + widget_move(wl, y, screen->label_x); + widget_move(wf, y, screen->field_x); + y += 1; + } + + y += 2; + y += layout_pair(screen, y, screen->widgets.network_l, widget_select_base(screen->widgets.network_f)); @@ -500,15 +557,69 @@ static void config_screen_network_change(void *arg, int value) widgetset_post(screen->widgetset); } -static void config_screen_autoboot_change(void *arg, int value) +static void config_screen_boot_order_change(void *arg, int value) +{ + (void)value; + struct config_screen *screen = arg; + widgetset_unpost(screen->widgetset); + config_screen_layout_widgets(screen); + widgetset_post(screen->widgetset); +} + +static void config_screen_add_device(void *arg) +{ + struct config_screen *screen = arg; + + screen->show_subset = true; + cui_show_subset(screen->cui, _("Select an option"), + screen->widgets.boot_order_f); +} + +static void config_screen_autoboot_none(void *arg) +{ + struct config_screen *screen = arg; + struct nc_widget_subset *subset = screen->widgets.boot_order_f; + + widget_subset_clear_active(subset); + screen->autoboot_enabled = false; + + widgetset_unpost(screen->widgetset); + config_screen_layout_widgets(screen); + widgetset_post(screen->widgetset); +} + +static void config_screen_autoboot_any(void *arg) { struct config_screen *screen = arg; - screen->autoboot_type = value; + const struct system_info *sysinfo = screen->cui->sysinfo; + struct nc_widget_subset *subset = screen->widgets.boot_order_f; + int idx; + + widget_subset_clear_active(subset); + + idx = sysinfo->n_blockdevs + sysinfo->n_interfaces + DEVICE_TYPE_ANY; + + widget_subset_make_active(screen->widgets.boot_order_f, idx); + + screen->autoboot_enabled = true; + widgetset_unpost(screen->widgetset); config_screen_layout_widgets(screen); widgetset_post(screen->widgetset); } +static void config_screen_update_subset(void *arg, + struct nc_widget_subset *subset, int idx) +{ + struct config_screen *screen = arg; + + if (idx >= 0) + widget_subset_make_active(subset, idx); + if (!screen->autoboot_enabled) + screen->autoboot_enabled = true; + config_screen_layout_widgets(screen); +} + static struct interface_config *first_active_interface( const struct config *config) { @@ -550,6 +661,31 @@ static void config_screen_setup_empty(struct config_screen *screen) cancel_click, screen); } +static int find_autoboot_idx(const struct system_info *sysinfo, + struct autoboot_option *opt) +{ + unsigned int i; + + if (opt->boot_type == BOOT_DEVICE_TYPE) + return sysinfo->n_blockdevs + sysinfo->n_interfaces + opt->type; + + for (i = 0; i < sysinfo->n_blockdevs; i++) { + if (!strcmp(sysinfo->blockdevs[i]->uuid, opt->uuid)) + return i; + } + + for (i = 0; i < sysinfo->n_interfaces; i++) { + struct interface_info *info = sysinfo->interfaces[i]; + char mac[20]; + + mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac)); + + if (!strcmp(mac, opt->uuid)) + return sysinfo->n_blockdevs + i; + } + + return -1; +} static void config_screen_setup_widgets(struct config_screen *screen, const struct config *config, @@ -560,7 +696,6 @@ static void config_screen_setup_widgets(struct config_screen *screen, char *str, *ip, *mask, *gw; enum net_conf_type type; unsigned int i; - bool found; build_assert(sizeof(screen->widgets) / sizeof(struct widget *) == N_FIELDS); @@ -568,81 +703,84 @@ static void config_screen_setup_widgets(struct config_screen *screen, type = screen->net_conf_type; ifcfg = first_active_interface(config); - screen->widgets.autoboot_l = widget_new_label(set, 0, 0, - _("Autoboot:")); - screen->widgets.autoboot_f = widget_new_select(set, 0, 0, 55); - - widget_select_on_change(screen->widgets.autoboot_f, - config_screen_autoboot_change, screen); - - screen->widgets.boot_device_f = widget_new_select(set, 0, 0, 55); - - widget_select_add_option(screen->widgets.autoboot_f, - AUTOBOOT_DISABLED, - _("Don't autoboot"), - screen->autoboot_type == - AUTOBOOT_DISABLED); - widget_select_add_option(screen->widgets.autoboot_f, - AUTOBOOT_ANY, - _("Autoboot from any " - "disk/network device"), - screen->autoboot_type == - AUTOBOOT_ANY); - widget_select_add_option(screen->widgets.autoboot_f, - AUTOBOOT_ONE, - _("Only autoboot from a specific " - "disk/network device"), - screen->autoboot_type == - AUTOBOOT_ONE); - - found = false; + screen->widgets.boot_add_b = widget_new_button(set, 0, 0, 10, + _("Add Device"), config_screen_add_device, + screen); + + screen->widgets.boot_none_b = widget_new_button(set, 0, 0, 10, + _("Clear"), + config_screen_autoboot_none, screen); + + screen->widgets.boot_any_b = widget_new_button(set, 0, 0, 16, + _("Clear & Boot Any"), config_screen_autoboot_any, + screen); + + screen->widgets.boot_order_l = widget_new_label(set, 0, 0, + _("Boot order:")); + screen->widgets.boot_order_f = widget_new_subset(set, 0, 0, + COLS - screen->field_x, + config_screen_update_subset); + screen->widgets.boot_empty_l = widget_new_label(set, 0, 0, + _("(None)")); + + widget_subset_on_change(screen->widgets.boot_order_f, + config_screen_boot_order_change, screen); for (i = 0; i < sysinfo->n_blockdevs; i++) { struct blockdev_info *bd = sysinfo->blockdevs[i]; - bool selected; char *label; - selected = config->boot_device && - !strcmp(config->boot_device, bd->uuid); - if (selected) - found = true; - label = talloc_asprintf(screen, _("disk: %s [uuid: %s]"), bd->name, bd->uuid); - widget_select_add_option(screen->widgets.boot_device_f, i, - label, selected); + widget_subset_add_option(screen->widgets.boot_order_f, label); } for (i = 0; i < sysinfo->n_interfaces; i++) { struct interface_info *info = sysinfo->interfaces[i]; char *label, mac[20]; - bool selected; mac_str(info->hwaddr, info->hwaddr_size, mac, sizeof(mac)); - selected = config->boot_device && - !strcmp(config->boot_device, mac); - if (selected) - found = true; label = talloc_asprintf(screen, _("net: %s [mac: %s]"), info->name, mac); - widget_select_add_option(screen->widgets.boot_device_f, - i + sysinfo->n_blockdevs, - label, selected); + widget_subset_add_option(screen->widgets.boot_order_f, label); } - if (screen->autoboot_type == AUTOBOOT_ONE && !found) { + for (i = DEVICE_TYPE_NETWORK; i < DEVICE_TYPE_NETWORK + 4; i++) { char *label; - label = talloc_asprintf(screen, _("Unknown UUID: %s"), - config->boot_device); + if (i == DEVICE_TYPE_ANY) + label = talloc_asprintf(screen, _("Any Device")); + else + label = talloc_asprintf(screen, _("Any %s device"), + device_type_display_name(i)); - widget_select_add_option(screen->widgets.boot_device_f, -1, - label, true); + widget_subset_add_option(screen->widgets.boot_order_f, label); + } + + screen->autoboot_enabled = config->n_autoboot_opts; + for (i = 0; i < config->n_autoboot_opts; i++) { + struct autoboot_option *opt = &config->autoboot_opts[i]; + int idx; + + idx = find_autoboot_idx(sysinfo, opt); + + if (idx >= 0) { + widget_subset_make_active(screen->widgets.boot_order_f, + idx); + } else { + if (opt->boot_type == BOOT_DEVICE_TYPE) + pb_log("%s: Unknown autoboot option: %d\n", + __func__, opt->type); + else + pb_log("%s: Unknown autoboot UUID: %s\n", + __func__, opt->uuid); + } } + str = talloc_asprintf(screen, "%d", config->autoboot_timeout_sec); screen->widgets.timeout_l = widget_new_label(set, 0, 0, _("Timeout:")); screen->widgets.timeout_f = widget_new_textbox(set, 0, 0, 5, str); @@ -652,6 +790,21 @@ static void config_screen_setup_widgets(struct config_screen *screen, widget_textbox_set_fixed_size(screen->widgets.timeout_f); widget_textbox_set_validator_integer(screen->widgets.timeout_f, 0, 999); + if (config->ipmi_bootdev) { + char *label = talloc_asprintf(screen, + _("%s IPMI boot option: %s"), + config->ipmi_bootdev_persistent ? + "Persistent" : "Temporary", + ipmi_bootdev_display_name(config->ipmi_bootdev)); + screen->widgets.ipmi_type_l = widget_new_label(set, 0, 0, + label); + screen->widgets.ipmi_clear_l = widget_new_label(set, 0, 0, + _("Clear option:")); + screen->widgets.ipmi_clear_cb = widget_new_checkbox(set, 0, 0, + false); + screen->ipmi_override = true; + } + screen->widgets.network_l = widget_new_label(set, 0, 0, _("Network:")); screen->widgets.network_f = widget_new_select(set, 0, 0, 50); @@ -818,12 +971,6 @@ static void config_screen_draw(struct config_screen *screen, config_screen_setup_empty(screen); } else { screen->net_conf_type = find_net_conf_type(config); - if (!config->autoboot_enabled) - screen->autoboot_type = AUTOBOOT_DISABLED; - else - screen->autoboot_type = config->boot_device ? - AUTOBOOT_ONE : AUTOBOOT_ANY; - config_screen_setup_widgets(screen, config, sysinfo); config_screen_layout_widgets(screen); } @@ -868,6 +1015,9 @@ struct config_screen *config_screen_init(struct cui *cui, screen->label_x = 2; screen->field_x = 17; + screen->ipmi_override = false; + screen->show_subset = false; + screen->scr.frame.ltitle = talloc_strdup(screen, _("Petitboot System Configuration")); screen->scr.frame.rtitle = NULL; diff --git a/ui/ncurses/nc-cui.c b/ui/ncurses/nc-cui.c index 3f1e0a2..56e7653 100644 --- a/ui/ncurses/nc-cui.c +++ b/ui/ncurses/nc-cui.c @@ -41,6 +41,7 @@ #include "nc-sysinfo.h" #include "nc-lang.h" #include "nc-helpscreen.h" +#include "nc-subset.h" extern const struct help_text main_menu_help_text; @@ -69,6 +70,16 @@ static void cui_start(void) */ define_key("\x1b[Z", KEY_BTAB); + /* We'll define a few other keys too since they're commonly + * used for navigation but the escape character will cause + * Petitboot to exit if they're left undefined */ + define_key("\x1b\x5b\x35\x7e", KEY_PPAGE); + define_key("\x1b\x5b\x36\x7e", KEY_NPAGE); + define_key("\x1b\x4f\x48", KEY_HOME); + define_key("\x1b\x4f\x46", KEY_END); + define_key("OH", KEY_HOME); + define_key("OF", KEY_END); + while (getch() != ERR) /* flush stdin */ (void)0; } @@ -173,6 +184,7 @@ static void cui_boot_editor_on_exit(struct cui *cui, { struct pmenu *menu = cui->main; struct cui_opt_data *cod; + int idx, top, rows, cols; static int user_idx = 0; /* Was the edit cancelled? */ @@ -211,6 +223,22 @@ static void cui_boot_editor_on_exit(struct cui *cui, /* Re-attach the items array. */ set_menu_items(menu->ncm, menu->items); + + /* If our index is above the current top row, align + * us to the new top. Otherwise, align us to the new + * bottom */ + menu_format(cui->main->ncm, &rows, &cols); + top = top_row(cui->main->ncm); + idx = item_index(item->nci); + + if (top >= idx) + top = idx; + else + top = idx < rows ? 0 : idx - rows + 1; + + set_top_row(cui->main->ncm, top); + set_current_item(item->pmenu->ncm, item->nci); + nc_scr_post(&menu->scr); } else { cod = item->data; @@ -218,7 +246,6 @@ static void cui_boot_editor_on_exit(struct cui *cui, cod->bd = talloc_steal(cod, bd); - set_current_item(item->pmenu->ncm, item->nci); out: cui_set_current(cui, &cui->main->scr); talloc_free(cui->boot_editor); @@ -318,6 +345,29 @@ void cui_show_help(struct cui *cui, const char *title, cui_set_current(cui, help_screen_scr(cui->help_screen)); } +static void cui_subset_exit(struct cui *cui) +{ + cui_set_current(cui, subset_screen_return_scr(cui->subset_screen)); + talloc_free(cui->subset_screen); + cui->subset_screen = NULL; +} + +void cui_show_subset(struct cui *cui, const char *title, + void *arg) +{ + if (!cui->current) + return; + + if (cui->subset_screen) + return; + + cui->subset_screen = subset_screen_init(cui, cui->current, + title, arg, cui_subset_exit); + + if (cui->subset_screen) + cui_set_current(cui, subset_screen_scr(cui->subset_screen)); +} + /** * cui_set_current - Set the currently active screen and redraw it. */ @@ -528,7 +578,7 @@ static int cui_boot_option_add(struct device *dev, struct boot_option *opt, /* If our index is above the current top row, align * us to the new top. Otherwise, align us to the new * bottom */ - top = top < idx ? idx - rows : idx; + top = top < idx ? idx - rows + 1 : idx; set_top_row(cui->main->ncm, top); set_current_item(cui->main->ncm, selected); @@ -552,6 +602,7 @@ static void cui_device_remove(struct device *dev, void *arg) struct cui *cui = cui_from_arg(arg); struct boot_option *opt; unsigned int i; + int rows, cols, top, last; int result; pb_log("%s: %p %s\n", __func__, dev, dev->id); @@ -588,6 +639,15 @@ static void cui_device_remove(struct device *dev, void *arg) result = set_menu_items(cui->main->ncm, cui->main->items); + /* Move cursor to 'Exit' menu entry */ + menu_format(cui->main->ncm, &rows, &cols); + last = cui->main->item_count - 1; + set_current_item(cui->main->ncm, cui->main->items[last]); + if (!item_visible(cui->main->items[last])) { + top = last < rows ? 0 : last - rows + 1; + set_top_row(cui->main->ncm, top); + } + if (result) pb_log("%s: set_menu_items failed: %d\n", __func__, result); diff --git a/ui/ncurses/nc-cui.h b/ui/ncurses/nc-cui.h index 694ebd1..24a0761 100644 --- a/ui/ncurses/nc-cui.h +++ b/ui/ncurses/nc-cui.h @@ -63,6 +63,7 @@ struct cui { struct boot_editor *boot_editor; struct lang_screen *lang_screen; struct help_screen *help_screen; + struct subset_screen *subset_screen; struct pjs *pjs; void *platform_info; unsigned int default_item; @@ -81,6 +82,8 @@ void cui_show_config(struct cui *cui); void cui_show_lang(struct cui *cui); void cui_show_help(struct cui *cui, const char *title, const struct help_text *text); +void cui_show_subset(struct cui *cui, const char *title, + void *arg); void cui_show_add_url(struct cui *cui); int cui_send_config(struct cui *cui, struct config *config); int cui_send_url(struct cui *cui, char *url); diff --git a/ui/ncurses/nc-scr.h b/ui/ncurses/nc-scr.h index ed87517..be99b48 100644 --- a/ui/ncurses/nc-scr.h +++ b/ui/ncurses/nc-scr.h @@ -48,6 +48,7 @@ enum pb_nc_sig { pb_config_screen_sig = 666, pb_lang_screen_sig = 777, pb_add_url_screen_sig = 888, + pb_subset_screen_sig = 101, pb_removed_sig = -999, }; diff --git a/ui/ncurses/nc-subset.c b/ui/ncurses/nc-subset.c new file mode 100644 index 0000000..d90ed20 --- /dev/null +++ b/ui/ncurses/nc-subset.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <talloc/talloc.h> +#include <types/types.h> +#include <i18n/i18n.h> +#include <log/log.h> + +#include "nc-cui.h" +#include "nc-subset.h" + +#define N_FIELDS 3 + +struct subset_screen { + struct nc_scr scr; + struct cui *cui; + struct nc_scr *return_scr; + struct nc_widgetset *widgetset; + WINDOW *pad; + struct nc_widget_subset *options; + + bool exit; + void (*on_exit)(struct cui *); + + int scroll_y; + + int label_x; + int field_x; + + struct { + struct nc_widget_select *options_f; + + struct nc_widget_button *ok_b; + struct nc_widget_button *cancel_b; + } widgets; +}; + +struct nc_scr *subset_screen_return_scr(struct subset_screen *screen) +{ + return screen->return_scr; +} + +static struct subset_screen *subset_screen_from_scr(struct nc_scr *scr) +{ + struct subset_screen *subset_screen; + + assert(scr->sig == pb_subset_screen_sig); + subset_screen = (struct subset_screen *) + ((char *)scr - (size_t)&((struct subset_screen *)0)->scr); + assert(subset_screen->scr.sig == pb_subset_screen_sig); + return subset_screen; +} + +static void pad_refresh(struct subset_screen *screen) +{ + int y, x, rows, cols; + + getmaxyx(screen->scr.sub_ncw, rows, cols); + getbegyx(screen->scr.sub_ncw, y, x); + + prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols); +} + +static void subset_screen_process_key(struct nc_scr *scr, int key) +{ + struct subset_screen *screen = subset_screen_from_scr(scr); + bool handled; + + handled = widgetset_process_key(screen->widgetset, key); + + if (!handled) { + switch (key) { + case 'x': + case 27: /* esc */ + screen->exit = true; + break; + } + } + + if (screen->exit) + screen->on_exit(screen->cui); + else if (handled) + pad_refresh(screen); +} + +static int subset_screen_post(struct nc_scr *scr) +{ + struct subset_screen *screen = subset_screen_from_scr(scr); + widgetset_post(screen->widgetset); + nc_scr_frame_draw(scr); + redrawwin(scr->main_ncw); + wrefresh(scr->main_ncw); + pad_refresh(screen); + return 0; +} + +static int subset_screen_unpost(struct nc_scr *scr) +{ + struct subset_screen *screen = subset_screen_from_scr(scr); + widgetset_unpost(screen->widgetset); + return 0; +} + +struct nc_scr *subset_screen_scr(struct subset_screen *screen) +{ + return &screen->scr; +} + +static void ok_click(void *arg) +{ + struct subset_screen *screen = arg; + int idx = widget_select_get_value(screen->widgets.options_f); + widget_subset_callback(screen->return_scr, screen->options, idx); + screen->exit = true; +} + +static void cancel_click(void *arg) +{ + struct subset_screen *screen = arg; + screen->exit = true; +} + +static void subset_screen_layout_widgets(struct subset_screen *screen) +{ + int y = 2; + + /* select */ + widget_move(widget_select_base(screen->widgets.options_f), + y, screen->label_x); + y+= widget_height(widget_select_base(screen->widgets.options_f)); + + /* ok, cancel */ + y += 1; + + widget_move(widget_button_base(screen->widgets.ok_b), + y, screen->field_x + 12); + widget_move(widget_button_base(screen->widgets.cancel_b), + y, screen->field_x + 24); +} + +static void subset_screen_option_select(void *arg, int value) +{ + struct subset_screen *screen = arg; + widgetset_unpost(screen->widgetset); + subset_screen_layout_widgets(screen); + widgetset_post(screen->widgetset); + (void)value; +} + +static void subset_screen_setup_widgets(struct subset_screen *screen) +{ + struct nc_widgetset *set = screen->widgetset; + struct nc_widget_subset *subset = screen->options; + + build_assert(sizeof(screen->widgets) / sizeof(struct widget *) + == N_FIELDS); + + screen->widgets.options_f = widget_new_select(set, 0, 0, + COLS - (2 * screen->label_x)); + + widget_select_on_change(screen->widgets.options_f, + subset_screen_option_select, screen); + + widget_subset_show_inactive(subset, screen->widgets.options_f); + + screen->widgets.ok_b = widget_new_button(set, 0, 0, 10, _("OK"), + ok_click, screen); + screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10, _("Cancel"), + cancel_click, screen); +} + +static void subset_screen_widget_focus(struct nc_widget *widget, void *arg) +{ + struct subset_screen *screen = arg; + int w_y, s_max; + + w_y = widget_y(widget) + widget_focus_y(widget); + s_max = getmaxy(screen->scr.sub_ncw) - 1; + + if (w_y < screen->scroll_y) + screen->scroll_y = w_y; + + else if (w_y + screen->scroll_y + 1 > s_max) + screen->scroll_y = 1 + w_y - s_max; + + else + return; + + pad_refresh(screen); +} + +static void subset_screen_draw(struct subset_screen *screen) +{ + bool repost = false; + int height; + + /* Size of pad = top space + number of available options */ + height = 1 + N_FIELDS + widget_subset_n_inactive(screen->options); + + if (!screen->pad || getmaxy(screen->pad) < height) { + if (screen->pad) + delwin(screen->pad); + screen->pad = newpad(height, COLS); + } + + if (screen->widgetset) { + widgetset_unpost(screen->widgetset); + talloc_free(screen->widgetset); + repost = true; + } + + screen->widgetset = widgetset_create(screen, screen->scr.main_ncw, + screen->pad); + widgetset_set_widget_focus(screen->widgetset, + subset_screen_widget_focus, screen); + + subset_screen_setup_widgets(screen); + subset_screen_layout_widgets(screen); + + if (repost) + widgetset_post(screen->widgetset); +} + +static int subset_screen_destroy(void *arg) +{ + struct subset_screen *screen = arg; + if (screen->pad) + delwin(screen->pad); + return 0; +} + +struct subset_screen *subset_screen_init(struct cui *cui, + struct nc_scr *current_scr, + const char *title_suffix, + void *subset, + void (*on_exit)(struct cui *)) +{ + struct subset_screen *screen; + + screen = talloc_zero(cui, struct subset_screen); + talloc_set_destructor(screen, subset_screen_destroy); + + screen->cui = cui; + screen->on_exit = on_exit; + screen->options = (struct nc_widget_subset *) subset; + screen->label_x = 2; + screen->field_x = 22; + + screen->return_scr = current_scr; + + nc_scr_init(&screen->scr, pb_subset_screen_sig, 0, + cui, subset_screen_process_key, + subset_screen_post, subset_screen_unpost, + NULL); + + screen->scr.frame.ltitle = talloc_strdup(screen, + title_suffix); + screen->scr.frame.rtitle = NULL; + screen->scr.frame.help = talloc_strdup(screen, + _("tab=next, shift+tab=previous, x=exit")); + + scrollok(screen->scr.sub_ncw, true); + + subset_screen_draw(screen); + + return screen; +} diff --git a/ui/ncurses/nc-subset.h b/ui/ncurses/nc-subset.h new file mode 100644 index 0000000..e967866 --- /dev/null +++ b/ui/ncurses/nc-subset.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NC_SUBSET_H +#define _NC_SUBSET_H + +#include "nc-cui.h" +#include "nc-widgets.h" + +struct subset_screen; + +struct subset_screen *subset_screen_init(struct cui *cui, + struct nc_scr *current_scr, + const char *title_suffix, + void *subset, + void (*on_exit)(struct cui *)); + +struct nc_scr *subset_screen_scr(struct subset_screen *screen); +struct nc_scr *subset_screen_return_scr(struct subset_screen *screen); +void subset_screen_update(struct subset_screen *screen); + +#endif /* defined _NC_SUBSET_H */ diff --git a/ui/ncurses/nc-widgets.c b/ui/ncurses/nc-widgets.c index f821929..3daced1 100644 --- a/ui/ncurses/nc-widgets.c +++ b/ui/ncurses/nc-widgets.c @@ -61,6 +61,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 = "[ ]"; @@ -112,6 +113,24 @@ struct nc_widget_textbox { 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 { @@ -140,6 +159,21 @@ 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)), int key __attribute__((unused))) @@ -155,6 +189,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); @@ -414,6 +453,329 @@ void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox) 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; @@ -679,7 +1041,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; @@ -704,19 +1066,19 @@ bool widgetset_process_key(struct nc_widgetset *set, int key) tab = true; /* fall through */ case KEY_UP: - req = REQ_PREV_FIELD; + req = REQ_SPREV_FIELD; break; case '\t': tab = true; /* fall through */ case KEY_DOWN: - req = REQ_NEXT_FIELD; + 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; } @@ -856,6 +1218,7 @@ static void widgetset_remove_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); diff --git a/ui/ncurses/nc-widgets.h b/ui/ncurses/nc-widgets.h index 09c3b1f..4b67da7 100644 --- a/ui/ncurses/nc-widgets.h +++ b/ui/ncurses/nc-widgets.h @@ -29,6 +29,8 @@ struct nc_widget_checkbox *widget_new_checkbox(struct nc_widgetset *set, int y, int x, bool checked); struct nc_widget_textbox *widget_new_textbox(struct nc_widgetset *set, int y, int x, int len, char *str); +struct nc_widget_subset *widget_new_subset(struct nc_widgetset *set, + int y, int x, int len, void *screen_cb); struct nc_widget_select *widget_new_select(struct nc_widgetset *set, int y, int x, int len); struct nc_widget_button *widget_new_button(struct nc_widgetset *set, @@ -41,6 +43,12 @@ void widget_textbox_set_validator_integer(struct nc_widget_textbox *textbox, void widget_textbox_set_validator_ipv4(struct nc_widget_textbox *textbox); void widget_textbox_set_validator_ipv4_multi(struct nc_widget_textbox *textbox); +void widget_subset_add_option(struct nc_widget_subset *subset, const char *text); +void widget_subset_make_active(struct nc_widget_subset *subset, int idx); + +void widget_subset_on_change(struct nc_widget_subset *subset, + void (*on_change)(void *arg, int value), void *arg); + void widget_select_add_option(struct nc_widget_select *select, int value, const char *text, bool selected); @@ -49,6 +57,16 @@ void widget_select_on_change(struct nc_widget_select *select, char *widget_textbox_get_value(struct nc_widget_textbox *textbox); bool widget_checkbox_get_value(struct nc_widget_checkbox *checkbox); +int widget_subset_get_order(void *ctx, unsigned int **order, + struct nc_widget_subset *subset); +void widget_subset_show_inactive(struct nc_widget_subset *subset, + struct nc_widget_select *select); +int widget_subset_n_inactive(struct nc_widget_subset *subset); +int widget_subset_height(struct nc_widget_subset *subset); +void widget_subset_drop_options(struct nc_widget_subset *subset); +void widget_subset_clear_active(struct nc_widget_subset *subset); +void widget_subset_callback(void *arg, + struct nc_widget_subset *subset, int idx); int widget_select_get_value(struct nc_widget_select *select); int widget_select_height(struct nc_widget_select *select); void widget_select_drop_options(struct nc_widget_select *select); @@ -56,6 +74,7 @@ void widget_select_drop_options(struct nc_widget_select *select); /* generic widget API */ struct nc_widget *widget_textbox_base(struct nc_widget_textbox *textbox); struct nc_widget *widget_checkbox_base(struct nc_widget_checkbox *checkbox); +struct nc_widget *widget_subset_base(struct nc_widget_subset *subset); struct nc_widget *widget_select_base(struct nc_widget_select *select); struct nc_widget *widget_label_base(struct nc_widget_label *label); struct nc_widget *widget_button_base(struct nc_widget_button *button); |