ui/gtk/menu.c

changeset 112
c3f2f16fa4b8
parent 110
c00e968d018b
child 113
dde28a806552
equal deleted inserted replaced
111:81c4f73236a4 112:c3f2f16fa4b8
31 #include <inttypes.h> 31 #include <inttypes.h>
32 #include <stdarg.h> 32 #include <stdarg.h>
33 33
34 #include "menu.h" 34 #include "menu.h"
35 #include "toolkit.h" 35 #include "toolkit.h"
36 #include "widget.h"
36 #include "../common/context.h" 37 #include "../common/context.h"
37 #include "../common/menu.h" 38 #include "../common/menu.h"
38 #include "../common/types.h" 39 #include "../common/types.h"
39 #include "../ui/properties.h" 40 #include "../ui/properties.h"
40 #include "../ui/window.h" 41 #include "../ui/window.h"
41 #include "container.h" 42 #include "container.h"
42 43
43 #include <cx/linked_list.h> 44 #include <cx/linked_list.h>
44 #include <cx/array_list.h> 45 #include <cx/array_list.h>
46 #include <cx/printf.h>
45 47
46 #if GTK_MAJOR_VERSION <= 3 48 #if GTK_MAJOR_VERSION <= 3
47 49
48 static ui_menu_add_f createMenuItem[] = { 50 static ui_menu_add_f createMenuItem[] = {
49 /* UI_MENU */ add_menu_widget, 51 /* UI_MENU */ add_menu_widget,
133 uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); 135 uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups);
134 cxListFree(groups); 136 cxListFree(groups);
135 } 137 }
136 } 138 }
137 139
138 /*
139 void add_menuitem_st_widget(
140 GtkWidget *parent,
141 int index,
142 UiMenuItemI *item,
143 UiObject *obj)
144 {
145 UiStMenuItem *i = (UiStMenuItem*)item;
146
147 GtkWidget *widget = gtk_image_menu_item_new_from_stock(i->stockid, obj->ctx->accel_group);
148
149 if(i->callback != NULL) {
150 UiEventData *event = malloc(sizeof(UiEventData));
151 event->obj = obj;
152 event->userdata = i->userdata;
153 event->callback = i->callback;
154 event->value = 0;
155
156 g_signal_connect(
157 widget,
158 "activate",
159 G_CALLBACK(ui_menu_event_wrapper),
160 event);
161 g_signal_connect(
162 widget,
163 "destroy",
164 G_CALLBACK(ui_destroy_userdata),
165 event);
166 }
167
168 gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
169
170 if(i->groups) {
171 uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, i->groups);
172 }
173 }
174 */
175
176 void add_menuseparator_widget( 140 void add_menuseparator_widget(
177 GtkWidget *parent, 141 GtkWidget *parent,
178 int index, 142 int index,
179 UiMenuItemI *item, 143 UiMenuItemI *item,
180 UiObject *obj) 144 UiObject *obj)
212 176
213 void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { 177 void add_radioitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
214 // TODO 178 // TODO
215 } 179 }
216 180
217 /*
218 void add_checkitemnv_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
219 UiCheckItemNV *ci = (UiCheckItemNV*)item;
220 GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label);
221 gtk_menu_shell_append(GTK_MENU_SHELL(p), widget);
222
223 UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER);
224 if(var) {
225 UiInteger *value = var->value;
226 value->obj = widget;
227 value->get = ui_checkitem_get;
228 value->set = ui_checkitem_set;
229 value = 0;
230 } else {
231 // TODO: error
232 }
233 }
234 */
235
236 static void menuitem_list_remove_binding(void *obj) { 181 static void menuitem_list_remove_binding(void *obj) {
237 UiActiveMenuItemList *ls = obj; 182 UiActiveMenuItemList *ls = obj;
238 UiList *list = ls->var->value; 183 UiList *list = ls->var->value;
239 CxList *bindings = list->obj; 184 CxList *bindings = list->obj;
240 if(bindings) { 185 if(bindings) {
259 ls->menu = GTK_MENU_SHELL(p); 204 ls->menu = GTK_MENU_SHELL(p);
260 ls->index = index; 205 ls->index = index;
261 ls->oldcount = 0; 206 ls->oldcount = 0;
262 ls->getvalue = il->getvalue; 207 ls->getvalue = il->getvalue;
263 208
264 //UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); 209 UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
265 UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST); 210 //UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
266 ls->var = var; 211 ls->var = var;
267 if(var) { 212 if(var) {
268 UiList *list = var->value; 213 UiList *list = var->value;
269 list->update = ui_menulist_update; 214 list->update = ui_menulist_update;
270 list->getselection = NULL; 215 list->getselection = NULL;
466 int index = 0; 411 int index = 0;
467 int index_section = 0; 412 int index_section = 0;
468 GMenu *section = NULL; 413 GMenu *section = NULL;
469 while(it) { 414 while(it) {
470 if(it->type == UI_MENU_SEPARATOR) { 415 if(it->type == UI_MENU_SEPARATOR) {
416 if(section) {
417 g_object_unref(section);
418 }
471 section = g_menu_new(); 419 section = g_menu_new();
472 g_menu_append_section(parent, NULL, G_MENU_MODEL(section)); 420 g_menu_append_section(parent, NULL, G_MENU_MODEL(section));
473 index++; 421 index++;
474 index_section = 0; 422 index_section = 0;
475 } else { 423 } else {
479 createMenuItem[it->type](parent, index++, it, obj); 427 createMenuItem[it->type](parent, index++, it, obj);
480 } 428 }
481 } 429 }
482 it = it->next; 430 it = it->next;
483 } 431 }
432 if(section) {
433 g_object_unref(section);
434 }
484 } 435 }
485 436
486 void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { 437 void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
487 UiMenu *mi = (UiMenu*)item; 438 UiMenu *mi = (UiMenu*)item;
488 GMenu *menu = g_menu_new(); 439 GMenu *menu = g_menu_new();
489 ui_gmenu_add_menu_items(menu, 0, mi, obj); 440 ui_gmenu_add_menu_items(menu, 0, mi, obj);
490 g_menu_append_submenu(parent, mi->label, G_MENU_MODEL(menu)); 441 g_menu_append_submenu(parent, mi->label, G_MENU_MODEL(menu));
442 g_object_unref(menu);
491 } 443 }
492 444
493 static void action_enable(GSimpleAction *action, int enabled) { 445 static void action_enable(GSimpleAction *action, int enabled) {
494 g_simple_action_set_enabled(action, enabled); 446 g_simple_action_set_enabled(action, enabled);
495 } 447 }
497 void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { 449 void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
498 UiMenuItem *i = (UiMenuItem*)item; 450 UiMenuItem *i = (UiMenuItem*)item;
499 451
500 GSimpleAction *action = g_simple_action_new(item->id, NULL); 452 GSimpleAction *action = g_simple_action_new(item->id, NULL);
501 g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); 453 g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
454 g_object_unref(action);
502 455
503 if(i->groups) { 456 if(i->groups) {
504 CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups); 457 CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups);
505 cxListAddArray(groups, i->groups, i->ngroups); 458 cxListAddArray(groups, i->groups, i->ngroups);
506 uic_add_group_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups); 459 uic_add_group_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups);
527 G_CALLBACK(ui_destroy_userdata), 480 G_CALLBACK(ui_destroy_userdata),
528 event); 481 event);
529 } 482 }
530 483
531 char action_name[32]; 484 char action_name[32];
532 snprintf(action_name, 32, "win.%s", item->id); 485 snprintf(action_name, 32, "win.%s", item->id);
486
533 g_menu_append(parent, i->label, action_name); 487 g_menu_append(parent, i->label, action_name);
534 } 488 }
535 489
536 void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { 490 void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) {
537 491
541 UiMenuCheckItem *checkitem = (UiMenuCheckItem*)item; 495 UiMenuCheckItem *checkitem = (UiMenuCheckItem*)item;
542 496
543 // TODO 497 // TODO
544 } 498 }
545 499
546 void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { 500
547 501
502 typedef struct UiCallbackData {
503 ui_callback callback;
504 void *userdata;
505 } UiCallbackData;
506
507 static void radiogroup_remove_binding(void *obj) {
508 UiMenuRadioGroup *group = obj;
509 if(group->var) {
510 UiInteger *i = group->var->value;
511 CxList *bindings = i->obj;
512 if(bindings) {
513 (void)cxListFindRemove(bindings, obj);
514 }
515 }
516 }
517
518 static void stateful_action_notify_group(UiMenuRadioGroup *group, UiInteger *i) {
519 UiEvent event;
520 event.obj = group->obj;
521 event.window = event.obj->window;
522 event.document = event.obj->ctx->document;
523 event.eventdata = NULL;
524 event.eventdatatype = 0;
525 event.intval = (int)i->value;
526 event.set = ui_get_setop();
527
528 CxIterator iter = cxListIterator(group->callbacks);
529 cx_foreach(UiCallbackData *, cb, iter) {
530 if(cb->callback) {
531 cb->callback(&event, cb->userdata);
532 }
533 }
534
535 UiObserver *obs = i->observers;
536 while(obs) {
537 if(obs->callback) {
538 obs->callback(&event, obs->data);
539 }
540 obs = obs->next;
541 }
542 }
543
544 static void ui_action_set_state(UiInteger *i, int64_t value) {
545 i->value = value;
546 CxList *bindings = i->obj;
547 CxIterator iter = cxListIterator(bindings);
548 cx_foreach(UiMenuRadioGroup *, group, iter) {
549 char buf[32];
550 snprintf(buf, 32, "%d", (int)value);
551 GVariant *state = g_variant_new_string(buf);
552 g_action_change_state(G_ACTION(group->action), state);
553 stateful_action_notify_group(group, i);
554 }
555 }
556
557 static int64_t ui_action_get_state(UiInteger *i) {
558 return i->value;
559 }
560
561 static UiMenuRadioGroup* create_radio_group(UiObject *obj, UiMenuRadioItem *item, GSimpleAction *action) {
562 UiMenuRadioGroup *group = cxZalloc(obj->ctx->allocator, sizeof(UiMenuRadioGroup));
563 group->callbacks = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(UiCallbackData), 8);
564 group->var = uic_create_var(ui_global_context(), item->varname, UI_VAR_INTEGER);
565 group->obj = obj;
566 group->action = action;
567 if(group->var) {
568 UiInteger *i = group->var->value;
569 CxList *bindings = i->obj;
570 if(!bindings) {
571 bindings = cxLinkedListCreate(group->var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
572 i->obj = bindings;
573 i->set = ui_action_set_state;
574 i->get = ui_action_get_state;
575 }
576 cxListAdd(bindings, group);
577 // the destruction of the toplevel obj must remove the binding
578 uic_context_add_destructor(obj->ctx, radiogroup_remove_binding, group);
579 }
580 return group;
581 }
582
583 static void stateful_action_activate(
584 GSimpleAction *action,
585 GVariant *state,
586 UiMenuRadioGroup *group)
587 {
588 g_simple_action_set_state(action, state);
589
590 UiVar *var = group->var;
591 if(!var) {
592 return;
593 }
594 UiInteger *i = var->value;
595
596 gsize len;
597 const char *s = g_variant_get_string(state, &len);
598 cxstring value = cx_strn(s, len);
599 int v;
600 if(!cx_strtoi(value, &v, 10)) {
601 i->value = v;
602 }
603
604 CxList *bindings = i->obj;
605 CxIterator iter = cxListIterator(bindings);
606 cx_foreach(UiMenuRadioGroup *, gr, iter) {
607 stateful_action_notify_group(gr, i);
608 }
609 }
610
611
612 void ui_gmenu_add_radioitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) {
613 UiMenuRadioItem *i = (UiMenuRadioItem*)item;
614
615 if(!i->varname) {
616 return;
617 }
618
619 // All radio buttons with the name varname use the same GAction
620 UiMenuRadioGroup *group = NULL;
621 GAction *action = g_action_map_lookup_action(obj->ctx->action_map, i->varname);
622 if(!action) {
623 GVariant *state = g_variant_new_string("0");
624 GSimpleAction *newAction = g_simple_action_new_stateful(i->varname, G_VARIANT_TYPE_STRING, state);
625 g_action_map_add_action(obj->ctx->action_map, G_ACTION(newAction));
626 g_object_unref(newAction);
627
628 group = create_radio_group(obj, i, newAction);
629 g_object_set_data(G_OBJECT(newAction), "ui_radiogroup", group);
630
631 g_signal_connect(
632 newAction,
633 "change-state",
634 G_CALLBACK(stateful_action_activate),
635 group);
636 } else {
637 group = g_object_get_data(G_OBJECT(action), "ui_radiogroup");
638 if(!group) {
639 fprintf(stderr, "Error: missing ui_radiogroup property for action %s\n", i->varname);
640 return; // error, should not happen
641 }
642 }
643
644 size_t item_index = cxListSize(group->callbacks);
645
646 UiCallbackData cb;
647 cb.callback = i->callback;
648 cb.userdata = i->userdata;
649 cxListAdd(group->callbacks, &cb);
650
651
652 cxmutstr action_name = cx_asprintf("win.%s::%d", i->varname, (int)item_index);
653 g_menu_append(parent, i->label, action_name.ptr);
654 free(action_name.ptr);
548 } 655 }
549 656
550 static void menuitem_list_remove_binding(void *obj) { 657 static void menuitem_list_remove_binding(void *obj) {
551 UiActiveGMenuItemList *ls = obj; 658 UiActiveGMenuItemList *ls = obj;
552 UiList *list = ls->var->value; 659 UiList *list = ls->var->value;
576 ls->oldcount = 0; 683 ls->oldcount = 0;
577 ls->getvalue = il->getvalue; 684 ls->getvalue = il->getvalue;
578 685
579 GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i")); 686 GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i"));
580 g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); 687 g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
688 g_object_unref(action);
581 snprintf(ls->action, 32, "win.%s", item->id); 689 snprintf(ls->action, 32, "win.%s", item->id);
582 690
583 UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); 691 UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
584 //UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST); 692 //UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
585 ls->var = var; 693 ls->var = var;
689 797
690 GMenuItem *item = g_menu_item_new(label, NULL); 798 GMenuItem *item = g_menu_item_new(label, NULL);
691 GVariant *v = g_variant_new("i", i); 799 GVariant *v = g_variant_new("i", i);
692 g_menu_item_set_action_and_target_value(item, list->action, v); 800 g_menu_item_set_action_and_target_value(item, list->action, v);
693 g_menu_insert_item(list->menu, list->index+i, item); 801 g_menu_insert_item(list->menu, list->index+i, item);
802 g_object_unref(item);
694 803
695 elm = ui_list_next(ls); 804 elm = ui_list_next(ls);
696 i++; 805 i++;
697 } 806 }
698 807
708 817
709 UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, GtkWidget *widget) { 818 UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, GtkWidget *widget) {
710 GMenu *menu = g_menu_new(); 819 GMenu *menu = g_menu_new();
711 ui_gmenu_add_menu_items(menu, 0, builder->menus_begin, obj); 820 ui_gmenu_add_menu_items(menu, 0, builder->menus_begin, obj);
712 GtkWidget *contextmenu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu)); 821 GtkWidget *contextmenu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu));
822 g_object_unref(menu);
713 gtk_popover_set_has_arrow(GTK_POPOVER(contextmenu), FALSE); 823 gtk_popover_set_has_arrow(GTK_POPOVER(contextmenu), FALSE);
714 gtk_widget_set_halign(contextmenu, GTK_ALIGN_START); 824 gtk_widget_set_halign(contextmenu, GTK_ALIGN_START);
715 gtk_widget_set_parent(GTK_WIDGET(contextmenu), widget); 825 gtk_widget_set_parent(GTK_WIDGET(contextmenu), widget);
716 g_signal_connect( 826 g_signal_connect(
717 widget, 827 widget,

mercurial