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