88e971a356a48378a6fbb99d384b25f22d88e725
[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_icon_set(Elm_Menu_Item *item, const char *icon)
718 {
719    elm_menu_item_object_icon_name_set(item, icon);
720 }
721
722 EAPI void
723 elm_menu_item_disabled_set(Elm_Menu_Item *item, Eina_Bool disabled)
724 {
725    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
726    if (disabled == item->disabled) return;
727    item->disabled = !!disabled;
728    if (disabled)
729      {
730         edje_object_signal_emit(item->base.view, "elm,state,disabled", "elm");
731         if (item->submenu.open) _submenu_hide(item);
732      }
733    else
734      edje_object_signal_emit(item->base.view, "elm,state,enabled", "elm");
735    edje_object_message_signal_process(item->base.view);
736 }
737
738 EAPI Eina_Bool
739 elm_menu_item_disabled_get(const Elm_Menu_Item *item)
740 {
741    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
742    return item->disabled;
743 }
744
745 EAPI Elm_Menu_Item *
746 elm_menu_item_separator_add(Evas_Object *obj, Elm_Menu_Item *parent)
747 {
748    Elm_Menu_Item *subitem;
749    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
750    Widget_Data *wd = elm_widget_data_get(obj);
751    if (!wd) return NULL;
752    /* don't add a separator as the first item */
753    if (!wd->items) return NULL;
754    /* don't allow adding more than one separator in a row */
755    if (parent) subitem = eina_list_last(parent->submenu.items)->data;
756    else subitem = eina_list_last(wd->items)->data;
757    if (subitem->separator) return NULL;
758
759    subitem = elm_widget_item_new(obj, Elm_Menu_Item);
760    if (!subitem) return NULL;
761    subitem->base.widget = obj;
762    subitem->separator = 1;
763    _item_separator_obj_create(subitem);
764    if (!parent)
765      {
766         elm_box_pack_end(wd->bx, subitem->base.view);
767         wd->items = eina_list_append(wd->items, subitem);
768      }
769    else
770      {
771         if (!parent->submenu.bx) _item_submenu_obj_create(parent);
772         elm_box_pack_end(parent->submenu.bx, subitem->base.view);
773         parent->submenu.items = eina_list_append(parent->submenu.items, subitem);
774      }
775    _sizing_eval(obj);
776    return subitem;
777 }
778
779 EAPI Eina_Bool
780 elm_menu_item_object_content_set(Elm_Menu_Item *item, Evas_Object *obj)
781 {
782    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
783    if (item->content)
784      {
785         elm_widget_sub_object_del(item->base.widget, item->content);
786         evas_object_del(item->content);
787      }
788
789    item->content = obj;
790
791    elm_widget_sub_object_add(item->base.widget, item->content);
792    edje_object_part_swallow(item->base.view, "elm.swallow.content", item->content);
793    _sizing_eval(item->base.widget);
794    return EINA_TRUE;
795 }
796
797 EAPI Evas_Object *
798 elm_menu_item_object_content_get(const Elm_Menu_Item *item)
799 {
800    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
801    return item->content;
802 }
803
804 EAPI Evas_Object *
805 elm_menu_item_object_icon_get(const Elm_Menu_Item *item)
806 {
807    return elm_menu_item_object_content_get(item);
808 }
809
810 EAPI const char *
811 elm_menu_item_object_icon_name_get(const Elm_Menu_Item *item)
812 {
813    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
814    return item->icon_str;
815 }
816
817 EAPI const char *
818 elm_menu_item_icon_get(const Elm_Menu_Item *item)
819 {
820    return elm_menu_item_object_icon_name_get(item);
821 }
822
823 EAPI Eina_Bool
824 elm_menu_item_is_separator(Elm_Menu_Item *item)
825 {
826    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
827    return item->separator;
828 }
829
830 EAPI void
831 elm_menu_item_del(Elm_Menu_Item *item)
832 {
833    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
834    Elm_Menu_Item *_item;
835
836    elm_widget_item_pre_notify_del(item);
837
838    EINA_LIST_FREE(item->submenu.items, _item) elm_menu_item_del(_item);
839    if (item->label) eina_stringshare_del(item->label);
840    if (item->content) evas_object_del(item->content);
841    if (item->submenu.hv) evas_object_del(item->submenu.hv);
842    if (item->submenu.location) evas_object_del(item->submenu.location);
843
844    if (item->parent)
845      item->parent->submenu.items = eina_list_remove(item->parent->submenu.items, item);
846    else
847      {
848         Widget_Data *wd = elm_widget_data_get(item->base.widget);
849         wd->items = eina_list_remove(wd->items, item);
850      }
851
852    elm_widget_item_del(item);
853 }
854
855 EAPI void
856 elm_menu_item_del_cb_set(Elm_Menu_Item *item, Evas_Smart_Cb func)
857 {
858    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
859    elm_widget_item_del_cb_set(item, func);
860 }
861
862 EAPI void *
863 elm_menu_item_data_get(const Elm_Menu_Item *item)
864 {
865    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
866    return elm_widget_item_data_get(item);
867 }
868
869 EAPI void
870 elm_menu_item_data_set(Elm_Menu_Item *item, const void *data)
871 {
872    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
873    elm_widget_item_data_set(item, data);
874 }
875
876 EAPI const Eina_List *
877 elm_menu_item_subitems_get(const Elm_Menu_Item *item)
878 {
879    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
880    return item->submenu.items;
881 }
882
883 EAPI const Eina_List *
884 elm_menu_items_get(const Evas_Object * obj)
885 {
886    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
887    Widget_Data *wd = elm_widget_data_get(obj);
888    return wd->items;
889 }
890
891 EAPI void
892 elm_menu_item_selected_set(Elm_Menu_Item *item, Eina_Bool selected)
893 {
894    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
895    if (selected == item->selected) return;
896    item->selected = selected;
897    if (selected)
898      {
899         edje_object_signal_emit(item->base.view, "elm,state,selected", "elm");
900         _menu_item_activate(item, NULL, NULL, NULL);
901      }
902    else
903      {
904         edje_object_signal_emit(item->base.view, "elm,state,unselected", "elm");
905         _menu_item_inactivate(item, NULL, NULL, NULL);
906      }
907    edje_object_message_signal_process(item->base.view);
908 }
909
910 EAPI Eina_Bool
911 elm_menu_item_selected_get(const Elm_Menu_Item *item)
912 {
913    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
914    return item->selected;
915 }
916
917 EAPI Elm_Menu_Item *
918 elm_menu_item_prev_get(const Elm_Menu_Item *it)
919 {
920    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
921    if (it->parent)
922      {
923         Eina_List *l = eina_list_data_find_list(it->parent->submenu.items, it);
924         l = eina_list_prev(l);
925         if (!l) return NULL;
926         return l->data;
927      }
928    else
929      {
930         Widget_Data *wd = elm_widget_data_get(it->base.widget);
931         if (!wd | !wd->items) return NULL;
932         Eina_List *l = eina_list_data_find_list(wd->items, it);
933         l = eina_list_prev(l);
934         if (!l) return NULL;
935         return l->data;
936      }
937    return NULL;
938 }
939
940 EAPI Elm_Menu_Item *
941 elm_menu_item_next_get(const Elm_Menu_Item *it)
942 {
943    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
944    if (it->parent)
945      {
946         Eina_List *l = eina_list_data_find_list(it->parent->submenu.items, it);
947         l = eina_list_next(l);
948         if (!l) return NULL;
949         return l->data;
950      }
951    else
952      {
953         Widget_Data *wd = elm_widget_data_get(it->base.widget);
954         if (!wd | !wd->items) return NULL;
955         Eina_List *l = eina_list_data_find_list(wd->items, it);
956         l = eina_list_next(l);
957         if (!l) return NULL;
958         return l->data;
959      }
960    return NULL;
961 }
962
963 EAPI Evas_Object *
964 elm_menu_item_menu_get(const Elm_Menu_Item *item)
965 {
966    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
967    return item->base.widget;
968 }
969
970 EAPI Elm_Menu_Item *
971 elm_menu_first_item_get(const Evas_Object * obj)
972 {
973    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
974    Widget_Data *wd = elm_widget_data_get(obj);
975    if (!wd) return NULL;
976    if (wd->items) return wd->items->data;
977    return NULL;
978 }
979
980 EAPI Elm_Menu_Item *
981 elm_menu_last_item_get(const Evas_Object * obj)
982 {
983    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
984    Widget_Data *wd = elm_widget_data_get(obj);
985    if (!wd) return NULL;
986    Eina_List *l = eina_list_last(wd->items);
987    if (l) return l->data;
988    return NULL;
989 }
990
991 EAPI Elm_Menu_Item *
992 elm_menu_selected_item_get(const Evas_Object * obj)
993 {
994    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
995    Widget_Data *wd = elm_widget_data_get(obj);
996    if (!wd) return NULL;
997    Eina_List *l;
998    Elm_Menu_Item *item;
999    EINA_LIST_FOREACH(wd->items, l, item)
1000      {
1001         if (item->selected) return item;
1002      }
1003    return NULL;
1004 }
1005