2 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3 * Copyright (C) 2004-2009 Kim Woelders
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:
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.
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.
28 #include "e16-ecore_list.h"
45 #include <X11/keysym.h>
47 #define DEBUG_MENU_EVENTS 0
49 #define MENU_UNLOAD_CHECK_INTERVAL 300 /* Seconds */
62 ImageClass *bg_iclass;
63 ImageClass *item_iclass;
64 ImageClass *sub_iclass;
70 unsigned int ref_count;
75 ImageClass *icon_iclass;
104 char internal; /* Don't destroy when reloading */
105 char dynamic; /* May be emptied on close */
109 char filled; /* Has been filled */
116 unsigned int ref_count;
119 #define MENU_ITEM_EVENT_MASK \
120 KeyPressMask | KeyReleaseMask | \
121 ButtonPressMask | ButtonReleaseMask | \
122 EnterWindowMask | LeaveWindowMask
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,
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);
134 static void MenusHide(void);
136 static Ecore_List *menu_list = NULL;
137 static Ecore_List *menu_style_list = NULL;
138 static Timer *menu_timer_submenu = NULL;
141 MenuFindItemByChild(Menu * m, Menu * mc)
146 return (m->num) ? m->items[0] : NULL;
148 for (i = 0; i < m->num; i++)
150 if (mc == m->items[i]->child)
166 MenuDrawItem(m, m->sel_item, 1, STATE_NORMAL);
172 EUnmapWindow(m->win);
173 EReparentWindow(m->win, VROOT, ewin->client.x, ewin->client.y);
181 m->last_access = time(0);
193 _MenuEwinInit(EWin * ewin)
195 Menu *m = (Menu *) ewin->data;
197 EwinSetTitle(ewin, _(m->title));
198 EwinSetClass(ewin, m->name, "Enlightenment_Menu");
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);
208 ICCCM_SetSizeConstraints(ewin, m->w, m->h, m->w, m->h, 0, 0, 1, 1,
210 ewin->icccm.grav = StaticGravity;
212 EoSetLayer(ewin, 12);
213 ewin->ewmh.opacity = OpacityFromPercent(Conf.opacity.menus);
217 _MenuEwinMoveResize(EWin * ewin, int resize __UNUSED__)
219 Menu *m = (Menu *) ewin->data;
224 if (Mode.mode != MODE_NONE && !m->redraw)
227 if (TransparencyUpdateNeeded())
230 if ((!m->style->use_item_bg && m->pmm.pmap == 0) || m->redraw)
233 EwinUpdateShapeInfo(ewin);
238 _MenuEwinClose(EWin * ewin)
240 Menu *m = (Menu *) ewin->data;
242 if (m == Mode_menus.active)
244 GrabKeyboardRelease();
245 Mode_menus.active = NULL;
251 static const EWinOps _MenuEwinOps = {
258 static void MenuShowMasker(Menu * m);
263 if (!m || !m->loader)
270 MenuShow(Menu * m, char noshow)
286 if (!m->win || !m->items[0]->win)
304 EGetGeometry(m->items[0]->win, NULL, &x, &y, &w, &h, NULL, NULL);
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)
315 b = BorderFind(m->style->border_name);
320 head_num = ScreenGetGeometryByPointer(&sx, &sy, &sw, &sh);
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;
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;
334 EMoveWindow(m->win, wx, wy);
336 ewin = AddInternalToFamily(m->win, m->style->border_name, EWIN_TYPE_MENU,
341 ewin->client.event_mask |= KeyPressMask;
342 ESelectInput(m->win, ewin->client.event_mask);
344 ewin->head = head_num;
346 EwinMoveToDesktop(ewin, EoGetDesk(ewin));
347 EwinResize(ewin, ewin->client.w, ewin->client.h);
349 if (Conf.menus.animate)
350 EwinInstantShade(ewin, 0);
355 EwinOpFloatAt(ewin, OPSRC_NA, EoGetX(ewin), EoGetY(ewin));
357 if (Conf.menus.animate)
364 m->last_access = time(0);
365 Mode_menus.just_shown = 1;
367 if (!Mode_menus.first)
369 Mode_menus.context_ewin = GetContextEwin();
371 Eprintf("Mode_menus.context_ewin set %s\n",
372 EwinGetTitle(Mode_menus.context_ewin));
375 Mode_menus.first = m;
378 GrabKeyboardSet(m->win);
384 MenuStyleCreate(const char *name)
388 ms = ECALLOC(MenuStyle, 1);
392 if (!menu_style_list)
393 menu_style_list = ecore_list_new();
394 ecore_list_prepend(menu_style_list, ms);
396 ms->name = Estrdup(name);
397 ms->iconpos = ICON_LEFT;
403 MenuItemCreate(const char *text, ImageClass * iclass,
404 const char *action_params, Menu * child)
408 mi = ECALLOC(MenuItem, 1);
410 mi->icon_iclass = iclass;
413 ImageclassIncRefcount(iclass);
416 mi->text = (text) ? Estrdup((text[0]) ? text : "?!?") : NULL;
417 mi->params = Estrdup(action_params);
421 mi->state = STATE_NORMAL;
427 _MenuStyleMatchName(const void *data, const void *match)
429 return strcmp(((const MenuStyle *)data)->name, (const char *)match);
433 MenuStyleFind(const char *name)
437 ms = (MenuStyle *) ecore_list_find(menu_style_list, _MenuStyleMatchName,
442 ms = (MenuStyle *) ecore_list_find(menu_style_list, _MenuStyleMatchName,
447 ms = MenuStyleCreate("__fb_ms");
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);
460 MenuSetInternal(Menu * m)
466 MenuSetDynamic(Menu * m)
472 MenuSetName(Menu * m, const char *name)
474 _EFDUP(m->name, name);
478 MenuSetAlias(Menu * m, const char *alias)
480 _EFDUP(m->alias, alias);
484 MenuSetTitle(Menu * m, const char *title)
486 _EFDUP(m->title, title);
490 MenuSetIconSize(Menu * m, int size)
498 MenuSetData(Menu * m, char *data)
505 MenuSetLoader(Menu * m, MenuLoader * loader)
511 MenuSetTimestamp(Menu * m, time_t t)
517 MenuGetName(const Menu * m)
523 MenuGetData(const Menu * m)
525 return (const char *)m->data;
529 MenuGetTimestamp(const Menu * m)
531 return m->last_change;
535 MenuSetStyle(Menu * m, MenuStyle * ms)
538 m->style->ref_count--;
539 if (!ms && m->parent)
540 ms = m->parent->style;
542 ms = MenuStyleFind("DEFAULT");
549 MenuCreate(const char *name, const char *title, Menu * parent, MenuStyle * ms)
553 m = ECALLOC(Menu, 1);
558 MenuSetName(m, name);
559 MenuSetTitle(m, title);
562 m->icon_size = -1; /* Use image size */
565 menu_list = ecore_list_new();
566 ecore_list_append(menu_list, m);
572 MenuDestroy(Menu * m)
577 if (!ecore_list_goto(menu_list, m))
586 ecore_list_node_remove(menu_list, m);
589 EDestroyWindow(m->win);
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
605 MenuEmpty(Menu * m, int destroying)
610 for (i = 0; i < m->num; i++)
618 mi->child->ref_count--;
619 MenuDestroy(mi->child);
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);
635 FreePmapMask(&m->pmm);
641 MenuFreePixmaps(Menu * m)
646 for (i = 0; i < m->num; i++)
652 for (j = 0; j < 3; j++)
653 FreePmapMask(mi->pmm + j);
656 FreePmapMask(&m->pmm);
675 ICCCM_SetSizeConstraints(ewin, m->w, m->h, m->w, m->h, 0, 0, 1, 1,
677 EwinResize(ewin, m->w, m->h);
683 MenuAddItem(Menu * m, MenuItem * item)
690 items = EREALLOC(MenuItem *, m->items, m->num + 1);
694 items[m->num] = item;
700 MenuRealize(Menu * m)
702 int i, maxh, maxw, nmaxy;
703 int maxx1, maxx2, w, h, x, y, r, mmw, mmh;
705 EImageBorder *pad, *pad_item, *pad_sub;
713 MenuSetStyle(m, NULL);
720 m->win = ECreateClientWindow(VROOT, 0, 0, 1, 1);
721 EventCallbackRegister(m->win, 0, MenuHandleEvents, m);
730 for (i = 0; i < m->num; i++)
732 m->items[i]->menu = m;
734 if (m->items[i]->child)
739 m->items[i]->win = ECreateWindow(m->win, 0, 0, 1, 1, 0);
740 EventCallbackRegister(m->items[i]->win, 0, MenuItemHandleEvents,
742 ESelectInput(m->items[i]->win, MENU_ITEM_EVENT_MASK);
743 EMapWindow(m->items[i]->win);
745 if ((m->style->tclass) && (m->items[i]->text))
747 TextSize(m->style->tclass, 0, 0, 0, _(m->items[i]->text), &w, &h,
753 m->items[i]->text_w = w;
754 m->items[i]->text_h = h;
756 if (m->items[i]->icon_iclass && Conf.menus.show_icons)
758 im = ImageclassGetImage(m->items[i]->icon_iclass, 0, 0, 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);
780 m->items[i]->icon_iclass = NULL;
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)))
789 if (pad_item->top > pad_sub->top)
790 maxh += pad_item->top;
792 maxh += pad_sub->top;
793 if (pad_item->bottom > pad_sub->bottom)
794 maxh += pad_item->bottom;
796 maxh += pad_sub->bottom;
797 maxw = maxx1 + maxx2;
798 if (pad_item->left > pad_sub->left)
799 maxw += pad_item->left;
801 maxw += pad_sub->left;
802 if (pad_item->right > pad_sub->right)
803 maxw += pad_item->right;
805 maxw += pad_sub->right;
809 maxh += pad_item->top;
810 maxh += pad_item->bottom;
811 maxw = maxx1 + maxx2;
812 maxw += pad_item->left;
813 maxw += pad_item->right;
817 maxh += pad_sub->top;
818 maxh += pad_sub->bottom;
819 maxw = maxx1 + maxx2;
820 maxw += pad_sub->left;
821 maxw += pad_sub->right;
827 nmaxy = 3 * WinGetH(VROOT) / (4 * maxh + 1);
828 if (m->style->maxy && nmaxy > m->style->maxy)
829 nmaxy = m->style->maxy;
834 for (i = 0; i < m->num; i++)
836 if (r == 0 && (m->style->bg_iclass) && (!m->style->use_item_bg))
841 EMoveResizeWindow(m->items[i]->win, x, y, maxw, maxh);
842 if (m->style->iconpos == ICON_LEFT)
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,
850 ((maxx2 - m->items[i]->icon_w) / 2),
851 ((maxh - m->items[i]->icon_h) / 2));
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));
864 if (m->items[i]->icon_iclass && Conf.menus.show_icons)
866 ImageclassApply(m->items[i]->icon_iclass, m->items[i]->icon_win,
867 0, 0, STATE_NORMAL, ST_MENU_ITEM);
873 if ((m->style->maxx) || (nmaxy))
890 if (r >= m->style->maxx)
902 if ((m->style->bg_iclass) && (!m->style->use_item_bg))
911 EResizeWindow(m->win, mmw, mmh);
921 for (i = 0; i < m->num; i++)
923 for (j = 0; j < 3; j++)
924 FreePmapMask(&(m->items[i]->pmm[j]));
930 if (!m->style->use_item_bg)
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,
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);
946 for (i = 0; i < m->num; i++)
947 MenuDrawItem(m, m->items[i], 0, -1);
948 EShapePropagate(m->win);
955 MenuDrawItem(Menu * m, MenuItem * mi, char shape, int state)
961 mi_pmm = &(mi->pmm[(int)(mi->state)]);
971 EGetGeometry(mi->win, NULL, &x, &y, &w, &h, NULL, NULL);
974 mi_pmm->pmap = ECreatePixmap(mi->win, w, h, 0);
977 ic = (mi->child) ? m->style->sub_iclass : m->style->item_iclass;
978 item_type = (mi->state != STATE_NORMAL) ? ST_MENU_ITEM : ST_MENU;
980 if (!m->style->use_item_bg)
982 gc = EXCreateGC(m->pmm.pmap, 0, NULL);
983 XCopyArea(disp, WinGetPmap(m->win), mi_pmm->pmap, gc, x, y, w, h,
985 if ((mi->state != STATE_NORMAL) || (mi->child))
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,
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,
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,
1009 TextclassGetJustification(m->style->tclass));
1013 ESetWindowBackgroundPixmap(mi->win, mi_pmm->pmap);
1014 EShapeSetMask(mi->win, 0, 0, mi_pmm->mask);
1015 EClearWindow(mi->win);
1017 if ((shape) && (m->style->use_item_bg))
1018 EShapePropagate(m->win);
1022 MenuShowMasker(Menu * m __UNUSED__)
1024 EObj *eo = Mode_menus.cover_win;
1028 eo = EobjWindowCreate(EOBJ_TYPE_EVENT,
1029 0, 0, WinGetW(VROOT), WinGetH(VROOT),
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);
1040 Mode_menus.cover_win = eo;
1047 MenuHideMasker(void)
1049 EObj *eo = Mode_menus.cover_win;
1054 EventCallbackUnregister(EobjGetWin(eo), 0, MenuMaskerHandleEvents, NULL);
1055 EobjWindowDestroy(eo);
1056 Mode_menus.cover_win = NULL;
1060 MenusDestroyLoaded(void)
1067 /* Free all menustyles first (gulp) */
1071 ECORE_LIST_FOR_EACH(menu_list, 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...
1091 _MenuMatchName(const void *data, const void *match)
1093 const Menu *m = (const Menu *)data;
1095 if ((m->name && !strcmp((const char *)match, m->name)) ||
1096 (m->alias && !strcmp((const char *)match, m->alias)))
1103 MenuFind(const char *name, const char *param)
1107 m = (Menu *) ecore_list_find(menu_list, _MenuMatchName, name);
1111 /* Not in list - try if we can load internal */
1112 m = MenusCreateInternal(NULL, name, NULL, param);
1118 * Aliases for "well-known" menus for backward compatibility.
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",
1128 #define N_MENU_ALIASES (sizeof(menu_aliases)/sizeof(char*)/2)
1131 _MenuCheckAlias(const char *name)
1135 for (i = 0; i < N_MENU_ALIASES; i++)
1136 if (!strcmp(name, menu_aliases[2 * i]))
1137 return menu_aliases[2 * i + 1];
1143 MenusShowNamed(const char *name, const char *param)
1148 name2 = _MenuCheckAlias(name);
1152 /* Hide any menus currently up */
1156 m = MenuFind(name, param);
1160 if (!m->ewin) /* Don't show if already shown */
1167 return Mode_menus.first != NULL;
1173 TIMER_DEL(menu_timer_submenu);
1175 MenuHide(Mode_menus.first);
1176 Mode_menus.first = NULL;
1186 ECORE_LIST_FOR_EACH(menu_list, m) m->redraw = 1;
1190 * Menu event handlers
1194 MenuFindNextItem(Menu * m, MenuItem * mi, int inc)
1206 for (i = 0; i < m->num; i++)
1207 if (m->items[i] == mi)
1209 i = (i + inc + m->num) % m->num;
1217 MenuKeyPressConversion(KeySym key)
1219 if (key == Conf.menus.key.left)
1221 if (key == Conf.menus.key.right)
1223 if (key == Conf.menus.key.up)
1225 if (key == Conf.menus.key.down)
1227 if (key == Conf.menus.key.escape)
1229 if (key == Conf.menus.key.ret)
1232 /* The key does not correspond to any set, use the default behavior
1233 * associated to the key */
1238 MenuEventKeyPress(Menu * m, XEvent * ev)
1245 if (Mode_menus.active)
1247 m = Mode_menus.active;
1253 key = XLookupKeysym(&ev->xkey, 0);
1254 switch (MenuKeyPressConversion(key))
1262 mi = MenuFindNextItem(m, mi, 1);
1263 goto check_activate;
1266 mi = MenuFindNextItem(m, mi, -1);
1267 goto check_activate;
1274 goto check_activate;
1280 if (!m || m->num <= 0)
1283 if (ewin == NULL || !EwinIsMapped(ewin))
1285 mi = MenuFindItemByChild(m, m->child);
1286 goto check_activate;
1291 MenuActivateItem(m, mi);
1299 EFuncDefer(Mode_menus.context_ewin, mi->params);
1307 MenuItemEventMouseDown(MenuItem * mi, XEvent * ev __UNUSED__)
1311 Mode_menus.just_shown = 0;
1314 MenuDrawItem(m, mi, 1, STATE_CLICKED);
1318 MenuItemEventMouseUp(MenuItem * mi, XEvent * ev __UNUSED__)
1322 if (Mode_menus.just_shown)
1324 Mode_menus.just_shown = 0;
1325 if (ev->xbutton.time - Mode.events.last_btime < 250)
1331 if ((m) && (mi->state))
1333 MenuDrawItem(m, mi, 1, STATE_HILITED);
1334 if ((mi->params) /* && (!Mode_menus.just_shown) */ )
1336 EFuncDefer(Mode_menus.context_ewin, mi->params);
1343 #if 0 /* Was in HandleMotion() */
1345 MenusHandleMotion(void)
1347 #define SCROLL_RATIO 2/3
1348 if ((MenusActive() || (Mode_menus.clicked)))
1350 int i, offx = 0, offy = 0, xdist = 0, ydist = 0;
1357 static int menu_scroll_dist = 4;
1358 int my_width, my_height, x_org, y_org, head_num = 0;
1361 ScreenGetGeometry(Mode.events.mx, Mode.events.my, &x_org, &y_org,
1362 &my_width, &my_height);
1364 if (Mode.events.mx > ((x_org + my_width) - (menu_scroll_dist + 1)))
1367 -(menu_scroll_dist + (Mode.events.mx - (x_org + my_width)));
1369 else if (Mode.events.mx < (menu_scroll_dist + x_org))
1371 xdist = x_org + menu_scroll_dist - (Mode.events.mx);
1374 if (Mode.events.my > (WinGetH(VROOT) - (menu_scroll_dist + 1)))
1377 -(menu_scroll_dist + (Mode.events.my - (y_org + my_height)));
1379 else if (Mode.events.my < (menu_scroll_dist + y_org))
1381 ydist = y_org + menu_scroll_dist - (Mode.events.my);
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 ...
1388 if ((xdist != 0) || (ydist != 0) || Mode.doingslide)
1390 /* -10 has no meaning, only makes sure that the if's */
1391 /* above can't be fulfilled ... */
1392 menu_scroll_dist = -10;
1396 menu_scroll_dist = 13;
1399 if (Mode_menus.current_depth > 0)
1403 x1 = x_org + my_width;
1405 y1 = y_org + my_height;
1407 /* work out the minimum and maximum extents of our */
1408 /* currently active menus */
1409 for (i = 0; i < Mode_menus.current_depth; i++)
1411 if (Mode_menus.list[i])
1413 ewin = Mode_menus.list[i]->ewin;
1416 if (EoGetX(ewin) < x1)
1418 if (EoGetY(ewin) < y1)
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;
1430 offx = (x_org + my_width) - x2;
1438 offy = (y_org + my_height) - y2;
1445 if ((xdist < 0) && (offx <= 0))
1447 if ((xdist > 0) && (offx >= 0))
1449 if ((ydist < 0) && (offy <= 0))
1451 if ((ydist > 0) && (offy >= 0))
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)))))
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;
1466 if (xdist < -my_height)
1467 xdist = -my_height * SCROLL_RATIO;
1468 if (xdist > my_height)
1469 xdist = my_height * SCROLL_RATIO;
1471 if (Mode_menus.current_depth)
1474 ewin = Mode_menus.list[0]->ewin;
1475 if (ewin->head == head_num)
1478 for (i = 0; i < Mode_menus.current_depth; i++)
1481 if (Mode_menus.list[i])
1483 ewin = Mode_menus.list[i]->ewin;
1487 fx[i] = EoGetX(ewin);
1488 fy[i] = EoGetY(ewin);
1489 tx[i] = EoGetX(ewin) + xdist;
1490 ty[i] = EoGetY(ewin) + ydist;
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);
1516 MenusSetEvents(int on)
1522 event_mask = (on) ? MENU_ITEM_EVENT_MASK : 0;
1524 for (m = Mode_menus.first; m; m = m->child)
1526 for (i = 0; i < m->num; i++)
1527 ESelectInput(m->items[i]->win, event_mask);
1532 MenuSelectItem(Menu * m, MenuItem * mi, int focus)
1536 if (Mode_menus.active != m)
1538 Mode_menus.active = m;
1539 GrabKeyboardRelease();
1540 GrabKeyboardSet(m->win);
1544 if (mi == m->sel_item)
1548 MenuDrawItem(m, m->sel_item, 1, STATE_NORMAL);
1551 MenuDrawItem(m, mi, 1, STATE_HILITED);
1557 MenuSelectItemByChild(Menu * m, Menu * mc)
1561 mi = MenuFindItemByChild(m, mc);
1565 MenuSelectItem(m, mi, 0);
1569 _SubmenuCheckSlide(Menu * m, MenuItem * mi, EWin * ewin, EWin * ewin2,
1570 int xo, int yo, int ww, int hh)
1572 EWin *menus[256], *etmp;
1573 int fx[256], fy[256], tx[256], ty[256];
1576 int xdist = 0, ydist = 0;
1578 if (!Conf.menus.onscreen)
1581 ScreenGetGeometryByHead(Mode_menus.first->ewin->head, &sx, &sy, &sw, &sh);
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);
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);
1597 if (xdist == 0 && ydist == 0)
1601 for (m = Mode_menus.first; m; m = m->child)
1604 if (!etmp || etmp == ewin2)
1607 fx[i] = EoGetX(etmp);
1608 fy[i] = EoGetY(etmp);
1609 tx[i] = EoGetX(etmp) + xdist;
1610 ty[i] = EoGetY(etmp) + ydist;
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;
1620 if (Conf.menus.warp)
1621 EXWarpPointer(WinGetXwin(mi->win), mi->text_w / 2, mi->text_h / 2);
1625 SubmenuShowTimeout(void *dat)
1627 int mx, my, my2, xo, yo, mw, ww, hh;
1631 struct _mdata *data;
1632 int bl1, br1, bt1, bb1;
1633 int bl2, br2, bt2, bb2;
1635 data = (struct _mdata *)dat;
1636 if (!data || !data->m)
1640 if (!ecore_list_goto(menu_list, m))
1643 if (!ewin || !EwinFindByPtr(ewin))
1645 if (!EoIsShown(ewin))
1652 if (mi->child != m->child)
1654 m->child = mi->child;
1657 _SubmenuCheckSlide(m, mi, ewin, NULL, 0, 0, 0, 0);
1661 mi->child->parent = m;
1662 MenuShow(mi->child, 1);
1663 ewin2 = mi->child->ewin;
1664 if (!ewin2 || !EwinFindByPtr(ewin2))
1667 EGetGeometry(mi->win, NULL, &mx, &my, &mw, NULL, NULL, NULL);
1669 if (mi->child->num > 0 && mi->child->items[0])
1670 EGetGeometry(mi->child->items[0]->win, NULL, NULL, &my2, NULL, NULL, NULL,
1673 /* Sub-menu offsets relative to parent menu origin */
1674 EwinBorderGetSize(ewin, &bl1, &br1, &bt1, &bb1);
1675 EwinBorderGetSize(ewin2, &bl2, &br2, &bt2, &bb2);
1677 yo = bt1 + my - (bt2 + my2);
1679 /* Size of new submenu (may be shaded atm.) */
1680 ww = mi->child->w + bl2 + br2;
1681 hh = mi->child->h + bt2 + bb2;
1683 _SubmenuCheckSlide(m, mi, ewin, ewin2, xo, yo, ww, hh);
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));
1692 if (Conf.menus.animate)
1696 menu_timer_submenu = NULL;
1701 MenuActivateItem(Menu * m, MenuItem * mi)
1703 static struct _mdata mdata;
1706 mi_prev = m->sel_item;
1709 MenuSelectItem(m->child, NULL, 0);
1710 MenuSelectItem(m, mi, 1);
1712 MenuSelectItemByChild(m->parent, m);
1717 if (mi && !mi->child && mi_prev && !mi_prev->child)
1720 TIMER_DEL(menu_timer_submenu);
1722 if ((mi && mi->child && !mi->child->shown) || (mi && mi->child != m->child))
1726 TIMER_ADD(menu_timer_submenu, 0.2, SubmenuShowTimeout, &mdata);
1731 MenuItemEventMouseIn(MenuItem * mi, XEvent * ev)
1733 if (ev->xcrossing.detail == NotifyInferior)
1736 MenuActivateItem(mi->menu, mi);
1740 MenuItemEventMouseOut(MenuItem * mi, XEvent * ev)
1742 if (ev->xcrossing.detail == NotifyInferior)
1746 MenuSelectItem(mi->menu, NULL, 0);
1750 MenuHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
1752 Menu *m = (Menu *) prm;
1754 #if DEBUG_MENU_EVENTS
1755 Eprintf("MenuHandleEvents %d\n", ev->type);
1760 MenuEventKeyPress(m, ev);
1765 GrabKeyboardSet(m->win);
1771 MenuItemHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
1773 MenuItem *mi = (MenuItem *) prm;
1775 #if DEBUG_MENU_EVENTS
1776 Eprintf("MenuItemHandleEvents %d\n", ev->type);
1781 MenuItemEventMouseDown(mi, ev);
1784 MenuItemEventMouseUp(mi, ev);
1787 MenuItemEventMouseIn(mi, ev);
1790 MenuItemEventMouseOut(mi, ev);
1796 MenuMaskerHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
1798 #if DEBUG_MENU_EVENTS
1799 Eprintf("MenuMaskerHandleEvents %d\n", ev->type);
1810 * Configuration load/save
1815 MenuStyleConfigLoad(FILE * fs)
1818 char s[FILEPATH_LEN_MAX];
1819 char s2[FILEPATH_LEN_MAX];
1821 MenuStyle *ms = NULL;
1823 while (GetLine(s, sizeof(s), fs))
1825 i1 = ConfigParseline1(s, s2, NULL, NULL);
1830 case CONFIG_CLASSNAME:
1831 ms = MenuStyleCreate(s2);
1834 ms->tclass = TextclassAlloc(s2, 1);
1836 case MENU_BG_ICLASS:
1837 ms->bg_iclass = ImageclassAlloc(s2, 1);
1839 case MENU_ITEM_ICLASS:
1840 ms->item_iclass = ImageclassAlloc(s2, 1);
1842 case MENU_SUBMENU_ICLASS:
1843 ms->sub_iclass = ImageclassAlloc(s2, 1);
1845 case MENU_USE_ITEM_BACKGROUND:
1846 ms->use_item_bg = atoi(s2);
1847 if (ms->use_item_bg)
1851 ImageclassFree(ms->bg_iclass);
1852 ms->bg_iclass = NULL;
1856 case MENU_MAX_COLUMNS:
1857 ms->maxx = atoi(s2);
1860 ms->maxy = atoi(s2);
1863 _EFDUP(ms->border_name, s2);
1876 MenuConfigLoad(FILE * fs)
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];
1888 Menu *m = NULL, *mm;
1890 ImageClass *ic = NULL;
1892 while (GetLine(s, sizeof(s), fs))
1894 i1 = ConfigParseline1(s, s2, &p2, &p3);
1900 case CONFIG_VERSION:
1906 if (i2 != CONFIG_OPEN)
1917 m = MenuFind(s2, NULL);
1920 sscanf(p3, "%4000s %4000s %4000s", s3, s4, s5);
1921 m = MenusCreateInternal(s4, s2, s3, s5);
1926 case CONFIG_CLASSNAME:
1928 m = MenuCreate(s2, NULL, NULL, NULL);
1931 params = _MenuCheckAlias(s2);
1933 MenuSetAlias(m, params);
1938 if (strcmp("NULL", s2))
1939 ic = ImageclassFind(s2, 0);
1944 /* The rest require the menu m to be created */
1947 ConfigParseError("Menu", s);
1953 case MENU_USE_STYLE:
1954 MenuSetStyle(m, MenuStyleFind(s2));
1957 MenuSetTitle(m, p2);
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"))
1968 sscanf(p3, "%1000s", s3);
1969 ok = path_canexec(s3);
1973 mi = MenuItemCreate(txt, ic, p2, NULL);
1982 sscanf(p3, "%s %n", s3, &len);
1984 if (strcmp("NULL", s3))
1985 ic = ImageclassFind(s3, 1);
1986 mm = MenuFind(s2, NULL);
1987 mi = MenuItemCreate(p3 + len, ic, NULL, mm);
1997 ConfigAlertLoad("Menu");
2004 MenusTimeout(void *data __UNUSED__)
2009 /* Unload contents if loadable and no access in > 5 min */
2011 ECORE_LIST_FOR_EACH(menu_list, m)
2013 if (m->shown || !m->filled ||
2014 ts - m->last_access < MENU_UNLOAD_CHECK_INTERVAL)
2020 else if (m->ref_count)
2034 MenusSighan(int sig, void *prm __UNUSED__)
2036 Timer *menu_unload_timer;
2040 case ESIGNAL_CONFIGURE:
2041 ConfigFileLoad("menus.cfg", NULL, MenuConfigLoad, 1);
2045 TIMER_ADD(menu_unload_timer, 1. * MENU_UNLOAD_CHECK_INTERVAL,
2046 MenusTimeout, NULL);
2049 case ESIGNAL_AREA_SWITCH_START:
2050 case ESIGNAL_DESK_SWITCH_START:
2054 case ESIGNAL_EWIN_UNMAP:
2055 if ((EWin *) prm == Mode_menus.context_ewin)
2059 case ESIGNAL_THEME_TRANS_CHANGE:
2067 * Configuration dialog
2070 static char tmp_warpmenus;
2071 static char tmp_animated_menus;
2072 static char tmp_menusonscreen;
2075 CB_ConfigureMenus(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
2079 Conf.menus.warp = tmp_warpmenus;
2080 Conf.menus.animate = tmp_animated_menus;
2081 Conf.menus.onscreen = tmp_menusonscreen;
2087 _DlgFillMenus(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
2091 tmp_warpmenus = Conf.menus.warp;
2092 tmp_animated_menus = Conf.menus.animate;
2093 tmp_menusonscreen = Conf.menus.onscreen;
2095 DialogItemTableSetOptions(table, 3, 0, 0, 0);
2097 di = DialogAddItem(table, DITEM_CHECKBUTTON);
2098 DialogItemSetColSpan(di, 3);
2099 DialogItemSetText(di, _("Animated display of menus"));
2100 DialogItemCheckButtonSetPtr(di, &tmp_animated_menus);
2102 di = DialogAddItem(table, DITEM_CHECKBUTTON);
2103 DialogItemSetColSpan(di, 3);
2104 DialogItemSetText(di, _("Always pop up menus on screen"));
2105 DialogItemCheckButtonSetPtr(di, &tmp_menusonscreen);
2107 di = DialogAddItem(table, DITEM_CHECKBUTTON);
2108 DialogItemSetColSpan(di, 3);
2109 DialogItemSetText(di, _("Warp pointer after moving menus"));
2110 DialogItemCheckButtonSetPtr(di, &tmp_warpmenus);
2113 const DialogDef DlgMenus = {
2116 N_("Menu Settings"),
2117 SOUND_SETTINGS_MENUS,
2119 N_("Enlightenment Menu\n" "Settings Dialog\n"),
2121 DLG_OAC, CB_ConfigureMenus,
2123 #endif /* ENABLE_DIALOGS */
2126 MenusIpc(const char *params)
2129 char cmd[128], prm[4096];
2133 cmd[0] = prm[0] = '\0';
2138 sscanf(p, "%100s %4000s %n", cmd, prm, &len);
2142 if (!p || cmd[0] == '?')
2144 IpcPrintf("Menus - active=%d\n", MenusActive());
2146 else if (!strncmp(cmd, "list", 2))
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),
2154 else if (!strncmp(cmd, "reload", 2))
2156 MenusDestroyLoaded();
2157 ConfigFileLoad("menus.cfg", NULL, MenuConfigLoad, 1);
2159 else if (!strncmp(cmd, "show", 2))
2163 if (p && !strcmp(prm, "named"))
2165 Esnprintf(prm, sizeof(prm), "%s", p);
2168 SoundPlay(SOUND_MENU_SHOW);
2169 MenusShowNamed(prm, p);
2173 static const IpcItem MenusIpcArray[] = {
2178 " menus list Show existing menus\n"
2179 " menus reload Reload menus.cfg without restarting\n"
2180 " menus show <name> Show named menu\n"}
2183 #define N_IPC_FUNCS (sizeof(MenusIpcArray)/sizeof(IpcItem))
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),
2198 #define N_CFG_ITEMS (sizeof(MenusCfgItems)/sizeof(CfgItem))
2203 extern const EModule ModMenus;
2204 const EModule ModMenus = {
2207 {N_IPC_FUNCS, MenusIpcArray},
2208 {N_CFG_ITEMS, MenusCfgItems}