ui/ncurses: Exit safe mode on configuration change
[petitboot] / ui / ncurses / nc-lang.c
1 /*
2  *  Copyright (C) 2013 IBM Corporation
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #if defined(HAVE_CONFIG_H)
19 #include "config.h"
20 #endif
21
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <talloc/talloc.h>
26 #include <types/types.h>
27 #include <log/log.h>
28 #include <i18n/i18n.h>
29 #include <pb-config/pb-config.h>
30
31 #include "nc-cui.h"
32 #include "nc-lang.h"
33 #include "nc-widgets.h"
34
35 #define N_FIELDS        5
36
37 static struct lang {
38         const char      *name;
39         const wchar_t   *label;
40 } languages[] = {
41         { "en_US.utf8", L"English"},
42 };
43
44 struct lang_screen {
45         struct nc_scr           scr;
46         struct cui              *cui;
47         struct nc_widgetset     *widgetset;
48         WINDOW                  *pad;
49
50         bool                    exit;
51         void                    (*on_exit)(struct cui *);
52
53         int                     scroll_y;
54
55         int                     label_x;
56         int                     field_x;
57
58         struct {
59                 struct nc_widget_select         *lang_f;
60                 struct nc_widget_label          *lang_l;
61
62                 struct nc_widget_label          *safe_mode;
63                 struct nc_widget_button         *ok_b;
64                 struct nc_widget_button         *cancel_b;
65         } widgets;
66 };
67
68 static struct lang_screen *lang_screen_from_scr(struct nc_scr *scr)
69 {
70         struct lang_screen *lang_screen;
71
72         assert(scr->sig == pb_lang_screen_sig);
73         lang_screen = (struct lang_screen *)
74                 ((char *)scr - (size_t)&((struct lang_screen *)0)->scr);
75         assert(lang_screen->scr.sig == pb_lang_screen_sig);
76         return lang_screen;
77 }
78
79 static void pad_refresh(struct lang_screen *screen)
80 {
81         int y, x, rows, cols;
82
83         getmaxyx(screen->scr.sub_ncw, rows, cols);
84         getbegyx(screen->scr.sub_ncw, y, x);
85
86         prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols);
87 }
88
89 static void lang_screen_process_key(struct nc_scr *scr, int key)
90 {
91         struct lang_screen *screen = lang_screen_from_scr(scr);
92         bool handled;
93
94         handled = widgetset_process_key(screen->widgetset, key);
95
96         if (!handled) {
97                 switch (key) {
98                 case 'x':
99                 case 27: /* esc */
100                         screen->exit = true;
101                         break;
102                 }
103         }
104
105         if (screen->exit) {
106                 screen->on_exit(screen->cui);
107
108         } else if (handled) {
109                 pad_refresh(screen);
110         }
111 }
112
113 static void lang_screen_resize(struct nc_scr *scr)
114 {
115         struct lang_screen *screen = lang_screen_from_scr(scr);
116         (void)screen;
117 }
118
119 static int lang_screen_post(struct nc_scr *scr)
120 {
121         struct lang_screen *screen = lang_screen_from_scr(scr);
122         widgetset_post(screen->widgetset);
123         nc_scr_frame_draw(scr);
124         redrawwin(scr->main_ncw);
125         wrefresh(screen->scr.main_ncw);
126         pad_refresh(screen);
127         return 0;
128 }
129
130 static int lang_screen_unpost(struct nc_scr *scr)
131 {
132         struct lang_screen *screen = lang_screen_from_scr(scr);
133         widgetset_unpost(screen->widgetset);
134         return 0;
135 }
136
137 struct nc_scr *lang_screen_scr(struct lang_screen *screen)
138 {
139         return &screen->scr;
140 }
141
142 static int lang_process_form(struct lang_screen *screen)
143 {
144         struct config *config;
145         struct lang *lang;
146         int idx, rc;
147
148         config = config_copy(screen, screen->cui->config);
149
150         idx = widget_select_get_value(screen->widgets.lang_f);
151
152         /* Option -1 ("Unknown") can only be populated from the current
153          * language, so there's no change here */
154         if (idx == -1)
155                 return 0;
156
157         lang = &languages[idx];
158
159         if (config->lang && !strcmp(lang->name, config->lang))
160                 return 0;
161
162         config->lang = talloc_strdup(screen, lang->name);
163
164         config->safe_mode = false;
165         rc = cui_send_config(screen->cui, config);
166         talloc_free(config);
167
168         if (rc)
169                 pb_log("cui_send_config failed!\n");
170         else
171                 pb_debug("config sent!\n");
172
173         return 0;
174 }
175
176 static void ok_click(void *arg)
177 {
178         struct lang_screen *screen = arg;
179         if (lang_process_form(screen))
180                 /* errors are written to the status line, so we'll need
181                  * to refresh */
182                 wrefresh(screen->scr.main_ncw);
183         else
184                 screen->exit = true;
185 }
186
187 static void cancel_click(void *arg)
188 {
189         struct lang_screen *screen = arg;
190         screen->exit = true;
191 }
192
193 static int layout_pair(struct lang_screen *screen, int y,
194                 struct nc_widget_label *label,
195                 struct nc_widget *field)
196 {
197         struct nc_widget *label_w = widget_label_base(label);
198         widget_move(label_w, y, screen->label_x);
199         widget_move(field, y, screen->field_x);
200         return max(widget_height(label_w), widget_height(field));
201 }
202
203 static void lang_screen_layout_widgets(struct lang_screen *screen)
204 {
205         int y;
206
207         y = 1;
208
209         y += layout_pair(screen, y, screen->widgets.lang_l,
210                         widget_select_base(screen->widgets.lang_f));
211
212         y += 1;
213
214         if (screen->cui->config->safe_mode) {
215                 widget_move(widget_label_base(screen->widgets.safe_mode),
216                         y, screen->field_x);
217                 y += 1;
218         }
219
220         widget_move(widget_button_base(screen->widgets.ok_b),
221                         y, screen->field_x);
222         widget_move(widget_button_base(screen->widgets.cancel_b),
223                         y, screen->field_x + 10);
224 }
225
226 static void lang_screen_setup_empty(struct lang_screen *screen)
227 {
228         widget_new_label(screen->widgetset, 2, screen->field_x,
229                         _("Waiting for configuration data..."));
230         screen->widgets.cancel_b = widget_new_button(screen->widgetset,
231                         4, screen->field_x, 6, _("Cancel"),
232                         cancel_click, screen);
233 }
234
235
236 static void lang_screen_setup_widgets(struct lang_screen *screen,
237                 const struct config *config)
238 {
239         struct nc_widgetset *set = screen->widgetset;
240         unsigned int i;
241         bool found;
242
243         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
244                         == N_FIELDS);
245
246         screen->widgets.lang_l = widget_new_label(set, 0, 0, _("Language"));
247         screen->widgets.lang_f = widget_new_select(set, 0, 0, 50);
248
249         found = false;
250
251         for (i = 0; i < ARRAY_SIZE(languages); i++) {
252                 struct lang *lang = &languages[i];
253                 bool selected;
254                 char *label;
255                 int len;
256
257                 len = wcstombs(NULL, lang->label, 0);
258                 assert(len >= 0);
259                 label = talloc_array(screen, char, len + 1);
260                 wcstombs(label, lang->label, len + 1);
261
262                 selected = config->lang && !strcmp(lang->name, config->lang);
263                 found |= selected;
264
265                 widget_select_add_option(screen->widgets.lang_f, i,
266                                         label, selected);
267         }
268
269         if (!found && config->lang) {
270                 char *label = talloc_asprintf(screen,
271                                 _("Unknown language '%s'"), config->lang);
272                 widget_select_add_option(screen->widgets.lang_f, -1,
273                                         label, true);
274         }
275
276         if (config->safe_mode)
277                 screen->widgets.safe_mode = widget_new_label(set, 0, 0,
278                          _("Selecting 'OK' will exit safe mode"));
279
280         screen->widgets.ok_b = widget_new_button(set, 0, 0, 6, _("OK"),
281                         ok_click, screen);
282         screen->widgets.cancel_b = widget_new_button(set, 0, 0, 6, _("Cancel"),
283                         cancel_click, screen);
284 }
285
286 static void lang_screen_widget_focus(struct nc_widget *widget, void *arg)
287 {
288         struct lang_screen *screen = arg;
289         int w_y, s_max;
290
291         w_y = widget_y(widget) + widget_focus_y(widget);
292         s_max = getmaxy(screen->scr.sub_ncw) - 1;
293
294         if (w_y < screen->scroll_y)
295                 screen->scroll_y = w_y;
296
297         else if (w_y + screen->scroll_y + 1 > s_max)
298                 screen->scroll_y = 1 + w_y - s_max;
299
300         else
301                 return;
302
303         pad_refresh(screen);
304 }
305
306 static void lang_screen_draw(struct lang_screen *screen,
307                 const struct config *config)
308 {
309         bool repost = false;
310         int height;
311
312         height = ARRAY_SIZE(languages) + 4;
313         if (!screen->pad || getmaxy(screen->pad) < height) {
314                 if (screen->pad)
315                         delwin(screen->pad);
316                 screen->pad = newpad(height, COLS);
317         }
318
319         if (screen->widgetset) {
320                 widgetset_unpost(screen->widgetset);
321                 talloc_free(screen->widgetset);
322                 repost = true;
323         }
324
325         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
326                         screen->pad);
327         widgetset_set_widget_focus(screen->widgetset,
328                         lang_screen_widget_focus, screen);
329
330         if (!config) {
331                 lang_screen_setup_empty(screen);
332         } else {
333                 lang_screen_setup_widgets(screen, config);
334                 lang_screen_layout_widgets(screen);
335         }
336
337         if (repost)
338                 widgetset_post(screen->widgetset);
339 }
340
341 void lang_screen_update(struct lang_screen *screen,
342                 const struct config *config)
343 {
344         lang_screen_draw(screen, config);
345         pad_refresh(screen);
346 }
347
348 static int lang_screen_destroy(void *arg)
349 {
350         struct lang_screen *screen = arg;
351         if (screen->pad)
352                 delwin(screen->pad);
353         return 0;
354 }
355
356 struct lang_screen *lang_screen_init(struct cui *cui,
357                 const struct config *config,
358                 void (*on_exit)(struct cui *))
359 {
360         struct lang_screen *screen;
361
362         screen = talloc_zero(cui, struct lang_screen);
363         talloc_set_destructor(screen, lang_screen_destroy);
364         nc_scr_init(&screen->scr, pb_lang_screen_sig, 0,
365                         cui, lang_screen_process_key,
366                         lang_screen_post, lang_screen_unpost,
367                         lang_screen_resize);
368
369         screen->cui = cui;
370         screen->on_exit = on_exit;
371         screen->label_x = 2;
372         screen->field_x = 17;
373
374         screen->scr.frame.ltitle = talloc_strdup(screen,
375                         _("Petitboot Language Selection"));
376         screen->scr.frame.rtitle = NULL;
377         screen->scr.frame.help = talloc_strdup(screen,
378                         _("tab=next, shift+tab=previous, x=exit, h=help"));
379         nc_scr_frame_draw(&screen->scr);
380
381         scrollok(screen->scr.sub_ncw, true);
382
383         lang_screen_draw(screen, config);
384
385         return screen;
386 }