ui/gtk/menu.c

changeset 0
804d8803eade
child 3
c04433993fbb
equal deleted inserted replaced
-1:000000000000 0:804d8803eade
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2017 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
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 "../ui/properties.h"
38 #include "../ui/window.h"
39 #include "container.h"
40
41 static UcxList *menus;
42 static UcxList *current;
43
44 void ui_menu(char *label) {
45 // free current menu hierarchy
46 ucx_list_free(current);
47
48 // create menu
49 UiMenu *menu = malloc(sizeof(UiMenu));
50 menu->item.add_to = (ui_menu_add_f)add_menu_widget;
51
52 menu->label = label;
53 menu->items = NULL;
54 menu->parent = NULL;
55
56 current = ucx_list_prepend(NULL, menu);
57 menus = ucx_list_append(menus, menu);
58
59 }
60
61 void ui_submenu(char *label) {
62 UiMenu *menu = malloc(sizeof(UiMenu));
63 menu->item.add_to = (ui_menu_add_f)add_menu_widget;
64
65 menu->label = label;
66 menu->items = NULL;
67 menu->parent = NULL;
68
69 // add submenu to current menu
70 UiMenu *cm = current->data;
71 cm->items = ucx_list_append(cm->items, menu);
72
73 // set the submenu to current menu
74 current = ucx_list_prepend(current, menu);
75 }
76
77 void ui_submenu_end() {
78 if(ucx_list_size(current) < 2) {
79 return;
80 }
81 current = ucx_list_remove(current, current);
82 //UcxList *c = current;
83 }
84
85 void ui_menuitem(char *label, ui_callback f, void *userdata) {
86 ui_menuitem_gr(label, f, userdata, -1);
87 }
88
89 void ui_menuitem_st(char *stockid, ui_callback f, void *userdata) {
90 ui_menuitem_stgr(stockid, f, userdata, -1);
91 }
92
93 void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...) {
94 if(!current) {
95 return;
96 }
97
98 UiMenuItem *item = malloc(sizeof(UiMenuItem));
99 item->item.add_to = (ui_menu_add_f)add_menuitem_widget;
100
101 item->label = label;
102 item->userdata = userdata;
103 item->callback = f;
104 item->groups = NULL;
105
106 // add groups
107 va_list ap;
108 va_start(ap, userdata);
109 int group;
110 while((group = va_arg(ap, int)) != -1) {
111 item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group);
112 }
113 va_end(ap);
114
115 UiMenu *cm = current->data;
116 cm->items = ucx_list_append(cm->items, item);
117 }
118
119 void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...) {
120 if(!current) {
121 return;
122 }
123
124 UiStMenuItem *item = malloc(sizeof(UiStMenuItem));
125 item->item.add_to = (ui_menu_add_f)add_menuitem_st_widget;
126
127 item->stockid = stockid;
128 item->userdata = userdata;
129 item->callback = f;
130 item->groups = NULL;
131
132 // add groups
133 va_list ap;
134 va_start(ap, userdata);
135 int group;
136 while((group = va_arg(ap, int)) != -1) {
137 item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group);
138 }
139 va_end(ap);
140
141 UiMenu *cm = current->data;
142 cm->items = ucx_list_append(cm->items, item);
143 }
144
145 void ui_menuseparator() {
146 if(!current) {
147 return;
148 }
149
150 UiMenuItemI *item = malloc(sizeof(UiMenuItemI));
151 item->add_to = (ui_menu_add_f)add_menuseparator_widget;
152
153 UiMenu *cm = current->data;
154 cm->items = ucx_list_append(cm->items, item);
155 }
156
157 void ui_checkitem(char *label, ui_callback f, void *userdata) {
158 if(!current) {
159 return;
160 }
161
162 UiCheckItem *item = malloc(sizeof(UiCheckItem));
163 item->item.add_to = (ui_menu_add_f)add_checkitem_widget;
164 item->label = label;
165 item->callback = f;
166 item->userdata = userdata;
167
168 UiMenu *cm = current->data;
169 cm->items = ucx_list_append(cm->items, item);
170 }
171
172 void ui_checkitem_nv(char *label, char *vname) {
173 if(!current) {
174 return;
175 }
176
177 UiCheckItemNV *item = malloc(sizeof(UiCheckItemNV));
178 item->item.add_to = (ui_menu_add_f)add_checkitemnv_widget;
179 item->varname = vname;
180 item->label = label;
181
182 UiMenu *cm = current->data;
183 cm->items = ucx_list_append(cm->items, item);
184 }
185
186 void ui_menuitem_list(UiList *items, ui_callback f, void *userdata) {
187 if(!current) {
188 return;
189 }
190
191 UiMenuItemList *item = malloc(sizeof(UiMenuItemList));
192 item->item.add_to = (ui_menu_add_f)add_menuitem_list_widget;
193 item->callback = f;
194 item->userdata = userdata;
195 item->list = items;
196
197 UiMenu *cm = current->data;
198 cm->items = ucx_list_append(cm->items, item);
199 }
200
201 // private menu functions
202 GtkWidget *ui_create_menubar(UiObject *obj) {
203 if(menus == NULL) {
204 return NULL;
205 }
206
207 GtkWidget *mb = gtk_menu_bar_new();
208
209 UcxList *ls = menus;
210 while(ls) {
211 UiMenu *menu = ls->data;
212 menu->item.add_to(mb, 0, &menu->item, obj);
213
214 ls = ls->next;
215 }
216
217 return mb;
218 }
219
220 void add_menu_widget(GtkWidget *parent, int i, UiMenuItemI *item, UiObject *obj) {
221 UiMenu *menu = (UiMenu*)item;
222
223 GtkWidget *menu_widget = gtk_menu_new();
224 GtkWidget *menu_item = gtk_menu_item_new_with_mnemonic(menu->label);
225 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu_widget);
226
227 UcxList *ls = menu->items;
228 int index = 0;
229 while(ls) {
230 UiMenuItemI *i = ls->data;
231 i->add_to(menu_widget, index, i, obj);
232
233 ls = ls->next;
234 index++;
235 }
236
237 gtk_menu_shell_append(GTK_MENU_SHELL(parent), menu_item);
238 }
239
240 void add_menuitem_widget(GtkWidget *parent, int index, UiMenuItemI *item, UiObject *obj) {
241 UiMenuItem *i = (UiMenuItem*)item;
242
243 //GtkWidget *widget = gtk_menu_item_new_with_label(i->title);
244 GtkWidget *widget = gtk_menu_item_new_with_mnemonic(i->label);
245
246 if(i->callback != NULL) {
247 UiEventData *event = malloc(sizeof(UiEventData));
248 event->obj = obj;
249 event->userdata = i->userdata;
250 event->callback = i->callback;
251 event->value = 0;
252
253 g_signal_connect(
254 widget,
255 "activate",
256 G_CALLBACK(ui_menu_event_wrapper),
257 event);
258 g_signal_connect(
259 widget,
260 "destroy",
261 G_CALLBACK(ui_destroy_userdata),
262 event);
263 }
264
265 gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
266
267 if(i->groups) {
268 uic_add_group_widget(obj->ctx, widget, i->groups);
269 }
270 }
271
272 void add_menuitem_st_widget(
273 GtkWidget *parent,
274 int index,
275 UiMenuItemI *item,
276 UiObject *obj)
277 {
278 UiStMenuItem *i = (UiStMenuItem*)item;
279
280 GtkWidget *widget = gtk_image_menu_item_new_from_stock(i->stockid, obj->ctx->accel_group);
281
282 if(i->callback != NULL) {
283 UiEventData *event = malloc(sizeof(UiEventData));
284 event->obj = obj;
285 event->userdata = i->userdata;
286 event->callback = i->callback;
287 event->value = 0;
288
289 g_signal_connect(
290 widget,
291 "activate",
292 G_CALLBACK(ui_menu_event_wrapper),
293 event);
294 g_signal_connect(
295 widget,
296 "destroy",
297 G_CALLBACK(ui_destroy_userdata),
298 event);
299 }
300
301 gtk_menu_shell_append(GTK_MENU_SHELL(parent), widget);
302
303 if(i->groups) {
304 uic_add_group_widget(obj->ctx, widget, i->groups);
305 }
306 }
307
308 void add_menuseparator_widget(
309 GtkWidget *parent,
310 int index,
311 UiMenuItemI *item,
312 UiObject *obj)
313 {
314 gtk_menu_shell_append(
315 GTK_MENU_SHELL(parent),
316 gtk_separator_menu_item_new());
317 }
318
319 void add_checkitem_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
320 UiCheckItem *ci = (UiCheckItem*)item;
321 GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label);
322 gtk_menu_shell_append(GTK_MENU_SHELL(p), widget);
323
324 if(ci->callback) {
325 UiEventData *event = malloc(sizeof(UiEventData));
326 event->obj = obj;
327 event->userdata = ci->userdata;
328 event->callback = ci->callback;
329 event->value = 0;
330
331 g_signal_connect(
332 widget,
333 "toggled",
334 G_CALLBACK(ui_menu_event_toggled),
335 event);
336 g_signal_connect(
337 widget,
338 "destroy",
339 G_CALLBACK(ui_destroy_userdata),
340 event);
341 }
342 }
343
344 void add_checkitemnv_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
345 UiCheckItemNV *ci = (UiCheckItemNV*)item;
346 GtkWidget *widget = gtk_check_menu_item_new_with_mnemonic(ci->label);
347 gtk_menu_shell_append(GTK_MENU_SHELL(p), widget);
348
349 UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER);
350 if(var) {
351 UiInteger *value = var->value;
352 value->obj = widget;
353 value->get = ui_checkitem_get;
354 value->set = ui_checkitem_set;
355 value = 0;
356 } else {
357 // TODO: error
358 }
359 }
360
361 void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
362 UiMenuItemList *il = (UiMenuItemList*)item;
363 UcxMempool *mp = obj->ctx->mempool;
364
365 UiActiveMenuItemList *ls = ucx_mempool_malloc(
366 mp,
367 sizeof(UiActiveMenuItemList));
368
369 ls->object = obj;
370 ls->menu = GTK_MENU_SHELL(p);
371 ls->index = index;
372 ls->oldcount = 0;
373 ls->list = il->list;
374 ls->callback = il->callback;
375 ls->userdata = il->userdata;
376
377 ls->list->observers = ui_add_observer(
378 ls->list->observers,
379 (ui_callback)ui_update_menuitem_list,
380 ls);
381
382 ui_update_menuitem_list(NULL, ls);
383 }
384
385
386 void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) {
387 // remove old items
388 if(list->oldcount > 0) {
389 int i = 0;
390 GList *mi = gtk_container_get_children(GTK_CONTAINER(list->menu));
391 while(mi) {
392 if(i >= list->index && i < list->index + list->oldcount) {
393 //gtk_container_remove(GTK_CONTAINER(list->menu), mi->data);
394 gtk_widget_destroy(mi->data);
395 }
396 mi = mi->next;
397 i++;
398 }
399 }
400
401 char *str = ui_list_first(list->list);
402 if(str) {
403 GtkWidget *widget = gtk_separator_menu_item_new();
404 gtk_menu_shell_insert(list->menu, widget, list->index);
405 gtk_widget_show(widget);
406 }
407 int i = 1;
408 while(str) {
409 GtkWidget *widget = gtk_menu_item_new_with_label(str);
410 gtk_menu_shell_insert(list->menu, widget, list->index + i);
411 gtk_widget_show(widget);
412
413 if(list->callback) {
414 // TODO: use mempool
415 UiEventData *event = malloc(sizeof(UiEventData));
416 event->obj = list->object;
417 event->userdata = list->userdata;
418 event->callback = list->callback;
419 event->value = i - 1;
420
421 g_signal_connect(
422 widget,
423 "activate",
424 G_CALLBACK(ui_menu_event_wrapper),
425 event);
426 g_signal_connect(
427 widget,
428 "destroy",
429 G_CALLBACK(ui_destroy_userdata),
430 event);
431 }
432
433 str = ui_list_next(list->list);
434 i++;
435 }
436
437 list->oldcount = i;
438 }
439
440 void ui_menu_event_wrapper(GtkMenuItem *item, UiEventData *event) {
441 UiEvent evt;
442 evt.obj = event->obj;
443 evt.window = event->obj->window;
444 evt.document = event->obj->ctx->document;
445 evt.eventdata = NULL;
446 evt.intval = event->value;
447 event->callback(&evt, event->userdata);
448 }
449
450 void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event) {
451 UiEvent evt;
452 evt.obj = event->obj;
453 evt.window = event->obj->window;
454 evt.document = event->obj->ctx->document;
455 evt.eventdata = NULL;
456 evt.intval = gtk_check_menu_item_get_active(ci);
457 event->callback(&evt, event->userdata);
458 }
459
460 int64_t ui_checkitem_get(UiInteger *i) {
461 int state = gtk_check_menu_item_get_active(i->obj);
462 i->value = state;
463 return state;
464 }
465
466 void ui_checkitem_set(UiInteger *i, int64_t value) {
467 i->value = value;
468 gtk_check_menu_item_set_active(i->obj, value);
469 }
470
471
472 /*
473 * widget menu functions
474 */
475
476 static gboolean ui_button_press_event(GtkWidget *widget, GdkEvent *event, GtkMenu *menu) {
477 if(event->type == GDK_BUTTON_PRESS) {
478 GdkEventButton *e = (GdkEventButton*)event;
479 if(e->button == 3) {
480 gtk_widget_show_all(GTK_WIDGET(menu));
481 ui_contextmenu_popup(menu);
482 return TRUE;
483 }
484 }
485 return FALSE;
486 }
487
488 UIMENU ui_contextmenu(UiObject *obj) {
489 UiContainer *ct = uic_get_current_container(obj);
490 return ui_contextmenu_w(obj, ct->current);
491 }
492
493 UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget) {
494 UiContainer *ct = uic_get_current_container(obj);
495
496 GtkMenu *menu = GTK_MENU(gtk_menu_new());
497 g_signal_connect(widget, "button-press-event", (GCallback) ui_button_press_event, menu);
498
499 ct->menu = menu;
500 return menu;
501 }
502
503 void ui_contextmenu_popup(UIMENU menu) {
504 #if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16
505 gtk_menu_popup_at_pointer(menu, NULL);
506 #else
507 gtk_menu_popup(menu, NULL, NULL, 0, 0, 0, gtk_get_current_event_time());
508 #endif
509 }
510
511 void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata) {
512 ui_widget_menuitem_gr(obj, label, f, userdata, -1);
513 }
514
515 void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...) {
516 UiContainer *ct = uic_get_current_container(obj);
517 if(!ct->menu) {
518 return;
519 }
520
521 // add groups
522 UcxList *groups = NULL;
523 va_list ap;
524 va_start(ap, userdata);
525 int group;
526 while((group = va_arg(ap, int)) != -1) {
527 ucx_list_append(groups, (void*)(intptr_t)group);
528 }
529 va_end(ap);
530
531 // create menuitem
532 GtkWidget *widget = gtk_menu_item_new_with_mnemonic(label);
533 gtk_widget_show(widget);
534
535 if(f) {
536 UiEventData *event = malloc(sizeof(UiEventData));
537 event->obj = obj;
538 event->userdata = userdata;
539 event->callback = f;
540 event->value = 0;
541
542 g_signal_connect(
543 widget,
544 "activate",
545 G_CALLBACK(ui_menu_event_wrapper),
546 event);
547 g_signal_connect(
548 widget,
549 "destroy",
550 G_CALLBACK(ui_destroy_userdata),
551 event);
552 }
553
554 gtk_menu_shell_append(GTK_MENU_SHELL(ct->menu), widget);
555
556 if(groups) {
557 uic_add_group_widget(obj->ctx, widget, groups);
558 }
559 }
560
561 void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata) {
562 ui_widget_menuitem_stgr(obj, stockid, f, userdata, -1);
563 }
564
565 void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...) {
566 UiContainer *ct = uic_get_current_container(obj);
567 if(!ct->menu) {
568 return;
569 }
570
571 // add groups
572 UcxList *groups = NULL;
573 va_list ap;
574 va_start(ap, userdata);
575 int group;
576 while((group = va_arg(ap, int)) != -1) {
577 ucx_list_append(groups, (void*)(intptr_t)group);
578 }
579 va_end(ap);
580
581 // create menuitem
582 GtkWidget *widget = gtk_image_menu_item_new_from_stock(stockid, obj->ctx->accel_group);
583 gtk_widget_show(widget);
584
585 if(f) {
586 UiEventData *event = malloc(sizeof(UiEventData));
587 event->obj = obj;
588 event->userdata = userdata;
589 event->callback = f;
590 event->value = 0;
591
592 g_signal_connect(
593 widget,
594 "activate",
595 G_CALLBACK(ui_menu_event_wrapper),
596 event);
597 g_signal_connect(
598 widget,
599 "destroy",
600 G_CALLBACK(ui_destroy_userdata),
601 event);
602 }
603
604 gtk_menu_shell_append(GTK_MENU_SHELL(ct->menu), widget);
605
606 if(groups) {
607 uic_add_group_widget(obj->ctx, widget, groups);
608 }
609 }

mercurial