1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 
5 #define ELM_WIDGET_PROTECTED
6 #define EFL_ACCESS_OBJECT_PROTECTED
7 #define EFL_ACCESS_SELECTION_PROTECTED
8 #define ELM_WIDGET_ITEM_PROTECTED
9 #define EFL_UI_L10N_PROTECTED
10 #define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED
11 
12 #include <Elementary.h>
13 
14 #include "elm_priv.h"
15 #include "elm_widget_menu.h"
16 
17 #define MY_CLASS ELM_MENU_CLASS
18 
19 #define MY_CLASS_NAME "Elm_Menu"
20 #define MY_CLASS_NAME_LEGACY "elm_menu"
21 
22 #define ELM_PRIV_MENU_SIGNALS(cmd) \
23    cmd(SIG_CLICKED, "clicked", "") \
24    cmd(SIG_DISMISSED, "dismissed", "")
25 
26 ELM_PRIV_MENU_SIGNALS(ELM_PRIV_STATIC_VARIABLE_DECLARE);
27 
28 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
29    ELM_PRIV_MENU_SIGNALS(ELM_PRIV_SMART_CALLBACKS_DESC)
30    {SIG_WIDGET_LANG_CHANGED, ""}, /**< handled by elm_widget */
31    {SIG_WIDGET_ACCESS_CHANGED, ""}, /**< handled by elm_widget */
32    {NULL, NULL}
33 };
34 #undef ELM_PRIV_MENU_SIGNALS
35 
36 EOLIAN static void
_elm_menu_efl_ui_l10n_translation_update(Eo * obj EINA_UNUSED,Elm_Menu_Data * sd)37 _elm_menu_efl_ui_l10n_translation_update(Eo *obj EINA_UNUSED, Elm_Menu_Data *sd)
38 {
39    Elm_Menu_Item_Data *it;
40    Eina_List *l;
41 
42    EINA_LIST_FOREACH(sd->items, l, it)
43      elm_wdg_item_translate(EO_OBJ(it));
44 }
45 
46 static void _item_del(Elm_Object_Item *eo_item);
47 
48 static void
_elm_menu_subitems_clear(Elm_Menu_Item_Data * it)49 _elm_menu_subitems_clear(Elm_Menu_Item_Data *it)
50 {
51    Elm_Object_Item *sub_it;
52 
53    EINA_LIST_FREE(it->submenu.items, sub_it)
54      {
55         ELM_MENU_ITEM_DATA_GET(sub_it, item);
56 
57         if (item)
58           {
59              item->parent = NULL;
60              _item_del(sub_it);
61           }
62      }
63 }
64 
65 static void
_item_del(Elm_Object_Item * eo_item)66 _item_del(Elm_Object_Item *eo_item)
67 {
68    ELM_MENU_ITEM_DATA_GET(eo_item, item);
69 
70    _elm_menu_subitems_clear(item);
71    efl_del(eo_item);
72 }
73 
74 static void
_submenu_hide(Elm_Menu_Item_Data * item)75 _submenu_hide(Elm_Menu_Item_Data *item)
76 {
77    Eina_List *l;
78    Elm_Object_Item *eo_item2;
79 
80    evas_object_hide(item->submenu.hv);
81    item->submenu.open = EINA_FALSE;
82 
83    EINA_LIST_FOREACH(item->submenu.items, l, eo_item2)
84      {
85         ELM_MENU_ITEM_DATA_GET(eo_item2, item2);
86         if (item2->submenu.open) _submenu_hide(item2);
87      }
88 }
89 
90 static void
_elm_menu_item_elm_widget_item_disable(Eo * eo_item,Elm_Menu_Item_Data * item)91 _elm_menu_item_elm_widget_item_disable(Eo *eo_item, Elm_Menu_Item_Data *item)
92 {
93    if (elm_object_item_disabled_get((Elm_Object_Item*)eo_item))
94      {
95         elm_layout_signal_emit(VIEW(item), "elm,state,disabled", "elm");
96         if (item->submenu.open) _submenu_hide(item);
97      }
98    else
99      elm_layout_signal_emit(VIEW(item), "elm,state,enabled", "elm");
100 
101    if (item->dbus_menu) _elm_dbus_menu_update(item->dbus_menu);
102    edje_object_message_signal_process(elm_layout_edje_get(VIEW(item)));
103 }
104 
105 static void
_elm_menu_item_elm_widget_item_signal_emit(Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * item,const char * emission,const char * source)106 _elm_menu_item_elm_widget_item_signal_emit(Eo *eo_item EINA_UNUSED, Elm_Menu_Item_Data *item,
107                        const char *emission,
108                        const char *source)
109 {
110    elm_layout_signal_emit(VIEW(item), emission, source);
111 }
112 
113 static inline void
_parent_geometry_get(Elm_Menu_Data * sd,int * x,int * y,int * w,int * h)114 _parent_geometry_get(Elm_Menu_Data *sd, int *x, int *y, int *w, int *h)
115 {
116    Eina_Rect r;
117 
118    r = efl_gfx_entity_geometry_get(sd->parent);
119    if (efl_isa(sd->parent, EFL_UI_WIN_CLASS))
120      {
121         if (sd->menu_bar && efl_canvas_object_is_frame_object_get(sd->obj))
122           r.pos = efl_gfx_entity_position_get(sd->obj);
123         else
124           r.pos = EINA_POSITION2D(0, 0);
125      }
126    if (x) *x = r.x;
127    if (y) *y = r.y;
128    if (w) *w = r.w;
129    if (h) *h = r.h;
130 }
131 
132 static void
_submenu_sizing_eval(Elm_Menu_Item_Data * parent_it)133 _submenu_sizing_eval(Elm_Menu_Item_Data *parent_it)
134 {
135    Eina_List *l;
136    Elm_Object_Item *eo_item;
137    Evas_Coord x_p, y_p, w_p, h_p, x2, y2, w2,
138               h2, bx, by, bw, bh, px, py, pw, ph;
139    ELM_MENU_DATA_GET_OR_RETURN(WIDGET(parent_it), sd);
140 
141    EINA_LIST_FOREACH(parent_it->submenu.items, l, eo_item)
142      {
143         ELM_MENU_ITEM_DATA_GET(eo_item, item);
144         elm_layout_sizing_eval(VIEW(item));
145         if (_elm_config->atspi_mode)
146           efl_access_state_changed_signal_emit(eo_item, EFL_ACCESS_STATE_TYPE_SHOWING, EINA_TRUE);
147      }
148 
149 
150    evas_object_geometry_get
151      (parent_it->submenu.location, &x_p, &y_p, &w_p, &h_p);
152    evas_object_geometry_get(VIEW(parent_it), &x2, &y2, &w2, &h2);
153    evas_object_geometry_get(parent_it->submenu.bx, &bx, &by, &bw, &bh);
154    _parent_geometry_get(sd, &px, &py, &pw, &ph);
155 
156    if (sd->menu_bar && !parent_it->parent)
157      {
158         x_p = x2;
159         y_p = y2 + h2;
160      }
161    else
162      {
163         x_p = x2 + w2;
164         y_p = y2;
165      }
166 
167    /* If it overflows on the right, adjust the x */
168    if ((x_p + bw > px + pw) || efl_ui_mirrored_get(WIDGET(parent_it)))
169      x_p = x2 - bw;
170 
171    /* If it overflows on the left, adjust the x - usually only happens
172     * with an RTL interface */
173    if (x_p < px)
174      x_p = x2 + w2;
175 
176    /* If after all the adjustments it still overflows, fix it */
177    if (x_p + bw > px + pw)
178      x_p = x2 - bw;
179 
180    if (y_p + bh > py + ph)
181      y_p -= y_p + bh - (py + ph);
182 
183    if (sd->menu_bar && (y_p < py))
184      y_p = py;
185 
186    evas_object_geometry_set(parent_it->submenu.location, x_p, y_p, bw, h_p);
187    evas_object_size_hint_min_set(parent_it->submenu.location, bw, h_p);
188    evas_object_size_hint_max_set(parent_it->submenu.location, bw, h_p);
189    elm_hover_target_set(parent_it->submenu.hv, parent_it->submenu.location);
190 
191    EINA_LIST_FOREACH(parent_it->submenu.items, l, eo_item)
192      {
193         ELM_MENU_ITEM_DATA_GET(eo_item, item);
194         if (item->submenu.open) _submenu_sizing_eval(item);
195      }
196 }
197 
198 static void
_sizing_eval(Evas_Object * obj)199 _sizing_eval(Evas_Object *obj)
200 {
201    Eina_List *l;
202    Elm_Object_Item *eo_item;
203    Evas_Coord x_p, y_p, w_p, h_p, x2, y2, w2, h2, bw, bh;
204    Elm_Widget_Smart_Data *hover;
205 
206    ELM_MENU_DATA_GET(obj, sd);
207 
208    if (!sd->parent) return;
209 
210    EINA_LIST_FOREACH(sd->items, l, eo_item)
211      {
212         ELM_MENU_ITEM_DATA_GET(eo_item, item);
213         elm_layout_sizing_eval(VIEW(item));
214      }
215 
216    evas_object_geometry_get(sd->location, NULL, NULL, &w_p, &h_p);
217    _parent_geometry_get(sd, &x2, &y2, &w2, &h2);
218    evas_object_geometry_get(sd->bx, NULL, NULL, &bw, &bh);
219 
220    x_p = sd->xloc;
221    y_p = sd->yloc;
222 
223    if (efl_ui_mirrored_get(obj)) x_p -= w_p;
224 
225    if (x_p + bw > x2 + w2) x_p -= x_p + bw - (x2 + w2);
226    if (x_p < x2) x_p = x2;
227 
228    if (y_p + h_p + bh > y2 + h2) y_p -= y_p + h_p + bh - (y2 + h2);
229    if (y_p < y2) y_p = y2;
230 
231    efl_gfx_entity_geometry_set(sd->location, EINA_RECT(x_p, y_p, bw, h_p));
232    evas_object_size_hint_min_set(sd->location, bw, h_p);
233    evas_object_size_hint_max_set(sd->location, bw, h_p);
234    elm_hover_target_set(sd->hv, sd->location);
235 
236    hover = efl_data_scope_get(sd->hv, EFL_UI_WIDGET_CLASS);
237    edje_object_part_geometry_get(hover->resize_obj, "bottom", NULL,
238                                  NULL, &bw, &bh);
239    evas_object_size_hint_min_set(obj, bw, bh);
240 
241    EINA_LIST_FOREACH(sd->items, l, eo_item)
242      {
243         ELM_MENU_ITEM_DATA_GET(eo_item, item);
244         if (item->submenu.open) _submenu_sizing_eval(item);
245      }
246 }
247 
248 EOLIAN static Eina_Error
_elm_menu_efl_ui_widget_theme_apply(Eo * obj,Elm_Menu_Data * sd)249 _elm_menu_efl_ui_widget_theme_apply(Eo *obj, Elm_Menu_Data *sd)
250 {
251    Eina_Error int_ret = EFL_UI_THEME_APPLY_ERROR_GENERIC;
252 
253    Eina_List *l, *_l, *_ll, *ll = NULL;
254    Elm_Object_Item *eo_item;
255    const char *s;
256    char style[1024];
257 
258    int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
259    if (int_ret == EFL_UI_THEME_APPLY_ERROR_GENERIC) return int_ret;
260 
261    if (sd->menu_bar)
262       snprintf(style, sizeof(style), "main_menu/%s", elm_widget_style_get(obj));
263    else
264       snprintf(style, sizeof(style), "menu/%s", elm_widget_style_get(obj));
265    elm_object_style_set(sd->hv, style);
266 
267    ll = eina_list_append(ll, sd->items);
268    EINA_LIST_FOREACH(ll, _ll, l)
269      {
270         EINA_LIST_FOREACH(l, _l, eo_item)
271           {
272              ELM_MENU_ITEM_DATA_GET(eo_item, item);
273              ll = eina_list_append(ll, item->submenu.items);
274              if (item->separator)
275                {
276                   if (!elm_layout_theme_set(VIEW(item), "menu", "separator",
277                                             elm_widget_style_get(obj)))
278                     CRI("Failed to set layout!");
279                }
280              else if (item->submenu.bx)
281                {
282                   if (sd->menu_bar && !item->parent) s = "main_menu_submenu";
283                   else s = "item_with_submenu";
284 
285                   if (!elm_layout_theme_set(VIEW(item), "menu", s,
286                                             elm_widget_style_get(obj)))
287                     CRI("Failed to set layout!");
288                   snprintf(style, sizeof(style), "menu/%s", elm_widget_style_get(WIDGET(item)));
289                   elm_object_style_set(item->submenu.hv, style);
290 
291                   elm_object_item_text_set(eo_item,
292                                            item->label);
293                   if (item->icon_str)
294                     elm_menu_item_icon_name_set(eo_item,
295                                                 item->icon_str);
296                }
297              else
298                {
299                   if (!elm_layout_theme_set(VIEW(item), "menu", "item",
300                                             elm_widget_style_get(obj)))
301                     CRI("Failed to set layout!");
302 
303                   elm_object_item_text_set(eo_item,
304                                            item->label);
305                   if (item->icon_str)
306                     elm_menu_item_icon_name_set(eo_item,
307                                                 item->icon_str);
308                }
309              elm_wdg_item_disable(eo_item);
310              /* SEOZ
311              edje_object_scale_set
312                (VIEW(item), efl_gfx_entity_scale_get(obj) *
313                elm_config_scale_get());
314                */
315           }
316      }
317 
318    eina_list_free(ll); //fixme: test
319 
320    _sizing_eval(obj);
321 
322    return int_ret;
323 }
324 
325 EOLIAN static void
_elm_menu_item_elm_widget_item_part_text_set(Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * item,const char * part,const char * label)326 _elm_menu_item_elm_widget_item_part_text_set(Eo *eo_item EINA_UNUSED,
327                                              Elm_Menu_Item_Data *item,
328                                              const char *part,
329                                              const char *label)
330 {
331    if (part && strcmp(part, "default")) return;
332 
333    eina_stringshare_replace(&item->label, label);
334 
335    if (label)
336      elm_layout_signal_emit(VIEW(item), "elm,state,text,visible", "elm");
337    else
338      elm_layout_signal_emit(VIEW(item), "elm,state,text,hidden", "elm");
339 
340    edje_object_message_signal_process(elm_layout_edje_get(VIEW(item)));
341    elm_layout_text_set(VIEW(item), "elm.text", label);
342 
343    _sizing_eval(WIDGET(item));
344 }
345 
346 EOLIAN static const char *
_elm_menu_item_elm_widget_item_part_text_get(const Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * it,const char * part)347 _elm_menu_item_elm_widget_item_part_text_get(const Eo *eo_item EINA_UNUSED,
348                                              Elm_Menu_Item_Data *it,
349                                              const char *part)
350 {
351    if (part && strcmp(part, "default")) return NULL;
352 
353    return it->label;
354 }
355 
356 EOLIAN static void
_elm_menu_item_elm_widget_item_part_content_set(Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * item,const char * part,Evas_Object * content)357 _elm_menu_item_elm_widget_item_part_content_set(Eo *eo_item EINA_UNUSED,
358                                                 Elm_Menu_Item_Data *item,
359                                                 const char *part,
360                                                 Evas_Object *content)
361 {
362    if (part && strcmp(part, "default")) return;
363 
364    if (content == item->content) return;
365 
366    evas_object_del(item->content);
367    item->content = content;
368    if (item->content)
369      elm_layout_content_set(VIEW(item), "elm.swallow.content", item->content);
370 
371    _sizing_eval(WIDGET(item));
372 }
373 
374 EOLIAN static Evas_Object *
_elm_menu_item_elm_widget_item_part_content_get(const Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * it,const char * part)375 _elm_menu_item_elm_widget_item_part_content_get(const Eo *eo_item EINA_UNUSED,
376                                                 Elm_Menu_Item_Data *it,
377                                                 const char *part)
378 {
379    if (part && strcmp(part, "default")) return NULL;
380 
381    return it->content;
382 }
383 
384 static void
_menu_resize_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)385 _menu_resize_cb(void *data,
386                 Evas *e EINA_UNUSED,
387                 Evas_Object *obj EINA_UNUSED,
388                 void *event_info EINA_UNUSED)
389 {
390    _sizing_eval(data);
391 }
392 
393 static void
_parent_resize_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)394 _parent_resize_cb(void *data,
395                   Evas *e EINA_UNUSED,
396                   Evas_Object *obj EINA_UNUSED,
397                   void *event_info EINA_UNUSED)
398 {
399    _sizing_eval(data);
400 }
401 
402 static void
_parent_del_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj,void * event_info EINA_UNUSED)403 _parent_del_cb(void *data,
404                Evas *e EINA_UNUSED,
405                Evas_Object *obj,
406                void *event_info EINA_UNUSED)
407 {
408    ELM_WIDGET_DATA_GET_OR_RETURN(data, wd);
409 
410    evas_object_event_callback_del_full
411      (obj, EVAS_CALLBACK_RESIZE, _parent_resize_cb, data);
412 }
413 
414 static void
_item_move_resize_cb(void * data,Evas * e EINA_UNUSED,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)415 _item_move_resize_cb(void *data,
416                      Evas *e EINA_UNUSED,
417                      Evas_Object *obj EINA_UNUSED,
418                      void *event_info EINA_UNUSED)
419 {
420    Elm_Menu_Item_Data *item = data;
421 
422    if (item->submenu.open) _submenu_sizing_eval(item);
423 }
424 
425 static void
_menu_hide(void * data,Evas_Object * obj EINA_UNUSED,void * event_info EINA_UNUSED)426 _menu_hide(void *data,
427            Evas_Object *obj EINA_UNUSED,
428            void *event_info EINA_UNUSED)
429 {
430    Eina_List *l;
431    Elm_Object_Item *eo_item2;
432 
433    ELM_MENU_DATA_GET(data, sd);
434 
435    if (!sd->menu_bar)
436      {
437         evas_object_hide(sd->hv);
438         evas_object_hide(data);
439      }
440 
441    EINA_LIST_FOREACH(sd->items, l, eo_item2)
442      {
443         ELM_MENU_ITEM_DATA_GET(eo_item2, item2);
444         if (item2->submenu.open) _submenu_hide(item2);
445      }
446 }
447 
448 static void
_hover_dismissed_cb(void * data,const Efl_Event * event)449 _hover_dismissed_cb(void *data, const Efl_Event *event)
450 {
451    _menu_hide(data, event->object, event->info);
452    evas_object_smart_callback_call
453      ( data, "clicked", NULL);
454    efl_event_callback_legacy_call(data, ELM_MENU_EVENT_DISMISSED, NULL);
455 }
456 
457 static void
_submenu_open_cb(void * data,Evas_Object * obj EINA_UNUSED,const char * emission EINA_UNUSED,const char * source EINA_UNUSED)458 _submenu_open_cb(void *data,
459                  Evas_Object *obj EINA_UNUSED,
460                  const char *emission EINA_UNUSED,
461                  const char *source EINA_UNUSED)
462 {
463    Elm_Menu_Item_Data *item = data;
464 
465    item->submenu.open = EINA_TRUE;
466    evas_object_show(item->submenu.hv);
467    _submenu_sizing_eval(item);
468 }
469 
470 void
_elm_dbus_menu_item_select_cb(Elm_Object_Item * obj_item)471 _elm_dbus_menu_item_select_cb(Elm_Object_Item *obj_item)
472 {
473   ELM_MENU_ITEM_DATA_GET(obj_item, item);
474   if (item->func) item->func((void *)(WIDGET_ITEM_DATA_GET(EO_OBJ(item))), WIDGET(item), obj_item);
475 }
476 
477 static void
_menu_item_select_cb(void * data,Evas_Object * obj EINA_UNUSED,const char * emission EINA_UNUSED,const char * source EINA_UNUSED)478 _menu_item_select_cb(void *data,
479                      Evas_Object *obj EINA_UNUSED,
480                      const char *emission EINA_UNUSED,
481                      const char *source EINA_UNUSED)
482 {
483    Elm_Menu_Item_Data *item = data;
484 
485    if (item->submenu.items)
486      {
487         if (!item->submenu.open) _submenu_open_cb(item, NULL, NULL, NULL);
488         else _submenu_hide(item);
489      }
490    else _menu_hide(WIDGET(item), NULL, NULL);
491 
492    if (item->func) item->func((void *)(WIDGET_ITEM_DATA_GET(EO_OBJ(item))), WIDGET(item), EO_OBJ(item));
493 }
494 
495 static void
_menu_item_activate_cb(void * data,Evas_Object * obj EINA_UNUSED,const char * emission EINA_UNUSED,const char * source EINA_UNUSED)496 _menu_item_activate_cb(void *data,
497                        Evas_Object *obj EINA_UNUSED,
498                        const char *emission EINA_UNUSED,
499                        const char *source EINA_UNUSED)
500 {
501    Eina_List *l;
502    Elm_Object_Item *eo_item2;
503    Elm_Menu_Item_Data *item = data;
504 
505    item->selected = 1;
506    if (item->parent)
507      {
508         EINA_LIST_FOREACH(item->parent->submenu.items, l, eo_item2)
509           {
510              if (eo_item2 != EO_OBJ(item))
511                elm_menu_item_selected_set(eo_item2, 0);
512           }
513         efl_access_object_event_emit(EO_OBJ(item->parent), EFL_ACCESS_SELECTION_EVENT_ACCESS_SELECTION_CHANGED, NULL);
514      }
515    else
516      {
517         Eina_Bool was_open = EINA_FALSE;
518         ELM_MENU_DATA_GET(WIDGET(item), sd);
519         EINA_LIST_FOREACH(sd->items, l, eo_item2)
520           {
521              if (eo_item2 != EO_OBJ(item))
522                {
523                   ELM_MENU_ITEM_DATA_GET(eo_item2, item2);
524                   was_open |= item2->submenu.open;
525                   elm_menu_item_selected_set(eo_item2, 0);
526                }
527           }
528         efl_access_object_event_emit(WIDGET(item), EFL_ACCESS_SELECTION_EVENT_ACCESS_SELECTION_CHANGED, NULL);
529         if (sd->menu_bar && was_open)
530           _menu_item_select_cb(item, NULL, NULL, NULL);
531      }
532    if (_elm_config->atspi_mode)
533      efl_access_state_changed_signal_emit(EO_OBJ(item), EFL_ACCESS_STATE_TYPE_SELECTED, EINA_TRUE);
534 }
535 
536 static void
_menu_item_inactivate_cb(void * data,Evas_Object * obj EINA_UNUSED,const char * emission EINA_UNUSED,const char * source EINA_UNUSED)537 _menu_item_inactivate_cb(void *data,
538                          Evas_Object *obj EINA_UNUSED,
539                          const char *emission EINA_UNUSED,
540                          const char *source EINA_UNUSED)
541 {
542    Elm_Menu_Item_Data *item = data;
543 
544    item->selected = 0;
545    if (item->submenu.open) _submenu_hide(item);
546    if (_elm_config->atspi_mode)
547      efl_access_state_changed_signal_emit(EO_OBJ(item), EFL_ACCESS_STATE_TYPE_SELECTED, EINA_FALSE);
548 }
549 
550 static void
_block_menu(void * _sd,const Efl_Event * event EINA_UNUSED)551 _block_menu(void *_sd, const Efl_Event *event EINA_UNUSED)
552 {
553    const Eina_List *l;
554    Elm_Object_Item *eo_current;
555    Elm_Menu_Data *sd = _sd;
556    Eina_List *items = sd->items;
557    EINA_LIST_FOREACH(items, l, eo_current)
558      {
559         ELM_MENU_ITEM_DATA_GET(eo_current, current);
560         if (!current->blocked) current->was_enabled = !elm_wdg_item_disabled_get(eo_current);
561         current->blocked = EINA_TRUE;
562         elm_object_item_disabled_set(eo_current, EINA_TRUE);
563      }
564 }
565 
566 static void
_unblock_menu(void * _sd,const Efl_Event * event EINA_UNUSED)567 _unblock_menu(void *_sd, const Efl_Event *event EINA_UNUSED)
568 {
569    const Eina_List *l;
570    Elm_Object_Item *eo_current;
571    Elm_Menu_Data *sd = _sd;
572    Eina_List *items = sd->items;
573    EINA_LIST_FOREACH(items, l, eo_current)
574      {
575         ELM_MENU_ITEM_DATA_GET(eo_current, current);
576         elm_object_item_disabled_set(eo_current, !current->was_enabled);
577         current->blocked = EINA_FALSE;
578      }
579 }
580 
581 EOLIAN static void
_elm_menu_efl_gfx_entity_visible_set(Eo * obj EINA_UNUSED,Elm_Menu_Data * sd,Eina_Bool vis)582 _elm_menu_efl_gfx_entity_visible_set(Eo *obj EINA_UNUSED, Elm_Menu_Data *sd, Eina_Bool vis)
583 {
584    if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis))
585      return;
586 
587    efl_gfx_entity_visible_set(efl_super(obj, MY_CLASS), vis);
588    if (vis) efl_gfx_entity_visible_set(sd->hv, EINA_TRUE);
589 }
590 
591 static void
_item_obj_create(Elm_Menu_Item_Data * item)592 _item_obj_create(Elm_Menu_Item_Data *item)
593 {
594    VIEW_SET(item, elm_layout_add(WIDGET(item)));
595    efl_access_object_access_type_set(VIEW(item), EFL_ACCESS_TYPE_SKIPPED);
596    evas_object_size_hint_weight_set
597      (VIEW(item), EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
598    evas_object_size_hint_fill_set(VIEW(item), EVAS_HINT_FILL, EVAS_HINT_FILL);
599    if (!elm_layout_theme_set(VIEW(item), "menu", "item",
600                         elm_widget_style_get(WIDGET(item))))
601      CRI("Failed to set layout!");
602    else
603      {
604         elm_layout_signal_callback_add(VIEW(item), "elm,action,click", "*",
605                                        _menu_item_select_cb, item);
606         elm_layout_signal_callback_add(VIEW(item), "elm,action,activate", "*",
607                                        _menu_item_activate_cb, item);
608         elm_layout_signal_callback_add(VIEW(item), "elm,action,inactivate", "*",
609                                        _menu_item_inactivate_cb,
610                                        item);
611         evas_object_show(VIEW(item));
612      }
613 }
614 
615 static void
_item_separator_obj_create(Elm_Menu_Item_Data * item)616 _item_separator_obj_create(Elm_Menu_Item_Data *item)
617 {
618    VIEW_SET(item, elm_layout_add(WIDGET(item)));
619    efl_access_object_access_type_set(VIEW(item), EFL_ACCESS_TYPE_SKIPPED);
620    evas_object_size_hint_weight_set
621      (VIEW(item), EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
622    evas_object_size_hint_fill_set(VIEW(item), EVAS_HINT_FILL, EVAS_HINT_FILL);
623    if (!elm_layout_theme_set(VIEW(item), "menu", "separator",
624                              elm_widget_style_get(WIDGET(item))))
625      CRI("Failed to set layout!");
626    else
627      {
628         elm_layout_signal_callback_add
629            (VIEW(item), "elm,action,activate", "*", _menu_item_activate_cb, item);
630         evas_object_show(VIEW(item));
631      }
632 }
633 
634 static void
_item_submenu_obj_create(Elm_Menu_Item_Data * item)635 _item_submenu_obj_create(Elm_Menu_Item_Data *item)
636 {
637    ELM_MENU_DATA_GET(WIDGET(item), sd);
638    Evas_Object *hv, *bx;
639    char style[1024];
640 
641    item->submenu.location = elm_icon_add(sd->bx);
642    efl_access_object_access_type_set(item->submenu.location, EFL_ACCESS_TYPE_DISABLED);
643    item->submenu.hv = hv = elm_hover_add(sd->bx);
644    efl_access_object_access_type_set(item->submenu.hv, EFL_ACCESS_TYPE_SKIPPED);
645    efl_ui_mirrored_set(hv, EINA_FALSE);
646    elm_hover_target_set(hv, item->submenu.location);
647    elm_hover_parent_set(hv, sd->parent);
648 
649    if (sd->menu_bar && !item->parent)
650      {
651         snprintf(style, sizeof(style), "main_menu_submenu/%s", elm_widget_style_get(WIDGET(item)));
652         elm_object_style_set(hv, style);
653         efl_event_callback_add
654           (hv, ELM_HOVER_EVENT_DISMISSED, _hover_dismissed_cb, WIDGET(item));
655      }
656    else
657      {
658         snprintf(style, sizeof(style), "submenu/%s", elm_widget_style_get(WIDGET(item)));
659         elm_object_style_set(hv, style);
660      }
661 
662    item->submenu.bx = bx = elm_box_add(sd->bx);
663    efl_ui_mirrored_set(bx, EINA_FALSE);
664    evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
665    evas_object_show(bx);
666    elm_object_part_content_set
667       (hv, elm_hover_best_content_location_get
668        (hv, ELM_HOVER_AXIS_VERTICAL), bx);
669 
670    if (sd->menu_bar && !item->parent)
671      {
672         if (!elm_layout_theme_set(VIEW(item), "menu",
673                                   "main_menu_submenu",
674                                   elm_widget_style_get(WIDGET(item))))
675           CRI("Failed to set layout!");
676      }
677    else
678      {
679         if (!elm_layout_theme_set(VIEW(item), "menu",
680                                   "item_with_submenu",
681                                   elm_widget_style_get(WIDGET(item))))
682           CRI("Failed to set layout!");
683      }
684 
685    elm_object_item_text_set(EO_OBJ(item), item->label);
686 
687    if (item->icon_str)
688      elm_menu_item_icon_name_set(EO_OBJ(item), item->icon_str);
689 
690    elm_layout_signal_callback_add(VIEW(item), "elm,action,open", "*",
691                                    _submenu_open_cb, item);
692    evas_object_event_callback_add
693      (VIEW(item), EVAS_CALLBACK_MOVE, _item_move_resize_cb, item);
694    evas_object_event_callback_add
695      (VIEW(item), EVAS_CALLBACK_RESIZE, _item_move_resize_cb, item);
696 
697    evas_object_event_callback_add(bx, EVAS_CALLBACK_RESIZE,
698                                   _menu_resize_cb, WIDGET(item));
699 }
700 
701 EOLIAN static void
_elm_menu_efl_canvas_group_group_add(Eo * obj,Elm_Menu_Data * priv)702 _elm_menu_efl_canvas_group_group_add(Eo *obj, Elm_Menu_Data *priv)
703 {
704    efl_canvas_group_add(efl_super(obj, MY_CLASS));
705 
706    elm_widget_can_focus_set(obj, EINA_FALSE);
707 
708    priv->location = elm_icon_add(obj);
709    efl_access_object_access_type_set(priv->location, EFL_ACCESS_TYPE_DISABLED);
710 
711    priv->hv = elm_hover_add(obj);
712    efl_access_object_access_type_set(priv->hv, EFL_ACCESS_TYPE_SKIPPED);
713    efl_ui_mirrored_set(priv->hv, EINA_FALSE);
714 
715    elm_object_style_set(priv->hv, "menu/default");
716    efl_event_callback_add
717      (priv->hv, ELM_HOVER_EVENT_DISMISSED, _hover_dismissed_cb, obj);
718 
719    priv->bx = elm_box_add(obj);
720    efl_access_object_access_type_set(priv->bx, EFL_ACCESS_TYPE_SKIPPED);
721    efl_ui_mirrored_set(priv->bx, EINA_FALSE);
722    evas_object_size_hint_weight_set
723      (priv->bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
724 
725    evas_object_event_callback_add
726      (priv->bx, EVAS_CALLBACK_RESIZE, _menu_resize_cb, obj);
727 }
728 
729 EOLIAN static void
_elm_menu_efl_canvas_group_group_del(Eo * obj,Elm_Menu_Data * sd)730 _elm_menu_efl_canvas_group_group_del(Eo *obj, Elm_Menu_Data *sd)
731 {
732    Elm_Object_Item *eo_item;
733 
734    _elm_dbus_menu_unregister(obj);
735 
736    if (sd->parent)
737      {
738         evas_object_event_callback_del_full
739            (sd->parent, EVAS_CALLBACK_RESIZE, _parent_resize_cb, obj);
740         evas_object_event_callback_del_full
741            (sd->parent, EVAS_CALLBACK_DEL, _parent_del_cb, obj);
742      }
743 
744    EINA_LIST_FREE(sd->items, eo_item)
745      _item_del(eo_item);
746 
747    evas_object_event_callback_del_full
748       (sd->bx, EVAS_CALLBACK_RESIZE, _menu_resize_cb, obj);
749 
750    evas_object_del(sd->hv);
751    evas_object_del(sd->location);
752 
753    efl_canvas_group_del(efl_super(obj, MY_CLASS));
754 }
755 
756 void
_elm_menu_menu_bar_hide(Eo * obj)757 _elm_menu_menu_bar_hide(Eo *obj)
758 {
759   ELM_MENU_DATA_GET_OR_RETURN(obj, sd);
760 
761   evas_object_hide(sd->hv);
762   evas_object_hide(obj);
763   _menu_hide(obj, NULL, NULL);
764 }
765 
766 void
_elm_menu_menu_bar_set(Eo * obj,Eina_Bool menu_bar)767 _elm_menu_menu_bar_set(Eo *obj, Eina_Bool menu_bar)
768 {
769    Eina_List *l;
770    Elm_Object_Item *eo_item;
771    char style[1024];
772 
773    ELM_MENU_DATA_GET_OR_RETURN(obj, sd);
774 
775    if (menu_bar == sd->menu_bar) return;
776 
777    elm_box_horizontal_set(sd->bx, menu_bar);
778    elm_box_homogeneous_set(sd->bx, !menu_bar);
779    sd->menu_bar = menu_bar;
780 
781    if (sd->menu_bar)
782      snprintf(style, sizeof(style), "main_menu/%s", elm_widget_style_get(obj));
783    else
784      snprintf(style, sizeof(style), "menu/%s", elm_widget_style_get(obj));
785    elm_object_style_set(sd->hv, style);
786 
787    EINA_LIST_FOREACH(sd->items, l, eo_item)
788      {
789         ELM_MENU_ITEM_DATA_GET(eo_item, item);
790         if (!item->submenu.bx) continue;
791 
792         if (menu_bar)
793           {
794              efl_event_callback_add
795                (item->submenu.hv, EFL_INPUT_EVENT_CLICKED, _hover_dismissed_cb, WIDGET(item));
796              snprintf(style, sizeof(style), "main_menu_submenu//%s", elm_widget_style_get(obj));
797              elm_object_style_set(item->submenu.hv, style);
798           }
799         else
800           {
801              efl_event_callback_del(item->submenu.hv, EFL_INPUT_EVENT_CLICKED, _hover_dismissed_cb, WIDGET(item));
802              snprintf(style, sizeof(style), "submenu/%s", elm_widget_style_get(obj));
803              elm_object_style_set(item->submenu.hv, style);
804           }
805      }
806 
807    efl_ui_widget_theme_apply(obj);
808 }
809 
810 EAPI Evas_Object *
elm_menu_add(Evas_Object * parent)811 elm_menu_add(Evas_Object *parent)
812 {
813    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
814    return elm_legacy_add(MY_CLASS, parent);
815 }
816 
817 EOLIAN static Efl_Ui_Focus_Manager*
_elm_menu_efl_ui_widget_focus_manager_focus_manager_create(Eo * obj EINA_UNUSED,Elm_Menu_Data * pd EINA_UNUSED,Efl_Ui_Focus_Object * root)818 _elm_menu_efl_ui_widget_focus_manager_focus_manager_create(Eo *obj EINA_UNUSED, Elm_Menu_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root)
819 {
820    Efl_Ui_Focus_Manager *manager;
821 
822    manager = efl_add(EFL_UI_FOCUS_MANAGER_CALC_CLASS, obj,
823      efl_ui_focus_manager_root_set(efl_added, root)
824    );
825 
826    return manager;
827 }
828 
829 static void
_parent_setup(Eo * obj,Elm_Menu_Data * sd,Evas_Object * parent)830 _parent_setup(Eo *obj, Elm_Menu_Data *sd, Evas_Object *parent)
831 {
832    Eina_List *l, *_l, *_ll, *ll = NULL;
833    Elm_Object_Item *eo_item;
834 
835    if (sd->parent == parent) return;
836    if (sd->parent)
837      {
838         evas_object_event_callback_del_full
839           (sd->parent, EVAS_CALLBACK_RESIZE, _parent_resize_cb, obj);
840         evas_object_event_callback_del_full
841           (sd->parent, EVAS_CALLBACK_DEL, _parent_del_cb, obj);
842      }
843    sd->parent = parent;
844    if (sd->parent)
845      {
846         evas_object_event_callback_add
847           (sd->parent, EVAS_CALLBACK_RESIZE, _parent_resize_cb, obj);
848         evas_object_event_callback_add
849           (sd->parent, EVAS_CALLBACK_DEL, _parent_del_cb, obj);
850      }
851    elm_hover_parent_set(sd->hv, parent);
852 
853    ll = eina_list_append(ll, sd->items);
854    EINA_LIST_FOREACH(ll, _ll, l)
855      {
856         EINA_LIST_FOREACH(l, _l, eo_item)
857           {
858              ELM_MENU_ITEM_DATA_GET(eo_item, item);
859              if (item->submenu.hv)
860                {
861                   elm_hover_parent_set(item->submenu.hv, parent);
862                   ll = eina_list_append(ll, item->submenu.items);
863                }
864           }
865      }
866 
867    eina_list_free(ll);
868 
869    _sizing_eval(obj);
870 }
871 
872 EOLIAN static Eo *
_elm_menu_efl_object_constructor(Eo * obj,Elm_Menu_Data * sd)873 _elm_menu_efl_object_constructor(Eo *obj, Elm_Menu_Data *sd)
874 {
875    Eo *parent = NULL;
876 
877    obj = efl_constructor(efl_super(obj, MY_CLASS));
878    _parent_setup(obj, sd, efl_parent_get(obj));
879    efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY);
880    evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks);
881    parent = efl_parent_get(obj);
882    efl_access_object_role_set(obj, EFL_ACCESS_ROLE_MENU);
883 
884    elm_menu_parent_set(obj, parent);
885    elm_hover_target_set(sd->hv, sd->location);
886    elm_layout_content_set
887      (sd->hv, elm_hover_best_content_location_get
888        (sd->hv, ELM_HOVER_AXIS_VERTICAL), sd->bx);
889 
890    _sizing_eval(obj);
891    efl_event_callback_add
892      (obj, ELM_MENU_EVENT_ELM_ACTION_BLOCK_MENU, _block_menu, sd);
893    efl_event_callback_add
894      (obj, ELM_MENU_EVENT_ELM_ACTION_UNBLOCK_MENU, _unblock_menu, sd);
895 
896    sd->obj = obj;
897    return obj;
898 }
899 
900 EOLIAN static void
_elm_menu_efl_object_destructor(Eo * obj,Elm_Menu_Data * sd)901 _elm_menu_efl_object_destructor(Eo *obj, Elm_Menu_Data *sd)
902 {
903    Eina_List *itr, *itr2;
904    Elm_Object_Item *eo_item;
905    EINA_LIST_FOREACH_SAFE(sd->items, itr, itr2, eo_item)
906      efl_del(eo_item);
907 
908    efl_destructor(efl_super(obj, MY_CLASS));
909 }
910 
911 EAPI void
elm_menu_parent_set(Evas_Object * obj,Evas_Object * parent)912 elm_menu_parent_set(Evas_Object *obj,
913                     Evas_Object *parent)
914 {
915    ELM_MENU_CHECK(obj);
916    ELM_MENU_DATA_GET(obj, sd);
917    efl_ui_widget_sub_object_add(parent, obj);
918    _parent_setup(obj, sd, parent);
919 }
920 
921 EAPI Evas_Object *
elm_menu_parent_get(const Evas_Object * obj)922 elm_menu_parent_get(const Evas_Object *obj)
923 {
924    ELM_MENU_CHECK(obj) NULL;
925    return efl_ui_widget_parent_get(obj);
926 }
927 
928 EOLIAN static void
_elm_menu_relative_move(Eo * obj,Elm_Menu_Data * sd,Evas_Coord x,Evas_Coord y)929 _elm_menu_relative_move(Eo *obj, Elm_Menu_Data *sd, Evas_Coord x, Evas_Coord y)
930 {
931    sd->xloc = x;
932    sd->yloc = y;
933    _sizing_eval(obj);
934 }
935 
936 EOLIAN static void
_elm_menu_open(Eo * obj,Elm_Menu_Data * sd EINA_UNUSED)937 _elm_menu_open(Eo *obj, Elm_Menu_Data *sd EINA_UNUSED)
938 {
939    evas_object_show(obj);
940 }
941 
942 EOLIAN static void
_elm_menu_close(Eo * obj,Elm_Menu_Data * sd)943 _elm_menu_close(Eo *obj, Elm_Menu_Data *sd)
944 {
945    _menu_hide(obj, sd->hv, NULL);
946 }
947 
948 EOLIAN static Evas_Object *
_elm_menu_item_object_get(const Eo * eo_it EINA_UNUSED,Elm_Menu_Item_Data * it)949 _elm_menu_item_object_get(const Eo *eo_it EINA_UNUSED, Elm_Menu_Item_Data *it)
950 {
951    return VIEW(it);
952 }
953 
954 static void
_item_clone(Evas_Object * obj,Elm_Object_Item * parent,Elm_Object_Item * eo_item)955 _item_clone(Evas_Object *obj,
956             Elm_Object_Item *parent,
957             Elm_Object_Item *eo_item)
958 {
959    Elm_Object_Item *new_item;
960    Elm_Object_Item *subitem;
961    Eina_List *iter;
962 
963    ELM_MENU_ITEM_DATA_GET(eo_item, item);
964    if (item->separator)
965      new_item = elm_menu_item_separator_add(obj, parent);
966    else
967      new_item = elm_menu_item_add(obj,
968                                   parent,
969                                   item->icon_str,
970                                   item->label,
971                                   item->func,
972                                   WIDGET_ITEM_DATA_GET(EO_OBJ(item)));
973 
974    Eina_Bool disabled;
975    disabled = elm_wdg_item_disabled_get(eo_item);
976    elm_wdg_item_disabled_set(new_item, disabled);
977 
978    EINA_LIST_FOREACH(item->submenu.items, iter, subitem)
979      _item_clone(obj, new_item, subitem);
980 }
981 
982 void
elm_menu_clone(Evas_Object * from_menu,Evas_Object * to_menu,Elm_Object_Item * parent)983 elm_menu_clone(Evas_Object *from_menu,
984                Evas_Object *to_menu,
985                Elm_Object_Item *parent)
986 {
987    Eina_List *iter;
988    Elm_Object_Item *eo_item;
989 
990    ELM_MENU_CHECK(from_menu);
991    ELM_MENU_CHECK(to_menu);
992 
993    ELM_MENU_DATA_GET_OR_RETURN(from_menu, from_sd);
994 
995    EINA_LIST_FOREACH(from_sd->items, iter, eo_item)
996      _item_clone(to_menu, parent, eo_item);
997 }
998 
999 static void
_elm_menu_item_add_helper(Evas_Object * obj,Elm_Menu_Item_Data * parent,Elm_Menu_Item_Data * subitem,Elm_Menu_Data * sd)1000 _elm_menu_item_add_helper(Evas_Object *obj,
1001                           Elm_Menu_Item_Data *parent,
1002                           Elm_Menu_Item_Data *subitem,
1003                           Elm_Menu_Data *sd)
1004 {
1005    if (parent)
1006      {
1007         if (!parent->submenu.bx) _item_submenu_obj_create(parent);
1008         elm_box_pack_end(parent->submenu.bx, VIEW(subitem));
1009         parent->submenu.items =
1010           eina_list_append(parent->submenu.items, EO_OBJ(subitem));
1011         subitem->idx = eina_list_count(parent->submenu.items) - 1;
1012      }
1013    else
1014      {
1015         elm_box_pack_end(sd->bx, VIEW(subitem));
1016         sd->items = eina_list_append(sd->items, EO_OBJ(subitem));
1017         subitem->idx = eina_list_count(sd->items) - 1;
1018      }
1019 
1020    _sizing_eval(obj);
1021 }
1022 
1023 EOLIAN static void
_elm_menu_item_efl_object_destructor(Eo * eo_item,Elm_Menu_Item_Data * item)1024 _elm_menu_item_efl_object_destructor(Eo *eo_item, Elm_Menu_Item_Data *item)
1025 {
1026    ELM_MENU_DATA_GET(WIDGET(item), sd);
1027 
1028    _elm_menu_subitems_clear(item);
1029    eina_stringshare_del(item->label);
1030    eina_stringshare_del(item->icon_str);
1031    evas_object_del(item->content);
1032    evas_object_del(item->submenu.hv);
1033    evas_object_del(item->submenu.location);
1034 
1035    if (item->parent)
1036      item->parent->submenu.items =
1037        eina_list_remove(item->parent->submenu.items, eo_item);
1038    else
1039      sd->items = eina_list_remove(sd->items, eo_item);
1040 
1041    if (sd->dbus_menu)
1042      _elm_dbus_menu_item_delete(sd->dbus_menu, item->dbus_idx);
1043 
1044    efl_destructor(efl_super(eo_item, ELM_MENU_ITEM_CLASS));
1045 }
1046 
1047 EOLIAN static Eo *
_elm_menu_item_efl_object_constructor(Eo * eo_item,Elm_Menu_Item_Data * item)1048 _elm_menu_item_efl_object_constructor(Eo *eo_item, Elm_Menu_Item_Data *item)
1049 {
1050    eo_item = efl_constructor(efl_super(eo_item, ELM_MENU_ITEM_CLASS));
1051    item->base = efl_data_scope_get(eo_item, ELM_WIDGET_ITEM_CLASS);
1052 
1053    return eo_item;
1054 }
1055 
1056 EOLIAN static Elm_Object_Item*
_elm_menu_item_add(Eo * obj,Elm_Menu_Data * sd,Elm_Object_Item * parent,const char * icon,const char * label,Evas_Smart_Cb func,const void * data)1057 _elm_menu_item_add(Eo *obj, Elm_Menu_Data *sd, Elm_Object_Item *parent, const char *icon, const char *label, Evas_Smart_Cb func, const void *data)
1058 {
1059    Elm_Object_Item *eo_item;
1060    Evas_Object *icon_obj;
1061 
1062    icon_obj = elm_icon_add(obj);
1063    efl_access_object_access_type_set(icon_obj, EFL_ACCESS_TYPE_DISABLED);
1064    if (!icon_obj) return NULL;
1065 
1066    eo_item = efl_add(ELM_MENU_ITEM_CLASS, obj);
1067    if (!eo_item)
1068      {
1069         evas_object_del(icon_obj);
1070         return NULL;
1071      }
1072 
1073    ELM_MENU_ITEM_DATA_GET(eo_item, it);
1074 
1075    WIDGET_ITEM_DATA_SET(eo_item, data);
1076    it->func = func;
1077    it->parent = efl_data_scope_get(parent, ELM_MENU_ITEM_CLASS);
1078    it->content = icon_obj;
1079 
1080    _item_obj_create(it);
1081    efl_canvas_object_is_frame_object_set(VIEW(it), efl_canvas_object_is_frame_object_get(obj));
1082    efl_canvas_object_is_frame_object_set(icon_obj, efl_canvas_object_is_frame_object_get(obj));
1083    elm_object_item_text_set(eo_item, label);
1084 
1085    elm_layout_content_set(VIEW(it), "elm.swallow.content",
1086                           it->content);
1087 
1088    if (icon) elm_menu_item_icon_name_set(eo_item, icon);
1089 
1090    _elm_menu_item_add_helper(obj, it->parent, it, sd);
1091 
1092    if (sd->dbus_menu)
1093    {
1094      it->dbus_idx = _elm_dbus_menu_item_add(sd->dbus_menu, eo_item);
1095      it->dbus_menu = sd->dbus_menu;
1096    }
1097    if (_elm_config->atspi_mode)
1098      {
1099         efl_access_added(eo_item);
1100         efl_access_children_changed_added_signal_emit(parent ? parent : obj, eo_item);
1101      }
1102 
1103    return eo_item;
1104 }
1105 
1106 EOLIAN static unsigned int
_elm_menu_item_index_get(const Eo * eo_it EINA_UNUSED,Elm_Menu_Item_Data * it)1107 _elm_menu_item_index_get(const Eo *eo_it EINA_UNUSED, Elm_Menu_Item_Data *it)
1108 {
1109    return it->idx;
1110 }
1111 
1112 EOLIAN static void
_elm_menu_item_icon_name_set(Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * item,const char * icon)1113 _elm_menu_item_icon_name_set(Eo *eo_item EINA_UNUSED,
1114                              Elm_Menu_Item_Data *item,
1115                              const char *icon)
1116 {
1117    char icon_tmp[512];
1118 
1119    EINA_SAFETY_ON_NULL_RETURN(icon);
1120 
1121    if ((icon[0] != '\0') &&
1122        (snprintf(icon_tmp, sizeof(icon_tmp), "menu/%s", icon) > 0) &&
1123        (elm_icon_standard_set(item->content, icon_tmp) ||
1124         elm_icon_standard_set(item->content, icon)))
1125      {
1126         eina_stringshare_replace(&item->icon_str, icon);
1127         elm_layout_signal_emit(VIEW(item), "elm,state,icon,visible", "elm");
1128      }
1129    else
1130      elm_layout_signal_emit(VIEW(item), "elm,state,icon,hidden", "elm");
1131 
1132    edje_object_message_signal_process(elm_layout_edje_get(VIEW(item)));
1133    _sizing_eval(WIDGET(item));
1134 }
1135 
1136 EOLIAN static Elm_Object_Item*
_elm_menu_item_separator_add(Eo * obj,Elm_Menu_Data * sd,Elm_Object_Item * eo_p_item)1137 _elm_menu_item_separator_add(Eo *obj, Elm_Menu_Data *sd, Elm_Object_Item *eo_p_item)
1138 {
1139    Elm_Object_Item *eo_subitem;
1140 
1141    ELM_MENU_ITEM_DATA_GET(eo_p_item, p_item);
1142    /* don't add a separator as the first item */
1143    if (!sd->items) return NULL;
1144 
1145    /* don't allow adding more than one separator in a row */
1146    if (eo_p_item)
1147      {
1148         if (!p_item->submenu.items) return NULL;
1149         eo_subitem = eina_list_last(p_item->submenu.items)->data;
1150      }
1151    else eo_subitem = eina_list_last(sd->items)->data;
1152 
1153    ELM_MENU_ITEM_DATA_GET(eo_subitem, subitem);
1154    if (subitem->separator) return NULL;
1155 
1156    eo_subitem = efl_add(ELM_MENU_ITEM_CLASS, obj);
1157    if (!eo_subitem) return NULL;
1158 
1159    subitem = efl_data_scope_get(eo_subitem, ELM_MENU_ITEM_CLASS);
1160 
1161    subitem->separator = EINA_TRUE;
1162    subitem->parent = efl_data_scope_get(eo_p_item, ELM_MENU_ITEM_CLASS);
1163 
1164    _item_separator_obj_create(subitem);
1165    _elm_menu_item_add_helper(obj, subitem->parent, subitem, sd);
1166 
1167    _sizing_eval(obj);
1168 
1169    if (sd->dbus_menu)
1170      subitem->dbus_idx = _elm_dbus_menu_item_add(sd->dbus_menu,
1171                                                  eo_subitem);
1172    return eo_subitem;
1173 }
1174 
1175 EOLIAN static const char *
_elm_menu_item_icon_name_get(const Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * item)1176 _elm_menu_item_icon_name_get(const Eo *eo_item EINA_UNUSED, Elm_Menu_Item_Data *item)
1177 {
1178    return item->icon_str;
1179 }
1180 
1181 EOLIAN static Eina_Bool
_elm_menu_item_is_separator(const Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * item)1182 _elm_menu_item_is_separator(const Eo *eo_item EINA_UNUSED, Elm_Menu_Item_Data *item)
1183 {
1184    return item->separator;
1185 }
1186 
1187 EOLIAN static const Eina_List *
_elm_menu_item_subitems_get(const Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * item)1188 _elm_menu_item_subitems_get(const Eo *eo_item EINA_UNUSED, Elm_Menu_Item_Data *item)
1189 {
1190    return item->submenu.items;
1191 }
1192 
1193 EOLIAN static void
_elm_menu_item_subitems_clear(Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * it)1194 _elm_menu_item_subitems_clear(Eo *eo_item EINA_UNUSED, Elm_Menu_Item_Data *it)
1195 {
1196    _elm_menu_subitems_clear(it);
1197 }
1198 
1199 EOLIAN static const Eina_List *
_elm_menu_items_get(const Eo * obj EINA_UNUSED,Elm_Menu_Data * sd)1200 _elm_menu_items_get(const Eo *obj EINA_UNUSED, Elm_Menu_Data *sd)
1201 {
1202    return sd->items;
1203 }
1204 
1205 EOLIAN static void
_elm_menu_item_selected_set(Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * item,Eina_Bool selected)1206 _elm_menu_item_selected_set(Eo *eo_item EINA_UNUSED,
1207                             Elm_Menu_Item_Data *item,
1208                             Eina_Bool selected)
1209 {
1210    if (selected == item->selected) return;
1211    item->selected = selected;
1212    if (selected)
1213      {
1214         elm_layout_signal_emit(VIEW(item), "elm,state,selected", "elm");
1215         _menu_item_activate_cb(item, NULL, NULL, NULL);
1216      }
1217    else
1218      {
1219         elm_layout_signal_emit(VIEW(item), "elm,state,unselected", "elm");
1220         _menu_item_inactivate_cb(item, NULL, NULL, NULL);
1221      }
1222    edje_object_message_signal_process(elm_layout_edje_get(VIEW(item)));
1223 }
1224 
1225 EOLIAN static Eina_Bool
_elm_menu_item_selected_get(const Eo * eo_item EINA_UNUSED,Elm_Menu_Item_Data * item)1226 _elm_menu_item_selected_get(const Eo *eo_item EINA_UNUSED, Elm_Menu_Item_Data *item)
1227 {
1228    return item->selected;
1229 }
1230 
1231 EOLIAN static Elm_Object_Item *
_elm_menu_item_prev_get(const Eo * eo_item,Elm_Menu_Item_Data * item)1232 _elm_menu_item_prev_get(const Eo *eo_item, Elm_Menu_Item_Data *item)
1233 {
1234    if (item->parent)
1235      {
1236         Eina_List *l = eina_list_data_find_list
1237             (item->parent->submenu.items, eo_item);
1238         l = eina_list_prev(l);
1239         if (!l) return NULL;
1240         return l->data;
1241      }
1242    else
1243      {
1244         ELM_MENU_DATA_GET(WIDGET(item), sd);
1245         if (!sd || !sd->items) return NULL;
1246         Eina_List *l = eina_list_data_find_list(sd->items, eo_item);
1247         l = eina_list_prev(l);
1248         if (!l) return NULL;
1249         return l->data;
1250      }
1251 
1252    return NULL;
1253 }
1254 
1255 EOLIAN static Elm_Object_Item *
_elm_menu_item_next_get(const Eo * eo_item,Elm_Menu_Item_Data * item)1256 _elm_menu_item_next_get(const Eo *eo_item, Elm_Menu_Item_Data *item)
1257 {
1258    if (item->parent)
1259      {
1260         Eina_List *l =
1261           eina_list_data_find_list(item->parent->submenu.items, eo_item);
1262         l = eina_list_next(l);
1263         if (!l) return NULL;
1264         return l->data;
1265      }
1266    else
1267      {
1268         ELM_MENU_DATA_GET(WIDGET(item), sd);
1269         if (!sd || !sd->items) return NULL;
1270         Eina_List *l = eina_list_data_find_list(sd->items, eo_item);
1271         l = eina_list_next(l);
1272         if (!l) return NULL;
1273         return l->data;
1274      }
1275 
1276    return NULL;
1277 }
1278 
1279 EOLIAN static Elm_Object_Item*
_elm_menu_first_item_get(const Eo * obj EINA_UNUSED,Elm_Menu_Data * sd)1280 _elm_menu_first_item_get(const Eo *obj EINA_UNUSED, Elm_Menu_Data *sd)
1281 {
1282    return (sd->items ? sd->items->data : NULL);
1283 }
1284 
1285 EOLIAN static Elm_Object_Item*
_elm_menu_last_item_get(const Eo * obj EINA_UNUSED,Elm_Menu_Data * sd)1286 _elm_menu_last_item_get(const Eo *obj EINA_UNUSED, Elm_Menu_Data *sd)
1287 {
1288    Eina_List *l = eina_list_last(sd->items);
1289    return (l ? l->data : NULL);
1290 }
1291 
1292 EOLIAN static Elm_Object_Item*
_elm_menu_selected_item_get(const Eo * obj EINA_UNUSED,Elm_Menu_Data * sd)1293 _elm_menu_selected_item_get(const Eo *obj EINA_UNUSED, Elm_Menu_Data *sd)
1294 {
1295    Eina_List *l;
1296    Elm_Object_Item *eo_item;
1297 
1298    EINA_LIST_FOREACH(sd->items, l, eo_item)
1299      {
1300         ELM_MENU_ITEM_DATA_GET(eo_item, item);
1301         if (item->selected) return eo_item;
1302      }
1303 
1304    return NULL;
1305 }
1306 
1307 static void
_elm_menu_class_constructor(Efl_Class * klass)1308 _elm_menu_class_constructor(Efl_Class *klass)
1309 {
1310    evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
1311 }
1312 
1313 EOLIAN static Eina_List*
_elm_menu_efl_access_object_access_children_get(const Eo * obj,Elm_Menu_Data * sd)1314 _elm_menu_efl_access_object_access_children_get(const Eo *obj, Elm_Menu_Data *sd)
1315 {
1316    Eina_List *ret;
1317    ret = efl_access_object_access_children_get(efl_super(obj, ELM_MENU_CLASS));
1318    return eina_list_merge(eina_list_clone(sd->items), ret);
1319 }
1320 
1321 EOLIAN static Eina_List*
_elm_menu_item_efl_access_object_access_children_get(const Eo * obj EINA_UNUSED,Elm_Menu_Item_Data * sd)1322 _elm_menu_item_efl_access_object_access_children_get(const Eo *obj EINA_UNUSED, Elm_Menu_Item_Data *sd)
1323 {
1324    return eina_list_clone(sd->submenu.items);
1325 }
1326 
1327 EOLIAN static Eo*
_elm_menu_item_efl_object_parent_get(const Eo * obj,Elm_Menu_Item_Data * sd)1328 _elm_menu_item_efl_object_parent_get(const Eo *obj, Elm_Menu_Item_Data *sd)
1329 {
1330    if (sd->parent) return EO_OBJ(sd->parent);
1331    if (sd->base) return WIDGET(sd);
1332    return efl_parent_get(efl_super(obj, ELM_MENU_ITEM_CLASS));
1333 }
1334 
1335 EOLIAN static Efl_Access_Role
_elm_menu_item_efl_access_object_role_get(const Eo * obj EINA_UNUSED,Elm_Menu_Item_Data * sd)1336 _elm_menu_item_efl_access_object_role_get(const Eo *obj EINA_UNUSED, Elm_Menu_Item_Data *sd)
1337 {
1338    return sd->submenu.items ? EFL_ACCESS_ROLE_MENU : EFL_ACCESS_ROLE_MENU_ITEM;
1339 }
1340 
1341 EOLIAN static const char*
_elm_menu_item_efl_access_object_i18n_name_get(const Eo * obj,Elm_Menu_Item_Data * sd)1342 _elm_menu_item_efl_access_object_i18n_name_get(const Eo *obj, Elm_Menu_Item_Data *sd)
1343 {
1344    const char *ret;
1345    ret = efl_access_object_i18n_name_get(efl_super(obj, ELM_MENU_ITEM_CLASS));
1346    if (ret) return ret;
1347    return sd->label;
1348 }
1349 
1350 EOLIAN static Efl_Access_State_Set
_elm_menu_item_efl_access_object_state_set_get(const Eo * obj EINA_UNUSED,Elm_Menu_Item_Data * sd)1351 _elm_menu_item_efl_access_object_state_set_get(const Eo *obj EINA_UNUSED, Elm_Menu_Item_Data *sd)
1352 {
1353    Efl_Access_State_Set ret;
1354    ret = efl_access_object_state_set_get(efl_super(obj, ELM_MENU_ITEM_CLASS));
1355 
1356    STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_SELECTABLE);
1357 
1358    if (sd->selected)
1359       STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_SELECTED);
1360 
1361    return ret;
1362 }
1363 
1364 EOLIAN static int
_elm_menu_item_efl_access_selection_selected_children_count_get(const Eo * obj EINA_UNUSED,Elm_Menu_Item_Data * sd)1365 _elm_menu_item_efl_access_selection_selected_children_count_get(const Eo *obj EINA_UNUSED, Elm_Menu_Item_Data *sd)
1366 {
1367    int ret = 0;
1368    Elm_Object_Item *sobj = NULL;
1369    Eina_List *l;
1370 
1371    EINA_LIST_FOREACH(sd->submenu.items, l, sobj)
1372      {
1373         if (!sobj) continue;
1374         ELM_MENU_ITEM_DATA_GET(sobj, item);
1375         if (item && item->selected) ret++;
1376      }
1377 
1378    return ret;
1379 }
1380 
1381 EOLIAN static Eo*
_elm_menu_item_efl_access_selection_selected_child_get(const Eo * obj EINA_UNUSED,Elm_Menu_Item_Data * sd,int child)1382 _elm_menu_item_efl_access_selection_selected_child_get(const Eo *obj EINA_UNUSED, Elm_Menu_Item_Data *sd, int child)
1383 {
1384    int seq = 0;
1385    Elm_Object_Item *sobj = NULL;
1386    Eina_List *l;
1387 
1388    EINA_LIST_FOREACH(sd->submenu.items, l, sobj)
1389      {
1390         if (!sobj) continue;
1391         ELM_MENU_ITEM_DATA_GET(sobj, item);
1392 
1393         if (item && item->selected)
1394           {
1395              if (child == seq++)
1396                break;
1397           }
1398      }
1399 
1400    return sobj;
1401 }
1402 
1403 EOLIAN static int
_elm_menu_efl_access_selection_selected_children_count_get(const Eo * obj EINA_UNUSED,Elm_Menu_Data * sd)1404 _elm_menu_efl_access_selection_selected_children_count_get(const Eo *obj EINA_UNUSED, Elm_Menu_Data *sd)
1405 {
1406    Elm_Object_Item *sobj = NULL;
1407    Eina_List *l;
1408    int ret = 0;
1409 
1410    EINA_LIST_FOREACH(sd->items, l, sobj)
1411      {
1412         ELM_MENU_ITEM_DATA_GET(sobj, item);
1413         if (item && item->selected) ret++;
1414      }
1415 
1416    return ret;
1417 }
1418 
1419 EOLIAN static Eo*
_elm_menu_efl_access_selection_selected_child_get(const Eo * obj EINA_UNUSED,Elm_Menu_Data * sd,int child)1420 _elm_menu_efl_access_selection_selected_child_get(const Eo *obj EINA_UNUSED, Elm_Menu_Data *sd, int child)
1421 {
1422    int seq = 0;
1423    Elm_Object_Item *sobj = NULL;
1424    Eina_List *l;
1425 
1426    EINA_LIST_FOREACH(sd->items, l, sobj)
1427      {
1428         if (!sobj) continue;
1429         ELM_MENU_ITEM_DATA_GET(sobj, item);
1430 
1431         if (item && item->selected)
1432           {
1433              if (child == seq++)
1434                break;
1435           }
1436      }
1437 
1438    return sobj;
1439 
1440 }
1441 
1442 EOLIAN static Efl_Object*
_elm_menu_efl_object_provider_find(const Eo * obj,Elm_Menu_Data * pd,const Efl_Object * klass)1443 _elm_menu_efl_object_provider_find(const Eo *obj, Elm_Menu_Data *pd, const Efl_Object *klass)
1444 {
1445    if (!pd->parent)
1446      return efl_provider_find(efl_super(obj, MY_CLASS), klass);
1447 
1448    return efl_provider_find(pd->parent, klass);
1449 }
1450 
1451 
1452 
1453 /* Internal EO APIs and hidden overrides */
1454 
1455 #define ELM_MENU_EXTRA_OPS \
1456    EFL_CANVAS_GROUP_ADD_DEL_OPS(elm_menu)
1457 
1458 #include "elm_menu_item_eo.c"
1459 #include "elm_menu_eo.c"
1460