ui/ncurses: Add nc-subset selection screen
[petitboot] / ui / ncurses / nc-subset.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 <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <talloc/talloc.h>
27 #include <types/types.h>
28 #include <i18n/i18n.h>
29 #include <log/log.h>
30
31 #include "nc-cui.h"
32 #include "nc-subset.h"
33
34 #define N_FIELDS        3
35
36 struct subset_screen {
37         struct nc_scr           scr;
38         struct cui              *cui;
39         struct nc_scr           *return_scr;
40         struct nc_widgetset     *widgetset;
41         WINDOW                  *pad;
42         struct nc_widget_subset *options;
43
44         bool                    exit;
45         void                    (*on_exit)(struct cui *);
46
47         int                     scroll_y;
48
49         int                     label_x;
50         int                     field_x;
51
52         struct {
53                 struct nc_widget_select         *options_f;
54
55                 struct nc_widget_button         *ok_b;
56                 struct nc_widget_button         *cancel_b;
57         } widgets;
58 };
59
60 struct nc_scr *subset_screen_return_scr(struct subset_screen *screen)
61 {
62         return screen->return_scr;
63 }
64
65 static struct subset_screen *subset_screen_from_scr(struct nc_scr *scr)
66 {
67         struct subset_screen *subset_screen;
68
69         assert(scr->sig == pb_subset_screen_sig);
70         subset_screen = (struct subset_screen *)
71                 ((char *)scr - (size_t)&((struct subset_screen *)0)->scr);
72         assert(subset_screen->scr.sig == pb_subset_screen_sig);
73         return subset_screen;
74 }
75
76 static void pad_refresh(struct subset_screen *screen)
77 {
78         int y, x, rows, cols;
79
80         getmaxyx(screen->scr.sub_ncw, rows, cols);
81         getbegyx(screen->scr.sub_ncw, y, x);
82
83         prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols);
84 }
85
86 static void subset_screen_process_key(struct nc_scr *scr, int key)
87 {
88         struct subset_screen *screen = subset_screen_from_scr(scr);
89         bool handled;
90
91         handled = widgetset_process_key(screen->widgetset, key);
92
93         if (!handled) {
94                 switch (key) {
95                 case 'x':
96                 case 27: /* esc */
97                         screen->exit = true;
98                         break;
99                 }
100         }
101
102         if (screen->exit)
103                 screen->on_exit(screen->cui);
104         else if (handled)
105                 pad_refresh(screen);
106 }
107
108 static int subset_screen_post(struct nc_scr *scr)
109 {
110         struct subset_screen *screen = subset_screen_from_scr(scr);
111         widgetset_post(screen->widgetset);
112         nc_scr_frame_draw(scr);
113         redrawwin(scr->main_ncw);
114         wrefresh(scr->main_ncw);
115         pad_refresh(screen);
116         return 0;
117 }
118
119 static int subset_screen_unpost(struct nc_scr *scr)
120 {
121         struct subset_screen *screen = subset_screen_from_scr(scr);
122         widgetset_unpost(screen->widgetset);
123         return 0;
124 }
125
126 struct nc_scr *subset_screen_scr(struct subset_screen *screen)
127 {
128         return &screen->scr;
129 }
130
131 static void ok_click(void *arg)
132 {
133         struct subset_screen *screen = arg;
134         int idx = widget_select_get_value(screen->widgets.options_f);
135         widget_subset_callback(screen->return_scr, screen->options, idx);
136         screen->exit = true;
137 }
138
139 static void cancel_click(void *arg)
140 {
141         struct subset_screen *screen = arg;
142         screen->exit = true;
143 }
144
145 static void subset_screen_layout_widgets(struct subset_screen *screen)
146 {
147         int y = 2;
148
149         /* select */
150         widget_move(widget_select_base(screen->widgets.options_f),
151                 y, screen->label_x);
152         y+= widget_height(widget_select_base(screen->widgets.options_f));
153
154         /* ok, cancel */
155         y += 1;
156
157         widget_move(widget_button_base(screen->widgets.ok_b),
158                 y, screen->field_x + 12);
159         widget_move(widget_button_base(screen->widgets.cancel_b),
160                 y, screen->field_x + 24);
161 }
162
163 static void subset_screen_option_select(void *arg, int value)
164 {
165         struct subset_screen *screen = arg;
166         widgetset_unpost(screen->widgetset);
167         subset_screen_layout_widgets(screen);
168         widgetset_post(screen->widgetset);
169         (void)value;
170 }
171
172 static void subset_screen_setup_widgets(struct subset_screen *screen)
173 {
174         struct nc_widgetset *set = screen->widgetset;
175         struct nc_widget_subset *subset = screen->options;
176
177         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
178                         == N_FIELDS);
179
180         screen->widgets.options_f = widget_new_select(set, 0, 0,
181                         COLS - (2 * screen->label_x));
182
183         widget_select_on_change(screen->widgets.options_f,
184                         subset_screen_option_select, screen);
185
186         widget_subset_show_inactive(subset, screen->widgets.options_f);
187
188         screen->widgets.ok_b = widget_new_button(set, 0, 0, 10, _("OK"),
189                         ok_click, screen);
190         screen->widgets.cancel_b = widget_new_button(set, 0, 0, 10, _("Cancel"),
191                         cancel_click, screen);
192 }
193
194 static void subset_screen_widget_focus(struct nc_widget *widget, void *arg)
195 {
196         struct subset_screen *screen = arg;
197         int w_y, s_max;
198
199         w_y = widget_y(widget) + widget_focus_y(widget);
200         s_max = getmaxy(screen->scr.sub_ncw) - 1;
201
202         if (w_y < screen->scroll_y)
203                 screen->scroll_y = w_y;
204
205         else if (w_y + screen->scroll_y + 1 > s_max)
206                 screen->scroll_y = 1 + w_y - s_max;
207
208         else
209                 return;
210
211         pad_refresh(screen);
212 }
213
214 static void subset_screen_draw(struct subset_screen *screen)
215 {
216         bool repost = false;
217         int height;
218
219         /* Size of pad = top space + number of available options */
220         height = 1 + N_FIELDS + widget_subset_n_inactive(screen->options);
221
222         if (!screen->pad || getmaxy(screen->pad) < height) {
223                 if (screen->pad)
224                         delwin(screen->pad);
225                 screen->pad = newpad(height, COLS);
226         }
227
228         if (screen->widgetset) {
229                 widgetset_unpost(screen->widgetset);
230                 talloc_free(screen->widgetset);
231                 repost = true;
232         }
233
234         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
235                         screen->pad);
236         widgetset_set_widget_focus(screen->widgetset,
237                         subset_screen_widget_focus, screen);
238
239         subset_screen_setup_widgets(screen);
240         subset_screen_layout_widgets(screen);
241
242         if (repost)
243                 widgetset_post(screen->widgetset);
244 }
245
246 static int subset_screen_destroy(void *arg)
247 {
248         struct subset_screen *screen = arg;
249         if (screen->pad)
250                 delwin(screen->pad);
251         return 0;
252 }
253
254 struct subset_screen *subset_screen_init(struct cui *cui,
255                 struct nc_scr *current_scr,
256                 const char *title_suffix,
257                 void *subset,
258                 void (*on_exit)(struct cui *))
259 {
260         struct subset_screen *screen;
261
262         screen = talloc_zero(cui, struct subset_screen);
263         talloc_set_destructor(screen, subset_screen_destroy);
264
265         screen->cui = cui;
266         screen->on_exit = on_exit;
267         screen->options = (struct nc_widget_subset *) subset;
268         screen->label_x = 2;
269         screen->field_x = 22;
270
271         screen->return_scr = current_scr;
272
273         nc_scr_init(&screen->scr, pb_subset_screen_sig, 0,
274                 cui, subset_screen_process_key,
275                 subset_screen_post, subset_screen_unpost,
276                 NULL);
277
278         screen->scr.frame.ltitle = talloc_strdup(screen,
279                         title_suffix);
280         screen->scr.frame.rtitle = NULL;
281         screen->scr.frame.help = talloc_strdup(screen,
282                         _("tab=next, shift+tab=previous, x=exit"));
283
284         scrollok(screen->scr.sub_ncw, true);
285
286         subset_screen_draw(screen);
287
288         return screen;
289 }