1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <inttypes.h>
32 #include <stdarg.h>
33
34 #include "menu.h"
35 #include "toolkit.h"
36 #include "../common/context.h"
37 #include "../common/menu.h"
38 #include "../common/types.h"
39 #include "../ui/properties.h"
40 #include "../ui/window.h"
41 #include "container.h"
42
43 #include <cx/linked_list.h>
44 #include <cx/array_list.h>
45
46 #if GTK_MAJOR_VERSION <=
3
47
48 static ui_menu_add_f createMenuItem[] = {
49 add_menu_widget,
50 add_menuitem_widget,
51 add_checkitem_widget,
52 add_radioitem_widget,
53 add_menuitem_list_widget,
54 add_menuitem_list_widget,
55 add_menuitem_list_widget,
56 add_menuseparator_widget
57 };
58
59
60 GtkWidget *ui_create_menubar(UiObject *obj) {
61 UiMenu *menus_begin = uic_get_menu_list();
62 if(menus_begin ==
NULL) {
63 return NULL;
64 }
65
66 GtkWidget *mb = gtk_menu_bar_new();
67
68 UiMenu *ls = menus_begin;
69 while(ls) {
70 UiMenu *menu = ls;
71 add_menu_widget(mb,
0, &menu->item, obj);
72
73 ls = (UiMenu*)ls->item.next;
74 }
75
76 return mb;
77 }
78
79 void ui_add_menu_items(GtkWidget *parent,
int i, UiMenu *menu, UiObject *obj) {
80 UiMenuItemI *it = menu->items_begin;
81 int index =
0;
82 while(it) {
83 createMenuItem[it->type](parent, index, it, obj);
84 it = it->next;
85 index++;
86 }
87 }
88
89 void add_menu_widget(GtkWidget *parent,
int i, UiMenuItemI *item, UiObject *obj) {
90 UiMenu *menu = (UiMenu*)item;
91
92 GtkWidget *menu_widget = gtk_menu_new();
93 GtkWidget *menu_item = gtk_menu_item_new_with_mnemonic(menu->label);
94 gtk_menu_item_set_submenu(
GTK_MENU_ITEM(menu_item), menu_widget);
95
96 ui_add_menu_items(menu_widget, i, menu, obj);
97
98
99 gtk_menu_shell_append(
GTK_MENU_SHELL(parent), menu_item);
100 }
101
102 void add_menuitem_widget(GtkWidget *parent,
int index, UiMenuItemI *item, UiObject *obj) {
103 UiMenuItem *i = (UiMenuItem*)item;
104
105
106 GtkWidget *widget = gtk_menu_item_new_with_mnemonic(i->label);
107
108 if(i->callback !=
NULL) {
109 UiEventData *event = malloc(
sizeof(UiEventData));
110 event->obj = obj;
111 event->userdata = i->userdata;
112 event->callback = i->callback;
113 event->value =
0;
114 event->customdata =
NULL;
115
116 g_signal_connect(
117 widget,
118 "activate",
119 G_CALLBACK(ui_menu_event_wrapper),
120 event);
121 g_signal_connect(
122 widget,
123 "destroy",
124 G_CALLBACK(ui_destroy_userdata),
125 event);
126 }
127
128 gtk_menu_shell_append(
GTK_MENU_SHELL(parent), widget);
129
130 if(i->groups) {
131 CxList *groups = cxArrayListCreateSimple(
sizeof(
int), i->ngroups);
132 cxListAddArray(groups, i->groups, i->ngroups);
133 uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups);
134 cxListFree(groups);
135 }
136 }
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 void add_menuseparator_widget(
177 GtkWidget *parent,
178 int index,
179 UiMenuItemI *item,
180 UiObject *obj)
181 {
182 gtk_menu_shell_append(
183 GTK_MENU_SHELL(parent),
184 gtk_separator_menu_item_new());
185 }
186
187 void add_checkitem_widget(GtkWidget *p,
int index, UiMenuItemI *item, UiObject *obj) {
188 UiMenuCheckItem *ci = (UiMenuCheckItem*)item;
189 GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label);
190 gtk_menu_shell_append(
GTK_MENU_SHELL(p), widget);
191
192 if(ci->callback) {
193 UiEventData *event = malloc(
sizeof(UiEventData));
194 event->obj = obj;
195 event->userdata = ci->userdata;
196 event->callback = ci->callback;
197 event->value =
0;
198 event->customdata =
NULL;
199
200 g_signal_connect(
201 widget,
202 "toggled",
203 G_CALLBACK(ui_menu_event_toggled),
204 event);
205 g_signal_connect(
206 widget,
207 "destroy",
208 G_CALLBACK(ui_destroy_userdata),
209 event);
210 }
211 }
212
213 void add_radioitem_widget(GtkWidget *p,
int index, UiMenuItemI *item, UiObject *obj) {
214
215 }
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236 void add_menuitem_list_widget(GtkWidget *p,
int index, UiMenuItemI *item, UiObject *obj) {
237 UiMenuItemList *il = (UiMenuItemList*)item;
238 const CxAllocator *a = obj->ctx->allocator;
239
240 UiActiveMenuItemList *ls = cxMalloc(
241 a,
242 sizeof(UiActiveMenuItemList));
243
244 ls->object = obj;
245 ls->menu =
GTK_MENU_SHELL(p);
246 ls->index = index;
247 ls->oldcount =
0;
248 ls->getvalue = il->getvalue;
249
250 UiVar* var = uic_create_var(ui_global_context(), il->varname,
UI_VAR_LIST);
251 ls->list = var->value;
252
253 ls->callback = il->callback;
254 ls->userdata = il->userdata;
255
256 UiObserver *observer = ui_observer_new((ui_callback)ui_update_menuitem_list, ls);
257 ls->list->observers = ui_obsvlist_add(ls->list->observers, observer);
258 uic_list_register_observer_destructor(obj->ctx, ls->list, observer);
259
260 ui_update_menuitem_list(
NULL, ls);
261 }
262
263
264 void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) {
265
266 if(list->oldcount >
0) {
267 int i =
0;
268 GList *mi = gtk_container_get_children(
GTK_CONTAINER(list->menu));
269 while(mi) {
270 if(i >= list->index && i < list->index + list->oldcount) {
271
272 gtk_widget_destroy(mi->data);
273 }
274 mi = mi->next;
275 i++;
276 }
277 }
278
279 void* elm = ui_list_first(list->list);
280 if(elm) {
281 GtkWidget *widget = gtk_separator_menu_item_new();
282 gtk_menu_shell_insert(list->menu, widget, list->index);
283 gtk_widget_show(widget);
284 }
285
286 ui_getvaluefunc getvalue = list->getvalue;
287 int i =
1;
288 while(elm) {
289 char *label = (
char*) (getvalue ? getvalue(elm,
0) : elm);
290
291 GtkWidget *widget = gtk_menu_item_new_with_label(label);
292 gtk_menu_shell_insert(list->menu, widget, list->index + i);
293 gtk_widget_show(widget);
294
295 if(list->callback) {
296 UiEventData *event = malloc(
sizeof(UiEventData));
297 event->obj = list->object;
298 event->userdata = list->userdata;
299 event->callback = list->callback;
300 event->value = i -
1;
301 event->customdata = elm;
302
303 g_signal_connect(
304 widget,
305 "activate",
306 G_CALLBACK(ui_menu_event_wrapper),
307 event);
308 g_signal_connect(
309 widget,
310 "destroy",
311 G_CALLBACK(ui_destroy_userdata),
312 event);
313 }
314
315 elm = ui_list_next(list->list);
316 i++;
317 }
318
319 list->oldcount = i;
320 }
321
322 void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event) {
323 UiEvent evt;
324 evt.obj = event->obj;
325 evt.window = event->obj->window;
326 evt.document = event->obj->ctx->document;
327 evt.eventdata = event->customdata;
328 evt.intval = event->value;
329 event->callback(&evt, event->userdata);
330 }
331
332 void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event) {
333 UiEvent evt;
334 evt.obj = event->obj;
335 evt.window = event->obj->window;
336 evt.document = event->obj->ctx->document;
337 evt.eventdata =
NULL;
338 evt.intval = gtk_check_menu_item_get_active(ci);
339 event->callback(&evt, event->userdata);
340 }
341
342 int64_t ui_checkitem_get(UiInteger *i) {
343 int state = gtk_check_menu_item_get_active(i->obj);
344 i->value = state;
345 return state;
346 }
347
348 void ui_checkitem_set(UiInteger *i,
int64_t value) {
349 i->value = value;
350 gtk_check_menu_item_set_active(i->obj, value);
351 }
352
353
354
355
356
357
358 UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj,
UIWIDGET widget) {
359 GtkWidget *menu_widget = gtk_menu_new();
360 ui_add_menu_items(menu_widget,
0, builder->menus_begin, obj);
361 return GTK_MENU(menu_widget);
362 }
363
364 static gboolean ui_button_press_event(GtkWidget *widget, GdkEvent *event, GtkMenu *menu) {
365 if(event->type ==
GDK_BUTTON_PRESS) {
366 GdkEventButton *e = (GdkEventButton*)event;
367 if(e->button ==
3) {
368 gtk_widget_show_all(
GTK_WIDGET(menu));
369 ui_contextmenu_popup(menu, widget,
0,
0);
370 }
371 }
372 return FALSE;
373 }
374
375 void ui_widget_set_contextmenu(GtkWidget *widget, GtkMenu *menu) {
376 g_signal_connect(widget,
"button-press-event", (GCallback) ui_button_press_event, menu);
377 }
378
379 void ui_contextmenu_popup(
UIMENU menu, GtkWidget *widget,
int x,
int y) {
380 #if GTK_MAJOR_VERSION >=
3 &&
GTK_MINOR_VERSION >=
16
381 gtk_menu_popup_at_pointer(menu,
NULL);
382 #else
383 gtk_menu_popup(menu,
NULL,
NULL,
0,
0,
0, gtk_get_current_event_time());
384 #endif
385 }
386
387 #endif
388
389
390
391 #if GTK_MAJOR_VERSION >=
4
392
393 GtkWidget *ui_create_menubar(UiObject *obj) {
394 UiMenu *menus_begin = uic_get_menu_list();
395 if(menus_begin ==
NULL) {
396 return NULL;
397 }
398
399 GMenu *menu = g_menu_new();
400 UiMenu *ls = menus_begin;
401 while(ls) {
402 GMenu *sub_menu = g_menu_new();
403 ui_gmenu_add_menu_items(sub_menu,
0, ls, obj);
404 g_menu_append_submenu(menu, ls->label,
G_MENU_MODEL(sub_menu));
405 ls = (UiMenu*)ls->item.next;
406 }
407
408
409
410 return gtk_popover_menu_bar_new_from_model(
G_MENU_MODEL(menu));
411 }
412
413 static ui_gmenu_add_f createMenuItem[] = {
414 ui_gmenu_add_menu,
415 ui_gmenu_add_menuitem,
416 ui_gmenu_add_checkitem,
417 ui_gmenu_add_radioitem,
418 ui_gmenu_add_menuitem_list,
419 ui_gmenu_add_menuitem_list,
420 ui_gmenu_add_menuitem_list,
421 ui_gmenu_add_menuseparator
422 };
423
424 void ui_gmenu_add_menu_items(GMenu *parent,
int i, UiMenu *menu, UiObject *obj) {
425 UiMenuItemI *it = menu->items_begin;
426 int index =
0;
427 int index_section =
0;
428 GMenu *section =
NULL;
429 while(it) {
430 if(it->type ==
UI_MENU_SEPARATOR) {
431 section = g_menu_new();
432 g_menu_append_section(parent,
NULL,
G_MENU_MODEL(section));
433 index++;
434 index_section =
0;
435 }
else {
436 if(section) {
437 createMenuItem[it->type](section, index_section++, it, obj);
438 }
else {
439 createMenuItem[it->type](parent, index++, it, obj);
440 }
441 }
442 it = it->next;
443 }
444 }
445
446 void ui_gmenu_add_menu(GMenu *parent,
int index, UiMenuItemI *item, UiObject *obj) {
447 UiMenu *mi = (UiMenu*)item;
448 GMenu *menu = g_menu_new();
449 ui_gmenu_add_menu_items(menu,
0, mi, obj);
450 g_menu_append_submenu(parent, mi->label,
G_MENU_MODEL(menu));
451 }
452
453 static void action_enable(GSimpleAction *action,
int enabled) {
454 g_simple_action_set_enabled(action, enabled);
455 }
456
457 void ui_gmenu_add_menuitem(GMenu *parent,
int index, UiMenuItemI *item, UiObject *obj) {
458 UiMenuItem *i = (UiMenuItem*)item;
459
460 GSimpleAction *action = g_simple_action_new(item->id,
NULL);
461 g_action_map_add_action(obj->ctx->action_map,
G_ACTION(action));
462
463 if(i->groups) {
464 CxList *groups = cxArrayListCreateSimple(
sizeof(
int), i->ngroups);
465 cxListAddArray(groups, i->groups, i->ngroups);
466 uic_add_group_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups);
467 cxListFree(groups);
468 }
469
470 if(i->callback !=
NULL) {
471 UiEventData *event = malloc(
sizeof(UiEventData));
472 event->obj = obj;
473 event->userdata = i->userdata;
474 event->callback = i->callback;
475 event->value =
0;
476 event->customdata =
NULL;
477
478 g_signal_connect(
479 action,
480 "activate",
481 G_CALLBACK(ui_activate_event_wrapper),
482 event);
483 g_signal_connect(
484 obj->widget,
485 "destroy",
486 G_CALLBACK(ui_destroy_userdata),
487 event);
488 }
489
490 char action_name[
32];
491 snprintf(action_name,
32,
"win.%s", item->id);
492 g_menu_append(parent, i->label, action_name);
493 }
494
495 void ui_gmenu_add_menuseparator(GMenu *p,
int index, UiMenuItemI *item, UiObject *obj) {
496
497 }
498
499 void ui_gmenu_add_checkitem(GMenu *p,
int index, UiMenuItemI *item, UiObject *obj) {
500 UiMenuCheckItem *checkitem = (UiMenuCheckItem*)item;
501
502
503 }
504
505 void ui_gmenu_add_radioitem(GMenu *p,
int index, UiMenuItemI *item, UiObject *obj) {
506
507 }
508
509 void ui_gmenu_add_menuitem_list(GMenu *p,
int index, UiMenuItemI *item, UiObject *obj) {
510 UiMenuItemList *il = (UiMenuItemList*)item;
511
512 const CxAllocator *a = obj->ctx->allocator;
513
514 UiActiveGMenuItemList *ls = cxMalloc(
515 a,
516 sizeof(UiActiveGMenuItemList));
517
518 ls->object = obj;
519 ls->menu = p;
520 ls->index = index;
521 ls->oldcount =
0;
522 ls->getvalue = il->getvalue;
523
524 UiVar* var = uic_create_var(ui_global_context(), il->varname,
UI_VAR_LIST);
525 ls->var = var;
526 UiList *list = var->value;
527
528 ls->callback = il->callback;
529 ls->userdata = il->userdata;
530
531 UiObserver *observer = ui_observer_new((ui_callback)ui_update_gmenu_item_list, ls);
532 list->observers = ui_obsvlist_add(list->observers, observer);
533 uic_list_register_observer_destructor(obj->ctx, list, observer);
534
535 GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new(
"i"));
536 g_action_map_add_action(obj->ctx->action_map,
G_ACTION(action));
537 snprintf(ls->action,
32,
"win.%s", item->id);
538
539
540 UiEventData *event = malloc(
sizeof(UiEventData));
541 event->obj = obj;
542 event->userdata = il->userdata;
543 event->callback = il->callback;
544 event->customdata = var;
545 event->value =
0;
546
547 g_signal_connect(
548 action,
549 "activate",
550 G_CALLBACK(ui_menu_list_item_activate_event_wrapper),
551 event);
552 g_signal_connect(
553 obj->widget,
554 "destroy",
555 G_CALLBACK(ui_destroy_userdata),
556 event);
557
558 ui_update_gmenu_item_list(
NULL, ls);
559 }
560
561 void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
562 int intval = event->value;
563 if(parameter && g_variant_is_of_type(parameter,
G_VARIANT_TYPE_INT32)) {
564 intval = g_variant_get_int32(parameter);
565 }
566
567 UiEvent evt;
568 evt.obj = event->obj;
569 evt.window = event->obj->window;
570 evt.document = event->obj->ctx->document;
571 evt.eventdata = event->customdata;
572 evt.intval = intval;
573 event->callback(&evt, event->userdata);
574 }
575
576 void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
577 int index = g_variant_get_int32(parameter);
578 UiVar *var = event->customdata;
579 UiList *list = var->value;
580
581 UiEvent evt;
582 evt.obj = event->obj;
583 evt.window = event->obj->window;
584 evt.document = event->obj->ctx->document;
585 evt.eventdata = ui_list_get(list, index);
586 evt.intval = index;
587 event->callback(&evt, event->userdata);
588
589 }
590
591 void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list) {
592
593 for(
int i=
0;i<list->oldcount;i++) {
594 g_menu_remove(list->menu, list->index);
595 }
596 UiList *ls = list->var->value;
597
598
599 ui_getvaluefunc getvalue = list->getvalue;
600 int i =
0;
601 void* elm = ui_list_first(ls);
602 while(elm) {
603 char *label = (
char*) (getvalue ? getvalue(elm,
0) : elm);
604
605 GMenuItem *item = g_menu_item_new(label,
NULL);
606 GVariant *v = g_variant_new(
"i", i);
607 g_menu_item_set_action_and_target_value(item, list->action, v);
608 g_menu_insert_item(list->menu, list->index+i, item);
609
610 elm = ui_list_next(ls);
611 i++;
612 }
613
614 list->oldcount = i;
615 }
616
617
618
619
620 static void remove_popover(GtkWidget *object, GtkPopoverMenu *menu) {
621 gtk_widget_unparent(
GTK_WIDGET(menu));
622 }
623
624 UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, GtkWidget *widget) {
625 GMenu *menu = g_menu_new();
626 ui_gmenu_add_menu_items(menu,
0, builder->menus_begin, obj);
627 GtkWidget *contextmenu = gtk_popover_menu_new_from_model(
G_MENU_MODEL(menu));
628 gtk_popover_set_has_arrow(
GTK_POPOVER(contextmenu),
FALSE);
629 gtk_widget_set_halign(contextmenu,
GTK_ALIGN_START);
630 gtk_widget_set_parent(
GTK_WIDGET(contextmenu), widget);
631 g_signal_connect(
632 widget,
633 "destroy",
634 G_CALLBACK(remove_popover),
635 contextmenu);
636 return GTK_POPOVER_MENU(contextmenu);
637 }
638
639 static void gesture_button_press(GtkGestureClick *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data) {
640 gtk_popover_set_pointing_to(
GTK_POPOVER(user_data), &(GdkRectangle){ x, y,
0,
0 });
641 gtk_popover_popup(
GTK_POPOVER(user_data));
642 }
643
644 void ui_widget_set_contextmenu(GtkWidget *widget, GtkPopoverMenu *menu) {
645 GtkGesture *gesture = gtk_gesture_click_new();
646 gtk_gesture_single_set_button(
GTK_GESTURE_SINGLE(gesture),
3);
647 gtk_widget_add_controller(widget,
GTK_EVENT_CONTROLLER(gesture));
648 g_signal_connect(gesture,
"pressed",
G_CALLBACK(gesture_button_press), menu);
649 }
650
651 void ui_contextmenu_popup(
UIMENU menu,
UIWIDGET widget,
int x,
int y) {
652 gtk_popover_set_pointing_to(
GTK_POPOVER(menu), &(GdkRectangle){ x, y,
0,
0 });
653 gtk_popover_popup(
GTK_POPOVER(menu));
654 }
655
656 #endif
657