ctxpopup: merge eo & header files for different profiles
[platform/upstream/elementary.git] / src / lib / elc_ctxpopup.c
1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #define ELM_INTERFACE_ATSPI_ACCESSIBLE_PROTECTED
6 #define ELM_INTERFACE_ATSPI_COMPONENT_PROTECTED
7 #define ELM_INTERFACE_ATSPI_WIDGET_ACTION_PROTECTED
8
9 #define ELM_WIDGET_ITEM_PROTECTED
10 #include <Elementary.h>
11
12 #include "elm_priv.h"
13 //TIZEN_ONLY(20160412): fix 'more' style size problem
14 #include "elm_interface_scrollable.h"
15 //
16 #include "elm_widget_ctxpopup.h"
17
18 #define MY_CLASS ELM_CTXPOPUP_CLASS
19
20 #define MY_CLASS_NAME "Elm_Ctxpopup"
21 #define MY_CLASS_NAME_LEGACY "elm_ctxpopup"
22
23 #define ELM_PRIV_CTXPOPUP_SIGNALS(cmd) \
24    cmd(SIG_DISMISSED, "dismissed", "") \
25
26 ELM_PRIV_CTXPOPUP_SIGNALS(ELM_PRIV_STATIC_VARIABLE_DECLARE);
27
28 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
29    ELM_PRIV_CTXPOPUP_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    {SIG_LAYOUT_FOCUSED, ""}, /**< handled by elm_layout */
33    {SIG_LAYOUT_UNFOCUSED, ""}, /**< handled by elm_layout */
34    {NULL, NULL}
35 };
36 #undef ELM_PRIV_CTXPOPUP_SIGNALS
37
38 static Eina_Bool _key_action_move(Evas_Object *obj, const char *params);
39 static Eina_Bool _key_action_escape(Evas_Object *obj, const char *params);
40
41 static const Elm_Action key_actions[] = {
42    {"move", _key_action_move},
43    {"escape", _key_action_escape},
44    {NULL, NULL}
45 };
46
47 EOLIAN static Eina_Bool
48 _elm_ctxpopup_elm_widget_translate(Eo *obj, Elm_Ctxpopup_Data *sd)
49 {
50    Eina_List *l;
51    Elm_Object_Item *it;
52
53    if (sd->auto_hide) evas_object_hide(obj);
54
55    EINA_LIST_FOREACH(sd->items, l, it)
56      eo_do(it, elm_wdg_item_translate());
57
58    eo_do_super(obj, MY_CLASS, elm_obj_widget_translate());
59
60    return EINA_TRUE;
61 }
62
63 EOLIAN static Eina_Bool
64 _elm_ctxpopup_elm_widget_focus_next_manager_is(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *_pd EINA_UNUSED)
65 {
66    return EINA_TRUE;
67 }
68
69 EOLIAN static Eina_Bool
70 _elm_ctxpopup_elm_widget_focus_direction_manager_is(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *_pd EINA_UNUSED)
71 {
72    return EINA_TRUE;
73 }
74
75 EOLIAN static Eina_Bool
76 _elm_ctxpopup_elm_widget_focus_next(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd, Elm_Focus_Direction dir, Evas_Object **next, Elm_Object_Item **next_item)
77 {
78    if (!sd)
79      return EINA_FALSE;
80
81    if (!elm_widget_focus_next_get(sd->box, dir, next, next_item))
82      {
83         elm_widget_focused_object_clear(sd->box);
84         elm_widget_focus_next_get(sd->box, dir, next, next_item);
85      }
86
87    return EINA_TRUE;
88 }
89
90 EOLIAN static Eina_Bool
91 _elm_ctxpopup_elm_widget_focus_direction(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd, const Evas_Object *base, double degree, Evas_Object **direction, Elm_Object_Item **direction_item, double *weight)
92 {
93    Eina_Bool int_ret;
94
95    Eina_List *l = NULL;
96    void *(*list_data_get)(const Eina_List *list);
97
98    if (!sd)
99      return EINA_FALSE;
100
101    list_data_get = eina_list_data_get;
102
103    l = eina_list_append(l, sd->box);
104
105    int_ret = elm_widget_focus_list_direction_get
106             (obj, base, l, list_data_get, degree, direction, direction_item, weight);
107    eina_list_free(l);
108
109    return int_ret;
110 }
111
112 static Eina_Bool
113 _key_action_move(Evas_Object *obj, const char *params)
114 {
115    ELM_CTXPOPUP_DATA_GET(obj, sd);
116    const char *dir = params;
117
118    if (!sd->box) return EINA_FALSE;
119
120    _elm_widget_focus_auto_show(obj);
121    if (!strcmp(dir, "previous"))
122      elm_widget_focus_cycle(sd->box, ELM_FOCUS_PREVIOUS);
123    else if (!strcmp(dir, "next"))
124      elm_widget_focus_cycle(sd->box, ELM_FOCUS_NEXT);
125    else if (!strcmp(dir, "left"))
126      elm_widget_focus_cycle(sd->box, ELM_FOCUS_LEFT);
127    else if (!strcmp(dir, "right"))
128      elm_widget_focus_cycle(sd->box, ELM_FOCUS_RIGHT);
129    else if (!strcmp(dir, "up"))
130      elm_widget_focus_cycle(sd->box, ELM_FOCUS_UP);
131    else if (!strcmp(dir, "down"))
132      elm_widget_focus_cycle(sd->box, ELM_FOCUS_DOWN);
133    else return EINA_FALSE;
134
135    return EINA_TRUE;
136 }
137
138 static Eina_Bool
139 _key_action_escape(Evas_Object *obj, const char *params EINA_UNUSED)
140 {
141    elm_ctxpopup_dismiss(obj);
142    return EINA_TRUE;
143 }
144
145 EOLIAN static Eina_Bool
146 _elm_ctxpopup_elm_widget_event(Eo *obj, Elm_Ctxpopup_Data *sd EINA_UNUSED, Evas_Object *src, Evas_Callback_Type type, void *event_info)
147 {
148    Evas_Event_Key_Down *ev = event_info;
149    (void)src;
150
151    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
152    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
153
154    if (!_elm_config_key_binding_call(obj, ev, key_actions))
155      return EINA_FALSE;
156
157    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
158    return EINA_TRUE;
159 }
160
161 static void
162 _x_pos_adjust(Evas_Coord_Point *pos,
163               Evas_Coord_Point *base_size,
164               Evas_Coord_Rectangle *hover_area)
165 {
166    pos->x -= (base_size->x / 2);
167
168    if (pos->x < hover_area->x)
169      pos->x = hover_area->x;
170    else if ((pos->x + base_size->x) > (hover_area->x + hover_area->w))
171      pos->x = (hover_area->x + hover_area->w) - base_size->x;
172
173    if (base_size->x > hover_area->w)
174      base_size->x -= (base_size->x - hover_area->w);
175
176    if (pos->x < hover_area->x)
177      pos->x = hover_area->x;
178 }
179
180 static void
181 _y_pos_adjust(Evas_Coord_Point *pos,
182               Evas_Coord_Point *base_size,
183               Evas_Coord_Rectangle *hover_area)
184 {
185    pos->y -= (base_size->y / 2);
186
187    if (pos->y < hover_area->y)
188      pos->y = hover_area->y;
189    else if ((pos->y + base_size->y) > (hover_area->y + hover_area->h))
190      pos->y = hover_area->y + hover_area->h - base_size->y;
191
192    if (base_size->y > hover_area->h)
193      base_size->y -= (base_size->y - hover_area->h);
194
195    if (pos->y < hover_area->y)
196      pos->y = hover_area->y;
197 }
198
199 static Elm_Ctxpopup_Direction
200 _base_geometry_calc(Evas_Object *obj,
201                     Evas_Coord_Rectangle *rect)
202 {
203    Elm_Ctxpopup_Direction dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
204    Evas_Coord_Rectangle hover_area;
205    Evas_Coord_Point pos = {0, 0};
206    Evas_Coord_Point arrow_size;
207    Evas_Coord_Point base_size;
208    Evas_Coord_Point max_size;
209    Evas_Coord_Point min_size;
210    Evas_Coord_Point temp;
211    //TIZEN_ONLY(20160412): fix 'more' style size problem
212    Evas_Coord maxh = 0;
213    Evas_Coord scrh = 0;
214    const char *str = NULL;
215    //
216    int idx;
217
218    ELM_CTXPOPUP_DATA_GET(obj, sd);
219    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, ELM_CTXPOPUP_DIRECTION_DOWN);
220
221    if (!rect || !sd->parent) return ELM_CTXPOPUP_DIRECTION_DOWN;
222
223    edje_object_part_geometry_get(sd->arrow, "base", NULL, NULL,
224                                  &arrow_size.x, &arrow_size.y);
225    evas_object_resize(sd->arrow, arrow_size.x, arrow_size.y);
226
227    //Initialize Area Rectangle.
228    evas_object_geometry_get
229      (sd->parent, &hover_area.x, &hover_area.y, &hover_area.w,
230      &hover_area.h);
231    if (sd->parent && eo_isa(sd->parent, ELM_WIN_CLASS))
232      hover_area.x = hover_area.y = 0;
233
234    evas_object_geometry_get(obj, &pos.x, &pos.y, NULL, NULL);
235
236    //recalc the edje
237    edje_object_size_min_calc
238      (wd->resize_obj, &base_size.x, &base_size.y);
239    evas_object_smart_calculate(wd->resize_obj);
240
241    //Limit to Max Size
242    evas_object_size_hint_max_get(obj, &max_size.x, &max_size.y);
243
244    //TIZEN_ONLY(20160412): fix 'more' style size problem
245    if (sd->list && sd->list_visible)
246      {
247         eo_do(sd->list, elm_interface_scrollable_content_size_get(NULL, &scrh));
248         if (base_size.y < scrh)
249           base_size.y = scrh;
250      }
251    //
252
253    if ((max_size.y > 0) && (base_size.y > max_size.y))
254      base_size.y = max_size.y;
255
256    if ((max_size.x > 0) && (base_size.x > max_size.x))
257      base_size.x = max_size.x;
258
259    //TIZEN_ONLY(20160412): fix 'more' style size problem
260    if ((wd->orient_mode == 90) || (wd->orient_mode == 270))
261      str = edje_object_data_get(elm_layout_edje_get(obj), "visible_landscape_maxh");
262
263    if (!str) str = edje_object_data_get(elm_layout_edje_get(obj), "visible_maxh");
264
265    if (str) maxh = (int)(atoi(str)
266                          * elm_config_scale_get() * elm_object_scale_get(obj)
267                          / edje_object_base_scale_get(elm_layout_edje_get(obj)));
268
269    if ((maxh > 0) && (base_size.y > maxh))
270      base_size.y = maxh;
271    //
272
273    //Limit to Min Size
274    evas_object_size_hint_min_get(obj, &min_size.x, &min_size.y);
275
276    //TIZEN_ONLY(20160412): fix 'more' style size problem
277    if ((min_size.y > 0) && (min_size.y > maxh))
278      min_size.y = maxh;
279    //
280
281    if ((min_size.y > 0) && (base_size.y < min_size.y))
282      base_size.y = min_size.y;
283
284    if ((min_size.x > 0) && (base_size.x < min_size.x))
285      base_size.x = min_size.x;
286
287    //Check available directions. If there is one, it adjusts ctxpopup's position
288    // and size.
289    for (idx = 0; idx < 4; idx++)
290      {
291         switch (sd->dir_priority[idx])
292           {
293            case ELM_CTXPOPUP_DIRECTION_UP:
294              temp.y = (pos.y - base_size.y);
295              if ((temp.y - arrow_size.y) < hover_area.y)
296                continue;
297
298              _x_pos_adjust(&pos, &base_size, &hover_area);
299              pos.y -= base_size.y;
300              dir = ELM_CTXPOPUP_DIRECTION_UP;
301              break;
302
303            case ELM_CTXPOPUP_DIRECTION_LEFT:
304              temp.x = (pos.x - base_size.x);
305              if ((temp.x - arrow_size.x) < hover_area.x)
306                continue;
307
308              _y_pos_adjust(&pos, &base_size, &hover_area);
309              pos.x -= base_size.x;
310              dir = ELM_CTXPOPUP_DIRECTION_LEFT;
311              break;
312
313            case ELM_CTXPOPUP_DIRECTION_RIGHT:
314              temp.x = (pos.x + base_size.x);
315              if ((temp.x + arrow_size.x) >
316                  (hover_area.x + hover_area.w))
317                continue;
318
319              _y_pos_adjust(&pos, &base_size, &hover_area);
320              dir = ELM_CTXPOPUP_DIRECTION_RIGHT;
321              break;
322
323            case ELM_CTXPOPUP_DIRECTION_DOWN:
324              temp.y = (pos.y + base_size.y);
325              if ((temp.y + arrow_size.y) >
326                  (hover_area.y + hover_area.h))
327                continue;
328
329              _x_pos_adjust(&pos, &base_size, &hover_area);
330              dir = ELM_CTXPOPUP_DIRECTION_DOWN;
331              break;
332
333            default:
334              continue;
335           }
336         break;
337      }
338
339    //In this case, all directions are invalid because of lack of space.
340    if (idx == 4)
341      {
342         Evas_Coord length[2];
343
344         if (!sd->horizontal)
345           {
346              length[0] = pos.y - hover_area.y;
347              length[1] = (hover_area.y + hover_area.h) - pos.y;
348
349              // ELM_CTXPOPUP_DIRECTION_UP
350              if (length[0] > length[1])
351                {
352                   _x_pos_adjust(&pos, &base_size, &hover_area);
353                   pos.y -= base_size.y;
354                   dir = ELM_CTXPOPUP_DIRECTION_UP;
355                   if (pos.y < (hover_area.y + arrow_size.y))
356                     {
357                        base_size.y -= ((hover_area.y + arrow_size.y) - pos.y);
358                        pos.y = hover_area.y + arrow_size.y;
359                     }
360                }
361              //ELM_CTXPOPUP_DIRECTION_DOWN
362              else
363                {
364                   _x_pos_adjust(&pos, &base_size, &hover_area);
365                   dir = ELM_CTXPOPUP_DIRECTION_DOWN;
366                   if ((pos.y + arrow_size.y + base_size.y) >
367                       (hover_area.y + hover_area.h))
368                     base_size.y -=
369                       ((pos.y + arrow_size.y + base_size.y) -
370                        (hover_area.y + hover_area.h));
371                }
372           }
373         else
374           {
375              length[0] = pos.x - hover_area.x;
376              length[1] = (hover_area.x + hover_area.w) - pos.x;
377
378              //ELM_CTXPOPUP_DIRECTION_LEFT
379              if (length[0] > length[1])
380                {
381                   _y_pos_adjust(&pos, &base_size, &hover_area);
382                   pos.x -= base_size.x;
383                   dir = ELM_CTXPOPUP_DIRECTION_LEFT;
384                   if (pos.x < (hover_area.x + arrow_size.x))
385                     {
386                        base_size.x -= ((hover_area.x + arrow_size.x) - pos.x);
387                        pos.x = hover_area.x + arrow_size.x;
388                     }
389                }
390              //ELM_CTXPOPUP_DIRECTION_RIGHT
391              else
392                {
393                   _y_pos_adjust(&pos, &base_size, &hover_area);
394                   dir = ELM_CTXPOPUP_DIRECTION_RIGHT;
395                   if (pos.x + (arrow_size.x + base_size.x) >
396                       hover_area.x + hover_area.w)
397                     base_size.x -=
398                       ((pos.x + arrow_size.x + base_size.x) -
399                        (hover_area.x + hover_area.w));
400                }
401           }
402      }
403
404    //Final position and size.
405    rect->x = pos.x;
406    rect->y = pos.y;
407    rect->w = base_size.x;
408    rect->h = base_size.y;
409
410    return dir;
411 }
412
413 static void
414 _arrow_update(Evas_Object *obj,
415               Elm_Ctxpopup_Direction dir,
416               Evas_Coord_Rectangle base_size)
417 {
418    Evas_Coord_Rectangle arrow_size;
419    Evas_Coord x, y;
420    double drag;
421
422    ELM_CTXPOPUP_DATA_GET(obj, sd);
423    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
424
425    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
426    evas_object_geometry_get
427      (sd->arrow, NULL, NULL, &arrow_size.w, &arrow_size.h);
428
429    /* arrow is not being kept as sub-object on purpose, here. the
430     * design of the widget does not help with the contrary */
431
432    switch (dir)
433      {
434       case ELM_CTXPOPUP_DIRECTION_RIGHT:
435         edje_object_signal_emit(sd->arrow, "elm,state,left", "elm");
436         if (!edje_object_part_swallow(wd->resize_obj,
437             (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_right" :
438              "elm.swallow.arrow_left"), sd->arrow))
439           {
440              evas_object_hide(sd->arrow);
441              break;
442           }
443
444         if (base_size.h > 0)
445           {
446              if (y < ((arrow_size.h * 0.5) + base_size.y))
447                y = 0;
448              else if (y > base_size.y + base_size.h - (arrow_size.h * 0.5))
449                y = base_size.h - arrow_size.h;
450              else
451                y = y - base_size.y - (arrow_size.h * 0.5);
452              drag = (double)(y) / (double)(base_size.h - arrow_size.h);
453              edje_object_part_drag_value_set
454                 (wd->resize_obj,
455                  (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_right" :
456                   "elm.swallow.arrow_left"), 1, drag);
457           }
458         break;
459
460       case ELM_CTXPOPUP_DIRECTION_LEFT:
461         edje_object_signal_emit(sd->arrow, "elm,state,right", "elm");
462         if (!edje_object_part_swallow(wd->resize_obj,
463             (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_left" :
464              "elm.swallow.arrow_right"), sd->arrow))
465           {
466              evas_object_hide(sd->arrow);
467              break;
468           }
469
470         if (base_size.h > 0)
471           {
472              if (y < ((arrow_size.h * 0.5) + base_size.y))
473                y = 0;
474              else if (y > (base_size.y + base_size.h - (arrow_size.h * 0.5)))
475                y = base_size.h - arrow_size.h;
476              else
477                y = y - base_size.y - (arrow_size.h * 0.5);
478              drag = (double)(y) / (double)(base_size.h - arrow_size.h);
479              edje_object_part_drag_value_set
480                 (wd->resize_obj,
481                  (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_left" :
482                   "elm.swallow.arrow_right"), 0, drag);
483           }
484         break;
485
486       case ELM_CTXPOPUP_DIRECTION_DOWN:
487         edje_object_signal_emit(sd->arrow, "elm,state,top", "elm");
488         if (!edje_object_part_swallow(wd->resize_obj, "elm.swallow.arrow_up", sd->arrow))
489           {
490              evas_object_hide(sd->arrow);
491              break;
492           }
493
494         if (base_size.w > 0)
495           {
496              if (x < ((arrow_size.w * 0.5) + base_size.x))
497                x = 0;
498              else if (x > (base_size.x + base_size.w - (arrow_size.w * 0.5)))
499                x = base_size.w - arrow_size.w;
500              else
501                x = x - base_size.x - (arrow_size.w * 0.5);
502              drag = (double)(x) / (double)(base_size.w - arrow_size.w);
503              edje_object_part_drag_value_set
504                (wd->resize_obj, "elm.swallow.arrow_up", drag,
505                1);
506           }
507         break;
508
509       case ELM_CTXPOPUP_DIRECTION_UP:
510         edje_object_signal_emit(sd->arrow, "elm,state,bottom", "elm");
511         if (!edje_object_part_swallow(wd->resize_obj, "elm.swallow.arrow_down", sd->arrow))
512           {
513              evas_object_hide(sd->arrow);
514              break;
515           }
516
517         if (base_size.w > 0)
518           {
519              if (x < ((arrow_size.w * 0.5) + base_size.x))
520                x = 0;
521              else if (x > (base_size.x + base_size.w - (arrow_size.w * 0.5)))
522                x = base_size.w - arrow_size.w;
523              else x = x - base_size.x - (arrow_size.w * 0.5);
524              drag = (double)(x) / (double)(base_size.w - arrow_size.w);
525              edje_object_part_drag_value_set
526                (wd->resize_obj, "elm.swallow.arrow_down",
527                drag, 0);
528           }
529         break;
530
531       default:
532         break;
533      }
534
535    //should be here for getting accurate geometry value
536    evas_object_smart_calculate(wd->resize_obj);
537 }
538
539 static void
540 _show_signals_emit(Evas_Object *obj,
541                    Elm_Ctxpopup_Direction dir)
542 {
543    ELM_CTXPOPUP_DATA_GET(obj, sd);
544
545    if (!sd->visible) return;
546    if ((sd->list) && (!sd->list_visible)) return;
547    if (sd->emitted) return;
548
549    sd->emitted = EINA_TRUE;
550    switch (dir)
551      {
552       case ELM_CTXPOPUP_DIRECTION_UP:
553         elm_layout_signal_emit(obj, "elm,state,show,up", "elm");
554         break;
555
556       case ELM_CTXPOPUP_DIRECTION_LEFT:
557         elm_layout_signal_emit(obj,
558               (elm_widget_mirrored_get(obj) ? "elm,state,show,right" :
559                "elm,state,show,left"), "elm");
560         break;
561
562       case ELM_CTXPOPUP_DIRECTION_RIGHT:
563         elm_layout_signal_emit(obj,
564               (elm_widget_mirrored_get(obj) ? "elm,state,show,left" :
565                "elm,state,show,right"), "elm");
566         break;
567
568       case ELM_CTXPOPUP_DIRECTION_DOWN:
569         elm_layout_signal_emit(obj, "elm,state,show,down", "elm");
570         break;
571
572       default:
573         break;
574      }
575
576    edje_object_signal_emit(sd->bg, "elm,state,show", "elm");
577    elm_layout_signal_emit(obj, "elm,state,show", "elm");
578 }
579
580 static void
581 _hide_signals_emit(Evas_Object *obj,
582                    Elm_Ctxpopup_Direction dir)
583 {
584    ELM_CTXPOPUP_DATA_GET(obj, sd);
585
586    if (!sd->visible) return;
587
588    switch (dir)
589      {
590       case ELM_CTXPOPUP_DIRECTION_UP:
591         elm_layout_signal_emit(obj, "elm,state,hide,up", "elm");
592         break;
593
594       case ELM_CTXPOPUP_DIRECTION_LEFT:
595         elm_layout_signal_emit(obj,
596               (elm_widget_mirrored_get(obj) ? "elm,state,hide,right" :
597                "elm,state,hide,left"), "elm");
598         break;
599
600       case ELM_CTXPOPUP_DIRECTION_RIGHT:
601         elm_layout_signal_emit(obj,
602               (elm_widget_mirrored_get(obj) ? "elm,state,hide,left" :
603                "elm,state,hide,right"), "elm");
604         break;
605
606       case ELM_CTXPOPUP_DIRECTION_DOWN:
607         elm_layout_signal_emit(obj, "elm,state,hide,down", "elm");
608         break;
609
610       default:
611         break;
612      }
613    edje_object_signal_emit(sd->bg, "elm,state,hide", "elm");
614    elm_layout_signal_emit(obj, "elm,state,hide", "elm");
615 }
616
617 static void
618 _base_shift_by_arrow(Evas_Object *arrow,
619                      Elm_Ctxpopup_Direction dir,
620                      Evas_Coord_Rectangle *rect)
621 {
622    Evas_Coord arrow_w, arrow_h;
623
624    evas_object_geometry_get(arrow, NULL, NULL, &arrow_w, &arrow_h);
625    switch (dir)
626      {
627       case ELM_CTXPOPUP_DIRECTION_RIGHT:
628         rect->x += arrow_w;
629         break;
630
631       case ELM_CTXPOPUP_DIRECTION_LEFT:
632         rect->x -= arrow_w;
633         break;
634
635       case ELM_CTXPOPUP_DIRECTION_DOWN:
636         rect->y += arrow_h;
637         break;
638
639       case ELM_CTXPOPUP_DIRECTION_UP:
640         rect->y -= arrow_h;
641         break;
642
643       default:
644         break;
645      }
646 }
647
648 EOLIAN static Eina_Bool
649 _elm_ctxpopup_elm_layout_sub_object_add_enable(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *_pd EINA_UNUSED)
650 {
651    return EINA_FALSE;
652 }
653
654 EOLIAN static Eina_Bool
655 _elm_ctxpopup_elm_widget_sub_object_add(Eo *obj, Elm_Ctxpopup_Data *_pd EINA_UNUSED, Evas_Object *sobj)
656 {
657    Eina_Bool int_ret = EINA_FALSE;
658
659    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_sub_object_add(sobj));
660
661    return int_ret;
662 }
663
664 EOLIAN static void
665 _elm_ctxpopup_elm_layout_sizing_eval(Eo *obj, Elm_Ctxpopup_Data *sd)
666 {
667    Evas_Coord_Rectangle rect = { 0, 0, 1, 1 };
668    Evas_Coord_Point list_size = { 0, 0 }, parent_size = {0, 0};
669
670    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
671
672    if (!sd->arrow) return;  /* simple way to flag "under deletion" */
673    if (!sd->parent) return; /* do not calculate sizes unless parent is set */
674
675    //Base
676    sd->dir = _base_geometry_calc(obj, &rect);
677
678    _arrow_update(obj, sd->dir, rect);
679
680    _base_shift_by_arrow(sd->arrow, sd->dir, &rect);
681
682    if ((sd->list) && (sd->list_visible))
683      {
684         evas_object_geometry_get(sd->list, 0, 0, &list_size.x, &list_size.y);
685         if ((list_size.x >= rect.w) || (list_size.y >= rect.h))
686           {
687              elm_list_mode_set(sd->list, ELM_LIST_COMPRESS);
688              evas_object_size_hint_min_set(obj, list_size.x, list_size.y);
689           }
690         /*
691             Decrease height of main object initiate recalc of size (sizing_eval).
692             At each iteration of calculating size height of the object
693             will be closer to the height of the list.
694          */
695         if (list_size.y < rect.h) rect.h--;
696      }
697    else if (sd->content)
698      {
699         evas_object_geometry_get(sd->content, 0, 0, &list_size.x, &list_size.y);
700         if ((list_size.x >= rect.w) || (list_size.y >= rect.h))
701           evas_object_size_hint_min_set(obj, list_size.x, list_size.y);
702      }
703
704    evas_object_geometry_get(sd->parent, NULL, NULL, &parent_size.x, &parent_size.y);
705    evas_object_resize(sd->bg, parent_size.x, parent_size.y);
706
707    evas_object_move(wd->resize_obj, rect.x, rect.y);
708    evas_object_resize(wd->resize_obj, rect.w, rect.h);
709
710    _show_signals_emit(obj, sd->dir);
711 }
712
713 static void
714 _on_parent_del(void *data,
715                Evas *e EINA_UNUSED,
716                Evas_Object *obj EINA_UNUSED,
717                void *event_info EINA_UNUSED)
718 {
719    evas_object_del(data);
720 }
721
722 static void
723 _on_parent_move(void *data,
724                 Evas *e EINA_UNUSED,
725                 Evas_Object *obj EINA_UNUSED,
726                 void *event_info EINA_UNUSED)
727 {
728    ELM_CTXPOPUP_DATA_GET(data, sd);
729
730    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
731
732    if (sd->visible) elm_layout_sizing_eval(data);
733 }
734
735 static void
736 _on_parent_resize(void *data,
737                   Evas *e EINA_UNUSED,
738                   Evas_Object *obj EINA_UNUSED,
739                   void *event_info EINA_UNUSED)
740 {
741    ELM_CTXPOPUP_DATA_GET(data, sd);
742
743    if (sd->auto_hide)
744      {
745         sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
746
747         evas_object_hide(data);
748         eo_do(data, eo_event_callback_call(ELM_CTXPOPUP_EVENT_DISMISSED, NULL));
749      }
750    else
751      {
752         if (sd->visible)
753           elm_layout_sizing_eval(data);
754      }
755 }
756
757 static void
758 _parent_detach(Evas_Object *obj)
759 {
760    ELM_CTXPOPUP_DATA_GET(obj, sd);
761
762    if (!sd->parent) return;
763
764    evas_object_event_callback_del_full
765      (sd->parent, EVAS_CALLBACK_DEL, _on_parent_del, obj);
766    evas_object_event_callback_del_full
767      (sd->parent, EVAS_CALLBACK_MOVE, _on_parent_move, obj);
768    evas_object_event_callback_del_full
769      (sd->parent, EVAS_CALLBACK_RESIZE, _on_parent_resize, obj);
770 }
771
772 static void
773 _on_content_resized(void *data,
774                     Evas *e EINA_UNUSED,
775                     Evas_Object *obj EINA_UNUSED,
776                     void *event_info EINA_UNUSED)
777 {
778    ELM_CTXPOPUP_DATA_GET(data, sd);
779
780    elm_box_recalculate(sd->box);
781    elm_layout_sizing_eval(data);
782 }
783
784 //FIXME: lost the content size when theme hook is called.
785 EOLIAN static Elm_Theme_Apply
786 _elm_ctxpopup_elm_widget_theme_apply(Eo *obj, Elm_Ctxpopup_Data *sd)
787 {
788    Elm_Theme_Apply int_ret = ELM_THEME_APPLY_FAILED;
789
790    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_theme_apply());
791    if (!int_ret) return ELM_THEME_APPLY_FAILED;
792
793    elm_widget_theme_object_set
794      (obj, sd->bg, "ctxpopup", "bg", elm_widget_style_get(obj));
795    elm_widget_theme_object_set
796      (obj, sd->arrow, "ctxpopup", "arrow", elm_widget_style_get(obj));
797
798    if (sd->list)
799      {
800         if (!strncmp(elm_object_style_get(obj), "default", strlen("default")))
801           elm_object_style_set(sd->list, "ctxpopup");
802         else
803           elm_object_style_set(sd->list, elm_object_style_get(obj));
804      }
805
806    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
807
808    if (sd->visible) elm_layout_sizing_eval(obj);
809
810    return int_ret;
811 }
812
813 /* kind of a big and tricky override here: an internal box will hold
814  * the actual content. content aliases won't be of much help here */
815 EOLIAN static Eina_Bool
816 _elm_ctxpopup_elm_container_content_set(Eo *obj, Elm_Ctxpopup_Data *sd, const char *part, Evas_Object *content)
817 {
818    Eina_Bool int_ret = EINA_TRUE;
819
820    if ((part) && (strcmp(part, "default")))
821      {
822         eo_do_super(obj, MY_CLASS, int_ret = elm_obj_container_content_set(part, content));
823         return int_ret;
824      }
825
826    if (!content) return EINA_FALSE;
827
828    if (content == sd->content) return EINA_TRUE;
829
830    evas_object_del(sd->content);
831    if (sd->content == sd->list) sd->list = NULL;
832
833    evas_object_size_hint_weight_set
834      (content, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
835    evas_object_size_hint_fill_set
836      (content, EVAS_HINT_FILL, EVAS_HINT_FILL);
837
838    elm_box_pack_end(sd->box, content);
839    evas_object_show(content);
840
841    sd->content = content;
842    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
843
844    if (sd->visible) elm_layout_sizing_eval(obj);
845
846    return EINA_TRUE;
847 }
848
849 EOLIAN static Evas_Object*
850 _elm_ctxpopup_elm_container_content_get(Eo *obj, Elm_Ctxpopup_Data *sd, const char *part)
851 {
852
853    if ((part) && (strcmp(part, "default")))
854      {
855         Evas_Object *ret = NULL;
856         eo_do_super(obj, MY_CLASS, ret = elm_obj_container_content_get(part));
857         return ret;
858      }
859
860    return sd->content;
861 }
862
863 EOLIAN static Evas_Object*
864 _elm_ctxpopup_elm_container_content_unset(Eo *obj, Elm_Ctxpopup_Data *sd, const char *part)
865 {
866    Evas_Object *content = NULL;
867
868    if ((part) && (strcmp(part, "default")))
869      {
870         eo_do_super(obj, MY_CLASS, content = elm_obj_container_content_unset(part));
871         return content;
872      }
873
874    content = sd->content;
875    if (!content) return content;
876
877    elm_box_unpack(sd->box, content);
878    sd->content = NULL;
879    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
880
881    if (sd->visible) elm_layout_sizing_eval(obj);
882
883    return content;
884 }
885
886 EOLIAN static void
887 _elm_ctxpopup_item_elm_widget_item_part_text_set(Eo *eo_ctxpopup_it EINA_UNUSED,
888                                                  Elm_Ctxpopup_Item_Data *ctxpopup_it,
889                                                  const char *part,
890                                                  const char *label)
891 {
892    if ((part) && (strcmp(part, "default"))) return;
893
894    ELM_CTXPOPUP_DATA_GET(WIDGET(ctxpopup_it), sd);
895
896    elm_object_item_part_text_set(ctxpopup_it->list_item, "default", label);
897    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
898
899    if (sd->visible) elm_layout_sizing_eval(WIDGET(ctxpopup_it));
900 }
901
902 EOLIAN static const char *
903 _elm_ctxpopup_item_elm_widget_item_part_text_get(Eo *eo_ctxpopup_it EINA_UNUSED,
904                                                  Elm_Ctxpopup_Item_Data *ctxpopup_it,
905                                                  const char *part)
906 {
907    if (part && strcmp(part, "default")) return NULL;
908
909    return elm_object_item_part_text_get(ctxpopup_it->list_item, "default");
910 }
911
912 EOLIAN static void
913 _elm_ctxpopup_item_elm_widget_item_part_content_set(Eo *eo_ctxpopup_it EINA_UNUSED,
914                                                     Elm_Ctxpopup_Item_Data *ctxpopup_it,
915                                                     const char *part,
916                                                     Evas_Object *content)
917 {
918    if ((part) && (strcmp(part, "icon"))
919        && (strcmp(part, "start"))
920        && (strcmp(part, "end"))) return;
921
922    ELM_CTXPOPUP_DATA_GET(WIDGET(ctxpopup_it), sd);
923
924    if ((part) && (!strcmp(part, "end")))
925      elm_object_item_part_content_set(ctxpopup_it->list_item, "end", content);
926    else
927      elm_object_item_part_content_set
928        (ctxpopup_it->list_item, "start", content);
929
930    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
931
932    if (sd->visible) elm_layout_sizing_eval(WIDGET(ctxpopup_it));
933 }
934
935 EOLIAN static Evas_Object *
936 _elm_ctxpopup_item_elm_widget_item_part_content_get(Eo *eo_ctxpopup_it EINA_UNUSED,
937                                                     Elm_Ctxpopup_Item_Data *ctxpopup_it,
938                                                     const char *part)
939 {
940    if (part && strcmp(part, "icon") && strcmp(part, "start")
941        && strcmp(part, "end")) return NULL;
942
943    if (part && !strcmp(part, "end"))
944      return elm_object_item_part_content_get(ctxpopup_it->list_item, "end");
945    else
946      return elm_object_item_part_content_get(ctxpopup_it->list_item, "start");
947 }
948
949 EOLIAN static void
950 _elm_ctxpopup_item_elm_widget_item_disable(Eo *eo_ctxpopup_it,
951                                            Elm_Ctxpopup_Item_Data *ctxpopup_it)
952 {
953    Eina_Bool tmp;
954    elm_object_item_disabled_set
955      (ctxpopup_it->list_item, eo_do_ret(eo_ctxpopup_it, tmp, elm_wdg_item_disabled_get()));
956 }
957
958 EOLIAN static void
959 _elm_ctxpopup_item_elm_widget_item_signal_emit(Eo *eo_ctxpopup_it EINA_UNUSED,
960                                                Elm_Ctxpopup_Item_Data *ctxpopup_it EINA_UNUSED,
961                                                const char *emission EINA_UNUSED,
962                                                const char *source EINA_UNUSED)
963 {
964    elm_object_item_signal_emit(ctxpopup_it->list_item, emission, source);
965 }
966
967 // TIZEN_ONLY(20170116): merge eo & header files for different profiles
968 EOLIAN static void
969 _elm_ctxpopup_item_elm_widget_item_style_set(Eo *eo_item EINA_UNUSED,
970                                              Elm_Ctxpopup_Item_Data *item EINA_UNUSED,
971                                              const char *style EINA_UNUSED)
972 {
973    // mobile & wearable only feature
974 }
975
976 EOLIAN static Eina_Bool
977 _elm_ctxpopup_item_elm_widget_item_del_pre(Eo *eo_item EINA_UNUSED, Elm_Ctxpopup_Item_Data *item EINA_UNUSED)
978 {
979    // mobile & wearable only feature
980    return EINA_TRUE;
981 }
982
983 EOLIAN static Elm_Atspi_State_Set
984 _elm_ctxpopup_item_elm_interface_atspi_accessible_state_set_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Item_Data *it EINA_UNUSED)
985 {
986    // mobile & wearable only feature
987    return 0;
988 }
989
990 EOLIAN static Eina_Bool
991 _elm_ctxpopup_item_elm_interface_atspi_component_highlight_grab(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Item_Data *sd EINA_UNUSED)
992 {
993    // mobile & wearable only feature
994    return EINA_FALSE;
995 }
996
997 EOLIAN static Eina_Bool
998 _elm_ctxpopup_item_elm_interface_atspi_component_highlight_clear(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Item_Data *sd EINA_UNUSED)
999 {
1000    // mobile & wearable only feature
1001    return EINA_FALSE;
1002 }
1003 //
1004
1005 EOLIAN static void
1006 _elm_ctxpopup_item_elm_widget_item_focus_set(Eo *eo_ctxpopup_it EINA_UNUSED,
1007                                              Elm_Ctxpopup_Item_Data *ctxpopup_it,
1008                                              Eina_Bool focused)
1009 {
1010    elm_object_item_focus_set(ctxpopup_it->list_item, focused);
1011 }
1012
1013 EOLIAN static Eina_Bool
1014 _elm_ctxpopup_item_elm_widget_item_focus_get(Eo *eo_ctxpopup_it EINA_UNUSED,
1015                                              Elm_Ctxpopup_Item_Data *ctxpopup_it)
1016 {
1017    return elm_object_item_focus_get(ctxpopup_it->list_item);
1018 }
1019
1020 static void
1021 _bg_clicked_cb(void *data,
1022                Evas_Object *obj EINA_UNUSED,
1023                const char *emission EINA_UNUSED,
1024                const char *source EINA_UNUSED)
1025 {
1026    ELM_CTXPOPUP_DATA_GET(data, sd);
1027
1028    if (sd->auto_hide)
1029      _hide_signals_emit(data, sd->dir);
1030 }
1031
1032 static void
1033 _on_show(void *data EINA_UNUSED,
1034          Evas *e EINA_UNUSED,
1035          Evas_Object *obj,
1036          void *event_info EINA_UNUSED)
1037 {
1038    ELM_CTXPOPUP_DATA_GET(obj, sd);
1039
1040    if (sd->list)
1041      {
1042         elm_list_go(sd->list);
1043         sd->visible = EINA_TRUE;
1044         /*
1045          * XXX: Giving focus to the list when it has nothing selected makes
1046          * it select the first of its items, which makes the popup in
1047          * Terminology never open and instead just trigger the first option.
1048          * I'll let as an exercise to the reader to figure out why that
1049          * is so fucking annoying. Extra points for noting why this is my
1050          * choice of a "fix" instead of fixing the actual focus/select issue
1051          * that seems to be spread all over Elementary.
1052          */
1053         //elm_object_focus_set(sd->list, EINA_TRUE);
1054
1055         //TIZEN_ONLY(20160412): fix 'more' style size problem
1056         elm_layout_sizing_eval(obj);
1057         //
1058
1059         return;
1060      }
1061
1062    if (!sd->content) return;
1063
1064    sd->emitted = EINA_FALSE;
1065    sd->visible = EINA_TRUE;
1066
1067    _show_signals_emit(obj, sd->dir);
1068
1069    elm_layout_sizing_eval(obj);
1070
1071    elm_object_focus_set(obj, EINA_TRUE);
1072 }
1073
1074 static void
1075 _on_hide(void *data EINA_UNUSED,
1076          Evas *e EINA_UNUSED,
1077          Evas_Object *obj,
1078          void *event_info EINA_UNUSED)
1079 {
1080    ELM_CTXPOPUP_DATA_GET(obj, sd);
1081
1082    if (!sd->visible) return;
1083
1084    sd->visible = EINA_FALSE;
1085    sd->list_visible = EINA_FALSE;
1086 }
1087
1088 static void
1089 _on_move(void *data EINA_UNUSED,
1090          Evas *e EINA_UNUSED,
1091          Evas_Object *obj,
1092          void *event_info EINA_UNUSED)
1093 {
1094    elm_layout_sizing_eval(obj);
1095 }
1096
1097 static void
1098 _hide_finished_cb(void *data,
1099                   Evas_Object *obj EINA_UNUSED,
1100                   const char *emission EINA_UNUSED,
1101                   const char *source EINA_UNUSED)
1102 {
1103    evas_object_hide(data);
1104    eo_do(data, eo_event_callback_call(ELM_CTXPOPUP_EVENT_DISMISSED, NULL));
1105 }
1106
1107 static void
1108 _list_resize_cb(void *data,
1109                 Evas *e EINA_UNUSED,
1110                 Evas_Object *obj EINA_UNUSED,
1111                 void *event_info EINA_UNUSED)
1112 {
1113    ELM_CTXPOPUP_DATA_GET(data, sd);
1114
1115    if (!sd->visible) return;
1116    if (sd->list_visible) return;
1117
1118    sd->list_visible = EINA_TRUE;
1119
1120    elm_layout_sizing_eval(data);
1121 }
1122
1123 static void
1124 _list_del(Elm_Ctxpopup_Data *sd)
1125 {
1126    ELM_SAFE_FREE(sd->list, evas_object_del);
1127 }
1128
1129 EOLIAN static void
1130 _elm_ctxpopup_item_eo_base_destructor(Eo *eo_ctxpopup_it,
1131                                       Elm_Ctxpopup_Item_Data *ctxpopup_it)
1132 {
1133    Evas_Object *list;
1134
1135    ELM_CTXPOPUP_DATA_GET(WIDGET(ctxpopup_it), sd);
1136
1137    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1138
1139    list = elm_object_item_widget_get(ctxpopup_it->list_item);
1140
1141    if (ctxpopup_it->list_item)
1142       eo_unref(ctxpopup_it->list_item);
1143    sd->items = eina_list_remove(sd->items, eo_ctxpopup_it);
1144    if (list && eina_list_count(elm_list_items_get(list)) < 2)
1145      {
1146         evas_object_hide(WIDGET(ctxpopup_it));
1147      }
1148    else
1149      {
1150         if (sd->list_visible) elm_layout_sizing_eval(WIDGET(ctxpopup_it));
1151      }
1152
1153    eo_do_super(eo_ctxpopup_it, ELM_CTXPOPUP_ITEM_CLASS, eo_destructor());
1154 }
1155
1156 EOLIAN static Eina_Bool
1157 _elm_ctxpopup_elm_widget_disable(Eo *obj, Elm_Ctxpopup_Data *sd)
1158 {
1159    Eina_Bool int_ret = EINA_FALSE;
1160
1161    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_disable());
1162    if (!int_ret) return EINA_FALSE;
1163    if (sd->list)
1164      elm_object_disabled_set(sd->list, elm_widget_disabled_get(obj));
1165    else if (sd->content)
1166      elm_object_disabled_set(sd->content, elm_widget_disabled_get(obj));
1167
1168    return EINA_TRUE;
1169 }
1170
1171 EOLIAN static void
1172 _elm_ctxpopup_evas_object_smart_add(Eo *obj, Elm_Ctxpopup_Data *priv)
1173 {
1174    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
1175
1176    eo_do_super(obj, MY_CLASS, evas_obj_smart_add());
1177    elm_widget_sub_object_parent_add(obj);
1178
1179    if (!elm_layout_theme_set
1180        (obj, "ctxpopup", "base", elm_widget_style_get(obj)))
1181      CRI("Failed to set layout!");
1182
1183    elm_layout_signal_callback_add
1184      (obj, "elm,action,hide,finished", "*", _hide_finished_cb, obj);
1185
1186    //Background
1187    priv->bg = edje_object_add(evas_object_evas_get(obj));
1188    /* TIZEN_ONLY(20161031): apply color_class parent-child relationship to all widgets */
1189    _elm_widget_color_class_parent_set(priv->bg, obj);
1190    /* END */
1191    elm_widget_theme_object_set(obj, priv->bg, "ctxpopup", "bg", "default");
1192    edje_object_signal_callback_add
1193      (priv->bg, "elm,action,click", "*", _bg_clicked_cb, obj);
1194    evas_object_smart_member_add(priv->bg, obj);
1195    evas_object_stack_below(priv->bg, wd->resize_obj);
1196
1197    //Arrow
1198    priv->arrow = edje_object_add(evas_object_evas_get(obj));
1199    /* TIZEN_ONLY(20161031): apply color_class parent-child relationship to all widgets */
1200    _elm_widget_color_class_parent_set(priv->arrow, obj);
1201    /* END */
1202    elm_widget_theme_object_set
1203      (obj, priv->arrow, "ctxpopup", "arrow", "default");
1204    evas_object_smart_member_add(priv->arrow, obj);
1205
1206    priv->dir_priority[0] = ELM_CTXPOPUP_DIRECTION_UP;
1207    priv->dir_priority[1] = ELM_CTXPOPUP_DIRECTION_LEFT;
1208    priv->dir_priority[2] = ELM_CTXPOPUP_DIRECTION_RIGHT;
1209    priv->dir_priority[3] = ELM_CTXPOPUP_DIRECTION_DOWN;
1210    priv->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1211    priv->auto_hide = EINA_TRUE;
1212
1213    priv->box = elm_box_add(obj);
1214    evas_object_size_hint_weight_set
1215      (priv->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1216
1217    evas_object_event_callback_add
1218      (priv->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1219
1220    /* box will be our content placeholder, thus the parent's version call */
1221    eo_do_super(obj, MY_CLASS, elm_obj_container_content_set("elm.swallow.content", priv->box));
1222
1223    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show, NULL);
1224    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide, NULL);
1225    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _on_move, NULL);
1226
1227    elm_widget_can_focus_set(obj, EINA_TRUE);
1228 }
1229
1230 EOLIAN static void
1231 _elm_ctxpopup_evas_object_smart_del(Eo *obj, Elm_Ctxpopup_Data *sd)
1232 {
1233    Elm_Object_Item *it;
1234
1235    evas_object_event_callback_del_full
1236      (sd->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1237    _parent_detach(obj);
1238
1239    elm_ctxpopup_clear(obj);
1240    ELM_SAFE_FREE(sd->arrow, evas_object_del); /* stops _sizing_eval() from going on on deletion */
1241    ELM_SAFE_FREE(sd->bg, evas_object_del);
1242
1243    EINA_LIST_FREE(sd->items, it)
1244      eo_del(it);
1245
1246    eo_do_super(obj, MY_CLASS, evas_obj_smart_del());
1247 }
1248
1249 EOLIAN static void
1250 _elm_ctxpopup_elm_widget_parent_set(Eo *obj, Elm_Ctxpopup_Data *_pd EINA_UNUSED, Evas_Object *parent)
1251 {
1252    //default parent is to be hover parent
1253    elm_ctxpopup_hover_parent_set(obj, parent);
1254 }
1255
1256 EAPI Evas_Object *
1257 elm_ctxpopup_add(Evas_Object *parent)
1258 {
1259    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1260    Evas_Object *obj = eo_add(MY_CLASS, parent);
1261
1262    /* access: parent could be any object such as elm_list which does
1263       not know elc_ctxpopup as its child object in the focus_next(); */
1264    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL);
1265    wd->highlight_root = EINA_TRUE;
1266
1267    return obj;
1268 }
1269
1270 EOLIAN static Eo *
1271 _elm_ctxpopup_eo_base_constructor(Eo *obj, Elm_Ctxpopup_Data *_pd EINA_UNUSED)
1272 {
1273    obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor());
1274    eo_do(obj,
1275          evas_obj_type_set(MY_CLASS_NAME_LEGACY),
1276          evas_obj_smart_callbacks_descriptions_set(_smart_callbacks),
1277          elm_interface_atspi_accessible_role_set(ELM_ATSPI_ROLE_POPUP_MENU));
1278
1279    return obj;
1280 }
1281
1282 EOLIAN static void
1283 _elm_ctxpopup_hover_parent_set(Eo *obj, Elm_Ctxpopup_Data *sd, Evas_Object *parent)
1284 {
1285    Evas_Coord x, y, w, h;
1286
1287    if (!parent) return;
1288
1289    _parent_detach(obj);
1290
1291    evas_object_event_callback_add
1292      (parent, EVAS_CALLBACK_DEL, _on_parent_del, obj);
1293    evas_object_event_callback_add
1294      (parent, EVAS_CALLBACK_MOVE, _on_parent_move, obj);
1295    evas_object_event_callback_add
1296      (parent, EVAS_CALLBACK_RESIZE, _on_parent_resize, obj);
1297
1298    sd->parent = parent;
1299
1300    //Update Background
1301    evas_object_geometry_get(parent, &x, &y, &w, &h);
1302    if (parent && eo_isa(parent, ELM_WIN_CLASS))
1303      x = y = 0;
1304    evas_object_move(sd->bg, x, y);
1305    evas_object_resize(sd->bg, w, h);
1306
1307    if (sd->visible) elm_layout_sizing_eval(obj);
1308 }
1309
1310 EOLIAN static Evas_Object*
1311 _elm_ctxpopup_hover_parent_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1312 {
1313    return sd->parent;
1314 }
1315
1316 EOLIAN static void
1317 _elm_ctxpopup_clear(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1318 {
1319    _list_del(sd);
1320    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1321 }
1322
1323 EOLIAN static void
1324 _elm_ctxpopup_horizontal_set(Eo *obj, Elm_Ctxpopup_Data *sd, Eina_Bool horizontal)
1325 {
1326    sd->horizontal = !!horizontal;
1327
1328    if (!sd->list) return;
1329
1330    elm_list_horizontal_set(sd->list, sd->horizontal);
1331
1332    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1333
1334    if (sd->visible) elm_layout_sizing_eval(obj);
1335 }
1336
1337 EOLIAN static Eina_Bool
1338 _elm_ctxpopup_horizontal_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1339 {
1340    return sd->horizontal;
1341 }
1342
1343 static void
1344 _item_wrap_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
1345 {
1346    Elm_Ctxpopup_Item_Data *item = data;
1347    if (!item->wcb.org_func_cb) return;
1348    item->wcb.org_func_cb((void *)item->wcb.org_data, item->wcb.cobj, EO_OBJ(item));
1349 }
1350
1351 EOLIAN static Eo *
1352 _elm_ctxpopup_item_eo_base_constructor(Eo *obj, Elm_Ctxpopup_Item_Data *it)
1353 {
1354    obj = eo_do_super_ret(obj, ELM_CTXPOPUP_ITEM_CLASS, obj, eo_constructor());
1355    it->base = eo_data_scope_get(obj, ELM_WIDGET_ITEM_CLASS);
1356    //TIZEN ONLY(20150710)ctxpopup: Accessible methods for children_get, extents_get and item name_get
1357    eo_do(obj, elm_interface_atspi_accessible_role_set(ELM_ATSPI_ROLE_LIST_ITEM));
1358    //
1359    return obj;
1360 }
1361
1362 EOLIAN static Elm_Object_Item*
1363 _elm_ctxpopup_item_append(Eo *obj, Elm_Ctxpopup_Data *sd, const char *label, Evas_Object *icon, Evas_Smart_Cb func, const void *data)
1364 {
1365    Eo *eo_item;
1366
1367    eo_item = eo_add(ELM_CTXPOPUP_ITEM_CLASS, obj, elm_obj_ctxpopup_item_init(func, data));
1368    if (!eo_item) return NULL;
1369
1370    ELM_CTXPOPUP_ITEM_DATA_GET(eo_item, item);
1371
1372    item->list_item =
1373      elm_list_item_append(sd->list, label, icon, NULL, _item_wrap_cb, item);
1374    eo_ref(item->list_item);
1375    sd->items = eina_list_append(sd->items, eo_item);
1376
1377    if (sd->visible) elm_layout_sizing_eval(obj);
1378
1379    return eo_item;
1380 }
1381
1382 EOLIAN static void
1383 _elm_ctxpopup_direction_priority_set(Eo *obj, Elm_Ctxpopup_Data *sd, Elm_Ctxpopup_Direction first, Elm_Ctxpopup_Direction second, Elm_Ctxpopup_Direction third, Elm_Ctxpopup_Direction fourth)
1384 {
1385    sd->dir_priority[0] = first;
1386    sd->dir_priority[1] = second;
1387    sd->dir_priority[2] = third;
1388    sd->dir_priority[3] = fourth;
1389
1390    if (sd->visible) elm_layout_sizing_eval(obj);
1391 }
1392
1393 EOLIAN static void
1394 _elm_ctxpopup_direction_priority_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd, Elm_Ctxpopup_Direction *first, Elm_Ctxpopup_Direction *second, Elm_Ctxpopup_Direction *third, Elm_Ctxpopup_Direction *fourth)
1395 {
1396    if (first) *first = sd->dir_priority[0];
1397    if (second) *second = sd->dir_priority[1];
1398    if (third) *third = sd->dir_priority[2];
1399    if (fourth) *fourth = sd->dir_priority[3];
1400 }
1401
1402 EOLIAN static Elm_Ctxpopup_Direction
1403 _elm_ctxpopup_direction_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1404 {
1405    return sd->dir;
1406 }
1407
1408 // TIZEN_ONLY(20160126): add direction available get function, it will be deprecated soon
1409 EINA_DEPRECATED EAPI Eina_Bool
1410 elm_ctxpopup_direction_available_get(Evas_Object *obj, Elm_Ctxpopup_Direction direction)
1411 {
1412    ELM_CTXPOPUP_CHECK(obj) EINA_FALSE;
1413    ELM_CTXPOPUP_DATA_GET(obj, sd);
1414
1415    elm_layout_sizing_eval(obj);
1416
1417    if (sd->dir == direction) return EINA_TRUE;
1418    return EINA_FALSE;
1419 }
1420 //
1421
1422 EOLIAN static void
1423 _elm_ctxpopup_dismiss(Eo *obj, Elm_Ctxpopup_Data *sd)
1424 {
1425    _hide_signals_emit(obj, sd->dir);
1426 }
1427
1428 EOLIAN static void
1429 _elm_ctxpopup_auto_hide_disabled_set(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd, Eina_Bool disabled)
1430 {
1431    disabled = !!disabled;
1432    if (sd->auto_hide == !disabled) return;
1433    sd->auto_hide = !disabled;
1434 }
1435
1436 EOLIAN static Eina_Bool
1437 _elm_ctxpopup_auto_hide_disabled_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1438 {
1439    return !sd->auto_hide;
1440 }
1441
1442 EOLIAN static void
1443 _elm_ctxpopup_class_constructor(Eo_Class *klass)
1444 {
1445    evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
1446 }
1447
1448 EOLIAN static const Eina_List*
1449 _elm_ctxpopup_items_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1450 {
1451    return sd->items;
1452 }
1453
1454 EOLIAN static Elm_Object_Item*
1455 _elm_ctxpopup_first_item_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1456 {
1457    if (!sd->items) return NULL;
1458
1459    return eina_list_data_get(sd->items);
1460 }
1461
1462 EOLIAN static Elm_Object_Item*
1463 _elm_ctxpopup_last_item_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1464 {
1465    if (!sd->items) return NULL;
1466
1467    return eina_list_data_get(eina_list_last(sd->items));
1468 }
1469
1470 EOLIAN static Elm_Object_Item*
1471 _elm_ctxpopup_item_prepend(Eo *obj, Elm_Ctxpopup_Data *sd, const char *label, Evas_Object *icon, Evas_Smart_Cb func, const void *data)
1472 {
1473    Eo *eo_item;
1474
1475    eo_item = eo_add(ELM_CTXPOPUP_ITEM_CLASS, obj, elm_obj_ctxpopup_item_init(func, data));
1476    if (!eo_item) return NULL;
1477
1478    ELM_CTXPOPUP_ITEM_DATA_GET(eo_item, item);
1479
1480    item->list_item =
1481      elm_list_item_prepend(sd->list, label, icon, NULL, _item_wrap_cb, item);
1482    eo_ref(item->list_item);
1483    sd->items = eina_list_prepend(sd->items, eo_item);
1484
1485    if (sd->visible) elm_layout_sizing_eval(obj);
1486
1487    return eo_item;
1488 }
1489
1490 EOLIAN static Elm_Object_Item *
1491 _elm_ctxpopup_item_prev_get(Eo *eo_item EINA_UNUSED, Elm_Ctxpopup_Item_Data *item)
1492 {
1493    Eina_List *l;
1494
1495    ELM_CTXPOPUP_DATA_GET(WIDGET(item), sd);
1496
1497    if (!sd->items) return NULL;
1498
1499    l = eina_list_data_find_list(sd->items, eo_item);
1500    if (l && l->prev) return eina_list_data_get(l->prev);
1501
1502    return NULL;
1503 }
1504
1505 EOLIAN static Elm_Object_Item *
1506 _elm_ctxpopup_item_next_get(Eo *eo_item EINA_UNUSED, Elm_Ctxpopup_Item_Data *item)
1507 {
1508    Eina_List *l;
1509
1510    ELM_CTXPOPUP_DATA_GET(WIDGET(item), sd);
1511
1512    if (!sd->items) return NULL;
1513
1514    l = eina_list_data_find_list(sd->items, eo_item);
1515    if (l && l->next) return eina_list_data_get(l->next);
1516
1517    return NULL;
1518 }
1519
1520 EOLIAN static void
1521 _elm_ctxpopup_item_init(Eo *eo_item,
1522           Elm_Ctxpopup_Item_Data *item,
1523           Evas_Smart_Cb func,
1524           const void *data)
1525 {
1526    Eo *obj;
1527    eo_do(eo_item, obj = eo_parent_get());
1528    Elm_Ctxpopup_Data *sd = eo_data_scope_get(obj, ELM_CTXPOPUP_CLASS);
1529    if (!sd->list)
1530      {
1531         sd->list = elm_list_add(obj);
1532         if (!strncmp(elm_object_style_get(obj), "default", strlen("default")))
1533           elm_object_style_set(sd->list, "ctxpopup");
1534         else elm_object_style_set(sd->list, elm_object_style_get(obj));
1535         elm_list_mode_set(sd->list, ELM_LIST_EXPAND);
1536         elm_list_horizontal_set(sd->list, sd->horizontal);
1537         evas_object_event_callback_add
1538           (sd->list, EVAS_CALLBACK_RESIZE, _list_resize_cb, obj);
1539         elm_layout_content_set(obj, "default", sd->list);
1540      }
1541
1542    item->wcb.org_func_cb = func;
1543    item->wcb.org_data = data;
1544    item->wcb.cobj = obj;
1545
1546    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1547 }
1548
1549 EOLIAN static const Elm_Atspi_Action*
1550 _elm_ctxpopup_elm_interface_atspi_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd EINA_UNUSED)
1551 {
1552    static Elm_Atspi_Action atspi_actions[] = {
1553           { "escape", "escape", NULL, _key_action_escape},
1554           { "move,previous", "move", "previous", _key_action_move},
1555           { "move,next", "move", "next", _key_action_move},
1556           { "move,left", "move", "left", _key_action_move},
1557           { "move,right", "move", "right", _key_action_move},
1558           { "move,up", "move", "up", _key_action_move},
1559           { "move,down", "move", "down", _key_action_move},
1560           { NULL, NULL, NULL, NULL }
1561    };
1562    return &atspi_actions[0];
1563 }
1564
1565 EOLIAN static Elm_Atspi_State_Set
1566 _elm_ctxpopup_elm_interface_atspi_accessible_state_set_get(Eo *obj, Elm_Ctxpopup_Data *sd EINA_UNUSED)
1567 {
1568    Elm_Atspi_State_Set ret;
1569    eo_do_super(obj, MY_CLASS, ret = elm_interface_atspi_accessible_state_set_get());
1570
1571    STATE_TYPE_SET(ret, ELM_ATSPI_STATE_MODAL);
1572
1573    return ret;
1574 }
1575
1576 // TIZEN_ONLY(20170116): merge eo & header files for different profiles
1577 EOLIAN static Eina_List *
1578 _elm_ctxpopup_elm_interface_atspi_accessible_children_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd EINA_UNUSED)
1579 {
1580    // mobile & wearable only feature
1581    return NULL;
1582 }
1583
1584 EOLIAN static const char*
1585 _elm_ctxpopup_elm_interface_atspi_accessible_name_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd EINA_UNUSED)
1586 {
1587    // mobile & wearable only feature
1588    return NULL;
1589 }
1590 //
1591
1592 //TIZEN ONLY(20150710): ctxpopup: Accessible methods for children_get, extents_get and item name_get
1593 EOLIAN static void
1594 _elm_ctxpopup_elm_interface_atspi_component_extents_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd, Eina_Bool screen_coords, int *x, int *y, int *w, int *h)
1595 {
1596    int ee_x, ee_y;
1597
1598    if (!sd->box)
1599      {
1600         if (x) *x = -1;
1601         if (y) *y = -1;
1602         if (w) *w = -1;
1603         if (h) *h = -1;
1604         return;
1605      }
1606    evas_object_geometry_get(sd->box, x, y, w, h);
1607
1608    if (screen_coords)
1609      {
1610         Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(sd->box));
1611         if (!ee) return;
1612         ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
1613         if (x) *x += ee_x;
1614         if (y) *y += ee_y;
1615      }
1616 }
1617 //
1618
1619 static Eina_Bool
1620 _item_action_activate(Evas_Object *obj, const char *params EINA_UNUSED)
1621 {
1622    ELM_CTXPOPUP_ITEM_DATA_GET(obj, item);
1623
1624    if (item->wcb.org_func_cb)
1625      item->wcb.org_func_cb((void*)item->wcb.org_data, WIDGET(item), EO_OBJ(item));
1626    return EINA_TRUE;
1627 }
1628
1629 EOLIAN static const Elm_Atspi_Action*
1630 _elm_ctxpopup_item_elm_interface_atspi_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Item_Data *sd EINA_UNUSED)
1631 {
1632    static Elm_Atspi_Action atspi_actions[] = {
1633           { "activate", "activate", NULL, _item_action_activate},
1634           { NULL, NULL, NULL, NULL }
1635    };
1636    return &atspi_actions[0];
1637 }
1638
1639 //TIZEN ONLY(20150708): popup and ctxpopup accessibility highlight impementation
1640 EOLIAN static Eina_Bool
1641 _elm_ctxpopup_elm_interface_atspi_component_highlight_grab(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1642 {
1643    if (sd->box)
1644      {
1645         elm_object_accessibility_highlight_set(sd->box, EINA_TRUE);
1646         return EINA_TRUE;
1647      }
1648    return EINA_FALSE;
1649 }
1650
1651 EOLIAN static Eina_Bool
1652 _elm_ctxpopup_elm_interface_atspi_component_highlight_clear(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1653 {
1654    if (sd->box)
1655      {
1656         elm_object_accessibility_highlight_set(sd->box, EINA_FALSE);
1657         return EINA_TRUE;
1658      }
1659    return EINA_FALSE;
1660 }
1661 //
1662
1663 //TIZEN ONLY(20150710)ctxpopup: Accessible methods for children_get, extents_get and item name_get
1664 EOLIAN const char *
1665 _elm_ctxpopup_item_elm_interface_atspi_accessible_name_get(Eo *eo_it EINA_UNUSED, Elm_Ctxpopup_Item_Data *item)
1666 {
1667    return elm_object_item_text_get(item->list_item);
1668 }
1669 //
1670
1671 #include "elm_ctxpopup_item.eo.c"
1672 #include "elm_ctxpopup.eo.c"