summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2015-06-26 11:36:19 +0800
committerJeremy Kerr <jk@ozlabs.org>2015-06-26 11:39:39 +0800
commit37b4861cb01bf6bd9da41aa1b311a87b0d26fc25 (patch)
tree3330d1958b04c9923de090273a4c0be4ee36e577 /ui
parent83a3159f19f237fa994d2c0a16f97f711d02472c (diff)
parent7df003a0d222ae08bff62de4fefff1cf56628123 (diff)
downloadtalos-petitboot-37b4861cb01bf6bd9da41aa1b311a87b0d26fc25.tar.gz
talos-petitboot-37b4861cb01bf6bd9da41aa1b311a87b0d26fc25.zip
Merge remote-tracking rbanch sammj/master
Conflicts: discover/platform-powerpc.c discover/ipmi.h Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Diffstat (limited to 'ui')
-rw-r--r--ui/ncurses/Makefile.am4
-rw-r--r--ui/ncurses/nc-config-help.c27
-rw-r--r--ui/ncurses/nc-config.c370
-rw-r--r--ui/ncurses/nc-cui.c64
-rw-r--r--ui/ncurses/nc-cui.h3
-rw-r--r--ui/ncurses/nc-scr.h1
-rw-r--r--ui/ncurses/nc-subset.c289
-rw-r--r--ui/ncurses/nc-subset.h36
-rw-r--r--ui/ncurses/nc-widgets.c373
-rw-r--r--ui/ncurses/nc-widgets.h19
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);
OpenPOWER on IntegriCloud