From: Tristan Lelong <tristan.lelong@blunderer.org>
[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  *
7  * A menu is a list of items displayed above the window. Each item can
8  * have a sub-menu. The menu object can be used to display a menu on right
9  * click, in a toolbar, anywhere.
10  *
11  */
12
13 typedef struct _Widget_Data Widget_Data;
14
15 struct _Elm_Menu_Item
16 {
17    Elm_Widget_Item base;
18    Elm_Menu_Item *parent;
19    Evas_Object *icon;
20    const char *icon_str;
21    const char *label;
22    Evas_Smart_Cb func;
23
24    struct {
25       Evas_Object *hv, *bx, *location;
26       Eina_List *items;
27       Eina_Bool open : 1;
28    } submenu;
29
30    Eina_Bool separator : 1;
31    Eina_Bool disabled : 1;
32    Eina_Bool selected: 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    _elm_widget_mirrored_reload(obj);
103    ll = eina_list_append(ll, wd->items);
104    EINA_LIST_FOREACH(ll, _ll, l)
105      {
106         EINA_LIST_FOREACH(l, _l, item)
107           {
108              edje_object_mirrored_set(item->base.view, elm_widget_mirrored_get(obj));
109              ll = eina_list_append(ll, item->submenu.items);
110              if (item->separator)
111                _elm_theme_object_set(obj, item->base.view, "menu", "separator",
112                                      elm_widget_style_get(obj));
113              else if (item->submenu.bx)
114                {
115                   _elm_theme_object_set
116                     (obj, item->base.view, "menu", "item_with_submenu",
117                      elm_widget_style_get(obj));
118                   elm_menu_item_label_set(item, item->label);
119                   elm_menu_item_icon_set(item, item->icon_str);
120                }
121              else
122                {
123                   _elm_theme_object_set(obj, item->base.view, "menu", "item",
124                                         elm_widget_style_get(obj));
125                   elm_menu_item_label_set(item, item->label);
126                   elm_menu_item_icon_set(item, item->icon_str);
127                }
128              if (item->disabled)
129                edje_object_signal_emit
130                  (item->base.view, "elm,state,disabled", "elm");
131              else
132                edje_object_signal_emit
133                  (item->base.view, "elm,state,enabled", "elm");
134              edje_object_message_signal_process(item->base.view);
135              edje_object_scale_set(item->base.view, elm_widget_scale_get(obj) *
136                                    _elm_config->scale);
137           }
138      }
139    _sizing_eval(obj);
140 }
141
142 static void
143 _sizing_eval(Evas_Object *obj)
144 {
145    Eina_List *l;
146    Elm_Menu_Item *item;
147    Evas_Coord x_p, y_p, w_p, h_p, x2, y2, w2, h2, bx, by, bw, bh;
148    Widget_Data *wd = elm_widget_data_get(obj);
149    if ((!wd) || (!wd->parent)) return;
150    EINA_LIST_FOREACH(wd->items,l,item) _item_sizing_eval(item);
151    evas_object_geometry_get(wd->location, &x_p, &y_p, &w_p, &h_p);
152    evas_object_geometry_get(wd->parent, &x2, &y2, &w2, &h2);
153    evas_object_geometry_get(wd->bx, &bx, &by, &bw, &bh);
154
155    x_p = wd->xloc;
156    y_p = wd->yloc;
157
158    if (elm_widget_mirrored_get(obj))
159      x_p -= w_p;
160
161    if (x_p+bw > x2+w2) x_p -= x_p+bw - (x2+w2);
162    if (x_p < x2) x_p += x2 - x_p;
163
164    if (y_p+h_p+bh > y2+h2) y_p -= y_p+h_p+bh - (y2+h2);
165    if (y_p < y2) y_p += y2 - y_p;
166
167
168    evas_object_move(wd->location, x_p, y_p);
169    evas_object_resize(wd->location, bw, h_p);
170    evas_object_size_hint_min_set(wd->location, bw, h_p);
171    evas_object_size_hint_max_set(wd->location, bw, h_p);
172    elm_hover_target_set(wd->hv, wd->location);
173
174    EINA_LIST_FOREACH(wd->items,l,item)
175      {
176         if (item->submenu.open) _submenu_sizing_eval(item);
177      }
178 }
179
180 static void
181 _submenu_sizing_eval(Elm_Menu_Item *parent)
182 {
183    Eina_List *l;
184    Elm_Menu_Item *item;
185    Evas_Coord x_p, y_p, w_p, h_p, x2, y2, w2, h2, bx, by, bw, bh, px, py, pw, ph;
186    Widget_Data *wd = elm_widget_data_get(parent->base.widget);
187    if (!wd) return;
188    EINA_LIST_FOREACH(parent->submenu.items, l, item) _item_sizing_eval(item);
189    evas_object_geometry_get(parent->submenu.location, &x_p, &y_p, &w_p, &h_p);
190    evas_object_geometry_get(parent->base.view, &x2, &y2, &w2, &h2);
191    evas_object_geometry_get(parent->submenu.bx, &bx, &by, &bw, &bh);
192    evas_object_geometry_get(wd->parent, &px, &py, &pw, &ph);
193
194    x_p = x2+w2;
195    y_p = y2;
196
197    /* If it overflows on the right, adjust the x */
198    if ((x_p + bw > px + pw) || elm_widget_mirrored_get(parent->base.widget))
199      x_p = x2-bw;
200
201    /* If it overflows on the left, adjust the x - usually only happens
202     * with an RTL interface */
203    if (x_p < px)
204      x_p = x2 + w2;
205
206    /* If after all the adjustments it still overflows, fix it */
207    if (x_p + bw > px + pw)
208      x_p = x2-bw;
209
210    if (y_p+bh > py+ph)
211      y_p -= y_p+bh - (py+ph);
212    if (y_p < py)
213      y_p += y_p - y_p;
214
215    evas_object_move(parent->submenu.location, x_p, y_p);
216    evas_object_resize(parent->submenu.location, bw, h_p);
217    evas_object_size_hint_min_set(parent->submenu.location, bw, h_p);
218    evas_object_size_hint_max_set(parent->submenu.location, bw, h_p);
219    elm_hover_target_set(parent->submenu.hv, parent->submenu.location);
220
221    EINA_LIST_FOREACH(parent->submenu.items, l, item)
222      {
223         if (item->submenu.open)
224           _submenu_sizing_eval(item);
225      }
226 }
227
228 static void
229 _item_sizing_eval(Elm_Menu_Item *item)
230 {
231    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
232    if (!item->separator)
233      elm_coords_finger_size_adjust(1, &minw, 1, &minh);
234    edje_object_size_min_restricted_calc(item->base.view, &minw, &minh, minw, minh);
235    if (!item->separator)
236      elm_coords_finger_size_adjust(1, &minw, 1, &minh);
237    evas_object_size_hint_min_set(item->base.view, minw, minh);
238    evas_object_size_hint_max_set(item->base.view, maxw, maxh);
239 }
240
241 static void
242 _menu_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
243 {
244    _sizing_eval(data);
245 }
246
247 static void
248 _parent_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
249 {
250    _sizing_eval(data);
251 }
252
253 static void
254 _parent_del(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
255 {
256    Widget_Data *wd = data;
257    evas_object_event_callback_del_full(obj, EVAS_CALLBACK_RESIZE, _parent_resize, wd->obj);
258    wd->parent = NULL;
259 }
260
261 static void
262 _item_move_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
263 {
264    Elm_Menu_Item *item = data;
265    if (item->submenu.open) _submenu_sizing_eval(item);
266 }
267
268 static void
269 _hover_clicked_cb(void *data, Evas_Object *obj, void *event_info)
270 {
271    _menu_hide(data, obj, event_info);
272    evas_object_smart_callback_call(data, "clicked", NULL);
273 }
274
275 static void
276 _menu_hide(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
277 {
278    Eina_List *l;
279    Elm_Menu_Item *item2;
280    Widget_Data *wd = elm_widget_data_get(data);
281    if (!wd) return;
282    evas_object_hide(wd->hv);
283    evas_object_hide(data);
284
285    EINA_LIST_FOREACH(wd->items, l, item2)
286      {
287         if (item2->submenu.open) _submenu_hide(item2);
288      }
289 }
290
291 static void
292 _submenu_hide(Elm_Menu_Item *item)
293 {
294    Eina_List *l;
295    Elm_Menu_Item *item2;
296    evas_object_hide(item->submenu.hv);
297    item->submenu.open = EINA_FALSE;
298    EINA_LIST_FOREACH(item->submenu.items, l, item2)
299      {
300         if (item2->submenu.open) _submenu_hide(item2);
301      }
302 }
303
304 static void
305 _menu_item_select(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
306 {
307    Elm_Menu_Item *item = data;
308    if (item->submenu.items)
309      {
310         if (!item->submenu.open) _submenu_open(item, NULL, NULL, NULL);
311         else _submenu_hide(item);
312      }
313    else
314      _menu_hide(item->base.widget, NULL, NULL);
315
316    if (item->func) item->func((void *)(item->base.data), item->base.widget, item);
317 }
318
319 static void
320 _menu_item_activate(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
321 {
322    Eina_List *l;
323    Elm_Menu_Item *item2;
324    Elm_Menu_Item *item = data;
325    item->selected = 1;
326    if (item->parent)
327      {
328         EINA_LIST_FOREACH(item->parent->submenu.items, l, item2)
329           {
330             if (item2 != item) elm_menu_item_selected_set(item2, 0);
331           }
332      }
333    else
334      {
335         Widget_Data *wd = elm_widget_data_get(item->base.widget);
336         EINA_LIST_FOREACH(wd->items, l, item2)
337           {
338             if (item2 != item) elm_menu_item_selected_set(item2, 0);
339           }
340      }
341 }
342
343 static void
344 _menu_item_inactivate(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
345 {
346    Elm_Menu_Item *item = data;
347    item->selected = 0;
348    if (item->submenu.open) _submenu_hide(item);
349 }
350
351 static void
352 _submenu_open(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
353 {
354    Elm_Menu_Item *item = data;
355    item->submenu.open = EINA_TRUE;
356    evas_object_show(item->submenu.hv);
357    _sizing_eval(item->base.widget);
358 }
359
360 static void
361 _show(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
362 {
363    Widget_Data *wd = elm_widget_data_get(data);
364    if (!wd) return;
365    evas_object_show(wd->hv);
366 }
367
368 static void
369 _item_obj_create(Elm_Menu_Item *item)
370 {
371    Widget_Data *wd = elm_widget_data_get(item->base.widget);
372    if (!wd) return;
373    item->base.view = edje_object_add(evas_object_evas_get(wd->bx));
374    edje_object_mirrored_set(item->base.view, elm_widget_mirrored_get(item->base.widget));
375    evas_object_size_hint_weight_set(item->base.view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
376    evas_object_size_hint_fill_set(item->base.view, EVAS_HINT_FILL, EVAS_HINT_FILL);
377    _elm_theme_object_set(item->base.widget, item->base.view, "menu", "item",  elm_widget_style_get(item->base.widget));
378    edje_object_signal_callback_add(item->base.view, "elm,action,click", "",
379                                    _menu_item_select, item);
380    edje_object_signal_callback_add(item->base.view, "elm,action,activate", "",
381                                    _menu_item_activate, item);
382    edje_object_signal_callback_add(item->base.view, "elm,action,inactivate", "",
383                                    _menu_item_inactivate, item);
384    evas_object_show(item->base.view);
385 }
386
387 static void
388 _item_separator_obj_create(Elm_Menu_Item *item)
389 {
390    Widget_Data *wd = elm_widget_data_get(item->base.widget);
391    if (!wd) return;
392    item->base.view = edje_object_add(evas_object_evas_get(wd->bx));
393    evas_object_size_hint_weight_set(item->base.view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
394    evas_object_size_hint_fill_set(item->base.view, EVAS_HINT_FILL, EVAS_HINT_FILL);
395    _elm_theme_object_set(item->base.widget, item->base.view, "menu", "separator",  elm_widget_style_get(item->base.widget));
396    edje_object_signal_callback_add(item->base.view, "elm,action,activate", "",
397                                    _menu_item_activate, item);
398    evas_object_show(item->base.view);
399 }
400
401 static void
402 _item_submenu_obj_create(Elm_Menu_Item *item)
403 {
404    Widget_Data *wd = elm_widget_data_get(item->base.widget);
405    if (!wd) return;
406    item->submenu.location = elm_icon_add(wd->bx);
407    item->submenu.hv = elm_hover_add(wd->bx);
408    elm_widget_mirrored_set(item->submenu.hv, EINA_FALSE);
409    elm_hover_target_set(item->submenu.hv, item->submenu.location);
410    elm_hover_parent_set(item->submenu.hv, wd->parent);
411    elm_object_style_set(item->submenu.hv, "submenu");
412
413    item->submenu.bx = elm_box_add(wd->bx);
414    elm_widget_mirrored_set(item->submenu.bx, EINA_FALSE);
415    evas_object_size_hint_weight_set(item->submenu.bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
416    evas_object_show(item->submenu.bx);
417    elm_hover_content_set(item->submenu.hv, elm_hover_best_content_location_get(item->submenu.hv, ELM_HOVER_AXIS_VERTICAL), item->submenu.bx);
418
419    edje_object_mirrored_set(item->base.view, elm_widget_mirrored_get(item->base.widget));
420    _elm_theme_object_set(item->base.widget, item->base.view, "menu", "item_with_submenu",  elm_widget_style_get(item->base.widget));
421    elm_menu_item_label_set(item, item->label);
422    elm_menu_item_icon_set(item, item->icon_str);
423
424    edje_object_signal_callback_add(item->base.view, "elm,action,open", "",
425                                    _submenu_open, item);
426    evas_object_event_callback_add(item->base.view, EVAS_CALLBACK_MOVE, _item_move_resize, item);
427    evas_object_event_callback_add(item->base.view, EVAS_CALLBACK_RESIZE, _item_move_resize, item);
428
429    evas_object_event_callback_add(item->submenu.bx, EVAS_CALLBACK_RESIZE, _menu_resize, item->base.widget);
430 }
431
432 /**
433  * Add a new menu to the parent
434  *
435  * @param parent The parent object.
436  * @return The new object or NULL if it cannot be created.
437  *
438  * @ingroup Menu
439  */
440 EAPI Evas_Object *
441 elm_menu_add(Evas_Object *parent)
442 {
443    Evas_Object *obj;
444    Evas *e;
445    Widget_Data *wd;
446
447    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
448
449    wd = ELM_NEW(Widget_Data);
450    e = evas_object_evas_get(parent);
451    if (!e) return NULL;
452    obj = elm_widget_add(e);
453    ELM_SET_WIDTYPE(widtype, "menu");
454    elm_widget_type_set(obj, "menu");
455    elm_widget_sub_object_add(parent, obj);
456    elm_widget_data_set(obj, wd);
457    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
458    elm_widget_del_hook_set(obj, _del_hook);
459    elm_widget_theme_hook_set(obj, _theme_hook);
460    elm_widget_can_focus_set(obj, EINA_FALSE);
461
462    wd->location = elm_icon_add(obj);
463    wd->parent = parent;
464    wd->obj = obj;
465
466    wd->hv = elm_hover_add(obj);
467    elm_widget_mirrored_set(wd->hv, EINA_FALSE);
468    elm_hover_parent_set(wd->hv, parent);
469    elm_hover_target_set(wd->hv, wd->location);
470    elm_object_style_set(wd->hv, "menu");
471    evas_object_smart_callback_add(wd->hv, "clicked", _hover_clicked_cb, obj);
472
473    wd->bx = elm_box_add(obj);
474    elm_widget_mirrored_set(wd->bx, EINA_FALSE);
475    evas_object_size_hint_weight_set(wd->bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
476    evas_object_show(wd->bx);
477    elm_hover_content_set(wd->hv, elm_hover_best_content_location_get(wd->hv, ELM_HOVER_AXIS_VERTICAL), wd->bx);
478
479    evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_RESIZE, _parent_resize, wd->obj);
480    evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_DEL, _parent_del, wd);
481
482    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _show, obj);
483
484    evas_object_event_callback_add(wd->bx, EVAS_CALLBACK_RESIZE, _menu_resize, obj);
485
486    _sizing_eval(obj);
487    return obj;
488 }
489
490 /**
491  * Set the parent
492  *
493  * @param obj The menu object.
494  * @param parent The new parent.
495  *
496  * @ingroup Menu
497  */
498 EAPI void
499 elm_menu_parent_set(Evas_Object *obj, Evas_Object *parent)
500 {
501    Eina_List *l, *_l, *_ll, *ll = NULL;
502    Elm_Menu_Item *item;
503    ELM_CHECK_WIDTYPE(obj, widtype);
504    Widget_Data *wd = elm_widget_data_get(obj);
505    if (!wd) return;
506
507    if (wd->parent == parent) return;
508    if (wd->parent)
509      {
510        evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE, _parent_resize, wd->obj);
511        evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_DEL, _parent_del, wd);
512      }
513    wd->parent = parent;
514    if (wd->parent)
515      {
516        evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_RESIZE, _parent_resize, wd->obj);
517        evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_DEL, _parent_del, wd);
518      }
519    elm_hover_parent_set(wd->hv, parent);
520
521    ll = eina_list_append(ll, wd->items);
522    EINA_LIST_FOREACH(ll, _ll, l)
523      {
524         EINA_LIST_FOREACH(l, _l, item)
525           {
526              if (item->submenu.hv)
527                {
528                   elm_hover_parent_set(item->submenu.hv, parent);
529                   ll = eina_list_append(ll, item->submenu.items);
530                }
531           }
532      }
533    _sizing_eval(obj);
534 }
535
536 /**
537  * Get the parent
538  *
539  * @param obj The menu object.
540  * @return The parent.
541  *
542  * @ingroup Menu
543  */
544 EAPI Evas_Object *
545 elm_menu_parent_get(const Evas_Object *obj)
546 {
547    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
548    Widget_Data *wd = elm_widget_data_get(obj);
549    if (!wd) return NULL;
550    return wd->parent;
551 }
552
553 /**
554  * Move the menu to a new position
555  *
556  * @param obj The menu object.
557  * @param x The new position.
558  * @param y The new position.
559  *
560  * @ingroup Menu
561  */
562 EAPI void
563 elm_menu_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
564 {
565    ELM_CHECK_WIDTYPE(obj, widtype);
566    Widget_Data *wd = elm_widget_data_get(obj);
567    if (!wd) return;
568    wd->xloc = x;
569    wd->yloc = y;
570    _sizing_eval(obj);
571 }
572
573 /**
574  * Get the Evas_Object of an Elm_Menu_Item
575  *
576  * @param item The menu item object.
577  *
578  * @ingroup Menu
579  */
580 EAPI Evas_Object *
581 elm_menu_item_object_get(const Elm_Menu_Item *item)
582 {
583    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
584    return item->base.view;
585 }
586
587 static void
588 _item_clone(Evas_Object *obj, Elm_Menu_Item *parent, Elm_Menu_Item *item)
589 {
590    Elm_Menu_Item *new_item, *subitem;
591    Eina_List *iter;
592
593    if (item->separator)
594       new_item = elm_menu_item_separator_add(obj, parent);
595    else
596       new_item = elm_menu_item_add(obj, parent, item->icon_str, item->label, item->func, item->base.data);
597    elm_menu_item_disabled_set(new_item, item->disabled);
598
599    EINA_LIST_FOREACH(item->submenu.items, iter, subitem)
600       _item_clone(obj, new_item, subitem);
601 }
602
603 void
604 elm_menu_clone(Evas_Object *from_menu, Evas_Object *to_menu, Elm_Menu_Item *parent)
605 {
606    ELM_CHECK_WIDTYPE(from_menu, widtype);
607    ELM_CHECK_WIDTYPE(to_menu, widtype);
608    Widget_Data *from_wd = elm_widget_data_get(from_menu);
609    Eina_List *iter;
610    Elm_Menu_Item *item;
611
612    if (!from_wd) return;
613    EINA_LIST_FOREACH(from_wd->items, iter, item)
614       _item_clone(to_menu, parent, item);
615 }
616
617 /**
618  * Add an item at the end
619  *
620  * @param obj The menu object.
621  * @param icon A icon display on the item. The icon will be destryed by the menu.
622  * @param label The label of the item.
623  * @param func Function called when the user select the item.
624  * @param data Data sent by the callback.
625  * @return Returns the new item.
626  *
627  * @ingroup Menu
628  */
629 EAPI Elm_Menu_Item *
630 elm_menu_item_add(Evas_Object *obj, Elm_Menu_Item *parent, const char *icon, const char *label, Evas_Smart_Cb func, const void *data)
631 {
632    Elm_Menu_Item *subitem;
633    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
634    Widget_Data *wd = elm_widget_data_get(obj);
635    Evas_Object *icon_obj;
636
637    if (!wd) return NULL;
638    icon_obj = elm_icon_add(obj);
639    if (!icon_obj) return NULL;
640    subitem = elm_widget_item_new(obj, Elm_Menu_Item);
641    if (!subitem)
642      {
643         evas_object_del(icon_obj);
644         return NULL;
645      }
646    subitem->base.data = data;
647    subitem->func = func;
648    subitem->parent = parent;
649    subitem->icon = icon_obj;
650
651    _item_obj_create(subitem);
652    elm_menu_item_label_set(subitem, label);
653
654    elm_widget_sub_object_add(subitem->base.widget, subitem->icon);
655    edje_object_part_swallow(subitem->base.view, "elm.swallow.content", subitem->icon);
656    if (icon) elm_menu_item_icon_set(subitem, icon);
657
658    if (parent)
659      {
660         if (!parent->submenu.bx) _item_submenu_obj_create(parent);
661         elm_box_pack_end(parent->submenu.bx, subitem->base.view);
662         parent->submenu.items = eina_list_append(parent->submenu.items, subitem);
663      }
664    else
665      {
666         elm_box_pack_end(wd->bx, subitem->base.view);
667         wd->items = eina_list_append(wd->items, subitem);
668      }
669
670    _sizing_eval(obj);
671    return subitem;
672 }
673
674 /**
675  * Set the label of a menu item
676  *
677  * @param item The menu item object.
678  * @param label The label to set for @p item
679  *
680  * @ingroup Menu
681  */
682 EAPI void
683 elm_menu_item_label_set(Elm_Menu_Item *item, const char *label)
684 {
685    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
686    eina_stringshare_replace(&item->label, label);
687
688    if (label)
689      edje_object_signal_emit(item->base.view, "elm,state,text,visible", "elm");
690    else
691      edje_object_signal_emit(item->base.view, "elm,state,text,hidden", "elm");
692
693    edje_object_message_signal_process(item->base.view);
694    edje_object_part_text_set(item->base.view, "elm.text", label);
695    _sizing_eval(item->base.widget);
696 }
697
698 /**
699  * Get the label of a menu item
700  *
701  * @param item The menu item object.
702  * @return The label of @p item
703  *
704  * @ingroup Menu
705  */
706 EAPI const char *
707 elm_menu_item_label_get(const Elm_Menu_Item *item)
708 {
709    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
710    return item->label;
711 }
712
713 /**
714  * Set the icon of a menu item
715  *
716  * Once the icon object is set, a previously set one will be deleted.
717  *
718  * @param item The menu item object.
719  * @param icon The icon object to set for @p item
720  *
721  * @ingroup Menu
722  */
723 EAPI void
724 elm_menu_item_icon_set(Elm_Menu_Item *item, const char *icon)
725 {
726    char icon_tmp[512];
727    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
728    EINA_SAFETY_ON_NULL_RETURN(icon);
729    if (!*icon) return;
730    if ((item->icon_str) && (!strcmp(item->icon_str, icon))) return;
731    if ((snprintf(icon_tmp, sizeof(icon_tmp), "menu/%s", icon) > 0) &&
732        (elm_icon_standard_set(item->icon, icon_tmp)))
733      {
734         eina_stringshare_replace(&item->icon_str, icon);
735         edje_object_signal_emit(item->base.view, "elm,state,icon,visible", "elm");
736      }
737    else
738      edje_object_signal_emit(item->base.view, "elm,state,icon,hidden", "elm");
739    edje_object_message_signal_process(item->base.view);
740    _sizing_eval(item->base.widget);
741 }
742
743 /**
744  * Set the disabled state of @p item.
745  *
746  * @param item The menu item object.
747  * @param disabled The enabled/disabled state of the item
748  *
749  * @ingroup Menu
750  */
751 EAPI void
752 elm_menu_item_disabled_set(Elm_Menu_Item *item, Eina_Bool disabled)
753 {
754    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
755    if (disabled == item->disabled) return;
756    item->disabled = disabled;
757    if (disabled)
758      {
759         edje_object_signal_emit(item->base.view, "elm,state,disabled", "elm");
760         if (item->submenu.open) _submenu_hide(item);
761      }
762    else
763      edje_object_signal_emit(item->base.view, "elm,state,enabled", "elm");
764    edje_object_message_signal_process(item->base.view);
765 }
766
767 /**
768  * Get the disabled state of @p item.
769  *
770  * @param item The menu item object.
771  * @return The enabled/disabled state of the item
772  *
773  * @ingroup Menu
774  */
775 EAPI Eina_Bool
776 elm_menu_item_disabled_get(const Elm_Menu_Item *item)
777 {
778    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
779    return item->disabled;
780 }
781
782 /**
783  * Add a separator item to menu @p obj under @p parent.
784  *
785  * @param obj The menu object
786  * @param parent The item to add the separator under
787  *
788  * @return The created item or NULL on failure
789  *
790  * @ingroup Menu
791  */
792 EAPI Elm_Menu_Item *
793 elm_menu_item_separator_add(Evas_Object *obj, Elm_Menu_Item *parent)
794 {
795    Elm_Menu_Item *subitem;
796    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
797    Widget_Data *wd = elm_widget_data_get(obj);
798    if (!wd) return NULL;
799    /* don't add a separator as the first item */
800    if (!wd->items) return NULL;
801    /* don't allow adding more than one separator in a row */
802    if (parent) subitem = eina_list_last(parent->submenu.items)->data;
803    else subitem = eina_list_last(wd->items)->data;
804    if (subitem->separator) return NULL;
805
806    subitem = elm_widget_item_new(obj, Elm_Menu_Item);
807    if (!subitem) return NULL;
808    subitem->base.widget = obj;
809    subitem->separator = 1;
810    _item_separator_obj_create(subitem);
811    if (!parent)
812      {
813         elm_box_pack_end(wd->bx, subitem->base.view);
814         wd->items = eina_list_append(wd->items, subitem);
815      }
816    else
817      {
818         if (!parent->submenu.bx) _item_submenu_obj_create(parent);
819         elm_box_pack_end(parent->submenu.bx, subitem->base.view);
820         parent->submenu.items = eina_list_append(parent->submenu.items, subitem);
821      }
822    _sizing_eval(obj);
823    return subitem;
824 }
825
826 /**
827  * Get the icon object from a menu item
828  *
829  * @param item The menu item object
830  * @return The icon object or NULL if there's no icon
831  *
832  * @ingroup Menu
833  */
834 EAPI const Evas_Object *
835 elm_menu_item_object_icon_get(const Elm_Menu_Item *item)
836 {
837    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
838    return (const Evas_Object *)item->icon;
839 }
840
841 /**
842  * Get the string representation from the icon of a menu item
843  *
844  * @param item The menu item object.
845  * @return The string representation of @p item's icon or NULL
846  *
847  * @ingroup Menu
848  */
849 EAPI const char *
850 elm_menu_item_icon_get(const Elm_Menu_Item *item)
851 {
852    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
853    return item->icon_str;
854 }
855
856 /**
857  * Returns whether @p item is a separator.
858  *
859  * @param item The item to check
860  * @return If true, @p item is a separator
861  *
862  * @ingroup Menu
863  */
864 EAPI Eina_Bool
865 elm_menu_item_is_separator(Elm_Menu_Item *item)
866 {
867    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
868    return item->separator;
869 }
870
871 /**
872  * Deletes an item from the menu.
873  *
874  * @param item The item to delete.
875  *
876  * @ingroup Menu
877  */
878 EAPI void
879 elm_menu_item_del(Elm_Menu_Item *item)
880 {
881    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
882    Elm_Menu_Item *_item;
883
884    elm_widget_item_pre_notify_del(item);
885
886    EINA_LIST_FREE(item->submenu.items, _item) elm_menu_item_del(_item);
887    if (item->label) eina_stringshare_del(item->label);
888    if (item->icon) evas_object_del(item->icon);
889    if (item->submenu.hv) evas_object_del(item->submenu.hv);
890    if (item->submenu.location) evas_object_del(item->submenu.location);
891
892    if (item->parent)
893       item->parent->submenu.items = eina_list_remove(item->parent->submenu.items, item);
894    else
895      {
896         Widget_Data *wd = elm_widget_data_get(item->base.widget);
897         wd->items = eina_list_remove(wd->items, item);
898      }
899
900    elm_widget_item_del(item);
901 }
902
903 /**
904  * Set the function called when a menu item is freed.
905  *
906  * @param item The item to set the callback on
907  * @param func The function called
908  *
909  * @ingroup Menu
910  */
911 EAPI void
912 elm_menu_item_del_cb_set(Elm_Menu_Item *item, Evas_Smart_Cb func)
913 {
914    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
915    elm_widget_item_del_cb_set(item, func);
916 }
917
918 /**
919  * Returns the data associated with menu item @p item.
920  *
921  * @param item The item
922  * @return The data associated with @p item
923  *
924  * @ingroup Menu
925  */
926 EAPI void *
927 elm_menu_item_data_get(const Elm_Menu_Item *item)
928 {
929    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
930    return elm_widget_item_data_get(item);
931 }
932
933 /**
934  * Sets the data to be associated with menu item @p item.
935  *
936  * @param item The item
937  * @param data The data to be associated with @p item
938  *
939  * @ingroup Menu
940  */
941 EAPI void
942 elm_menu_item_data_set(Elm_Menu_Item *item, const void *data)
943 {
944    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
945    elm_widget_item_data_set(item, data);
946 }
947
948 /**
949  * Returns a list of @p item's subitems.
950  *
951  * @param item The item
952  * @return An Eina_List* of @p item's subitems
953  *
954  * @ingroup Menu
955  */
956 EAPI const Eina_List *
957 elm_menu_item_subitems_get(const Elm_Menu_Item *item)
958 {
959    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
960    return item->submenu.items;
961 }
962
963 /**
964  * Returns a list of @p item's items.
965  *
966  * @param obj The menu object
967  * @return An Eina_List* of @p item's items
968  *
969  * @ingroup Menu
970  */
971 EAPI const Eina_List *
972 elm_menu_items_get(const Evas_Object * obj)
973 {
974    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
975    Widget_Data *wd = elm_widget_data_get(obj);
976    return wd->items;
977 }
978
979 /**
980  * Set the selected state of @p item.
981  *
982  * @param item The menu item object.
983  * @param selected The selected/unselected state of the item
984  *
985  * @ingroup Menu
986  */
987 EAPI void
988 elm_menu_item_selected_set(Elm_Menu_Item *item, Eina_Bool selected)
989 {
990    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
991    if (selected == item->selected) return;
992    item->selected = selected;
993    if (selected)
994      {
995         edje_object_signal_emit(item->base.view, "elm,state,selected", "elm");
996         _menu_item_activate(item, NULL, NULL, NULL);
997      }
998    else
999      {
1000         edje_object_signal_emit(item->base.view, "elm,state,unselected", "elm");
1001         _menu_item_inactivate(item, NULL, NULL, NULL);
1002      }
1003    edje_object_message_signal_process(item->base.view);
1004 }
1005
1006 /**
1007  * Get the selected state of @p item.
1008  *
1009  * @param item The menu item object.
1010  * @return The selected/unselected state of the item
1011  *
1012  * @ingroup Menu
1013  */
1014 EAPI Eina_Bool
1015 elm_menu_item_selected_get(const Elm_Menu_Item *item)
1016 {
1017    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
1018    return item->selected;
1019 }
1020
1021 /**
1022  * Get the previous item in the menu.
1023  *
1024  * @param item The menu item object.
1025  * @return The item before it, or NULL if none
1026  *
1027  * @ingroup Menu
1028  */
1029 EAPI const Elm_Menu_Item *
1030 elm_menu_item_prev_get(const Elm_Menu_Item *it)
1031 {
1032    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
1033    if (it->parent)
1034      {
1035         Eina_List *l = eina_list_data_find_list(it->parent->submenu.items, it);
1036         l = eina_list_prev(l);
1037         if (!l) return NULL;
1038         return l->data;
1039      }
1040    else
1041      {
1042         Widget_Data *wd = elm_widget_data_get(it->base.widget);
1043         if (!wd | !wd->items) return NULL;
1044         Eina_List *l = eina_list_data_find_list(wd->items, it);
1045         l = eina_list_prev(l);
1046         if (!l) return NULL;
1047         return l->data;
1048      }
1049    return NULL;
1050 }
1051
1052 /**
1053  * Get the next item in the menu.
1054  *
1055  * @param item The menu item object.
1056  * @return The item after it, or NULL if none
1057  *
1058  * @ingroup Menu
1059  */
1060 EAPI const Elm_Menu_Item *
1061 elm_menu_item_next_get(const Elm_Menu_Item *it)
1062 {
1063    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
1064    if (it->parent)
1065      {
1066         Eina_List *l = eina_list_data_find_list(it->parent->submenu.items, it);
1067         l = eina_list_next(l);
1068         if (!l) return NULL;
1069         return l->data;
1070      }
1071    else
1072      {
1073         Widget_Data *wd = elm_widget_data_get(it->base.widget);
1074         if (!wd | !wd->items) return NULL;
1075         Eina_List *l = eina_list_data_find_list(wd->items, it);
1076         l = eina_list_next(l);
1077         if (!l) return NULL;
1078         return l->data;
1079      }
1080    return NULL;
1081 }
1082
1083 /**
1084  * Get the first item in the menu
1085  *
1086  * @param obj The menu object
1087  * @return The first item, or NULL if none
1088  *
1089  * @ingroup Menu
1090  */
1091 EAPI const Elm_Menu_Item *
1092 elm_menu_first_item_get(const Evas_Object * obj)
1093 {
1094    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1095    Widget_Data *wd = elm_widget_data_get(obj);
1096    if (!wd) return NULL;
1097    if (wd->items) return wd->items->data;
1098    return NULL;
1099 }
1100
1101 /**
1102  * Get the last item in the menu
1103  *
1104  * @param obj The menu object
1105  * @return The last item, or NULL if none
1106  *
1107  * @ingroup Menu
1108  */
1109 EAPI const Elm_Menu_Item *
1110 elm_menu_last_item_get(const Evas_Object * obj)
1111 {
1112    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1113    Widget_Data *wd = elm_widget_data_get(obj);
1114    if (!wd) return NULL;
1115    Eina_List *l = eina_list_last(wd->items);
1116    if (l) return l->data;
1117    return NULL;
1118 }
1119
1120 /**
1121  * Get the selected item in the menu
1122  *
1123  * @param obj The menu object
1124  * @return The selected item, or NULL if none
1125  *
1126  * @ingroup Menu
1127  */
1128 EAPI const Elm_Menu_Item *
1129 elm_menu_selected_item_get(const Evas_Object * obj)
1130 {
1131    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1132    Widget_Data *wd = elm_widget_data_get(obj);
1133    if (!wd) return NULL;
1134    Eina_List *l;
1135    Elm_Menu_Item *item;
1136    EINA_LIST_FOREACH(wd->items, l, item)
1137      {
1138         if (item->selected) return item;
1139      }
1140    return NULL;
1141 }
1142