2383897535d603de9803d379a65470c1c2338fb0
[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,
961                                                const char *emission,
962                                                const char *source)
963 {
964    elm_object_item_signal_emit(ctxpopup_it->list_item, emission, source);
965 }
966
967 EOLIAN static void
968 _elm_ctxpopup_item_elm_widget_item_focus_set(Eo *eo_ctxpopup_it EINA_UNUSED,
969                                              Elm_Ctxpopup_Item_Data *ctxpopup_it,
970                                              Eina_Bool focused)
971 {
972    elm_object_item_focus_set(ctxpopup_it->list_item, focused);
973 }
974
975 EOLIAN static Eina_Bool
976 _elm_ctxpopup_item_elm_widget_item_focus_get(Eo *eo_ctxpopup_it EINA_UNUSED,
977                                              Elm_Ctxpopup_Item_Data *ctxpopup_it)
978 {
979    return elm_object_item_focus_get(ctxpopup_it->list_item);
980 }
981
982 static void
983 _bg_clicked_cb(void *data,
984                Evas_Object *obj EINA_UNUSED,
985                const char *emission EINA_UNUSED,
986                const char *source EINA_UNUSED)
987 {
988    ELM_CTXPOPUP_DATA_GET(data, sd);
989
990    if (sd->auto_hide)
991      _hide_signals_emit(data, sd->dir);
992 }
993
994 static void
995 _on_show(void *data EINA_UNUSED,
996          Evas *e EINA_UNUSED,
997          Evas_Object *obj,
998          void *event_info EINA_UNUSED)
999 {
1000    ELM_CTXPOPUP_DATA_GET(obj, sd);
1001
1002    if (sd->list)
1003      {
1004         elm_list_go(sd->list);
1005         sd->visible = EINA_TRUE;
1006         /*
1007          * XXX: Giving focus to the list when it has nothing selected makes
1008          * it select the first of its items, which makes the popup in
1009          * Terminology never open and instead just trigger the first option.
1010          * I'll let as an exercise to the reader to figure out why that
1011          * is so fucking annoying. Extra points for noting why this is my
1012          * choice of a "fix" instead of fixing the actual focus/select issue
1013          * that seems to be spread all over Elementary.
1014          */
1015         //elm_object_focus_set(sd->list, EINA_TRUE);
1016
1017         //TIZEN_ONLY(20160412): fix 'more' style size problem
1018         elm_layout_sizing_eval(obj);
1019         //
1020
1021         return;
1022      }
1023
1024    if (!sd->content) return;
1025
1026    sd->emitted = EINA_FALSE;
1027    sd->visible = EINA_TRUE;
1028
1029    _show_signals_emit(obj, sd->dir);
1030
1031    elm_layout_sizing_eval(obj);
1032
1033    elm_object_focus_set(obj, EINA_TRUE);
1034 }
1035
1036 static void
1037 _on_hide(void *data EINA_UNUSED,
1038          Evas *e EINA_UNUSED,
1039          Evas_Object *obj,
1040          void *event_info EINA_UNUSED)
1041 {
1042    ELM_CTXPOPUP_DATA_GET(obj, sd);
1043
1044    if (!sd->visible) return;
1045
1046    sd->visible = EINA_FALSE;
1047    sd->list_visible = EINA_FALSE;
1048 }
1049
1050 static void
1051 _on_move(void *data EINA_UNUSED,
1052          Evas *e EINA_UNUSED,
1053          Evas_Object *obj,
1054          void *event_info EINA_UNUSED)
1055 {
1056    elm_layout_sizing_eval(obj);
1057 }
1058
1059 static void
1060 _hide_finished_cb(void *data,
1061                   Evas_Object *obj EINA_UNUSED,
1062                   const char *emission EINA_UNUSED,
1063                   const char *source EINA_UNUSED)
1064 {
1065    evas_object_hide(data);
1066    eo_do(data, eo_event_callback_call(ELM_CTXPOPUP_EVENT_DISMISSED, NULL));
1067 }
1068
1069 static void
1070 _list_resize_cb(void *data,
1071                 Evas *e EINA_UNUSED,
1072                 Evas_Object *obj EINA_UNUSED,
1073                 void *event_info EINA_UNUSED)
1074 {
1075    ELM_CTXPOPUP_DATA_GET(data, sd);
1076
1077    if (!sd->visible) return;
1078    if (sd->list_visible) return;
1079
1080    sd->list_visible = EINA_TRUE;
1081
1082    elm_layout_sizing_eval(data);
1083 }
1084
1085 static void
1086 _list_del(Elm_Ctxpopup_Data *sd)
1087 {
1088    ELM_SAFE_FREE(sd->list, evas_object_del);
1089 }
1090
1091 EOLIAN static void
1092 _elm_ctxpopup_item_eo_base_destructor(Eo *eo_ctxpopup_it,
1093                                       Elm_Ctxpopup_Item_Data *ctxpopup_it)
1094 {
1095    Evas_Object *list;
1096
1097    ELM_CTXPOPUP_DATA_GET(WIDGET(ctxpopup_it), sd);
1098
1099    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1100
1101    list = elm_object_item_widget_get(ctxpopup_it->list_item);
1102
1103    if (ctxpopup_it->list_item)
1104       eo_unref(ctxpopup_it->list_item);
1105    sd->items = eina_list_remove(sd->items, eo_ctxpopup_it);
1106    if (list && eina_list_count(elm_list_items_get(list)) < 2)
1107      {
1108         evas_object_hide(WIDGET(ctxpopup_it));
1109      }
1110    else
1111      {
1112         if (sd->list_visible) elm_layout_sizing_eval(WIDGET(ctxpopup_it));
1113      }
1114
1115    eo_do_super(eo_ctxpopup_it, ELM_CTXPOPUP_ITEM_CLASS, eo_destructor());
1116 }
1117
1118 EOLIAN static Eina_Bool
1119 _elm_ctxpopup_elm_widget_disable(Eo *obj, Elm_Ctxpopup_Data *sd)
1120 {
1121    Eina_Bool int_ret = EINA_FALSE;
1122
1123    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_disable());
1124    if (!int_ret) return EINA_FALSE;
1125    if (sd->list)
1126      elm_object_disabled_set(sd->list, elm_widget_disabled_get(obj));
1127    else if (sd->content)
1128      elm_object_disabled_set(sd->content, elm_widget_disabled_get(obj));
1129
1130    return EINA_TRUE;
1131 }
1132
1133 EOLIAN static void
1134 _elm_ctxpopup_evas_object_smart_add(Eo *obj, Elm_Ctxpopup_Data *priv)
1135 {
1136    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
1137
1138    eo_do_super(obj, MY_CLASS, evas_obj_smart_add());
1139    elm_widget_sub_object_parent_add(obj);
1140
1141    if (!elm_layout_theme_set
1142        (obj, "ctxpopup", "base", elm_widget_style_get(obj)))
1143      CRI("Failed to set layout!");
1144
1145    elm_layout_signal_callback_add
1146      (obj, "elm,action,hide,finished", "*", _hide_finished_cb, obj);
1147
1148    //Background
1149    priv->bg = edje_object_add(evas_object_evas_get(obj));
1150    /* TIZEN_ONLY(20161031): apply color_class parent-child relationship to all widgets */
1151    _elm_widget_color_class_parent_set(priv->bg, obj);
1152    /* END */
1153    elm_widget_theme_object_set(obj, priv->bg, "ctxpopup", "bg", "default");
1154    edje_object_signal_callback_add
1155      (priv->bg, "elm,action,click", "*", _bg_clicked_cb, obj);
1156    evas_object_smart_member_add(priv->bg, obj);
1157    evas_object_stack_below(priv->bg, wd->resize_obj);
1158
1159    //Arrow
1160    priv->arrow = edje_object_add(evas_object_evas_get(obj));
1161    /* TIZEN_ONLY(20161031): apply color_class parent-child relationship to all widgets */
1162    _elm_widget_color_class_parent_set(priv->arrow, obj);
1163    /* END */
1164    elm_widget_theme_object_set
1165      (obj, priv->arrow, "ctxpopup", "arrow", "default");
1166    evas_object_smart_member_add(priv->arrow, obj);
1167
1168    priv->dir_priority[0] = ELM_CTXPOPUP_DIRECTION_UP;
1169    priv->dir_priority[1] = ELM_CTXPOPUP_DIRECTION_LEFT;
1170    priv->dir_priority[2] = ELM_CTXPOPUP_DIRECTION_RIGHT;
1171    priv->dir_priority[3] = ELM_CTXPOPUP_DIRECTION_DOWN;
1172    priv->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1173    priv->auto_hide = EINA_TRUE;
1174
1175    priv->box = elm_box_add(obj);
1176    evas_object_size_hint_weight_set
1177      (priv->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1178
1179    evas_object_event_callback_add
1180      (priv->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1181
1182    /* box will be our content placeholder, thus the parent's version call */
1183    eo_do_super(obj, MY_CLASS, elm_obj_container_content_set("elm.swallow.content", priv->box));
1184
1185    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show, NULL);
1186    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide, NULL);
1187    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _on_move, NULL);
1188
1189    elm_widget_can_focus_set(obj, EINA_TRUE);
1190 }
1191
1192 EOLIAN static void
1193 _elm_ctxpopup_evas_object_smart_del(Eo *obj, Elm_Ctxpopup_Data *sd)
1194 {
1195    Elm_Object_Item *it;
1196
1197    evas_object_event_callback_del_full
1198      (sd->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1199    _parent_detach(obj);
1200
1201    elm_ctxpopup_clear(obj);
1202    ELM_SAFE_FREE(sd->arrow, evas_object_del); /* stops _sizing_eval() from going on on deletion */
1203    ELM_SAFE_FREE(sd->bg, evas_object_del);
1204
1205    EINA_LIST_FREE(sd->items, it)
1206      eo_del(it);
1207
1208    eo_do_super(obj, MY_CLASS, evas_obj_smart_del());
1209 }
1210
1211 EOLIAN static void
1212 _elm_ctxpopup_elm_widget_parent_set(Eo *obj, Elm_Ctxpopup_Data *_pd EINA_UNUSED, Evas_Object *parent)
1213 {
1214    //default parent is to be hover parent
1215    elm_ctxpopup_hover_parent_set(obj, parent);
1216 }
1217
1218 EAPI Evas_Object *
1219 elm_ctxpopup_add(Evas_Object *parent)
1220 {
1221    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1222    Evas_Object *obj = eo_add(MY_CLASS, parent);
1223
1224    /* access: parent could be any object such as elm_list which does
1225       not know elc_ctxpopup as its child object in the focus_next(); */
1226    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL);
1227    wd->highlight_root = EINA_TRUE;
1228
1229    return obj;
1230 }
1231
1232 EOLIAN static Eo *
1233 _elm_ctxpopup_eo_base_constructor(Eo *obj, Elm_Ctxpopup_Data *_pd EINA_UNUSED)
1234 {
1235    obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor());
1236    eo_do(obj,
1237          evas_obj_type_set(MY_CLASS_NAME_LEGACY),
1238          evas_obj_smart_callbacks_descriptions_set(_smart_callbacks),
1239          elm_interface_atspi_accessible_role_set(ELM_ATSPI_ROLE_POPUP_MENU));
1240
1241    return obj;
1242 }
1243
1244 EOLIAN static void
1245 _elm_ctxpopup_hover_parent_set(Eo *obj, Elm_Ctxpopup_Data *sd, Evas_Object *parent)
1246 {
1247    Evas_Coord x, y, w, h;
1248
1249    if (!parent) return;
1250
1251    _parent_detach(obj);
1252
1253    evas_object_event_callback_add
1254      (parent, EVAS_CALLBACK_DEL, _on_parent_del, obj);
1255    evas_object_event_callback_add
1256      (parent, EVAS_CALLBACK_MOVE, _on_parent_move, obj);
1257    evas_object_event_callback_add
1258      (parent, EVAS_CALLBACK_RESIZE, _on_parent_resize, obj);
1259
1260    sd->parent = parent;
1261
1262    //Update Background
1263    evas_object_geometry_get(parent, &x, &y, &w, &h);
1264    if (parent && eo_isa(parent, ELM_WIN_CLASS))
1265      x = y = 0;
1266    evas_object_move(sd->bg, x, y);
1267    evas_object_resize(sd->bg, w, h);
1268
1269    if (sd->visible) elm_layout_sizing_eval(obj);
1270 }
1271
1272 EOLIAN static Evas_Object*
1273 _elm_ctxpopup_hover_parent_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1274 {
1275    return sd->parent;
1276 }
1277
1278 EOLIAN static void
1279 _elm_ctxpopup_clear(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1280 {
1281    _list_del(sd);
1282    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1283 }
1284
1285 EOLIAN static void
1286 _elm_ctxpopup_horizontal_set(Eo *obj, Elm_Ctxpopup_Data *sd, Eina_Bool horizontal)
1287 {
1288    sd->horizontal = !!horizontal;
1289
1290    if (!sd->list) return;
1291
1292    elm_list_horizontal_set(sd->list, sd->horizontal);
1293
1294    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1295
1296    if (sd->visible) elm_layout_sizing_eval(obj);
1297 }
1298
1299 EOLIAN static Eina_Bool
1300 _elm_ctxpopup_horizontal_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1301 {
1302    return sd->horizontal;
1303 }
1304
1305 static void
1306 _item_wrap_cb(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
1307 {
1308    Elm_Ctxpopup_Item_Data *item = data;
1309    if (!item->wcb.org_func_cb) return;
1310    item->wcb.org_func_cb((void *)item->wcb.org_data, item->wcb.cobj, EO_OBJ(item));
1311 }
1312
1313 EOLIAN static Eo *
1314 _elm_ctxpopup_item_eo_base_constructor(Eo *obj, Elm_Ctxpopup_Item_Data *it)
1315 {
1316    obj = eo_do_super_ret(obj, ELM_CTXPOPUP_ITEM_CLASS, obj, eo_constructor());
1317    it->base = eo_data_scope_get(obj, ELM_WIDGET_ITEM_CLASS);
1318    //TIZEN ONLY(20150710)ctxpopup: Accessible methods for children_get, extents_get and item name_get
1319    eo_do(obj, elm_interface_atspi_accessible_role_set(ELM_ATSPI_ROLE_LIST_ITEM));
1320    //
1321    return obj;
1322 }
1323
1324 EOLIAN static Elm_Object_Item*
1325 _elm_ctxpopup_item_append(Eo *obj, Elm_Ctxpopup_Data *sd, const char *label, Evas_Object *icon, Evas_Smart_Cb func, const void *data)
1326 {
1327    Eo *eo_item;
1328
1329    eo_item = eo_add(ELM_CTXPOPUP_ITEM_CLASS, obj, elm_obj_ctxpopup_item_init(func, data));
1330    if (!eo_item) return NULL;
1331
1332    ELM_CTXPOPUP_ITEM_DATA_GET(eo_item, item);
1333
1334    item->list_item =
1335      elm_list_item_append(sd->list, label, icon, NULL, _item_wrap_cb, item);
1336    eo_ref(item->list_item);
1337    sd->items = eina_list_append(sd->items, eo_item);
1338
1339    if (sd->visible) elm_layout_sizing_eval(obj);
1340
1341    return eo_item;
1342 }
1343
1344 EOLIAN static void
1345 _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)
1346 {
1347    sd->dir_priority[0] = first;
1348    sd->dir_priority[1] = second;
1349    sd->dir_priority[2] = third;
1350    sd->dir_priority[3] = fourth;
1351
1352    if (sd->visible) elm_layout_sizing_eval(obj);
1353 }
1354
1355 EOLIAN static void
1356 _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)
1357 {
1358    if (first) *first = sd->dir_priority[0];
1359    if (second) *second = sd->dir_priority[1];
1360    if (third) *third = sd->dir_priority[2];
1361    if (fourth) *fourth = sd->dir_priority[3];
1362 }
1363
1364 EOLIAN static Elm_Ctxpopup_Direction
1365 _elm_ctxpopup_direction_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1366 {
1367    return sd->dir;
1368 }
1369
1370 // TIZEN_ONLY(20160126): add direction available get function, it will be deprecated soon
1371 EINA_DEPRECATED EAPI Eina_Bool
1372 elm_ctxpopup_direction_available_get(Evas_Object *obj, Elm_Ctxpopup_Direction direction)
1373 {
1374    ELM_CTXPOPUP_CHECK(obj) EINA_FALSE;
1375    ELM_CTXPOPUP_DATA_GET(obj, sd);
1376
1377    elm_layout_sizing_eval(obj);
1378
1379    if (sd->dir == direction) return EINA_TRUE;
1380    return EINA_FALSE;
1381 }
1382 //
1383
1384 EOLIAN static void
1385 _elm_ctxpopup_dismiss(Eo *obj, Elm_Ctxpopup_Data *sd)
1386 {
1387    _hide_signals_emit(obj, sd->dir);
1388 }
1389
1390 EOLIAN static void
1391 _elm_ctxpopup_auto_hide_disabled_set(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd, Eina_Bool disabled)
1392 {
1393    disabled = !!disabled;
1394    if (sd->auto_hide == !disabled) return;
1395    sd->auto_hide = !disabled;
1396 }
1397
1398 EOLIAN static Eina_Bool
1399 _elm_ctxpopup_auto_hide_disabled_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1400 {
1401    return !sd->auto_hide;
1402 }
1403
1404 EOLIAN static void
1405 _elm_ctxpopup_class_constructor(Eo_Class *klass)
1406 {
1407    evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
1408 }
1409
1410 EOLIAN static const Eina_List*
1411 _elm_ctxpopup_items_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1412 {
1413    return sd->items;
1414 }
1415
1416 EOLIAN static Elm_Object_Item*
1417 _elm_ctxpopup_first_item_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1418 {
1419    if (!sd->items) return NULL;
1420
1421    return eina_list_data_get(sd->items);
1422 }
1423
1424 EOLIAN static Elm_Object_Item*
1425 _elm_ctxpopup_last_item_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1426 {
1427    if (!sd->items) return NULL;
1428
1429    return eina_list_data_get(eina_list_last(sd->items));
1430 }
1431
1432 EOLIAN static Elm_Object_Item*
1433 _elm_ctxpopup_item_prepend(Eo *obj, Elm_Ctxpopup_Data *sd, const char *label, Evas_Object *icon, Evas_Smart_Cb func, const void *data)
1434 {
1435    Eo *eo_item;
1436
1437    eo_item = eo_add(ELM_CTXPOPUP_ITEM_CLASS, obj, elm_obj_ctxpopup_item_init(func, data));
1438    if (!eo_item) return NULL;
1439
1440    ELM_CTXPOPUP_ITEM_DATA_GET(eo_item, item);
1441
1442    item->list_item =
1443      elm_list_item_prepend(sd->list, label, icon, NULL, _item_wrap_cb, item);
1444    eo_ref(item->list_item);
1445    sd->items = eina_list_prepend(sd->items, eo_item);
1446
1447    if (sd->visible) elm_layout_sizing_eval(obj);
1448
1449    return eo_item;
1450 }
1451
1452 EOLIAN static Elm_Object_Item *
1453 _elm_ctxpopup_item_prev_get(Eo *eo_item EINA_UNUSED, Elm_Ctxpopup_Item_Data *item)
1454 {
1455    Eina_List *l;
1456
1457    ELM_CTXPOPUP_DATA_GET(WIDGET(item), sd);
1458
1459    if (!sd->items) return NULL;
1460
1461    l = eina_list_data_find_list(sd->items, eo_item);
1462    if (l && l->prev) return eina_list_data_get(l->prev);
1463
1464    return NULL;
1465 }
1466
1467 EOLIAN static Elm_Object_Item *
1468 _elm_ctxpopup_item_next_get(Eo *eo_item EINA_UNUSED, Elm_Ctxpopup_Item_Data *item)
1469 {
1470    Eina_List *l;
1471
1472    ELM_CTXPOPUP_DATA_GET(WIDGET(item), sd);
1473
1474    if (!sd->items) return NULL;
1475
1476    l = eina_list_data_find_list(sd->items, eo_item);
1477    if (l && l->next) return eina_list_data_get(l->next);
1478
1479    return NULL;
1480 }
1481
1482 EOLIAN static void
1483 _elm_ctxpopup_item_init(Eo *eo_item,
1484           Elm_Ctxpopup_Item_Data *item,
1485           Evas_Smart_Cb func,
1486           const void *data)
1487 {
1488    Eo *obj;
1489    eo_do(eo_item, obj = eo_parent_get());
1490    Elm_Ctxpopup_Data *sd = eo_data_scope_get(obj, ELM_CTXPOPUP_CLASS);
1491    if (!sd->list)
1492      {
1493         sd->list = elm_list_add(obj);
1494         if (!strncmp(elm_object_style_get(obj), "default", strlen("default")))
1495           elm_object_style_set(sd->list, "ctxpopup");
1496         else elm_object_style_set(sd->list, elm_object_style_get(obj));
1497         elm_list_mode_set(sd->list, ELM_LIST_EXPAND);
1498         elm_list_horizontal_set(sd->list, sd->horizontal);
1499         evas_object_event_callback_add
1500           (sd->list, EVAS_CALLBACK_RESIZE, _list_resize_cb, obj);
1501         elm_layout_content_set(obj, "default", sd->list);
1502      }
1503
1504    item->wcb.org_func_cb = func;
1505    item->wcb.org_data = data;
1506    item->wcb.cobj = obj;
1507
1508    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1509 }
1510
1511 EOLIAN static const Elm_Atspi_Action*
1512 _elm_ctxpopup_elm_interface_atspi_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd EINA_UNUSED)
1513 {
1514    static Elm_Atspi_Action atspi_actions[] = {
1515           { "escape", "escape", NULL, _key_action_escape},
1516           { "move,previous", "move", "previous", _key_action_move},
1517           { "move,next", "move", "next", _key_action_move},
1518           { "move,left", "move", "left", _key_action_move},
1519           { "move,right", "move", "right", _key_action_move},
1520           { "move,up", "move", "up", _key_action_move},
1521           { "move,down", "move", "down", _key_action_move},
1522           { NULL, NULL, NULL, NULL }
1523    };
1524    return &atspi_actions[0];
1525 }
1526
1527 EOLIAN static Elm_Atspi_State_Set
1528 _elm_ctxpopup_elm_interface_atspi_accessible_state_set_get(Eo *obj, Elm_Ctxpopup_Data *sd EINA_UNUSED)
1529 {
1530    Elm_Atspi_State_Set ret;
1531    eo_do_super(obj, MY_CLASS, ret = elm_interface_atspi_accessible_state_set_get());
1532
1533    STATE_TYPE_SET(ret, ELM_ATSPI_STATE_MODAL);
1534
1535    return ret;
1536 }
1537
1538 //TIZEN ONLY(20150710): ctxpopup: Accessible methods for children_get, extents_get and item name_get
1539 EOLIAN static void
1540 _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)
1541 {
1542    int ee_x, ee_y;
1543
1544    if (!sd->box)
1545      {
1546         if (x) *x = -1;
1547         if (y) *y = -1;
1548         if (w) *w = -1;
1549         if (h) *h = -1;
1550         return;
1551      }
1552    evas_object_geometry_get(sd->box, x, y, w, h);
1553
1554    if (screen_coords)
1555      {
1556         Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(sd->box));
1557         if (!ee) return;
1558         ecore_evas_geometry_get(ee, &ee_x, &ee_y, NULL, NULL);
1559         if (x) *x += ee_x;
1560         if (y) *y += ee_y;
1561      }
1562 }
1563 //
1564
1565 static Eina_Bool
1566 _item_action_activate(Evas_Object *obj, const char *params EINA_UNUSED)
1567 {
1568    ELM_CTXPOPUP_ITEM_DATA_GET(obj, item);
1569
1570    if (item->wcb.org_func_cb)
1571      item->wcb.org_func_cb((void*)item->wcb.org_data, WIDGET(item), EO_OBJ(item));
1572    return EINA_TRUE;
1573 }
1574
1575 EOLIAN static const Elm_Atspi_Action*
1576 _elm_ctxpopup_item_elm_interface_atspi_widget_action_elm_actions_get(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Item_Data *sd EINA_UNUSED)
1577 {
1578    static Elm_Atspi_Action atspi_actions[] = {
1579           { "activate", "activate", NULL, _item_action_activate},
1580           { NULL, NULL, NULL, NULL }
1581    };
1582    return &atspi_actions[0];
1583 }
1584
1585 //TIZEN ONLY(20150708): popup and ctxpopup accessibility highlight impementation
1586 EOLIAN static Eina_Bool
1587 _elm_ctxpopup_elm_interface_atspi_component_highlight_grab(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1588 {
1589    if (sd->box)
1590      {
1591         elm_object_accessibility_highlight_set(sd->box, EINA_TRUE);
1592         return EINA_TRUE;
1593      }
1594    return EINA_FALSE;
1595 }
1596
1597 EOLIAN static Eina_Bool
1598 _elm_ctxpopup_elm_interface_atspi_component_highlight_clear(Eo *obj EINA_UNUSED, Elm_Ctxpopup_Data *sd)
1599 {
1600    if (sd->box)
1601      {
1602         elm_object_accessibility_highlight_set(sd->box, EINA_FALSE);
1603         return EINA_TRUE;
1604      }
1605    return EINA_FALSE;
1606 }
1607 //
1608
1609 //TIZEN ONLY(20150710)ctxpopup: Accessible methods for children_get, extents_get and item name_get
1610 EOLIAN const char *
1611 _elm_ctxpopup_item_elm_interface_atspi_accessible_name_get(Eo *eo_it EINA_UNUSED, Elm_Ctxpopup_Item_Data *item)
1612 {
1613    return elm_object_item_text_get(item->list_item);
1614 }
1615 //
1616
1617 #include "elm_ctxpopup_item.eo.c"
1618 #include "elm_ctxpopup.eo.c"