1 /*
2 * (SLIK) SimpLIstic sKin functions
3 * (C) 2005 John Ellis
4 *
5 * Author: John Ellis
6 *
7 * This software is released under the GNU General Public License (GNU GPL).
8 * Please read the included file COPYING for more information.
9 * This software comes with no warranty of any kind, use at your own risk!
10 */
11
12 #include "ui2_includes.h"
13 #include "ui2_typedefs.h"
14 #include "ui2_menu.h"
15
16 #include "ui2_display.h"
17 #include "ui2_list.h"
18 #include "ui2_main.h"
19 #include "ui2_skin.h"
20 #include "ui2_util.h"
21 #include "ui2_widget.h"
22
23
24 #include <gdk/gdkkeysyms.h> /* for key values */
25
26
27 typedef struct _MenuItemData MenuItemData;
28 struct _MenuItemData
29 {
30 gchar *text;
31
32 gchar *path; /* path to this menu (/item, /submenu/item, etc.) */
33 gint id; /* numerical id assigned when menu was added */
34
35 gint is_toggle;
36 gint toggle_active;
37
38 gint sensitive;
39
40 MenuItemData *parent; /* parent of this menu, top is NULL */
41 GList *children; /* if non NULL, this is a submenu */
42
43 void (*select_func)(UIMenuData *md, const gchar *path, gint id, gpointer select_data);
44 gpointer select_data;
45
46 UIData *ui; /* when submenu showing, this points to the window */
47 gint x;
48 gint y;
49 GdkPixbuf *backing;
50 gint go_left;
51
52 UIMenuData *md;
53 };
54
55
56 static void ui_menu_submenu_activate(UIMenuData *md, MenuItemData *mi,
57 gint x, gint y, gint button, guint32 event_time);
58
59 static UIData *ui_menu_submenu_new(MenuItemData *mi, const gchar *class, const gchar *key);
60
61 static void ui_menu_submenu_show(UIMenuData *md, MenuItemData *mi, gint x, gint y);
62 static void ui_menu_submenu_ungrab(MenuItemData *mi, guint32 event_time);
63 static void ui_menu_submenu_grab(MenuItemData *mi, guint32 event_time);
64 static void ui_menu_submenu_grab_focus(UIMenuData *md, MenuItemData *mi, gint button, guint32 event_time);
65
66
67 /*
68 *---------------------------------------------------------
69 * misc
70 *---------------------------------------------------------
71 */
72
ui_menu_is_button_press(void)73 static gint ui_menu_is_button_press(void)
74 {
75 GdkEvent *event;
76 gint ret = 0;
77
78 event = gtk_get_current_event();
79
80 if (event && event->type == GDK_BUTTON_PRESS)
81 {
82 ret = event->button.button;
83 }
84 if (event) gdk_event_free(event);
85
86 return ret;
87 }
88
ui_menu_event_time(void)89 static guint32 ui_menu_event_time(void)
90 {
91 GdkEvent *event;
92 guint32 ret = 0;
93
94 event = gtk_get_current_event();
95 if (!event) return 0;
96
97 if (event->type == GDK_BUTTON_PRESS)
98 {
99 ret = event->button.time;
100 }
101 else if (event->type == GDK_KEY_PRESS)
102 {
103 ret = event->key.time;
104 }
105
106 gdk_event_free(event);
107 return ret;
108 }
109
ui_menu_hide_all(UIMenuData * md)110 static void ui_menu_hide_all(UIMenuData *md)
111 {
112 GList *work;
113
114 work = g_list_last(md->submenu_list);
115 while (work)
116 {
117 MenuItemData *mi = work->data;
118 work = work->prev;
119
120 if (mi->ui && GTK_WIDGET_VISIBLE(mi->ui->window))
121 {
122 gtk_widget_hide(mi->ui->window);
123 }
124 }
125 }
126
127 /*
128 *---------------------------------------------------------
129 * menu items, etc.
130 *---------------------------------------------------------
131 */
132
ui_menu_item_free(MenuItemData * mi)133 static void ui_menu_item_free(MenuItemData *mi)
134 {
135 if (!mi) return;
136
137 if (mi->children)
138 {
139 GList *work;
140
141 work = mi->children;
142 while (work)
143 {
144 MenuItemData *tmp = work->data;
145 work = work->next;
146
147 ui_menu_item_free(tmp);
148 }
149 g_list_free(mi->children);
150 }
151
152 if (mi->ui) gtk_widget_destroy(mi->ui->window);
153 if (mi->backing) gdk_pixbuf_unref(mi->backing);
154
155 g_free(mi->text);
156 g_free(mi->path);
157 g_free(mi);
158 }
159
simple_base_length(const gchar * path)160 static gint simple_base_length(const gchar *path)
161 {
162 gint p;
163
164 if (!path) return 0;
165
166 p = strlen(path) - 1;
167 if (p < 0) return 0;
168
169 while (path[p] != '/' && p > 0) p--;
170 if (p == 0 && path[p] == '/') p++;
171 return p;
172 }
173
ui_menu_find_parent(GList * list,const gchar * path)174 static MenuItemData *ui_menu_find_parent(GList *list, const gchar *path)
175 {
176 GList *work;
177
178 if (!list || !path || path[0] != '/') return NULL;
179
180 work = list;
181 while (work)
182 {
183 MenuItemData *mi;
184 MenuItemData *ret;
185 gint l;
186
187 mi = work->data;
188 work = work->next;
189
190 l = simple_base_length(path);
191 if (l == strlen(mi->path) && strncmp(mi->path, path, l) == 0)
192 {
193 return mi;
194 }
195 ret = ui_menu_find_parent(mi->children, path);
196 if (ret) return ret;
197 }
198
199 return NULL;
200 }
201
ui_menu_item_add_real(UIMenuData * md,const gchar * text,const gchar * path,gint id,void (* select_func)(UIMenuData * md,const gchar * path,gint id,gpointer select_data),gpointer select_data)202 static MenuItemData *ui_menu_item_add_real(UIMenuData *md, const gchar *text, const gchar *path, gint id,
203 void (*select_func)(UIMenuData *md, const gchar *path, gint id, gpointer select_data),
204 gpointer select_data)
205 {
206 MenuItemData *mi;
207 MenuItemData *parent;
208
209 if (!md || !text) return NULL;
210
211 mi = g_new0(MenuItemData, 1);
212
213 mi->text = g_strdup(text);
214 mi->path = g_strdup((path) ? path : "/");
215 mi->id = id;
216
217 mi->select_func = select_func;
218 mi->select_data = select_data;
219
220 mi->ui = NULL;
221 mi->backing = NULL;
222 mi->go_left = FALSE;
223
224 mi->sensitive = TRUE;
225
226 parent = ui_menu_find_parent(md->list, path);
227 mi->parent = parent;
228 mi->md = md;
229
230 if (parent)
231 {
232 if (debug_mode) printf("menu %s is submenu of %s\n", mi->text, parent->text);
233 parent->children = g_list_append(parent->children, mi);
234 }
235 else
236 {
237 md->list = g_list_append(md->list, mi);
238 }
239
240 return mi;
241 }
242
ui_menu_item_add(UIMenuData * md,const gchar * text,const gchar * path,gint id,void (* select_func)(UIMenuData * md,const gchar * path,gint id,gpointer select_data),gpointer select_data)243 void ui_menu_item_add(UIMenuData *md, const gchar *text, const gchar *path, gint id,
244 void (*select_func)(UIMenuData *md, const gchar *path, gint id, gpointer select_data),
245 gpointer select_data)
246 {
247 ui_menu_item_add_real(md, text, path, id, select_func, select_data);
248 }
249
ui_menu_item_add_check(UIMenuData * md,const gchar * text,const gchar * path,gint id,gint active,void (* select_func)(UIMenuData * md,const gchar * path,gint id,gpointer select_data),gpointer select_data)250 void ui_menu_item_add_check(UIMenuData *md, const gchar *text, const gchar *path, gint id, gint active,
251 void (*select_func)(UIMenuData *md, const gchar *path, gint id, gpointer select_data),
252 gpointer select_data)
253 {
254 MenuItemData *mi;
255
256 mi = ui_menu_item_add_real(md, text, path, id, select_func, select_data);
257 if (!mi) return;
258 mi->is_toggle = TRUE;
259 mi->toggle_active = active;
260 }
261
ui_menu_item_add_sensitive(UIMenuData * md,const gchar * text,const gchar * path,gint id,gint sensitive,void (* select_func)(UIMenuData * md,const gchar * path,gint id,gpointer select_data),gpointer select_data)262 void ui_menu_item_add_sensitive(UIMenuData *md, const gchar *text, const gchar *path, gint id, gint sensitive,
263 void (*select_func)(UIMenuData *md, const gchar *path, gint id, gpointer select_data),
264 gpointer select_data)
265 {
266 MenuItemData *mi;
267
268 mi = ui_menu_item_add_real(md, text, path, id, select_func, select_data);
269 if (mi) mi->sensitive = sensitive;
270 }
271
272 /*
273 *---------------------------------------------------------
274 * menu computation
275 *---------------------------------------------------------
276 */
277
ui_menu_calc_size(MenuItemData * mi)278 static void ui_menu_calc_size(MenuItemData *mi)
279 {
280 UIData *ui;
281 SkinData *skin;
282 WidgetData *wd;
283 gint w, h;
284 gint ww, wh;
285 gint px, py;
286
287 ui = mi->ui;
288 skin = ui->skin;
289
290 w = skin->width_def;
291 h = skin->height_def;
292
293 wd = skin_widget_get_by_key(skin, "menu", list_type_id());
294 if (wd)
295 {
296 ListData *ld;
297 GList *work;
298 gint rows;
299 gint string_width;
300
301 ld = wd->widget;
302
303 rows = MAX(0, g_list_length(mi->children) - 1);
304 string_width = 32;
305
306 work = mi->children;
307 while (work)
308 {
309 MenuItemData *mi;
310 gint cw;
311
312 mi = work->data;
313 work = work->next;
314
315 cw = font_string_length(ld->font, mi->text);
316 if (cw > string_width) string_width = cw;
317 }
318
319 h += ld->row_height * rows;
320 w += string_width;
321
322 mi->y -= ld->y + ld->border_top;
323 }
324 else
325 {
326 printf("warning: ui menu \"%s\" contains no list widget\n", ui->key);
327 }
328
329 ww = gdk_screen_width();
330 wh = gdk_screen_height();
331
332 if (mi->parent)
333 {
334 MenuItemData *parent;
335 ListData *ld;
336
337 parent = mi->parent;
338
339 wd = skin_widget_get_by_key(parent->ui->skin, "menu", list_type_id());
340 if (wd)
341 {
342 ld = wd->widget;
343 }
344 else
345 {
346 ld = NULL;
347 }
348
349 if (parent->go_left ||
350 mi->x >= ww - w ||
351 (ld && mi->x < parent->x + ld->x + (ld->width - ld->border_right)))
352 {
353 mi->go_left = TRUE;
354 }
355
356 if (mi->go_left)
357 {
358 if (ld)
359 {
360 px = CLAMP(parent->x - w + ld->x + ld->border_left, 0, ww - w);
361 }
362 else
363 {
364 px = CLAMP(parent->x - w, 0, ww - w);
365 }
366 }
367 else
368 {
369 px = CLAMP(mi->x, 0, ww - w);
370 }
371 }
372 else
373 {
374 px = CLAMP(mi->x, 0, ww - w);
375 }
376
377 py = CLAMP(mi->y, 0, wh - h);
378
379 if (px < mi->x) mi->go_left = TRUE;
380
381 mi->x = px;
382 mi->y = py;
383
384 skin_resize(ui, w, h);
385 }
386
387 /*
388 *---------------------------------------------------------
389 * mouse / keyboard callbacks
390 *---------------------------------------------------------
391 */
392
ui_menu_proximity(MenuItemData * mi,gint x,gint y,MenuItemData * second)393 static gint ui_menu_proximity(MenuItemData *mi, gint x, gint y, MenuItemData *second)
394 {
395 if (second)
396 {
397 if (ui_menu_proximity(mi, x, y, NULL)) return FALSE;
398 return ui_menu_proximity(second, mi->x - second->x + x, mi->y - second->y + y, NULL);
399 }
400
401 return (x >= 0 && x <= mi->ui->skin->width &&
402 y >= 0 && y <= mi->ui->skin->height);
403 }
404
405 #if 0
406 static gint ui_menu_press_cb(GtkWidget *w, GdkEventButton *event, gpointer data)
407 {
408 MenuItemData *mi = data;
409
410 return FALSE;
411 }
412 #endif
413
ui_menu_release_cb(GtkWidget * w,GdkEventButton * event,gpointer data)414 static gint ui_menu_release_cb(GtkWidget *w, GdkEventButton *event, gpointer data)
415 {
416 MenuItemData *mi = data;
417 GList *work;
418
419 if (!ui_menu_proximity(mi, event->x, event->y, NULL))
420 {
421 if (mi->parent && ui_menu_proximity(mi, event->x, event->y, mi->parent))
422 {
423 gtk_widget_hide(mi->ui->window);
424 }
425 else
426 {
427 ui_menu_hide_all(mi->md);
428 }
429 return TRUE;
430 }
431
432 if (mi->md->press_button != 0 && mi->md->press_button == event->button)
433 {
434 mi->md->press_button = 0;
435 if (!mi->md->submenu_list->next && !mi->md->selection_done)
436 {
437 gtk_grab_add(mi->ui->display);
438 }
439 return FALSE;
440 }
441
442 work = g_list_last(mi->md->submenu_list);
443 if (work->data != mi) return FALSE;
444
445 ui_menu_hide_all(mi->md);
446 return TRUE;
447 }
448
ui_menu_key_cb(GtkWidget * w,GdkEventKey * event,gpointer data)449 static gint ui_menu_key_cb(GtkWidget *w, GdkEventKey *event, gpointer data)
450 {
451 MenuItemData *mi = data;
452 gint ret = FALSE;
453
454 switch (event->keyval)
455 {
456 case GDK_Escape:
457 ui_menu_hide_all(mi->md);
458 ret = TRUE;
459 break;
460 default:
461 break;
462 }
463
464 return ret;
465 }
466
467 /*
468 *---------------------------------------------------------
469 * the list callbacks
470 *---------------------------------------------------------
471 */
472
ui_menu_list_size_cb(ListData * list,const gchar * key,gpointer data)473 static gint ui_menu_list_size_cb(ListData *list, const gchar *key, gpointer data)
474 {
475 MenuItemData *mi = data;
476
477 list_set_menu_style(list, TRUE);
478
479 return g_list_length(mi->children);
480 }
481
ui_menu_item_info_cb(ListData * list,const gchar * key,gint row,ListRowData * rd,gpointer data)482 static gint ui_menu_item_info_cb(ListData *list, const gchar *key,
483 gint row, ListRowData *rd, gpointer data)
484 {
485 MenuItemData *mi = data;
486 MenuItemData *item;
487 gint flag;
488
489 item = g_list_nth_data(mi->children, row);
490 if (!item) return FALSE;
491
492 list_row_column_set_text(list, rd, "text", item->text);
493
494 if (item->children)
495 {
496 flag = 1;
497 }
498 else if (item->is_toggle)
499 {
500 flag = (item->toggle_active) ? 3 : 2;
501 }
502 else
503 {
504 flag = 0;
505 }
506 list_row_set_flag(rd, flag);
507 list_row_set_sensitive(rd, item->sensitive && mi->sensitive);
508
509 return TRUE;
510 }
511
ui_menu_list_activate(MenuItemData * mi,ListData * ld,gint row)512 static gint ui_menu_list_activate(MenuItemData *mi, ListData *ld, gint row)
513 {
514 gint x, y;
515
516 if (!mi->children) return FALSE;
517
518 x = ld->x + ld->width - ld->border_right;
519 y = ld->y + ld->border_top + (ld->row_height * (row - ld->row_start));
520
521 x += mi->parent->x;
522 y += mi->parent->y;
523
524 ui_menu_submenu_activate(mi->md, mi, x, y, 0, ui_menu_event_time());
525
526 return TRUE;
527 }
528
ui_menu_list_click_cb(ListData * list,const gchar * key,gint row,ListRowData * rd,gint button,gpointer data)529 static void ui_menu_list_click_cb(ListData *list, const gchar *key, gint row,
530 ListRowData *rd, gint button, gpointer data)
531 {
532 MenuItemData *mi = data;
533 MenuItemData *item;
534
535 item = g_list_nth_data(mi->children, row);
536 if (!item)
537 {
538 mi->md->selection_done = TRUE;
539 return;
540 }
541
542 if (ui_menu_list_activate(item, list, row)) return;
543
544 item->md->selection_done = TRUE;
545 if (item->select_func)
546 {
547 item->select_func(item->md, item->path, item->id, item->select_data);
548 }
549
550 ui_menu_hide_all(mi->md);
551 }
552
ui_menu_list_select_cb(ListData * list,const gchar * key,gint row,ListRowData * rd,gpointer data)553 static void ui_menu_list_select_cb(ListData *list, const gchar *key, gint row,
554 ListRowData *rd, gpointer data)
555 {
556 ui_menu_list_click_cb(list, key, row, rd, 0, data);
557 }
558
ui_menu_list_move_cb(ListData * list,const gchar * key,gint row,gint activated,gint up,gpointer data)559 static gint ui_menu_list_move_cb(ListData *list, const gchar *key,
560 gint row, gint activated, gint up, gpointer data)
561 {
562 MenuItemData *mi = data;
563 MenuItemData *item;
564
565 item = g_list_nth_data(mi->children, row);
566 if (!item)
567 {
568 mi->md->selection_done = TRUE;
569 return FALSE;
570 }
571
572 if (activated)
573 {
574 if (up && ui_menu_list_activate(item, list, row)) return TRUE;
575 if (!up && mi->parent)
576 {
577 gtk_widget_hide(mi->ui->window);
578 return TRUE;
579 }
580 }
581
582 return FALSE;
583 }
584
ui_menu_back_cb(UIData * ui,GdkPixbuf * pixbuf,gpointer data)585 static gint ui_menu_back_cb(UIData *ui, GdkPixbuf *pixbuf, gpointer data)
586 {
587 MenuItemData *mi = data;
588
589 if (!GTK_WIDGET_VISIBLE(mi->ui->display) || !GTK_WIDGET_REALIZED(mi->ui->display)) return FALSE;
590
591 util_pixbuf_fill_from_root_window(pixbuf, mi->x, mi->y, TRUE);
592
593 return TRUE;
594 }
595
596 /*
597 *---------------------------------------------------------
598 * submenu handling
599 *---------------------------------------------------------
600 */
601
ui_menu_submenu_hide_cb(GtkWidget * widget,gpointer data)602 static void ui_menu_submenu_hide_cb(GtkWidget *widget, gpointer data)
603 {
604 MenuItemData *mi = data;
605 UIMenuData *md;
606
607 md = mi->md;
608
609 if (md->preview == mi)
610 {
611 md->preview = NULL;
612 return;
613 }
614
615 gtk_grab_remove(mi->ui->display);
616 md->submenu_list = g_list_remove(md->submenu_list, mi);
617
618 if (!mi->parent)
619 {
620 printf("warning: backed off tree at %s\n", mi->path);
621 return;
622 }
623
624 ui_menu_submenu_grab(mi->parent, ui_menu_event_time());
625 }
626
ui_menu_submenu_delete_cb(GtkWidget * widget,GdkEventAny * event,gpointer data)627 static gint ui_menu_submenu_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer data)
628 {
629 gtk_widget_hide(widget);
630 return TRUE;
631 }
632
ui_menu_submenu_activate(UIMenuData * md,MenuItemData * mi,gint x,gint y,gint button,guint32 event_time)633 static void ui_menu_submenu_activate(UIMenuData *md, MenuItemData *mi,
634 gint x, gint y, gint button, guint32 event_time)
635 {
636 if (!mi->ui)
637 {
638 const gchar *key = NULL;
639
640 ui_menu_submenu_new(mi, md->ui->class, mi->path);
641 g_signal_connect(G_OBJECT(mi->ui->window), "delete_event",
642 G_CALLBACK(ui_menu_submenu_delete_cb), mi);
643 g_signal_connect(G_OBJECT(mi->ui->window), "hide",
644 G_CALLBACK(ui_menu_submenu_hide_cb), mi);
645
646 if (mi->parent)
647 {
648 MenuItemData *parent;
649 WidgetData *wd;
650
651 parent = mi->parent;
652 wd = skin_widget_get_by_key(parent->ui->skin, "menu", list_type_id());
653 if (wd)
654 {
655 key = ui_widget_get_data(wd, "data");
656 }
657 if (!key) key = parent->ui->skin_mode_key;
658 }
659
660 if (!key) key = "skindata_menu";
661
662 if (!ui_skin_load(mi->ui, md->ui->skin_path, key))
663 {
664 /* try default */
665 if (!ui_skin_load(mi->ui, md->ui->skin_path, "skindata_menu"))
666 {
667 /* fall back to something */
668 ui_skin_load(mi->ui, NULL, "skindata_menu");
669 }
670 }
671 }
672
673 md->submenu_list = g_list_append(md->submenu_list, mi);
674
675 ui_menu_submenu_ungrab(mi->parent, event_time);
676
677 ui_menu_submenu_show(md, mi, x, y);
678 ui_menu_submenu_grab_focus(md, mi, button, event_time);
679 }
680
681 /*
682 *---------------------------------------------------------
683 * setup, show, etc.
684 *---------------------------------------------------------
685 */
686
ui_menu_destroy(GtkWidget * widget,gpointer data)687 static void ui_menu_destroy(GtkWidget *widget, gpointer data)
688 {
689 UIMenuData *md = data;
690 GList *work;
691
692 work = md->list;
693 while (work)
694 {
695 MenuItemData *mi;
696
697 mi = work->data;
698 work = work->next;
699
700 ui_menu_item_free(mi);
701 }
702 g_list_free(md->list);
703
704 g_free(md);
705 }
706
ui_menu_hide_cb(GtkWidget * widget,gpointer data)707 static void ui_menu_hide_cb(GtkWidget *widget, gpointer data)
708 {
709 UIData *ui = data;
710
711 gtk_grab_remove(ui->display);
712 ui_close(ui);
713 }
714
ui_menu_delete_cb(GtkWidget * widget,GdkEventAny * event,gpointer data)715 static gint ui_menu_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer data)
716 {
717 gtk_widget_hide(widget);
718 return TRUE;
719 }
720
ui_menu_submenu_new(MenuItemData * mi,const gchar * class,const gchar * key)721 static UIData *ui_menu_submenu_new(MenuItemData *mi, const gchar *class, const gchar *key)
722 {
723 UIData *ui;
724 GtkWidget *window;
725
726 window = gtk_window_new(GTK_WINDOW_POPUP);
727
728 ui = ui_new_into_container(class, key, window);
729 ui->window = window;
730 mi->ui = ui;
731
732 ui_focus_set(ui, TRUE);
733
734 #if 0
735 g_signal_connect(G_OBJECT(ui->display), "button_press_event",
736 G_CALLBACK(ui_menu_press_cb), mi);
737 #endif
738 g_signal_connect(G_OBJECT(ui->display), "button_release_event",
739 G_CALLBACK(ui_menu_release_cb), mi);
740 g_signal_connect(G_OBJECT(ui->display), "key_press_event",
741 G_CALLBACK(ui_menu_key_cb), mi);
742
743 /* set it up */
744 list_register_key("menu", ui,
745 ui_menu_list_size_cb, mi,
746 ui_menu_item_info_cb, mi,
747 ui_menu_list_click_cb, mi,
748 ui_menu_list_select_cb, mi,
749 NULL, NULL);
750 list_register_menu_funcs("menu", ui, ui_menu_list_move_cb, mi);
751
752 if (mi->md->ui)
753 {
754 /* sub windows inheret parent functions */
755 ui->skin_func = mi->md->ui->skin_func;
756 ui->skin_data = mi->md->ui->skin_data;
757
758 /* and the ui group */
759 ui_group_set_child(mi->md->ui, ui);
760 }
761 ui_set_back_callback(ui, ui_menu_back_cb, mi);
762
763 return ui;
764 }
765
ui_menu_new(const gchar * class,const gchar * key)766 UIMenuData *ui_menu_new(const gchar *class, const gchar *key)
767 {
768 UIMenuData *md;
769 MenuItemData *mi;
770
771 md = g_new0(UIMenuData, 1);
772
773 md->list = NULL;
774 md->preview = NULL;
775
776 ui_menu_item_add(md, "/", "/", 0, NULL, NULL);
777 mi = md->list->data;
778 md->submenu_list = g_list_append(md->submenu_list, mi);
779 md->ui = ui_menu_submenu_new(mi, class, key);
780
781 g_signal_connect(G_OBJECT(mi->ui->window), "destroy",
782 G_CALLBACK(ui_menu_destroy), md);
783 g_signal_connect(G_OBJECT(mi->ui->window), "delete_event",
784 G_CALLBACK(ui_menu_delete_cb), md);
785 g_signal_connect(G_OBJECT(mi->ui->window), "hide",
786 G_CALLBACK(ui_menu_hide_cb), mi->ui);
787
788 return md;
789 }
790
ui_menu_submenu_show(UIMenuData * md,MenuItemData * mi,gint x,gint y)791 static void ui_menu_submenu_show(UIMenuData *md, MenuItemData *mi, gint x, gint y)
792 {
793 if (!mi || !mi->ui->skin) return;
794
795 if (x < 0 || y < 0)
796 {
797 GdkModifierType modmask;
798 gdk_window_get_pointer(NULL, &x, &y, &modmask);
799 }
800
801 mi->x = x;
802 mi->y = y;
803 ui_menu_calc_size(mi);
804
805 gtk_window_move(GTK_WINDOW(mi->ui->window), mi->x, mi->y);
806 gtk_widget_show(mi->ui->window);
807
808 mi->md->selection_done = FALSE;
809 }
810
ui_menu_submenu_ungrab(MenuItemData * mi,guint32 event_time)811 static void ui_menu_submenu_ungrab(MenuItemData *mi, guint32 event_time)
812 {
813 if (GTK_WIDGET_VISIBLE(mi->ui->window))
814 {
815 gdk_pointer_ungrab(event_time);
816 gdk_keyboard_ungrab(event_time);
817 }
818 gtk_grab_remove(mi->ui->display);
819 }
820
ui_menu_submenu_grab(MenuItemData * mi,guint32 event_time)821 static void ui_menu_submenu_grab(MenuItemData *mi, guint32 event_time)
822 {
823 gdk_pointer_grab(mi->ui->display->window, FALSE,
824 GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK,
825 NULL, NULL, event_time);
826 gdk_keyboard_grab(mi->ui->display->window, TRUE, event_time);
827 gtk_grab_add(mi->ui->display);
828 }
829
ui_menu_submenu_grab_focus(UIMenuData * md,MenuItemData * mi,gint button,guint32 event_time)830 static void ui_menu_submenu_grab_focus(UIMenuData *md, MenuItemData *mi, gint button, guint32 event_time)
831 {
832 WidgetData *wd;
833
834 /* we peek at current event if -1 */
835 if (button < 0) button = ui_menu_is_button_press();
836
837 /* set focus to menu list widget,
838 * enable it for display,
839 * then redraw first row to show the focus
840 */
841 wd = skin_widget_get_by_key(mi->ui->skin, "menu", list_type_id());
842 if (wd)
843 {
844 mi->ui->focus_widget = wd;
845 if (button > 0)
846 {
847 ListData *ld = wd->widget;
848
849 mi->ui->active_widget = wd;
850
851 /* force pressed state */
852 ld->pressed = TRUE;
853 ld->press_row = 0;
854 }
855 }
856
857 GTK_WIDGET_SET_FLAGS(mi->ui->display, GTK_HAS_FOCUS);
858 list_row_update("menu", mi->ui, 0);
859
860 ui_menu_submenu_grab(mi, event_time);
861
862 mi->md->press_button = button;
863 }
864
ui_menu_show(UIMenuData * md,gint x,gint y,gint button,guint32 event_time)865 void ui_menu_show(UIMenuData *md, gint x, gint y, gint button, guint32 event_time)
866 {
867 ui_menu_submenu_show(md, md->list->data, x, y);
868 ui_menu_submenu_grab_focus(md, md->list->data, -1, event_time);
869 }
870
871