+static void config_screen_widget_focus(struct nc_widget *widget, void *arg)
+{
+ struct config_screen *screen = arg;
+ int w_y, w_height, w_focus, s_max, adjust;
+
+ w_height = widget_height(widget);
+ w_focus = widget_focus_y(widget);
+ w_y = widget_y(widget) + w_focus;
+ 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) {
+ /* Fit as much of the widget into the screen as possible */
+ adjust = min(s_max - 1, w_height - w_focus);
+ if (w_y + adjust >= screen->scroll_y + s_max)
+ screen->scroll_y = max(0, 1 + w_y + adjust - s_max);
+ } else
+ return;
+
+ pad_refresh(screen);
+}
+
+static void config_screen_draw(struct config_screen *screen,
+ const struct config *config,
+ const struct system_info *sysinfo)
+{
+ bool repost = false;
+ int height;
+
+ /* The size of the pad we'll need depends on the number of interfaces.
+ *
+ * We use N_FIELDS (which is quite conservative, as some fields share
+ * a line) as a base, then:
+ * - add 6 (as the network select & boot device select fields take 3
+ * lines each),
+ * - add n_interfaces for every field in the network select field, and
+ * - add (n_blockdevs + n_interfaces) for every field in the boot device
+ * select field
+ */
+ height = N_FIELDS + 6;
+ if (sysinfo) {
+ height += sysinfo->n_interfaces;
+ height += (sysinfo->n_blockdevs + sysinfo->n_interfaces);
+ }
+ if (!screen->pad || getmaxy(screen->pad) < height) {
+ if (screen->pad)
+ delwin(screen->pad);
+ screen->pad = newpad(height, COLS + 10);
+ }
+
+ 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,
+ config_screen_widget_focus, screen);
+
+ if (!config || !sysinfo) {
+ config_screen_setup_empty(screen);
+ } else {
+ screen->net_conf_type = find_net_conf_type(config);
+ config_screen_setup_widgets(screen, config, sysinfo);
+ config_screen_layout_widgets(screen);
+ }
+
+ if (repost)
+ widgetset_post(screen->widgetset);
+}
+
+void config_screen_update(struct config_screen *screen,
+ const struct config *config,
+ const struct system_info *sysinfo)
+{
+ if (screen->cui->current != config_screen_scr(screen)) {
+ screen->need_update = true;
+ return;
+ }
+
+ config_screen_draw(screen, config, sysinfo);
+ pad_refresh(screen);
+}
+
+static int config_screen_post(struct nc_scr *scr)
+{
+ struct config_screen *screen = config_screen_from_scr(scr);
+ screen->show_subset = false;
+
+ if (screen->need_update) {
+ config_screen_draw(screen, screen->cui->config,
+ screen->cui->sysinfo);
+ screen->need_update = false;
+ } else {
+ widgetset_post(screen->widgetset);
+ }
+
+ nc_scr_frame_draw(scr);
+ if (screen->need_redraw) {
+ redrawwin(scr->main_ncw);
+ screen->need_redraw = false;
+ }
+ wrefresh(screen->scr.main_ncw);
+ pad_refresh(screen);
+ return 0;
+}
+
+static int config_screen_destroy(void *arg)
+{
+ struct config_screen *screen = arg;
+ if (screen->pad)
+ delwin(screen->pad);
+ return 0;
+}
+