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 cxListDestroy(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
394
395 static ui_gmenu_add_f createMenuItem[] = {
396 ui_gmenu_add_menu,
397 ui_gmenu_add_menuitem,
398 ui_gmenu_add_checkitem,
399 ui_gmenu_add_radioitem,
400 ui_gmenu_add_menuitem_list,
401 ui_gmenu_add_menuitem_list,
402 ui_gmenu_add_menuitem_list,
403 ui_gmenu_add_menuseparator
404 };
405
406 void ui_gmenu_add_menu_items(GMenu *parent,
int i, UiMenu *menu, UiObject *obj) {
407 UiMenuItemI *it = menu->items_begin;
408 int index =
0;
409 int index_section =
0;
410 GMenu *section =
NULL;
411 while(it) {
412 if(it->type ==
UI_MENU_SEPARATOR) {
413 section = g_menu_new();
414 g_menu_append_section(parent,
NULL,
G_MENU_MODEL(section));
415 index++;
416 index_section =
0;
417 }
else {
418 if(section) {
419 createMenuItem[it->type](section, index_section++, it, obj);
420 }
else {
421 createMenuItem[it->type](parent, index++, it, obj);
422 }
423 }
424 it = it->next;
425 }
426 }
427
428 void ui_gmenu_add_menu(GMenu *parent,
int index, UiMenuItemI *item, UiObject *obj) {
429 UiMenu *mi = (UiMenu*)item;
430 GMenu *menu = g_menu_new();
431 ui_gmenu_add_menu_items(menu,
0, mi, obj);
432 g_menu_append_submenu(parent, mi->label,
G_MENU_MODEL(menu));
433 }
434
435 static void action_enable(GSimpleAction *action,
int enabled) {
436 g_simple_action_set_enabled(action, enabled);
437 }
438
439 void ui_gmenu_add_menuitem(GMenu *parent,
int index, UiMenuItemI *item, UiObject *obj) {
440 UiMenuItem *i = (UiMenuItem*)item;
441
442 GSimpleAction *action = g_simple_action_new(item->id,
NULL);
443 g_action_map_add_action(obj->ctx->action_map,
G_ACTION(action));
444
445 if(i->groups) {
446 CxList *groups = cxArrayListCreateSimple(
sizeof(
int), i->ngroups);
447 cxListAddArray(groups, i->groups, i->ngroups);
448 uic_add_group_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups);
449 cxListDestroy(groups);
450 }
451
452 if(i->callback !=
NULL) {
453 UiEventData *event = malloc(
sizeof(UiEventData));
454 event->obj = obj;
455 event->userdata = i->userdata;
456 event->callback = i->callback;
457 event->value =
0;
458 event->customdata =
NULL;
459
460 g_signal_connect(
461 action,
462 "activate",
463 G_CALLBACK(ui_activate_event_wrapper),
464 event);
465 g_signal_connect(
466 obj->widget,
467 "destroy",
468 G_CALLBACK(ui_destroy_userdata),
469 event);
470 }
471
472 char action_name[
32];
473 snprintf(action_name,
32,
"win.%s", item->id);
474 g_menu_append(parent, i->label, action_name);
475 }
476
477 void ui_gmenu_add_menuseparator(GMenu *p,
int index, UiMenuItemI *item, UiObject *obj) {
478
479 }
480
481 void ui_gmenu_add_checkitem(GMenu *p,
int index, UiMenuItemI *item, UiObject *obj) {
482 UiMenuCheckItem *checkitem = (UiMenuCheckItem*)item;
483
484
485 }
486
487 void ui_gmenu_add_radioitem(GMenu *p,
int index, UiMenuItemI *item, UiObject *obj) {
488
489 }
490
491 void ui_gmenu_add_menuitem_list(GMenu *p,
int index, UiMenuItemI *item, UiObject *obj) {
492 UiMenuItemList *il = (UiMenuItemList*)item;
493
494 const CxAllocator *a = obj->ctx->allocator;
495
496 UiActiveGMenuItemList *ls = cxMalloc(
497 a,
498 sizeof(UiActiveGMenuItemList));
499
500 ls->object = obj;
501 ls->menu = p;
502 ls->index = index;
503 ls->oldcount =
0;
504 ls->getvalue = il->getvalue;
505
506 UiVar* var = uic_create_var(ui_global_context(), il->varname,
UI_VAR_LIST);
507 ls->var = var;
508 UiList *list = var->value;
509
510 ls->callback = il->callback;
511 ls->userdata = il->userdata;
512
513 UiObserver *observer = ui_observer_new((ui_callback)ui_update_gmenu_item_list, ls);
514 list->observers = ui_obsvlist_add(list->observers, observer);
515 uic_list_register_observer_destructor(obj->ctx, list, observer);
516
517 GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new(
"i"));
518 g_action_map_add_action(obj->ctx->action_map,
G_ACTION(action));
519 snprintf(ls->action,
32,
"win.%s", item->id);
520
521
522 UiEventData *event = malloc(
sizeof(UiEventData));
523 event->obj = obj;
524 event->userdata = il->userdata;
525 event->callback = il->callback;
526 event->customdata = var;
527 event->value =
0;
528
529 g_signal_connect(
530 action,
531 "activate",
532 G_CALLBACK(ui_menu_list_item_activate_event_wrapper),
533 event);
534 g_signal_connect(
535 obj->widget,
536 "destroy",
537 G_CALLBACK(ui_destroy_userdata),
538 event);
539
540 ui_update_gmenu_item_list(
NULL, ls);
541 }
542
543 void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
544 int intval = event->value;
545 if(parameter && g_variant_is_of_type(parameter,
G_VARIANT_TYPE_INT32)) {
546 intval = g_variant_get_int32(parameter);
547 }
548
549 UiEvent evt;
550 evt.obj = event->obj;
551 evt.window = event->obj->window;
552 evt.document = event->obj->ctx->document;
553 evt.eventdata = event->customdata;
554 evt.intval = intval;
555 event->callback(&evt, event->userdata);
556 }
557
558 void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
559 int index = g_variant_get_int32(parameter);
560 UiVar *var = event->customdata;
561 UiList *list = var->value;
562
563 UiEvent evt;
564 evt.obj = event->obj;
565 evt.window = event->obj->window;
566 evt.document = event->obj->ctx->document;
567 evt.eventdata = ui_list_get(list, index);
568 evt.intval = index;
569 event->callback(&evt, event->userdata);
570
571 }
572
573 void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list) {
574
575 for(
int i=
0;i<list->oldcount;i++) {
576 g_menu_remove(list->menu, list->index);
577 }
578 UiList *ls = list->var->value;
579
580
581 ui_getvaluefunc getvalue = list->getvalue;
582 int i =
0;
583 void* elm = ui_list_first(ls);
584 while(elm) {
585 char *label = (
char*) (getvalue ? getvalue(elm,
0) : elm);
586
587 GMenuItem *item = g_menu_item_new(label,
NULL);
588 GVariant *v = g_variant_new(
"i", i);
589 g_menu_item_set_action_and_target_value(item, list->action, v);
590 g_menu_insert_item(list->menu, list->index+i, item);
591
592 elm = ui_list_next(ls);
593 i++;
594 }
595
596 list->oldcount = i;
597 }
598
599
600
601
602 static void remove_popover(GtkWidget *object, GtkPopoverMenu *menu) {
603 gtk_widget_unparent(
GTK_WIDGET(menu));
604 }
605
606 UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, GtkWidget *widget) {
607 GMenu *menu = g_menu_new();
608 ui_gmenu_add_menu_items(menu,
0, builder->menus_begin, obj);
609 GtkWidget *contextmenu = gtk_popover_menu_new_from_model(
G_MENU_MODEL(menu));
610 gtk_popover_set_has_arrow(
GTK_POPOVER(contextmenu),
FALSE);
611 gtk_widget_set_halign(contextmenu,
GTK_ALIGN_START);
612 gtk_widget_set_parent(
GTK_WIDGET(contextmenu), widget);
613 g_signal_connect(
614 widget,
615 "destroy",
616 G_CALLBACK(remove_popover),
617 contextmenu);
618 return GTK_POPOVER_MENU(contextmenu);
619 }
620
621 static void gesture_button_press(GtkGestureClick *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data) {
622 gtk_popover_set_pointing_to(
GTK_POPOVER(user_data), &(GdkRectangle){ x, y,
0,
0 });
623 gtk_popover_popup(
GTK_POPOVER(user_data));
624 }
625
626 void ui_widget_set_contextmenu(GtkWidget *widget, GtkPopoverMenu *menu) {
627 GtkGesture *gesture = gtk_gesture_click_new();
628 gtk_gesture_single_set_button(
GTK_GESTURE_SINGLE(gesture),
3);
629 gtk_widget_add_controller(widget,
GTK_EVENT_CONTROLLER(gesture));
630 g_signal_connect(gesture,
"pressed",
G_CALLBACK(gesture_button_press), menu);
631 }
632
633 void ui_contextmenu_popup(
UIMENU menu,
UIWIDGET widget,
int x,
int y) {
634 gtk_popover_set_pointing_to(
GTK_POPOVER(menu), &(GdkRectangle){ x, y,
0,
0 });
635 gtk_popover_popup(
GTK_POPOVER(menu));
636 }
637
638 #endif
639