]> git.ozlabs.org Git - petitboot/blob - ui/twin/pbt-menu.c
discover/device-handler: Ensure we free unresolved boot options on remove
[petitboot] / ui / twin / pbt-menu.c
1 /*
2  *  Copyright Geoff Levand <geoff@infradead.org>
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 #define _GNU_SOURCE
23 #include <assert.h>
24 #include <string.h>
25 #include <linux/input.h>
26
27 #include "log/log.h"
28 #include "talloc/talloc.h"
29 #include "ui/common/ui-system.h"
30
31 #include "pbt-menu.h"
32
33 static void pbt_item_draw_cb(twin_window_t *window)
34 {
35         struct pbt_item *item = pbt_item_from_window(window);
36         twin_pixmap_t *image;
37
38         assert(window == item->window);
39
40         pbt_dump_item(item);
41
42         //pbt_dump_pixmap(window->pixmap);
43
44         if (pbt_item_is_selected(item) && item->menu->has_focus)
45                 image = item->pixmap_active;
46         else if (pbt_item_is_selected(item) && !item->menu->has_focus)
47                 image = item->pixmap_selected;
48         else
49                 image = item->pixmap_idle;
50
51         pbt_image_draw(window->pixmap, image);
52 }
53
54 static twin_bool_t pbt_item_event_cb(twin_window_t *window,
55         twin_event_t *event)
56 {
57         struct pbt_item *item = pbt_item_from_window(window);
58
59         pbt_dump_event(pbt_item_name(item), window, event);
60
61         switch(event->kind) {
62         case TwinEventButtonDown:
63                 if (item->on_execute)
64                         item->on_execute(item);
65                 break;
66         case TwinEventButtonUp:
67                 break;
68         case TwinEventMotion:
69                 /* prevent window drag */
70                 return TWIN_TRUE;
71         case TwinEventEnter:
72                 pbt_item_set_as_selected(item);
73                 break;
74         case TwinEventLeave:
75                 break;
76         case TwinEventKeyDown:
77                 switch(event->u.key.key) {
78                 case (twin_keysym_t)XK_Return:
79                 case (twin_keysym_t)KEY_ENTER:
80                         if (item->on_execute)
81                                 item->on_execute(item);
82                         break;
83                 case (twin_keysym_t)'e':
84                         if (item->on_edit)
85                                 item->on_edit(item);
86                         break;
87                 default:
88                         break;
89                 }
90                 break;
91         default:
92                 break;
93         }
94         return TWIN_FALSE;
95 }
96
97 int pbt_item_editor(struct pbt_item *item)
98 {
99         (void)item;
100         return -1;
101 }
102
103 struct pbt_item *pbt_item_create(struct pbt_menu *menu, const char *name,
104         unsigned int position, const char *icon_filename, const char *title,
105         const char *text)
106 {
107         struct pbt_quad q;
108         struct pbt_item *item;
109         twin_pixmap_t *icon;
110         twin_operand_t src;
111         const struct pbt_menu_layout *layout = &menu->layout;
112         twin_path_t *path;
113         static const twin_argb32_t focus_color = 0x10404040;
114         enum {
115                 corner_rounding = 8,
116                 stroke_width = 2,
117         };
118
119         assert(menu);
120
121         DBGS("%d:%s (%s)\n", position, name, icon_filename);
122
123         item = talloc_zero(menu, struct pbt_item);
124
125         if (!item)
126                 return NULL;
127
128         item->menu = menu;
129         item->on_edit = pbt_item_editor;
130
131         pbt_menu_get_item_quad(menu, position, &q);
132
133         item->window = twin_window_create(menu->scr->tscreen,
134                 TWIN_ARGB32, TwinWindowPlain,
135                 q.x, q.y, q.width, q.height);
136
137         if (!item->window)
138                 goto fail_window_create;
139
140         twin_window_set_name(item->window, name);
141         item->window->client_data = item;
142         item->window->draw = pbt_item_draw_cb;
143         item->window->event = pbt_item_event_cb;
144
145         item->pixmap_idle = twin_pixmap_create(TWIN_ARGB32, q.width, q.height);
146         assert(item->pixmap_idle);
147
148         item->pixmap_selected = twin_pixmap_create(TWIN_ARGB32, q.width,
149                 q.height);
150         assert(item->pixmap_selected);
151
152         item->pixmap_active = twin_pixmap_create(TWIN_ARGB32, q.width,
153                 q.height);
154         assert(item->pixmap_active);
155
156         if (!item->pixmap_idle || !item->pixmap_selected || !item->pixmap_active)
157                 goto fail_pixmap_create;
158
159         twin_fill(item->pixmap_idle, 0x01000000, TWIN_SOURCE, 0, 0, q.width,
160                 q.height);
161
162         /* Add item icon */
163
164         icon = pbt_icon_load(icon_filename);
165
166         if (!icon)
167                 goto fail_icon;
168
169         src.source_kind = TWIN_PIXMAP;
170         src.u.pixmap = icon;
171
172         twin_composite(item->pixmap_idle,
173                 //0, (item->pixmap_idle->height - icon->height) / 2,
174                 0, 0,
175                 &src, 0, 0,
176                 NULL, 0, 0,
177                 TWIN_SOURCE,
178                 icon->width, icon->height);
179
180         /* Add item text */
181
182         path = twin_path_create();
183         assert(path);
184
185         if (title) {
186                 twin_path_set_font_size(path,
187                         twin_int_to_fixed(layout->title.font_size));
188                 twin_path_set_font_style(path, TWIN_TEXT_UNHINTED);
189
190                 twin_path_move(path,
191                         twin_int_to_fixed(icon->width + layout->text_space),
192                         twin_int_to_fixed(layout->title.font_size
193                                 + layout->text_space));
194                 twin_path_utf8(path, title);
195                 twin_paint_path(item->pixmap_idle, layout->title.color, path);
196                 twin_path_empty(path);
197         }
198
199         if (text) {
200                 twin_path_set_font_size(path,
201                         twin_int_to_fixed(layout->text.font_size));
202                 twin_path_move(path,
203                         twin_int_to_fixed(icon->width + layout->text_space),
204                         twin_int_to_fixed(layout->title.font_size
205                                 + layout->text.font_size
206                                 + layout->text_space));
207                 twin_path_utf8(path, text);
208                 twin_paint_path(item->pixmap_idle, layout->text.color, path);
209                 twin_path_empty(path);
210         }
211
212         pbt_image_draw(item->pixmap_selected, item->pixmap_idle);
213         pbt_image_draw(item->pixmap_active, item->pixmap_idle);
214
215 if (0) {
216         static const struct pbt_border grey_border = {
217                 .right = 1,
218                 .left = 1,
219                 .top = 1,
220                 .bottom = 1,
221                 .fill_color = 0xffe0e0e0,
222         };
223
224         //pbt_border_draw(item->pixmap_idle, &pbt_blue_debug_border);
225         pbt_border_draw(item->pixmap_selected, &grey_border);
226         pbt_border_draw(item->pixmap_active, &pbt_green_debug_border);
227 } else {
228         assert(!(stroke_width % 2));
229
230         /* pixmap_selected */
231
232         twin_path_rounded_rectangle(path,
233                 twin_int_to_fixed(stroke_width / 2),
234                 twin_int_to_fixed(stroke_width / 2),
235                 twin_int_to_fixed(item->pixmap_selected->width - stroke_width),
236                 twin_int_to_fixed(item->pixmap_selected->height - stroke_width),
237                 twin_int_to_fixed(corner_rounding),
238                 twin_int_to_fixed(corner_rounding));
239
240         twin_paint_stroke(item->pixmap_selected, focus_color, path,
241                 twin_int_to_fixed(stroke_width));
242
243         twin_path_empty(path);
244
245         /* pixmap_active */
246
247         twin_path_rounded_rectangle(path, 0, 0,
248                 twin_int_to_fixed(item->pixmap_active->width),
249                 twin_int_to_fixed(item->pixmap_active->height),
250                 twin_int_to_fixed(corner_rounding),
251                 twin_int_to_fixed(corner_rounding));
252
253         twin_paint_path(item->pixmap_active, focus_color, path);
254
255         twin_path_empty(path); // FIXME: need it???
256 }
257         twin_path_destroy(path);
258
259         list_add_tail(menu->item_list, &item->list);
260
261         pbt_item_redraw(item);
262
263         return item;
264
265 fail_window_create:
266 fail_pixmap_create:
267 fail_icon:
268         return NULL;
269 }
270
271 void _pbt_dump_item(const struct pbt_item* item, const char *func, int line)
272 {
273         DBG("%s:%d: %p: %sselected, %sfocus\n", func, line, item,
274                 (pbt_item_is_selected(item) ? "+" : "-"),
275                 (item->menu->has_focus ? "+" : "-"));
276 }
277
278 /**
279  * pbt_menu_get_item_quad - Return item coords relative to screen origin.
280  */
281
282 struct pbt_quad *pbt_menu_get_item_quad(const struct pbt_menu *menu,
283         unsigned int pos, struct pbt_quad *q)
284 {
285         const struct pbt_menu_layout *layout = &menu->layout;
286
287         q->x = menu->window->pixmap->x + layout->item_space;
288
289         q->width = menu->window->pixmap->width - 2 * layout->item_space;
290
291         q->y = menu->window->pixmap->y + layout->item_space
292                 + pos * (layout->item_height + layout->item_space);
293
294         q->height = layout->item_height;
295
296         return q;
297 }
298
299 static void pbt_menu_draw_cb(twin_window_t *window)
300 {
301         struct pbt_menu *menu = pbt_menu_from_window(window);
302         twin_path_t *path = twin_path_create();
303
304         assert(path);
305
306         pbt_dump_pixmap(window->pixmap);
307
308         twin_fill(window->pixmap, menu->background_color, TWIN_SOURCE,
309                 0, 0, window->pixmap->width, window->pixmap->height);
310
311         pbt_border_draw(window->pixmap, &menu->border);
312
313         twin_path_destroy(path);
314 }
315
316 static twin_bool_t pbt_menu_event_cb(twin_window_t *window,
317         twin_event_t *event)
318 {
319         struct pbt_menu *menu = pbt_menu_from_window(window);
320         struct pbt_item *i;
321
322         pbt_dump_event(pbt_menu_name(menu), window, event);
323
324         switch(event->kind) {
325         case TwinEventButtonDown:
326         case TwinEventButtonUp:
327         case TwinEventMotion:
328                 /* prevent window drag */
329                 return TWIN_TRUE;
330         case TwinEventEnter:
331                 pbt_menu_set_focus(menu, 1);
332                 break;
333         case TwinEventLeave:
334                 if (!pbt_window_contains(window, event))
335                         pbt_menu_set_focus(menu, 0);
336                 break;
337         case TwinEventKeyDown:
338                 switch(event->u.key.key) {
339                 case (twin_keysym_t)XK_Up:
340                 case (twin_keysym_t)KEY_UP:
341                         i = list_prev_entry(menu->item_list, menu->selected,
342                                 list);
343                         if (i)
344                                 pbt_item_set_as_selected(i);
345                         break;
346                 case (twin_keysym_t)XK_Down:
347                 case (twin_keysym_t)KEY_DOWN:
348                         i = list_next_entry(menu->item_list, menu->selected,
349                                 list);
350                         if (i)
351                                 pbt_item_set_as_selected(i);
352                         break;
353                 case (twin_keysym_t)XK_Left:
354                 case (twin_keysym_t)KEY_LEFT:
355                         if (menu->parent) {
356                                 pbt_menu_set_focus(menu, 0);
357                                 pbt_menu_set_focus(menu->parent, 1);
358                         } else
359                                 DBGS("no parent\n");
360                         break;
361                 case (twin_keysym_t)XK_Right:
362                 case (twin_keysym_t)KEY_RIGHT:
363                         if (menu->selected->sub_menu) {
364                                 pbt_menu_set_focus(menu, 0);
365                                 pbt_menu_set_focus(menu->selected->sub_menu, 1);
366                         } else
367                                 DBGS("no sub_menu\n");
368                         break;
369                 default:
370                         return pbt_item_event_cb(menu->selected->window, event);
371                 }
372                 break;
373         default:
374                 break;
375         }
376         return TWIN_FALSE;
377 }
378
379 struct pbt_menu *pbt_menu_create(void *talloc_ctx, const char *name,
380         struct pbt_scr *scr, struct pbt_menu *parent, const struct pbt_quad *q,
381         const struct pbt_menu_layout *layout)
382 {
383         struct pbt_menu *menu;
384
385         assert(scr);
386
387         DBGS("%s\n", name);
388
389         menu = talloc_zero(talloc_ctx, struct pbt_menu);
390
391         if (!menu)
392                 return NULL;
393
394         menu->scr = scr;
395         menu->parent = parent;
396         menu->layout = *layout;
397
398         menu->item_list = talloc(menu, struct list);
399         list_init(menu->item_list);
400
401         menu->window = twin_window_create(scr->tscreen, TWIN_ARGB32,
402                 TwinWindowPlain, q->x, q->y,
403                 q->width, q->height);
404
405         if (!menu->window)
406                 goto fail_window;
407
408         DBGS("window = %p\n", menu->window);
409
410         twin_window_set_name(menu->window, name);
411
412         menu->background_color = 0x01000000; //FIXME: what value???
413
414         menu->window->draw = pbt_menu_draw_cb;
415         menu->window->event = pbt_menu_event_cb;
416         menu->window->client_data = menu;
417
418         pbt_dump_pixmap(menu->window->pixmap);
419
420         pbt_menu_redraw(menu);
421
422         return menu;
423
424 fail_window:
425         assert(0);
426         talloc_free(menu);
427         return NULL;
428 }
429
430 void pbt_menu_set_focus(struct pbt_menu *menu, int focus)
431 {
432         DBGS("%s(%p): %d -> %d\n", pbt_menu_name(menu), menu, menu->has_focus,
433                 focus);
434
435         assert(menu->selected);
436
437         if (!menu->has_focus == !focus)
438                 return;
439
440         menu->has_focus = !!focus;
441
442         /* Route key events to menu with focus. */
443
444         if (menu->has_focus)
445                 menu->scr->tscreen->active = menu->window->pixmap;
446
447         pbt_item_redraw(menu->selected);
448 }
449
450 void pbt_menu_hide(struct pbt_menu *menu)
451 {
452         struct pbt_item *item;
453
454         if (!menu)
455                 return;
456
457         list_for_each_entry(menu->item_list, item, list) {
458                 if (item->sub_menu)
459                         pbt_menu_hide(item->sub_menu);
460
461                 twin_window_hide(item->window);
462                 //twin_window_queue_paint(item->window);
463         }
464
465         twin_window_hide(menu->window);
466         //twin_window_queue_paint(menu->window);
467 }
468
469 void pbt_menu_show(struct pbt_menu *menu, int hide)
470 {
471         struct pbt_item *item;
472
473         if (!menu)
474                 return;
475
476         twin_window_show(menu->window);
477         pbt_menu_redraw(menu);
478
479         list_for_each_entry(menu->item_list, item, list) {
480                 twin_window_show(item->window);
481                 pbt_item_redraw(item);
482
483                 if (item->sub_menu) {
484                         if (pbt_item_is_selected(item))
485                                 pbt_menu_show(item->sub_menu, hide);
486                         else if (hide)
487                                 pbt_menu_hide(item->sub_menu);
488                 }
489         }
490 }
491
492 void pbt_menu_set_selected(struct pbt_menu *menu, struct pbt_item *item)
493 {
494         struct pbt_item *last_selected;
495
496         assert(item);
497
498         DBGS("%s(%p): %s(%p) -> %s(%p)\n", pbt_menu_name(menu), menu,
499                 (menu->selected ? pbt_menu_name(menu) : "none"),
500                 menu->selected, pbt_item_name(item), item);
501
502         if (menu->selected == item)
503                 return;
504
505         last_selected = menu->selected;
506         menu->selected = item;
507
508         if (last_selected) {
509                 pbt_menu_hide(last_selected->sub_menu);
510                 pbt_item_redraw(last_selected);
511         }
512
513         pbt_item_redraw(item);
514         pbt_menu_show(item->sub_menu, 0);
515 }