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