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