elementary - removed deprecated widgets & APIs.
[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;
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(VIEW(item), elm_widget_mirrored_get(obj));
110              ll = eina_list_append(ll, item->submenu.items);
111              if (item->separator)
112                _elm_theme_object_set(obj, VIEW(item), "menu", "separator",
113                                      elm_widget_style_get(obj));
114              else if (item->submenu.bx)
115                {
116                   _elm_theme_object_set
117                      (obj, VIEW(item), "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, VIEW(item), "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                   (VIEW(item), "elm,state,disabled", "elm");
132              else
133                edje_object_signal_emit
134                   (VIEW(item), "elm,state,enabled", "elm");
135              edje_object_message_signal_process(VIEW(item));
136              edje_object_scale_set(VIEW(item), 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(WIDGET(parent));
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(VIEW(parent), &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(WIDGET(parent)))
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(VIEW(item), &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(VIEW(item), minw, minh);
239    evas_object_size_hint_max_set(VIEW(item), 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(WIDGET(item), NULL, NULL);
316
317    if (item->func) item->func((void *)(item->base.data), WIDGET(item), 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(WIDGET(item));
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(WIDGET(item));
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(WIDGET(item));
373    if (!wd) return;
374    VIEW(item) = edje_object_add(evas_object_evas_get(wd->bx));
375    edje_object_mirrored_set(VIEW(item), elm_widget_mirrored_get(WIDGET(item)));
376    evas_object_size_hint_weight_set(VIEW(item), EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
377    evas_object_size_hint_fill_set(VIEW(item), EVAS_HINT_FILL, EVAS_HINT_FILL);
378    _elm_theme_object_set(WIDGET(item), VIEW(item), "menu", "item",  elm_widget_style_get(WIDGET(item)));
379    edje_object_signal_callback_add(VIEW(item), "elm,action,click", "",
380                                    _menu_item_select, item);
381    edje_object_signal_callback_add(VIEW(item), "elm,action,activate", "",
382                                    _menu_item_activate, item);
383    edje_object_signal_callback_add(VIEW(item), "elm,action,inactivate", "",
384                                    _menu_item_inactivate, item);
385    evas_object_show(VIEW(item));
386 }
387
388 static void
389 _item_separator_obj_create(Elm_Menu_Item *item)
390 {
391    Widget_Data *wd = elm_widget_data_get(WIDGET(item));
392    if (!wd) return;
393    VIEW(item) = edje_object_add(evas_object_evas_get(wd->bx));
394    evas_object_size_hint_weight_set(VIEW(item), EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
395    evas_object_size_hint_fill_set(VIEW(item), EVAS_HINT_FILL, EVAS_HINT_FILL);
396    _elm_theme_object_set(WIDGET(item), VIEW(item), "menu", "separator",  elm_widget_style_get(WIDGET(item)));
397    edje_object_signal_callback_add(VIEW(item), "elm,action,activate", "",
398                                    _menu_item_activate, item);
399    evas_object_show(VIEW(item));
400 }
401
402 static void
403 _item_submenu_obj_create(Elm_Menu_Item *item)
404 {
405    Widget_Data *wd = elm_widget_data_get(WIDGET(item));
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(VIEW(item), elm_widget_mirrored_get(WIDGET(item)));
421    _elm_theme_object_set(WIDGET(item), VIEW(item), "menu", "item_with_submenu",  elm_widget_style_get(WIDGET(item)));
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(VIEW(item), "elm,action,open", "",
426                                    _submenu_open, item);
427    evas_object_event_callback_add(VIEW(item), EVAS_CALLBACK_MOVE, _item_move_resize, item);
428    evas_object_event_callback_add(VIEW(item), EVAS_CALLBACK_RESIZE, _item_move_resize, item);
429
430    evas_object_event_callback_add(item->submenu.bx, EVAS_CALLBACK_RESIZE, _menu_resize, WIDGET(item));
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 VIEW(item);
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, VIEW(subitem));
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, VIEW(subitem));
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(WIDGET(subitem), subitem->content);
630    edje_object_part_swallow(VIEW(subitem), "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(WIDGET(subitem), subitem->content);
658    edje_object_part_swallow(VIEW(subitem), "elm.swallow.content", subobj);
659    _sizing_eval(WIDGET(subitem));
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(VIEW(item), "elm,state,text,visible", "elm");
681    else
682      edje_object_signal_emit(VIEW(item), "elm,state,text,hidden", "elm");
683
684    edje_object_message_signal_process(VIEW(item));
685    edje_object_part_text_set(VIEW(item), "elm.text", label);
686    _sizing_eval(WIDGET(item));
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         elm_icon_standard_set(item->content, icon)))
707      {
708         eina_stringshare_replace(&item->icon_str, icon);
709         edje_object_signal_emit(VIEW(item), "elm,state,icon,visible", "elm");
710      }
711    else
712      edje_object_signal_emit(VIEW(item), "elm,state,icon,hidden", "elm");
713    edje_object_message_signal_process(VIEW(item));
714    _sizing_eval(WIDGET(item));
715 }
716
717 EAPI void
718 elm_menu_item_disabled_set(Elm_Menu_Item *item, Eina_Bool disabled)
719 {
720    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
721    if (disabled == item->disabled) return;
722    item->disabled = !!disabled;
723    if (disabled)
724      {
725         edje_object_signal_emit(VIEW(item), "elm,state,disabled", "elm");
726         if (item->submenu.open) _submenu_hide(item);
727      }
728    else
729      edje_object_signal_emit(VIEW(item), "elm,state,enabled", "elm");
730    edje_object_message_signal_process(VIEW(item));
731 }
732
733 EAPI Eina_Bool
734 elm_menu_item_disabled_get(const Elm_Menu_Item *item)
735 {
736    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
737    return item->disabled;
738 }
739
740 EAPI Elm_Menu_Item *
741 elm_menu_item_separator_add(Evas_Object *obj, Elm_Menu_Item *parent)
742 {
743    Elm_Menu_Item *subitem;
744    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
745    Widget_Data *wd = elm_widget_data_get(obj);
746    if (!wd) return NULL;
747    /* don't add a separator as the first item */
748    if (!wd->items) return NULL;
749    /* don't allow adding more than one separator in a row */
750    if (parent) subitem = eina_list_last(parent->submenu.items)->data;
751    else subitem = eina_list_last(wd->items)->data;
752    if (subitem->separator) return NULL;
753
754    subitem = elm_widget_item_new(obj, Elm_Menu_Item);
755    if (!subitem) return NULL;
756    WIDGET(subitem) = obj;
757    subitem->separator = 1;
758    _item_separator_obj_create(subitem);
759    if (!parent)
760      {
761         elm_box_pack_end(wd->bx, VIEW(subitem));
762         wd->items = eina_list_append(wd->items, subitem);
763      }
764    else
765      {
766         if (!parent->submenu.bx) _item_submenu_obj_create(parent);
767         elm_box_pack_end(parent->submenu.bx, VIEW(subitem));
768         parent->submenu.items = eina_list_append(parent->submenu.items, subitem);
769      }
770    _sizing_eval(obj);
771    return subitem;
772 }
773
774 EAPI Eina_Bool
775 elm_menu_item_object_content_set(Elm_Menu_Item *item, Evas_Object *obj)
776 {
777    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
778    if (item->content)
779      {
780         elm_widget_sub_object_del(WIDGET(item), item->content);
781         evas_object_del(item->content);
782      }
783
784    item->content = obj;
785
786    elm_widget_sub_object_add(WIDGET(item), item->content);
787    edje_object_part_swallow(VIEW(item), "elm.swallow.content", item->content);
788    _sizing_eval(WIDGET(item));
789    return EINA_TRUE;
790 }
791
792 EAPI Evas_Object *
793 elm_menu_item_object_content_get(const Elm_Menu_Item *item)
794 {
795    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
796    return item->content;
797 }
798
799 EAPI const char *
800 elm_menu_item_object_icon_name_get(const Elm_Menu_Item *item)
801 {
802    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
803    return item->icon_str;
804 }
805
806 EAPI Eina_Bool
807 elm_menu_item_is_separator(Elm_Menu_Item *item)
808 {
809    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
810    return item->separator;
811 }
812
813 EAPI void
814 elm_menu_item_del(Elm_Menu_Item *item)
815 {
816    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
817    Elm_Menu_Item *_item;
818
819    elm_widget_item_pre_notify_del(item);
820
821    EINA_LIST_FREE(item->submenu.items, _item) elm_menu_item_del(_item);
822    if (item->label) eina_stringshare_del(item->label);
823    if (item->content) evas_object_del(item->content);
824    if (item->submenu.hv) evas_object_del(item->submenu.hv);
825    if (item->submenu.location) evas_object_del(item->submenu.location);
826
827    if (item->parent)
828      item->parent->submenu.items = eina_list_remove(item->parent->submenu.items, item);
829    else
830      {
831         Widget_Data *wd = elm_widget_data_get(WIDGET(item));
832         wd->items = eina_list_remove(wd->items, item);
833      }
834
835    elm_widget_item_del(item);
836 }
837
838 EAPI void
839 elm_menu_item_del_cb_set(Elm_Menu_Item *item, Evas_Smart_Cb func)
840 {
841    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
842    elm_widget_item_del_cb_set(item, func);
843 }
844
845 EAPI void *
846 elm_menu_item_data_get(const Elm_Menu_Item *item)
847 {
848    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
849    return elm_widget_item_data_get(item);
850 }
851
852 EAPI void
853 elm_menu_item_data_set(Elm_Menu_Item *item, const void *data)
854 {
855    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
856    elm_widget_item_data_set(item, data);
857 }
858
859 EAPI const Eina_List *
860 elm_menu_item_subitems_get(const Elm_Menu_Item *item)
861 {
862    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
863    return item->submenu.items;
864 }
865
866 EAPI const Eina_List *
867 elm_menu_items_get(const Evas_Object * obj)
868 {
869    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
870    Widget_Data *wd = elm_widget_data_get(obj);
871    return wd->items;
872 }
873
874 EAPI void
875 elm_menu_item_selected_set(Elm_Menu_Item *item, Eina_Bool selected)
876 {
877    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item);
878    if (selected == item->selected) return;
879    item->selected = selected;
880    if (selected)
881      {
882         edje_object_signal_emit(VIEW(item), "elm,state,selected", "elm");
883         _menu_item_activate(item, NULL, NULL, NULL);
884      }
885    else
886      {
887         edje_object_signal_emit(VIEW(item), "elm,state,unselected", "elm");
888         _menu_item_inactivate(item, NULL, NULL, NULL);
889      }
890    edje_object_message_signal_process(VIEW(item));
891 }
892
893 EAPI Eina_Bool
894 elm_menu_item_selected_get(const Elm_Menu_Item *item)
895 {
896    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, EINA_FALSE);
897    return item->selected;
898 }
899
900 EAPI Elm_Menu_Item *
901 elm_menu_item_prev_get(const Elm_Menu_Item *it)
902 {
903    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
904    if (it->parent)
905      {
906         Eina_List *l = eina_list_data_find_list(it->parent->submenu.items, it);
907         l = eina_list_prev(l);
908         if (!l) return NULL;
909         return l->data;
910      }
911    else
912      {
913         Widget_Data *wd = elm_widget_data_get(WIDGET(it));
914         if (!wd || !wd->items) return NULL;
915         Eina_List *l = eina_list_data_find_list(wd->items, it);
916         l = eina_list_prev(l);
917         if (!l) return NULL;
918         return l->data;
919      }
920    return NULL;
921 }
922
923 EAPI Elm_Menu_Item *
924 elm_menu_item_next_get(const Elm_Menu_Item *it)
925 {
926    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
927    if (it->parent)
928      {
929         Eina_List *l = eina_list_data_find_list(it->parent->submenu.items, it);
930         l = eina_list_next(l);
931         if (!l) return NULL;
932         return l->data;
933      }
934    else
935      {
936         Widget_Data *wd = elm_widget_data_get(WIDGET(it));
937         if (!wd || !wd->items) return NULL;
938         Eina_List *l = eina_list_data_find_list(wd->items, it);
939         l = eina_list_next(l);
940         if (!l) return NULL;
941         return l->data;
942      }
943    return NULL;
944 }
945
946 EAPI Evas_Object *
947 elm_menu_item_menu_get(const Elm_Menu_Item *item)
948 {
949    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(item, NULL);
950    return WIDGET(item);
951 }
952
953 EAPI Elm_Menu_Item *
954 elm_menu_first_item_get(const Evas_Object * obj)
955 {
956    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
957    Widget_Data *wd = elm_widget_data_get(obj);
958    if (!wd) return NULL;
959    if (wd->items) return wd->items->data;
960    return NULL;
961 }
962
963 EAPI Elm_Menu_Item *
964 elm_menu_last_item_get(const Evas_Object * obj)
965 {
966    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
967    Widget_Data *wd = elm_widget_data_get(obj);
968    if (!wd) return NULL;
969    Eina_List *l = eina_list_last(wd->items);
970    if (l) return l->data;
971    return NULL;
972 }
973
974 EAPI Elm_Menu_Item *
975 elm_menu_selected_item_get(const Evas_Object * obj)
976 {
977    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
978    Widget_Data *wd = elm_widget_data_get(obj);
979    if (!wd) return NULL;
980    Eina_List *l;
981    Elm_Menu_Item *item;
982    EINA_LIST_FOREACH(wd->items, l, item)
983      {
984         if (item->selected) return item;
985      }
986    return NULL;
987 }
988