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