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