as promised, deprecated menu api functions have now been removed
[framework/uifw/elementary.git] / src / lib / elm_menu.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5
6 struct _Elm_Menu_Item
7 {
8    Elm_Widget_Item base;
9    Elm_Menu_Item *parent;
10    Evas_Object *content;
11    const char *icon_str;
12    const char *label;
13    Evas_Smart_Cb func;
14    unsigned int idx;
15
16    struct {
17       Evas_Object *hv, *bx, *location;
18       Eina_List *items;
19       Eina_Bool open : 1;
20    } submenu;
21
22    Eina_Bool separator : 1;
23    Eina_Bool disabled : 1;
24    Eina_Bool selected : 1;
25    Eina_Bool object_item : 1;
26 };
27
28 struct _Widget_Data
29 {
30    Evas_Object *hv, *bx, *location, *parent, *obj;
31    Eina_List *items;
32    Evas_Coord xloc, yloc;
33 };
34
35 static const char *widtype = NULL;
36 static void _del_hook(Evas_Object *obj);
37 static void _theme_hook(Evas_Object *obj);
38 static void _sizing_eval(Evas_Object *obj);
39 static void _submenu_sizing_eval(Elm_Menu_Item *parent);
40 static void _item_sizing_eval(Elm_Menu_Item *item);
41 static void _submenu_hide(Elm_Menu_Item *item);
42 static void _submenu_open(void *data, Evas_Object *obj, const char *emission, const char *source);
43 static void _parent_resize(void *data, Evas *e, Evas_Object *obj, void *event_info);
44 static void _parent_del(void *data, Evas *e, Evas_Object *obj, void *event_info);
45 static void _menu_hide(void *data, Evas_Object *obj, void *event_info);
46
47 static const char SIG_CLICKED[] = "clicked";
48
49 static const Evas_Smart_Cb_Description _signals[] = {
50    {SIG_CLICKED, ""},
51    {NULL, NULL}
52 };
53
54
55 static void
56 _del_item(Elm_Menu_Item *item)
57 {
58    Elm_Menu_Item *child;
59
60    elm_widget_item_pre_notify_del(item);
61
62    EINA_LIST_FREE(item->submenu.items, child)
63      _del_item(child);
64
65    if (item->label) eina_stringshare_del(item->label);
66    if (item->submenu.hv) evas_object_del(item->submenu.hv);
67    if (item->submenu.location) evas_object_del(item->submenu.location);
68    if (item->icon_str) eina_stringshare_del(item->icon_str);
69    elm_widget_item_del(item);
70 }
71
72 static void
73 _del_pre_hook(Evas_Object *obj)
74 {
75    Elm_Menu_Item *item;
76    Widget_Data *wd = elm_widget_data_get(obj);
77    if (!wd) return;
78
79    evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE, _parent_resize, obj);
80    evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_DEL, _parent_del, wd);
81
82    EINA_LIST_FREE(wd->items, item)
83       _del_item(item);
84
85    if (wd->hv) evas_object_del(wd->hv);
86    if (wd->location) evas_object_del(wd->location);
87 }
88
89 static void
90 _del_hook(Evas_Object *obj)
91 {
92    Widget_Data *wd = elm_widget_data_get(obj);
93    free(wd);
94 }
95
96 static void
97 _theme_hook(Evas_Object *obj)
98 {
99    Eina_List *l, *_l, *_ll, *ll = NULL;
100    Elm_Menu_Item *item;
101    Widget_Data *wd = elm_widget_data_get(obj);
102    if (!wd) return;
103    _elm_widget_mirrored_reload(obj);
104    ll = eina_list_append(ll, wd->items);
105    EINA_LIST_FOREACH(ll, _ll, l)
106      {
107         EINA_LIST_FOREACH(l, _l, item)
108           {
109              edje_object_mirrored_set(item->base.view, elm_widget_mirrored_get(obj));
110              ll = eina_list_append(ll, item->submenu.items);
111              if (item->separator)
112                _elm_theme_object_set(obj, item->base.view, "menu", "separator",
113                                      elm_widget_style_get(obj));
114              else if (item->submenu.bx)
115                {
116                   _elm_theme_object_set
117                      (obj, item->base.view, "menu", "item_with_submenu",
118                       elm_widget_style_get(obj));
119                   elm_menu_item_label_set(item, item->label);
120                   elm_menu_item_object_icon_name_set(item, item->icon_str);
121                }
122              else
123                {
124                   _elm_theme_object_set(obj, item->base.view, "menu", "item",
125                                         elm_widget_style_get(obj));
126                   elm_menu_item_label_set(item, item->label);
127                   elm_menu_item_object_icon_name_set(item, item->icon_str);
128                }
129              if (item->disabled)
130                edje_object_signal_emit
131                   (item->base.view, "elm,state,disabled", "elm");
132              else
133                edje_object_signal_emit
134                   (item->base.view, "elm,state,enabled", "elm");
135              edje_object_message_signal_process(item->base.view);
136              edje_object_scale_set(item->base.view, elm_widget_scale_get(obj) *
137                                    _elm_config->scale);
138           }
139      }
140    _sizing_eval(obj);
141 }
142
143 static void
144 _sizing_eval(Evas_Object *obj)
145 {
146    Eina_List *l;
147    Elm_Menu_Item *item;
148    Evas_Coord x_p, y_p, w_p, h_p, x2, y2, w2, h2, bx, by, bw, bh;
149    Widget_Data *wd = elm_widget_data_get(obj);
150    if ((!wd) || (!wd->parent)) return;
151    EINA_LIST_FOREACH(wd->items,l,item) _item_sizing_eval(item);
152    evas_object_geometry_get(wd->location, &x_p, &y_p, &w_p, &h_p);
153    evas_object_geometry_get(wd->parent, &x2, &y2, &w2, &h2);
154    evas_object_geometry_get(wd->bx, &bx, &by, &bw, &bh);
155
156    x_p = wd->xloc;
157    y_p = wd->yloc;
158
159    if (elm_widget_mirrored_get(obj))
160      x_p -= w_p;
161
162    if (x_p+bw > x2+w2) x_p -= x_p+bw - (x2+w2);
163    if (x_p < x2) x_p += x2 - x_p;
164
165    if (y_p+h_p+bh > y2+h2) y_p -= y_p+h_p+bh - (y2+h2);
166    if (y_p < y2) y_p += y2 - y_p;
167
168
169    evas_object_move(wd->location, x_p, y_p);
170    evas_object_resize(wd->location, bw, h_p);
171    evas_object_size_hint_min_set(wd->location, bw, h_p);
172    evas_object_size_hint_max_set(wd->location, bw, h_p);
173    elm_hover_target_set(wd->hv, wd->location);
174
175    EINA_LIST_FOREACH(wd->items,l,item)
176      {
177         if (item->submenu.open) _submenu_sizing_eval(item);
178      }
179 }
180
181 static void
182 _submenu_sizing_eval(Elm_Menu_Item *parent)
183 {
184    Eina_List *l;
185    Elm_Menu_Item *item;
186    Evas_Coord x_p, y_p, w_p, h_p, x2, y2, w2, h2, bx, by, bw, bh, px, py, pw, ph;
187    Widget_Data *wd = elm_widget_data_get(parent->base.widget);
188    if (!wd) return;
189    EINA_LIST_FOREACH(parent->submenu.items, l, item) _item_sizing_eval(item);
190    evas_object_geometry_get(parent->submenu.location, &x_p, &y_p, &w_p, &h_p);
191    evas_object_geometry_get(parent->base.view, &x2, &y2, &w2, &h2);
192    evas_object_geometry_get(parent->submenu.bx, &bx, &by, &bw, &bh);
193    evas_object_geometry_get(wd->parent, &px, &py, &pw, &ph);
194
195    x_p = x2+w2;
196    y_p = y2;
197
198    /* If it overflows on the right, adjust the x */
199    if ((x_p + bw > px + pw) || elm_widget_mirrored_get(parent->base.widget))
200      x_p = x2-bw;
201
202    /* If it overflows on the left, adjust the x - usually only happens
203     * with an RTL interface */
204    if (x_p < px)
205      x_p = x2 + w2;
206
207    /* If after all the adjustments it still overflows, fix it */
208    if (x_p + bw > px + pw)
209      x_p = x2-bw;
210
211    if (y_p+bh > py+ph)
212      y_p -= y_p+bh - (py+ph);
213    if (y_p < py)
214      y_p += y_p - y_p;
215
216    evas_object_move(parent->submenu.location, x_p, y_p);
217    evas_object_resize(parent->submenu.location, bw, h_p);
218    evas_object_size_hint_min_set(parent->submenu.location, bw, h_p);
219    evas_object_size_hint_max_set(parent->submenu.location, bw, h_p);
220    elm_hover_target_set(parent->submenu.hv, parent->submenu.location);
221
222    EINA_LIST_FOREACH(parent->submenu.items, l, item)
223      {
224         if (item->submenu.open)
225           _submenu_sizing_eval(item);
226      }
227 }
228
229 static void
230 _item_sizing_eval(Elm_Menu_Item *item)
231 {
232    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
233    if (!item->separator)
234      elm_coords_finger_size_adjust(1, &minw, 1, &minh);
235    edje_object_size_min_restricted_calc(item->base.view, &minw, &minh, minw, minh);
236    if (!item->separator)
237      elm_coords_finger_size_adjust(1, &minw, 1, &minh);
238    evas_object_size_hint_min_set(item->base.view, minw, minh);
239    evas_object_size_hint_max_set(item->base.view, maxw, maxh);
240 }
241
242 static void
243 _menu_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
244 {
245    _sizing_eval(data);
246 }
247
248 static void
249 _parent_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
250 {
251    _sizing_eval(data);
252 }
253
254 static void
255 _parent_del(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
256 {
257    Widget_Data *wd = data;
258    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESIZE, _parent_resize, wd->obj);
259    wd->parent = NULL;
260 }
261
262 static void
263 _item_move_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
264 {
265    Elm_Menu_Item *item = data;
266    if (item->submenu.open) _submenu_sizing_eval(item);
267 }
268
269 static void
270 _hover_clicked_cb(void *data, Evas_Object *obj, void *event_info)
271 {
272    _menu_hide(data, obj, event_info);
273    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
274 }
275
276 static void
277 _menu_hide(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
278 {
279    Eina_List *l;
280    Elm_Menu_Item *item2;
281    Widget_Data *wd = elm_widget_data_get(data);
282    if (!wd) return;
283    evas_object_hide(wd->hv);
284    evas_object_hide(data);
285
286    EINA_LIST_FOREACH(wd->items, l, item2)
287      {
288         if (item2->submenu.open) _submenu_hide(item2);
289      }
290 }
291
292 static void
293 _submenu_hide(Elm_Menu_Item *item)
294 {
295    Eina_List *l;
296    Elm_Menu_Item *item2;
297    evas_object_hide(item->submenu.hv);
298    item->submenu.open = EINA_FALSE;
299    EINA_LIST_FOREACH(item->submenu.items, l, item2)
300      {
301         if (item2->submenu.open) _submenu_hide(item2);
302      }
303 }
304
305 static void
306 _menu_item_select(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
307 {
308    Elm_Menu_Item *item = data;
309    if (item->submenu.items)
310      {
311         if (!item->submenu.open) _submenu_open(item, NULL, NULL, NULL);
312         else _submenu_hide(item);
313      }
314    else
315      _menu_hide(item->base.widget, NULL, NULL);
316
317    if (item->func) item->func((void *)(item->base.data), item->base.widget, item);
318 }
319
320 static void
321 _menu_item_activate(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
322 {
323    Eina_List *l;
324    Elm_Menu_Item *item2;
325    Elm_Menu_Item *item = data;
326    item->selected = 1;
327    if (item->parent)
328      {
329         EINA_LIST_FOREACH(item->parent->submenu.items, l, item2)
330           {
331              if (item2 != item) elm_menu_item_selected_set(item2, 0);
332           }
333      }
334    else
335      {
336         Widget_Data *wd = elm_widget_data_get(item->base.widget);
337         EINA_LIST_FOREACH(wd->items, l, item2)
338           {
339              if (item2 != item) elm_menu_item_selected_set(item2, 0);
340           }
341      }
342 }
343
344 static void
345 _menu_item_inactivate(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
346 {
347    Elm_Menu_Item *item = data;
348    item->selected = 0;
349    if (item->submenu.open) _submenu_hide(item);
350 }
351
352 static void
353 _submenu_open(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
354 {
355    Elm_Menu_Item *item = data;
356    item->submenu.open = EINA_TRUE;
357    evas_object_show(item->submenu.hv);
358    _sizing_eval(item->base.widget);
359 }
360
361 static void
362 _show(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
363 {
364    Widget_Data *wd = elm_widget_data_get(data);
365    if (!wd) return;
366    evas_object_show(wd->hv);
367 }
368
369 static void
370 _item_obj_create(Elm_Menu_Item *item)
371 {
372    Widget_Data *wd = elm_widget_data_get(item->base.widget);
373    if (!wd) return;
374    item->base.view = edje_object_add(evas_object_evas_get(wd->bx));
375    edje_object_mirrored_set(item->base.view, elm_widget_mirrored_get(item->base.widget));
376    evas_object_size_hint_weight_set(item->base.view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
377    evas_object_size_hint_fill_set(item->base.view, EVAS_HINT_FILL, EVAS_HINT_FILL);
378    _elm_theme_object_set(item->base.widget, item->base.view, "menu", "item",  elm_widget_style_get(item->base.widget));
379    edje_object_signal_callback_add(item->base.view, "elm,action,click", "",
380                                    _menu_item_select, item);
381    edje_object_signal_callback_add(item->base.view, "elm,action,activate", "",
382                                    _menu_item_activate, item);
383    edje_object_signal_callback_add(item->base.view, "elm,action,inactivate", "",
384                                    _menu_item_inactivate, item);
385    evas_object_show(item->base.view);
386 }
387
388 static void
389 _item_separator_obj_create(Elm_Menu_Item *item)
390 {
391    Widget_Data *wd = elm_widget_data_get(item->base.widget);
392    if (!wd) return;
393    item->base.view = edje_object_add(evas_object_evas_get(wd->bx));
394    evas_object_size_hint_weight_set(item->base.view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
395    evas_object_size_hint_fill_set(item->base.view, EVAS_HINT_FILL, EVAS_HINT_FILL);
396    _elm_theme_object_set(item->base.widget, item->base.view, "menu", "separator",  elm_widget_style_get(item->base.widget));
397    edje_object_signal_callback_add(item->base.view, "elm,action,activate", "",
398                                    _menu_item_activate, item);
399    evas_object_show(item->base.view);
400 }
401
402 static void
403 _item_submenu_obj_create(Elm_Menu_Item *item)
404 {
405    Widget_Data *wd = elm_widget_data_get(item->base.widget);
406    if (!wd) return;
407    item->submenu.location = elm_icon_add(wd->bx);
408    item->submenu.hv = elm_hover_add(wd->bx);
409    elm_widget_mirrored_set(item->submenu.hv, EINA_FALSE);
410    elm_hover_target_set(item->submenu.hv, item->submenu.location);
411    elm_hover_parent_set(item->submenu.hv, wd->parent);
412    elm_object_style_set(item->submenu.hv, "submenu");
413
414    item->submenu.bx = elm_box_add(wd->bx);
415    elm_widget_mirrored_set(item->submenu.bx, EINA_FALSE);
416    evas_object_size_hint_weight_set(item->submenu.bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
417    evas_object_show(item->submenu.bx);
418    elm_hover_content_set(item->submenu.hv, elm_hover_best_content_location_get(item->submenu.hv, ELM_HOVER_AXIS_VERTICAL), item->submenu.bx);
419
420    edje_object_mirrored_set(item->base.view, elm_widget_mirrored_get(item->base.widget));
421    _elm_theme_object_set(item->base.widget, item->base.view, "menu", "item_with_submenu",  elm_widget_style_get(item->base.widget));
422    elm_menu_item_label_set(item, item->label);
423    if (item->icon_str) elm_menu_item_object_icon_name_set(item, item->icon_str);
424
425    edje_object_signal_callback_add(item->base.view, "elm,action,open", "",
426                                    _submenu_open, item);
427    evas_object_event_callback_add(item->base.view, EVAS_CALLBACK_MOVE, _item_move_resize, item);
428    evas_object_event_callback_add(item->base.view, EVAS_CALLBACK_RESIZE, _item_move_resize, item);
429
430    evas_object_event_callback_add(item->submenu.bx, EVAS_CALLBACK_RESIZE, _menu_resize, item->base.widget);
431 }
432
433 EAPI Evas_Object *
434 elm_menu_add(Evas_Object *parent)
435 {
436    Evas_Object *obj;
437    Evas *e;
438    Widget_Data *wd;
439
440    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
441
442    ELM_SET_WIDTYPE(widtype, "menu");
443    elm_widget_type_set(obj, "menu");
444    elm_widget_sub_object_add(parent, obj);
445    elm_widget_data_set(obj, wd);
446    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
447    elm_widget_del_hook_set(obj, _del_hook);
448    elm_widget_theme_hook_set(obj, _theme_hook);
449    elm_widget_can_focus_set(obj, EINA_FALSE);
450
451    wd->location = elm_icon_add(obj);
452    wd->parent = parent;
453    wd->obj = obj;
454
455    wd->hv = elm_hover_add(obj);
456    elm_widget_mirrored_set(wd->hv, EINA_FALSE);
457    elm_hover_parent_set(wd->hv, parent);
458    elm_hover_target_set(wd->hv, wd->location);
459    elm_object_style_set(wd->hv, "menu");
460    evas_object_smart_callback_add(wd->hv, "clicked", _hover_clicked_cb, obj);
461
462    wd->bx = elm_box_add(obj);
463    elm_widget_mirrored_set(wd->bx, EINA_FALSE);
464    evas_object_size_hint_weight_set(wd->bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
465    evas_object_show(wd->bx);
466    elm_hover_content_set(wd->hv, elm_hover_best_content_location_get(wd->hv, ELM_HOVER_AXIS_VERTICAL), wd->bx);
467
468    evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_RESIZE, _parent_resize, wd->obj);
469    evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_DEL, _parent_del, wd);
470
471    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _show, obj);
472
473    evas_object_event_callback_add(wd->bx, EVAS_CALLBACK_RESIZE, _menu_resize, obj);
474
475    evas_object_smart_callbacks_descriptions_set(obj, _signals);
476
477    _sizing_eval(obj);
478    return obj;
479 }
480
481 EAPI void
482 elm_menu_parent_set(Evas_Object *obj, Evas_Object *parent)
483 {
484    Eina_List *l, *_l, *_ll, *ll = NULL;
485    Elm_Menu_Item *item;
486    ELM_CHECK_WIDTYPE(obj, widtype);
487    Widget_Data *wd = elm_widget_data_get(obj);
488    if (!wd) return;
489
490    if (wd->parent == parent) return;
491    if (wd->parent)
492      {
493         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE, _parent_resize, wd->obj);
494         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_DEL, _parent_del, wd);
495      }
496    wd->parent = parent;
497    if (wd->parent)
498      {
499         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_RESIZE, _parent_resize, wd->obj);
500         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_DEL, _parent_del, wd);
501      }
502    elm_hover_parent_set(wd->hv, parent);
503
504    ll = eina_list_append(ll, wd->items);
505    EINA_LIST_FOREACH(ll, _ll, l)
506      {
507         EINA_LIST_FOREACH(l, _l, item)
508           {
509              if (item->submenu.hv)
510                {
511                   elm_hover_parent_set(item->submenu.hv, parent);
512                   ll = eina_list_append(ll, item->submenu.items);
513                }
514           }
515      }
516    _sizing_eval(obj);
517 }
518
519 EAPI Evas_Object *
520 elm_menu_parent_get(const Evas_Object *obj)
521 {
522    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
523    Widget_Data *wd = elm_widget_data_get(obj);
524    if (!wd) return NULL;
525    return wd->parent;
526 }
527
528 EAPI void
529 elm_menu_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
530 {
531    ELM_CHECK_WIDTYPE(obj, widtype);
532    Widget_Data *wd = elm_widget_data_get(obj);
533    if (!wd) return;
534    wd->xloc = x;
535    wd->yloc = y;
536    _sizing_eval(obj);
537 }
538
539 EAPI void
540 elm_menu_close(Evas_Object *obj)
541 {
542    ELM_CHECK_WIDTYPE(obj, widtype);
543    Widget_Data *wd = elm_widget_data_get(obj);
544    _menu_hide(obj, wd->hv, NULL);
545 }
546
547 EAPI Evas_Object *
548 elm_menu_item_object_get(const Elm_Menu_Item *item)
549 {
550    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
551    return item->base.view;
552 }
553
554 static void
555 _item_clone(Evas_Object *obj, Elm_Menu_Item *parent, Elm_Menu_Item *item)
556 {
557    Elm_Menu_Item *new_item, *subitem;
558    Eina_List *iter;
559
560    if (item->separator)
561      new_item = elm_menu_item_separator_add(obj, parent);
562    else
563      new_item = elm_menu_item_add(obj, parent, item->icon_str, item->label, item->func, item->base.data);
564    elm_menu_item_disabled_set(new_item, item->disabled);
565
566    EINA_LIST_FOREACH(item->submenu.items, iter, subitem)
567       _item_clone(obj, new_item, subitem);
568 }
569
570 void
571 elm_menu_clone(Evas_Object *from_menu, Evas_Object *to_menu, Elm_Menu_Item *parent)
572 {
573    ELM_CHECK_WIDTYPE(from_menu, widtype);
574    ELM_CHECK_WIDTYPE(to_menu, widtype);
575    Widget_Data *from_wd = elm_widget_data_get(from_menu);
576    Eina_List *iter;
577    Elm_Menu_Item *item;
578
579    if (!from_wd) return;
580    EINA_LIST_FOREACH(from_wd->items, iter, item)
581       _item_clone(to_menu, parent, item);
582 }
583
584 static void
585 _elm_menu_item_add_helper(Evas_Object *obj, Elm_Menu_Item *parent, Elm_Menu_Item *subitem, Widget_Data *wd)
586 {
587    if (parent)
588      {
589         if (!parent->submenu.bx) _item_submenu_obj_create(parent);
590         elm_box_pack_end(parent->submenu.bx, subitem->base.view);
591         parent->submenu.items = eina_list_append(parent->submenu.items, subitem);
592         subitem->idx = eina_list_count(parent->submenu.items) - 1;
593      }
594    else
595      {
596         elm_box_pack_end(wd->bx, subitem->base.view);
597         wd->items = eina_list_append(wd->items, subitem);
598         subitem->idx = eina_list_count(wd->items) - 1;
599      }
600
601    _sizing_eval(obj);
602 }
603
604 EAPI Elm_Menu_Item *
605 elm_menu_item_add(Evas_Object *obj, Elm_Menu_Item *parent, const char *icon, const char *label, Evas_Smart_Cb func, const void *data)
606 {
607    Elm_Menu_Item *subitem;
608    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
609    Widget_Data *wd = elm_widget_data_get(obj);
610    Evas_Object *icon_obj;
611
612    if (!wd) return NULL;
613    icon_obj = elm_icon_add(obj);
614    if (!icon_obj) return NULL;
615    subitem = elm_widget_item_new(obj, Elm_Menu_Item);
616    if (!subitem)
617      {
618         evas_object_del(icon_obj);
619         return NULL;
620      }
621    subitem->base.data = data;
622    subitem->func = func;
623    subitem->parent = parent;
624    subitem->content = icon_obj;
625
626    _item_obj_create(subitem);
627    elm_menu_item_label_set(subitem, label);
628
629    elm_widget_sub_object_add(subitem->base.widget, subitem->content);
630    edje_object_part_swallow(subitem->base.view, "elm.swallow.content", subitem->content);
631    if (icon) elm_menu_item_object_icon_name_set(subitem, icon);
632
633    _elm_menu_item_add_helper(obj, parent, subitem, wd);
634
635    return subitem;
636 }
637
638 EAPI Elm_Menu_Item *
639 elm_menu_item_add_object(Evas_Object *obj, Elm_Menu_Item *parent, Evas_Object *subobj, Evas_Smart_Cb func, const void *data)
640 {
641    Elm_Menu_Item *subitem;
642    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
643    Widget_Data *wd = elm_widget_data_get(obj);
644
645    if (!wd) return NULL;
646    subitem = elm_widget_item_new(obj, Elm_Menu_Item);
647    if (!subitem) return NULL;
648
649    subitem->base.data = data;
650    subitem->func = func;
651    subitem->parent = parent;
652    subitem->object_item = EINA_TRUE;
653    subitem->content = subobj;
654
655    _item_obj_create(subitem);
656
657    elm_widget_sub_object_add(subitem->base.widget, subitem->content);
658    edje_object_part_swallow(subitem->base.view, "elm.swallow.content", subobj);
659    _sizing_eval(subitem->base.widget);
660
661    _elm_menu_item_add_helper(obj, parent, subitem, wd);
662
663    return subitem;
664 }
665
666 EAPI unsigned int
667 elm_menu_item_index_get(const Elm_Menu_Item *item)
668 {
669    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, 0);
670    return item->idx;
671 }
672
673 EAPI void
674 elm_menu_item_label_set(Elm_Menu_Item *item, const char *label)
675 {
676    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
677    eina_stringshare_replace(&item->label, label);
678
679    if (label)
680      edje_object_signal_emit(item->base.view, "elm,state,text,visible", "elm");
681    else
682      edje_object_signal_emit(item->base.view, "elm,state,text,hidden", "elm");
683
684    edje_object_message_signal_process(item->base.view);
685    edje_object_part_text_set(item->base.view, "elm.text", label);
686    _sizing_eval(item->base.widget);
687 }
688
689 EAPI const char *
690 elm_menu_item_label_get(const Elm_Menu_Item *item)
691 {
692    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
693    return item->label;
694 }
695
696 EAPI void
697 elm_menu_item_object_icon_name_set(Elm_Menu_Item *item, const char *icon)
698 {
699    char icon_tmp[512];
700    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
701    EINA_SAFETY_ON_NULL_RETURN(icon);
702    if (!*icon) return;
703    if ((item->icon_str) && (!strcmp(item->icon_str, icon))) return;
704    if ((snprintf(icon_tmp, sizeof(icon_tmp), "menu/%s", icon) > 0) &&
705        (elm_icon_standard_set(item->content, icon_tmp)))
706      {
707         eina_stringshare_replace(&item->icon_str, icon);
708         edje_object_signal_emit(item->base.view, "elm,state,icon,visible", "elm");
709      }
710    else
711      edje_object_signal_emit(item->base.view, "elm,state,icon,hidden", "elm");
712    edje_object_message_signal_process(item->base.view);
713    _sizing_eval(item->base.widget);
714 }
715
716 EAPI void
717 elm_menu_item_disabled_set(Elm_Menu_Item *item, Eina_Bool disabled)
718 {
719    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
720    if (disabled == item->disabled) return;
721    item->disabled = !!disabled;
722    if (disabled)
723      {
724         edje_object_signal_emit(item->base.view, "elm,state,disabled", "elm");
725         if (item->submenu.open) _submenu_hide(item);
726      }
727    else
728      edje_object_signal_emit(item->base.view, "elm,state,enabled", "elm");
729    edje_object_message_signal_process(item->base.view);
730 }
731
732 EAPI Eina_Bool
733 elm_menu_item_disabled_get(const Elm_Menu_Item *item)
734 {
735    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
736    return item->disabled;
737 }
738
739 EAPI Elm_Menu_Item *
740 elm_menu_item_separator_add(Evas_Object *obj, Elm_Menu_Item *parent)
741 {
742    Elm_Menu_Item *subitem;
743    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
744    Widget_Data *wd = elm_widget_data_get(obj);
745    if (!wd) return NULL;
746    /* don't add a separator as the first item */
747    if (!wd->items) return NULL;
748    /* don't allow adding more than one separator in a row */
749    if (parent) subitem = eina_list_last(parent->submenu.items)->data;
750    else subitem = eina_list_last(wd->items)->data;
751    if (subitem->separator) return NULL;
752
753    subitem = elm_widget_item_new(obj, Elm_Menu_Item);
754    if (!subitem) return NULL;
755    subitem->base.widget = obj;
756    subitem->separator = 1;
757    _item_separator_obj_create(subitem);
758    if (!parent)
759      {
760         elm_box_pack_end(wd->bx, subitem->base.view);
761         wd->items = eina_list_append(wd->items, subitem);
762      }
763    else
764      {
765         if (!parent->submenu.bx) _item_submenu_obj_create(parent);
766         elm_box_pack_end(parent->submenu.bx, subitem->base.view);
767         parent->submenu.items = eina_list_append(parent->submenu.items, subitem);
768      }
769    _sizing_eval(obj);
770    return subitem;
771 }
772
773 EAPI Eina_Bool
774 elm_menu_item_object_content_set(Elm_Menu_Item *item, Evas_Object *obj)
775 {
776    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
777    if (item->content)
778      {
779         elm_widget_sub_object_del(item->base.widget, item->content);
780         evas_object_del(item->content);
781      }
782
783    item->content = obj;
784
785    elm_widget_sub_object_add(item->base.widget, item->content);
786    edje_object_part_swallow(item->base.view, "elm.swallow.content", item->content);
787    _sizing_eval(item->base.widget);
788    return EINA_TRUE;
789 }
790
791 EAPI Evas_Object *
792 elm_menu_item_object_content_get(const Elm_Menu_Item *item)
793 {
794    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
795    return item->content;
796 }
797
798 EAPI const char *
799 elm_menu_item_object_icon_name_get(const Elm_Menu_Item *item)
800 {
801    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
802    return item->icon_str;
803 }
804
805 EAPI Eina_Bool
806 elm_menu_item_is_separator(Elm_Menu_Item *item)
807 {
808    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
809    return item->separator;
810 }
811
812 EAPI void
813 elm_menu_item_del(Elm_Menu_Item *item)
814 {
815    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
816    Elm_Menu_Item *_item;
817
818    elm_widget_item_pre_notify_del(item);
819
820    EINA_LIST_FREE(item->submenu.items, _item) elm_menu_item_del(_item);
821    if (item->label) eina_stringshare_del(item->label);
822    if (item->content) evas_object_del(item->content);
823    if (item->submenu.hv) evas_object_del(item->submenu.hv);
824    if (item->submenu.location) evas_object_del(item->submenu.location);
825
826    if (item->parent)
827      item->parent->submenu.items = eina_list_remove(item->parent->submenu.items, item);
828    else
829      {
830         Widget_Data *wd = elm_widget_data_get(item->base.widget);
831         wd->items = eina_list_remove(wd->items, item);
832      }
833
834    elm_widget_item_del(item);
835 }
836
837 EAPI void
838 elm_menu_item_del_cb_set(Elm_Menu_Item *item, Evas_Smart_Cb func)
839 {
840    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
841    elm_widget_item_del_cb_set(item, func);
842 }
843
844 EAPI void *
845 elm_menu_item_data_get(const Elm_Menu_Item *item)
846 {
847    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
848    return elm_widget_item_data_get(item);
849 }
850
851 EAPI void
852 elm_menu_item_data_set(Elm_Menu_Item *item, const void *data)
853 {
854    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
855    elm_widget_item_data_set(item, data);
856 }
857
858 EAPI const Eina_List *
859 elm_menu_item_subitems_get(const Elm_Menu_Item *item)
860 {
861    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
862    return item->submenu.items;
863 }
864
865 EAPI const Eina_List *
866 elm_menu_items_get(const Evas_Object * obj)
867 {
868    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
869    Widget_Data *wd = elm_widget_data_get(obj);
870    return wd->items;
871 }
872
873 EAPI void
874 elm_menu_item_selected_set(Elm_Menu_Item *item, Eina_Bool selected)
875 {
876    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
877    if (selected == item->selected) return;
878    item->selected = selected;
879    if (selected)
880      {
881         edje_object_signal_emit(item->base.view, "elm,state,selected", "elm");
882         _menu_item_activate(item, NULL, NULL, NULL);
883      }
884    else
885      {
886         edje_object_signal_emit(item->base.view, "elm,state,unselected", "elm");
887         _menu_item_inactivate(item, NULL, NULL, NULL);
888      }
889    edje_object_message_signal_process(item->base.view);
890 }
891
892 EAPI Eina_Bool
893 elm_menu_item_selected_get(const Elm_Menu_Item *item)
894 {
895    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
896    return item->selected;
897 }
898
899 EAPI Elm_Menu_Item *
900 elm_menu_item_prev_get(const Elm_Menu_Item *it)
901 {
902    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
903    if (it->parent)
904      {
905         Eina_List *l = eina_list_data_find_list(it->parent->submenu.items, it);
906         l = eina_list_prev(l);
907         if (!l) return NULL;
908         return l->data;
909      }
910    else
911      {
912         Widget_Data *wd = elm_widget_data_get(it->base.widget);
913         if (!wd | !wd->items) return NULL;
914         Eina_List *l = eina_list_data_find_list(wd->items, it);
915         l = eina_list_prev(l);
916         if (!l) return NULL;
917         return l->data;
918      }
919    return NULL;
920 }
921
922 EAPI Elm_Menu_Item *
923 elm_menu_item_next_get(const Elm_Menu_Item *it)
924 {
925    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
926    if (it->parent)
927      {
928         Eina_List *l = eina_list_data_find_list(it->parent->submenu.items, it);
929         l = eina_list_next(l);
930         if (!l) return NULL;
931         return l->data;
932      }
933    else
934      {
935         Widget_Data *wd = elm_widget_data_get(it->base.widget);
936         if (!wd | !wd->items) return NULL;
937         Eina_List *l = eina_list_data_find_list(wd->items, it);
938         l = eina_list_next(l);
939         if (!l) return NULL;
940         return l->data;
941      }
942    return NULL;
943 }
944
945 EAPI Evas_Object *
946 elm_menu_item_menu_get(const Elm_Menu_Item *item)
947 {
948    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
949    return item->base.widget;
950 }
951
952 EAPI Elm_Menu_Item *
953 elm_menu_first_item_get(const Evas_Object * obj)
954 {
955    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
956    Widget_Data *wd = elm_widget_data_get(obj);
957    if (!wd) return NULL;
958    if (wd->items) return wd->items->data;
959    return NULL;
960 }
961
962 EAPI Elm_Menu_Item *
963 elm_menu_last_item_get(const Evas_Object * obj)
964 {
965    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
966    Widget_Data *wd = elm_widget_data_get(obj);
967    if (!wd) return NULL;
968    Eina_List *l = eina_list_last(wd->items);
969    if (l) return l->data;
970    return NULL;
971 }
972
973 EAPI Elm_Menu_Item *
974 elm_menu_selected_item_get(const Evas_Object * obj)
975 {
976    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
977    Widget_Data *wd = elm_widget_data_get(obj);
978    if (!wd) return NULL;
979    Eina_List *l;
980    Elm_Menu_Item *item;
981    EINA_LIST_FOREACH(wd->items, l, item)
982      {
983         if (item->selected) return item;
984      }
985    return NULL;
986 }
987