chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / menus.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "borders.h"
26 #include "desktops.h"
27 #include "dialog.h"
28 #include "e16-ecore_list.h"
29 #include "eimage.h"
30 #include "emodule.h"
31 #include "eobj.h"
32 #include "ewins.h"
33 #include "file.h"
34 #include "grabs.h"
35 #include "hints.h"
36 #include "iclass.h"
37 #include "menus.h"
38 #include "screen.h"
39 #include "settings.h"
40 #include "tclass.h"
41 #include "timers.h"
42 #include "tooltips.h"
43 #include "xwin.h"
44 #include <time.h>
45 #include <X11/keysym.h>
46
47 #define DEBUG_MENU_EVENTS 0
48
49 #define MENU_UNLOAD_CHECK_INTERVAL 300  /* Seconds */
50
51 static struct {
52    Menu               *first;
53    Menu               *active;
54    EWin               *context_ewin;
55    EObj               *cover_win;
56    char                just_shown;
57 } Mode_menus;
58
59 struct _menustyle {
60    char               *name;
61    TextClass          *tclass;
62    ImageClass         *bg_iclass;
63    ImageClass         *item_iclass;
64    ImageClass         *sub_iclass;
65    char                use_item_bg;
66    char                iconpos;
67    int                 maxx;
68    int                 maxy;
69    char               *border_name;
70    unsigned int        ref_count;
71 };
72
73 struct _menuitem {
74    Menu               *menu;
75    ImageClass         *icon_iclass;
76    char               *text;
77    char               *params;
78    Menu               *child;
79    char                state;
80    PmapMask            pmm[3];
81    Win                 win;
82    Win                 icon_win;
83    short               icon_w;
84    short               icon_h;
85    short               text_w;
86    short               text_h;
87    short               text_x;
88    short               text_y;
89 };
90
91 struct _menu {
92    EWin               *ewin;
93    Win                 win;
94    PmapMask            pmm;
95    int                 w, h;
96    char               *name;
97    char               *title;
98    char               *alias;
99    MenuStyle          *style;
100    MenuLoader         *loader;
101    int                 num;
102    MenuItem          **items;
103    int                 icon_size;
104    char                internal;        /* Don't destroy when reloading */
105    char                dynamic; /* May be emptied on close */
106    char                shown;
107    char                stuck;
108    char                redraw;
109    char                filled;  /* Has been filled */
110    Menu               *parent;
111    Menu               *child;
112    MenuItem           *sel_item;
113    time_t              last_change;
114    time_t              last_access;
115    void               *data;
116    unsigned int        ref_count;
117 };
118
119 #define MENU_ITEM_EVENT_MASK \
120         KeyPressMask | KeyReleaseMask | \
121         ButtonPressMask | ButtonReleaseMask | \
122         EnterWindowMask | LeaveWindowMask
123
124 static void         MenuRedraw(Menu * m);
125 static void         MenuRealize(Menu * m);
126 static void         MenuActivateItem(Menu * m, MenuItem * mi);
127 static void         MenuDrawItem(Menu * m, MenuItem * mi, char shape,
128                                  int state);
129
130 static void         MenuHandleEvents(Win win, XEvent * ev, void *m);
131 static void         MenuItemHandleEvents(Win win, XEvent * ev, void *mi);
132 static void         MenuMaskerHandleEvents(Win win, XEvent * ev, void *prm);
133
134 static void         MenusHide(void);
135
136 static Ecore_List  *menu_list = NULL;
137 static Ecore_List  *menu_style_list = NULL;
138 static Timer       *menu_timer_submenu = NULL;
139
140 static MenuItem    *
141 MenuFindItemByChild(Menu * m, Menu * mc)
142 {
143    int                 i;
144
145    if (!mc)
146       return (m->num) ? m->items[0] : NULL;
147
148    for (i = 0; i < m->num; i++)
149      {
150         if (mc == m->items[i]->child)
151            return m->items[i];
152      }
153
154    return NULL;
155 }
156
157 void
158 MenuHide(Menu * m)
159 {
160    EWin               *ewin;
161
162    if (!m || !m->shown)
163       return;
164
165    if (m->sel_item)
166       MenuDrawItem(m, m->sel_item, 1, STATE_NORMAL);
167    m->sel_item = NULL;
168
169    ewin = m->ewin;
170    if (ewin)
171      {
172         EUnmapWindow(m->win);
173         EReparentWindow(m->win, VROOT, ewin->client.x, ewin->client.y);
174         EwinHide(ewin);
175      }
176    m->ewin = NULL;
177
178    m->stuck = 0;
179    m->shown = 0;
180    m->parent = NULL;
181    m->last_access = time(0);
182    if (m->child)
183      {
184         MenuHide(m->child);
185         m->child = NULL;
186      }
187    if (m->dynamic)
188       MenuEmpty(m, 0);
189    m->ref_count--;
190 }
191
192 static void
193 _MenuEwinInit(EWin * ewin)
194 {
195    Menu               *m = (Menu *) ewin->data;
196
197    EwinSetTitle(ewin, _(m->title));
198    EwinSetClass(ewin, m->name, "Enlightenment_Menu");
199
200    ewin->props.skip_ext_task = 1;
201    ewin->props.skip_ext_pager = 1;
202    ewin->props.no_actions = 1;
203    ewin->props.skip_focuslist = 1;
204    ewin->props.skip_winlist = 1;
205    ewin->props.ignorearrange = 1;
206    EwinInhSetWM(ewin, focus, 1);
207
208    ICCCM_SetSizeConstraints(ewin, m->w, m->h, m->w, m->h, 0, 0, 1, 1,
209                             0.0, 65535.0);
210    ewin->icccm.grav = StaticGravity;
211
212    EoSetLayer(ewin, 12);
213    ewin->ewmh.opacity = OpacityFromPercent(Conf.opacity.menus);
214 }
215
216 static void
217 _MenuEwinMoveResize(EWin * ewin, int resize __UNUSED__)
218 {
219    Menu               *m = (Menu *) ewin->data;
220
221    if (!m)
222       return;
223
224    if (Mode.mode != MODE_NONE && !m->redraw)
225       return;
226
227    if (TransparencyUpdateNeeded())
228       m->redraw = 1;
229
230    if ((!m->style->use_item_bg && m->pmm.pmap == 0) || m->redraw)
231      {
232         MenuRedraw(m);
233         EwinUpdateShapeInfo(ewin);
234      }
235 }
236
237 static void
238 _MenuEwinClose(EWin * ewin)
239 {
240    Menu               *m = (Menu *) ewin->data;
241
242    if (m == Mode_menus.active)
243      {
244         GrabKeyboardRelease();
245         Mode_menus.active = NULL;
246      }
247
248    ewin->data = NULL;
249 }
250
251 static const EWinOps _MenuEwinOps = {
252    _MenuEwinInit,
253    NULL,
254    _MenuEwinMoveResize,
255    _MenuEwinClose,
256 };
257
258 static void         MenuShowMasker(Menu * m);
259
260 int
261 MenuLoad(Menu * m)
262 {
263    if (!m || !m->loader)
264       return 0;
265
266    return m->loader(m);
267 }
268
269 static void
270 MenuShow(Menu * m, char noshow)
271 {
272    EWin               *ewin;
273    int                 x, y, w, h;
274    int                 wx, wy, mw, mh;
275    int                 head_num = 0;
276
277    if (m->shown)
278       return;
279
280    if (MenuLoad(m))
281       MenuRealize(m);
282
283    if (m->num <= 0)
284       return;
285
286    if (!m->win || !m->items[0]->win)
287       MenuRealize(m);
288
289    if (!m->style)
290       return;
291
292    ewin = m->ewin;
293    if (ewin)
294      {
295 #if 0                           /* ??? */
296         EwinRaise(ewin);
297         EwinShow(ewin);
298         return;
299 #else
300         MenuHide(m);
301 #endif
302      }
303
304    EGetGeometry(m->items[0]->win, NULL, &x, &y, &w, &h, NULL, NULL);
305    mw = m->w;
306    mh = m->h;
307
308    EQueryPointer(NULL, &wx, &wy, NULL, NULL);
309    wx -= EoGetX(DesksGetCurrent()) + x + (w / 2);
310    wy -= EoGetY(DesksGetCurrent()) + y + (h / 2);
311    if (Conf.menus.onscreen)
312      {
313         Border             *b;
314
315         b = BorderFind(m->style->border_name);
316         if (b)
317           {
318              int                 sx, sy, sw, sh;
319
320              head_num = ScreenGetGeometryByPointer(&sx, &sy, &sw, &sh);
321
322              if (wx > sx + sw - mw - b->border.right)
323                 wx = sx + sw - mw - b->border.right;
324              if (wx < sx + b->border.left)
325                 wx = sx + b->border.left;
326
327              if (wy > sy + sh - mh - b->border.bottom)
328                 wy = sy + sh - mh - b->border.bottom;
329              if (wy < sy + b->border.top)
330                 wy = sy + b->border.top;
331           }
332      }
333
334    EMoveWindow(m->win, wx, wy);
335
336    ewin = AddInternalToFamily(m->win, m->style->border_name, EWIN_TYPE_MENU,
337                               &_MenuEwinOps, m);
338    m->ewin = ewin;
339    if (ewin)
340      {
341         ewin->client.event_mask |= KeyPressMask;
342         ESelectInput(m->win, ewin->client.event_mask);
343
344         ewin->head = head_num;
345
346         EwinMoveToDesktop(ewin, EoGetDesk(ewin));
347         EwinResize(ewin, ewin->client.w, ewin->client.h);
348
349         if (Conf.menus.animate)
350            EwinInstantShade(ewin, 0);
351
352         if (!noshow)
353           {
354              ICCCM_Cmap(NULL);
355              EwinOpFloatAt(ewin, OPSRC_NA, EoGetX(ewin), EoGetY(ewin));
356              EwinShow(ewin);
357              if (Conf.menus.animate)
358                 EwinUnShade(ewin);
359           }
360      }
361
362    m->stuck = 0;
363    m->shown = 1;
364    m->last_access = time(0);
365    Mode_menus.just_shown = 1;
366
367    if (!Mode_menus.first)
368      {
369         Mode_menus.context_ewin = GetContextEwin();
370 #if 0
371         Eprintf("Mode_menus.context_ewin set %s\n",
372                 EwinGetTitle(Mode_menus.context_ewin));
373 #endif
374         ESync(ESYNC_MENUS);
375         Mode_menus.first = m;
376         MenuShowMasker(m);
377         TooltipsEnable(0);
378         GrabKeyboardSet(m->win);
379      }
380    m->ref_count++;
381 }
382
383 static MenuStyle   *
384 MenuStyleCreate(const char *name)
385 {
386    MenuStyle          *ms;
387
388    ms = ECALLOC(MenuStyle, 1);
389    if (!ms)
390       return NULL;
391
392    if (!menu_style_list)
393       menu_style_list = ecore_list_new();
394    ecore_list_prepend(menu_style_list, ms);
395
396    ms->name = Estrdup(name);
397    ms->iconpos = ICON_LEFT;
398
399    return ms;
400 }
401
402 MenuItem           *
403 MenuItemCreate(const char *text, ImageClass * iclass,
404                const char *action_params, Menu * child)
405 {
406    MenuItem           *mi;
407
408    mi = ECALLOC(MenuItem, 1);
409
410    mi->icon_iclass = iclass;
411 #if 0
412    if (iclass)
413       ImageclassIncRefcount(iclass);
414 #endif
415
416    mi->text = (text) ? Estrdup((text[0]) ? text : "?!?") : NULL;
417    mi->params = Estrdup(action_params);
418    mi->child = child;
419    if (child)
420       child->ref_count++;
421    mi->state = STATE_NORMAL;
422
423    return mi;
424 }
425
426 static int
427 _MenuStyleMatchName(const void *data, const void *match)
428 {
429    return strcmp(((const MenuStyle *)data)->name, (const char *)match);
430 }
431
432 MenuStyle          *
433 MenuStyleFind(const char *name)
434 {
435    MenuStyle          *ms;
436
437    ms = (MenuStyle *) ecore_list_find(menu_style_list, _MenuStyleMatchName,
438                                       name);
439    if (ms)
440       return ms;
441
442    ms = (MenuStyle *) ecore_list_find(menu_style_list, _MenuStyleMatchName,
443                                       "__fb_ms");
444    if (ms)
445       return ms;
446
447    ms = MenuStyleCreate("__fb_ms");
448    if (!ms)
449       return ms;
450
451    ms->tclass = TextclassFind(NULL, 1);
452    ms->bg_iclass = ImageclassFind(NULL, 1);
453    ms->item_iclass = ImageclassFind(NULL, 1);
454    ms->sub_iclass = ImageclassFind(NULL, 1);
455
456    return ms;
457 }
458
459 void
460 MenuSetInternal(Menu * m)
461 {
462    m->internal = 1;
463 }
464
465 void
466 MenuSetDynamic(Menu * m)
467 {
468    m->dynamic = 1;
469 }
470
471 void
472 MenuSetName(Menu * m, const char *name)
473 {
474    _EFDUP(m->name, name);
475 }
476
477 void
478 MenuSetAlias(Menu * m, const char *alias)
479 {
480    _EFDUP(m->alias, alias);
481 }
482
483 void
484 MenuSetTitle(Menu * m, const char *title)
485 {
486    _EFDUP(m->title, title);
487 }
488
489 void
490 MenuSetIconSize(Menu * m, int size)
491 {
492    if (size > 48)
493       size = 48;
494    m->icon_size = size;
495 }
496
497 void
498 MenuSetData(Menu * m, char *data)
499 {
500    Efree(m->data);
501    m->data = data;
502 }
503
504 void
505 MenuSetLoader(Menu * m, MenuLoader * loader)
506 {
507    m->loader = loader;
508 }
509
510 void
511 MenuSetTimestamp(Menu * m, time_t t)
512 {
513    m->last_change = t;
514 }
515
516 const char         *
517 MenuGetName(const Menu * m)
518 {
519    return m->name;
520 }
521
522 const char         *
523 MenuGetData(const Menu * m)
524 {
525    return (const char *)m->data;
526 }
527
528 time_t
529 MenuGetTimestamp(const Menu * m)
530 {
531    return m->last_change;
532 }
533
534 void
535 MenuSetStyle(Menu * m, MenuStyle * ms)
536 {
537    if (m->style)
538       m->style->ref_count--;
539    if (!ms && m->parent)
540       ms = m->parent->style;
541    if (!ms)
542       ms = MenuStyleFind("DEFAULT");
543    m->style = ms;
544    if (ms)
545       ms->ref_count++;
546 }
547
548 Menu               *
549 MenuCreate(const char *name, const char *title, Menu * parent, MenuStyle * ms)
550 {
551    Menu               *m;
552
553    m = ECALLOC(Menu, 1);
554    if (!m)
555       return m;
556
557    m->parent = parent;
558    MenuSetName(m, name);
559    MenuSetTitle(m, title);
560    if (ms)
561       MenuSetStyle(m, ms);
562    m->icon_size = -1;           /* Use image size */
563
564    if (!menu_list)
565       menu_list = ecore_list_new();
566    ecore_list_append(menu_list, m);
567
568    return m;
569 }
570
571 void
572 MenuDestroy(Menu * m)
573 {
574    if (!m)
575       return;
576
577    if (!ecore_list_goto(menu_list, m))
578       return;
579
580    MenuHide(m);
581    MenuEmpty(m, 1);
582
583    if (m->ref_count)
584       return;
585
586    ecore_list_node_remove(menu_list, m);
587
588    if (m->win)
589       EDestroyWindow(m->win);
590
591    Efree(m->name);
592    Efree(m->alias);
593    Efree(m->title);
594    Efree(m->data);
595
596    Efree(m);
597 }
598
599 /* NB - this doesnt free imageclasses if we created them for the menu
600  * FIXME: so it will leak if we create new imageclasses and stop using
601  * old ones for menu icons. we need to add some ref counting in menu icon
602  * imageclasses to knw to free them when not used
603  */
604 void
605 MenuEmpty(Menu * m, int destroying)
606 {
607    int                 i, j;
608    MenuItem           *mi;
609
610    for (i = 0; i < m->num; i++)
611      {
612         mi = m->items[i];
613         if (!mi)
614            continue;
615
616         if (mi->child)
617           {
618              mi->child->ref_count--;
619              MenuDestroy(mi->child);
620           }
621         Efree(mi->text);
622         Efree(mi->params);
623         for (j = 0; j < 3; j++)
624            FreePmapMask(&(mi->pmm[j]));
625         if (!destroying && mi->win)
626            EDestroyWindow(mi->win);
627         ImageclassFree(mi->icon_iclass);
628         Efree(mi);
629      }
630    Efree(m->items);
631    m->items = NULL;
632    m->num = 0;
633    m->sel_item = NULL;
634
635    FreePmapMask(&m->pmm);
636
637    m->filled = 0;
638 }
639
640 static void
641 MenuFreePixmaps(Menu * m)
642 {
643    int                 i, j;
644    MenuItem           *mi;
645
646    for (i = 0; i < m->num; i++)
647      {
648         mi = m->items[i];
649         if (!mi)
650            continue;
651
652         for (j = 0; j < 3; j++)
653            FreePmapMask(mi->pmm + j);
654      }
655
656    FreePmapMask(&m->pmm);
657
658    m->filled = 0;
659 }
660
661 #if 0                           /* Unused */
662 void
663 MenuRepack(Menu * m)
664 {
665    EWin               *ewin;
666
667    m->redraw = 1;
668    if (m->win)
669       MenuRealize(m);
670
671    ewin = m->ewin;
672    if (!ewin)
673       return;
674
675    ICCCM_SetSizeConstraints(ewin, m->w, m->h, m->w, m->h, 0, 0, 1, 1,
676                             0.0, 65535.0);
677    EwinResize(ewin, m->w, m->h);
678    EwinRaise(ewin);
679 }
680 #endif
681
682 void
683 MenuAddItem(Menu * m, MenuItem * item)
684 {
685    MenuItem          **items;
686
687    if (!item)
688       return;
689
690    items = EREALLOC(MenuItem *, m->items, m->num + 1);
691    if (!items)
692       return;
693
694    items[m->num] = item;
695    m->items = items;
696    m->num++;
697 }
698
699 static void
700 MenuRealize(Menu * m)
701 {
702    int                 i, maxh, maxw, nmaxy;
703    int                 maxx1, maxx2, w, h, x, y, r, mmw, mmh;
704    EImage             *im;
705    EImageBorder       *pad, *pad_item, *pad_sub;
706    char                has_i, has_s;
707
708    if (m->num <= 0)
709       return;
710
711    if (!m->style)
712      {
713         MenuSetStyle(m, NULL);
714         if (!m->style)
715            return;
716      }
717
718    if (!m->win)
719      {
720         m->win = ECreateClientWindow(VROOT, 0, 0, 1, 1);
721         EventCallbackRegister(m->win, 0, MenuHandleEvents, m);
722      }
723
724    maxh = maxw = 0;
725    maxx1 = 0;
726    maxx2 = 0;
727    has_i = 0;
728    has_s = 0;
729
730    for (i = 0; i < m->num; i++)
731      {
732         m->items[i]->menu = m;
733
734         if (m->items[i]->child)
735            has_s = 1;
736         else
737            has_i = 1;
738
739         m->items[i]->win = ECreateWindow(m->win, 0, 0, 1, 1, 0);
740         EventCallbackRegister(m->items[i]->win, 0, MenuItemHandleEvents,
741                               m->items[i]);
742         ESelectInput(m->items[i]->win, MENU_ITEM_EVENT_MASK);
743         EMapWindow(m->items[i]->win);
744
745         if ((m->style->tclass) && (m->items[i]->text))
746           {
747              TextSize(m->style->tclass, 0, 0, 0, _(m->items[i]->text), &w, &h,
748                       17);
749              if (h > maxh)
750                 maxh = h;
751              if (w > maxx1)
752                 maxx1 = w;
753              m->items[i]->text_w = w;
754              m->items[i]->text_h = h;
755           }
756         if (m->items[i]->icon_iclass && Conf.menus.show_icons)
757           {
758              im = ImageclassGetImage(m->items[i]->icon_iclass, 0, 0, 0);
759              if (im)
760                {
761                   w = h = 0;
762                   if (m->icon_size > 0)
763                      w = h = m->icon_size;
764                   else if (m->icon_size == 0)
765                      w = h = Conf.menus.icon_size;
766                   if (w <= 0 || h <= 0)
767                      EImageGetSize(im, &w, &h);
768                   m->items[i]->icon_w = w;
769                   m->items[i]->icon_h = h;
770                   m->items[i]->icon_win =
771                      ECreateWindow(m->items[i]->win, 0, 0, w, h, 0);
772                   EMapWindow(m->items[i]->icon_win);
773                   if (h > maxh)
774                      maxh = h;
775                   if (w > maxx2)
776                      maxx2 = w;
777                   EImageFree(im);
778                }
779              else
780                 m->items[i]->icon_iclass = NULL;
781           }
782      }
783
784    pad = ImageclassGetPadding(m->style->bg_iclass);
785    pad_item = ImageclassGetPadding(m->style->item_iclass);
786    pad_sub = ImageclassGetPadding(m->style->sub_iclass);
787    if (((has_i) && (has_s)) || ((!has_i) && (!has_s)))
788      {
789         if (pad_item->top > pad_sub->top)
790            maxh += pad_item->top;
791         else
792            maxh += pad_sub->top;
793         if (pad_item->bottom > pad_sub->bottom)
794            maxh += pad_item->bottom;
795         else
796            maxh += pad_sub->bottom;
797         maxw = maxx1 + maxx2;
798         if (pad_item->left > pad_sub->left)
799            maxw += pad_item->left;
800         else
801            maxw += pad_sub->left;
802         if (pad_item->right > pad_sub->right)
803            maxw += pad_item->right;
804         else
805            maxw += pad_sub->right;
806      }
807    else if (has_i)
808      {
809         maxh += pad_item->top;
810         maxh += pad_item->bottom;
811         maxw = maxx1 + maxx2;
812         maxw += pad_item->left;
813         maxw += pad_item->right;
814      }
815    else if (has_s)
816      {
817         maxh += pad_sub->top;
818         maxh += pad_sub->bottom;
819         maxw = maxx1 + maxx2;
820         maxw += pad_sub->left;
821         maxw += pad_sub->right;
822      }
823
824    mmw = 0;
825    mmh = 0;
826
827    nmaxy = 3 * WinGetH(VROOT) / (4 * maxh + 1);
828    if (m->style->maxy && nmaxy > m->style->maxy)
829       nmaxy = m->style->maxy;
830
831    r = 0;
832    x = 0;
833    y = 0;
834    for (i = 0; i < m->num; i++)
835      {
836         if (r == 0 && (m->style->bg_iclass) && (!m->style->use_item_bg))
837           {
838              x += pad->left;
839              y += pad->top;
840           }
841         EMoveResizeWindow(m->items[i]->win, x, y, maxw, maxh);
842         if (m->style->iconpos == ICON_LEFT)
843           {
844              m->items[i]->text_x = pad_item->left + maxx2;
845              m->items[i]->text_w = maxx1;
846              m->items[i]->text_y = (maxh - m->items[i]->text_h) / 2;
847              if (m->items[i]->icon_win)
848                 EMoveWindow(m->items[i]->icon_win,
849                             pad_item->left +
850                             ((maxx2 - m->items[i]->icon_w) / 2),
851                             ((maxh - m->items[i]->icon_h) / 2));
852           }
853         else
854           {
855              m->items[i]->text_x = pad_item->left;
856              m->items[i]->text_w = maxx1;
857              m->items[i]->text_y = (maxh - m->items[i]->text_h) / 2;
858              if (m->items[i]->icon_win)
859                 EMoveWindow(m->items[i]->icon_win,
860                             maxw - pad_item->right - maxx2 +
861                             ((maxx2 - m->items[i]->icon_w) / 2),
862                             ((maxh - m->items[i]->icon_h) / 2));
863           }
864         if (m->items[i]->icon_iclass && Conf.menus.show_icons)
865           {
866              ImageclassApply(m->items[i]->icon_iclass, m->items[i]->icon_win,
867                              0, 0, STATE_NORMAL, ST_MENU_ITEM);
868           }
869         if (x + maxw > mmw)
870            mmw = x + maxw;
871         if (y + maxh > mmh)
872            mmh = y + maxh;
873         if ((m->style->maxx) || (nmaxy))
874           {
875              if (nmaxy)
876                {
877                   y += maxh;
878                   r++;
879                   if (r >= nmaxy)
880                     {
881                        r = 0;
882                        x += maxw;
883                        y = 0;
884                     }
885                }
886              else
887                {
888                   x += maxw;
889                   r++;
890                   if (r >= m->style->maxx)
891                     {
892                        r = 0;
893                        y += maxh;
894                        x = 0;
895                     }
896                }
897           }
898         else
899            y += maxh;
900      }
901
902    if ((m->style->bg_iclass) && (!m->style->use_item_bg))
903      {
904         mmw += pad->right;
905         mmh += pad->bottom;
906      }
907
908    m->redraw = 1;
909    m->w = mmw;
910    m->h = mmh;
911    EResizeWindow(m->win, mmw, mmh);
912 }
913
914 static void
915 MenuRedraw(Menu * m)
916 {
917    int                 i, j;
918
919    if (m->redraw)
920      {
921         for (i = 0; i < m->num; i++)
922           {
923              for (j = 0; j < 3; j++)
924                 FreePmapMask(&(m->items[i]->pmm[j]));
925
926           }
927         m->redraw = 0;
928      }
929
930    if (!m->style->use_item_bg)
931      {
932         FreePmapMask(&m->pmm);
933         ImageclassApplyCopy(m->style->bg_iclass, m->win, m->w, m->h, 0,
934                             0, STATE_NORMAL, &m->pmm, IC_FLAG_MAKE_MASK,
935                             ST_MENU);
936         EGetWindowBackgroundPixmap(m->win);
937         EXCopyAreaTiled(m->pmm.pmap, None, WinGetPmap(m->win),
938                         0, 0, m->w, m->h, 0, 0);
939         EShapeSetMask(m->win, 0, 0, m->pmm.mask);
940         EClearWindow(m->win);
941         for (i = 0; i < m->num; i++)
942            MenuDrawItem(m, m->items[i], 0, -1);
943      }
944    else
945      {
946         for (i = 0; i < m->num; i++)
947            MenuDrawItem(m, m->items[i], 0, -1);
948         EShapePropagate(m->win);
949      }
950
951    m->filled = 1;
952 }
953
954 static void
955 MenuDrawItem(Menu * m, MenuItem * mi, char shape, int state)
956 {
957    PmapMask           *mi_pmm;
958
959    if (state >= 0)
960       mi->state = state;
961    mi_pmm = &(mi->pmm[(int)(mi->state)]);
962
963    if (!mi_pmm->pmap)
964      {
965         int                 x, y, w, h;
966         int                 item_type;
967         ImageClass         *ic;
968         GC                  gc;
969         PmapMask            pmm;
970
971         EGetGeometry(mi->win, NULL, &x, &y, &w, &h, NULL, NULL);
972
973         mi_pmm->type = 0;
974         mi_pmm->pmap = ECreatePixmap(mi->win, w, h, 0);
975         mi_pmm->mask = None;
976
977         ic = (mi->child) ? m->style->sub_iclass : m->style->item_iclass;
978         item_type = (mi->state != STATE_NORMAL) ? ST_MENU_ITEM : ST_MENU;
979
980         if (!m->style->use_item_bg)
981           {
982              gc = EXCreateGC(m->pmm.pmap, 0, NULL);
983              XCopyArea(disp, WinGetPmap(m->win), mi_pmm->pmap, gc, x, y, w, h,
984                        0, 0);
985              if ((mi->state != STATE_NORMAL) || (mi->child))
986                {
987                   ImageclassApplyCopy(ic, mi->win, w, h, 0, 0, mi->state, &pmm,
988                                       IC_FLAG_MAKE_MASK, item_type);
989                   EXCopyAreaTiled(pmm.pmap, pmm.mask, mi_pmm->pmap,
990                                   0, 0, w, h, 0, 0);
991                   FreePmapMask(&pmm);
992                }
993              EXFreeGC(gc);
994           }
995         else
996           {
997              ImageclassApplyCopy(ic, mi->win, w, h, 0, 0, mi->state, &pmm,
998                                  IC_FLAG_MAKE_MASK, item_type);
999              EXCopyAreaTiled(pmm.pmap, pmm.mask, mi_pmm->pmap,
1000                              0, 0, w, h, 0, 0);
1001              FreePmapMask(&pmm);
1002           }
1003
1004         if (mi->text)
1005           {
1006              TextDraw(m->style->tclass, mi->win, mi_pmm->pmap, 0, 0, mi->state,
1007                       _(mi->text), mi->text_x, mi->text_y, mi->text_w,
1008                       mi->text_h, 17,
1009                       TextclassGetJustification(m->style->tclass));
1010           }
1011      }
1012
1013    ESetWindowBackgroundPixmap(mi->win, mi_pmm->pmap);
1014    EShapeSetMask(mi->win, 0, 0, mi_pmm->mask);
1015    EClearWindow(mi->win);
1016
1017    if ((shape) && (m->style->use_item_bg))
1018       EShapePropagate(m->win);
1019 }
1020
1021 static void
1022 MenuShowMasker(Menu * m __UNUSED__)
1023 {
1024    EObj               *eo = Mode_menus.cover_win;
1025
1026    if (!eo)
1027      {
1028         eo = EobjWindowCreate(EOBJ_TYPE_EVENT,
1029                               0, 0, WinGetW(VROOT), WinGetH(VROOT),
1030                               0, "Masker");
1031         if (!eo)
1032            return;
1033
1034         EobjReparent(eo, EoObj(DesksGetCurrent()), 0, 0);
1035         EobjSetLayer(eo, 11);
1036         ESelectInput(EobjGetWin(eo), ButtonPressMask | ButtonReleaseMask |
1037                      EnterWindowMask | LeaveWindowMask);
1038         EventCallbackRegister(EobjGetWin(eo), 0, MenuMaskerHandleEvents, NULL);
1039
1040         Mode_menus.cover_win = eo;
1041      }
1042
1043    EobjMap(eo, 0);
1044 }
1045
1046 static void
1047 MenuHideMasker(void)
1048 {
1049    EObj               *eo = Mode_menus.cover_win;
1050
1051    if (!eo)
1052       return;
1053
1054    EventCallbackUnregister(EobjGetWin(eo), 0, MenuMaskerHandleEvents, NULL);
1055    EobjWindowDestroy(eo);
1056    Mode_menus.cover_win = NULL;
1057 }
1058
1059 static void
1060 MenusDestroyLoaded(void)
1061 {
1062    Menu               *m;
1063    int                 found_one;
1064
1065    MenusHide();
1066
1067    /* Free all menustyles first (gulp) */
1068    do
1069      {
1070         found_one = 0;
1071         ECORE_LIST_FOR_EACH(menu_list, m)
1072         {
1073            if (m->internal)
1074               continue;
1075            if (m->ref_count)
1076               continue;
1077
1078            MenuDestroy(m);
1079            /* Destroying a menu may result in sub-menus being
1080             * destroyed too, so we have to re-find all menus
1081             * afterwards. Inefficient yes, but it works...
1082             */
1083            found_one = 1;
1084            break;
1085         }
1086      }
1087    while (found_one);
1088 }
1089
1090 static int
1091 _MenuMatchName(const void *data, const void *match)
1092 {
1093    const Menu         *m = (const Menu *)data;
1094
1095    if ((m->name && !strcmp((const char *)match, m->name)) ||
1096        (m->alias && !strcmp((const char *)match, m->alias)))
1097       return 0;
1098
1099    return 1;
1100 }
1101
1102 Menu               *
1103 MenuFind(const char *name, const char *param)
1104 {
1105    Menu               *m;
1106
1107    m = (Menu *) ecore_list_find(menu_list, _MenuMatchName, name);
1108    if (m)
1109       return (m);
1110
1111    /* Not in list - try if we can load internal */
1112    m = MenusCreateInternal(NULL, name, NULL, param);
1113
1114    return m;
1115 }
1116
1117 /*
1118  * Aliases for "well-known" menus for backward compatibility.
1119  */
1120 static const char  *const menu_aliases[] = {
1121    "APPS_SUBMENU", "file.menu",
1122    "CONFIG_SUBMENU", "settings.menu",
1123    "DESKTOP_SUBMENU", "desktop.menu",
1124    "MAINT_SUBMENU", "maintenance.menu",
1125    "ROOT_2", "enlightenment.menu",
1126    "WINOPS_MENU", "winops.menu",
1127 };
1128 #define N_MENU_ALIASES (sizeof(menu_aliases)/sizeof(char*)/2)
1129
1130 static const char  *
1131 _MenuCheckAlias(const char *name)
1132 {
1133    unsigned int        i;
1134
1135    for (i = 0; i < N_MENU_ALIASES; i++)
1136       if (!strcmp(name, menu_aliases[2 * i]))
1137          return menu_aliases[2 * i + 1];
1138
1139    return NULL;
1140 }
1141
1142 static void
1143 MenusShowNamed(const char *name, const char *param)
1144 {
1145    Menu               *m;
1146    const char         *name2;
1147
1148    name2 = _MenuCheckAlias(name);
1149    if (name2)
1150       name = name2;
1151
1152    /* Hide any menus currently up */
1153    if (MenusActive())
1154       MenusHide();
1155
1156    m = MenuFind(name, param);
1157    if (!m)
1158       return;
1159
1160    if (!m->ewin)                /* Don't show if already shown */
1161       MenuShow(m, 0);
1162 }
1163
1164 int
1165 MenusActive(void)
1166 {
1167    return Mode_menus.first != NULL;
1168 }
1169
1170 static void
1171 MenusHide(void)
1172 {
1173    TIMER_DEL(menu_timer_submenu);
1174
1175    MenuHide(Mode_menus.first);
1176    Mode_menus.first = NULL;
1177    MenuHideMasker();
1178    TooltipsEnable(1);
1179 }
1180
1181 static void
1182 MenusTouch(void)
1183 {
1184    Menu               *m;
1185
1186    ECORE_LIST_FOR_EACH(menu_list, m) m->redraw = 1;
1187 }
1188
1189 /*
1190  * Menu event handlers
1191  */
1192
1193 static MenuItem    *
1194 MenuFindNextItem(Menu * m, MenuItem * mi, int inc)
1195 {
1196    int                 i;
1197
1198    if (mi == NULL)
1199      {
1200         if (m->num > 0)
1201            return m->items[0];
1202         else
1203            return NULL;
1204      }
1205
1206    for (i = 0; i < m->num; i++)
1207       if (m->items[i] == mi)
1208         {
1209            i = (i + inc + m->num) % m->num;
1210            return m->items[i];
1211         }
1212
1213    return NULL;
1214 }
1215
1216 static              KeySym
1217 MenuKeyPressConversion(KeySym key)
1218 {
1219    if (key == Conf.menus.key.left)
1220       return XK_Left;
1221    if (key == Conf.menus.key.right)
1222       return XK_Right;
1223    if (key == Conf.menus.key.up)
1224       return XK_Up;
1225    if (key == Conf.menus.key.down)
1226       return XK_Down;
1227    if (key == Conf.menus.key.escape)
1228       return XK_Escape;
1229    if (key == Conf.menus.key.ret)
1230       return XK_Return;
1231
1232    /* The key does not correspond to any set, use the default behavior 
1233     * associated to the key */
1234    return key;
1235 }
1236
1237 static void
1238 MenuEventKeyPress(Menu * m, XEvent * ev)
1239 {
1240    KeySym              key;
1241    MenuItem           *mi;
1242    EWin               *ewin;
1243
1244    mi = NULL;
1245    if (Mode_menus.active)
1246      {
1247         m = Mode_menus.active;
1248         mi = m->sel_item;
1249      }
1250
1251    /* NB! m != NULL */
1252
1253    key = XLookupKeysym(&ev->xkey, 0);
1254    switch (MenuKeyPressConversion(key))
1255      {
1256      case XK_Escape:
1257         MenusHide();
1258         break;
1259
1260      case XK_Down:
1261       check_next:
1262         mi = MenuFindNextItem(m, mi, 1);
1263         goto check_activate;
1264
1265      case XK_Up:
1266         mi = MenuFindNextItem(m, mi, -1);
1267         goto check_activate;
1268
1269      case XK_Left:
1270         m = m->parent;
1271         if (!m)
1272            break;
1273         mi = m->sel_item;
1274         goto check_activate;
1275
1276      case XK_Right:
1277         if (mi == NULL)
1278            goto check_next;
1279         m = mi->child;
1280         if (!m || m->num <= 0)
1281            break;
1282         ewin = m->ewin;
1283         if (ewin == NULL || !EwinIsMapped(ewin))
1284            break;
1285         mi = MenuFindItemByChild(m, m->child);
1286         goto check_activate;
1287
1288       check_activate:
1289         if (!mi)
1290            break;
1291         MenuActivateItem(m, mi);
1292         break;
1293
1294      case XK_Return:
1295         if (!mi)
1296            break;
1297         if (!mi->params)
1298            break;
1299         EFuncDefer(Mode_menus.context_ewin, mi->params);
1300         MenusHide();
1301         EobjsRepaint();
1302         break;
1303      }
1304 }
1305
1306 static void
1307 MenuItemEventMouseDown(MenuItem * mi, XEvent * ev __UNUSED__)
1308 {
1309    Menu               *m;
1310
1311    Mode_menus.just_shown = 0;
1312
1313    m = mi->menu;
1314    MenuDrawItem(m, mi, 1, STATE_CLICKED);
1315 }
1316
1317 static void
1318 MenuItemEventMouseUp(MenuItem * mi, XEvent * ev __UNUSED__)
1319 {
1320    Menu               *m;
1321
1322    if (Mode_menus.just_shown)
1323      {
1324         Mode_menus.just_shown = 0;
1325         if (ev->xbutton.time - Mode.events.last_btime < 250)
1326            return;
1327      }
1328
1329    m = mi->menu;
1330
1331    if ((m) && (mi->state))
1332      {
1333         MenuDrawItem(m, mi, 1, STATE_HILITED);
1334         if ((mi->params) /* && (!Mode_menus.just_shown) */ )
1335           {
1336              EFuncDefer(Mode_menus.context_ewin, mi->params);
1337              MenusHide();
1338              EobjsRepaint();
1339           }
1340      }
1341 }
1342
1343 #if 0                           /* Was in HandleMotion() */
1344 void
1345 MenusHandleMotion(void)
1346 {
1347 #define SCROLL_RATIO 2/3
1348    if ((MenusActive() || (Mode_menus.clicked)))
1349      {
1350         int                 i, offx = 0, offy = 0, xdist = 0, ydist = 0;
1351         EWin               *ewin;
1352         EWin               *menus[256];
1353         int                 fx[256];
1354         int                 fy[256];
1355         int                 tx[256];
1356         int                 ty[256];
1357         static int          menu_scroll_dist = 4;
1358         int                 my_width, my_height, x_org, y_org, head_num = 0;
1359
1360         head_num =
1361            ScreenGetGeometry(Mode.events.mx, Mode.events.my, &x_org, &y_org,
1362                              &my_width, &my_height);
1363
1364         if (Mode.events.mx > ((x_org + my_width) - (menu_scroll_dist + 1)))
1365           {
1366              xdist =
1367                 -(menu_scroll_dist + (Mode.events.mx - (x_org + my_width)));
1368           }
1369         else if (Mode.events.mx < (menu_scroll_dist + x_org))
1370           {
1371              xdist = x_org + menu_scroll_dist - (Mode.events.mx);
1372           }
1373
1374         if (Mode.events.my > (WinGetH(VROOT) - (menu_scroll_dist + 1)))
1375           {
1376              ydist =
1377                 -(menu_scroll_dist + (Mode.events.my - (y_org + my_height)));
1378           }
1379         else if (Mode.events.my < (menu_scroll_dist + y_org))
1380           {
1381              ydist = y_org + menu_scroll_dist - (Mode.events.my);
1382           }
1383
1384         /* That's a hack to avoid unwanted events:
1385          * If the user entered the border area, he has to
1386          * leave it first, before he can scroll menus again ...
1387          */
1388         if ((xdist != 0) || (ydist != 0) || Mode.doingslide)
1389           {
1390              /* -10 has no meaning, only makes sure that the if's */
1391              /* above can't be fulfilled ... */
1392              menu_scroll_dist = -10;
1393           }
1394         else
1395           {
1396              menu_scroll_dist = 13;
1397           }
1398
1399         if (Mode_menus.current_depth > 0)
1400           {
1401              int                 x1, y1, x2, y2;
1402
1403              x1 = x_org + my_width;
1404              x2 = x_org - 1;
1405              y1 = y_org + my_height;
1406              y2 = y_org - 1;
1407              /* work out the minimum and maximum extents of our */
1408              /* currently active menus */
1409              for (i = 0; i < Mode_menus.current_depth; i++)
1410                {
1411                   if (Mode_menus.list[i])
1412                     {
1413                        ewin = Mode_menus.list[i]->ewin;
1414                        if (ewin)
1415                          {
1416                             if (EoGetX(ewin) < x1)
1417                                x1 = EoGetX(ewin);
1418                             if (EoGetY(ewin) < y1)
1419                                y1 = EoGetY(ewin);
1420                             if ((EoGetX(ewin) + EoGetW(ewin) - 1) > x2)
1421                                x2 = EoGetX(ewin) + EoGetW(ewin) - 1;
1422                             if ((EoGetY(ewin) + EoGetH(ewin) - 1) > y2)
1423                                y2 = EoGetY(ewin) + EoGetH(ewin) - 1;
1424                          }
1425                     }
1426                }
1427
1428              if (xdist < 0)
1429                {
1430                   offx = (x_org + my_width) - x2;
1431                }
1432              else if (xdist > 0)
1433                {
1434                   offx = x_org - x1;
1435                }
1436              if (ydist < 0)
1437                {
1438                   offy = (y_org + my_height) - y2;
1439                }
1440              else if (ydist > 0)
1441                {
1442                   offy = y_org - y1;
1443                }
1444
1445              if ((xdist < 0) && (offx <= 0))
1446                 xdist = offx;
1447              if ((xdist > 0) && (offx >= 0))
1448                 xdist = offx;
1449              if ((ydist < 0) && (offy <= 0))
1450                 ydist = offy;
1451              if ((ydist > 0) && (offy >= 0))
1452                 ydist = offy;
1453
1454              /* only if any active menus are partially off screen then scroll */
1455              if ((((xdist > 0) && (x1 < x_org))
1456                   || ((xdist < 0) && (x2 >= (x_org + my_width))))
1457                  || (((ydist > 0) && (y1 < y_org))
1458                      || ((ydist < 0) && (y2 >= (y_org + my_height)))))
1459                {
1460                   /* If we would scroll too far, limit scrolling to 2/3s of screen */
1461                   if (ydist < -my_width)
1462                      ydist = -my_width * SCROLL_RATIO;
1463                   if (ydist > my_width)
1464                      ydist = my_width * SCROLL_RATIO;
1465
1466                   if (xdist < -my_height)
1467                      xdist = -my_height * SCROLL_RATIO;
1468                   if (xdist > my_height)
1469                      xdist = my_height * SCROLL_RATIO;
1470
1471                   if (Mode_menus.current_depth)
1472                     {
1473 #ifdef USE_XINERAMA
1474                        ewin = Mode_menus.list[0]->ewin;
1475                        if (ewin->head == head_num)
1476                          {
1477 #endif
1478                             for (i = 0; i < Mode_menus.current_depth; i++)
1479                               {
1480                                  menus[i] = NULL;
1481                                  if (Mode_menus.list[i])
1482                                    {
1483                                       ewin = Mode_menus.list[i]->ewin;
1484                                       if (ewin)
1485                                         {
1486                                            menus[i] = ewin;
1487                                            fx[i] = EoGetX(ewin);
1488                                            fy[i] = EoGetY(ewin);
1489                                            tx[i] = EoGetX(ewin) + xdist;
1490                                            ty[i] = EoGetY(ewin) + ydist;
1491                                         }
1492                                    }
1493                               }
1494                             SlideEwinsTo(menus, fx, fy, tx, ty,
1495                                          Mode_menus.current_depth,
1496                                          Conf.shadespeed, 0);
1497                             if (((xdist != 0) || (ydist != 0))
1498                                 && (Conf.menus.warp))
1499                                EXWarpPointer(None, xdist, ydist);
1500 #ifdef USE_XINERAMA
1501                          }
1502 #endif
1503                     }
1504                }
1505           }
1506      }
1507 }
1508 #endif
1509
1510 struct _mdata {
1511    Menu               *m;
1512    MenuItem           *mi;
1513 };
1514
1515 static void
1516 MenusSetEvents(int on)
1517 {
1518    int                 i;
1519    Menu               *m;
1520    long                event_mask;
1521
1522    event_mask = (on) ? MENU_ITEM_EVENT_MASK : 0;
1523
1524    for (m = Mode_menus.first; m; m = m->child)
1525      {
1526         for (i = 0; i < m->num; i++)
1527            ESelectInput(m->items[i]->win, event_mask);
1528      }
1529 }
1530
1531 static void
1532 MenuSelectItem(Menu * m, MenuItem * mi, int focus)
1533 {
1534    if (mi && focus)
1535      {
1536         if (Mode_menus.active != m)
1537           {
1538              Mode_menus.active = m;
1539              GrabKeyboardRelease();
1540              GrabKeyboardSet(m->win);
1541           }
1542      }
1543
1544    if (mi == m->sel_item)
1545       return;
1546
1547    if (m->sel_item)
1548       MenuDrawItem(m, m->sel_item, 1, STATE_NORMAL);
1549
1550    if (mi)
1551       MenuDrawItem(m, mi, 1, STATE_HILITED);
1552
1553    m->sel_item = mi;
1554 }
1555
1556 static void
1557 MenuSelectItemByChild(Menu * m, Menu * mc)
1558 {
1559    MenuItem           *mi;
1560
1561    mi = MenuFindItemByChild(m, mc);
1562    if (!mi)
1563       return;
1564
1565    MenuSelectItem(m, mi, 0);
1566 }
1567
1568 static void
1569 _SubmenuCheckSlide(Menu * m, MenuItem * mi, EWin * ewin, EWin * ewin2,
1570                    int xo, int yo, int ww, int hh)
1571 {
1572    EWin               *menus[256], *etmp;
1573    int                 fx[256], fy[256], tx[256], ty[256];
1574    int                 i;
1575    int                 sx, sy, sw, sh;
1576    int                 xdist = 0, ydist = 0;
1577
1578    if (!Conf.menus.onscreen)
1579       return;
1580
1581    ScreenGetGeometryByHead(Mode_menus.first->ewin->head, &sx, &sy, &sw, &sh);
1582
1583    if (EoGetX(Mode_menus.first->ewin) < sx)
1584       xdist = sx - EoGetX(Mode_menus.first->ewin);
1585    if (xdist + EoGetX(ewin) + xo + ww > sx + sw)
1586       xdist = sx + sw - (EoGetX(ewin) + xo + ww);
1587    if (EoGetX(ewin) + xdist < sx)
1588       xdist = sx - EoGetX(ewin);
1589
1590    if (EoGetY(Mode_menus.first->ewin) < sy)
1591       ydist = sy - EoGetY(Mode_menus.first->ewin);
1592    if (ydist + EoGetY(ewin) + yo + hh > sy + sh)
1593       ydist = sy + sh - (EoGetY(ewin) + yo + hh);
1594    if (EoGetY(ewin) + ydist < sy)
1595       ydist = sy - EoGetY(ewin);
1596
1597    if (xdist == 0 && ydist == 0)
1598       return;
1599
1600    i = 0;
1601    for (m = Mode_menus.first; m; m = m->child)
1602      {
1603         etmp = m->ewin;
1604         if (!etmp || etmp == ewin2)
1605            break;
1606         menus[i] = etmp;
1607         fx[i] = EoGetX(etmp);
1608         fy[i] = EoGetY(etmp);
1609         tx[i] = EoGetX(etmp) + xdist;
1610         ty[i] = EoGetY(etmp) + ydist;
1611         i++;
1612      }
1613
1614    MenusSetEvents(0);           /* Disable menu item events while sliding */
1615    Mode.move.check = 0;         /* Bypass on-screen checks */
1616    SlideEwinsTo(menus, fx, fy, tx, ty, i, Conf.shading.speed, 0);
1617    Mode.move.check = 1;
1618    MenusSetEvents(1);
1619
1620    if (Conf.menus.warp)
1621       EXWarpPointer(WinGetXwin(mi->win), mi->text_w / 2, mi->text_h / 2);
1622 }
1623
1624 static int
1625 SubmenuShowTimeout(void *dat)
1626 {
1627    int                 mx, my, my2, xo, yo, mw, ww, hh;
1628    Menu               *m;
1629    MenuItem           *mi;
1630    EWin               *ewin2, *ewin;
1631    struct _mdata      *data;
1632    int                 bl1, br1, bt1, bb1;
1633    int                 bl2, br2, bt2, bb2;
1634
1635    data = (struct _mdata *)dat;
1636    if (!data || !data->m)
1637       goto done;
1638
1639    m = data->m;
1640    if (!ecore_list_goto(menu_list, m))
1641       goto done;
1642    ewin = m->ewin;
1643    if (!ewin || !EwinFindByPtr(ewin))
1644       goto done;
1645    if (!EoIsShown(ewin))
1646       goto done;
1647
1648    mi = data->mi;
1649    if (!mi)
1650       goto done;
1651
1652    if (mi->child != m->child)
1653       MenuHide(m->child);
1654    m->child = mi->child;
1655    if (!mi->child)
1656      {
1657         _SubmenuCheckSlide(m, mi, ewin, NULL, 0, 0, 0, 0);
1658         goto done;
1659      }
1660
1661    mi->child->parent = m;
1662    MenuShow(mi->child, 1);
1663    ewin2 = mi->child->ewin;
1664    if (!ewin2 || !EwinFindByPtr(ewin2))
1665       goto done;
1666
1667    EGetGeometry(mi->win, NULL, &mx, &my, &mw, NULL, NULL, NULL);
1668    my2 = 0;
1669    if (mi->child->num > 0 && mi->child->items[0])
1670       EGetGeometry(mi->child->items[0]->win, NULL, NULL, &my2, NULL, NULL, NULL,
1671                    NULL);
1672
1673    /* Sub-menu offsets relative to parent menu origin */
1674    EwinBorderGetSize(ewin, &bl1, &br1, &bt1, &bb1);
1675    EwinBorderGetSize(ewin2, &bl2, &br2, &bt2, &bb2);
1676    xo = bl1 + mx + mw;
1677    yo = bt1 + my - (bt2 + my2);
1678
1679    /* Size of new submenu (may be shaded atm.) */
1680    ww = mi->child->w + bl2 + br2;
1681    hh = mi->child->h + bt2 + bb2;
1682
1683    _SubmenuCheckSlide(m, mi, ewin, ewin2, xo, yo, ww, hh);
1684
1685    Mode.move.check = 0;         /* Bypass on-screen checks */
1686    EwinMove(ewin2, EoGetX(ewin) + xo, EoGetY(ewin) + yo);
1687    Mode.move.check = 1;
1688    EwinOpFloatAt(ewin2, OPSRC_NA, EoGetX(ewin2), EoGetY(ewin2));
1689    EwinRaise(ewin2);
1690    EwinShow(ewin2);
1691
1692    if (Conf.menus.animate)
1693       EwinUnShade(ewin2);
1694
1695  done:
1696    menu_timer_submenu = NULL;
1697    return 0;
1698 }
1699
1700 static void
1701 MenuActivateItem(Menu * m, MenuItem * mi)
1702 {
1703    static struct _mdata mdata;
1704    MenuItem           *mi_prev;
1705
1706    mi_prev = m->sel_item;
1707
1708    if (m->child)
1709       MenuSelectItem(m->child, NULL, 0);
1710    MenuSelectItem(m, mi, 1);
1711    if (m->parent)
1712       MenuSelectItemByChild(m->parent, m);
1713
1714    if (mi == mi_prev)
1715       return;
1716
1717    if (mi && !mi->child && mi_prev && !mi_prev->child)
1718       return;
1719
1720    TIMER_DEL(menu_timer_submenu);
1721
1722    if ((mi && mi->child && !mi->child->shown) || (mi && mi->child != m->child))
1723      {
1724         mdata.m = m;
1725         mdata.mi = mi;
1726         TIMER_ADD(menu_timer_submenu, 0.2, SubmenuShowTimeout, &mdata);
1727      }
1728 }
1729
1730 static void
1731 MenuItemEventMouseIn(MenuItem * mi, XEvent * ev)
1732 {
1733    if (ev->xcrossing.detail == NotifyInferior)
1734       return;
1735
1736    MenuActivateItem(mi->menu, mi);
1737 }
1738
1739 static void
1740 MenuItemEventMouseOut(MenuItem * mi, XEvent * ev)
1741 {
1742    if (ev->xcrossing.detail == NotifyInferior)
1743       return;
1744
1745    if (!mi->child)
1746       MenuSelectItem(mi->menu, NULL, 0);
1747 }
1748
1749 static void
1750 MenuHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
1751 {
1752    Menu               *m = (Menu *) prm;
1753
1754 #if DEBUG_MENU_EVENTS
1755    Eprintf("MenuHandleEvents %d\n", ev->type);
1756 #endif
1757    switch (ev->type)
1758      {
1759      case KeyPress:
1760         MenuEventKeyPress(m, ev);
1761         break;
1762      case ButtonRelease:
1763         break;
1764      case EnterNotify:
1765         GrabKeyboardSet(m->win);
1766         break;
1767      }
1768 }
1769
1770 static void
1771 MenuItemHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
1772 {
1773    MenuItem           *mi = (MenuItem *) prm;
1774
1775 #if DEBUG_MENU_EVENTS
1776    Eprintf("MenuItemHandleEvents %d\n", ev->type);
1777 #endif
1778    switch (ev->type)
1779      {
1780      case ButtonPress:
1781         MenuItemEventMouseDown(mi, ev);
1782         break;
1783      case ButtonRelease:
1784         MenuItemEventMouseUp(mi, ev);
1785         break;
1786      case EnterNotify:
1787         MenuItemEventMouseIn(mi, ev);
1788         break;
1789      case LeaveNotify:
1790         MenuItemEventMouseOut(mi, ev);
1791         break;
1792      }
1793 }
1794
1795 static void
1796 MenuMaskerHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
1797 {
1798 #if DEBUG_MENU_EVENTS
1799    Eprintf("MenuMaskerHandleEvents %d\n", ev->type);
1800 #endif
1801    switch (ev->type)
1802      {
1803      case ButtonRelease:
1804         MenusHide();
1805         break;
1806      }
1807 }
1808
1809 /*
1810  * Configuration load/save
1811  */
1812 #include "conf.h"
1813
1814 int
1815 MenuStyleConfigLoad(FILE * fs)
1816 {
1817    int                 err = 0;
1818    char                s[FILEPATH_LEN_MAX];
1819    char                s2[FILEPATH_LEN_MAX];
1820    int                 i1;
1821    MenuStyle          *ms = NULL;
1822
1823    while (GetLine(s, sizeof(s), fs))
1824      {
1825         i1 = ConfigParseline1(s, s2, NULL, NULL);
1826         switch (i1)
1827           {
1828           case CONFIG_CLOSE:
1829              goto done;
1830           case CONFIG_CLASSNAME:
1831              ms = MenuStyleCreate(s2);
1832              break;
1833           case CONFIG_TEXT:
1834              ms->tclass = TextclassAlloc(s2, 1);
1835              break;
1836           case MENU_BG_ICLASS:
1837              ms->bg_iclass = ImageclassAlloc(s2, 1);
1838              break;
1839           case MENU_ITEM_ICLASS:
1840              ms->item_iclass = ImageclassAlloc(s2, 1);
1841              break;
1842           case MENU_SUBMENU_ICLASS:
1843              ms->sub_iclass = ImageclassAlloc(s2, 1);
1844              break;
1845           case MENU_USE_ITEM_BACKGROUND:
1846              ms->use_item_bg = atoi(s2);
1847              if (ms->use_item_bg)
1848                {
1849                   if (ms->bg_iclass)
1850                     {
1851                        ImageclassFree(ms->bg_iclass);
1852                        ms->bg_iclass = NULL;
1853                     }
1854                }
1855              break;
1856           case MENU_MAX_COLUMNS:
1857              ms->maxx = atoi(s2);
1858              break;
1859           case MENU_MAX_ROWS:
1860              ms->maxy = atoi(s2);
1861              break;
1862           case CONFIG_BORDER:
1863              _EFDUP(ms->border_name, s2);
1864              break;
1865           default:
1866              break;
1867           }
1868      }
1869    err = -1;
1870
1871  done:
1872    return err;
1873 }
1874
1875 int
1876 MenuConfigLoad(FILE * fs)
1877 {
1878    int                 err = 0;
1879    char                s[FILEPATH_LEN_MAX];
1880    char                s2[FILEPATH_LEN_MAX];
1881    char                s3[FILEPATH_LEN_MAX];
1882    char                s4[FILEPATH_LEN_MAX];
1883    char                s5[FILEPATH_LEN_MAX];
1884    char               *p2, *p3;
1885    char               *txt = NULL;
1886    const char         *params;
1887    int                 i1, i2, len;
1888    Menu               *m = NULL, *mm;
1889    MenuItem           *mi;
1890    ImageClass         *ic = NULL;
1891
1892    while (GetLine(s, sizeof(s), fs))
1893      {
1894         i1 = ConfigParseline1(s, s2, &p2, &p3);
1895         switch (i1)
1896           {
1897           default:
1898              break;
1899
1900           case CONFIG_VERSION:
1901              continue;
1902
1903           case CONFIG_MENU:
1904              err = -1;
1905              i2 = atoi(s2);
1906              if (i2 != CONFIG_OPEN)
1907                 goto done;
1908              m = NULL;
1909              ic = NULL;
1910              _EFREE(txt);
1911              continue;
1912           case CONFIG_CLOSE:
1913              err = 0;
1914              continue;
1915
1916           case MENU_PREBUILT:
1917              m = MenuFind(s2, NULL);
1918              if (m)
1919                 continue;
1920              sscanf(p3, "%4000s %4000s %4000s", s3, s4, s5);
1921              m = MenusCreateInternal(s4, s2, s3, s5);
1922              if (m)
1923                 m->ref_count++;
1924              continue;
1925
1926           case CONFIG_CLASSNAME:
1927              if (!m)
1928                 m = MenuCreate(s2, NULL, NULL, NULL);
1929              else
1930                 MenuSetName(m, s2);
1931              params = _MenuCheckAlias(s2);
1932              if (m && params)
1933                 MenuSetAlias(m, params);
1934              continue;
1935
1936           case MENU_ITEM:
1937              ic = NULL;
1938              if (strcmp("NULL", s2))
1939                 ic = ImageclassFind(s2, 0);
1940              _EFDUP(txt, p3);
1941              continue;
1942           }
1943
1944         /* The rest require the menu m to be created */
1945         if (!m)
1946           {
1947              ConfigParseError("Menu", s);
1948              continue;
1949           }
1950
1951         switch (i1)
1952           {
1953           case MENU_USE_STYLE:
1954              MenuSetStyle(m, MenuStyleFind(s2));
1955              break;
1956           case MENU_TITLE:
1957              MenuSetTitle(m, p2);
1958              break;
1959           case MENU_ACTION:
1960              if ((txt) || (ic))
1961                {
1962                   char                ok = 1;
1963
1964                   /* if its an execute line then check to see if the exec is 
1965                    * on your system before adding the menu entry */
1966                   if (!strcmp(s2, "exec"))
1967                     {
1968                        sscanf(p3, "%1000s", s3);
1969                        ok = path_canexec(s3);
1970                     }
1971                   if (ok)
1972                     {
1973                        mi = MenuItemCreate(txt, ic, p2, NULL);
1974                        MenuAddItem(m, mi);
1975                     }
1976                   ic = NULL;
1977                   _EFREE(txt);
1978                }
1979              break;
1980           case MENU_SUBMENU:
1981              len = 0;
1982              sscanf(p3, "%s %n", s3, &len);
1983              ic = NULL;
1984              if (strcmp("NULL", s3))
1985                 ic = ImageclassFind(s3, 1);
1986              mm = MenuFind(s2, NULL);
1987              mi = MenuItemCreate(p3 + len, ic, NULL, mm);
1988              MenuAddItem(m, mi);
1989              break;
1990           default:
1991              break;
1992           }
1993      }
1994
1995  done:
1996    if (err)
1997       ConfigAlertLoad("Menu");
1998    _EFREE(txt);
1999
2000    return err;
2001 }
2002
2003 static int
2004 MenusTimeout(void *data __UNUSED__)
2005 {
2006    Menu               *m;
2007    time_t              ts;
2008
2009    /* Unload contents if loadable and no access in > 5 min */
2010    ts = time(0);
2011    ECORE_LIST_FOR_EACH(menu_list, m)
2012    {
2013       if (m->shown || !m->filled ||
2014           ts - m->last_access < MENU_UNLOAD_CHECK_INTERVAL)
2015          continue;
2016
2017       m->last_change = 0;
2018       if (!m->loader)
2019          MenuFreePixmaps(m);
2020       else if (m->ref_count)
2021          MenuEmpty(m, 0);
2022       else
2023          MenuDestroy(m);
2024    }
2025
2026    return 1;
2027 }
2028
2029 /*
2030  * Menus Module
2031  */
2032
2033 static void
2034 MenusSighan(int sig, void *prm __UNUSED__)
2035 {
2036    Timer              *menu_unload_timer;
2037
2038    switch (sig)
2039      {
2040      case ESIGNAL_CONFIGURE:
2041         ConfigFileLoad("menus.cfg", NULL, MenuConfigLoad, 1);
2042         break;
2043
2044      case ESIGNAL_START:
2045         TIMER_ADD(menu_unload_timer, 1. * MENU_UNLOAD_CHECK_INTERVAL,
2046                   MenusTimeout, NULL);
2047         break;
2048
2049      case ESIGNAL_AREA_SWITCH_START:
2050      case ESIGNAL_DESK_SWITCH_START:
2051         MenusHide();
2052         break;
2053
2054      case ESIGNAL_EWIN_UNMAP:
2055         if ((EWin *) prm == Mode_menus.context_ewin)
2056            MenusHide();
2057         break;
2058
2059      case ESIGNAL_THEME_TRANS_CHANGE:
2060         MenusTouch();
2061         break;
2062      }
2063 }
2064
2065 #if ENABLE_DIALOGS
2066 /*
2067  * Configuration dialog
2068  */
2069
2070 static char         tmp_warpmenus;
2071 static char         tmp_animated_menus;
2072 static char         tmp_menusonscreen;
2073
2074 static void
2075 CB_ConfigureMenus(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
2076 {
2077    if (val < 2)
2078      {
2079         Conf.menus.warp = tmp_warpmenus;
2080         Conf.menus.animate = tmp_animated_menus;
2081         Conf.menus.onscreen = tmp_menusonscreen;
2082      }
2083    autosave();
2084 }
2085
2086 static void
2087 _DlgFillMenus(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
2088 {
2089    DItem              *di;
2090
2091    tmp_warpmenus = Conf.menus.warp;
2092    tmp_animated_menus = Conf.menus.animate;
2093    tmp_menusonscreen = Conf.menus.onscreen;
2094
2095    DialogItemTableSetOptions(table, 3, 0, 0, 0);
2096
2097    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2098    DialogItemSetColSpan(di, 3);
2099    DialogItemSetText(di, _("Animated display of menus"));
2100    DialogItemCheckButtonSetPtr(di, &tmp_animated_menus);
2101
2102    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2103    DialogItemSetColSpan(di, 3);
2104    DialogItemSetText(di, _("Always pop up menus on screen"));
2105    DialogItemCheckButtonSetPtr(di, &tmp_menusonscreen);
2106
2107    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2108    DialogItemSetColSpan(di, 3);
2109    DialogItemSetText(di, _("Warp pointer after moving menus"));
2110    DialogItemCheckButtonSetPtr(di, &tmp_warpmenus);
2111 }
2112
2113 const DialogDef     DlgMenus = {
2114    "CONFIGURE_MENUS",
2115    N_("Menus"),
2116    N_("Menu Settings"),
2117    SOUND_SETTINGS_MENUS,
2118    "pix/place.png",
2119    N_("Enlightenment Menu\n" "Settings Dialog\n"),
2120    _DlgFillMenus,
2121    DLG_OAC, CB_ConfigureMenus,
2122 };
2123 #endif /* ENABLE_DIALOGS */
2124
2125 static void
2126 MenusIpc(const char *params)
2127 {
2128    const char         *p;
2129    char                cmd[128], prm[4096];
2130    int                 len;
2131    Menu               *m;
2132
2133    cmd[0] = prm[0] = '\0';
2134    p = params;
2135    if (p)
2136      {
2137         len = 0;
2138         sscanf(p, "%100s %4000s %n", cmd, prm, &len);
2139         p += len;
2140      }
2141
2142    if (!p || cmd[0] == '?')
2143      {
2144         IpcPrintf("Menus - active=%d\n", MenusActive());
2145      }
2146    else if (!strncmp(cmd, "list", 2))
2147      {
2148 #define SS(s) ((s) ? (s) : "-")
2149 #define ST(s) ((s) ? (s->name) : "-")
2150         ECORE_LIST_FOR_EACH(menu_list, m)
2151            IpcPrintf("%s(%s/%s): %s\n", m->name, SS(m->alias), ST(m->style),
2152                      SS(m->title));
2153      }
2154    else if (!strncmp(cmd, "reload", 2))
2155      {
2156         MenusDestroyLoaded();
2157         ConfigFileLoad("menus.cfg", NULL, MenuConfigLoad, 1);
2158      }
2159    else if (!strncmp(cmd, "show", 2))
2160      {
2161         if (*p == '\0')
2162            p = NULL;
2163         if (p && !strcmp(prm, "named"))
2164           {
2165              Esnprintf(prm, sizeof(prm), "%s", p);
2166              p = NULL;
2167           }
2168         SoundPlay(SOUND_MENU_SHOW);
2169         MenusShowNamed(prm, p);
2170      }
2171 }
2172
2173 static const IpcItem MenusIpcArray[] = {
2174    {
2175     MenusIpc,
2176     "menus", "mnu",
2177     "Menu functions",
2178     "  menus list               Show existing menus\n"
2179     "  menus reload             Reload menus.cfg without restarting\n"
2180     "  menus show <name>        Show named menu\n"}
2181    ,
2182 };
2183 #define N_IPC_FUNCS (sizeof(MenusIpcArray)/sizeof(IpcItem))
2184
2185 static const CfgItem MenusCfgItems[] = {
2186    CFG_ITEM_BOOL(Conf.menus, animate, 0),
2187    CFG_ITEM_BOOL(Conf.menus, onscreen, 1),
2188    CFG_ITEM_BOOL(Conf.menus, warp, 1),
2189    CFG_ITEM_BOOL(Conf.menus, show_icons, 1),
2190    CFG_ITEM_INT(Conf.menus, icon_size, 16),
2191    CFG_ITEM_HEX(Conf.menus, key.left, XK_Left),
2192    CFG_ITEM_HEX(Conf.menus, key.right, XK_Right),
2193    CFG_ITEM_HEX(Conf.menus, key.up, XK_Up),
2194    CFG_ITEM_HEX(Conf.menus, key.down, XK_Down),
2195    CFG_ITEM_HEX(Conf.menus, key.escape, XK_Escape),
2196    CFG_ITEM_HEX(Conf.menus, key.ret, XK_Return),
2197 };
2198 #define N_CFG_ITEMS (sizeof(MenusCfgItems)/sizeof(CfgItem))
2199
2200 /*
2201  * Module descriptor
2202  */
2203 extern const EModule ModMenus;
2204 const EModule       ModMenus = {
2205    "menus", "menu",
2206    MenusSighan,
2207    {N_IPC_FUNCS, MenusIpcArray},
2208    {N_CFG_ITEMS, MenusCfgItems}
2209 };