a7c9ccc53bb53f24b755a2d2c3109752dc4da05e
[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 #include <locale.h>
25
26 #include <talloc/talloc.h>
27 #include <types/types.h>
28 #include <log/log.h>
29 #include <i18n/i18n.h>
30 #include <pb-config/pb-config.h>
31
32 #include "nc-cui.h"
33 #include "nc-lang.h"
34 #include "nc-widgets.h"
35
36 #define N_FIELDS        5
37
38 static struct lang {
39         const char      *name;
40         const wchar_t   *label;
41 } languages[] = {
42         { "de_DE.utf8", L"Deutsch"},
43         { "en_US.utf8", L"English"},
44         { "es_ES.utf8", L"Espa\u00f1ol"},
45         { "fr_FR.utf8", L"Fran\u00e7ais"},
46         { "it_IT.utf8", L"Italiano"},
47         { "ja_JP.utf8", L"\u65e5\u672c\u8a9e"},
48         { "ko_KR.utf8", L"\ud55c\uad6d\uc5b4"},
49         { "pt_BR.utf8", L"Portugu\u00eas/Brasil"},
50         { "ru_RU.utf8", L"\u0420\u0443\u0441\u0441\u043a\u0438\u0439"},
51         { "zh_CN.utf8", L"\u7b80\u4f53\u4e2d\u6587"},
52         { "zh_TW.utf8", L"\u7e41\u9ad4\u4e2d\u6587"},
53 };
54
55 struct lang_screen {
56         struct nc_scr           scr;
57         struct cui              *cui;
58         struct nc_widgetset     *widgetset;
59         WINDOW                  *pad;
60
61         bool                    exit;
62         void                    (*on_exit)(struct cui *);
63
64         int                     scroll_y;
65
66         int                     label_x;
67         int                     field_x;
68
69         struct {
70                 struct nc_widget_select         *lang_f;
71                 struct nc_widget_label          *lang_l;
72
73                 struct nc_widget_label          *safe_mode;
74                 struct nc_widget_button         *ok_b;
75                 struct nc_widget_button         *cancel_b;
76         } widgets;
77 };
78
79 static struct lang_screen *lang_screen_from_scr(struct nc_scr *scr)
80 {
81         struct lang_screen *lang_screen;
82
83         assert(scr->sig == pb_lang_screen_sig);
84         lang_screen = (struct lang_screen *)
85                 ((char *)scr - (size_t)&((struct lang_screen *)0)->scr);
86         assert(lang_screen->scr.sig == pb_lang_screen_sig);
87         return lang_screen;
88 }
89
90 static void pad_refresh(struct lang_screen *screen)
91 {
92         int y, x, rows, cols;
93
94         getmaxyx(screen->scr.sub_ncw, rows, cols);
95         getbegyx(screen->scr.sub_ncw, y, x);
96
97         prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols);
98 }
99
100 static void lang_screen_process_key(struct nc_scr *scr, int key)
101 {
102         struct lang_screen *screen = lang_screen_from_scr(scr);
103         bool handled;
104
105         handled = widgetset_process_key(screen->widgetset, key);
106
107         if (!handled) {
108                 switch (key) {
109                 case 'x':
110                 case 27: /* esc */
111                         screen->exit = true;
112                         break;
113                 }
114         }
115
116         if (screen->exit) {
117                 screen->on_exit(screen->cui);
118
119         } else if (handled) {
120                 pad_refresh(screen);
121         }
122 }
123
124 static void lang_screen_resize(struct nc_scr *scr)
125 {
126         struct lang_screen *screen = lang_screen_from_scr(scr);
127         (void)screen;
128 }
129
130 static int lang_screen_post(struct nc_scr *scr)
131 {
132         struct lang_screen *screen = lang_screen_from_scr(scr);
133         widgetset_post(screen->widgetset);
134         nc_scr_frame_draw(scr);
135         wrefresh(screen->scr.main_ncw);
136         pad_refresh(screen);
137         return 0;
138 }
139
140 static int lang_screen_unpost(struct nc_scr *scr)
141 {
142         struct lang_screen *screen = lang_screen_from_scr(scr);
143         widgetset_unpost(screen->widgetset);
144         return 0;
145 }
146
147 struct nc_scr *lang_screen_scr(struct lang_screen *screen)
148 {
149         return &screen->scr;
150 }
151
152 static int lang_process_form(struct lang_screen *screen)
153 {
154         struct config *config;
155         struct lang *lang;
156         int idx, rc;
157
158         config = config_copy(screen, screen->cui->config);
159
160         idx = widget_select_get_value(screen->widgets.lang_f);
161
162         /* Option -1 ("Unknown") can only be populated from the current
163          * language, so there's no change here */
164         if (idx == -1)
165                 return 0;
166
167         lang = &languages[idx];
168
169         if (config->lang && !strcmp(lang->name, config->lang))
170                 return 0;
171
172         config->lang = talloc_strdup(screen, lang->name);
173
174         config->safe_mode = false;
175         rc = cui_send_config(screen->cui, config);
176         talloc_free(config);
177
178         if (rc)
179                 pb_log("cui_send_config failed!\n");
180         else
181                 pb_debug("config sent!\n");
182
183         return 0;
184 }
185
186 static void ok_click(void *arg)
187 {
188         struct lang_screen *screen = arg;
189         if (lang_process_form(screen))
190                 /* errors are written to the status line, so we'll need
191                  * to refresh */
192                 wrefresh(screen->scr.main_ncw);
193         else
194                 screen->exit = true;
195 }
196
197 static void cancel_click(void *arg)
198 {
199         struct lang_screen *screen = arg;
200         screen->exit = true;
201 }
202
203 static int layout_pair(struct lang_screen *screen, int y,
204                 struct nc_widget_label *label,
205                 struct nc_widget *field)
206 {
207         struct nc_widget *label_w = widget_label_base(label);
208         widget_move(label_w, y, screen->label_x);
209         widget_move(field, y, screen->field_x);
210         return max(widget_height(label_w), widget_height(field));
211 }
212
213 static void lang_screen_layout_widgets(struct lang_screen *screen)
214 {
215         int y;
216
217         y = 1;
218
219         y += layout_pair(screen, y, screen->widgets.lang_l,
220                         widget_select_base(screen->widgets.lang_f));
221
222         y += 1;
223
224         if (screen->cui->config->safe_mode) {
225                 widget_move(widget_label_base(screen->widgets.safe_mode),
226                         y, screen->field_x);
227                 y += 1;
228         }
229
230         widget_move(widget_button_base(screen->widgets.ok_b),
231                         y, screen->field_x);
232         widget_move(widget_button_base(screen->widgets.cancel_b),
233                         y, screen->field_x + 14);
234 }
235
236 static void lang_screen_setup_empty(struct lang_screen *screen)
237 {
238         widget_new_label(screen->widgetset, 2, screen->field_x,
239                         _("Waiting for configuration data..."));
240         screen->widgets.cancel_b = widget_new_button(screen->widgetset,
241                         4, screen->field_x, 9, _("Cancel"),
242                         cancel_click, screen);
243 }
244
245
246 static void lang_screen_setup_widgets(struct lang_screen *screen,
247                 const struct config *config)
248 {
249         struct nc_widgetset *set = screen->widgetset;
250         unsigned int i;
251         bool found;
252
253         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
254                         == N_FIELDS);
255
256         screen->widgets.lang_l = widget_new_label(set, 0, 0, _("Language"));
257         screen->widgets.lang_f = widget_new_select(set, 0, 0, 50);
258
259         found = false;
260
261         for (i = 0; i < ARRAY_SIZE(languages); i++) {
262                 struct lang *lang = &languages[i];
263                 bool selected;
264                 char *label;
265                 int len;
266
267                 len = wcstombs(NULL, lang->label, 0);
268                 assert(len >= 0);
269                 if (len < 0) {
270                         label = talloc_asprintf(screen,
271                                 "Unable to display text in this locale (%s)\n",
272                                 setlocale(LC_ALL, NULL));
273                 } else {
274                         label = talloc_array(screen, char, len + 1);
275                         wcstombs(label, lang->label, len + 1);
276                 }
277
278                 selected = config->lang && !strcmp(lang->name, config->lang);
279                 found |= selected;
280
281                 widget_select_add_option(screen->widgets.lang_f, i,
282                                         label, selected);
283         }
284
285         if (!found && config->lang) {
286                 char *label = talloc_asprintf(screen,
287                                 _("Unknown language '%s'"), config->lang);
288                 widget_select_add_option(screen->widgets.lang_f, -1,
289                                         label, true);
290         }
291
292         if (config->safe_mode)
293                 screen->widgets.safe_mode = widget_new_label(set, 0, 0,
294                          _("Selecting 'OK' will exit safe mode"));
295
296         screen->widgets.ok_b = widget_new_button(set, 0, 0, 10, _("OK"),
297                         ok_click, screen);
298         screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10, _("Cancel"),
299                         cancel_click, screen);
300 }
301
302 static void lang_screen_widget_focus(struct nc_widget *widget, void *arg)
303 {
304         struct lang_screen *screen = arg;
305         int w_y, s_max;
306
307         w_y = widget_y(widget) + widget_focus_y(widget);
308         s_max = getmaxy(screen->scr.sub_ncw) - 1;
309
310         if (w_y < screen->scroll_y)
311                 screen->scroll_y = w_y;
312
313         else if (w_y + screen->scroll_y + 1 > s_max)
314                 screen->scroll_y = 1 + w_y - s_max;
315
316         else
317                 return;
318
319         pad_refresh(screen);
320 }
321
322 static void lang_screen_draw(struct lang_screen *screen,
323                 const struct config *config)
324 {
325         bool repost = false;
326         int height;
327
328         height = ARRAY_SIZE(languages) + 4;
329         if (!screen->pad || getmaxy(screen->pad) < height) {
330                 if (screen->pad)
331                         delwin(screen->pad);
332                 screen->pad = newpad(height, COLS);
333         }
334
335         if (screen->widgetset) {
336                 widgetset_unpost(screen->widgetset);
337                 talloc_free(screen->widgetset);
338                 repost = true;
339         }
340
341         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
342                         screen->pad);
343         widgetset_set_widget_focus(screen->widgetset,
344                         lang_screen_widget_focus, screen);
345
346         if (!config) {
347                 lang_screen_setup_empty(screen);
348         } else {
349                 lang_screen_setup_widgets(screen, config);
350                 lang_screen_layout_widgets(screen);
351         }
352
353         if (repost)
354                 widgetset_post(screen->widgetset);
355 }
356
357 void lang_screen_update(struct lang_screen *screen,
358                 const struct config *config)
359 {
360         lang_screen_draw(screen, config);
361         pad_refresh(screen);
362 }
363
364 static int lang_screen_destroy(void *arg)
365 {
366         struct lang_screen *screen = arg;
367         if (screen->pad)
368                 delwin(screen->pad);
369         return 0;
370 }
371
372 struct lang_screen *lang_screen_init(struct cui *cui,
373                 const struct config *config,
374                 void (*on_exit)(struct cui *))
375 {
376         struct lang_screen *screen;
377
378         screen = talloc_zero(cui, struct lang_screen);
379         talloc_set_destructor(screen, lang_screen_destroy);
380         nc_scr_init(&screen->scr, pb_lang_screen_sig, 0,
381                         cui, lang_screen_process_key,
382                         lang_screen_post, lang_screen_unpost,
383                         lang_screen_resize);
384
385         screen->cui = cui;
386         screen->on_exit = on_exit;
387         screen->label_x = 2;
388         screen->field_x = 17;
389
390         screen->scr.frame.ltitle = talloc_strdup(screen,
391                         _("Petitboot Language Selection"));
392         screen->scr.frame.rtitle = NULL;
393         screen->scr.frame.help = talloc_strdup(screen,
394                         _("tab=next, shift+tab=previous, x=exit"));
395         nc_scr_frame_draw(&screen->scr);
396
397         scrollok(screen->scr.sub_ncw, true);
398
399         lang_screen_draw(screen, config);
400
401         return screen;
402 }