1 #include <Elementary.h>
8 * A menu is a list of items displayed above the window. Each item can
9 * have a sub-menu. The menu object can be used to display a menu on right
10 * click, in a toolbar, anywhere.
14 typedef struct _Widget_Data Widget_Data;
20 Elm_Menu_Item *parent;
25 //if classic item or submenu
35 Evas_Object *hv, *bx, *location;
41 Evas_Object *hv, *bx, *location, *parent;
43 Evas_Coord xloc, yloc;
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 _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
53 static void _submenu_hide(Elm_Menu_Item *item);
54 static void _submenu_open(void *data, Evas_Object *obj, const char *emission, const char *source);
55 static void _parent_resize(void *data, Evas *e, Evas_Object *obj, void *event_info);
56 static void _menu_hide(void *data, Evas_Object *obj, void *event_info);
59 _del_pre_hook(Evas_Object *obj)
61 Widget_Data *wd = elm_widget_data_get(obj);
63 evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE, _parent_resize, obj);
67 _del_hook(Evas_Object *obj)
69 Eina_List *l, *ll = NULL;
71 Widget_Data *wd = elm_widget_data_get(obj);
73 ll = eina_list_append(ll, wd->items);
74 EINA_LIST_FOREACH(ll, ll, l)
76 EINA_LIST_FREE(l, item)
78 ll = eina_list_append(ll, item->items);
79 if (item->del_cb) item->del_cb((void*)item->data, item->o, item);
80 if (item->label) eina_stringshare_del(item->label);
81 if (item->hv) evas_object_del(item->hv);
82 if (item->location) evas_object_del(item->location);
86 if (wd->hv) evas_object_del(wd->hv);
87 if (wd->location) evas_object_del(wd->location);
92 _theme_hook(Evas_Object *obj)
94 Eina_List *l, *_l, *ll = NULL;
96 Widget_Data *wd = elm_widget_data_get(obj);
98 ll = eina_list_append(ll, wd->items);
99 EINA_LIST_FOREACH(ll, ll, l)
101 EINA_LIST_FOREACH(l, _l, item)
103 ll = eina_list_append(ll, item->items);
105 _elm_theme_object_set(obj, item->o, "menu", "separator",
106 elm_widget_style_get(obj));
109 _elm_theme_object_set(obj, item->o, "menu", "item_with_submenu",
110 elm_widget_style_get(obj));
111 elm_menu_item_label_set(item, item->label);
112 elm_menu_item_icon_set(item, item->icon);
116 _elm_theme_object_set(obj, item->o, "menu", "item",
117 elm_widget_style_get(obj));
118 elm_menu_item_label_set(item, item->label);
119 elm_menu_item_icon_set(item, item->icon);
122 edje_object_signal_emit(item->o, "elm,state,disabled", "elm");
124 edje_object_signal_emit(item->o, "elm,state,enabled", "elm");
125 edje_object_message_signal_process(item->o);
126 edje_object_scale_set(item->o, elm_widget_scale_get(obj) *
134 _sizing_eval(Evas_Object *obj)
138 Evas_Coord x_p, y_p, w_p, h_p, x2, y2, w2, h2, bx, by, bw, bh;
139 Widget_Data *wd = elm_widget_data_get(obj);
140 if ((!wd) || (!wd->parent)) return;
141 EINA_LIST_FOREACH(wd->items,l,item) _item_sizing_eval(item);
142 evas_object_geometry_get(wd->location, &x_p, &y_p, &w_p, &h_p);
143 evas_object_geometry_get(wd->parent, &x2, &y2, &w2, &h2);
144 evas_object_geometry_get(wd->bx, &bx, &by, &bw, &bh);
149 if (x_p+bw > x2+w2) x_p -= x_p+bw - (x2+w2);
150 if (x_p < x2) x_p += x2 - x_p;
152 if (y_p+h_p+bh > y2+h2) y_p -= y_p+h_p+bh - (y2+h2);
153 if (y_p < y2) y_p += y2 - y_p;
155 evas_object_move(wd->location, x_p, y_p);
156 evas_object_resize(wd->location, bw, h_p);
157 evas_object_size_hint_min_set(wd->location, bw, h_p);
158 evas_object_size_hint_max_set(wd->location, bw, h_p);
159 elm_hover_target_set(wd->hv, wd->location);
161 EINA_LIST_FOREACH(wd->items,l,item)
163 if (item->open) _submenu_sizing_eval(item);
168 _submenu_sizing_eval(Elm_Menu_Item *parent)
172 Evas_Coord x_p, y_p, w_p, h_p, x2, y2, w2, h2, bx, by, bw, bh, px, py, pw, ph;
173 Widget_Data *wd = elm_widget_data_get(parent->menu);
175 EINA_LIST_FOREACH(parent->items,l,item) _item_sizing_eval(item);
176 evas_object_geometry_get(parent->location, &x_p, &y_p, &w_p, &h_p);
177 evas_object_geometry_get(parent->o, &x2, &y2, &w2, &h2);
178 evas_object_geometry_get(parent->bx, &bx, &by, &bw, &bh);
179 evas_object_geometry_get(wd->parent, &px, &py, &pw, &ph);
184 if (x_p + bw > px + pw)
188 y_p -= y_p+bh - (py+ph);
192 evas_object_move(parent->location, x_p, y_p);
193 evas_object_resize(parent->location, bw, h_p);
194 evas_object_size_hint_min_set(parent->location, bw, h_p);
195 evas_object_size_hint_max_set(parent->location, bw, h_p);
196 elm_hover_target_set(parent->hv, parent->location);
198 EINA_LIST_FOREACH(parent->items,l,item)
201 _submenu_sizing_eval(item);
206 _item_sizing_eval(Elm_Menu_Item *item)
208 Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
209 if (!item->separator)
210 elm_coords_finger_size_adjust(1, &minw, 1, &minh);
211 edje_object_size_min_restricted_calc(item->o, &minw, &minh, minw, minh);
212 if (!item->separator)
213 elm_coords_finger_size_adjust(1, &minw, 1, &minh);
214 evas_object_size_hint_min_set(item->o, minw, minh);
215 evas_object_size_hint_max_set(item->o, maxw, maxh);
219 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
225 _menu_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
231 _parent_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
237 _item_move_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
239 Elm_Menu_Item *item = data;
240 if (item->open) _submenu_sizing_eval(item);
244 _hover_clicked_cb(void *data, Evas_Object *obj, void *event_info)
246 _menu_hide(data, obj, event_info);
247 evas_object_smart_callback_call(data, "clicked", NULL);
251 _menu_hide(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
254 Elm_Menu_Item *item2;
255 Widget_Data *wd = elm_widget_data_get(data);
257 evas_object_hide(wd->hv);
258 evas_object_hide(data);
260 EINA_LIST_FOREACH(wd->items, l, item2)
262 if (item2->open) _submenu_hide(item2);
267 _submenu_hide(Elm_Menu_Item *item)
270 Elm_Menu_Item *item2;
271 evas_object_hide(item->hv);
272 item->open = EINA_FALSE;
273 EINA_LIST_FOREACH(item->items, l, item2)
275 if (item2->open) _submenu_hide(item2);
280 _menu_item_select(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
282 Elm_Menu_Item *it = data;
285 if (!it->open) _submenu_open(it, NULL, NULL, NULL);
286 else _submenu_hide(it);
289 _menu_hide(it->menu, NULL, NULL);
291 if (it->func) it->func((void *)(it->data), it->menu, it);
295 _menu_item_activate(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
298 Elm_Menu_Item *item2;
299 Elm_Menu_Item *item = data;
302 EINA_LIST_FOREACH(item->parent->items, l, item2)
304 if ((item2->open) && (item2 != item)) _submenu_hide(item2);
309 Widget_Data *wd = elm_widget_data_get(item->menu);
310 EINA_LIST_FOREACH(wd->items, l, item2)
312 if ((item2->open) && (item2 != item)) _submenu_hide(item2);
318 _submenu_open(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
320 Elm_Menu_Item *it = data;
321 it->open = EINA_TRUE;
322 evas_object_show(it->hv);
323 _sizing_eval(it->menu);
327 _show(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
329 Widget_Data *wd = elm_widget_data_get(data);
331 evas_object_show(wd->hv);
335 _item_obj_create(Elm_Menu_Item *item)
337 Widget_Data *wd = elm_widget_data_get(item->menu);
339 item->o = edje_object_add(evas_object_evas_get(wd->bx));
340 evas_object_size_hint_weight_set(item->o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
341 evas_object_size_hint_fill_set(item->o, EVAS_HINT_FILL, EVAS_HINT_FILL);
342 _elm_theme_object_set(item->menu, item->o, "menu", "item", elm_widget_style_get(item->menu));
343 edje_object_signal_callback_add(item->o, "elm,action,click", "",
344 _menu_item_select, item);
345 edje_object_signal_callback_add(item->o, "elm,action,activate", "",
346 _menu_item_activate, item);
347 evas_object_show(item->o);
351 _item_separator_obj_create(Elm_Menu_Item *item)
353 Widget_Data *wd = elm_widget_data_get(item->menu);
355 item->o = edje_object_add(evas_object_evas_get(wd->bx));
356 evas_object_size_hint_weight_set(item->o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
357 evas_object_size_hint_fill_set(item->o, EVAS_HINT_FILL, EVAS_HINT_FILL);
358 _elm_theme_object_set(item->menu, item->o, "menu", "separator", elm_widget_style_get(item->menu));
359 edje_object_signal_callback_add(item->o, "elm,action,activate", "",
360 _menu_item_activate, item);
361 evas_object_show(item->o);
365 _item_submenu_obj_create(Elm_Menu_Item *item)
367 Widget_Data *wd = elm_widget_data_get(item->menu);
369 item->location = elm_icon_add(wd->bx);
370 item->hv = elm_hover_add(wd->bx);
371 elm_hover_target_set(item->hv, item->location);
372 elm_hover_parent_set(item->hv, wd->parent);
373 elm_object_style_set(item->hv, "submenu");
375 item->bx = elm_box_add(wd->bx);
376 evas_object_size_hint_weight_set(item->bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
377 evas_object_show(item->bx);
378 elm_hover_content_set(item->hv, elm_hover_best_content_location_get(item->hv, ELM_HOVER_AXIS_VERTICAL), item->bx);
380 _elm_theme_object_set(item->menu, item->o, "menu", "item_with_submenu", elm_widget_style_get(item->menu));
381 elm_menu_item_label_set(item, item->label);
382 elm_menu_item_icon_set(item, item->icon);
384 edje_object_signal_callback_add(item->o, "elm,action,open", "",
385 _submenu_open, item);
386 evas_object_event_callback_add(item->o, EVAS_CALLBACK_MOVE, _item_move_resize, item);
387 evas_object_event_callback_add(item->o, EVAS_CALLBACK_RESIZE, _item_move_resize, item);
389 evas_object_event_callback_add(item->bx, EVAS_CALLBACK_RESIZE, _menu_resize, item->menu);
393 * Add a new menu to the parent
395 * @param parent The parent object.
396 * @return The new object or NULL if it cannot be created.
401 elm_menu_add(Evas_Object *parent)
407 wd = ELM_NEW(Widget_Data);
408 e = evas_object_evas_get(parent);
409 obj = elm_widget_add(e);
410 ELM_SET_WIDTYPE(widtype, "menu");
411 elm_widget_type_set(obj, "menu");
412 elm_widget_sub_object_add(parent, obj);
413 elm_widget_data_set(obj, wd);
414 elm_widget_del_pre_hook_set(obj, _del_pre_hook);
415 elm_widget_del_hook_set(obj, _del_hook);
416 elm_widget_theme_hook_set(obj, _theme_hook);
418 wd->location = elm_icon_add(obj);
421 wd->hv = elm_hover_add(obj);
422 elm_hover_parent_set(wd->hv, parent);
423 elm_hover_target_set(wd->hv, wd->location);
424 elm_object_style_set(wd->hv, "menu");
425 evas_object_smart_callback_add(wd->hv, "clicked", _hover_clicked_cb, obj);
427 wd->bx = elm_box_add(obj);
428 evas_object_size_hint_weight_set(wd->bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
429 evas_object_show(wd->bx);
430 elm_hover_content_set(wd->hv, elm_hover_best_content_location_get(wd->hv, ELM_HOVER_AXIS_VERTICAL), wd->bx);
432 evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_RESIZE, _parent_resize, obj);
434 evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _show, obj);
436 evas_object_event_callback_add(wd->bx, EVAS_CALLBACK_RESIZE, _menu_resize, obj);
445 * @param obj The menu object.
446 * @param parent The new parent.
451 elm_menu_parent_set(Evas_Object *obj, Evas_Object *parent)
453 Eina_List *l, *_l, *ll = NULL;
455 ELM_CHECK_WIDTYPE(obj, widtype);
456 Widget_Data *wd = elm_widget_data_get(obj);
458 elm_hover_parent_set(wd->hv, parent);
461 ll = eina_list_append(ll, wd->items);
462 EINA_LIST_FOREACH(ll, ll, l)
464 EINA_LIST_FOREACH(l, _l, item)
468 elm_hover_parent_set(item->hv, parent);
469 ll = eina_list_append(ll, item->items);
477 * Move the menu to a new position
479 * @param obj The menu object.
480 * @param x The new position.
481 * @param y The new position.
486 elm_menu_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
488 ELM_CHECK_WIDTYPE(obj, widtype);
489 Widget_Data *wd = elm_widget_data_get(obj);
497 * Get the Evas_Object of an Elm_Menu_Item
499 * @param it The menu item object.
504 elm_menu_object_get(const Elm_Menu_Item *it)
510 * Add an item at the end
512 * @param obj The menu object.
513 * @param icon A icon display on the item. The icon will be destryed by the menu.
514 * @param label The label of the item.
515 * @param func Function called when the user select the item.
516 * @param data Data sent by the callback.
517 * @return Returns the new item.
522 elm_menu_item_add(Evas_Object *obj, Elm_Menu_Item *parent, Evas_Object *icon, const char *label, Evas_Smart_Cb func, const void *data)
524 Elm_Menu_Item *subitem;
525 ELM_CHECK_WIDTYPE(obj, widtype) NULL;
526 Widget_Data *wd = elm_widget_data_get(obj);
528 if (!wd) return NULL;
529 subitem = ELM_NEW(Elm_Menu_Item);
530 if (!subitem) return NULL;
531 subitem->func = func;
532 subitem->data = data;
534 subitem->parent = parent;
536 _item_obj_create(subitem);
537 elm_menu_item_label_set(subitem, label);
538 elm_menu_item_icon_set(subitem, icon);
542 if (!parent->bx) _item_submenu_obj_create(parent);
543 elm_box_pack_end(parent->bx, subitem->o);
544 parent->items = eina_list_append(parent->items, subitem);
548 elm_box_pack_end(wd->bx, subitem->o);
549 wd->items = eina_list_append(wd->items, subitem);
557 * Set the label of a menu item
559 * @param it The menu item object.
560 * @param label The label to set for @p item
565 elm_menu_item_label_set(Elm_Menu_Item *item, const char *label)
567 eina_stringshare_replace(&item->label, label);
570 edje_object_signal_emit(item->o, "elm,state,text,visible", "elm");
572 edje_object_signal_emit(item->o, "elm,state,text,hidden", "elm");
574 edje_object_message_signal_process(item->o);
575 edje_object_part_text_set(item->o, "elm.text", label);
576 _sizing_eval(item->menu);
580 * Get the label of a menu item
582 * @param it The menu item object.
583 * @return The label of @p item
588 elm_menu_item_label_get(Elm_Menu_Item *item)
590 if (!item) return NULL;
595 * Set the icon of a menu item
597 * Once the icon object is set, a previously set one will be deleted.
599 * @param it The menu item object.
600 * @param icon The icon object to set for @p item
605 elm_menu_item_icon_set(Elm_Menu_Item *item, Evas_Object *icon)
607 if(item->icon == icon) return;
608 if (item->icon) evas_object_del(item->icon);
612 elm_widget_sub_object_add(item->menu, icon);
613 evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
614 _changed_size_hints, item->menu);
615 edje_object_part_swallow(item->o, "elm.swallow.content", icon);
616 edje_object_signal_emit(item->o, "elm,state,icon,visible", "elm");
617 edje_object_message_signal_process(item->o);
619 _sizing_eval(item->menu);
623 * Set the disabled state of @p item.
625 * @param it The menu item object.
626 * @param disabled The enabled/disabled state of the item
631 elm_menu_item_disabled_set(Elm_Menu_Item *item, Eina_Bool disabled)
633 if (disabled == item->disabled) return;
634 item->disabled = disabled;
637 edje_object_signal_emit(item->o, "elm,state,disabled", "elm");
638 if (item->open) _submenu_hide(item);
641 edje_object_signal_emit(item->o, "elm,state,enabled", "elm");
642 edje_object_message_signal_process(item->o);
646 * Get the disabled state of @p item.
648 * @param it The menu item object.
649 * @return The enabled/disabled state of the item
654 elm_menu_item_disabled_get(Elm_Menu_Item *item)
656 if (!item) return EINA_FALSE;
657 return item->disabled;
661 * Add a separator item to menu @p obj under @p parent.
663 * @param obj The menu object
664 * @param parent The item to add the separator under
666 * @return The created item or NULL on failure
671 elm_menu_item_separator_add(Evas_Object *obj, Elm_Menu_Item *parent)
673 Elm_Menu_Item *subitem;
674 ELM_CHECK_WIDTYPE(obj, widtype) NULL;
675 Widget_Data *wd = elm_widget_data_get(obj);
676 if (!wd) return NULL;
677 subitem = ELM_NEW(Elm_Menu_Item);
678 if (!subitem) return NULL;
680 subitem->separator = 1;
681 _item_separator_obj_create(subitem);
684 elm_box_pack_end(wd->bx, subitem->o);
685 wd->items = eina_list_append(wd->items, subitem);
689 if (!parent->bx) _item_submenu_obj_create(parent);
690 elm_box_pack_end(parent->bx, subitem->o);
691 parent->items = eina_list_append(parent->items, subitem);
699 * Deletes an item from the menu.
701 * @param item The item to delete
707 * Get the icon of a menu item
709 * @param it The menu item object.
710 * @return The icon object of @p item or NULL
715 elm_menu_item_icon_get(Elm_Menu_Item *item)
717 if (!item) return NULL;
722 * Returns whether @p item is a separator.
724 * @param item The item to check
725 * @return If true, @p item is a separator
730 elm_menu_item_is_separator(Elm_Menu_Item *item)
732 if (!item) return EINA_FALSE;
733 return item->separator;
737 elm_menu_item_del(Elm_Menu_Item *item)
739 Elm_Menu_Item *_item;
742 if (item->del_cb) item->del_cb((void*)item->data, item->o, item);
744 EINA_LIST_FREE(item->items, _item) elm_menu_item_del(_item);
745 if (item->label) eina_stringshare_del(item->label);
746 if (item->icon) evas_object_del(item->icon);
747 if (item->hv) evas_object_del(item->hv);
748 if (item->location) evas_object_del(item->location);
749 if (item->o) evas_object_del(item->o);
752 item->parent->items = eina_list_remove(item->parent->items, item);
755 Widget_Data *wd = elm_widget_data_get(item->menu);
756 wd->items = eina_list_remove(wd->items, item);
763 * Set the function called when a menu item is freed.
765 * @param it The item to set the callback on
766 * @param func The function called
771 elm_menu_item_del_cb_set(Elm_Menu_Item *it, Evas_Smart_Cb func)
778 * Returns the data associated with menu item @p it.
781 * @return The data associated with @p it
786 elm_menu_item_data_get(const Elm_Menu_Item *it)
788 if (!it) return NULL;
789 return (void *)it->data;
793 * Sets the data to be associated with menu item @p it.
795 * @param item The item
796 * @param data The data to be associated with @p item
801 elm_menu_item_data_set(Elm_Menu_Item *item, const void *data)
808 * Returns a list of @p item's subitems.
811 * @return An Eina_List* of @p item's subitems
815 EAPI const Eina_List *
816 elm_menu_item_subitems_get(Elm_Menu_Item *item)
818 if (!item) return NULL;