update for beta release
[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 = 
1497      e_canvas_new(e_config->evas_engine_menus, m->zone->container->win,
1498                   m->cur.x, m->cur.y, m->cur.w, m->cur.h, 1, 1,
1499                   &(m->evas_win));
1500    e_canvas_add(m->ecore_evas);
1501    eina_hash_add(_e_menu_hash, e_util_winid_str_get(m->evas_win), m);
1502    m->shape = e_container_shape_add(m->zone->container);
1503    e_container_shape_move(m->shape, m->cur.x, m->cur.y);
1504    e_container_shape_resize(m->shape, m->cur.w, m->cur.h);
1505    
1506    ecore_evas_callback_resize_set(m->ecore_evas, _e_menu_cb_ecore_evas_resize);
1507    m->evas = ecore_evas_get(m->ecore_evas);
1508    evas_event_freeze(m->evas);
1509    /* move cursor out to avoid event cycles during setup */
1510    evas_event_feed_mouse_in(m->evas, ecore_x_current_time_get(), NULL);
1511    evas_event_feed_mouse_move(m->evas, -1000000, -1000000, 
1512                               ecore_x_current_time_get(), NULL);
1513    ecore_x_window_shape_events_select(m->evas_win, 1);
1514    ecore_evas_name_class_set(m->ecore_evas, "E", "_e_menu_window");
1515    ecore_evas_title_set(m->ecore_evas, "E Menu");
1516
1517    o = edje_object_add(m->evas);
1518    m->bg_object = o;
1519    evas_object_name_set(o, "menu/background");
1520    evas_object_data_set(o, "e_menu", m);
1521    evas_object_move(o, 0, 0);
1522    evas_object_resize(o, m->cur.w, m->cur.h);
1523    ok = e_theme_edje_object_set(o, "base/theme/menus",
1524                                 "e/widgets/menu/default/background");
1525    if (ok)
1526      {
1527         const char *shape_option;
1528
1529         shape_option = edje_object_data_get(o, "shaped");
1530         if (shape_option)
1531           {
1532              if (!strcmp(shape_option, "1")) m->shaped = 1;
1533           }
1534      }
1535    if (m->header.title)
1536      {
1537         edje_object_part_text_set(o, "e.text.title", m->header.title);
1538         edje_object_signal_emit(o, "e,action,show,title", "e");
1539         edje_object_message_signal_process(o);
1540      }
1541    evas_object_show(o);
1542
1543    if (m->shaped)
1544      {       
1545         if (e_config->use_composite)
1546           {
1547              ecore_evas_alpha_set(m->ecore_evas, m->shaped);
1548
1549              eina_hash_del(_e_menu_hash, e_util_winid_str_get(m->evas_win), m);
1550              m->evas_win = ecore_evas_software_x11_window_get(m->ecore_evas);
1551              eina_hash_add(_e_menu_hash, e_util_winid_str_get(m->evas_win), m);
1552           }
1553         else
1554           ecore_evas_shaped_set(m->ecore_evas, m->shaped);
1555      }
1556
1557    ecore_x_netwm_window_type_set(m->evas_win, ECORE_X_WINDOW_TYPE_MENU);
1558    
1559    o = e_box_add(m->evas);
1560    m->container_object = o;
1561    evas_object_intercept_move_callback_add(o, _e_menu_cb_intercept_container_move, m);
1562    evas_object_intercept_resize_callback_add(o, _e_menu_cb_intercept_container_resize, m);
1563    e_box_freeze(o);
1564    evas_object_show(o);
1565    e_box_homogenous_set(o, 0);
1566    edje_object_part_swallow(m->bg_object, "e.swallow.content", m->container_object);
1567    
1568    EINA_LIST_FOREACH(m->items, l, mi)
1569      _e_menu_item_realize(mi);
1570
1571    _e_menu_items_layout_update(m);
1572    e_box_thaw(m->container_object);
1573    evas_object_resize(m->bg_object, m->cur.w, m->cur.h);
1574    evas_event_thaw(m->evas);
1575 }
1576
1577 static void
1578 _e_menu_items_layout_update(E_Menu *m)
1579 {
1580    Eina_List *l;
1581    E_Menu_Item *mi;
1582    Evas_Coord bw, bh, mw, mh;
1583    int toggles_on = 0;
1584    int icons_on = 0;
1585    int labels_on = 0;
1586    int submenus_on = 0;
1587    int min_icon_w = 0, min_icon_h = 0;
1588    int min_label_w = 0, min_label_h = 0;
1589    int min_submenu_w = 0, min_submenu_h = 0;
1590    int min_toggle_w = 0, min_toggle_h = 0;
1591    int min_w = 0, min_h = 0;
1592    
1593    e_box_freeze(m->container_object);
1594    EINA_LIST_FOREACH(m->items, l, mi)
1595      {
1596         if (mi->icon) icons_on = 1;
1597         if (mi->icon_object) icons_on = 1;
1598         if (mi->label) labels_on = 1;
1599         if (mi->submenu) submenus_on = 1;
1600         if (mi->check) toggles_on = 1;
1601         if (mi->radio) toggles_on = 1;
1602         
1603         if (mi->icon_w > min_icon_w) min_icon_w = mi->icon_w;
1604         if (mi->icon_h > min_icon_h) min_icon_h = mi->icon_h;
1605         if (mi->label_w > min_label_w) min_label_w = mi->label_w;
1606         if (mi->label_h > min_label_h) min_label_h = mi->label_h;
1607         if (mi->submenu_w > min_submenu_w) min_submenu_w = mi->submenu_w;
1608         if (mi->submenu_h > min_submenu_h) min_submenu_h = mi->submenu_h;
1609         if (mi->toggle_w > min_toggle_w) min_toggle_w = mi->toggle_w;
1610         if (mi->toggle_h > min_toggle_h) min_toggle_h = mi->toggle_h;
1611      }
1612    if (labels_on)
1613      {
1614         if (submenus_on)
1615           {
1616              if (min_label_h < min_submenu_h)
1617                min_label_h = min_submenu_h;
1618           }
1619         if (toggles_on)
1620           {
1621              if (min_label_h < min_toggle_h)
1622                min_label_h = min_toggle_h;
1623           }
1624         if ((icons_on) && (min_icon_h > 0))
1625           {
1626              min_icon_w = (min_icon_w * min_label_h) / min_icon_h;
1627              min_icon_h = min_label_h;
1628           }
1629         min_w = min_label_w + min_icon_w + min_submenu_w + min_toggle_w;
1630         min_h = min_label_h;
1631      }
1632    else if (icons_on)
1633      {
1634         if (submenus_on)
1635           {
1636              if (min_icon_h < min_submenu_h)
1637                min_icon_h = min_submenu_h;
1638           }
1639         if (toggles_on)
1640           {
1641              if (min_icon_h < min_toggle_h)
1642                min_icon_h = min_toggle_h;
1643           }
1644         min_w = min_icon_w + min_toggle_w + min_submenu_w;
1645         min_h = min_icon_h;
1646      }
1647    else if (toggles_on)
1648      {
1649         if (submenus_on)
1650           {
1651              if (min_toggle_h < min_submenu_h)
1652                min_toggle_h = min_submenu_h;
1653           }
1654         min_w = min_toggle_w + min_submenu_w;
1655         min_h = min_toggle_h;
1656      }
1657    EINA_LIST_FOREACH(m->items, l, mi)
1658      {
1659         if (mi->separator)
1660           {
1661              e_box_pack_options_set(mi->separator_object, 
1662                                     1, 1, /* fill */
1663                                     1, 0, /* expand */
1664                                     0.5, 0.5, /* align */
1665                                     mi->separator_w, mi->separator_h, /* min */
1666                                     -1, mi->separator_h /* max */
1667                                     );
1668           }
1669         else
1670           {
1671              e_box_freeze(mi->container_object);
1672              if (toggles_on)
1673                e_box_pack_options_set(mi->toggle_object,
1674                                       1, 1, /* fill */
1675                                       0, 1, /* expand */
1676                                       0.5, 0.5, /* align */
1677                                       min_toggle_w, min_toggle_h, /* min */
1678                                       -1, -1 /* max */
1679                                       );
1680              else
1681                e_box_pack_options_set(mi->toggle_object,
1682                                       1, 1, /* fill */
1683                                       0, 0, /* expand */
1684                                       0.5, 0.5, /* align */
1685                                       0, 0, /* min */
1686                                       0, 0 /* max */
1687                                       );
1688              if (icons_on)
1689                {
1690                   if (mi->icon_bg_object)
1691                     e_box_pack_options_set(mi->icon_bg_object,
1692                                            1, 1, /* fill */
1693                                            0, 1, /* expand */
1694                                            0.5, 0.5, /* align */
1695                                            min_icon_w, min_icon_h, /* min */
1696                                            -1, -1 /* max */
1697                                            );
1698                   else
1699                     e_box_pack_options_set(mi->icon_object,
1700                                            1, 1, /* fill */
1701                                            0, 1, /* expand */
1702                                            0.5, 0.5, /* align */
1703                                            min_icon_w, min_icon_h, /* min */
1704                                            -1, -1 /* max */
1705                                            );
1706                }
1707              else
1708                e_box_pack_options_set(mi->icon_object,
1709                                       1, 1, /* fill */
1710                                       0, 1, /* expand */
1711                                       0.5, 0.5, /* align */
1712                                       0, 0, /* min */
1713                                       0, 0 /* max */
1714                                       );
1715              if (labels_on)
1716                e_box_pack_options_set(mi->label_object,
1717                                       1, 1, /* fill */
1718                                       0, 1, /* expand */
1719                                       0.5, 0.5, /* align */
1720                                       min_label_w, min_label_h, /* min */
1721                                       -1, -1 /* max */
1722                                       );
1723              else
1724                e_box_pack_options_set(mi->label_object,
1725                                       1, 1, /* fill */
1726                                       0, 0, /* expand */
1727                                       0.5, 0.5, /* align */
1728                                       0, 0, /* min */
1729                                       0, 0 /* max */
1730                                       );
1731              if (submenus_on)
1732                e_box_pack_options_set(mi->submenu_object,
1733                                       1, 1, /* fill */
1734                                       0, 1, /* expand */
1735                                       0.5, 0.5, /* align */
1736                                       min_submenu_w, min_submenu_h, /* min */
1737                                       -1, -1 /* max */
1738                                       );
1739              else
1740                e_box_pack_options_set(mi->submenu_object,
1741                                       1, 1, /* fill */
1742                                       0, 0, /* expand */
1743                                       0.5, 0.5, /* align */
1744                                       0, 0, /* min */
1745                                       0, 0 /* max */
1746                                       );
1747              edje_extern_object_min_size_set(mi->container_object, 
1748                                              min_w, min_h);
1749              edje_object_part_swallow(mi->bg_object, "e.swallow.content", 
1750                                       mi->container_object);
1751              edje_object_size_min_calc(mi->bg_object, &mw, &mh);
1752              e_box_pack_options_set(mi->bg_object,
1753                                     1, 1, /* fill */
1754                                     1, 0, /* expand */
1755                                     0.5, 0.5, /* align */
1756                                     mw, mh, /* min */
1757                                     -1, -1 /* max */
1758                                     );
1759              e_box_thaw(mi->container_object);
1760           }
1761      }
1762    e_box_size_min_get(m->container_object, &bw, &bh);
1763    edje_extern_object_min_size_set(m->container_object, bw, bh);
1764    edje_extern_object_max_size_set(m->container_object, bw, bh);
1765    edje_object_part_swallow(m->bg_object, "e.swallow.content", m->container_object);
1766    edje_object_size_min_calc(m->bg_object, &mw, &mh);
1767    e_box_thaw(m->container_object);
1768    m->cur.w = mw;
1769    m->cur.h = mh;
1770 }
1771
1772 static void
1773 _e_menu_item_unrealize(E_Menu_Item *mi)
1774 {
1775    if (mi->container_object) e_box_freeze(mi->container_object);
1776    if (mi->separator_object) evas_object_del(mi->separator_object);
1777    mi->separator_object = NULL;
1778    if (mi->bg_object) evas_object_del(mi->bg_object);
1779    mi->bg_object = NULL;
1780    if (mi->container_object) evas_object_del(mi->container_object);
1781    mi->container_object = NULL;
1782    if (mi->toggle_object) evas_object_del(mi->toggle_object);
1783    mi->toggle_object = NULL;
1784    if (mi->icon_bg_object) evas_object_del(mi->icon_bg_object);
1785    mi->icon_bg_object = NULL;
1786    if (mi->icon_object) evas_object_del(mi->icon_object);
1787    mi->icon_object = NULL;
1788    if (mi->label_object) evas_object_del(mi->label_object);
1789    mi->label_object = NULL;
1790    if (mi->submenu_object) evas_object_del(mi->submenu_object);
1791    mi->submenu_object = NULL;
1792    if (mi->event_object) evas_object_del(mi->event_object);
1793    mi->event_object = NULL;
1794 }
1795
1796 static void
1797 _e_menu_unrealize(E_Menu *m)
1798 {
1799    Eina_List *l;
1800    E_Menu_Item *mi;
1801
1802    if (!m->realized) return;
1803    evas_event_freeze(m->evas);
1804    e_container_shape_hide(m->shape);
1805    e_object_del(E_OBJECT(m->shape));
1806    m->shape = NULL;
1807    e_box_freeze(m->container_object);
1808    EINA_LIST_FOREACH(m->items, l, mi)
1809      _e_menu_item_unrealize(mi);
1810    if (m->header.icon) evas_object_del(m->header.icon);
1811    m->header.icon = NULL;
1812    if (m->bg_object) evas_object_del(m->bg_object);
1813    m->bg_object = NULL;
1814    if (m->container_object) evas_object_del(m->container_object);
1815    m->container_object = NULL;
1816    m->cur.visible = 0;
1817    m->prev.visible = 0;
1818    m->realized = 0;
1819    m->zone = NULL;
1820    e_canvas_del(m->ecore_evas);
1821    ecore_evas_free(m->ecore_evas);
1822    m->ecore_evas = NULL;
1823    m->evas = NULL;
1824    eina_hash_del(_e_menu_hash, e_util_winid_str_get(m->evas_win), m);
1825    m->evas_win = 0;
1826 }
1827
1828 static void
1829 _e_menu_activate_internal(E_Menu *m, E_Zone *zone)
1830 {
1831    E_Menu_Category *cat = NULL;
1832    Eina_List *l;
1833
1834    if (m->pre_activate_cb.func)
1835      m->pre_activate_cb.func(m->pre_activate_cb.data, m);
1836    m->fast_mouse = 0;
1837    m->pending_new_submenu = 0;
1838    if (!_e_menu_win)
1839      {
1840         _e_menu_win = ecore_x_window_input_new(zone->container->win, 
1841                                                zone->x, zone->y,
1842                                                zone->w, zone->h);
1843         ecore_x_window_show(_e_menu_win);
1844         if (!e_grabinput_get(_e_menu_win, 1, _e_menu_win))
1845           {
1846              ecore_x_window_free(_e_menu_win);
1847              _e_menu_win = 0;
1848              return;
1849           }
1850      }
1851    if ((m->zone) && (m->zone->container != zone->container))
1852      {
1853         printf("FIXME: cannot move menus between containers yet\n");
1854         return;
1855      }
1856    m->zone = zone;
1857    if (!m->active)
1858      {
1859         /* this remove is in case the menu is marked as inactive but hasn't */
1860         /* been removed from the list yet */
1861         if (m->in_active_list)
1862           {
1863              _e_active_menus = eina_list_remove(_e_active_menus, m);
1864              m->in_active_list = 0;
1865              e_object_unref(E_OBJECT(m));
1866           }
1867         _e_active_menus = eina_list_append(_e_active_menus, m);
1868         m->in_active_list = 1;
1869         m->active = 1;
1870         e_object_ref(E_OBJECT(m));
1871      }
1872    /* the foreign menu items */
1873    if (m->category)
1874      {
1875         cat = eina_hash_find(_e_menu_categories, m->category);
1876         if (cat)
1877           {
1878              E_Menu_Category_Callback *cb;
1879              EINA_LIST_FOREACH(cat->callbacks, l, cb)
1880                if (cb->create) cb->create(m, cat->data, cb->data);
1881           }
1882      }
1883    m->cur.visible = 1;
1884 }
1885
1886 static void
1887 _e_menu_deactivate_all(void)
1888 {
1889    Eina_List *tmp;
1890    E_Menu *m;
1891
1892    tmp = _e_active_menus_copy_ref();
1893
1894    EINA_LIST_FREE(tmp, m)
1895      {
1896         e_menu_deactivate(m);
1897         m->parent_item = NULL;
1898         e_object_unref(E_OBJECT(m));
1899      }
1900    _e_menu_activate_floating = 0;
1901    _e_menu_activate_maybe_drag = 0;
1902    _e_menu_activate_dragging = 0;
1903 }
1904
1905 static void
1906 _e_menu_deactivate_above(E_Menu *ma)
1907 {
1908    Eina_List *tmp;
1909    int above = 0;
1910    E_Menu *m;
1911
1912    tmp = _e_active_menus_copy_ref();
1913
1914    EINA_LIST_FREE(tmp, m)
1915      {
1916         if (above)
1917           {
1918              e_menu_deactivate(m);
1919              m->parent_item = NULL;
1920           }
1921         if (ma == m) above = 1;
1922         e_object_unref(E_OBJECT(m));
1923      }
1924 }
1925
1926 static void
1927 _e_menu_submenu_activate(E_Menu_Item *mi)
1928 {
1929    if (!mi->menu->active) return;
1930    if (mi->menu->fast_mouse)
1931      {
1932         mi->menu->pending_new_submenu = 1;
1933         return;
1934      }
1935    mi->menu->pending_new_submenu = 0;
1936    _e_menu_deactivate_above(mi->menu);
1937    if (mi->submenu_pre_cb.func)
1938      mi->submenu_pre_cb.func(mi->submenu_pre_cb.data, mi->menu, mi);
1939    if (mi->submenu)
1940      {
1941         E_Menu *m;
1942         
1943         m = mi->submenu;
1944         e_object_ref(E_OBJECT(m));
1945         m->parent_item = mi;
1946         _e_menu_activate_internal(m, mi->menu->zone);
1947         _e_menu_reposition(m);
1948         e_object_unref(E_OBJECT(m));
1949      }
1950 }
1951
1952 static void
1953 _e_menu_submenu_deactivate(E_Menu_Item *mi)
1954 {
1955    if (!mi->menu->active) return;
1956    if (mi->submenu_post_cb.func)
1957      mi->submenu_post_cb.func(mi->submenu_post_cb.data, mi->menu, mi);
1958 }
1959
1960 static void
1961 _e_menu_reposition(E_Menu *m)
1962 {
1963    Eina_List *l, *tmp;
1964    E_Menu_Item *mi;
1965    int parent_item_bottom;
1966    
1967    if (!m->parent_item) return;
1968    m->cur.x = m->parent_item->menu->cur.x + m->parent_item->menu->cur.w;
1969
1970    parent_item_bottom = m->parent_item->menu->cur.y + m->parent_item->y;
1971    if (m->cur.h > m->zone->h)
1972      {
1973         /* menu is larger than screen */
1974         if (parent_item_bottom > (m->zone->h / 2))
1975           /* more is shown if menu goes up */
1976           m->cur.y = (parent_item_bottom - (m->container_h + 1)); 
1977         else
1978           /* more is shown if menu goes down */
1979           m->cur.y = parent_item_bottom - m->container_y;
1980      }
1981    else
1982      {  
1983         /* menu is smaller than screen */
1984         if (((parent_item_bottom + m->cur.h - m->container_y) > m->zone->h) && 
1985            (parent_item_bottom > (m->zone->h / 2)))
1986           /* menu is partially out of screen and more is shown if menu goes up */
1987           m->cur.y = (parent_item_bottom - (m->container_h + 1)) + m->parent_item->h; 
1988         else
1989           m->cur.y = parent_item_bottom - m->container_y;
1990      }
1991    
1992    /* FIXME: this will suck for big menus */
1993    tmp = _e_active_menus_copy_ref();
1994
1995    EINA_LIST_FOREACH(m->items, l, mi)
1996      if ((mi->active) && (mi->submenu)) _e_menu_reposition(mi->submenu);
1997
1998    _e_menu_list_free_unref(tmp);
1999 }
2000
2001 static int
2002 _e_menu_active_call(void)
2003 {
2004    E_Menu_Item *mi;
2005
2006    mi = _e_menu_item_active_get();
2007    if (mi)
2008      {
2009         if (mi->submenu) return 0;
2010         if (mi->check)
2011           e_menu_item_toggle_set(mi, !mi->toggle);
2012         if ((mi->radio) && (!e_menu_item_toggle_get(mi)))
2013           e_menu_item_toggle_set(mi, 1);
2014         if (mi->cb.func)
2015           mi->cb.func(mi->cb.data, mi->menu, mi);
2016         return 1;
2017      }
2018    return -1;
2019 }
2020
2021 static int
2022 _e_menu_realize_call(E_Menu_Item *mi)
2023 {
2024    if (mi)
2025      {
2026         if (mi->realize_cb.func)
2027           {
2028              mi->realize_cb.func(mi->realize_cb.data, mi->menu, mi);
2029              return 1;
2030           }
2031      }
2032    return 0;
2033 }
2034
2035 static void
2036 _e_menu_item_activate_next(void)
2037 {
2038    E_Menu_Item *mi;
2039    Eina_List *ll;
2040
2041    ll = _e_menu_list_item_active_get();
2042    mi = _e_menu_item_active_get();
2043    if (ll && mi) 
2044      {
2045         /* Look at the next item and then cycle until we're not on
2046          * a separator. */
2047         do
2048           { 
2049              if (!eina_list_next(ll)) 
2050                ll = mi->menu->items;
2051              else
2052                ll = eina_list_next(ll);
2053              mi = eina_list_data_get(ll);
2054           }
2055         while (mi->separator || mi->disable);
2056
2057         e_menu_item_active_set(mi, 1);
2058         _e_menu_item_ensure_onscreen(mi);
2059         return;
2060      }
2061
2062    _e_menu_activate_first();
2063 }
2064
2065 static void
2066 _e_menu_item_activate_previous(void)
2067 {
2068    E_Menu_Item *mi;
2069    Eina_List *ll;
2070
2071    ll = _e_menu_list_item_active_get();
2072    mi = _e_menu_item_active_get();
2073    if (ll && mi) 
2074      {
2075         /* Look at the prev item and then cycle until we're not on
2076          * a separator. */
2077         do
2078           { 
2079              if (!eina_list_prev(ll)) 
2080                ll = eina_list_last(ll);
2081              else
2082                ll = eina_list_prev(ll);
2083              mi = eina_list_data_get(ll);
2084           }
2085         while ((mi->separator) || (mi->disable));
2086
2087         e_menu_item_active_set(mi, 1);
2088         _e_menu_item_ensure_onscreen(mi);
2089         return;
2090      }
2091
2092    _e_menu_activate_first();
2093 }
2094
2095 static void
2096 _e_menu_item_activate_first(void)
2097 {
2098    E_Menu *m;
2099    Eina_List *ll;
2100    E_Menu_Item *mi;
2101
2102    m = _e_menu_active_get();
2103    if (m)
2104      {
2105         ll = m->items;
2106         mi = eina_list_data_get(ll);
2107         while ((mi->separator) && eina_list_next(ll))
2108           {
2109              ll = eina_list_next(ll);
2110              mi = eina_list_data_get(ll);
2111           }
2112         if (mi->separator) return;
2113         e_menu_item_active_set(mi, 1);
2114         _e_menu_item_ensure_onscreen(mi);
2115         return;
2116      }
2117    _e_menu_activate_first();
2118 }
2119
2120 static void
2121 _e_menu_item_activate_last(void)
2122 {
2123    E_Menu *m;
2124    Eina_List *ll;
2125    E_Menu_Item *mi;
2126
2127    m = _e_menu_active_get();
2128    if (m)
2129      {
2130         ll = eina_list_last(m->items);
2131         mi = eina_list_data_get(ll);
2132         while ((mi->separator) && eina_list_prev(ll))
2133           {
2134              ll = eina_list_prev(ll);
2135              mi = eina_list_data_get(ll);
2136           }
2137         if (mi->separator) return;
2138         e_menu_item_active_set(mi, 1);
2139         _e_menu_item_ensure_onscreen(mi);
2140         return;
2141      }
2142    _e_menu_activate_first();
2143 }
2144
2145 static void
2146 _e_menu_item_activate_nth(int n)
2147 {
2148    E_Menu *m;
2149    E_Menu_Item *mi;
2150    Eina_List *ll;
2151    int i = -1;
2152    
2153    mi = _e_menu_item_active_get();
2154    if (!mi)
2155      {
2156         _e_menu_activate_first();
2157         mi = _e_menu_item_active_get();
2158         if (!mi) return;
2159      }
2160    m = mi->menu;
2161    EINA_LIST_FOREACH(m->items, ll, mi)
2162      {
2163         if (!mi->separator) i++;
2164         if (i == n) break;
2165      }
2166    e_menu_item_active_set(mi, 1);
2167    _e_menu_item_ensure_onscreen(mi);
2168 }
2169
2170 static void
2171 _e_menu_item_activate_char(const char * key_compose)
2172 {
2173    E_Menu *m;
2174    E_Menu_Item *mi;
2175    Eina_List *ll, *ll_orig;
2176
2177    /* Ignore modifiers and such. */
2178    if (!key_compose) return;
2179
2180    /* Check we've got a menu and it's active. */
2181    m = _e_menu_active_get();
2182    if (!m) 
2183      {
2184         if (!_e_active_menus) return;
2185         m = eina_list_data_get(_e_active_menus);
2186         if (!m) return;
2187      }
2188
2189    ll = _e_menu_list_item_active_get();
2190    /* If we don't have an active item, start from the top of the list. */
2191    if (!ll)
2192      {
2193         ll = m->items;
2194         mi = eina_list_data_get(ll);
2195         /* Only check the current item if it wasn't active before. */
2196         if (!mi->separator && mi->label && !strncasecmp(key_compose, mi->label, strlen(key_compose))) 
2197           {
2198              e_menu_item_active_set(mi, 1);
2199              _e_menu_item_ensure_onscreen(mi);
2200              return;
2201           }
2202      }
2203
2204    ll_orig = ll;
2205
2206    mi = eina_list_data_get(ll);
2207    if (!eina_list_next(ll)) 
2208      ll = mi->menu->items;
2209    else
2210      ll = eina_list_next(ll);
2211    mi = eina_list_data_get(ll);
2212
2213    /* While we don't have a label OR we don't match  AND we haven't
2214     * wrapped around */
2215    while ((!mi->label || strncasecmp(key_compose, mi->label, strlen(key_compose))) 
2216           && ll != ll_orig)
2217      {
2218         do
2219           {
2220              if (!eina_list_next(ll)) 
2221                ll = mi->menu->items;
2222              else
2223                ll = eina_list_next(ll);
2224              mi = eina_list_data_get(ll);
2225           }
2226         while (mi->separator);
2227      }
2228
2229    e_menu_item_active_set(mi, 1);
2230    _e_menu_item_ensure_onscreen(mi);
2231    return;
2232 }
2233
2234 static void
2235 _e_menu_activate_next(void)
2236 {
2237    E_Menu_Item *mi;
2238
2239    mi = _e_menu_item_active_get();
2240    if (mi)
2241      {
2242         if (mi->submenu)
2243           {
2244              if (mi->submenu->items)
2245                {
2246                   mi = eina_list_data_get(mi->submenu->items);
2247                   e_menu_item_active_set(mi, 1);
2248                   _e_menu_item_ensure_onscreen(mi);
2249                }
2250           }
2251         return;
2252      }
2253    _e_menu_activate_first();
2254 }
2255
2256 static void
2257 _e_menu_activate_previous(void)
2258 {
2259    E_Menu_Item *mi;
2260
2261    mi = _e_menu_item_active_get();
2262    if (mi)
2263      {
2264         if (mi->menu->parent_item)
2265           {
2266              mi = mi->menu->parent_item;
2267              e_menu_item_active_set(mi, 1);
2268              _e_menu_item_ensure_onscreen(mi);
2269           }
2270         return;
2271      }
2272    _e_menu_activate_last();
2273 }
2274
2275 static void
2276 _e_menu_activate_first(void)
2277 {
2278    E_Menu *m;
2279    E_Menu_Item *mi;
2280    Eina_List *ll;
2281    
2282    if (!_e_active_menus) return;
2283    m = eina_list_data_get(_e_active_menus);
2284    if (!m->items) return;
2285    ll = m->items;
2286    mi = eina_list_data_get(ll);
2287    while ((mi->separator) && eina_list_next(ll))
2288      {
2289         ll = eina_list_next(ll);
2290         mi = eina_list_data_get(ll);
2291      }
2292    if (mi->separator) return;
2293    e_menu_item_active_set(mi, 1);   
2294    _e_menu_item_ensure_onscreen(mi);
2295 }
2296
2297 static void
2298 _e_menu_activate_last(void)
2299 {
2300    E_Menu *m;
2301    E_Menu_Item *mi;
2302    Eina_List *ll;
2303    
2304    if (!_e_active_menus) return;
2305    m = eina_list_data_get(_e_active_menus);
2306    if (!m->items) return;
2307    ll = eina_list_last(m->items);
2308    mi = eina_list_data_get(ll);
2309    while ((mi->separator) && eina_list_prev(ll))
2310      {
2311         ll = eina_list_prev(ll);
2312         mi = eina_list_data_get(ll);
2313      }
2314    if (mi->separator) return;
2315    e_menu_item_active_set(mi, 1);   
2316    _e_menu_item_ensure_onscreen(mi);
2317 }
2318
2319 #if 0
2320 static void
2321 _e_menu_activate_nth(int n)
2322 {
2323    E_Menu *m;
2324    E_Menu_Item *mi;
2325    Eina_List *ll;
2326    int i = -1;
2327    
2328    mi = _e_menu_item_active_get();
2329    if (!mi)
2330      {
2331         _e_menu_activate_first();
2332         mi = _e_menu_item_active_get();
2333         if (!mi) return;
2334      }
2335    m = mi->menu;
2336    EINA_LIST_FOREACH(m->items, ll, mi)
2337      {
2338         if (!mi->separator) i++;
2339         if (i == n)
2340           {
2341              e_menu_item_active_set(mi, 1);
2342              _e_menu_item_ensure_onscreen(mi);
2343              return;
2344           }
2345      }
2346 }
2347 #endif
2348
2349 static E_Menu *
2350 _e_menu_active_get(void)
2351 {
2352    if (_e_active_menu_item) return _e_active_menu_item->menu;
2353    return NULL;
2354 }
2355
2356 static E_Menu_Item *
2357 _e_menu_item_active_get(void)
2358 {
2359    return _e_active_menu_item;
2360 }
2361
2362 static Eina_List * 
2363 _e_menu_list_item_active_get(void)
2364 {
2365    if (_e_active_menu_item)
2366      return _e_active_menu_item->list_position;
2367    else
2368      return NULL;
2369 }
2370
2371 static int
2372 _e_menu_outside_bounds_get(int xdir, int ydir)
2373 {
2374    Eina_List *l;
2375    E_Menu *m;
2376    int outl = 0;
2377    int outr = 0;
2378    int outt = 0;
2379    int outb = 0;
2380    int i = 0;
2381
2382    EINA_LIST_FOREACH(_e_active_menus, l, m)
2383      {
2384         if (m->cur.x < m->zone->x + e_config->menu_autoscroll_margin)
2385           {
2386              i = m->zone->x - m->cur.x + e_config->menu_autoscroll_margin;
2387              if (i > outl) outl = i;
2388           }
2389         if (m->cur.y < m->zone->y + e_config->menu_autoscroll_margin)
2390           {
2391              i = m->zone->y - m->cur.y + e_config->menu_autoscroll_margin;
2392              if (i > outt) outt = i;
2393           }
2394         if ((m->cur.x + m->cur.w) > (m->zone->w - e_config->menu_autoscroll_margin))
2395           {
2396              i = m->cur.x + m->cur.w - (m->zone->x + m->zone->w - e_config->menu_autoscroll_margin);
2397              if (i > outr) outr = i;
2398           }
2399         if ((m->cur.y + m->cur.h) > (m->zone->h - e_config->menu_autoscroll_margin))
2400           {
2401              i = m->cur.y + m->cur.h - (m->zone->y + m->zone->h - e_config->menu_autoscroll_margin);
2402              if (i > outb) outb = i;
2403           }
2404      }
2405    if (xdir == -1)
2406      {
2407         if (outl) return outl;
2408      }
2409    else if (xdir == 1)
2410      {
2411         if (outr) return outr;
2412      }
2413    else if (ydir == -1)
2414      {
2415         if (outt) return outt;
2416      }
2417    else if (ydir == 1)
2418      {
2419         if (outb) return outb;
2420      }
2421    return 0;
2422 }
2423
2424 static void
2425 _e_menu_scroll_by(int dx, int dy)
2426 {
2427    Eina_List *l;
2428    E_Menu *m;
2429
2430    EINA_LIST_FOREACH(_e_active_menus, l, m)
2431      {
2432         m->cur.x += dx;
2433         m->cur.y += dy;
2434      }
2435 }
2436
2437 static void
2438 _e_menu_mouse_autoscroll_check(void)
2439 {
2440    int autoscroll_x = 0;
2441    int autoscroll_y = 0;
2442    
2443    if (_e_menu_x - e_config->menu_autoscroll_cursor_margin <= 0)
2444      {
2445         if (_e_menu_outside_bounds_get(-1, 0)) autoscroll_x = -1;
2446      }
2447    if (_e_menu_y - e_config->menu_autoscroll_cursor_margin <= 0)
2448      {
2449         if (_e_menu_outside_bounds_get(0, -1)) autoscroll_y = -1;
2450      }
2451    if ((!autoscroll_x) && (!autoscroll_y))
2452      {
2453         if (_e_active_menus)
2454           {
2455              E_Menu *m;
2456              
2457              m = eina_list_data_get(_e_active_menus);
2458              if (_e_menu_x + e_config->menu_autoscroll_cursor_margin >= (m->zone->w - 1))
2459                {
2460                   if (_e_menu_outside_bounds_get(1, 0)) autoscroll_x = 1;
2461                }
2462              if (_e_menu_y + e_config->menu_autoscroll_cursor_margin >= (m->zone->h - 1))
2463                {
2464                   if (_e_menu_outside_bounds_get(0, 1)) autoscroll_y = 1;
2465                }
2466           }
2467      }
2468    _e_menu_autoscroll_x = autoscroll_x;
2469    _e_menu_autoscroll_y = autoscroll_y;
2470    if ((!autoscroll_x) && (!autoscroll_y)) return;
2471    if (_e_menu_scroll_animator) return;
2472    _e_menu_scroll_animator = ecore_animator_add(_e_menu_cb_scroll_animator,
2473                                                 NULL);
2474    _e_menu_scroll_start = ecore_loop_time_get();
2475 }
2476
2477 static void
2478 _e_menu_item_ensure_onscreen(E_Menu_Item *mi)
2479 {
2480    int x = 0, y = 0, w = 0, h = 0;
2481    int dx = 0, dy = 0;
2482    
2483    if (!mi->menu) return;
2484    if (!mi->menu->zone) return;
2485    x = mi->x + mi->menu->cur.x;
2486    y = mi->y + mi->menu->cur.y;
2487    w = mi->w;
2488    h = mi->h;
2489    if ((x + w) > (mi->menu->zone->x + mi->menu->zone->w))
2490      dx = (mi->menu->zone->x + mi->menu->zone->w) - (x + w);
2491    else if (x < mi->menu->zone->x)
2492      dx = mi->menu->zone->x - x;
2493    if ((y + h) > (mi->menu->zone->y + mi->menu->zone->h))
2494      dy = (mi->menu->zone->y + mi->menu->zone->h) - (y + h);
2495    else if (y < mi->menu->zone->y)
2496      dy = mi->menu->zone->y - y;
2497    if ((dx != 0) || (dy != 0))
2498      _e_menu_scroll_by(dx, dy);
2499 }
2500
2501 static int
2502 _e_menu_auto_place(E_Menu *m, int x, int y, int w, int h)
2503 {
2504    double xr, yr;
2505    
2506    _e_menu_realize(m);
2507    /* +-----+
2508     * |\ T /|
2509     * | \ / |
2510     * |L X R|
2511     * | / \ |
2512     * |/ B \|
2513     * +-----+
2514     * 
2515     * quadrants... which one
2516     */
2517    if (w != m->zone->w)
2518      xr = (double)(x - m->zone->x) /
2519      (double)(m->zone->w - w);
2520    else
2521      xr = 0.0;
2522    if (h != m->zone->h)
2523      yr = (double)(y - m->zone->y) /
2524      (double)(m->zone->h - h);
2525    else
2526      yr = 0.0;
2527    if ((xr + yr) < 0.99) /* top or left */
2528      {
2529         if (((1.0 - yr) + xr) <= 1.0)
2530           /* L */
2531           {
2532              m->cur.x = x + w;
2533              if (y < (m->zone->y + ((m->zone->h * 1) / 3)))
2534                m->cur.y = y;
2535              else if (y < (m->zone->y + ((m->zone->h * 2) / 3)))
2536                m->cur.y = y + ((h - m->cur.h) / 2);
2537              else
2538                m->cur.y = y + h - m->cur.h;
2539              return 1;
2540           }
2541         else
2542           /* T */
2543           {
2544              m->cur.y = y + h;
2545              if (x < (m->zone->x + ((m->zone->w * 1) / 3)))
2546                m->cur.x = x;
2547              else if (x < (m->zone->x + ((m->zone->w * 2) / 3)))
2548                m->cur.x = x + ((w - m->cur.w) / 2);
2549              else
2550                m->cur.x = x + w - m->cur.w;
2551              return 3;
2552           }
2553      }
2554    else /* bottom or right */
2555      {
2556         if (((1.0 - yr) + xr) <= 1.01)
2557           /* B */
2558           {
2559              m->cur.y = y - m->cur.h;
2560              if (x < (m->zone->x + ((m->zone->w * 1) / 3)))
2561                m->cur.x = x;
2562              else if (x < (m->zone->x + ((m->zone->w * 2) / 3)))
2563                m->cur.x = x + ((w - m->cur.w) / 2);
2564              else
2565                m->cur.x = x + w - m->cur.w;
2566              return 4;
2567           }
2568         else
2569           /* R */
2570           {
2571              m->cur.x = x - m->cur.w;
2572              if (y < (m->zone->y + ((m->zone->h * 1) / 3)))
2573                m->cur.y = y;
2574              else if (y < (m->zone->y + ((m->zone->h * 2) / 3)))
2575                m->cur.y = y + ((h - m->cur.h) / 2);
2576              else
2577                m->cur.y = y + h - m->cur.h;
2578              return 2;
2579           }
2580      }
2581    return 0;
2582 }
2583
2584 static void
2585 _e_menu_cb_ecore_evas_resize(Ecore_Evas *ee)
2586 {
2587    Evas *evas;
2588    Evas_Object *o;
2589    Evas_Coord w, h;
2590    
2591    evas = ecore_evas_get(ee);
2592    evas_output_viewport_get(evas, NULL, NULL, &w, &h);
2593    o = evas_object_name_find(evas, "menu/background");
2594    evas_object_resize(o, w, h);
2595 }
2596
2597 static void
2598 _e_menu_cb_item_in(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
2599 {
2600    E_Menu_Item *mi;
2601    
2602    mi = data;
2603    e_menu_item_active_set(mi, 1);
2604 }
2605
2606 static void
2607 _e_menu_cb_item_out(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
2608 {
2609    E_Menu_Item *mi;
2610    Evas_Event_Mouse_In *ev;
2611    
2612    mi = data;
2613    ev = event_info;
2614    e_menu_item_active_set(mi, 0);
2615    if (_e_menu_activate_maybe_drag)
2616      {
2617         if (mi->drag_cb.func)
2618           {
2619              /* User is dragging a draggable item elsewhere. */
2620              mi->drag.x = ev->output.x - (ev->output.x - mi->x);
2621              mi->drag.y = ev->output.y - (ev->output.y - mi->y);
2622              _e_menu_deactivate_all();
2623              mi->drag_cb.func(mi->drag_cb.data, mi->menu, mi);
2624           }
2625         /* Either way, the maybe drag stops here. */
2626         _e_menu_activate_maybe_drag = 0;
2627      }
2628 }
2629
2630 static Eina_Bool
2631 _e_menu_cb_key_down(void *data __UNUSED__, int type __UNUSED__, void *event)
2632 {
2633    Ecore_Event_Key *ev;
2634    
2635    ev = event;
2636    if (ev->window != _e_menu_win) return ECORE_CALLBACK_PASS_ON;
2637    if ((!strcmp(ev->key, "Up")) || (!strcmp(ev->key, "KP_Up")))
2638      _e_menu_item_activate_previous();
2639    else if ((!strcmp(ev->key, "Down")) || (!strcmp(ev->key, "KP_Down")))
2640      _e_menu_item_activate_next();
2641    else if ((!strcmp(ev->key, "Left")) || (!strcmp(ev->key, "KP_Left")))
2642      _e_menu_activate_previous();
2643    else if ((!strcmp(ev->key, "Right")) || (!strcmp(ev->key, "KP_Right")))
2644      _e_menu_activate_next();
2645    else if ((!strcmp(ev->key, "Home")) || (!strcmp(ev->key, "KP_Home")))
2646      _e_menu_item_activate_first();
2647    else if ((!strcmp(ev->key, "End")) || (!strcmp(ev->key, "KP_End")))
2648      _e_menu_item_activate_last();
2649    else if (!strcmp(ev->key, "space"))
2650      _e_menu_active_call();
2651    else if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter")))
2652      {
2653         _e_menu_active_call();
2654         _e_menu_deactivate_all();
2655      }
2656    else if (!strcmp(ev->key, "Escape"))
2657      _e_menu_deactivate_all();
2658    else if ((!strcmp(ev->key, "1")) || (!strcmp(ev->key, "KP_1")))
2659      _e_menu_item_activate_first();
2660    else if ((!strcmp(ev->key, "2")) || (!strcmp(ev->key, "KP_2")))
2661      _e_menu_item_activate_nth(1);
2662    else if ((!strcmp(ev->key, "3")) || (!strcmp(ev->key, "KP_3")))
2663      _e_menu_item_activate_nth(2);
2664    else if ((!strcmp(ev->key, "4")) || (!strcmp(ev->key, "KP_4")))
2665      _e_menu_item_activate_nth(3);
2666    else if ((!strcmp(ev->key, "5")) || (!strcmp(ev->key, "KP_5")))
2667      _e_menu_item_activate_nth(4);
2668    else if ((!strcmp(ev->key, "6")) || (!strcmp(ev->key, "KP_6")))
2669      _e_menu_item_activate_nth(5);
2670    else if ((!strcmp(ev->key, "7")) || (!strcmp(ev->key, "KP_7")))
2671      _e_menu_item_activate_nth(6);
2672    else if ((!strcmp(ev->key, "8")) || (!strcmp(ev->key, "KP_8")))
2673      _e_menu_item_activate_nth(7);
2674    else if ((!strcmp(ev->key, "9")) || (!strcmp(ev->key, "KP_9")))
2675      _e_menu_item_activate_nth(8);
2676    else if ((!strcmp(ev->key, "0")) || (!strcmp(ev->key, "KP_0")))
2677      _e_menu_item_activate_last();
2678    else if (ev->compose)
2679      _e_menu_item_activate_char(ev->compose);
2680    return ECORE_CALLBACK_PASS_ON;
2681 }
2682
2683 static Eina_Bool
2684 _e_menu_cb_key_up(void *data __UNUSED__, int type __UNUSED__, void *event)
2685 {
2686    Ecore_Event_Key *ev;
2687    
2688    ev = event;
2689    if (ev->window != _e_menu_win) return ECORE_CALLBACK_PASS_ON;
2690    return ECORE_CALLBACK_PASS_ON;
2691 }
2692
2693 /* we need all of these because menus are special and grab the mouse and
2694  * keyboard and thus the normal event mechanism doesn't work, so we feed
2695  * events directly to the canvases from our grab window
2696  */
2697
2698 static Eina_Bool
2699 _e_menu_cb_mouse_down(void *data __UNUSED__, int type __UNUSED__, void *event)
2700 {
2701    Ecore_Event_Mouse_Button *ev;
2702    
2703    ev = event;
2704    if (ev->window != _e_menu_win) return ECORE_CALLBACK_PASS_ON;
2705
2706    /* Only allow dragging from floating menus for now.
2707     * The reason for this is that for non floating menus, 
2708     * the mouse is already down and dragging, so the decision
2709     * to start a drag is much more complex.
2710     */
2711    if (_e_menu_activate_floating)
2712      _e_menu_activate_maybe_drag = 1;
2713
2714    return ECORE_CALLBACK_PASS_ON;
2715 }
2716
2717 static Eina_Bool
2718 _e_menu_cb_mouse_up(void *data __UNUSED__, int type __UNUSED__, void *event)
2719 {
2720    Ecore_Event_Mouse_Button *ev;
2721    Ecore_X_Time t;
2722    int ret = 0;
2723    
2724    ev = event;
2725    if (ev->window != _e_menu_win) return ECORE_CALLBACK_PASS_ON;
2726
2727    t = ev->timestamp - _e_menu_activate_time;
2728    if ((_e_menu_activate_time != 0) &&
2729        (t < (e_config->menus_click_drag_timeout * 1000)))
2730      {
2731         _e_menu_activate_floating = 1;
2732         return ECORE_CALLBACK_PASS_ON;
2733      }
2734
2735    if (_e_menu_activate_dragging)
2736      {
2737         /* FIXME: This is a drop, which is not allowed for now.
2738          * Once dragging is working, this will be subject to some experimenattion.
2739          */
2740      }
2741    else
2742       ret = _e_menu_active_call();
2743    _e_menu_activate_maybe_drag = 0;
2744    _e_menu_activate_dragging = 0;
2745    if (ret == 1)
2746      {
2747 /* allow mouse to pop down menu if clicked elsewhere */ 
2748 /*      if (_e_menu_activate_time != 0) */
2749           _e_menu_deactivate_all();
2750      }
2751    else if (ret == -1)
2752      _e_menu_deactivate_all();
2753    else if (!_e_menu_activate_floating)
2754      _e_menu_deactivate_all();
2755    return ECORE_CALLBACK_PASS_ON;
2756 }
2757
2758 static Eina_Bool
2759 _e_menu_cb_mouse_move(void *data __UNUSED__, int type __UNUSED__, void *event)
2760 {
2761    Ecore_Event_Mouse_Move *ev;
2762    Eina_List *l, *tmp;
2763    E_Menu *m;
2764    int dx, dy, d;
2765    double dt;
2766    double fast_move_threshold;
2767    int is_fast = 0;
2768    
2769    ev = event;
2770    if (ev->window != _e_menu_win) return ECORE_CALLBACK_PASS_ON;
2771    fast_move_threshold = e_config->menus_fast_mouse_move_threshhold;
2772    dx = ev->x - _e_menu_x;
2773    dy = ev->y - _e_menu_y;
2774    d = (dx * dx) + (dy * dy);
2775    dt = (double)(ev->timestamp - _e_menu_time) / 1000.0;
2776    dt = dt * dt;
2777    if ((dt > 0.0) && ((d / dt) >= (fast_move_threshold * fast_move_threshold)))
2778      is_fast = 1;
2779
2780    tmp = _e_active_menus_copy_ref();
2781
2782    EINA_LIST_FOREACH(_e_active_menus, l, m)
2783      {
2784         if ((m->realized) && (m->cur.visible))
2785           {
2786              if (is_fast)
2787                m->fast_mouse = 1;
2788              else if (dt > 0.0)
2789                {
2790                   m->fast_mouse = 0;
2791                   if (m->pending_new_submenu)
2792                     {
2793                        E_Menu_Item *mi;
2794                        
2795                        mi = _e_menu_item_active_get();
2796                        if (mi)
2797                          _e_menu_submenu_activate(mi);
2798                     }
2799                }
2800              evas_event_feed_mouse_move(m->evas,
2801                                         ev->x - m->cur.x + m->zone->x,
2802                                         ev->y - m->cur.y + m->zone->y,
2803                                         ev->timestamp, NULL);
2804           }
2805      }
2806
2807    _e_menu_list_free_unref(tmp);
2808
2809    _e_menu_x = ev->x;
2810    _e_menu_y = ev->y;
2811    _e_menu_time = ev->timestamp;
2812    _e_menu_mouse_autoscroll_check();
2813    return ECORE_CALLBACK_PASS_ON;
2814 }
2815
2816 static Eina_Bool
2817 _e_menu_cb_mouse_wheel(void *data __UNUSED__, int type __UNUSED__, void *event)
2818 {
2819    Ecore_Event_Mouse_Wheel *ev;
2820    
2821    ev = event;
2822    if (ev->window != _e_menu_win) return ECORE_CALLBACK_PASS_ON;
2823    if (ev->z < 0) /* up */
2824      {
2825         int i;
2826         
2827         for (i = ev->z; i < 0; i++)
2828           _e_menu_item_activate_previous();
2829      }
2830    else if (ev->z > 0) /* down */
2831      {
2832         int i;
2833         
2834         for (i = ev->z; i > 0; i--)
2835           _e_menu_item_activate_next();
2836      }
2837    return ECORE_CALLBACK_PASS_ON;
2838 }
2839
2840 static Eina_Bool
2841 _e_menu_cb_scroll_animator(void *data __UNUSED__)
2842 {
2843    double t, dt;
2844    double dx, dy;
2845    int out;
2846    double spd;
2847    
2848    t = ecore_loop_time_get();
2849    spd = e_config->menus_scroll_speed;
2850    dt = t - _e_menu_scroll_start;
2851    _e_menu_scroll_start = t;
2852    dx = 0;
2853    dy = 0;
2854    if (_e_menu_autoscroll_x)
2855      {
2856         out = _e_menu_outside_bounds_get(_e_menu_autoscroll_x, 0);
2857         dx = (-_e_menu_autoscroll_x) * spd * dt;
2858         if (_e_menu_autoscroll_x == -1)
2859           {
2860              if (dx > out) dx = out;
2861           }
2862         else
2863           {
2864              if (dx < -out) dx = -out;
2865           }
2866      }
2867    if (_e_menu_autoscroll_y)
2868      {
2869         out = _e_menu_outside_bounds_get(0, _e_menu_autoscroll_y);
2870         dy = (-_e_menu_autoscroll_y) * spd * dt;
2871         if (_e_menu_autoscroll_y == -1)
2872           {
2873              if (dy > out) dy = out;
2874           }
2875         else
2876           {
2877              if (dy < -out) dy = -out;
2878           }
2879      }
2880    _e_menu_scroll_by(dx, dy);
2881    _e_menu_mouse_autoscroll_check();
2882    if ((_e_menu_autoscroll_x == 0) && (_e_menu_autoscroll_y == 0))
2883      {
2884         _e_menu_scroll_animator = NULL;
2885         return 0;
2886      }
2887    return 1;
2888 }
2889
2890 static Eina_Bool
2891 _e_menu_cb_window_shape(void *data __UNUSED__, int ev_type __UNUSED__, void *ev)
2892 {
2893    Eina_List *l;
2894    Ecore_X_Event_Window_Shape *e;
2895    E_Menu *m;
2896
2897    e = ev;
2898    EINA_LIST_FOREACH(_e_active_menus, l, m)
2899      {
2900         if (m->evas_win == e->win)
2901           m->need_shape_export = 1;
2902      }
2903    return ECORE_CALLBACK_PASS_ON;
2904 }
2905
2906 static void
2907 _e_menu_cb_item_submenu_post_default(void *data __UNUSED__, E_Menu *m __UNUSED__, E_Menu_Item *mi)
2908 {
2909    E_Menu *subm;
2910
2911    if (!mi->submenu) return;
2912
2913    subm = mi->submenu;
2914    e_menu_item_submenu_set(mi, NULL);
2915    e_object_del(E_OBJECT(subm));
2916 }
2917
2918
2919 static Eina_Bool
2920 _e_menu_categories_free_cb(const Eina_Hash __UNUSED__ *hash, const void __UNUSED__ *key, void *data, void *fdata __UNUSED__)
2921 {
2922    E_Menu_Category_Callback *cb;
2923    E_Menu_Category *cat;
2924
2925    cat = (E_Menu_Category *) data;
2926    EINA_LIST_FREE(cat->callbacks, cb)
2927      free(cb); /* free the callback struct */
2928    free(cat);
2929    return EINA_TRUE;
2930 }