1 #include "e.h"
2
3 /* TODO List:
4 *
5 * * support change of menu items after realize
6 * * support add/del of menu items after realize
7 * * support text/color classes
8 * * refcount menu up while looping thru and calling other fn's
9 * * support alignment (x, y) as well as spawn direction
10 * * need different menu style support for different menus
11 * * add menu icon/title support
12 * * use event timestamps not clock for "click and release" detect
13 * * menu icons can set if/how they will be scaled
14 * * support move/resize of "box" that spawned the menu
15 * * add image item (label is replaced by image/icon)
16 * * add generic evas object item type (label replaced by object)
17 * * allow menus to stretch width/height to fit spawner widget/box
18 * * allow menus to auto-shrink (horizontally) if forced to
19 * * support auto left/right direction spawn
20 * * support obscures to indicate offscreen/not visible menu parts
21 */
22
23 /* local subsystem data types */
24 typedef struct _E_Menu_Category E_Menu_Category;
25
26 struct _E_Menu_Category
27 {
28 void *data;
29 Eina_List *callbacks;
30 };
31
32 /* local subsystem functions */
33 static void _e_menu_free(E_Menu *m);
34 static void _e_menu_item_free(E_Menu_Item *mi);
35 static void _e_menu_item_realize(E_Menu_Item *mi);
36 static void _e_menu_realize(E_Menu *m);
37 static void _e_menu_items_layout_update(E_Menu *m);
38 static void _e_menu_item_unrealize(E_Menu_Item *mi);
39 static void _e_menu_unrealize(E_Menu *m);
40 static void _e_menu_activate_internal(E_Menu *m, E_Zone *zone);
41 static void _e_menu_deactivate_all(void);
42 static void _e_menu_deactivate_above(E_Menu *m);
43 static void _e_menu_submenu_activate(E_Menu_Item *mi);
44 static void _e_menu_submenu_deactivate(E_Menu_Item *mi);
45 static void _e_menu_reposition(E_Menu *m);
46 static int _e_menu_active_call(void);
47 static int _e_menu_realize_call(E_Menu_Item *mi);
48 static void _e_menu_item_activate_next(void);
49 static void _e_menu_item_activate_previous(void);
50 static void _e_menu_item_activate_first(void);
51 static void _e_menu_item_activate_last(void);
52 static void _e_menu_item_activate_nth(int n);
53 static void _e_menu_item_activate_char(const char *key_compose);
54 static void _e_menu_activate_next(void);
55 static void _e_menu_activate_previous(void);
56 static void _e_menu_activate_first(void);
57 static void _e_menu_activate_last(void);
58 #if 0
59 static void _e_menu_activate_nth(int n);
60 #endif
61 static E_Menu *_e_menu_active_get(void);
62 static E_Menu_Item *_e_menu_item_active_get(void);
63 static Eina_List *_e_menu_list_item_active_get(void);
64 static int _e_menu_outside_bounds_get(int xdir, int ydir);
65 static void _e_menu_scroll_by(int dx, int dy);
66 static void _e_menu_mouse_autoscroll_check(void);
67 static void _e_menu_item_ensure_onscreen(E_Menu_Item *mi);
68 static int _e_menu_auto_place(E_Menu *m, int x, int y, int w, int h);
69 static void _e_menu_cb_intercept_item_move(void *data, Evas_Object *o, Evas_Coord x, Evas_Coord y);
70 static void _e_menu_cb_intercept_item_resize(void *data, Evas_Object *o, Evas_Coord w, Evas_Coord h);
71 static void _e_menu_cb_intercept_container_move(void *data, Evas_Object *o, Evas_Coord x, Evas_Coord y);
72 static void _e_menu_cb_intercept_container_resize(void *data, Evas_Object *o, Evas_Coord w, Evas_Coord h);
73 static void _e_menu_cb_item_in(void *data, Evas *evas, Evas_Object *obj, void *event_info);
74 static void _e_menu_cb_item_out(void *data, Evas *evas, Evas_Object *obj, void *event_info);
75 static Eina_Bool _e_menu_cb_key_down(void *data EINA_UNUSED, Ecore_Event_Key *ev);
76 static Eina_Bool _e_menu_cb_mouse_down(void *data, int type, void *event);
77 static Eina_Bool _e_menu_cb_mouse_up(void *data, int type, void *event);
78 static Eina_Bool _e_menu_cb_mouse_move(void *data, int type, void *event);
79 static Eina_Bool _e_menu_cb_mouse_wheel(void *data, int type, void *event);
80 static Eina_Bool _e_menu_cb_scroll_animator(void *data);
81 static void _e_menu_cb_item_submenu_post_default(void *data, E_Menu *m, E_Menu_Item *mi);
82 static void _e_menu_category_free_cb(E_Menu_Category *cat);
83 static void _e_menu_cb_mouse_evas_down(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED);
84 static void _e_menu_hide_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED);
85
86 typedef enum
87 {
88 NAV_BY_NONE,
89 NAV_BY_MOUSE,
90 NAV_BY_WHEEL,
91 NAV_BY_KEY
92 } Nav_By;
93
94 /* local subsystem globals */
95 static Ecore_Window _e_menu_win = UINT_MAX;
96 static Eina_Bool _e_menu_grabbed = EINA_FALSE;
97 static Eina_List *_e_active_menus = NULL;
98 static E_Menu_Item *_e_active_menu_item = NULL;
99 static E_Menu_Item *_e_prev_active_menu_item = NULL;
100 /*static Eina_Hash *_e_menu_category_items = NULL;*/
101 static Eina_Hash *_e_menu_categories = NULL;
102 static unsigned int _e_menu_activate_time = 0;
103 static int _e_menu_activate_floating = 0;
104 static int _e_menu_activate_maybe_drag = 0;
105 static int _e_menu_activate_dragging = 0;
106 static Ecore_Animator *_e_menu_scroll_animator = NULL;
107 static double _e_menu_scroll_start = 0.0;
108 static int _e_menu_x = 0;
109 static int _e_menu_y = 0;
110 static unsigned int _e_menu_time = 0;
111 static int _e_menu_autoscroll_x = 0;
112 static int _e_menu_autoscroll_y = 0;
113 static Eina_List *handlers = NULL;
114 static Eina_Bool _e_menu_lock = EINA_FALSE;
115 static Nav_By _e_menu_nav_by = NAV_BY_NONE;
116
117 static Eina_Bool pending_feed;
118 static unsigned int pending_activate_time;
119
120 static Eina_List *
_e_active_menus_copy_ref(void)121 _e_active_menus_copy_ref(void)
122 {
123 E_Object *o;
124 Eina_List *l, *ret = NULL;
125
126 EINA_LIST_FOREACH(_e_active_menus, l, o)
127 {
128 ret = eina_list_append(ret, o);
129 e_object_ref(o);
130 }
131 return ret;
132 }
133
134 static Eina_List *
_e_menu_list_free_unref(Eina_List * l)135 _e_menu_list_free_unref(Eina_List *l)
136 {
137 E_Object *o;
138 Eina_List *ll, *lll;
139
140 /* list must be freed in reverse to ensure that submenus
141 * (which are added to the end of the list)
142 * are deleted before their parents
143 */
144 EINA_LIST_REVERSE_FOREACH_SAFE(l, ll, lll, o)
145 {
146 e_object_unref(o);
147 l = eina_list_remove_list(l, ll);
148 }
149 return l;
150 }
151
152 /* macros for debugging menu refcounts */
153 #if 0
154 #define e_object_ref(X) do { \
155 int xx; \
156 xx = e_object_ref(X); \
157 INF("REF: %p || %d", X, xx); \
158 } while (0)
159 #define e_object_unref(X) do { \
160 int xx; \
161 xx = e_object_unref(X); \
162 INF("UNREF: %p || %d", X, xx); \
163 } while (0)
164 #endif
165
166 /* externally accessible functions */
167 EINTERN int
e_menu_init(void)168 e_menu_init(void)
169 {
170 E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_MOUSE_BUTTON_DOWN, _e_menu_cb_mouse_down, NULL);
171 E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_MOUSE_BUTTON_UP, _e_menu_cb_mouse_up, NULL);
172 E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_MOUSE_MOVE, _e_menu_cb_mouse_move, NULL);
173 E_LIST_HANDLER_APPEND(handlers, ECORE_EVENT_MOUSE_WHEEL, _e_menu_cb_mouse_wheel, NULL);
174 _e_menu_categories = eina_hash_string_superfast_new((Eina_Free_Cb)_e_menu_category_free_cb);
175
176 e_int_menus_init();
177 return 1;
178 }
179
180 EINTERN int
e_menu_shutdown(void)181 e_menu_shutdown(void)
182 {
183 E_FREE_LIST(handlers, ecore_event_handler_del);
184
185 if (!x_fatal)
186 e_menu_hide_all();
187 _e_active_menus = NULL;
188 E_FREE_FUNC(_e_menu_categories, eina_hash_free);
189
190 _e_menu_lock = EINA_FALSE;
191 e_int_menus_shutdown();
192
193 return 1;
194 }
195
196 E_API void
e_menu_hide_all(void)197 e_menu_hide_all(void)
198 {
199 E_Menu *m;
200
201 EINA_LIST_FREE(_e_active_menus, m)
202 {
203 if (m->post_deactivate_cb.func)
204 m->post_deactivate_cb.func(m->post_deactivate_cb.data, m);
205 m->active = 0;
206 if (m->comp_object == e_comp->autoclose.obj)
207 e_comp_object_util_autoclose(NULL, NULL, NULL, NULL);
208 _e_menu_unrealize(m);
209 m->in_active_list = 0;
210 e_object_unref(E_OBJECT(m));
211 }
212 if (_e_menu_grabbed)
213 {
214 e_comp_ungrab_input(1, 1);
215 _e_menu_grabbed = EINA_FALSE;
216 }
217 }
218
219 E_API E_Menu *
e_menu_new(void)220 e_menu_new(void)
221 {
222 E_Menu *m;
223
224 m = E_OBJECT_ALLOC(E_Menu, E_MENU_TYPE, _e_menu_free);
225 if (!m) return NULL;
226 m->cur.w = 1;
227 m->cur.h = 1;
228 m->category = NULL;
229 m->hold_mode = EINA_TRUE;
230 return m;
231 }
232
233 E_API void
e_menu_hold_mode_set(E_Menu * m,Eina_Bool hold_mode)234 e_menu_hold_mode_set(E_Menu *m, Eina_Bool hold_mode)
235 {
236 E_OBJECT_CHECK(m);
237 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
238 m->hold_mode = hold_mode;
239 }
240
241 E_API void
e_menu_activate_key(E_Menu * m,E_Zone * zone,int x,int y,int w,int h,int dir)242 e_menu_activate_key(E_Menu *m, E_Zone *zone, int x, int y, int w, int h, int dir)
243 {
244 E_OBJECT_CHECK(m);
245 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
246 E_OBJECT_CHECK(zone);
247 E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
248 if (_e_active_menus) e_menu_hide_all();
249 _e_menu_activate_time = 0;
250 _e_menu_activate_floating = 0;
251 _e_menu_activate_internal(m, zone);
252 if (!m->zone)
253 {
254 e_menu_deactivate(m);
255 return;
256 }
257 _e_menu_nav_by = NAV_BY_KEY;
258 switch (dir)
259 {
260 case E_MENU_POP_DIRECTION_LEFT:
261 _e_menu_realize(m);
262 m->cur.x = x - m->cur.w;
263 m->cur.y = y;
264 if ((m->cur.y + m->cur.h) > (m->zone->y + m->zone->h))
265 m->cur.y = y + h - m->cur.h;
266 _e_menu_activate_first();
267 break;
268
269 case E_MENU_POP_DIRECTION_RIGHT:
270 _e_menu_realize(m);
271 m->cur.x = x + w;
272 m->cur.y = y;
273 _e_menu_activate_first();
274 break;
275
276 case E_MENU_POP_DIRECTION_UP:
277 _e_menu_realize(m);
278 m->cur.x = x;
279 if ((m->cur.x + m->cur.w) > (m->zone->x + m->zone->w))
280 m->cur.x = x + w - m->cur.w;
281 m->cur.y = y - m->cur.h;
282 _e_menu_activate_last();
283 break;
284
285 case E_MENU_POP_DIRECTION_DOWN:
286 _e_menu_realize(m);
287 m->cur.x = x;
288 if ((m->cur.x + m->cur.w) > (m->zone->x + m->zone->w))
289 m->cur.x = x + w - m->cur.w;
290 m->cur.y = y + h;
291 if ((m->cur.y + m->cur.h) > (m->zone->y + m->zone->h))
292 m->cur.y = y + h - m->cur.h;
293 _e_menu_activate_first();
294 break;
295
296 case E_MENU_POP_DIRECTION_AUTO:
297 {
298 int pos = 0;
299
300 pos = _e_menu_auto_place(m, x, y, w, h);
301 if (pos == 4)
302 _e_menu_activate_last();
303 else
304 _e_menu_activate_first();
305 }
306 break;
307
308 default:
309 m->cur.x = x + w;
310 m->cur.y = y + h;
311 _e_menu_activate_first();
312 break;
313 }
314 }
315
316 E_API void
e_menu_activate_mouse(E_Menu * m,E_Zone * zone,int x,int y,int w,int h,int dir,unsigned int activate_time)317 e_menu_activate_mouse(E_Menu *m, E_Zone *zone, int x, int y, int w, int h, int dir, unsigned int activate_time)
318 {
319 E_Menu_Item *pmi;
320
321 E_OBJECT_CHECK(m);
322 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
323 E_OBJECT_CHECK(zone);
324 E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
325
326 e_object_ref(E_OBJECT(m));
327 e_menu_hide_all();
328 _e_menu_activate_time = 0;
329 _e_menu_activate_floating = 0;
330 _e_menu_activate_internal(m, zone);
331 if (!m->zone)
332 {
333 e_menu_deactivate(m);
334 e_object_unref(E_OBJECT(m));
335 return;
336 }
337 _e_menu_nav_by = NAV_BY_MOUSE;
338 switch (dir)
339 {
340 case E_MENU_POP_DIRECTION_LEFT:
341 _e_menu_realize(m);
342 m->cur.x = x - m->cur.w;
343 m->cur.y = y;
344 if ((m->cur.y + m->cur.h) > (m->zone->y + m->zone->h))
345 m->cur.y = y + h - m->cur.h;
346 break;
347
348 case E_MENU_POP_DIRECTION_RIGHT:
349 _e_menu_realize(m);
350 m->cur.x = x + w;
351 m->cur.y = y;
352 break;
353
354 case E_MENU_POP_DIRECTION_UP:
355 _e_menu_realize(m);
356 m->cur.x = x;
357 if ((m->cur.x + m->cur.w) > (m->zone->x + m->zone->w))
358 m->cur.x = x + w - m->cur.w;
359 m->cur.y = y - m->cur.h;
360 break;
361
362 case E_MENU_POP_DIRECTION_DOWN:
363 _e_menu_realize(m);
364 m->cur.x = x;
365 if ((m->cur.x + m->cur.w) > (m->zone->x + m->zone->w))
366 m->cur.x = x + w - m->cur.w;
367 m->cur.y = y + h;
368 if ((m->cur.y + m->cur.h) > (m->zone->y + m->zone->h))
369 m->cur.y = y + h - m->cur.h;
370 break;
371
372 case E_MENU_POP_DIRECTION_AUTO:
373 _e_menu_auto_place(m, x, y, w, h);
374 break;
375
376 default:
377 m->cur.x = x + w;
378 m->cur.y = y + h;
379 break;
380 }
381 pmi = _e_menu_item_active_get();
382 if (pmi) e_menu_item_active_set(pmi, 0);
383 pending_feed = 1;
384 if (!activate_time) activate_time = lround(ecore_loop_time_get() * 1000);
385 _e_menu_activate_time = pending_activate_time = activate_time;
386 e_object_unref(E_OBJECT(m));
387 }
388
389 E_API void
e_menu_activate(E_Menu * m,E_Zone * zone,int x,int y,int w,int h,int dir)390 e_menu_activate(E_Menu *m, E_Zone *zone, int x, int y, int w, int h, int dir)
391 {
392 E_Menu_Item *pmi;
393
394 E_OBJECT_CHECK(m);
395 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
396 E_OBJECT_CHECK(zone);
397 E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
398 if (_e_active_menus) e_menu_hide_all();
399 _e_menu_activate_time = 0;
400 _e_menu_activate_floating = 0;
401 _e_menu_activate_internal(m, zone);
402 if (!m->zone)
403 {
404 e_menu_deactivate(m);
405 return;
406 }
407 _e_menu_nav_by = NAV_BY_NONE;
408 switch (dir)
409 {
410 case E_MENU_POP_DIRECTION_LEFT:
411 _e_menu_realize(m);
412 m->cur.x = x - m->cur.w;
413 m->cur.y = y;
414 if ((m->cur.y + m->cur.h) > (m->zone->y + m->zone->h))
415 m->cur.y = y + h - m->cur.h;
416 _e_menu_activate_first();
417 break;
418
419 case E_MENU_POP_DIRECTION_RIGHT:
420 _e_menu_realize(m);
421 m->cur.x = x + w;
422 m->cur.y = y;
423 _e_menu_activate_first();
424 break;
425
426 case E_MENU_POP_DIRECTION_UP:
427 _e_menu_realize(m);
428 m->cur.x = x;
429 if ((m->cur.x + m->cur.w) > (m->zone->x + m->zone->w))
430 m->cur.x = x + w - m->cur.w;
431 m->cur.y = y - m->cur.h;
432 _e_menu_activate_last();
433 break;
434
435 case E_MENU_POP_DIRECTION_DOWN:
436 _e_menu_realize(m);
437 m->cur.x = x;
438 if ((m->cur.x + m->cur.w) > (m->zone->x + m->zone->w))
439 m->cur.x = x + w - m->cur.w;
440 m->cur.y = y + h;
441 if ((m->cur.y + m->cur.h) > (m->zone->y + m->zone->h))
442 m->cur.y = y + h - m->cur.h;
443 _e_menu_activate_first();
444 break;
445
446 case E_MENU_POP_DIRECTION_AUTO:
447 _e_menu_auto_place(m, x, y, w, h);
448 break;
449
450 default:
451 m->cur.x = x + w;
452 m->cur.y = y + h;
453 break;
454 }
455 pmi = _e_menu_item_active_get();
456 if (pmi) e_menu_item_active_set(pmi, 0);
457 }
458
459 E_API void
e_menu_deactivate(E_Menu * m)460 e_menu_deactivate(E_Menu *m)
461 {
462 Eina_List *l;
463 E_Menu_Item *mi;
464
465 E_OBJECT_CHECK(m);
466 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
467 if (!m->active) return;
468
469 EINA_LIST_FOREACH(m->items, l, mi)
470 {
471 if (mi->submenu) e_menu_deactivate(mi->submenu);
472 }
473
474 m->cur.visible = 0;
475 m->active = 0;
476 if (m->post_deactivate_cb.func)
477 m->post_deactivate_cb.func(m->post_deactivate_cb.data, m);
478 }
479
480 E_API int
e_menu_freeze(E_Menu * m)481 e_menu_freeze(E_Menu *m)
482 {
483 E_OBJECT_CHECK_RETURN(m, 0);
484 E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, 0);
485 m->frozen++;
486 return m->frozen;
487 }
488
489 E_API int
e_menu_thaw(E_Menu * m)490 e_menu_thaw(E_Menu *m)
491 {
492 E_OBJECT_CHECK_RETURN(m, 0);
493 E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, 0);
494 m->frozen--;
495 if (m->frozen < 0) m->frozen = 0;
496 return m->frozen;
497 }
498
499 E_API void
e_menu_title_set(E_Menu * m,const char * title)500 e_menu_title_set(E_Menu *m, const char *title)
501 {
502 E_OBJECT_CHECK(m);
503 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
504 if ((m->header.title) && (title) && (!strcmp(m->header.title, title)))
505 return;
506 if (m->header.title)
507 {
508 eina_stringshare_del(m->header.title);
509 m->header.title = NULL;
510 }
511 if (title) m->header.title = eina_stringshare_add(title);
512 else m->header.title = NULL;
513 m->changed = 1;
514 if (!m->realized) return;
515 edje_object_part_text_set(m->bg_object, "e.text.title", m->header.title);
516 if (m->header.title)
517 edje_object_signal_emit(m->bg_object, "e,action,show,title", "e");
518 else
519 edje_object_signal_emit(m->bg_object, "e,action,hide,title", "e");
520 edje_object_message_signal_process(m->bg_object);
521 }
522
523 E_API void
e_menu_icon_file_set(E_Menu * m EINA_UNUSED,const char * icon EINA_UNUSED)524 e_menu_icon_file_set(E_Menu *m EINA_UNUSED, const char *icon EINA_UNUSED)
525 {
526 /* FIXME: support menu icons
527 E_OBJECT_CHECK(m);
528 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
529 */
530 }
531
532 E_API void
e_menu_category_set(E_Menu * m,const char * category)533 e_menu_category_set(E_Menu *m, const char *category)
534 {
535 E_OBJECT_CHECK(m);
536 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
537 if (m->category)
538 {
539 eina_stringshare_del(m->category);
540 m->category = NULL;
541 }
542 if (category)
543 m->category = eina_stringshare_add(category);
544 else
545 m->category = NULL;
546 m->changed = 1;
547 }
548
549 E_API void
e_menu_category_data_set(char * category,void * data)550 e_menu_category_data_set(char *category, void *data)
551 {
552 E_Menu_Category *cat;
553
554 cat = eina_hash_find(_e_menu_categories, category);
555 if (cat)
556 cat->data = data;
557 else /* if it isn't found create the new hash */
558 {
559 cat = calloc(1, sizeof(E_Menu_Category));
560 cat->data = data;
561 eina_hash_add(_e_menu_categories, category, cat);
562 }
563 }
564
565 E_API E_Menu_Category_Callback *
e_menu_category_callback_add(char * category,void (* create_cb)(void * data,E_Menu * m,void * category_data),Ecore_Cb free_cb,void * data)566 e_menu_category_callback_add(char *category, void (*create_cb)(void *data, E_Menu *m, void *category_data), Ecore_Cb free_cb, void *data)
567 {
568 E_Menu_Category *cat;
569 E_Menu_Category_Callback *cb = NULL;
570
571 cat = eina_hash_find(_e_menu_categories, category);
572 if (!cat) /* if it isn't found create the new hash */
573 {
574 cat = calloc(1, sizeof(E_Menu_Category));
575 eina_hash_add(_e_menu_categories, category, cat);
576 }
577 if (cat)
578 {
579 cb = calloc(1, sizeof(E_Menu_Category_Callback));
580 if (cb)
581 {
582 cb->data = data;
583 cb->create = create_cb;
584 cb->free = free_cb;
585 cb->category = eina_stringshare_add(category);
586 cat->callbacks = eina_list_append(cat->callbacks, cb);
587 }
588 }
589 return cb;
590 }
591
592 E_API void
e_menu_category_callback_del(E_Menu_Category_Callback * cb)593 e_menu_category_callback_del(E_Menu_Category_Callback *cb)
594 {
595 E_Menu_Category *cat;
596
597 if (cb)
598 {
599 cat = eina_hash_find(_e_menu_categories, cb->category);
600 if (cat)
601 cat->callbacks = eina_list_remove(cat->callbacks, cb);
602 eina_stringshare_del(cb->category);
603 free(cb);
604 }
605 }
606
607 E_API void
e_menu_pre_activate_callback_set(E_Menu * m,void (* func)(void * data,E_Menu * m),void * data)608 e_menu_pre_activate_callback_set(E_Menu *m, void (*func)(void *data, E_Menu *m), void *data)
609 {
610 E_OBJECT_CHECK(m);
611 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
612 m->pre_activate_cb.func = func;
613 m->pre_activate_cb.data = data;
614 }
615
616 E_API void
e_menu_post_deactivate_callback_set(E_Menu * m,void (* func)(void * data,E_Menu * m),void * data)617 e_menu_post_deactivate_callback_set(E_Menu *m, void (*func)(void *data, E_Menu *m), void *data)
618 {
619 E_OBJECT_CHECK(m);
620 E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
621 m->post_deactivate_cb.func = func;
622 m->post_deactivate_cb.data = data;
623 }
624
625 E_API E_Menu *
e_menu_root_get(E_Menu * m)626 e_menu_root_get(E_Menu *m)
627 {
628 E_Menu *ret;
629
630 E_OBJECT_CHECK_RETURN(m, NULL);
631 E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, NULL);
632 ret = m;
633 while ((ret->parent_item) && (ret->parent_item->menu))
634 ret = ret->parent_item->menu;
635
636 return ret;
637 }
638
639 E_API E_Menu_Item *
e_menu_item_new(E_Menu * m)640 e_menu_item_new(E_Menu *m)
641 {
642 E_Menu_Item *mi;
643
644 E_OBJECT_CHECK_RETURN(m, NULL);
645 E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, NULL);
646 mi = E_OBJECT_ALLOC(E_Menu_Item, E_MENU_ITEM_TYPE, _e_menu_item_free);
647 mi->menu = m;
648 mi->menu->items = eina_list_append(mi->menu->items, mi);
649 mi->list_position = eina_list_last(mi->menu->items);
650 return mi;
651 }
652
653 E_API E_Menu_Item *
e_menu_item_new_relative(E_Menu * m,E_Menu_Item * rel)654 e_menu_item_new_relative(E_Menu *m, E_Menu_Item *rel)
655 {
656 E_Menu_Item *mi;
657 E_OBJECT_CHECK_RETURN(m, NULL);
658 E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, NULL);
659 if (rel)
660 {
661 E_OBJECT_CHECK_RETURN(rel, NULL);
662 E_OBJECT_TYPE_CHECK_RETURN(rel, E_MENU_ITEM_TYPE, NULL);
663 if (rel->menu != m) return NULL;
664 }
665
666 mi = E_OBJECT_ALLOC(E_Menu_Item, E_MENU_ITEM_TYPE, _e_menu_item_free);
667 mi->menu = m;
668
669 if (rel)
670 {
671 Eina_List *l;
672
673 l = eina_list_data_find_list(m->items, rel);
674 m->items = eina_list_append_relative_list(m->items, mi, l);
675 mi->list_position = l->next;
676 }
677 else
678 {
679 m->items = eina_list_prepend(m->items, mi);
680 mi->list_position = m->items;
681 }
682
683 return mi;
684 }
685
686 E_API E_Menu_Item *
e_menu_item_nth(E_Menu * m,int n)687 e_menu_item_nth(E_Menu *m, int n)
688 {
689 E_OBJECT_CHECK_RETURN(m, NULL);
690 E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, NULL);
691 return (E_Menu_Item *)eina_list_nth(m->items, n);
692 }
693
694 E_API int
e_menu_item_num_get(const E_Menu_Item * mi)695 e_menu_item_num_get(const E_Menu_Item *mi)
696 {
697 const Eina_List *l;
698 const E_Menu_Item *mi2;
699 int i = 0;
700
701 E_OBJECT_CHECK_RETURN(mi, -1);
702 E_OBJECT_CHECK_RETURN(mi->menu, -1);
703 E_OBJECT_TYPE_CHECK_RETURN(mi, E_MENU_TYPE, -1);
704 EINA_LIST_FOREACH(mi->menu->items, l, mi2)
705 {
706 if (mi2 == mi) return i;
707 i++;
708 }
709 return -1;
710 }
711
712 E_API void
e_menu_item_icon_file_set(E_Menu_Item * mi,const char * icon)713 e_menu_item_icon_file_set(E_Menu_Item *mi, const char *icon)
714 {
715 E_OBJECT_CHECK(mi);
716 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
717 if (((mi->icon) && (icon) && (!strcmp(icon, mi->icon))) ||
718 ((!mi->icon) && (!icon)))
719 return;
720 if (mi->icon) eina_stringshare_del(mi->icon);
721 if (mi->icon_key) eina_stringshare_del(mi->icon_key);
722 mi->icon = NULL;
723 mi->icon_key = NULL;
724 if (icon)
725 {
726 mi->icon = eina_stringshare_add(icon);
727 if (eina_str_has_extension(mi->icon, ".edj"))
728 mi->icon_key = eina_stringshare_add("icon");
729 }
730 mi->changed = 1;
731 mi->menu->changed = 1;
732 }
733
734 E_API void
e_menu_item_icon_edje_set(E_Menu_Item * mi,const char * icon,const char * key)735 e_menu_item_icon_edje_set(E_Menu_Item *mi, const char *icon, const char *key)
736 {
737 E_OBJECT_CHECK(mi);
738 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
739 if (((mi->icon) && (icon) && (!strcmp(icon, mi->icon))) ||
740 ((!mi->icon) && (!icon)) ||
741 ((key) && (mi->icon_key) && (!strcmp(key, mi->icon_key))))
742 return;
743 eina_stringshare_replace(&mi->icon, icon);
744 eina_stringshare_replace(&mi->icon_key, key);
745 mi->changed = 1;
746 mi->menu->changed = 1;
747 }
748
749 E_API void
e_menu_item_label_set(E_Menu_Item * mi,const char * label)750 e_menu_item_label_set(E_Menu_Item *mi, const char *label)
751 {
752 E_OBJECT_CHECK(mi);
753 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
754 if (((mi->label) && (label) && (!strcmp(label, mi->label))) ||
755 ((!mi->label) && (!label)))
756 return;
757 if (mi->label) eina_stringshare_del(mi->label);
758 mi->label = NULL;
759 if (label) mi->label = eina_stringshare_add(label);
760 mi->changed = 1;
761 mi->menu->changed = 1;
762 }
763
764 E_API void
e_menu_item_submenu_set(E_Menu_Item * mi,E_Menu * sub)765 e_menu_item_submenu_set(E_Menu_Item *mi, E_Menu *sub)
766 {
767 Eina_Bool submenu = EINA_FALSE;
768 Evas_Object *o;
769 Eina_List *tmp = NULL;
770 int ww, hh;
771 E_OBJECT_CHECK(mi);
772 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
773
774 if (mi->submenu == sub) return;
775 tmp = _e_active_menus_copy_ref();
776 submenu = !!mi->submenu;
777 if (mi->submenu) e_object_unref(E_OBJECT(mi->submenu));
778 if (sub) e_object_ref(E_OBJECT(sub));
779 mi->submenu = sub;
780 mi->changed = 1;
781 mi->menu->changed = 1;
782 if (!!sub == submenu) goto out;
783 if (!mi->bg_object) goto out;
784 if (sub) e_object_ref(E_OBJECT(sub));
785 _e_menu_lock = EINA_TRUE;
786 if ((mi->submenu) || (mi->submenu_pre_cb.func))
787 {
788 if (mi->submenu_object)
789 {
790 if (isedje(mi->submenu_object))
791 {
792 /* already have a correct submenu object, don't re-set it */
793 _e_menu_lock = EINA_FALSE;
794 if (sub) e_object_unref(E_OBJECT(sub));
795 edje_object_size_min_calc(mi->submenu_object, &ww, &hh);
796 mi->submenu_w = ww;
797 mi->submenu_h = hh;
798 E_WEIGHT(mi->submenu_object, 0, 1);
799 E_FILL(mi->submenu_object);
800 evas_object_size_hint_min_set(mi->submenu_object, ww, hh);
801 goto out;
802 }
803 evas_object_del(mi->submenu_object);
804 }
805 o = edje_object_add(mi->menu->evas);
806 if (sub && (mi->submenu != sub)) e_object_ref(E_OBJECT(sub));
807 mi->submenu = sub;
808 mi->submenu_object = o;
809 e_theme_edje_object_set(o, "base/theme/menus",
810 "e/widgets/menu/default/submenu");
811 evas_object_pass_events_set(o, 1);
812 elm_box_pack_end(mi->container_object, o);
813 evas_object_show(o);
814 edje_object_size_min_calc(mi->submenu_object, &ww, &hh);
815 mi->submenu_w = ww;
816 mi->submenu_h = hh;
817 E_WEIGHT(mi->submenu_object, 0, 1);
818 E_FILL(mi->submenu_object);
819 evas_object_size_hint_min_set(mi->submenu_object, ww, hh);
820 edje_object_part_swallow(mi->bg_object, "e.swallow.content",
821 mi->container_object);
822 edje_object_size_min_calc(mi->bg_object, &ww, &hh);
823 E_WEIGHT(mi->bg_object, 1, 0);
824 E_FILL(mi->bg_object);
825 evas_object_size_hint_min_set(mi->bg_object, ww, hh);
826 }
827 else
828 {
829 if (mi->submenu_object) evas_object_del(mi->submenu_object);
830 o = evas_object_rectangle_add(mi->menu->evas);
831 mi->submenu_object = o;
832 evas_object_color_set(o, 0, 0, 0, 0);
833 evas_object_pass_events_set(o, 1);
834 elm_box_pack_end(mi->container_object, o);
835 }
836 _e_menu_lock = EINA_FALSE;
837 if (sub) e_object_unref(E_OBJECT(sub));
838 if ((mi->submenu) || (mi->submenu_pre_cb.func))
839 {
840 if (e_theme_edje_object_set(mi->bg_object, "base/theme/menus",
841 "e/widgets/menu/default/submenu_bg"))
842 goto out;
843 }
844
845 e_theme_edje_object_set(mi->bg_object, "base/theme/menus",
846 "e/widgets/menu/default/item_bg");
847 out:
848 _e_menu_list_free_unref(tmp);
849 }
850
851 E_API void
e_menu_item_separator_set(E_Menu_Item * mi,int sep)852 e_menu_item_separator_set(E_Menu_Item *mi, int sep)
853 {
854 E_OBJECT_CHECK(mi);
855 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
856 if (((mi->separator) && (sep)) || ((!mi->separator) && (!sep))) return;
857 mi->separator = sep;
858 mi->changed = 1;
859 mi->menu->changed = 1;
860 }
861
862 E_API void
e_menu_item_check_set(E_Menu_Item * mi,int chk)863 e_menu_item_check_set(E_Menu_Item *mi, int chk)
864 {
865 E_OBJECT_CHECK(mi);
866 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
867 if (((mi->check) && (chk)) || ((!mi->check) && (!chk))) return;
868 mi->check = chk;
869 mi->changed = 1;
870 mi->menu->changed = 1;
871 }
872
873 E_API void
e_menu_item_radio_set(E_Menu_Item * mi,int rad)874 e_menu_item_radio_set(E_Menu_Item *mi, int rad)
875 {
876 E_OBJECT_CHECK(mi);
877 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
878 if (((mi->radio) && (rad)) || ((!mi->radio) && (!rad))) return;
879 mi->radio = rad;
880 mi->changed = 1;
881 mi->menu->changed = 1;
882 }
883
884 E_API void
e_menu_item_radio_group_set(E_Menu_Item * mi,int radg)885 e_menu_item_radio_group_set(E_Menu_Item *mi, int radg)
886 {
887 E_OBJECT_CHECK(mi);
888 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
889 if (mi->radio_group == radg) return;
890 mi->radio_group = radg;
891 mi->changed = 1;
892 mi->menu->changed = 1;
893 }
894
895 E_API void
e_menu_item_toggle_set(E_Menu_Item * mi,int tog)896 e_menu_item_toggle_set(E_Menu_Item *mi, int tog)
897 {
898 E_OBJECT_CHECK(mi);
899 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
900 if (mi->separator) return;
901 if (tog)
902 {
903 mi->toggle = 1;
904 if (mi->bg_object)
905 edje_object_signal_emit(mi->bg_object, "e,state,on", "e");
906 if (mi->icon_bg_object)
907 edje_object_signal_emit(mi->icon_bg_object, "e,state,on", "e");
908 if (isedje(mi->label_object))
909 edje_object_signal_emit(mi->label_object, "e,state,on", "e");
910 if (isedje(mi->submenu_object))
911 edje_object_signal_emit(mi->submenu_object, "e,state,on", "e");
912 if (isedje(mi->toggle_object))
913 edje_object_signal_emit(mi->toggle_object, "e,state,on", "e");
914 if (mi->menu->bg_object)
915 edje_object_signal_emit(mi->menu->bg_object, "e,state,on", "e");
916 }
917 else
918 {
919 mi->toggle = 0;
920 if (mi->bg_object)
921 edje_object_signal_emit(mi->bg_object, "e,state,off", "e");
922 if (mi->icon_bg_object)
923 edje_object_signal_emit(mi->icon_bg_object, "e,state,off", "e");
924 if (isedje(mi->label_object))
925 edje_object_signal_emit(mi->label_object, "e,state,off", "e");
926 if (isedje(mi->submenu_object))
927 edje_object_signal_emit(mi->submenu_object, "e,state,off", "e");
928 if (isedje(mi->toggle_object))
929 edje_object_signal_emit(mi->toggle_object, "e,state,off", "e");
930 if (mi->menu->bg_object)
931 edje_object_signal_emit(mi->menu->bg_object, "e,state,off", "e");
932 }
933 if (tog)
934 {
935 if (mi->radio)
936 {
937 const Eina_List *l;
938 E_Menu_Item *mi2;
939
940 EINA_LIST_FOREACH(mi->menu->items, l, mi2)
941 {
942 if ((mi2 != mi) && (mi2->radio) &&
943 (mi2->radio_group == mi->radio_group))
944 e_menu_item_toggle_set(mi2, 0);
945 }
946 }
947 }
948 }
949
950 E_API int
e_menu_item_toggle_get(E_Menu_Item * mi)951 e_menu_item_toggle_get(E_Menu_Item *mi)
952 {
953 E_OBJECT_CHECK_RETURN(mi, 0);
954 E_OBJECT_TYPE_CHECK_RETURN(mi, E_MENU_ITEM_TYPE, 0);
955 return mi->toggle;
956 }
957
958 E_API void
e_menu_item_callback_set(E_Menu_Item * mi,void (* func)(void * data,E_Menu * m,E_Menu_Item * mi),const void * data)959 e_menu_item_callback_set(E_Menu_Item *mi, void (*func)(void *data, E_Menu *m, E_Menu_Item *mi), const void *data)
960 {
961 E_OBJECT_CHECK(mi);
962 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
963 mi->cb.func = func;
964 mi->cb.data = (void *)data;
965 }
966
967 E_API void
e_menu_item_realize_callback_set(E_Menu_Item * mi,void (* func)(void * data,E_Menu * m,E_Menu_Item * mi),void * data)968 e_menu_item_realize_callback_set(E_Menu_Item *mi, void (*func)(void *data, E_Menu *m, E_Menu_Item *mi), void *data)
969 {
970 E_OBJECT_CHECK(mi);
971 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
972 mi->realize_cb.func = func;
973 mi->realize_cb.data = data;
974 }
975
976 E_API void
e_menu_item_submenu_pre_callback_set(E_Menu_Item * mi,void (* func)(void * data,E_Menu * m,E_Menu_Item * mi),const void * data)977 e_menu_item_submenu_pre_callback_set(E_Menu_Item *mi, void (*func)(void *data, E_Menu *m, E_Menu_Item *mi), const void *data)
978 {
979 E_OBJECT_CHECK(mi);
980 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
981 mi->submenu_pre_cb.func = func;
982 mi->submenu_pre_cb.data = (void *)data;
983 if (!mi->submenu_post_cb.func)
984 mi->submenu_post_cb.func = _e_menu_cb_item_submenu_post_default;
985 }
986
987 E_API void
e_menu_item_submenu_post_callback_set(E_Menu_Item * mi,void (* func)(void * data,E_Menu * m,E_Menu_Item * mi),const void * data)988 e_menu_item_submenu_post_callback_set(E_Menu_Item *mi, void (*func)(void *data, E_Menu *m, E_Menu_Item *mi), const void *data)
989 {
990 E_OBJECT_CHECK(mi);
991 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
992 mi->submenu_post_cb.func = func;
993 mi->submenu_post_cb.data = (void *)data;
994 }
995
996 E_API void
e_menu_item_drag_callback_set(E_Menu_Item * mi,void (* func)(void * data,E_Menu * m,E_Menu_Item * mi),void * data)997 e_menu_item_drag_callback_set(E_Menu_Item *mi, void (*func)(void *data, E_Menu *m, E_Menu_Item *mi), void *data)
998 {
999 E_OBJECT_CHECK(mi);
1000 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
1001 mi->drag_cb.func = func;
1002 mi->drag_cb.data = data;
1003 }
1004
1005 E_API void
e_menu_item_active_set(E_Menu_Item * mi,int active)1006 e_menu_item_active_set(E_Menu_Item *mi, int active)
1007 {
1008 Eina_List *tmp = NULL;
1009
1010 E_OBJECT_CHECK(mi);
1011 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
1012 if (mi->separator) return;
1013 active = !!active;
1014 if (active == mi->active) return;
1015 if ((active) && (!mi->active))
1016 {
1017 E_Menu_Item *pmi;
1018
1019 if (mi->disable) return;
1020 pmi = _e_menu_item_active_get();
1021 if (mi == pmi) return;
1022 if (pmi)
1023 {
1024 tmp = _e_active_menus_copy_ref();
1025 e_menu_item_active_set(pmi, 0);
1026 }
1027 if (_e_prev_active_menu_item && (mi != _e_prev_active_menu_item))
1028 {
1029 if (mi->menu->parent_item && (_e_prev_active_menu_item != mi->menu->parent_item))
1030 _e_menu_submenu_deactivate(_e_prev_active_menu_item);
1031 }
1032 mi->active = 1;
1033 _e_active_menu_item = mi;
1034 if (mi->bg_object)
1035 edje_object_signal_emit(mi->bg_object, "e,state,selected", "e");
1036 if (mi->icon_bg_object)
1037 edje_object_signal_emit(mi->icon_bg_object, "e,state,selected", "e");
1038 if (isedje(mi->label_object))
1039 edje_object_signal_emit(mi->label_object, "e,state,selected", "e");
1040 if (isedje(mi->submenu_object))
1041 edje_object_signal_emit(mi->submenu_object, "e,state,selected", "e");
1042 if (isedje(mi->toggle_object))
1043 edje_object_signal_emit(mi->toggle_object, "e,state,selected", "e");
1044 if (mi->icon_key)
1045 {
1046 if (mi->icon_object)
1047 {
1048 if (isedje(mi->icon_object))
1049 edje_object_signal_emit(mi->icon_object, "e,state,selected", "e");
1050 else
1051 e_icon_selected_set(mi->icon_object, EINA_TRUE);
1052 }
1053 }
1054 edje_object_signal_emit(mi->menu->bg_object, "e,state,selected", "e");
1055 _e_menu_submenu_activate(mi);
1056 }
1057 else if ((!active) && (mi->active))
1058 {
1059 tmp = _e_active_menus_copy_ref();
1060 mi->active = 0;
1061 _e_prev_active_menu_item = mi;
1062 _e_active_menu_item = NULL;
1063 if (mi->bg_object)
1064 edje_object_signal_emit(mi->bg_object, "e,state,unselected", "e");
1065 if (mi->icon_bg_object)
1066 edje_object_signal_emit(mi->icon_bg_object, "e,state,unselected", "e");
1067 if (isedje(mi->label_object))
1068 edje_object_signal_emit(mi->label_object, "e,state,unselected", "e");
1069 if (isedje(mi->submenu_object))
1070 edje_object_signal_emit(mi->submenu_object, "e,state,unselected", "e");
1071 if (isedje(mi->toggle_object))
1072 edje_object_signal_emit(mi->toggle_object, "e,state,unselected", "e");
1073 if (mi->icon_key)
1074 {
1075 if (mi->icon_object)
1076 {
1077 if (isedje(mi->icon_object))
1078 edje_object_signal_emit(mi->icon_object, "e,state,unselected", "e");
1079 else
1080 e_icon_selected_set(mi->icon_object, EINA_FALSE);
1081 }
1082 }
1083 edje_object_signal_emit(mi->menu->bg_object, "e,state,unselected", "e");
1084 }
1085 _e_menu_list_free_unref(tmp);
1086 }
1087
1088 E_API E_Menu_Item *
e_menu_item_active_get(void)1089 e_menu_item_active_get(void)
1090 {
1091 return _e_active_menu_item;
1092 }
1093
1094 E_API void
e_menu_active_item_activate(void)1095 e_menu_active_item_activate(void)
1096 {
1097 _e_menu_active_call();
1098 _e_menu_deactivate_all();
1099 }
1100
1101 E_API void
e_menu_item_disabled_set(E_Menu_Item * mi,int disable)1102 e_menu_item_disabled_set(E_Menu_Item *mi, int disable)
1103 {
1104 E_OBJECT_CHECK(mi);
1105 E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
1106 if (mi->separator) return;
1107 if ((disable))
1108 {
1109 if (mi->active) e_menu_item_active_set(mi, 0);
1110 mi->disable = 1;
1111 if (mi->icon_bg_object)
1112 edje_object_signal_emit(mi->icon_bg_object, "e,state,disable", "e");
1113 if (isedje(mi->label_object))
1114 edje_object_signal_emit(mi->label_object, "e,state,disable", "e");
1115 if (isedje(mi->toggle_object))
1116 edje_object_signal_emit(mi->toggle_object, "e,state,disable", "e");
1117 }
1118 else
1119 {
1120 mi->disable = 0;
1121 if (mi->icon_bg_object)
1122 edje_object_signal_emit(mi->icon_bg_object, "e,state,enable", "e");
1123 if (isedje(mi->label_object))
1124 edje_object_signal_emit(mi->label_object, "e,state,enable", "e");
1125 if (isedje(mi->toggle_object))
1126 edje_object_signal_emit(mi->toggle_object, "e,state,enable", "e");
1127 }
1128 }
1129
1130 E_API void
e_menu_idler_before(void)1131 e_menu_idler_before(void)
1132 {
1133 /* when e goes "idle" this gets called so leave all our hard work till */
1134 /* idle time to avoid falling behind the user. just evaluate the high */
1135 /* level state machine */
1136 Eina_List *l, *removals = NULL, *tmp;
1137 E_Menu *m;
1138 int active_count = 0;
1139
1140 if (pending_feed)
1141 {
1142 e_comp_canvas_feed_mouse_up(pending_activate_time);
1143 pending_feed = 0;
1144 }
1145
1146 /* add refcount to all menus we will work with */
1147 tmp = _e_active_menus_copy_ref();
1148 /* phase 1. hide all the menus that want to be hidden */
1149 EINA_LIST_FOREACH(_e_active_menus, l, m)
1150 {
1151 if ((!m->cur.visible) && (m->prev.visible))
1152 {
1153 m->prev.visible = m->cur.visible;
1154 e_object_ref(E_OBJECT(m));
1155 evas_object_pass_events_set(m->comp_object, 1);
1156 if (m->container_object)
1157 {
1158 evas_object_intercept_move_callback_del(m->container_object, _e_menu_cb_intercept_container_move);
1159 evas_object_intercept_resize_callback_del(m->container_object, _e_menu_cb_intercept_container_resize);
1160 }
1161 evas_object_hide(m->comp_object);
1162 }
1163 }
1164 /* phase 2. move & reisze all the menus that want to moves/resized */
1165 EINA_LIST_FOREACH(_e_active_menus, l, m)
1166 {
1167 if (m->frozen || (!m->active) || (!m->zone)) continue;
1168 if (!m->realized) _e_menu_realize(m);
1169 if (!m->realized) continue;
1170 if (((m->cur.w) != (m->prev.w)) ||
1171 ((m->cur.h) != (m->prev.h)))
1172 {
1173 int w, h;
1174
1175 m->prev.w = m->cur.w;
1176 m->prev.h = m->cur.h;
1177 w = m->cur.w;
1178 h = m->cur.h;
1179 evas_object_resize(m->comp_object, w, h);
1180 }
1181 if (((m->cur.x) != (m->prev.x)) ||
1182 ((m->cur.y) != (m->prev.y)))
1183 {
1184 if (!m->parent_item)
1185 {
1186 int x, y, w, h;
1187
1188 e_zone_useful_geometry_get(m->zone, &x, &y, &w, &h);
1189 if (m->cur.w <= w)
1190 {
1191 if ((m->cur.x + m->cur.w) > (x + w))
1192 m->cur.x = x + w - m->cur.w;
1193 }
1194 if (m->cur.h <= h)
1195 {
1196 if ((m->cur.y + m->cur.h) > (y + h))
1197 m->cur.y = y + h - m->cur.h;
1198 }
1199 }
1200 m->prev.x = m->cur.x;
1201 m->prev.y = m->cur.y;
1202 _e_menu_lock = 1;
1203 evas_object_move(m->comp_object, m->cur.x, m->cur.y);
1204 _e_menu_lock = 0;
1205 }
1206 }
1207 /* phase 3. show all the menus that want to be shown */
1208 EINA_LIST_FOREACH(_e_active_menus, l, m)
1209 {
1210 if (m->frozen) continue;
1211 if (!m->realized) continue;
1212 if (m->cur.visible)
1213 {
1214 m->prev.visible = m->cur.visible;
1215 evas_object_pass_events_set(m->comp_object, 0);
1216 evas_object_show(m->comp_object);
1217 }
1218 }
1219 /* phase 4. de-activate... */
1220 EINA_LIST_REVERSE_FOREACH(_e_active_menus, l, m)
1221 {
1222 if (!m->active)
1223 {
1224 if ((m->realized) &&
1225 (!evas_object_visible_get(m->comp_object)))
1226 {
1227 _e_menu_unrealize(m);
1228 removals = eina_list_append(removals, m);
1229 }
1230 }
1231 }
1232 EINA_LIST_FREE(removals, m)
1233 {
1234 if (m->in_active_list)
1235 {
1236 _e_active_menus = eina_list_remove(_e_active_menus, m);
1237 m->in_active_list = 0;
1238 e_object_unref(E_OBJECT(m));
1239 }
1240 }
1241 /* del refcount to all menus we worked with */
1242 _e_menu_list_free_unref(tmp);
1243
1244 EINA_LIST_FOREACH(_e_active_menus, l, m)
1245 {
1246 if (m->active) active_count++;
1247 }
1248 if (!active_count)
1249 {
1250 if (_e_menu_win == e_comp->ee_win)
1251 {
1252 if (_e_menu_grabbed)
1253 {
1254 e_comp_ungrab_input(1, 1);
1255 _e_menu_grabbed = EINA_FALSE;
1256 }
1257 _e_menu_win = UINT_MAX;
1258 e_bindings_disabled_set(0);
1259 }
1260 }
1261 }
1262
1263 E_API Eina_Bool
e_menu_is_active(void)1264 e_menu_is_active(void)
1265 {
1266 return _e_menu_win == e_comp->ee_win;
1267 }
1268
1269 E_API E_Menu *
e_menu_active_get(void)1270 e_menu_active_get(void)
1271 {
1272 return _e_active_menus ? eina_list_last_data_get(_e_active_menus) : NULL;
1273 }
1274
1275 /* local subsystem functions */
1276 static void
_e_menu_dangling_cb(void * data)1277 _e_menu_dangling_cb(void *data)
1278 {
1279 E_Menu *m = data;
1280
1281 WRN("DANGLING SUBMENU: REF(%d)||MENU(%p)", e_object_ref_get(data), data);
1282 m->dangling_job = NULL;
1283 }
1284
1285 static void
_e_menu_free(E_Menu * m)1286 _e_menu_free(E_Menu *m)
1287 {
1288 Eina_List *l, *l_next;
1289 E_Menu_Item *mi;
1290 E_Menu_Category *cat = NULL;
1291
1292 /* the foreign menu items */
1293 if (m->category) cat = eina_hash_find(_e_menu_categories, m->category);
1294 if (cat)
1295 {
1296 E_Menu_Category_Callback *cb;
1297
1298 EINA_LIST_FOREACH(cat->callbacks, l, cb)
1299 {
1300 if (cb->free) cb->free(cb->data);
1301 }
1302 }
1303 eina_stringshare_replace(&m->category, NULL);
1304 if (m->parent_item)
1305 m->parent_item->submenu = NULL;
1306 _e_menu_unrealize(m);
1307 if (m->realized) return;
1308 EINA_LIST_FOREACH_SAFE(m->items, l, l_next, mi)
1309 e_object_del(E_OBJECT(mi));
1310 if (m->in_active_list)
1311 {
1312 _e_active_menus = eina_list_remove(_e_active_menus, m);
1313 m->in_active_list = 0;
1314 }
1315 if (m->header.title) eina_stringshare_del(m->header.title);
1316 if (m->header.icon_file) eina_stringshare_del(m->header.icon_file);
1317 if (m->dangling_job) ecore_job_del(m->dangling_job);
1318 free(m);
1319 }
1320
1321 static void
_e_menu_item_free(E_Menu_Item * mi)1322 _e_menu_item_free(E_Menu_Item *mi)
1323 {
1324 if (mi == _e_active_menu_item) _e_active_menu_item = NULL;
1325 if (mi == _e_prev_active_menu_item) _e_prev_active_menu_item = NULL;
1326 if (mi->submenu)
1327 {
1328 int ref = 0;
1329
1330 /* parent_item gets unset in a few places, reapply it for use in cleanup */
1331 if (!mi->submenu->parent_item)
1332 mi->submenu->parent_item = mi;
1333 /* menu may not have been deactivated, ensure deactivate callback is called */
1334 if (mi->active)
1335 _e_menu_submenu_deactivate(mi);
1336 /* submenus CANNOT exist without their parent menu+item, so ensure that they get deleted */
1337 if (mi->submenu)
1338 {
1339 ref = e_object_ref_get(E_OBJECT(mi->submenu)) - 1;
1340 e_object_unref(E_OBJECT(mi->submenu));
1341 }
1342 if (ref)
1343 {
1344 if (!mi->submenu->dangling_job)
1345 mi->submenu->dangling_job = ecore_job_add(_e_menu_dangling_cb, mi->submenu);
1346 mi->submenu->parent_item = NULL;
1347 }
1348 }
1349 if (mi->menu->realized) _e_menu_item_unrealize(mi);
1350 mi->menu->items = eina_list_remove(mi->menu->items, mi);
1351 if (mi->icon) eina_stringshare_del(mi->icon);
1352 if (mi->icon_key) eina_stringshare_del(mi->icon_key);
1353 if (mi->label) eina_stringshare_del(mi->label);
1354 free(mi);
1355 }
1356
1357 static void
_e_menu_cb_intercept_item_move(void * data,Evas_Object * o,Evas_Coord x,Evas_Coord y)1358 _e_menu_cb_intercept_item_move(void *data, Evas_Object *o, Evas_Coord x, Evas_Coord y)
1359 {
1360 E_Menu_Item *mi;
1361
1362 mi = data;
1363 mi->x = x;
1364 mi->y = y;
1365 evas_object_move(o, x, y);
1366 if ((mi->submenu) && (mi->submenu->parent_item))
1367 {
1368 mi->submenu->zone = mi->menu->zone;
1369 _e_menu_reposition(mi->submenu);
1370 }
1371 }
1372
1373 static void
_e_menu_cb_intercept_item_resize(void * data,Evas_Object * o,Evas_Coord w,Evas_Coord h)1374 _e_menu_cb_intercept_item_resize(void *data, Evas_Object *o, Evas_Coord w, Evas_Coord h)
1375 {
1376 E_Menu_Item *mi;
1377
1378 mi = data;
1379 mi->w = w;
1380 mi->h = h;
1381 evas_object_resize(o, w, h);
1382 if ((mi->submenu) && (mi->submenu->parent_item))
1383 _e_menu_reposition(mi->submenu);
1384 }
1385
1386 static void
_e_menu_cb_intercept_container_move(void * data,Evas_Object * o,Evas_Coord x,Evas_Coord y)1387 _e_menu_cb_intercept_container_move(void *data, Evas_Object *o, Evas_Coord x, Evas_Coord y)
1388 {
1389 E_Menu *m;
1390
1391 m = data;
1392 m->container_x = x;
1393 m->container_y = y;
1394 if (m->parent_item) _e_menu_reposition(m);
1395 evas_object_move(o, x, y);
1396 }
1397
1398 static void
_e_menu_cb_intercept_container_resize(void * data,Evas_Object * o,Evas_Coord w,Evas_Coord h)1399 _e_menu_cb_intercept_container_resize(void *data, Evas_Object *o, Evas_Coord w, Evas_Coord h)
1400 {
1401 E_Menu *m;
1402
1403 m = data;
1404 m->container_w = w;
1405 m->container_h = h;
1406 if (m->parent_item) _e_menu_reposition(m);
1407 evas_object_resize(o, w, h);
1408 }
1409
1410 static void
_e_menu_hide_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)1411 _e_menu_hide_cb(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
1412 {
1413 e_object_unref(data);
1414 }
1415
1416 static void
_e_menu_item_realize(E_Menu_Item * mi)1417 _e_menu_item_realize(E_Menu_Item *mi)
1418 {
1419 Evas_Object *o;
1420 Evas_Coord ww = 1, hh = 1;
1421
1422 /* and set up initial item state */
1423 if (mi->separator)
1424 {
1425 o = edje_object_add(mi->menu->evas);
1426 mi->separator_object = o;
1427 e_theme_edje_object_set(o, "base/theme/menus",
1428 "e/widgets/menu/default/separator");
1429 edje_object_size_min_calc(mi->separator_object, &ww, &hh);
1430 E_FILL(mi->separator_object);
1431 mi->separator_w = ww;
1432 mi->separator_h = hh;
1433 evas_object_size_hint_min_set(mi->separator_object, ww, hh);
1434 elm_box_pack_end(mi->menu->container_object, mi->separator_object);
1435 evas_object_show(o);
1436 }
1437 else
1438 {
1439 o = edje_object_add(mi->menu->evas);
1440 mi->bg_object = o;
1441 evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_IN, _e_menu_cb_item_in, mi);
1442 evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_OUT, _e_menu_cb_item_out, mi);
1443 evas_object_intercept_move_callback_add(o, _e_menu_cb_intercept_item_move, mi);
1444 evas_object_intercept_resize_callback_add(o, _e_menu_cb_intercept_item_resize, mi);
1445 if ((mi->submenu) || (mi->submenu_pre_cb.func))
1446 {
1447 if (!e_theme_edje_object_set(mi->bg_object, "base/theme/menus",
1448 "e/widgets/menu/default/submenu_bg"))
1449 goto no_submenu_item;
1450 }
1451 else
1452 {
1453 no_submenu_item:
1454 e_theme_edje_object_set(mi->bg_object, "base/theme/menus",
1455 "e/widgets/menu/default/item_bg");
1456 }
1457 o = elm_box_add(e_comp->elm);
1458 mi->container_object = o;
1459 elm_box_horizontal_set(o, 1);
1460 evas_object_show(o);
1461
1462
1463 if (mi->check)
1464 {
1465 o = edje_object_add(mi->menu->evas);
1466 mi->toggle_object = o;
1467 e_theme_edje_object_set(o, "base/theme/menus",
1468 "e/widgets/menu/default/check");
1469 edje_object_size_min_calc(mi->toggle_object, &ww, &hh);
1470 mi->toggle_w = ww;
1471 mi->toggle_h = hh;
1472 E_WEIGHT(mi->toggle_object, 0, 1);
1473 E_FILL(mi->toggle_object);
1474 evas_object_size_hint_min_set(mi->toggle_object, ww, hh);
1475 elm_box_pack_end(mi->container_object, o);
1476 evas_object_show(o);
1477 }
1478 else if (mi->radio)
1479 {
1480 o = edje_object_add(mi->menu->evas);
1481 mi->toggle_object = o;
1482 e_theme_edje_object_set(o, "base/theme/menus",
1483 "e/widgets/menu/default/radio");
1484 edje_object_size_min_calc(mi->toggle_object, &ww, &hh);
1485 mi->toggle_w = ww;
1486 mi->toggle_h = hh;
1487 E_WEIGHT(mi->toggle_object, 0, 1);
1488 E_FILL(mi->toggle_object);
1489 evas_object_size_hint_min_set(mi->toggle_object, ww, hh);
1490 elm_box_pack_end(mi->container_object, o);
1491 evas_object_show(o);
1492 }
1493 else
1494 {
1495 o = evas_object_rectangle_add(mi->menu->evas);
1496 mi->toggle_object = o;
1497 evas_object_color_set(o, 0, 0, 0, 0);
1498 elm_box_pack_end(mi->container_object, o);
1499 }
1500 if ((!e_config->menu_icons_hide) && ((mi->icon) || (mi->realize_cb.func)))
1501 {
1502 int icon_w = 0, icon_h = 0;
1503
1504 o = edje_object_add(mi->menu->evas);
1505 if (e_theme_edje_object_set(o, "base/theme/menus",
1506 "e/widgets/menu/default/icon"))
1507 {
1508 mi->icon_bg_object = o;
1509 }
1510 else
1511 {
1512 evas_object_del(o);
1513 o = NULL;
1514 }
1515 //if (o) evas_object_pass_events_set(o, 1);
1516
1517 /* FIXME: Not sure why there are two different tries to get the icon size, surely only the last one is needed. */
1518 /* FIXME: Do it this way later, when e_app_icon_add() just registers a request for an icon to be filled in when it's ready.
1519 if (mi->app)
1520 {
1521 o = e_app_icon_add(mi->menu->evas, mi->app);
1522 mi->icon_object = o;
1523 e_icon_size_get(mi->icon_object, &icon_w, &icon_h);
1524 }
1525 else
1526 */
1527 if (mi->icon)
1528 {
1529 /* This is done this way to match up with how e_app_icon_add does it. */
1530 if (mi->icon_key)
1531 {
1532 Evas_Coord iww, ihh;
1533
1534 o = edje_object_add(mi->menu->evas);
1535 if (edje_object_file_set(o, mi->icon, mi->icon_key))
1536 {
1537 mi->icon_object = o;
1538 edje_object_size_max_get(o, &iww, &ihh);
1539 icon_w = iww;
1540 icon_h = ihh;
1541 }
1542 else
1543 {
1544 evas_object_del(o);
1545 o = NULL;
1546 }
1547 }
1548 if (!mi->icon_object)
1549 {
1550 o = e_icon_add(mi->menu->evas);
1551 mi->icon_object = o;
1552 e_icon_scale_size_set(o, e_util_icon_size_normalize(96 * e_scale));
1553 e_icon_preload_set(mi->icon_object, 1);
1554 e_icon_file_set(o, mi->icon);
1555 e_icon_fill_inside_set(mi->icon_object, 1);
1556 e_icon_size_get(mi->icon_object, &icon_w, &icon_h);
1557 }
1558 }
1559 if (_e_menu_realize_call(mi))
1560 {
1561 o = mi->icon_object;
1562 e_icon_fill_inside_set(o, 1);
1563 e_icon_size_get(o, &icon_w, &icon_h);
1564 }
1565
1566 evas_object_show(o);
1567
1568 if (mi->icon_bg_object)
1569 {
1570 evas_object_size_hint_min_set(mi->icon_object, 0, 0);
1571 edje_object_part_swallow(mi->icon_bg_object,
1572 "e.swallow.content",
1573 mi->icon_object);
1574 edje_object_size_min_calc(mi->icon_bg_object, &ww, &hh);
1575 mi->icon_w = ww;
1576 mi->icon_h = hh;
1577 evas_object_size_hint_min_set(mi->icon_bg_object, ww, hh);
1578 E_WEIGHT(mi->icon_bg_object, 0, 1);
1579 E_FILL(mi->icon_bg_object);
1580 elm_box_pack_end(mi->container_object, mi->icon_bg_object);
1581 evas_object_show(mi->icon_bg_object);
1582 }
1583 else
1584 {
1585 o = edje_object_add(mi->menu->evas);
1586 e_icon_size_get(mi->icon_object, &icon_w, &icon_h);
1587 mi->icon_w = icon_w;
1588 mi->icon_h = icon_h;
1589 E_WEIGHT(mi->icon_object, 0, 1);
1590 E_FILL(mi->icon_object);
1591 evas_object_size_hint_min_set(mi->icon_object, ww, hh);
1592 elm_box_pack_end(mi->container_object, o);
1593 evas_object_show(o);
1594 }
1595 }
1596 else
1597 {
1598 o = evas_object_rectangle_add(mi->menu->evas);
1599 mi->icon_object = o;
1600 evas_object_color_set(o, 0, 0, 0, 0);
1601 elm_box_pack_end(mi->container_object, o);
1602 }
1603
1604 if (mi->label)
1605 {
1606 o = edje_object_add(mi->menu->evas);
1607 mi->label_object = o;
1608 e_theme_edje_object_set(o, "base/theme/menus",
1609 "e/widgets/menu/default/label");
1610 /* default label */
1611 edje_object_part_text_set(o, "e.text.label", mi->label);
1612 edje_object_size_min_calc(mi->label_object, &ww, &hh);
1613 mi->label_w = ww;
1614 mi->label_h = hh;
1615 evas_object_size_hint_min_set(mi->label_object, ww, hh);
1616 E_EXPAND(mi->label_object);
1617 E_FILL(mi->label_object);
1618 elm_box_pack_end(mi->container_object, o);
1619 evas_object_show(o);
1620 }
1621 else
1622 {
1623 o = evas_object_rectangle_add(mi->menu->evas);
1624 mi->label_object = o;
1625 evas_object_color_set(o, 0, 0, 0, 0);
1626 elm_box_pack_end(mi->container_object, o);
1627 }
1628 if ((mi->submenu) || (mi->submenu_pre_cb.func))
1629 {
1630 o = edje_object_add(mi->menu->evas);
1631 mi->submenu_object = o;
1632 e_theme_edje_object_set(o, "base/theme/menus",
1633 "e/widgets/menu/default/submenu");
1634 edje_object_size_min_calc(mi->submenu_object, &ww, &hh);
1635 mi->submenu_w = ww;
1636 mi->submenu_h = hh;
1637 E_WEIGHT(mi->submenu_object, 0, 1);
1638 E_FILL(mi->submenu_object);
1639 evas_object_size_hint_min_set(mi->submenu_object, ww, hh);
1640 elm_box_pack_end(mi->container_object, o);
1641 evas_object_show(o);
1642 }
1643 else
1644 {
1645 o = evas_object_rectangle_add(mi->menu->evas);
1646 mi->submenu_object = o;
1647 evas_object_color_set(o, 0, 0, 0, 0);
1648 elm_box_pack_end(mi->container_object, o);
1649 }
1650
1651 edje_object_part_swallow(mi->bg_object, "e.swallow.content",
1652 mi->container_object);
1653
1654 elm_box_pack_end(mi->menu->container_object, mi->bg_object);
1655 evas_object_show(mi->container_object);
1656 evas_object_show(mi->bg_object);
1657 }
1658 if (mi->active) e_menu_item_active_set(mi, 1);
1659 if (mi->toggle) e_menu_item_toggle_set(mi, 1);
1660 if (mi->disable) e_menu_item_disabled_set(mi, 1);
1661 }
1662
1663 static void
_e_menu_realize(E_Menu * m)1664 _e_menu_realize(E_Menu *m)
1665 {
1666 Evas_Object *o;
1667 Eina_List *l;
1668 E_Menu_Item *mi;
1669
1670 if (m->realized || (!m->items)) return;
1671
1672 if (m->parent_item && m->parent_item->menu)
1673 m->zone = m->parent_item->menu->zone;
1674 if (!m->zone) return; //menu not ready!
1675 m->evas = evas_object_evas_get(e_comp->elm);
1676 evas_event_freeze(m->evas);
1677
1678 o = edje_object_add(m->evas);
1679 m->bg_object = o;
1680 e_theme_edje_object_set(o, "base/theme/menus", "e/widgets/menu/default/background");
1681 if (m->header.title)
1682 {
1683 edje_object_part_text_set(o, "e.text.title", m->header.title);
1684 edje_object_signal_emit(o, "e,action,show,title", "e");
1685 edje_object_message_signal_process(o);
1686 }
1687
1688 m->comp_object = e_comp_object_util_add(o, E_COMP_OBJECT_TYPE_MENU);
1689 if (!m->parent_item)
1690 e_comp_object_util_autoclose(m->comp_object, _e_menu_cb_mouse_evas_down, _e_menu_cb_key_down, m);
1691 evas_object_event_callback_add(m->comp_object, EVAS_CALLBACK_HIDE, _e_menu_hide_cb, m);
1692 evas_object_layer_set(m->comp_object, E_LAYER_MENU);
1693 evas_object_geometry_set(m->comp_object, m->cur.x, m->cur.y, m->cur.w, m->cur.h);
1694
1695 o = elm_box_add(e_comp->elm);
1696 m->container_object = o;
1697 evas_object_intercept_move_callback_add(o, _e_menu_cb_intercept_container_move, m);
1698 evas_object_intercept_resize_callback_add(o, _e_menu_cb_intercept_container_resize, m);
1699
1700 EINA_LIST_FOREACH(m->items, l, mi)
1701 _e_menu_item_realize(mi);
1702
1703 edje_object_part_swallow(m->bg_object, "e.swallow.content", m->container_object);
1704
1705 _e_menu_items_layout_update(m);
1706
1707
1708 evas_event_thaw(m->evas);
1709 evas_event_thaw_eval(m->evas);
1710 m->realized = 1;
1711 }
1712
1713 static void
_e_menu_items_layout_update(E_Menu * m)1714 _e_menu_items_layout_update(E_Menu *m)
1715 {
1716 Eina_List *l;
1717 E_Menu_Item *mi;
1718 Evas_Coord bw, bh, mw = 0, mh = 0;
1719 int toggles_on = 0;
1720 int icons_on = 0;
1721 int labels_on = 0;
1722 int submenus_on = 0;
1723 int min_icon_w = 0, min_icon_h = 0;
1724 int min_label_w = 0, min_label_h = 0;
1725 int min_submenu_w = 0, min_submenu_h = 0;
1726 int min_toggle_w = 0, min_toggle_h = 0;
1727 int min_w = 0, min_h = 1;
1728 int zh = 0, ms = 0, maxh = 0;
1729 unsigned int cur_items = 0, max_items = -1;
1730
1731 if (!m->zone) return;
1732 EINA_LIST_FOREACH(m->items, l, mi)
1733 {
1734 if (mi->icon) icons_on = 1;
1735 if (mi->icon_object) icons_on = 1;
1736 if (mi->label) labels_on = 1;
1737 if (mi->submenu) submenus_on = 1;
1738 if (mi->submenu_pre_cb.func) submenus_on = 1;
1739 if (mi->check) toggles_on = 1;
1740 if (mi->radio) toggles_on = 1;
1741
1742 if (mi->icon_w > min_icon_w) min_icon_w = mi->icon_w;
1743 if (mi->icon_h > min_icon_h) min_icon_h = mi->icon_h;
1744 if (mi->label_w > min_label_w) min_label_w = mi->label_w;
1745 if (mi->label_h > min_label_h) min_label_h = mi->label_h;
1746 if (mi->submenu_w > min_submenu_w) min_submenu_w = mi->submenu_w;
1747 if (mi->submenu_h > min_submenu_h) min_submenu_h = mi->submenu_h;
1748 if (mi->toggle_w > min_toggle_w) min_toggle_w = mi->toggle_w;
1749 if (mi->toggle_h > min_toggle_h) min_toggle_h = mi->toggle_h;
1750 }
1751 if (labels_on)
1752 {
1753 if (submenus_on)
1754 {
1755 if (min_label_h < min_submenu_h)
1756 min_label_h = min_submenu_h;
1757 }
1758 if (toggles_on)
1759 {
1760 if (min_label_h < min_toggle_h)
1761 min_label_h = min_toggle_h;
1762 }
1763 if ((icons_on) && (min_icon_h > 0))
1764 {
1765 min_icon_w = (min_icon_w * min_label_h) / min_icon_h;
1766 min_icon_h = min_label_h;
1767 }
1768 min_w = min_label_w + min_icon_w + min_submenu_w + min_toggle_w;
1769 min_h = min_label_h;
1770 }
1771 else if (icons_on)
1772 {
1773 if (submenus_on)
1774 {
1775 if (min_icon_h < min_submenu_h)
1776 min_icon_h = min_submenu_h;
1777 }
1778 if (toggles_on)
1779 {
1780 if (min_icon_h < min_toggle_h)
1781 min_icon_h = min_toggle_h;
1782 }
1783 min_w = min_icon_w + min_toggle_w + min_submenu_w;
1784 min_h = min_icon_h;
1785 }
1786 else if (toggles_on)
1787 {
1788 if (submenus_on)
1789 {
1790 if (min_toggle_h < min_submenu_h)
1791 min_toggle_h = min_submenu_h;
1792 }
1793 min_w = min_toggle_w + min_submenu_w;
1794 min_h = min_toggle_h;
1795 }
1796 if (min_h * eina_list_count(m->items) >= (unsigned int)m->zone->h)
1797 {
1798 e_zone_useful_geometry_get(m->zone, NULL, NULL, NULL, &zh);
1799 maxh = zh * 4;
1800 if (maxh > 30000) maxh = 30000; // 32k x 32k mx coord limit for wins
1801 max_items = (maxh / min_h) - 1;
1802 }
1803 EINA_LIST_FOREACH(m->items, l, mi)
1804 {
1805 if (cur_items >= max_items)
1806 {
1807 _e_menu_item_unrealize(mi);
1808 continue;
1809 }
1810 cur_items++;
1811 if (mi->separator)
1812 {
1813 E_WEIGHT(mi->separator_object, 1, 0);
1814 E_FILL(mi->separator_object);
1815 evas_object_size_hint_min_set(mi->separator_object, mi->separator_w, mi->separator_h);
1816 evas_object_size_hint_max_set(mi->separator_object, -1, mi->separator_h);
1817 ms += mi->separator_h;
1818 continue;
1819 }
1820 E_WEIGHT(mi->toggle_object, 0, toggles_on);
1821 E_FILL(mi->toggle_object);
1822 evas_object_size_hint_min_set(mi->toggle_object, min_toggle_w * toggles_on, min_toggle_h * toggles_on);
1823 if (icons_on)
1824 {
1825 E_WEIGHT(mi->icon_bg_object ?: mi->icon_object, 0, 1);
1826 E_FILL(mi->icon_bg_object ?: mi->icon_object);
1827 evas_object_size_hint_min_set(mi->icon_bg_object ?: mi->icon_object, min_icon_w, min_icon_h);
1828 }
1829 else
1830 {
1831 E_WEIGHT(mi->icon_object, 0, 1);
1832 E_FILL(mi->icon_object);
1833 evas_object_size_hint_min_set(mi->icon_object, 0, 0);
1834 }
1835
1836 E_WEIGHT(mi->label_object, 0, 1 * labels_on);
1837 E_FILL(mi->label_object);
1838 evas_object_size_hint_min_set(mi->label_object, min_label_w * labels_on, min_label_h * labels_on);
1839
1840 E_WEIGHT(mi->submenu_object, 0, 1 * submenus_on);
1841 E_FILL(mi->submenu_object);
1842 evas_object_size_hint_min_set(mi->submenu_object, min_submenu_w * submenus_on, min_submenu_h * submenus_on);
1843
1844 evas_object_size_hint_min_set(mi->container_object,
1845 min_w, min_h);
1846 edje_object_size_min_calc(mi->bg_object, &mw, &mh);
1847 E_WEIGHT(mi->bg_object, 0, 1);
1848 E_FILL(mi->bg_object);
1849 evas_object_size_hint_min_set(mi->bg_object, mw, mh);
1850 ms += mh;
1851 }
1852 elm_box_recalculate(m->container_object);
1853 evas_object_size_hint_min_get(m->container_object, &bw, &bh);
1854 evas_object_size_hint_max_set(m->container_object, bw, bh);
1855 edje_object_size_min_calc(m->bg_object, &mw, &mh);
1856 m->cur.w = mw;
1857 m->cur.h = mh;
1858 }
1859
1860 static void
_e_menu_item_unrealize(E_Menu_Item * mi)1861 _e_menu_item_unrealize(E_Menu_Item *mi)
1862 {
1863 if (mi->separator_object) evas_object_del(mi->separator_object);
1864 mi->separator_object = NULL;
1865 if (mi->bg_object) evas_object_del(mi->bg_object);
1866 mi->bg_object = NULL;
1867 if (mi->container_object) evas_object_del(mi->container_object);
1868 mi->container_object = NULL;
1869 if (mi->toggle_object) evas_object_del(mi->toggle_object);
1870 mi->toggle_object = NULL;
1871 if (mi->icon_bg_object) evas_object_del(mi->icon_bg_object);
1872 mi->icon_bg_object = NULL;
1873 if (mi->icon_object) evas_object_del(mi->icon_object);
1874 mi->icon_object = NULL;
1875 if (mi->label_object) evas_object_del(mi->label_object);
1876 mi->label_object = NULL;
1877 if (mi->submenu_object) evas_object_del(mi->submenu_object);
1878 mi->submenu_object = NULL;
1879 }
1880
1881 static void
_e_menu_unrealize(E_Menu * m)1882 _e_menu_unrealize(E_Menu *m)
1883 {
1884 Eina_List *l;
1885 E_Menu_Item *mi;
1886
1887 if (!m->realized) return;
1888 /* freeze+thaw here breaks the universe. don't do it. */
1889 //evas_event_freeze(m->evas);
1890 if (m->comp_object)
1891 evas_object_event_callback_del(m->comp_object, EVAS_CALLBACK_HIDE, _e_menu_hide_cb);
1892 if (m->cur.visible && m->comp_object && (!stopping))
1893 {
1894 /* force unref in smart object */
1895 if (m->container_object)
1896 {
1897 evas_object_intercept_move_callback_del(m->container_object, _e_menu_cb_intercept_container_move);
1898 evas_object_intercept_resize_callback_del(m->container_object, _e_menu_cb_intercept_container_resize);
1899 }
1900 evas_object_pass_events_set(m->comp_object, 1);
1901 evas_object_hide(m->comp_object);
1902 E_FREE_FUNC(m->comp_object, evas_object_del);
1903 return;
1904 }
1905 evas_object_hide(m->comp_object);
1906 evas_object_del(m->comp_object);
1907 if (stopping && m->comp_object) evas_object_unref(m->comp_object);
1908 EINA_LIST_FOREACH(m->items, l, mi)
1909 _e_menu_item_unrealize(mi);
1910 E_FREE_FUNC(m->header.icon, evas_object_del);
1911 E_FREE_FUNC(m->bg_object, evas_object_del);
1912 E_FREE_FUNC(m->container_object, evas_object_del);
1913 m->cur.visible = 0;
1914 m->prev.visible = 0;
1915 m->realized = 0;
1916 m->zone = NULL;
1917 //evas_event_thaw(m->evas);
1918 m->evas = NULL;
1919 }
1920
1921 static void
_e_menu_activate_internal(E_Menu * m,E_Zone * zone)1922 _e_menu_activate_internal(E_Menu *m, E_Zone *zone)
1923 {
1924 E_Menu_Category *cat = NULL;
1925 Eina_List *l;
1926
1927 if (m->pre_activate_cb.func)
1928 m->pre_activate_cb.func(m->pre_activate_cb.data, m);
1929 m->fast_mouse = 0;
1930 m->pending_new_submenu = 0;
1931 if (_e_menu_win != e_comp->ee_win)
1932 {
1933 _e_menu_win = e_comp->ee_win;
1934 if (!e_comp_grab_input(1, 1))
1935 {
1936 _e_menu_win = UINT_MAX;
1937 return;
1938 }
1939 _e_menu_grabbed = EINA_TRUE;
1940 e_bindings_disabled_set(1);
1941 }
1942 m->zone = zone;
1943 if (!m->active)
1944 {
1945 /* this remove is in case the menu is marked as inactive but hasn't */
1946 /* been removed from the list yet */
1947 if (m->in_active_list)
1948 _e_active_menus = eina_list_remove(_e_active_menus, m);
1949 _e_active_menus = eina_list_append(_e_active_menus, m);
1950 if (!m->in_active_list)
1951 e_object_ref(E_OBJECT(m));
1952 m->in_active_list = 1;
1953 m->active = 1;
1954 }
1955 /* the foreign menu items */
1956 if (m->category)
1957 {
1958 cat = eina_hash_find(_e_menu_categories, m->category);
1959 if (cat)
1960 {
1961 E_Menu_Category_Callback *cb;
1962 EINA_LIST_FOREACH(cat->callbacks, l, cb)
1963 if (cb->create) cb->create(cb->data, m, cat->data);
1964 }
1965 }
1966 m->cur.visible = 1;
1967 }
1968
1969 static void
_e_menu_deactivate_all(void)1970 _e_menu_deactivate_all(void)
1971 {
1972 Eina_List *tmp;
1973 E_Menu *m;
1974
1975 tmp = _e_active_menus_copy_ref();
1976
1977 EINA_LIST_FREE(tmp, m)
1978 {
1979 e_menu_deactivate(m);
1980 m->parent_item = NULL;
1981 e_object_unref(E_OBJECT(m));
1982 }
1983 _e_menu_activate_floating = 0;
1984 _e_menu_activate_maybe_drag = 0;
1985 _e_menu_activate_dragging = 0;
1986 }
1987
1988 static void
_e_menu_deactivate_above(E_Menu * ma)1989 _e_menu_deactivate_above(E_Menu *ma)
1990 {
1991 Eina_List *tmp;
1992 int above = 0;
1993 E_Menu *m;
1994
1995 tmp = _e_active_menus_copy_ref();
1996
1997 EINA_LIST_FREE(tmp, m)
1998 {
1999 if (above)
2000 {
2001 e_menu_deactivate(m);
2002 m->parent_item = NULL;
2003 }
2004 if (ma == m) above = 1;
2005 e_object_unref(E_OBJECT(m));
2006 }
2007 }
2008
2009 static void
_e_menu_submenu_activate(E_Menu_Item * mi)2010 _e_menu_submenu_activate(E_Menu_Item *mi)
2011 {
2012 if (!mi->menu->active) return;
2013 if (mi->submenu && mi->submenu->active) return;
2014 if (mi->menu->fast_mouse)
2015 {
2016 mi->menu->pending_new_submenu = 1;
2017 return;
2018 }
2019 mi->menu->pending_new_submenu = 0;
2020 _e_menu_deactivate_above(mi->menu);
2021 if (mi->submenu_pre_cb.func)
2022 mi->submenu_pre_cb.func(mi->submenu_pre_cb.data, mi->menu, mi);
2023 if (mi->submenu)
2024 {
2025 E_Menu *m;
2026
2027 m = mi->submenu;
2028 e_object_ref(E_OBJECT(m));
2029 m->parent_item = mi;
2030 _e_menu_activate_internal(m, mi->menu->zone);
2031 _e_menu_reposition(m);
2032 e_object_unref(E_OBJECT(m));
2033 mi->menu->have_submenu = 1;
2034 }
2035 }
2036
2037 static void
_e_menu_submenu_deactivate(E_Menu_Item * mi)2038 _e_menu_submenu_deactivate(E_Menu_Item *mi)
2039 {
2040 if (!mi->menu->active) return;
2041 mi->menu->have_submenu = 0;
2042 if (mi->submenu_post_cb.func)
2043 mi->submenu_post_cb.func(mi->submenu_post_cb.data, mi->menu, mi);
2044 }
2045
2046 static void
_e_menu_reposition(E_Menu * m)2047 _e_menu_reposition(E_Menu *m)
2048 {
2049 Eina_List *l, *tmp;
2050 E_Menu_Item *mi;
2051 int parent_item_bottom;
2052
2053 if (!m->zone) return;
2054 if (!m->parent_item) return;
2055 m->cur.x = m->parent_item->menu->cur.x + m->parent_item->menu->cur.w;
2056
2057 parent_item_bottom = m->parent_item->y;
2058 if (m->cur.h > m->zone->h)
2059 {
2060 #if 0 // we can't win - we just flip back and forth, so let it go off and use scrolling
2061 /* menu is larger than screen */
2062 if (parent_item_bottom > (m->zone->y + (m->zone->h / 2)))
2063 /* more is shown if menu goes up */
2064 m->cur.y = (parent_item_bottom - (m->zone->h + 1));
2065 else
2066 /* more is shown if menu goes down */
2067 m->cur.y = parent_item_bottom - m->zone->y;
2068 #endif
2069 }
2070 else
2071 {
2072 /* menu is on top or bottom half of screen */
2073 if (parent_item_bottom > (m->zone->y + (m->zone->h / 2)))
2074 m->cur.y = parent_item_bottom - m->cur.h + m->parent_item->h;
2075 else
2076 m->cur.y = parent_item_bottom;
2077 }
2078
2079 /* FIXME: this will suck for big menus */
2080 tmp = _e_active_menus_copy_ref();
2081
2082 EINA_LIST_FOREACH(m->items, l, mi)
2083 if ((mi->active) && (mi->submenu)) _e_menu_reposition(mi->submenu);
2084
2085 _e_menu_list_free_unref(tmp);
2086 }
2087
2088 static int
_e_menu_active_call(void)2089 _e_menu_active_call(void)
2090 {
2091 E_Menu_Item *mi;
2092
2093 mi = _e_menu_item_active_get();
2094 if (mi)
2095 {
2096 if (mi->check)
2097 e_menu_item_toggle_set(mi, !mi->toggle);
2098 if ((mi->radio) && (!e_menu_item_toggle_get(mi)))
2099 e_menu_item_toggle_set(mi, 1);
2100 if (mi->cb.func)
2101 mi->cb.func(mi->cb.data, mi->menu, mi);
2102 return 1;
2103 }
2104 return -1;
2105 }
2106
2107 static int
_e_menu_realize_call(E_Menu_Item * mi)2108 _e_menu_realize_call(E_Menu_Item *mi)
2109 {
2110 if (mi)
2111 {
2112 if (mi->realize_cb.func)
2113 {
2114 mi->realize_cb.func(mi->realize_cb.data, mi->menu, mi);
2115 return 1;
2116 }
2117 }
2118 return 0;
2119 }
2120
2121 static void
_e_menu_item_activate_next(void)2122 _e_menu_item_activate_next(void)
2123 {
2124 E_Menu_Item *mi;
2125 Eina_List *ll;
2126
2127 ll = _e_menu_list_item_active_get();
2128 mi = _e_menu_item_active_get();
2129 if (ll && mi)
2130 {
2131 /* Look at the next item and then cycle until we're not on
2132 * a separator. */
2133 do
2134 {
2135 if (!eina_list_next(ll))
2136 ll = mi->menu->items;
2137 else
2138 ll = eina_list_next(ll);
2139 mi = eina_list_data_get(ll);
2140 }
2141 while (mi->separator || mi->disable);
2142
2143 e_menu_item_active_set(mi, 1);
2144 _e_menu_item_ensure_onscreen(mi);
2145 return;
2146 }
2147
2148 _e_menu_activate_first();
2149 }
2150
2151 static void
_e_menu_item_activate_previous(void)2152 _e_menu_item_activate_previous(void)
2153 {
2154 E_Menu_Item *mi;
2155 Eina_List *ll;
2156
2157 ll = _e_menu_list_item_active_get();
2158 mi = _e_menu_item_active_get();
2159 if (ll && mi)
2160 {
2161 /* Look at the prev item and then cycle until we're not on
2162 * a separator. */
2163 do
2164 {
2165 if (!eina_list_prev(ll))
2166 ll = eina_list_last(ll);
2167 else
2168 ll = eina_list_prev(ll);
2169 mi = eina_list_data_get(ll);
2170 }
2171 while ((mi->separator) || (mi->disable));
2172
2173 e_menu_item_active_set(mi, 1);
2174 _e_menu_item_ensure_onscreen(mi);
2175 return;
2176 }
2177
2178 _e_menu_activate_first();
2179 }
2180
2181 static void
_e_menu_item_activate_first(void)2182 _e_menu_item_activate_first(void)
2183 {
2184 E_Menu *m;
2185 Eina_List *ll;
2186 E_Menu_Item *mi;
2187
2188 m = _e_menu_active_get();
2189 if (m)
2190 {
2191 ll = m->items;
2192 mi = eina_list_data_get(ll);
2193 while ((mi->separator) && eina_list_next(ll))
2194 {
2195 ll = eina_list_next(ll);
2196 mi = eina_list_data_get(ll);
2197 }
2198 if (mi->separator) return;
2199 e_menu_item_active_set(mi, 1);
2200 _e_menu_item_ensure_onscreen(mi);
2201 return;
2202 }
2203 _e_menu_activate_first();
2204 }
2205
2206 static void
_e_menu_item_activate_last(void)2207 _e_menu_item_activate_last(void)
2208 {
2209 E_Menu *m;
2210 Eina_List *ll;
2211 E_Menu_Item *mi;
2212
2213 m = _e_menu_active_get();
2214 if (m)
2215 {
2216 ll = eina_list_last(m->items);
2217 mi = eina_list_data_get(ll);
2218 while ((mi->separator) && eina_list_prev(ll))
2219 {
2220 ll = eina_list_prev(ll);
2221 mi = eina_list_data_get(ll);
2222 }
2223 if (mi->separator) return;
2224 e_menu_item_active_set(mi, 1);
2225 _e_menu_item_ensure_onscreen(mi);
2226 return;
2227 }
2228 _e_menu_activate_first();
2229 }
2230
2231 static void
_e_menu_item_activate_nth(int n)2232 _e_menu_item_activate_nth(int n)
2233 {
2234 E_Menu *m;
2235 E_Menu_Item *mi;
2236 Eina_List *ll;
2237 int i = -1;
2238
2239 mi = _e_menu_item_active_get();
2240 if (!mi)
2241 {
2242 _e_menu_activate_first();
2243 mi = _e_menu_item_active_get();
2244 if (!mi) return;
2245 }
2246 m = mi->menu;
2247 EINA_LIST_FOREACH(m->items, ll, mi)
2248 {
2249 if (!mi->separator) i++;
2250 if (i == n) break;
2251 }
2252 if (!mi) return;
2253 e_menu_item_active_set(mi, 1);
2254 _e_menu_item_ensure_onscreen(mi);
2255 }
2256
2257 static void
_e_menu_item_activate_char(const char * key_compose)2258 _e_menu_item_activate_char(const char *key_compose)
2259 {
2260 E_Menu *m;
2261 E_Menu_Item *mi;
2262 Eina_List *ll, *ll_orig;
2263
2264 /* Ignore modifiers and such. */
2265 if (!key_compose) return;
2266
2267 /* Check we've got a menu and it's active. */
2268 m = _e_menu_active_get();
2269 if (!m)
2270 {
2271 if (!_e_active_menus) return;
2272 m = eina_list_data_get(_e_active_menus);
2273 if (!m) return;
2274 }
2275
2276 ll = _e_menu_list_item_active_get();
2277 /* If we don't have an active item, start from the top of the list. */
2278 if (!ll)
2279 {
2280 ll = m->items;
2281 mi = eina_list_data_get(ll);
2282 /* Only check the current item if it wasn't active before. */
2283 if (!mi->separator && mi->label && !strncasecmp(key_compose, mi->label, strlen(key_compose)))
2284 {
2285 e_menu_item_active_set(mi, 1);
2286 _e_menu_item_ensure_onscreen(mi);
2287 return;
2288 }
2289 }
2290
2291 ll_orig = ll;
2292
2293 mi = eina_list_data_get(ll);
2294 if (!eina_list_next(ll))
2295 ll = mi->menu->items;
2296 else
2297 ll = eina_list_next(ll);
2298 mi = eina_list_data_get(ll);
2299
2300 /* While we don't have a label OR we don't match AND we haven't
2301 * wrapped around */
2302 while ((!mi->label || strncasecmp(key_compose, mi->label, strlen(key_compose)))
2303 && ll != ll_orig)
2304 {
2305 do
2306 {
2307 if (!eina_list_next(ll))
2308 ll = mi->menu->items;
2309 else
2310 ll = eina_list_next(ll);
2311 mi = eina_list_data_get(ll);
2312 }
2313 while (mi->separator);
2314 }
2315
2316 e_menu_item_active_set(mi, 1);
2317 _e_menu_item_ensure_onscreen(mi);
2318 return;
2319 }
2320
2321 static void
_e_menu_activate_next(void)2322 _e_menu_activate_next(void)
2323 {
2324 E_Menu_Item *mi;
2325
2326 mi = _e_menu_item_active_get();
2327 if (mi)
2328 {
2329 if (mi->submenu)
2330 {
2331 if (mi->submenu->items)
2332 {
2333 Eina_List *l;
2334 EINA_LIST_FOREACH(mi->submenu->items, l, mi)
2335 if (!mi->disable)
2336 {
2337 e_menu_item_active_set(mi, 1);
2338 _e_menu_item_ensure_onscreen(mi);
2339 break;
2340 }
2341 }
2342 }
2343 return;
2344 }
2345 _e_menu_activate_first();
2346 }
2347
2348 static void
_e_menu_activate_previous(void)2349 _e_menu_activate_previous(void)
2350 {
2351 E_Menu_Item *mi;
2352
2353 mi = _e_menu_item_active_get();
2354 if (mi)
2355 {
2356 if (mi->menu->parent_item)
2357 {
2358 mi = mi->menu->parent_item;
2359 e_menu_item_active_set(mi, 1);
2360 _e_menu_item_ensure_onscreen(mi);
2361 }
2362 return;
2363 }
2364 _e_menu_activate_last();
2365 }
2366
2367 static void
_e_menu_activate_first(void)2368 _e_menu_activate_first(void)
2369 {
2370 E_Menu *m;
2371 E_Menu_Item *mi;
2372 Eina_List *ll;
2373
2374 if (!_e_active_menus) return;
2375 m = eina_list_data_get(_e_active_menus);
2376 if (!m->items) return;
2377 ll = m->items;
2378 mi = eina_list_data_get(ll);
2379 while ((mi->separator) && eina_list_next(ll))
2380 {
2381 ll = eina_list_next(ll);
2382 mi = eina_list_data_get(ll);
2383 }
2384 if (mi->separator) return;
2385 e_menu_item_active_set(mi, 1);
2386 _e_menu_item_ensure_onscreen(mi);
2387 }
2388
2389 static void
_e_menu_activate_last(void)2390 _e_menu_activate_last(void)
2391 {
2392 E_Menu *m;
2393 E_Menu_Item *mi;
2394 Eina_List *ll;
2395
2396 if (!_e_active_menus) return;
2397 m = eina_list_data_get(_e_active_menus);
2398 if (!m->items) return;
2399 ll = eina_list_last(m->items);
2400 mi = eina_list_data_get(ll);
2401 while ((mi->separator) && eina_list_prev(ll))
2402 {
2403 ll = eina_list_prev(ll);
2404 mi = eina_list_data_get(ll);
2405 }
2406 if (mi->separator) return;
2407 e_menu_item_active_set(mi, 1);
2408 _e_menu_item_ensure_onscreen(mi);
2409 }
2410
2411 #if 0
2412 static void
2413 _e_menu_activate_nth(int n)
2414 {
2415 E_Menu *m;
2416 E_Menu_Item *mi;
2417 Eina_List *ll;
2418 int i = -1;
2419
2420 mi = _e_menu_item_active_get();
2421 if (!mi)
2422 {
2423 _e_menu_activate_first();
2424 mi = _e_menu_item_active_get();
2425 if (!mi) return;
2426 }
2427 m = mi->menu;
2428 EINA_LIST_FOREACH(m->items, ll, mi)
2429 {
2430 if (!mi->separator) i++;
2431 if (i == n)
2432 {
2433 e_menu_item_active_set(mi, 1);
2434 _e_menu_item_ensure_onscreen(mi);
2435 return;
2436 }
2437 }
2438 }
2439
2440 #endif
2441
2442 static E_Menu *
_e_menu_active_get(void)2443 _e_menu_active_get(void)
2444 {
2445 if (_e_active_menu_item) return _e_active_menu_item->menu;
2446 return NULL;
2447 }
2448
2449 static E_Menu_Item *
_e_menu_item_active_get(void)2450 _e_menu_item_active_get(void)
2451 {
2452 return _e_active_menu_item;
2453 }
2454
2455 static Eina_List *
_e_menu_list_item_active_get(void)2456 _e_menu_list_item_active_get(void)
2457 {
2458 if (_e_active_menu_item)
2459 return _e_active_menu_item->list_position;
2460 else
2461 return NULL;
2462 }
2463
2464 static int
_e_menu_outside_bounds_get(int xdir,int ydir)2465 _e_menu_outside_bounds_get(int xdir, int ydir)
2466 {
2467 Eina_List *l;
2468 E_Menu *m;
2469 int outl = 0;
2470 int outr = 0;
2471 int outt = 0;
2472 int outb = 0;
2473 int i = 0;
2474
2475 EINA_LIST_FOREACH(_e_active_menus, l, m)
2476 {
2477 if (!m->zone) continue;
2478 if (m->cur.x < m->zone->x + e_config->menu_autoscroll_margin)
2479 {
2480 i = m->zone->x - m->cur.x + e_config->menu_autoscroll_margin;
2481 if (i > outl) outl = i;
2482 }
2483 if (m->cur.y < m->zone->y + e_config->menu_autoscroll_margin)
2484 {
2485 i = m->zone->y - m->cur.y + e_config->menu_autoscroll_margin;
2486 if (i > outt) outt = i;
2487 }
2488 if ((m->cur.x + m->cur.w) > (m->zone->w - e_config->menu_autoscroll_margin))
2489 {
2490 i = m->cur.x + m->cur.w - (m->zone->x + m->zone->w - e_config->menu_autoscroll_margin);
2491 if (i > outr) outr = i;
2492 }
2493 if ((m->cur.y + m->cur.h) > (m->zone->h - e_config->menu_autoscroll_margin))
2494 {
2495 i = m->cur.y + m->cur.h - (m->zone->y + m->zone->h - e_config->menu_autoscroll_margin);
2496 if (i > outb) outb = i;
2497 }
2498 }
2499 if (xdir == -1)
2500 {
2501 if (outl) return outl;
2502 }
2503 else if (xdir == 1)
2504 {
2505 if (outr) return outr;
2506 }
2507 else if (ydir == -1)
2508 {
2509 if (outt) return outt;
2510 }
2511 else if (ydir == 1)
2512 {
2513 if (outb) return outb;
2514 }
2515 return 0;
2516 }
2517
2518 static void
_e_menu_scroll_by(int dx,int dy)2519 _e_menu_scroll_by(int dx, int dy)
2520 {
2521 Eina_List *l;
2522 E_Menu *m;
2523
2524 EINA_LIST_FOREACH(_e_active_menus, l, m)
2525 {
2526 m->cur.x += dx;
2527 m->cur.y += dy;
2528 }
2529 }
2530
2531 static void
_e_menu_mouse_autoscroll_check(void)2532 _e_menu_mouse_autoscroll_check(void)
2533 {
2534 int autoscroll_x = 0;
2535 int autoscroll_y = 0;
2536 E_Menu *m = NULL;
2537 int mx, my;
2538
2539 mx = _e_menu_x;
2540 my = _e_menu_y;
2541 if (_e_active_menus)
2542 {
2543 m = eina_list_data_get(_e_active_menus);
2544 if ((m) && (m->zone))
2545 {
2546 mx -= m->zone->x;
2547 my -= m->zone->y;
2548 }
2549 }
2550 if (mx - e_config->menu_autoscroll_cursor_margin <= 0)
2551 {
2552 if (_e_menu_outside_bounds_get(-1, 0)) autoscroll_x = -1;
2553 }
2554 if (my - e_config->menu_autoscroll_cursor_margin <= 0)
2555 {
2556 if (_e_menu_outside_bounds_get(0, -1)) autoscroll_y = -1;
2557 }
2558 if ((!autoscroll_x) && (!autoscroll_y))
2559 {
2560 if ((m) && (m->zone))
2561 {
2562 if (mx + e_config->menu_autoscroll_cursor_margin >= (m->zone->w - 1))
2563 {
2564 if (_e_menu_outside_bounds_get(1, 0)) autoscroll_x = 1;
2565 }
2566 if (my + e_config->menu_autoscroll_cursor_margin >= (m->zone->h - 1))
2567 {
2568 if (_e_menu_outside_bounds_get(0, 1)) autoscroll_y = 1;
2569 }
2570 }
2571 }
2572 _e_menu_autoscroll_x = autoscroll_x;
2573 _e_menu_autoscroll_y = autoscroll_y;
2574 if ((!autoscroll_x) && (!autoscroll_y)) return;
2575 if (_e_menu_scroll_animator) return;
2576 _e_menu_scroll_animator = ecore_animator_add(_e_menu_cb_scroll_animator,
2577 NULL);
2578 _e_menu_scroll_start = ecore_loop_time_get();
2579 }
2580
2581 static void
_e_menu_item_ensure_onscreen(E_Menu_Item * mi)2582 _e_menu_item_ensure_onscreen(E_Menu_Item *mi)
2583 {
2584 int x = 0, y = 0, w = 0, h = 0;
2585 int dx = 0, dy = 0;
2586
2587 if (!mi->menu) return;
2588 if (!mi->menu->zone) return;
2589 evas_object_geometry_get(mi->container_object, &x, &y, &w, &h);
2590 if ((x + w) > (mi->menu->zone->x + mi->menu->zone->w))
2591 dx = (mi->menu->zone->x + mi->menu->zone->w) - (x + w);
2592 else if (x < mi->menu->zone->x)
2593 dx = mi->menu->zone->x - x;
2594 if ((y + h) > (mi->menu->zone->y + mi->menu->zone->h))
2595 dy = (mi->menu->zone->y + mi->menu->zone->h) - (y + h);
2596 else if (y < mi->menu->zone->y)
2597 dy = mi->menu->zone->y - y;
2598 if ((dx != 0) || (dy != 0))
2599 _e_menu_scroll_by(dx, dy);
2600 }
2601
2602 static void
_e_menu_auto_place_vert(E_Menu * m,int x,int y,int w,int h)2603 _e_menu_auto_place_vert(E_Menu *m, int x, int y, int w, int h)
2604 {
2605 int zx, zy, zw, zh;
2606
2607 if (!m->zone) return;
2608 e_zone_useful_geometry_get(m->zone, &zx, &zy, &zw, &zh);
2609 if (E_CONTAINS(zx, zy, zw, zh, m->cur.x, y, m->cur.w, m->cur.h))
2610 {
2611 if (w + h > 2)
2612 {
2613 if (E_INTERSECTS(m->cur.x, y, m->cur.w, m->cur.h, x, y, w, h))
2614 m->cur.y = y + h - m->cur.h;
2615 else
2616 m->cur.y = y;
2617 }
2618 else
2619 m->cur.y = y;
2620 }
2621 else
2622 m->cur.y = y + h - m->cur.h;
2623 }
2624
2625 static int
_e_menu_auto_place(E_Menu * m,int x,int y,int w,int h)2626 _e_menu_auto_place(E_Menu *m, int x, int y, int w, int h)
2627 {
2628 double xr, yr;
2629
2630 _e_menu_realize(m);
2631 /* +-----+
2632 * |\ T /|
2633 * | \ / |
2634 * |L X R|
2635 * | / \ |
2636 * |/ B \|
2637 * +-----+
2638 *
2639 * quadrants... which one
2640 */
2641 if (!m->zone) return 0;
2642 if (w != m->zone->w)
2643 xr = (double)(x - m->zone->x) /
2644 (double)(m->zone->w - w);
2645 else
2646 xr = 0.0;
2647 if (h != m->zone->h)
2648 yr = (double)(y - m->zone->y) /
2649 (double)(m->zone->h - h);
2650 else
2651 yr = 0.0;
2652
2653 if ((xr + yr) < 0.99) /* top or left */
2654 {
2655 if (((1.0 - yr) + xr) <= 1.0)
2656 {
2657 /* L */
2658 m->cur.x = x + w;
2659 _e_menu_auto_place_vert(m, x, y, w, h);
2660 return 1;
2661 }
2662 else
2663 {
2664 /* T */
2665 m->cur.y = y + h;
2666 if (x < (m->zone->x + ((m->zone->w * 1) / 3)))
2667 m->cur.x = x;
2668 else if (x < (m->zone->x + ((m->zone->w * 2) / 3)))
2669 m->cur.x = x + ((w - m->cur.w) / 2);
2670 else
2671 m->cur.x = x + w - m->cur.w;
2672 return 3;
2673 }
2674 }
2675 else /* bottom or right */
2676 {
2677 if (((1.0 - yr) + xr) <= 1.01)
2678 {
2679 /* B */
2680 m->cur.y = y - m->cur.h;
2681 if (x < (m->zone->x + ((m->zone->w * 1) / 3)))
2682 m->cur.x = x;
2683 else if (x < (m->zone->x + ((m->zone->w * 2) / 3)))
2684 m->cur.x = x + ((w - m->cur.w) / 2);
2685 else
2686 m->cur.x = x + w - m->cur.w;
2687 return 4;
2688 }
2689 else
2690 {
2691 /* R */
2692 m->cur.x = x - m->cur.w;
2693 _e_menu_auto_place_vert(m, x, y, w, h);
2694 return 2;
2695 }
2696 }
2697 return 0;
2698 }
2699
2700 static void
_e_menu_cb_item_in(void * data,Evas * evas EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)2701 _e_menu_cb_item_in(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
2702 {
2703 E_Menu_Item *mi = data;
2704
2705 if (_e_menu_lock) return;
2706 // ignore in/out not due to deliberate mouse move by user
2707 if (_e_menu_nav_by != NAV_BY_MOUSE) return;
2708 /* this can be triggered when creating menus if the new menu is on top of its parent */
2709 if (!mi->menu->realized) return;
2710 e_menu_item_active_set(mi, 1);
2711 }
2712
2713 static void
_e_menu_cb_item_out(void * data,Evas * evas EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)2714 _e_menu_cb_item_out(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
2715 {
2716 E_Menu_Item *mi = data;
2717 // ignore in/out not due to deliberate mouse move by user
2718 if (_e_menu_nav_by != NAV_BY_MOUSE) return;
2719 /* this can be triggered when creating menus if the new menu is on top of its parent */
2720 if (!mi->menu->realized) return;
2721 e_menu_item_active_set(mi, 0);
2722 }
2723
2724 static Eina_Bool
_e_menu_cb_key_down(void * data EINA_UNUSED,Ecore_Event_Key * ev)2725 _e_menu_cb_key_down(void *data EINA_UNUSED, Ecore_Event_Key *ev)
2726 {
2727 _e_menu_nav_by = NAV_BY_KEY;
2728 if ((!strcmp(ev->key, "Up")) || (!strcmp(ev->key, "KP_Up")))
2729 _e_menu_item_activate_previous();
2730 else if ((!strcmp(ev->key, "Down")) || (!strcmp(ev->key, "KP_Down")))
2731 _e_menu_item_activate_next();
2732 else if ((!strcmp(ev->key, "Left")) || (!strcmp(ev->key, "KP_Left")))
2733 _e_menu_activate_previous();
2734 else if ((!strcmp(ev->key, "Right")) || (!strcmp(ev->key, "KP_Right")))
2735 _e_menu_activate_next();
2736 else if ((!strcmp(ev->key, "Home")) || (!strcmp(ev->key, "KP_Home")))
2737 _e_menu_item_activate_first();
2738 else if ((!strcmp(ev->key, "End")) || (!strcmp(ev->key, "KP_End")))
2739 _e_menu_item_activate_last();
2740 else if (!strcmp(ev->key, "space"))
2741 _e_menu_active_call();
2742 else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
2743 {
2744 _e_menu_active_call();
2745 _e_menu_deactivate_all();
2746 }
2747 else if (!strcmp(ev->key, "Escape"))
2748 _e_menu_deactivate_all();
2749 else if ((!strcmp(ev->key, "1")) || (!strcmp(ev->key, "KP_1")))
2750 _e_menu_item_activate_first();
2751 else if ((!strcmp(ev->key, "2")) || (!strcmp(ev->key, "KP_2")))
2752 _e_menu_item_activate_nth(1);
2753 else if ((!strcmp(ev->key, "3")) || (!strcmp(ev->key, "KP_3")))
2754 _e_menu_item_activate_nth(2);
2755 else if ((!strcmp(ev->key, "4")) || (!strcmp(ev->key, "KP_4")))
2756 _e_menu_item_activate_nth(3);
2757 else if ((!strcmp(ev->key, "5")) || (!strcmp(ev->key, "KP_5")))
2758 _e_menu_item_activate_nth(4);
2759 else if ((!strcmp(ev->key, "6")) || (!strcmp(ev->key, "KP_6")))
2760 _e_menu_item_activate_nth(5);
2761 else if ((!strcmp(ev->key, "7")) || (!strcmp(ev->key, "KP_7")))
2762 _e_menu_item_activate_nth(6);
2763 else if ((!strcmp(ev->key, "8")) || (!strcmp(ev->key, "KP_8")))
2764 _e_menu_item_activate_nth(7);
2765 else if ((!strcmp(ev->key, "9")) || (!strcmp(ev->key, "KP_9")))
2766 _e_menu_item_activate_nth(8);
2767 else if ((!strcmp(ev->key, "0")) || (!strcmp(ev->key, "KP_0")))
2768 _e_menu_item_activate_last();
2769 else if (ev->compose)
2770 _e_menu_item_activate_char(ev->compose);
2771 return ECORE_CALLBACK_PASS_ON;
2772 }
2773
2774 /* we need all of these because menus are special and grab the mouse and
2775 * keyboard and thus the normal event mechanism doesn't work, so we feed
2776 * events directly to the canvases from our grab window
2777 */
2778
2779 static void
_e_menu_cb_mouse_evas_down(void * data,Evas_Object * obj EINA_UNUSED)2780 _e_menu_cb_mouse_evas_down(void *data, Evas_Object *obj EINA_UNUSED)
2781 {
2782 E_Menu *m = data;
2783
2784 while (m->parent_item)
2785 m = m->parent_item->menu;
2786 e_menu_deactivate(m);
2787 }
2788
2789 static Eina_Bool
_e_menu_cb_mouse_down(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)2790 _e_menu_cb_mouse_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
2791 {
2792 Ecore_Event_Mouse_Button *ev;
2793
2794 ev = event;
2795 _e_menu_nav_by = NAV_BY_MOUSE;
2796 if (ev->window != _e_menu_win)
2797 {
2798 if (_e_menu_active_get())
2799 _e_menu_deactivate_all();
2800 return ECORE_CALLBACK_PASS_ON;
2801 }
2802
2803 _e_menu_lock = 1;
2804 e_comp_canvas_feed_mouse_up(0);
2805 _e_menu_lock = 0;
2806
2807 /* Only allow dragging from floating menus for now.
2808 * The reason for this is that for non floating menus,
2809 * the mouse is already down and dragging, so the decision
2810 * to start a drag is much more complex.
2811 */
2812 if (_e_menu_activate_floating)
2813 _e_menu_activate_maybe_drag = 1;
2814
2815 return ECORE_CALLBACK_PASS_ON;
2816 }
2817
2818 static Eina_Bool
_e_menu_cb_mouse_up(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)2819 _e_menu_cb_mouse_up(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
2820 {
2821 Ecore_Event_Mouse_Button *ev;
2822 E_Menu *m;
2823 Eina_List *l;
2824 unsigned int t;
2825 int ret = 0;
2826
2827 if (_e_menu_lock) return ECORE_CALLBACK_RENEW;
2828 ev = event;
2829 if (ev->window != _e_menu_win) return ECORE_CALLBACK_RENEW;
2830
2831 _e_menu_nav_by = NAV_BY_MOUSE;
2832 if (!_e_menu_activate_floating)
2833 {
2834 EINA_LIST_FOREACH(_e_active_menus, l, m)
2835 {
2836 if (!m->hold_mode)
2837 {
2838 _e_menu_activate_floating = 1;
2839 return ECORE_CALLBACK_PASS_ON;
2840 }
2841 }
2842 }
2843
2844 t = ev->timestamp - _e_menu_activate_time;
2845 if ((_e_menu_activate_time != 0) &&
2846 (t < (e_config->menus_click_drag_timeout * 1000)))
2847 {
2848 _e_menu_activate_floating = 1;
2849 return ECORE_CALLBACK_PASS_ON;
2850 }
2851
2852 if (_e_menu_activate_dragging)
2853 {
2854 /* FIXME: This is a drop, which is not allowed for now.
2855 * Once dragging is working, this will be subject to some experimentation.
2856 */
2857 }
2858 else
2859 {
2860 E_Menu_Item *mi;
2861
2862 mi = _e_menu_item_active_get();
2863 if ((!mi) ||
2864 (E_INSIDE(e_comp_canvas_x_root_adjust(ev->root.x),
2865 e_comp_canvas_y_root_adjust(ev->root.y),
2866 mi->x, mi->y, mi->w, mi->h))
2867 )
2868 ret = _e_menu_active_call();
2869 }
2870 _e_menu_activate_maybe_drag = 0;
2871 _e_menu_activate_dragging = 0;
2872 if (ret == 1)
2873 {
2874 /* allow mouse to pop down menu if clicked elsewhere */
2875 /* if (_e_menu_activate_time != 0) */
2876 _e_menu_deactivate_all();
2877 }
2878 else if (ret == -1)
2879 _e_menu_deactivate_all();
2880 else if (!_e_menu_activate_floating)
2881 _e_menu_deactivate_all();
2882 return ECORE_CALLBACK_PASS_ON;
2883 }
2884
2885 static Eina_Bool
_e_menu_cb_mouse_move(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)2886 _e_menu_cb_mouse_move(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
2887 {
2888 Ecore_Event_Mouse_Move *ev;
2889 Eina_List *l, *tmp;
2890 E_Menu *m;
2891 int dx, dy, d;
2892 double dt;
2893 double fast_move_threshold;
2894 int is_fast = 0;
2895
2896 ev = event;
2897 if (ev->window != _e_menu_win) return ECORE_CALLBACK_PASS_ON;
2898 _e_menu_nav_by = NAV_BY_MOUSE;
2899 fast_move_threshold = e_config->menus_fast_mouse_move_threshhold;
2900 dx = ev->x - _e_menu_x;
2901 dy = ev->y - _e_menu_y;
2902 d = (dx * dx) + (dy * dy);
2903 dt = (double)(ev->timestamp - _e_menu_time) / 1000.0;
2904 dt = dt * dt;
2905 if ((dt > 0.0) && ((d / dt) >= (fast_move_threshold * fast_move_threshold)))
2906 is_fast = 1;
2907
2908 tmp = _e_active_menus_copy_ref();
2909
2910 EINA_LIST_FOREACH(_e_active_menus, l, m)
2911 {
2912 if ((!m->realized) || (!m->cur.visible)) continue;
2913 if (is_fast)
2914 m->fast_mouse = 1;
2915 else if (dt > 0.0)
2916 {
2917 m->fast_mouse = 0;
2918 if (m->pending_new_submenu)
2919 {
2920 E_Menu_Item *mi;
2921
2922 mi = _e_menu_item_active_get();
2923 if (mi)
2924 _e_menu_submenu_activate(mi);
2925 }
2926 }
2927 if (!_e_menu_activate_maybe_drag)
2928 /* this is useless while the mouse is down */
2929 evas_event_feed_mouse_move(m->evas, ev->x, ev->y, ev->timestamp, NULL);
2930 }
2931 if (_e_menu_activate_maybe_drag)
2932 {
2933 if (_e_active_menu_item)
2934 {
2935 if (!E_INSIDE(ev->x, ev->y, _e_active_menu_item->x, _e_active_menu_item->y, _e_active_menu_item->w, _e_active_menu_item->h))
2936 {
2937 if (_e_active_menu_item->drag_cb.func)
2938 {
2939 /* User is dragging a draggable item elsewhere. */
2940 _e_active_menu_item->drag.x = ev->x - (ev->x - _e_active_menu_item->x);
2941 _e_active_menu_item->drag.y = ev->y - (ev->y - _e_active_menu_item->y);
2942 _e_menu_deactivate_all();
2943 _e_active_menu_item->drag_cb.func(_e_active_menu_item->drag_cb.data, _e_active_menu_item->menu, _e_active_menu_item);
2944 }
2945 /* Either way, the maybe drag stops here. */
2946 _e_menu_activate_maybe_drag = 0;
2947 }
2948 }
2949 }
2950
2951 _e_menu_list_free_unref(tmp);
2952
2953 _e_menu_x = ev->x;
2954 _e_menu_y = ev->y;
2955 _e_menu_time = ev->timestamp;
2956 _e_menu_mouse_autoscroll_check();
2957 return ECORE_CALLBACK_PASS_ON;
2958 }
2959
2960 static Eina_Bool
_e_menu_cb_mouse_wheel(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)2961 _e_menu_cb_mouse_wheel(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
2962 {
2963 Ecore_Event_Mouse_Wheel *ev;
2964
2965 ev = event;
2966 if (ev->window != _e_menu_win) return ECORE_CALLBACK_PASS_ON;
2967 _e_menu_nav_by = NAV_BY_WHEEL;
2968 if (ev->z < 0) /* up */
2969 {
2970 int i;
2971
2972 for (i = ev->z; i < 0; i++)
2973 _e_menu_item_activate_previous();
2974 }
2975 else if (ev->z > 0) /* down */
2976 {
2977 int i;
2978
2979 for (i = ev->z; i > 0; i--)
2980 _e_menu_item_activate_next();
2981 }
2982 return ECORE_CALLBACK_PASS_ON;
2983 }
2984
2985 static Eina_Bool
_e_menu_cb_scroll_animator(void * data EINA_UNUSED)2986 _e_menu_cb_scroll_animator(void *data EINA_UNUSED)
2987 {
2988 double t, dt;
2989 double dx, dy;
2990 int out;
2991 double spd;
2992
2993 t = ecore_loop_time_get();
2994 spd = e_config->menus_scroll_speed;
2995 dt = t - _e_menu_scroll_start;
2996 _e_menu_scroll_start = t;
2997 dx = 0;
2998 dy = 0;
2999 if (_e_menu_autoscroll_x)
3000 {
3001 out = _e_menu_outside_bounds_get(_e_menu_autoscroll_x, 0);
3002 dx = (-_e_menu_autoscroll_x) * spd * dt;
3003 if (_e_menu_autoscroll_x == -1)
3004 {
3005 if (dx > out) dx = out;
3006 }
3007 else
3008 {
3009 if (dx < -out) dx = -out;
3010 }
3011 }
3012 if (_e_menu_autoscroll_y)
3013 {
3014 out = _e_menu_outside_bounds_get(0, _e_menu_autoscroll_y);
3015 dy = (-_e_menu_autoscroll_y) * spd * dt;
3016 if (_e_menu_autoscroll_y == -1)
3017 {
3018 if (dy > out) dy = out;
3019 }
3020 else
3021 {
3022 if (dy < -out) dy = -out;
3023 }
3024 }
3025 _e_menu_scroll_by(dx, dy);
3026 _e_menu_mouse_autoscroll_check();
3027 if ((_e_menu_autoscroll_x == 0) && (_e_menu_autoscroll_y == 0))
3028 {
3029 _e_menu_scroll_animator = NULL;
3030 return 0;
3031 }
3032 return 1;
3033 }
3034
3035 static void
_e_menu_cb_item_submenu_post_default(void * data EINA_UNUSED,E_Menu * m EINA_UNUSED,E_Menu_Item * mi)3036 _e_menu_cb_item_submenu_post_default(void *data EINA_UNUSED, E_Menu *m EINA_UNUSED, E_Menu_Item *mi)
3037 {
3038 if (!mi->submenu) return;
3039 e_menu_item_submenu_set(mi, NULL); // unis unrefs previous submenu
3040 }
3041
3042 static void
_e_menu_category_free_cb(E_Menu_Category * cat)3043 _e_menu_category_free_cb(E_Menu_Category *cat)
3044 {
3045 E_FREE_LIST(cat->callbacks, free);
3046 free(cat);
3047 }
3048
3049