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