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