add existing code (build system, libs, initial mizucp code)
[mizunara.git] / ui / motif / menu.c
1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright 2014 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 <stdarg.h>
32
33 #include "menu.h"
34 #include "button.h"
35 #include "toolkit.h"
36 #include "stock.h"
37 #include "container.h"
38 #include "../common/context.h"
39 #include "../ui/window.h"
40
41 UcxList *menus;
42 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 }
83
84 void ui_menuitem(char *label, ui_callback f, void *userdata) {
85     ui_menuitem_gr(label, f, userdata, -1);
86 }
87
88 void ui_menuitem_st(char *stockid, ui_callback f, void *userdata) {
89     ui_menuitem_stgr(stockid, f, userdata, -1);
90 }
91
92 void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...) {
93     if(!current) {
94         return;
95     }
96     
97     UiMenuItem *item = malloc(sizeof(UiMenuItem));
98     item->item.add_to = (ui_menu_add_f)add_menuitem_widget;
99     
100     item->label = label;
101     item->userdata = userdata;
102     item->callback = f;
103     item->groups = NULL;
104     
105     // add groups
106     va_list ap;
107     va_start(ap, userdata);
108     int group;
109     while((group = va_arg(ap, int)) != -1) {
110         item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group);
111     }
112     va_end(ap);
113     
114     UiMenu *cm = current->data;
115     cm->items = ucx_list_append(cm->items, item);
116 }
117
118 void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...) {
119     if(!current) {
120         return;
121     }
122     
123     UiStMenuItem *item = malloc(sizeof(UiStMenuItem));
124     item->item.add_to = (ui_menu_add_f)add_menuitem_st_widget;
125     
126     item->stockid = stockid;
127     item->userdata = userdata;
128     item->callback = f;
129     item->groups = NULL;
130     
131     // add groups
132     va_list ap;
133     va_start(ap, userdata);
134     int group;
135     while((group = va_arg(ap, int)) != -1) {
136         item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group);
137     }
138     va_end(ap);
139     
140     UiMenu *cm = current->data;
141     cm->items = ucx_list_append(cm->items, item);
142 }
143
144 void ui_menuseparator() {
145     if(!current) {
146         return;
147     }
148     
149     UiMenuItemI *item = malloc(sizeof(UiMenuItemI));
150     item->add_to = (ui_menu_add_f)add_menuseparator_widget;
151     
152     UiMenu *cm = current->data;
153     cm->items = ucx_list_append(cm->items, item);
154 }
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
202 // private menu functions
203 void ui_create_menubar(UiObject *obj) {
204     if(!menus) {
205         return;
206     }
207     
208     Widget menubar = XmCreateMenuBar(obj->widget, "main_list", NULL, 0);
209     XtManageChild(menubar);
210     
211     UcxList *ls = menus;
212     int menu_index = 0;
213     while(ls) {
214         UiMenu *menu = ls->data;
215         menu_index += menu->item.add_to(menubar, menu_index, &menu->item, obj);
216         
217         ls = ls->next;
218     }
219 }
220
221 int add_menu_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) {
222     UiMenu *menu = (UiMenu*)item;
223     
224     Widget menuItem = XtVaCreateManagedWidget(
225             menu->label,
226             xmCascadeButtonWidgetClass,
227             parent,
228             NULL);
229     Widget m = XmVaCreateSimplePulldownMenu(parent, menu->label, i, NULL, NULL);
230     
231     UcxList *ls = menu->items;
232     int menu_index = 0;
233     while(ls) {
234         UiMenuItemI *mi = ls->data;
235         menu_index += mi->add_to(m, menu_index, mi, obj);
236         ls = ls->next;
237     }
238     
239     return 1;
240 }
241
242 int add_menuitem_widget(
243         Widget parent,
244         int i,
245         UiMenuItemI *item,
246         UiObject *obj)
247 {
248     UiMenuItem *mi = (UiMenuItem*)item;
249     
250     Arg args[1];
251     XmString label = XmStringCreateLocalized(mi->label);
252     XtSetArg(args[0], XmNlabelString, label);
253     
254     Widget mitem = XtCreateManagedWidget(
255             "menubutton",
256             xmPushButtonWidgetClass,
257             parent,
258             args,
259             1);
260     XmStringFree(label);
261     
262     if(mi->callback != NULL) {
263         UiEventData *event = ucx_mempool_malloc(
264                 obj->ctx->mempool,
265                 sizeof(UiEventData));
266         event->obj = obj;
267         event->userdata = mi->userdata;
268         event->callback = mi->callback;
269         event->value = 0;
270         XtAddCallback(
271                 mitem,
272                 XmNactivateCallback,
273                 (XtCallbackProc)ui_push_button_callback,
274                 event);
275     }
276     
277     if(mi->groups) {
278         uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups);
279     }
280     
281     return 1;
282 }
283
284 int add_menuitem_st_widget(Widget parent, int i, UiMenuItemI *item, UiObject *obj) {
285     UiStMenuItem *mi = (UiStMenuItem*)item;
286     
287     UiStockItem *si = ui_get_stock_item(mi->stockid);
288     if(!si) {
289         fprintf(stderr, "UI Error: unknown stock id: %s\n", mi->stockid);
290         return 0;
291     }
292     
293     int n = 0;
294     Arg args[4];
295     XmString label = XmStringCreateLocalized(si->label);
296     XmString at = NULL;
297     
298     XtSetArg(args[n], XmNlabelString, label);
299     n++;
300     if(si->accelerator) {
301         XtSetArg(args[n], XmNaccelerator, si->accelerator);
302         n++;
303     }
304     if(si->accelerator_label) {
305         at = XmStringCreateLocalized(si->accelerator_label);
306         XtSetArg(args[n], XmNacceleratorText, at);
307         n++;
308     }
309     
310     Widget mitem = XtCreateManagedWidget(
311             "menubutton",
312             xmPushButtonWidgetClass,
313             parent,
314             args,
315             n);
316     XmStringFree(label);
317     if(at) {
318         XmStringFree(at);
319     }
320     
321     if(mi->callback != NULL) {
322         UiEventData *event = ucx_mempool_malloc(
323                 obj->ctx->mempool,
324                 sizeof(UiEventData));
325         event->obj = obj;
326         event->userdata = mi->userdata;
327         event->callback = mi->callback;
328         event->value = 0;
329         XtAddCallback(
330                 mitem,
331                 XmNactivateCallback,
332                 (XtCallbackProc)ui_push_button_callback,
333                 event);
334     }
335     
336     if(mi->groups) {
337         uic_add_group_widget(obj->ctx, mitem, (ui_enablefunc)XtSetSensitive, mi->groups);
338     }
339     
340     return 1;
341 }
342
343 int add_menuseparator_widget(
344         Widget parent,
345         int i,
346         UiMenuItemI *item,
347         UiObject *obj)
348 {
349     Widget s = XmCreateSeparatorGadget (parent, "menu_separator", NULL, 0);
350     XtManageChild(s);
351     return 1;
352 }
353
354 int add_checkitem_widget(
355         Widget parent,
356         int i,
357         UiMenuItemI *item,
358         UiObject *obj)
359 {
360     UiCheckItem *ci = (UiCheckItem*)item;
361     
362     Arg args[3];
363     XmString label = XmStringCreateLocalized(ci->label);
364     XtSetArg(args[0], XmNlabelString, label);
365     XtSetArg(args[1], XmNvisibleWhenOff, 1);
366     Widget checkbox = XtCreateManagedWidget(
367             "menutogglebutton",
368             xmToggleButtonWidgetClass,
369             parent,
370             args,
371             2);
372     XmStringFree(label);
373     
374     if(ci->callback) {
375         UiEventData *event = ucx_mempool_malloc(
376                 obj->ctx->mempool,
377                 sizeof(UiEventData));
378         event->obj = obj;
379         event->userdata = ci->userdata;
380         event->callback = ci->callback;
381         XtAddCallback(
382             checkbox,
383             XmNvalueChangedCallback,
384             (XtCallbackProc)ui_toggle_button_callback,
385             event);
386     }
387     
388     return 1;
389 }
390
391 int add_checkitemnv_widget(
392         Widget parent,
393         int i,
394         UiMenuItemI *item,
395         UiObject *obj)
396 {
397     UiCheckItemNV *ci = (UiCheckItemNV*)item;
398     
399     Arg args[3];
400     XmString label = XmStringCreateLocalized(ci->label);
401     XtSetArg(args[0], XmNlabelString, label);
402     XtSetArg(args[1], XmNvisibleWhenOff, 1);
403     Widget checkbox = XtCreateManagedWidget(
404             "menutogglebutton",
405             xmToggleButtonWidgetClass,
406             parent,
407             args,
408             2);
409     XmStringFree(label);
410     
411     UiVar *var = uic_create_var(obj->ctx, ci->varname, UI_VAR_INTEGER);
412     if(var) {
413         UiInteger *value = var->value;
414         value->obj = checkbox;
415         value->get = ui_toggle_button_get;
416         value->set = ui_toggle_button_set;
417         value = 0;
418     } else {
419         // TODO: error
420     }
421     
422     return 1;
423 }
424
425 int add_menuitem_list_widget(
426         Widget parent,
427         int i,
428         UiMenuItemI *item,
429         UiObject *obj)
430 {
431     UiMenuItemList *il = (UiMenuItemList*)item;
432     UcxMempool *mp = obj->ctx->mempool;
433     
434     UiActiveMenuItemList *ls = ucx_mempool_malloc(
435             mp,
436             sizeof(UiActiveMenuItemList));
437     
438     ls->object = obj;
439     ls->menu = parent;
440     ls->index = i;
441     ls->oldcount = 0;
442     ls->list = il->list;
443     ls->callback = il->callback;
444     ls->userdata = il->userdata;
445     
446     ls->list->observers = ui_add_observer(
447             ls->list->observers,
448             (ui_callback)ui_update_menuitem_list,
449             ls);
450     
451     ui_update_menuitem_list(NULL, ls);
452     
453     return 0;
454 }
455
456 void ui_update_menuitem_list(UiEvent *event, UiActiveMenuItemList *list) {
457     Arg args[4];
458     
459     // remove old items
460     if(list->oldcount > 0) {
461         Widget *children;
462         int nc;
463         
464         XtVaGetValues(
465                 list->menu,
466                 XmNchildren,
467                 &children,
468                 XmNnumChildren,
469                 &nc,
470                 NULL);
471         
472         for(int i=0;i<list->oldcount;i++) {
473             XtDestroyWidget(children[list->index + i]);
474         }
475     }
476     
477     char *str = ui_list_first(list->list);
478     if(str) {
479         // add separator
480         XtSetArg(args[0], XmNpositionIndex, list->index);
481         Widget s = XmCreateSeparatorGadget (list->menu, "menu_separator", args, 1);
482         XtManageChild(s);
483     }
484     int i = 1;
485     while(str) {
486         XmString label = XmStringCreateLocalized(str);
487         XtSetArg(args[0], XmNlabelString, label);
488         XtSetArg(args[1], XmNpositionIndex, list->index + i);
489
490         Widget mitem = XtCreateManagedWidget(
491                 "menubutton",
492                 xmPushButtonWidgetClass,
493                 list->menu,
494                 args,
495                 2);
496         XmStringFree(label);
497         
498         if(list->callback) {
499             // TODO: use mempool
500             UiEventData *event = malloc(sizeof(UiEventData));
501             event->obj = list->object;
502             event->userdata = list->userdata;
503             event->callback = list->callback;
504             event->value = i - 1;
505
506             XtAddCallback(
507                 mitem,
508                 XmNactivateCallback,
509                 (XtCallbackProc)ui_push_button_callback,
510                 event);
511         }
512         
513         str = ui_list_next(list->list);
514         i++;
515     }
516     
517     list->oldcount = i;
518 }
519
520 void ui_menu_event_wrapper(Widget widget, XtPointer udata, XtPointer cdata) {
521     UiEventData *event = udata;
522     UiEvent e;
523     e.obj = event->obj;
524     e.window = event->obj->window;
525     e.document = event->obj->ctx->document;
526     e.intval = 0;
527     event->callback(&e, event->userdata);    
528 }
529
530
531 /*
532  * widget menu functions
533  */
534
535 static void ui_popup_handler(Widget widget, XtPointer data,  XEvent *event, Boolean *c) {
536     Widget menu = data;
537     XmMenuPosition(menu, (XButtonPressedEvent *)event);
538     XtManageChild(menu);
539     
540     *c = FALSE;
541 }
542
543 UIMENU ui_contextmenu(UiObject *obj) {
544     UiContainer *ct = uic_get_current_container(obj);
545     if(ct->current) {
546         return ui_contextmenu_w(obj, ct->current);
547     } else {
548         return NULL; // TODO: warn
549     }
550 }
551
552 UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget) {
553     UiContainer *ct = uic_get_current_container(obj);
554     
555     Widget menu = XmCreatePopupMenu(widget, "popup_menu", NULL, 0);
556     ct->menu = menu;
557     
558     XtAddEventHandler(widget, ButtonPressMask, FALSE, ui_popup_handler, menu);
559     
560     return menu;
561 }
562
563 void ui_contextmenu_popup(UIMENU menu) {
564     
565 }
566
567 void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata) {
568     ui_widget_menuitem_gr(obj, label, f, userdata, -1);
569 }
570
571 void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...) {
572     UiContainer *ct = uic_get_current_container(obj);
573     if(!ct->menu) {
574         return;
575     }
576     
577     // add groups
578     UcxList *groups = NULL;
579     va_list ap;
580     va_start(ap, userdata);
581     int group;
582     while((group = va_arg(ap, int)) != -1) {
583         ucx_list_append(groups, (void*)(intptr_t)group);
584     }
585     va_end(ap);
586     
587     // create menuitem
588     Arg args[4];
589     XmString labelstr = XmStringCreateLocalized(label);
590     XtSetArg(args[0], XmNlabelString, labelstr);
591     
592     Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1);
593     XtManageChild(item);
594     XmStringFree(labelstr);
595 }
596
597 void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata) {
598     ui_widget_menuitem_stgr(obj, stockid, f, userdata, -1);
599 }
600
601 void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...) {
602     UiContainer *ct = uic_get_current_container(obj);
603     if(!ct->menu) {
604         return;
605     }
606     
607     // add groups
608     UcxList *groups = NULL;
609     va_list ap;
610     va_start(ap, userdata);
611     int group;
612     while((group = va_arg(ap, int)) != -1) {
613         ucx_list_append(groups, (void*)(intptr_t)group);
614     }
615     va_end(ap);
616     
617     // create menuitem
618     UiStockItem *stockItem = ui_get_stock_item(stockid);
619     Arg args[4];
620     XmString labelstr = XmStringCreateLocalized(stockItem->label);
621     XtSetArg(args[0], XmNlabelString, labelstr);
622     
623     Widget item = XmCreatePushButton(ct->menu, "menu_button", args, 1);
624     XtManageChild(item);
625     XmStringFree(labelstr);
626 }