1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <stdio.h>
26 
27 #include "_xitk.h"
28 
29 #include "utils.h"
30 
31 #undef DEBUG_MENU
32 #undef DUMP_MENU
33 
34 static void _menu_create_menu_from_branch(menu_node_t *, xitk_widget_t *, int, int);
35 
_menu_new_menu_window(ImlibData * im,xitk_window_t * xwin)36 static menu_window_t *_menu_new_menu_window(ImlibData *im, xitk_window_t *xwin) {
37   menu_window_t *menu_window;
38   XSetWindowAttributes menu_attr;
39 
40   menu_window          = (menu_window_t *) xitk_xmalloc(sizeof(menu_window_t));
41   menu_window->display = im->x.disp;
42   menu_window->im      = im;
43   menu_window->xwin    = xwin;
44 
45   menu_window->bevel_plain     = NULL;
46   menu_window->bevel_arrow     = NULL;
47   menu_window->bevel_unchecked = NULL;
48   menu_window->bevel_checked   = NULL;
49 
50   xitk_dlist_init (&menu_window->wl.list);
51 
52   /* HACK: embedded widget list - make sure it is not accidentally (un)listed. */
53   xitk_dnode_init (&menu_window->wl.node);
54   menu_window->wl.win  = xitk_window_get_window(xwin);
55 
56   menu_attr.override_redirect = True;
57 
58   XLOCK (im->x.x_lock_display, im->x.disp);
59   XChangeWindowAttributes(im->x.disp, menu_window->wl.win, CWOverrideRedirect, &menu_attr);
60   menu_window->wl.gc   = XCreateGC(im->x.disp, (xitk_window_get_window(xwin)), None, None);
61   XUNLOCK (im->x.x_unlock_display, im->x.disp);
62 
63   return menu_window;
64 }
65 
_menu_new_node(xitk_widget_t * w)66 static menu_node_t *_menu_new_node(xitk_widget_t *w) {
67   menu_node_t  *node;
68 
69   node             = (menu_node_t *) xitk_xmalloc(sizeof(menu_node_t));
70   node->prev       = NULL;
71   node->menu_entry = NULL;
72   node->widget     = w;
73   node->branch     = NULL;
74   node->next       = NULL;
75 
76   return node;
77 }
_menu_build_menu_entry(xitk_menu_entry_t * me,char * name)78 static xitk_menu_entry_t *_menu_build_menu_entry(xitk_menu_entry_t *me, char *name) {
79   xitk_menu_entry_t  *mentry;
80   char                buffer[name ? (strlen(name) + 3) : 1];
81 
82   if(name) {
83     char *s = name;
84     char *d = buffer;
85 
86     while(*s) {
87 
88       switch(*s) {
89       case '\\':
90 	*d = *(++s);
91 	break;
92 
93       default:
94 	*d = *s;
95 	break;
96       }
97 
98       d++;
99       s++;
100     }
101     *d = '\0';
102   }
103 
104   mentry            = (xitk_menu_entry_t *) xitk_xmalloc(sizeof(xitk_menu_entry_t));
105   mentry->menu      = strdup((name) ? buffer : me->menu);
106   mentry->shortcut  = (me->shortcut) ? strdup(me->shortcut) : NULL;
107   mentry->type      = (me->type) ? strdup(me->type) : NULL;
108   mentry->cb        = me->cb;
109   mentry->user_data = me->user_data;
110   mentry->user_id   = me->user_id;
111 
112   return mentry;
113 }
_menu_add_to_node_branch(xitk_widget_t * w,menu_node_t ** mnode,xitk_menu_entry_t * me,char * name)114 static menu_node_t *_menu_add_to_node_branch(xitk_widget_t *w, menu_node_t **mnode,
115 					     xitk_menu_entry_t *me, char *name) {
116   menu_node_t        *node = _menu_new_node(w);
117 
118   node->menu_entry  = _menu_build_menu_entry(me, name);
119   node->prev        = (*mnode);
120   (*mnode)->branch  = node;
121 
122   return node;
123 }
_menu_append_to_node(xitk_widget_t * w,menu_node_t ** mnode,xitk_menu_entry_t * me,char * name)124 static menu_node_t *_menu_append_to_node(xitk_widget_t *w,
125 					 menu_node_t **mnode, xitk_menu_entry_t *me, char *name) {
126   menu_node_t *node = _menu_new_node(w);
127 
128   node->menu_entry = _menu_build_menu_entry(me, name);
129   node->prev       = (*mnode);
130 
131   if((*mnode))
132     (*mnode)->next = node;
133 
134   return node;
135 }
136 
_menu_is_separator(xitk_menu_entry_t * me)137 static int _menu_is_separator(xitk_menu_entry_t *me) {
138   if(me && me->type && ((strncasecmp(me->type, "<separator>", 11) == 0)))
139     return 1;
140   return 0;
141 }
_menu_is_branch(xitk_menu_entry_t * me)142 static int _menu_is_branch(xitk_menu_entry_t *me) {
143   if(me && me->type && ((strncasecmp(me->type, "<branch>", 8) == 0)))
144     return 1;
145   return 0;
146 }
_menu_is_check(xitk_menu_entry_t * me)147 static int _menu_is_check(xitk_menu_entry_t *me) {
148   if(me && me->type && ((strncasecmp(me->type, "<check", 6) == 0)))
149     return 1;
150   return 0;
151 }
_menu_is_checked(xitk_menu_entry_t * me)152 static int _menu_is_checked(xitk_menu_entry_t *me) {
153   if(me && me->type && ((strncasecmp(me->type, "<checked>", 9) == 0)))
154     return 1;
155   return 0;
156 }
_menu_is_title(xitk_menu_entry_t * me)157 static int _menu_is_title(xitk_menu_entry_t *me) {
158   if(me && me->type && ((strncasecmp(me->type, "<title>", 7) == 0)))
159     return 1;
160   return 0;
161 }
_menu_branch_have_check(menu_node_t * branch)162 static int _menu_branch_have_check(menu_node_t *branch) {
163   while(branch) {
164     if(_menu_is_check(branch->menu_entry) || _menu_is_checked(branch->menu_entry))
165       return 1;
166 
167     branch = branch->next;
168   }
169   return 0;
170 }
_menu_branch_have_branch(menu_node_t * branch)171 static int _menu_branch_have_branch(menu_node_t *branch) {
172   while(branch) {
173     if(_menu_is_branch(branch->menu_entry))
174       return 1;
175 
176     branch = branch->next;
177   }
178   return 0;
179 }
_menu_branch_is_in_curbranch(menu_node_t * branch)180 static int _menu_branch_is_in_curbranch(menu_node_t *branch) {
181   menu_private_data_t  *private_data = (menu_private_data_t *) branch->widget->private_data;
182   menu_node_t          *me = private_data->curbranch;
183 
184   if(!me)
185     return 0;
186 
187   while(branch != me) {
188     if(!me->prev)
189       return 0;
190 
191     while(me->prev) {
192       me = me->prev;
193       if(me->prev && me->prev->branch == me)
194 	break;
195     }
196   }
197   return 1;
198 }
199 
200 #ifdef DUMP_MENU
201 #ifdef	__GNUC__
202 #define prints(fmt, args...) do { int j; for(j = 0; j < i; j++) printf(" "); printf(fmt, ##args); } while(0)
203 #else
204 #define prints(...) do { int j; for(j = 0; j < i; j++) printf(" "); printf(__VA_ARGS__); } while(0)
205 #endif
206 static int i = 0;
_menu_dump_branch(menu_node_t * branch)207 static void _menu_dump_branch(menu_node_t *branch) {
208 
209   while(branch) {
210     prints("[%s]\n", branch->menu_entry->menu);
211 
212     if(branch->branch) {
213       i += 3;
214       _menu_dump_branch(branch->branch);
215     }
216 
217     branch = branch->next;
218   }
219   i -= 3;
220 }
_menu_dump(menu_private_data_t * private_data)221 static void _menu_dump(menu_private_data_t *private_data) {
222   menu_tree_t *mtree = private_data->mtree;
223   menu_node_t *me;
224 
225   printf("\n");
226   me = mtree->first;
227   while(me) {
228     xitk_menu_entry_t *entry = me->menu_entry;
229     menu_node_t *branch = me->branch;
230 
231     if(_menu_is_separator(entry))
232       prints("===================\n");
233     else
234       prints("[%s]\n", entry->menu);
235 
236     if(branch) {
237       i += 3;
238       _menu_dump_branch(branch);
239       branch = branch->next;
240     }
241 
242     me = me->next;
243   }
244 }
245 #endif
246 
notify_event(xitk_widget_t * w,widget_event_t * event,widget_event_result_t * result)247 static int notify_event(xitk_widget_t *w, widget_event_t *event, widget_event_result_t *result) {
248   int retval = 0;
249 
250   switch(event->type) {
251   case WIDGET_EVENT_PAINT:
252     break;
253   case WIDGET_EVENT_CLICK:
254     retval = 1;
255     break;
256   case WIDGET_EVENT_FOCUS:
257     break;
258   case WIDGET_EVENT_INSIDE:
259     retval = 1;
260     break;
261   case WIDGET_EVENT_CHANGE_SKIN:
262     break;
263   case WIDGET_EVENT_DESTROY:
264     break;
265   case WIDGET_EVENT_GET_SKIN:
266     break;
267   }
268 
269   return retval;
270 }
271 
_menu_count_entry_from_branch(menu_node_t * branch)272 static int _menu_count_entry_from_branch(menu_node_t *branch) {
273   menu_node_t  *me = branch;
274   int           ret = 0;
275 
276   while(me) {
277     if(me->menu_entry)
278       ret++;
279     me = me->next;
280   }
281 
282   return ret;
283 }
_menu_count_separator_from_branch(menu_node_t * branch)284 static int _menu_count_separator_from_branch(menu_node_t *branch) {
285   menu_node_t  *me = branch;
286   int           ret = 0;
287 
288   while(me) {
289     if(_menu_is_separator(me->menu_entry))
290       ret++;
291     me = me->next;
292   }
293 
294   return ret;
295 }
_menu_is_title_in_branch(menu_node_t * branch)296 static int _menu_is_title_in_branch(menu_node_t *branch) {
297   menu_node_t  *me = branch;
298   int           ret = 0;
299 
300   while(me) {
301     if(_menu_is_title(me->menu_entry)) {
302       ret = 1;
303       break;
304     }
305     me = me->next;
306   }
307 
308   return ret;
309 }
_menu_get_wider_menuitem_node(menu_node_t * branch)310 static menu_node_t *_menu_get_wider_menuitem_node(menu_node_t *branch) {
311   menu_node_t  *me = branch;
312   menu_node_t  *max = NULL;
313   size_t        len = 0;
314 
315   while(me) {
316     if((!_menu_is_separator(me->menu_entry)) &&
317        (me->menu_entry->menu &&
318 	((strlen(me->menu_entry->menu) > len)))) {
319       len = strlen(me->menu_entry->menu);
320       max = me;
321     }
322 
323     me = me->next;
324   }
325 
326   return max;
327 }
_menu_branch_have_shortcut(menu_node_t * branch)328 static int _menu_branch_have_shortcut(menu_node_t *branch) {
329   menu_node_t  *me = branch;
330 
331   while(me) {
332     if((!_menu_is_separator(me->menu_entry)) && me->menu_entry->shortcut)
333       return 1;
334     me = me->next;
335   }
336 
337   return 0;
338 }
_menu_get_wider_shortcut_node(menu_node_t * branch)339 static menu_node_t *_menu_get_wider_shortcut_node(menu_node_t *branch) {
340   menu_node_t  *me = branch;
341   menu_node_t  *max = NULL;
342   size_t        len = 0;
343 
344   while(me) {
345     if((!_menu_is_separator(me->menu_entry)) &&
346        (me->menu_entry->shortcut &&
347 	((strlen(me->menu_entry->shortcut) > len)))) {
348       len = strlen(me->menu_entry->shortcut);
349       max = me;
350     }
351 
352     me = me->next;
353   }
354 
355   return max;
356 }
357 
_menu_get_branch_from_node(menu_node_t * node)358 static menu_node_t *_menu_get_branch_from_node(menu_node_t *node) {
359   menu_node_t          *me = node;
360   menu_private_data_t  *private_data = (menu_private_data_t *) me->widget->private_data;
361 
362   while(me->prev) {
363     if(me->prev->branch == me)
364       return me;
365     me = me->prev;
366   }
367 
368   return private_data->mtree->first;
369 }
370 
_menu_get_prev_branch_from_node(menu_node_t * node)371 static menu_node_t *_menu_get_prev_branch_from_node(menu_node_t *node) {
372   menu_node_t          *me;
373 
374   me = _menu_get_branch_from_node(node);
375   if(me->prev)
376     return _menu_get_branch_from_node(me->prev);
377   else
378     return NULL;
379 }
380 
_menu_destroy_menu_window(menu_window_t ** mw)381 static void _menu_destroy_menu_window(menu_window_t **mw) {
382   menu_private_data_t *private_data = (menu_private_data_t *)(*mw)->widget->private_data;
383 
384   xitk_unregister_event_handler(&(*mw)->key);
385   xitk_destroy_widgets(&(*mw)->wl);
386   if ((*mw)->bevel_plain)
387     xitk_image_free_image (private_data->imlibdata, &(*mw)->bevel_plain);
388   if ((*mw)->bevel_arrow)
389     xitk_image_free_image (private_data->imlibdata, &(*mw)->bevel_arrow);
390   if ((*mw)->bevel_unchecked)
391     xitk_image_free_image (private_data->imlibdata, &(*mw)->bevel_unchecked);
392   if ((*mw)->bevel_checked)
393     xitk_image_free_image (private_data->imlibdata, &(*mw)->bevel_checked);
394   xitk_window_destroy_window((*mw)->im, (*mw)->xwin);
395   (*mw)->xwin = NULL;
396   /* xitk_dlist_init (&(*mw)->wl.list); */
397 
398   XLOCK ((*mw)->im->x.x_lock_display, (*mw)->display);
399   XFreeGC((*mw)->display, (*mw)->wl.gc);
400   XUNLOCK ((*mw)->im->x.x_unlock_display, (*mw)->display);
401 
402   /* deferred free as widget list */
403   xitk_dnode_remove (&(*mw)->wl.node);
404   XITK_WIDGET_LIST_FREE((xitk_widget_list_t *)(*mw));
405   (*mw) = NULL;
406 }
407 
_menu_destroy_subs(menu_private_data_t * private_data,menu_window_t * menu_window)408 static void _menu_destroy_subs(menu_private_data_t *private_data, menu_window_t *menu_window) {
409   menu_window_t *mw;
410 
411   mw = (menu_window_t *)private_data->menu_windows.tail.prev;
412   if(!mw->wl.node.prev || (mw == menu_window))
413     /* Nothing to be done, save useless following efforts */
414     return;
415   do {
416     xitk_dnode_remove (&mw->wl.node);
417     _menu_destroy_menu_window(&mw);
418     mw = (menu_window_t *)private_data->menu_windows.tail.prev;
419   } while (mw->wl.node.prev && (mw != menu_window));
420   /* Set focus to parent menu */
421   if(xitk_is_window_visible(menu_window->display, (xitk_window_get_window(menu_window->xwin)))) {
422     int    t = 5000;
423     Window focused_win;
424 
425     do {
426       int revert;
427 
428       XLOCK (menu_window->im->x.x_lock_display, menu_window->display);
429       XSetInputFocus(menu_window->display, (xitk_window_get_window(menu_window->xwin)),
430 		     RevertToParent, CurrentTime);
431       XSync(menu_window->display, False);
432       XUNLOCK (menu_window->im->x.x_unlock_display, menu_window->display);
433 
434       /* Retry 5/15/30/50/75/105/140ms until the WM was mercyful to give us the focus */
435       xitk_usec_sleep (t);
436       t += 5000;
437       XLOCK (menu_window->im->x.x_lock_display, menu_window->display);
438       XGetInputFocus(menu_window->display, &focused_win, &revert);
439       XUNLOCK (menu_window->im->x.x_unlock_display, menu_window->display);
440 
441     } while ((focused_win != xitk_window_get_window (menu_window->xwin)) && (t <= 140000));
442   }
443 }
444 
_menu_show_subs(menu_private_data_t * private_data,menu_window_t * menu_window)445 static int _menu_show_subs(menu_private_data_t *private_data, menu_window_t *menu_window) {
446   menu_window_t *mw;
447 
448   mw = (menu_window_t *)private_data->menu_windows.tail.prev;
449   if (!mw->wl.node.prev)
450     mw = NULL;
451 
452   return (mw != menu_window);
453 }
454 
_menu_hide_menu(menu_private_data_t * private_data)455 static void _menu_hide_menu(menu_private_data_t *private_data) {
456   menu_window_t *mw;
457 
458   XLOCK (private_data->imlibdata->x.x_lock_display, private_data->imlibdata->x.disp);
459   mw = (menu_window_t *)private_data->menu_windows.tail.prev;
460   while (mw->wl.node.prev) {
461     XUnmapWindow(private_data->imlibdata->x.disp, xitk_window_get_window(mw->xwin));
462     XSync(private_data->imlibdata->x.disp, False);
463     mw = (menu_window_t *)mw->wl.node.prev;
464   }
465   XUNLOCK (private_data->imlibdata->x.x_unlock_display, private_data->imlibdata->x.disp);
466 }
467 
_menu_destroy_ntree(menu_node_t ** mn)468 static void _menu_destroy_ntree(menu_node_t **mn) {
469 
470   if((*mn)->branch)
471     _menu_destroy_ntree(&((*mn)->branch));
472 
473   if((*mn)->next)
474     _menu_destroy_ntree(&((*mn)->next));
475 
476   if((*mn)->menu_entry) {
477     free((*mn)->menu_entry->menu);
478     free((*mn)->menu_entry->shortcut);
479     free((*mn)->menu_entry->type);
480     free((*mn)->menu_entry);
481   }
482 
483   free((*mn));
484   (*mn) = NULL;
485 }
486 
xitk_menu_get_menu(xitk_widget_t * w)487 xitk_widget_t *xitk_menu_get_menu(xitk_widget_t *w) {
488   xitk_widget_t   *widget = NULL;
489 
490   if(w && ((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU)) {
491 
492     if(w->type & WIDGET_GROUP_WIDGET)
493       widget = w;
494     else {
495       menu_window_t *mw = (menu_window_t *) w->wl;
496       widget = mw->widget;
497     }
498   }
499 
500   return widget;
501 }
502 
xitk_menu_destroy(xitk_widget_t * w)503 void xitk_menu_destroy(xitk_widget_t *w) {
504 
505   if(w && (((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU) &&
506 	   (w->type & WIDGET_GROUP_WIDGET))) {
507     menu_private_data_t *private_data = (menu_private_data_t *) w->private_data;
508     menu_window_t       *mw;
509 
510     private_data->curbranch = NULL;
511     xitk_unset_current_menu();
512 
513     mw = (menu_window_t *)private_data->menu_windows.head.next;
514     while (mw->wl.node.next) {
515       xitk_dnode_remove (&mw->wl.node);
516       _menu_destroy_menu_window(&mw);
517       mw = (menu_window_t *)private_data->menu_windows.head.next;
518     }
519 
520     xitk_dlist_clear (&private_data->menu_windows);
521     _menu_destroy_ntree(&private_data->mtree->first);
522   }
523 }
524 
_menu_click_cb(xitk_widget_t * w,void * data)525 static void _menu_click_cb(xitk_widget_t *w, void *data) {
526   menu_node_t          *me = (menu_node_t *) data;
527   xitk_widget_t        *widget = me->widget;
528   menu_private_data_t  *private_data = (menu_private_data_t *) widget->private_data;
529 
530   if(_menu_is_branch(me->menu_entry)) {
531 
532     if(me->branch) {
533 
534       if(!_menu_branch_is_in_curbranch(me->branch)) {
535 	int   wx = 0, wy = 0, x = 0, y = 0;
536 
537 	xitk_window_get_window_position(private_data->imlibdata,
538 					me->menu_window->xwin, &wx, &wy, NULL, NULL);
539 	xitk_get_widget_pos(me->button, &x, &y);
540 
541 	x += (xitk_get_widget_width(me->button)) + wx;
542 	y += wy;
543 
544 	_menu_destroy_subs(private_data, me->menu_window);
545 
546 	_menu_create_menu_from_branch(me->branch, me->widget, x, y);
547       }
548       else {
549         XLOCK (me->branch->menu_window->im->x.x_lock_display, private_data->imlibdata->x.disp);
550 	XRaiseWindow(me->branch->menu_window->display,
551 		     xitk_window_get_window(me->branch->menu_window->xwin));
552 	XSetInputFocus(me->branch->menu_window->display,
553 		       (xitk_window_get_window(me->branch->menu_window->xwin)),
554 		       RevertToParent, CurrentTime);
555         XUNLOCK (me->branch->menu_window->im->x.x_unlock_display, private_data->imlibdata->x.disp);
556       }
557     }
558     else {
559       if(_menu_show_subs(private_data, me->menu_window)) {
560 	_menu_destroy_subs(private_data, me->menu_window);
561 	private_data->curbranch = _menu_get_branch_from_node(me);
562       }
563     }
564   }
565   else if(!_menu_is_title(me->menu_entry) && !_menu_is_separator(me->menu_entry)) {
566     _menu_hide_menu(private_data);
567 
568     if(me->menu_entry->cb)
569       me->menu_entry->cb(widget, me->menu_entry, me->menu_entry->user_data);
570 
571     xitk_menu_destroy(widget);
572   }
573 #ifdef DEBUG_MENU
574   if(_menu_is_separator(me->menu_entry))
575     printf("SEPARATOR\n");
576 #endif
577 }
578 
_menu_handle_xevents(XEvent * event,void * data)579 static void _menu_handle_xevents(XEvent *event, void *data) {
580   /*
581   menu_window_t *me = (menu_window_t *) data;
582   menu_private_data_t  *private_data = (menu_private_data_t *) me->widget->private_data;
583 
584   switch(event->type) {
585   }
586   */
587 }
588 
__menu_find_branch_by_name(menu_node_t * mnode,menu_node_t ** fnode,char * name)589 static void __menu_find_branch_by_name(menu_node_t *mnode, menu_node_t **fnode, char *name) {
590   menu_node_t *m = mnode;
591 
592   if((*fnode))
593     return;
594 
595   if(m->branch) {
596     if(!strcmp(m->menu_entry->menu, name)) {
597       (*fnode) = m;
598       return;
599     }
600     __menu_find_branch_by_name(m->branch, fnode, name);
601   }
602 
603   if((*fnode))
604     return;
605 
606   if(m->next) {
607     if(!strcmp(m->menu_entry->menu, name)) {
608       (*fnode) = m;
609       return;
610     }
611     __menu_find_branch_by_name(m->next, fnode, name);
612   }
613   else {
614     if(!strcmp(m->menu_entry->menu, name)) {
615       (*fnode) = m;
616     }
617   }
618 
619 }
620 
_menu_find_branch_by_name(menu_node_t * mnode,char * name)621 static menu_node_t *_menu_find_branch_by_name(menu_node_t *mnode, char *name) {
622   menu_node_t *m = NULL;
623 
624   if (mnode) __menu_find_branch_by_name(mnode, &m, name);
625 
626   return m;
627 }
xitk_menu_add_entry(xitk_widget_t * w,xitk_menu_entry_t * me)628 void xitk_menu_add_entry(xitk_widget_t *w, xitk_menu_entry_t *me) {
629 
630   if(w && me && (((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU) &&
631 		 (w->type & WIDGET_GROUP_WIDGET))) {
632 #ifdef DEBUG_MENU
633     printf("======== ENTER =========\n%s\n", me->menu);
634 #endif
635 
636     if(me->menu) {
637       menu_private_data_t *private_data = (menu_private_data_t *) w->private_data;
638       char                *o, *c, *new_entry;
639       char                 buffer[strlen(me->menu) + 1];
640       menu_node_t         *branch = NULL;
641       int                  in_trunk = 1;
642 
643       strlcpy(buffer, me->menu, sizeof(buffer));
644 
645       o = c = new_entry = buffer;
646 
647       if(!(branch = private_data->mtree->first)) {
648 #ifdef DEBUG_MENU
649 	printf("FIRST ENTRY OF MENU\n");
650 #endif
651 	private_data->mtree->last = private_data->mtree->cur = private_data->mtree->first =
652 	  _menu_append_to_node(w, &(private_data->mtree->first), me, new_entry);
653 	return;
654       }
655 
656       while( (c = strchr(c, '/')) && (c && (*(c - 1) != '\\'))/*  && branch */) {
657 	*c = '\0';
658 
659 	in_trunk = 0;
660 
661 #ifdef DEBUG_MENU
662 	printf("LOOKING FOR '%s': ", o);
663 #endif
664 
665 	/* Try to find branch */
666 	branch = _menu_find_branch_by_name(branch, o);
667 
668 #ifdef DEBUG_MENU
669 	if(branch) {
670 	  printf(" FOUND %s\n", branch->menu_entry->menu);
671 	}
672 	else
673 	  printf("NOT FOUND\n");
674 #endif
675 
676 	c++;
677 	o = new_entry = c;
678       }
679 
680       if(branch) {
681 #ifdef DEBUG_MENU
682 	printf("ADD NEW ENTRY '%s' ", new_entry);
683 #endif
684 	if(_menu_is_branch(branch->menu_entry)) {
685 #ifdef DEBUG_MENU
686 	  printf("[IS BRANCH], ");
687 #endif
688 	  if(branch->branch == NULL) {
689 #ifdef DEBUG_MENU
690 	    printf(" NEW BRANCH [%s]\n", branch->menu_entry->menu);
691 #endif
692 	    _menu_add_to_node_branch(w, &branch, me, new_entry);
693 	    goto __done;
694 	  }
695 	  else {
696 	    branch = branch->branch;
697 #ifdef DEBUG_MENU
698 	    printf(" IN BRANCH [%s] ", branch->menu_entry->menu);
699 #endif
700 	  }
701 	}
702 
703 	while(branch->next)
704 	  branch = branch->next;
705 
706 #ifdef DEBUG_MENU
707 	printf("AFTER '%s'\n", branch->menu_entry->menu);
708 #endif
709 	branch = _menu_append_to_node(w, &branch, me, new_entry);
710 	if(in_trunk)
711 	  private_data->mtree->last = branch;
712       }
713     __done: ;
714 #ifdef DEBUG_MENU
715       printf("******* BYE ************\n");
716 #endif
717 #ifdef DUMP_MENU
718       _menu_dump(private_data);
719 #endif
720     }
721   }
722 }
723 
_menu_scissor(xitk_widget_t * w,menu_private_data_t * private_data,xitk_menu_entry_t * me)724 static void _menu_scissor(xitk_widget_t *w,
725 			  menu_private_data_t *private_data, xitk_menu_entry_t *me) {
726   menu_tree_t  *mtree;
727 
728   mtree        = (menu_tree_t *) xitk_xmalloc(sizeof(menu_tree_t));
729   mtree->first = NULL;
730   mtree->cur   = NULL;
731   mtree->last  = NULL;
732 
733   private_data->mtree = mtree;
734 
735   while(me->menu) {
736     xitk_menu_add_entry(w, me);
737     me++;
738   }
739 
740 }
741 
_menu_create_menu_from_branch(menu_node_t * branch,xitk_widget_t * w,int x,int y)742 static void _menu_create_menu_from_branch(menu_node_t *branch, xitk_widget_t *w, int x, int y) {
743   xitk_skin_element_info_t    info;
744   menu_private_data_t        *private_data;
745   int                         bentries, bsep, btitle, rentries;
746   menu_node_t                *maxnode, *me;
747   int                         maxlen, wwidth, wheight, swidth, sheight, shortcutlen = 0, shortcutpos = 0;
748   xitk_font_t                *fs;
749   static xitk_window_t       *xwin;
750   menu_window_t              *menu_window;
751   xitk_labelbutton_widget_t   lb;
752   xitk_widget_t              *btn;
753   xitk_pixmap_t              *bg = NULL;
754   int                         yy = 0, got_title = 0;
755 
756   private_data = (menu_private_data_t *) w->private_data;
757 
758   private_data->curbranch = branch;
759 
760   XITK_WIDGET_INIT(&lb, private_data->imlibdata);
761 
762   bentries = _menu_count_entry_from_branch(branch);
763   bsep     = _menu_count_separator_from_branch(branch);
764   btitle   = _menu_is_title_in_branch(branch);
765   rentries = bentries - bsep;
766   maxnode  = _menu_get_wider_menuitem_node(branch);
767 
768   if(_menu_is_title(maxnode->menu_entry))
769     fs = xitk_font_load_font(private_data->imlibdata->x.disp, DEFAULT_BOLD_FONT_14);
770   else
771     fs = xitk_font_load_font(private_data->imlibdata->x.disp, DEFAULT_BOLD_FONT_12);
772 
773   xitk_font_set_font(fs, private_data->widget->wl->gc);
774   maxlen = xitk_font_get_string_length(fs, maxnode->menu_entry->menu);
775 
776   if(xitk_get_menu_shortcuts_enability() && _menu_branch_have_shortcut(branch)) {
777     xitk_font_t *short_font;
778     short_font = xitk_font_load_font(private_data->imlibdata->x.disp, DEFAULT_FONT_12);
779     shortcutlen = xitk_font_get_string_length(short_font, (_menu_get_wider_shortcut_node(branch))->menu_entry->shortcut);
780     maxlen += shortcutlen + 15;
781     xitk_font_unload_font(short_font);
782   }
783 
784   xitk_font_unload_font(fs);
785 
786   wwidth = maxlen + 40;
787 
788   if(_menu_branch_have_check(branch) || _menu_branch_have_branch(branch))
789     wwidth += 20;
790   wheight = (rentries * 20) + (bsep * 2) + (btitle * 2);
791 
792   shortcutpos = (wwidth - shortcutlen) - 15;
793 
794   XLOCK (private_data->imlibdata->x.x_lock_display, private_data->imlibdata->x.disp);
795   swidth = DisplayWidth(private_data->imlibdata->x.disp,
796 			(DefaultScreen(private_data->imlibdata->x.disp)));
797   sheight = DisplayHeight(private_data->imlibdata->x.disp,
798 			  (DefaultScreen(private_data->imlibdata->x.disp)));
799   XUNLOCK (private_data->imlibdata->x.x_unlock_display, private_data->imlibdata->x.disp);
800 
801   if(branch != private_data->mtree->first) {
802     x -= 4; /* Overlap parent menu but leave text and symbols visible */
803     y -= 1; /* Top item of submenu in line with parent item */
804   }
805   else {
806     x++; y++; /* Upper left corner 1 pix distance to mouse pointer */
807   }
808 
809   /* Check if menu fits on screen and adjust position if necessary in a way */
810   /* that it doesn't obscure the parent menu or get under the mouse pointer */
811 
812   if((x + (wwidth + 2)) > swidth) { /* Exceeds right edge of screen */
813     if(branch != private_data->mtree->first) {
814       /* Align right edge of submenu to left edge of parent item */
815       x -= xitk_get_widget_width(branch->prev->button) + (wwidth + 2) - 4;
816     }
817     else {
818       /* Align right edge of top level menu to right edge of screen */
819       x  = swidth - (wwidth + 2);
820     }
821   }
822   if((y + (wheight + 2)) > sheight) { /* Exceeds bottom edge of screen */
823     if(branch != private_data->mtree->first) {
824       /* Align bottom edge of submenu for bottom item in line with parent item */
825       y -= wheight - xitk_get_widget_height(branch->prev->button);
826     }
827     else {
828       /* Align bottom edge of top level menu to requested (i.e. pointer) pos */
829       y -= (wheight + 2) + 1;
830     }
831   }
832 
833   xwin = xitk_window_create_simple_window(private_data->imlibdata,
834 					  x, y, wwidth + 2, wheight + 2);
835 
836   if(bsep || btitle) {
837     int width, height;
838 
839     xitk_window_get_window_size(xwin, &width, &height);
840     bg = xitk_image_create_xitk_pixmap(private_data->imlibdata, width, height);
841     XLOCK (private_data->imlibdata->x.x_lock_display, private_data->imlibdata->x.disp);
842     XCopyArea(private_data->imlibdata->x.disp, (xitk_window_get_background(xwin)), bg->pixmap,
843     	      bg->gc, 0, 0, width, height, 0, 0);
844     XUNLOCK (private_data->imlibdata->x.x_unlock_display, private_data->imlibdata->x.disp);
845   }
846 
847   menu_window         = _menu_new_menu_window(private_data->imlibdata, xwin);
848   menu_window->widget = w;
849 
850   menu_window->bevel_plain = xitk_image_create_image (private_data->imlibdata, wwidth * 3, 20);
851   if (menu_window->bevel_plain)
852     draw_flat_three_state (private_data->imlibdata, menu_window->bevel_plain);
853 
854   menu_window->bevel_arrow = xitk_image_create_image (private_data->imlibdata, wwidth * 3, 20);
855   if (menu_window->bevel_arrow) {
856     draw_flat_three_state (private_data->imlibdata, menu_window->bevel_arrow);
857     menu_draw_arrow_branch (private_data->imlibdata, menu_window->bevel_arrow);
858   }
859 
860   menu_window->bevel_unchecked = xitk_image_create_image (private_data->imlibdata, wwidth * 3, 20);
861   if (menu_window->bevel_unchecked) {
862     draw_flat_three_state (private_data->imlibdata, menu_window->bevel_unchecked);
863     menu_draw_check (private_data->imlibdata, menu_window->bevel_unchecked, 0);
864   }
865 
866   menu_window->bevel_checked = xitk_image_create_image (private_data->imlibdata, wwidth * 3, 20);
867   if (menu_window->bevel_checked) {
868     draw_flat_three_state (private_data->imlibdata, menu_window->bevel_checked);
869     menu_draw_check (private_data->imlibdata, menu_window->bevel_checked, 1);
870   }
871 
872   memset (&info, 0, sizeof (info));
873   info.x                 = 1;
874   info.visibility        = 0;
875   info.enability         = 0;
876   info.pixmap_name       = NULL;
877   info.pixmap_img        = menu_window->bevel_plain;
878   info.label_alignment   = ALIGN_LEFT;
879   info.label_printable   = 1;
880   info.label_staticity   = 0;
881   info.label_color       =
882   info.label_color_focus = (char *)"Black";
883   info.label_color_click = (char *)"White";
884   info.label_fontname    = (char *)DEFAULT_BOLD_FONT_12;
885 
886   xitk_dlist_add_tail (&private_data->menu_windows, &menu_window->wl.node);
887 
888   me = branch;
889   yy = 1;
890   while(me) {
891 
892     me->menu_window      = menu_window;
893     me->button           = NULL;
894 
895     if(_menu_is_title(me->menu_entry)) {
896 
897       if(bg && (!got_title)) {
898 	int           lbear, rbear, width, asc, des;
899 	unsigned int  cfg, cbg;
900 	XColor        xcolorf, xcolorb;
901 
902 	fs = xitk_font_load_font(private_data->imlibdata->x.disp, DEFAULT_BOLD_FONT_14);
903 	xitk_font_set_font(fs, private_data->widget->wl->gc);
904 
905 	xitk_font_string_extent(fs, me->menu_entry->menu, &lbear, &rbear, &width, &asc, &des);
906 
907 	xcolorb.red = xcolorb.blue = xcolorb.green = 140<<8;
908 	xcolorf.red = xcolorf.blue = xcolorf.green = 255<<8;
909 
910         XLOCK (private_data->imlibdata->x.x_lock_display, private_data->imlibdata->x.disp);
911 	XAllocColor(private_data->imlibdata->x.disp,
912 		    Imlib_get_colormap(private_data->imlibdata), &xcolorb);
913 	XAllocColor(private_data->imlibdata->x.disp,
914 		    Imlib_get_colormap(private_data->imlibdata), &xcolorf);
915         XUNLOCK (private_data->imlibdata->x.x_unlock_display, private_data->imlibdata->x.disp);
916 
917 	cfg = xcolorf.pixel;
918 	cbg = xcolorb.pixel;
919 
920         XLOCK (private_data->imlibdata->x.x_lock_display, private_data->imlibdata->x.disp);
921 	XSetForeground(private_data->imlibdata->x.disp, private_data->widget->wl->gc, cbg);
922 	XFillRectangle(private_data->imlibdata->x.disp, bg->pixmap, private_data->widget->wl->gc,
923 		       1, 1, wwidth , 20);
924 	XSetForeground(private_data->imlibdata->x.disp, private_data->widget->wl->gc, cfg);
925 	xitk_font_draw_string(fs, bg->pixmap,
926 			      private_data->widget->wl->gc,
927 			      5, 1+ ((20+asc+des)>>1)-des,
928 			      me->menu_entry->menu, strlen(me->menu_entry->menu));
929         XUNLOCK (private_data->imlibdata->x.x_unlock_display, private_data->imlibdata->x.disp);
930 
931 	xitk_font_unload_font(fs);
932 
933 	yy += 20;
934 	got_title = 1;
935 
936 	goto __sep;
937       }
938     }
939     else if(_menu_is_separator(me->menu_entry)) {
940     __sep:
941       if(bg)
942 	draw_rectangular_inner_box_light(private_data->imlibdata, bg, 3, yy, wwidth - 6, 1);
943       yy += 2;
944     }
945     else {
946       lb.button_type       = CLICK_BUTTON;
947       lb.label             = me->menu_entry->menu;
948       lb.align             = ALIGN_LEFT;
949       lb.callback          = _menu_click_cb;
950       lb.state_callback    = NULL;
951       lb.userdata          = (void *) me;
952       lb.skin_element_name = NULL;
953 
954       info.y = yy;
955       if (_menu_is_branch (me->menu_entry)) {
956         info.pixmap_img = menu_window->bevel_arrow;
957       } else if (_menu_is_check (me->menu_entry)) {
958         info.pixmap_img = _menu_is_checked (me->menu_entry) ? menu_window->bevel_checked : menu_window->bevel_unchecked;
959       } else {
960         info.pixmap_img = menu_window->bevel_plain;
961       }
962 
963       btn = xitk_info_labelbutton_create (&menu_window->wl, &lb, &info);
964       xitk_dlist_add_tail (&menu_window->wl.list, &btn->node);
965       btn->type |= WIDGET_GROUP | WIDGET_GROUP_MENU;
966       me->button = btn;
967 
968       if(xitk_get_menu_shortcuts_enability()  && me->menu_entry->shortcut)
969 	xitk_labelbutton_change_shortcut_label(btn, me->menu_entry->shortcut, shortcutpos, DEFAULT_FONT_12);
970 
971       xitk_labelbutton_set_label_offset(btn, 20);
972       xitk_enable_and_show_widget(btn);
973 
974       yy += 20;
975     }
976 
977     me = me->next;
978   }
979 
980   if(bg) {
981     xitk_window_change_background(private_data->imlibdata, xwin,
982 				  bg->pixmap, bg->width, bg->height);
983     xitk_image_destroy_xitk_pixmap(bg);
984   }
985 
986   xitk_set_layer_above((xitk_window_get_window(xwin)));
987 
988   XLOCK (private_data->imlibdata->x.x_lock_display, private_data->imlibdata->x.disp);
989   /* Set transient-for-hint to the immediate predecessor,     */
990   /* so window stacking of submenus is kept upon raise/lower. */
991   if(branch == private_data->mtree->first)
992     XSetTransientForHint(private_data->imlibdata->x.disp,
993 			 (xitk_window_get_window(xwin)), private_data->parent_wlist->win);
994   else
995     XSetTransientForHint(private_data->imlibdata->x.disp,
996 			 (xitk_window_get_window(xwin)), (xitk_window_get_window(branch->prev->menu_window->xwin)));
997   XUNLOCK (private_data->imlibdata->x.x_unlock_display, private_data->imlibdata->x.disp);
998 
999   menu_window->key = xitk_register_event_handler("xitk menu",
1000 						 (xitk_window_get_window(menu_window->xwin)),
1001 						 _menu_handle_xevents,
1002 						 NULL,
1003 						 NULL,
1004 						 &(menu_window->wl),
1005 						 (void *) menu_window);
1006 
1007   XLOCK (private_data->imlibdata->x.x_lock_display, private_data->imlibdata->x.disp);
1008   XMapRaised(private_data->imlibdata->x.disp, (xitk_window_get_window(xwin)));
1009   XSync(private_data->imlibdata->x.disp, False);
1010   XUNLOCK (private_data->imlibdata->x.x_unlock_display, private_data->imlibdata->x.disp);
1011 
1012   {
1013     int t = 5000;
1014     while (!xitk_is_window_visible (private_data->imlibdata->x.disp, (xitk_window_get_window(xwin))) && (t <= 140000)) {
1015       xitk_usec_sleep (t);
1016       t += 5000;
1017     }
1018   }
1019 
1020   if(!(xitk_get_wm_type() & WM_TYPE_KWIN))
1021     /* WINDOW_TYPE_MENU seems to be the natural choice. */
1022     xitk_set_wm_window_type(xitk_window_get_window(xwin), WINDOW_TYPE_MENU);
1023   else
1024     /* However, KWin has unacceptable behaviour for WINDOW_TYPE_MENU in  */
1025     /* our transient-for scheme: The transient-for window must be mapped */
1026     /* and the transient-for window or another transient window (incl.   */
1027     /* the menu itself) must have focus, otherwise it unmaps the menu.   */
1028     /* This causes menus not to be shown under many several conditions.  */
1029     /* WINDOW_TYPE_DOCK is definitely the right choice for KWin.         */
1030     xitk_set_wm_window_type(xitk_window_get_window(xwin), WINDOW_TYPE_DOCK);
1031 
1032   XLOCK (private_data->imlibdata->x.x_lock_display, private_data->imlibdata->x.disp);
1033   XSetInputFocus(private_data->imlibdata->x.disp,
1034 		 (xitk_window_get_window(xwin)), RevertToParent, CurrentTime);
1035   XUNLOCK (private_data->imlibdata->x.x_unlock_display, private_data->imlibdata->x.disp);
1036 
1037   btn = (xitk_widget_t *)menu_window->wl.list.head.next;
1038   if (btn) {
1039     xitk_set_focus_to_widget(btn);
1040   }
1041 }
1042 
xitk_menu_destroy_sub_branchs(xitk_widget_t * w)1043 void xitk_menu_destroy_sub_branchs(xitk_widget_t *w) {
1044 
1045   if(w && (((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU) &&
1046 	   (w->type & WIDGET_GROUP_WIDGET))) {
1047     menu_private_data_t *private_data = (menu_private_data_t *) w->private_data;
1048     menu_node_t         *me = private_data->mtree->first;
1049 
1050     if (me) {
1051       if (me->next)
1052         me = me->next;
1053 
1054       if (me)
1055         _menu_destroy_subs(private_data, me->menu_window);
1056     }
1057 
1058     private_data->curbranch = private_data->mtree->first;
1059   }
1060 }
1061 
xitk_menu_show_sub_branchs(xitk_widget_t * w)1062 int xitk_menu_show_sub_branchs(xitk_widget_t *w) {
1063   int ret = 0;
1064 
1065   if(w && (((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU) &&
1066 	   (w->type & WIDGET_GROUP_WIDGET))) {
1067     menu_private_data_t *private_data = (menu_private_data_t *) w->private_data;
1068 
1069     if(private_data->curbranch && (private_data->curbranch != private_data->mtree->first))
1070       ret = 1;
1071   }
1072 
1073   return ret;
1074 }
1075 
xitk_menu_destroy_branch(xitk_widget_t * w)1076 void xitk_menu_destroy_branch(xitk_widget_t *w) {
1077 
1078   if(w && (((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU) &&
1079 	   (w->type & WIDGET_TYPE_LABELBUTTON))) {
1080     menu_node_t         *me = labelbutton_get_user_data(w);
1081 
1082     me = _menu_get_prev_branch_from_node(me);
1083     if(me) {
1084       menu_private_data_t *private_data = (menu_private_data_t *) me->widget->private_data;
1085 
1086       _menu_destroy_subs(private_data, me->menu_window);
1087       private_data->curbranch = me;
1088     }
1089     else {
1090       xitk_menu_destroy(xitk_menu_get_menu(w));
1091     }
1092   }
1093 }
1094 
menu_auto_pop(xitk_widget_t * w)1095 void menu_auto_pop(xitk_widget_t *w) {
1096 
1097   if(w && (((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU) &&
1098 	   (w->type & WIDGET_TYPE_LABELBUTTON) && (!(w->type & WIDGET_GROUP_WIDGET)))) {
1099     menu_window_t       *mw = (menu_window_t *) w->wl;
1100     menu_node_t         *me = labelbutton_get_user_data(w);
1101     xitk_widget_t       *widget;
1102     menu_private_data_t *private_data;
1103 
1104     widget = mw->widget;
1105     private_data = (menu_private_data_t *) widget->private_data;
1106 
1107     if(_menu_is_branch(me->menu_entry))
1108       xitk_labelbutton_callback_exec(w);
1109     else {
1110       if(_menu_show_subs(private_data, me->menu_window)) {
1111 	_menu_destroy_subs(private_data, me->menu_window);
1112 	private_data->curbranch = _menu_get_branch_from_node(me);
1113       }
1114     }
1115   }
1116 }
1117 
xitk_menu_show_menu(xitk_widget_t * w)1118 void xitk_menu_show_menu(xitk_widget_t *w) {
1119 
1120   if(w && (((w->type & WIDGET_GROUP_MASK) & WIDGET_GROUP_MENU) &&
1121 	   (w->type & WIDGET_GROUP_WIDGET))) {
1122     menu_private_data_t *private_data = (menu_private_data_t *) w->private_data;
1123 
1124     w->visible = 1;
1125     _menu_create_menu_from_branch(private_data->mtree->first, w, private_data->x, private_data->y);
1126     xitk_set_current_menu(w);
1127   }
1128 }
1129 
xitk_noskin_menu_create(xitk_widget_list_t * wl,xitk_menu_widget_t * m,int x,int y)1130 xitk_widget_t *xitk_noskin_menu_create(xitk_widget_list_t *wl,
1131 				       xitk_menu_widget_t *m, int x, int y) {
1132   xitk_widget_t         *mywidget;
1133   menu_private_data_t   *private_data;
1134 
1135   XITK_CHECK_CONSTITENCY(m);
1136 
1137   mywidget                   = (xitk_widget_t *) xitk_xmalloc (sizeof(xitk_widget_t));
1138   private_data               = (menu_private_data_t *) xitk_xmalloc(sizeof(menu_private_data_t));
1139   private_data->imlibdata    = m->imlibdata;
1140   private_data->parent_wlist = m->parent_wlist;
1141   private_data->widget       = mywidget;
1142   xitk_dlist_init (&private_data->menu_windows);
1143   private_data->x            = x;
1144   private_data->y            = y;
1145   private_data->curbranch    = NULL;
1146 
1147 
1148   if(!m->menu_tree) {
1149     printf("Empty menu entries. You will not .\n");
1150     abort();
1151   }
1152 
1153   mywidget->private_data                 = private_data;
1154   mywidget->wl                           = wl;
1155   mywidget->type                         = WIDGET_GROUP | WIDGET_GROUP_WIDGET | WIDGET_GROUP_MENU | WIDGET_FOCUSABLE;
1156 
1157   _menu_scissor(mywidget, private_data, m->menu_tree);
1158 
1159 #ifdef DEBUG_DUMP
1160   _menu_dump(private_data);
1161 #endif
1162 
1163   mywidget->enable                       = 1;
1164   mywidget->running                      = 1;
1165   mywidget->visible                      = 0;
1166   mywidget->have_focus                   = FOCUS_RECEIVED;
1167   mywidget->imlibdata                    = private_data->imlibdata;
1168   mywidget->x = mywidget->y = mywidget->width = mywidget->height = 0;
1169   mywidget->event                        = notify_event;
1170   mywidget->tips_timeout                 = 0;
1171   mywidget->tips_string                  = NULL;
1172   return mywidget;
1173 }
1174