ctxpopup: Fixed ctxpopup geometry when parent is an elm_win. Forwardport from element...
[platform/upstream/elementary.git] / src / lib / elc_ctxpopup.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "elm_widget_ctxpopup.h"
4
5 EAPI Eo_Op ELM_OBJ_CTXPOPUP_BASE_ID = EO_NOOP;
6
7 #define MY_CLASS ELM_OBJ_CTXPOPUP_CLASS
8
9 #define MY_CLASS_NAME "elm_ctxpopup"
10
11 EAPI const char ELM_CTXPOPUP_SMART_NAME[] = "elm_ctxpopup";
12
13 static const char SIG_DISMISSED[] = "dismissed";
14 static const char SIG_LANG_CHANGED[] = "language,changed";
15
16 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
17    {SIG_DISMISSED, ""},
18    {SIG_LANG_CHANGED, ""},
19    {NULL, NULL}
20 };
21
22 static void
23 _elm_ctxpopup_smart_translate(Eo *obj, void *_pd EINA_UNUSED, va_list *list)
24 {
25    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
26    evas_object_hide(obj);
27
28    if (ret) *ret = EINA_TRUE;
29 }
30
31 static void
32 _elm_ctxpopup_smart_focus_next_manager_is(Eo *obj EINA_UNUSED, void *_pd EINA_UNUSED, va_list *list)
33 {
34    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
35    *ret = EINA_TRUE;
36 }
37
38 static void
39 _elm_ctxpopup_smart_focus_direction_manager_is(Eo *obj EINA_UNUSED, void *_pd EINA_UNUSED, va_list *list)
40 {
41    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
42    *ret = EINA_FALSE;
43 }
44
45 static void
46 _elm_ctxpopup_smart_focus_next(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
47 {
48    Elm_Ctxpopup_Smart_Data *sd = _pd;
49
50    Elm_Focus_Direction dir = va_arg(*list, Elm_Focus_Direction);
51    Evas_Object **next =  va_arg(*list, Evas_Object **);
52    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
53    if (ret) *ret = EINA_FALSE;
54
55    if (!sd)
56      return;
57
58    if (!elm_widget_focus_next_get(sd->box, dir, next))
59      {
60         elm_widget_focused_object_clear(sd->box);
61         elm_widget_focus_next_get(sd->box, dir, next);
62      }
63
64    if (ret) *ret = EINA_TRUE;
65 }
66
67 static void
68 _elm_ctxpopup_smart_event(Eo *obj, void *_pd, va_list *list)
69 {
70    Evas_Object *src = va_arg(*list, Evas_Object *);
71    (void)src;
72    Evas_Callback_Type type = va_arg(*list, Evas_Callback_Type);
73    void *event_info = va_arg(*list, void *);
74    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
75    if (ret) *ret = EINA_FALSE;
76
77    Evas_Event_Key_Down *ev = event_info;
78
79    Elm_Ctxpopup_Smart_Data *sd = _pd;
80
81    if (elm_widget_disabled_get(obj)) return;
82    if (type != EVAS_CALLBACK_KEY_DOWN) return;
83    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
84
85    if (!strcmp(ev->keyname, "Tab"))
86      {
87         if (evas_key_modifier_is_set(ev->modifiers, "Shift"))
88           elm_widget_focus_cycle(sd->box, ELM_FOCUS_PREVIOUS);
89         else
90           elm_widget_focus_cycle(sd->box, ELM_FOCUS_NEXT);
91         if (ret) *ret = EINA_TRUE;
92         return;
93      }
94
95    if (strcmp(ev->keyname, "Escape")) return;
96
97    evas_object_hide(obj);
98    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
99    if (ret) *ret = EINA_TRUE;
100 }
101
102 static void
103 _x_pos_adjust(Evas_Coord_Point *pos,
104               Evas_Coord_Point *base_size,
105               Evas_Coord_Rectangle *hover_area)
106 {
107    pos->x -= (base_size->x / 2);
108
109    if (pos->x < hover_area->x)
110      pos->x = hover_area->x;
111    else if ((pos->x + base_size->x) > (hover_area->x + hover_area->w))
112      pos->x = (hover_area->x + hover_area->w) - base_size->x;
113
114    if (base_size->x > hover_area->w)
115      base_size->x -= (base_size->x - hover_area->w);
116
117    if (pos->x < hover_area->x)
118      pos->x = hover_area->x;
119 }
120
121 static void
122 _y_pos_adjust(Evas_Coord_Point *pos,
123               Evas_Coord_Point *base_size,
124               Evas_Coord_Rectangle *hover_area)
125 {
126    pos->y -= (base_size->y / 2);
127
128    if (pos->y < hover_area->y)
129      pos->y = hover_area->y;
130    else if ((pos->y + base_size->y) > (hover_area->y + hover_area->h))
131      pos->y = hover_area->y + hover_area->h - base_size->y;
132
133    if (base_size->y > hover_area->h)
134      base_size->y -= (base_size->y - hover_area->h);
135
136    if (pos->y < hover_area->y)
137      pos->y = hover_area->y;
138 }
139
140 static Elm_Ctxpopup_Direction
141 _base_geometry_calc(Evas_Object *obj,
142                     Evas_Coord_Rectangle *rect)
143 {
144    Elm_Ctxpopup_Direction dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
145    Evas_Coord_Rectangle hover_area;
146    Evas_Coord_Point pos = {0, 0};
147    Evas_Coord_Point arrow_size;
148    Evas_Coord_Point base_size;
149    Evas_Coord_Point max_size;
150    Evas_Coord_Point min_size;
151    Evas_Coord_Point temp;
152    int idx;
153
154    ELM_CTXPOPUP_DATA_GET(obj, sd);
155    Elm_Widget_Smart_Data *wd = eo_data_get(obj, ELM_OBJ_WIDGET_CLASS);
156
157    if (!rect) return ELM_CTXPOPUP_DIRECTION_DOWN;
158
159    edje_object_part_geometry_get
160      (sd->arrow, "ctxpopup_arrow", NULL, NULL, &arrow_size.x, &arrow_size.y);
161    evas_object_resize(sd->arrow, arrow_size.x, arrow_size.y);
162
163    //Initialize Area Rectangle.
164    evas_object_geometry_get
165      (sd->parent, &hover_area.x, &hover_area.y, &hover_area.w,
166      &hover_area.h);
167    if (!strcmp(evas_object_type_get(sd->parent), "elm_win"))
168      hover_area.x = hover_area.y = 0;
169
170    evas_object_geometry_get(obj, &pos.x, &pos.y, NULL, NULL);
171
172    //recalc the edje
173    edje_object_size_min_calc
174      (wd->resize_obj, &base_size.x, &base_size.y);
175    evas_object_smart_calculate(wd->resize_obj);
176
177    //Limit to Max Size
178    evas_object_size_hint_max_get(obj, &max_size.x, &max_size.y);
179
180    if ((max_size.y > 0) && (base_size.y > max_size.y))
181      base_size.y = max_size.y;
182
183    if ((max_size.x > 0) && (base_size.x > max_size.x))
184      base_size.x = max_size.x;
185
186    //Limit to Min Size
187    evas_object_size_hint_min_get(obj, &min_size.x, &min_size.y);
188
189    if ((min_size.y > 0) && (base_size.y < min_size.y))
190      base_size.y = min_size.y;
191
192    if ((min_size.x > 0) && (base_size.x < min_size.x))
193      base_size.x = min_size.x;
194
195    //Check the Which direction is available.
196    //If find a avaialble direction, it adjusts position and size.
197    for (idx = 0; idx < 4; idx++)
198      {
199         switch (sd->dir_priority[idx])
200           {
201            case ELM_CTXPOPUP_DIRECTION_UP:
202              temp.y = (pos.y - base_size.y);
203              if ((temp.y - arrow_size.y) < hover_area.y)
204                continue;
205
206              _x_pos_adjust(&pos, &base_size, &hover_area);
207              pos.y -= base_size.y;
208              dir = ELM_CTXPOPUP_DIRECTION_UP;
209              break;
210
211            case ELM_CTXPOPUP_DIRECTION_LEFT:
212              temp.x = (pos.x - base_size.x);
213              if ((temp.x - arrow_size.x) < hover_area.x)
214                continue;
215
216              _y_pos_adjust(&pos, &base_size, &hover_area);
217              pos.x -= base_size.x;
218              dir = ELM_CTXPOPUP_DIRECTION_LEFT;
219              break;
220
221            case ELM_CTXPOPUP_DIRECTION_RIGHT:
222              temp.x = (pos.x + base_size.x);
223              if ((temp.x + arrow_size.x) >
224                  (hover_area.x + hover_area.w))
225                continue;
226
227              _y_pos_adjust(&pos, &base_size, &hover_area);
228              dir = ELM_CTXPOPUP_DIRECTION_RIGHT;
229              break;
230
231            case ELM_CTXPOPUP_DIRECTION_DOWN:
232              temp.y = (pos.y + base_size.y);
233              if ((temp.y + arrow_size.y) >
234                  (hover_area.y + hover_area.h))
235                continue;
236
237              _x_pos_adjust(&pos, &base_size, &hover_area);
238              dir = ELM_CTXPOPUP_DIRECTION_DOWN;
239              break;
240
241            default:
242              continue;
243           }
244         break;
245      }
246
247    //In this case, all directions are invalid because of lack of space.
248    if (idx == 4)
249      {
250         Evas_Coord length[2];
251
252         if (!sd->horizontal)
253           {
254              length[0] = pos.y - hover_area.y;
255              length[1] = (hover_area.y + hover_area.h) - pos.y;
256
257              // ELM_CTXPOPUP_DIRECTION_UP
258              if (length[0] > length[1])
259                {
260                   _x_pos_adjust(&pos, &base_size, &hover_area);
261                   pos.y -= base_size.y;
262                   dir = ELM_CTXPOPUP_DIRECTION_UP;
263                   if (pos.y < (hover_area.y + arrow_size.y))
264                     {
265                        base_size.y -= ((hover_area.y + arrow_size.y) - pos.y);
266                        pos.y = hover_area.y + arrow_size.y;
267                     }
268                }
269              //ELM_CTXPOPUP_DIRECTION_DOWN
270              else
271                {
272                   _x_pos_adjust(&pos, &base_size, &hover_area);
273                   dir = ELM_CTXPOPUP_DIRECTION_DOWN;
274                   if ((pos.y + arrow_size.y + base_size.y) >
275                       (hover_area.y + hover_area.h))
276                     base_size.y -=
277                       ((pos.y + arrow_size.y + base_size.y) -
278                        (hover_area.y + hover_area.h));
279                }
280           }
281         else
282           {
283              length[0] = pos.x - hover_area.x;
284              length[1] = (hover_area.x + hover_area.w) - pos.x;
285
286              //ELM_CTXPOPUP_DIRECTION_LEFT
287              if (length[0] > length[1])
288                {
289                   _y_pos_adjust(&pos, &base_size, &hover_area);
290                   pos.x -= base_size.x;
291                   dir = ELM_CTXPOPUP_DIRECTION_LEFT;
292                   if (pos.x < (hover_area.x + arrow_size.x))
293                     {
294                        base_size.x -= ((hover_area.x + arrow_size.x) - pos.x);
295                        pos.x = hover_area.x + arrow_size.x;
296                     }
297                }
298              //ELM_CTXPOPUP_DIRECTION_RIGHT
299              else
300                {
301                   _y_pos_adjust(&pos, &base_size, &hover_area);
302                   dir = ELM_CTXPOPUP_DIRECTION_RIGHT;
303                   if (pos.x + (arrow_size.x + base_size.x) >
304                       hover_area.x + hover_area.w)
305                     base_size.x -=
306                       ((pos.x + arrow_size.x + base_size.x) -
307                        (hover_area.x + hover_area.w));
308                }
309           }
310      }
311
312    //Final position and size.
313    rect->x = pos.x;
314    rect->y = pos.y;
315    rect->w = base_size.x;
316    rect->h = base_size.y;
317
318    return dir;
319 }
320
321 static void
322 _arrow_update(Evas_Object *obj,
323               Elm_Ctxpopup_Direction dir,
324               Evas_Coord_Rectangle base_size)
325 {
326    Evas_Coord_Rectangle arrow_size;
327    Evas_Coord x, y;
328    double drag;
329
330    ELM_CTXPOPUP_DATA_GET(obj, sd);
331    Elm_Widget_Smart_Data *wd = eo_data_get(obj, ELM_OBJ_WIDGET_CLASS);
332
333    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
334    evas_object_geometry_get
335      (sd->arrow, NULL, NULL, &arrow_size.w, &arrow_size.h);
336
337    /* arrow is not being kept as sub-object on purpose, here. the
338     * design of the widget does not help with the contrary */
339
340    switch (dir)
341      {
342       case ELM_CTXPOPUP_DIRECTION_RIGHT:
343         edje_object_signal_emit(sd->arrow, "elm,state,left", "elm");
344         edje_object_part_swallow
345            (wd->resize_obj,
346             (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_right" :
347              "elm.swallow.arrow_left"), sd->arrow);
348
349         if (base_size.h > 0)
350           {
351              if (y < ((arrow_size.h * 0.5) + base_size.y))
352                y = 0;
353              else if (y > base_size.y + base_size.h - (arrow_size.h * 0.5))
354                y = base_size.h - arrow_size.h;
355              else
356                y = y - base_size.y - (arrow_size.h * 0.5);
357              drag = (double)(y) / (double)(base_size.h - arrow_size.h);
358              edje_object_part_drag_value_set
359                 (wd->resize_obj,
360                  (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_right" :
361                   "elm.swallow.arrow_left"), 1, drag);
362           }
363         break;
364
365       case ELM_CTXPOPUP_DIRECTION_LEFT:
366         edje_object_signal_emit(sd->arrow, "elm,state,right", "elm");
367         edje_object_part_swallow
368            (wd->resize_obj,
369             (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_left" :
370              "elm.swallow.arrow_right"), sd->arrow);
371
372         if (base_size.h > 0)
373           {
374              if (y < ((arrow_size.h * 0.5) + base_size.y))
375                y = 0;
376              else if (y > (base_size.y + base_size.h - (arrow_size.h * 0.5)))
377                y = base_size.h - arrow_size.h;
378              else
379                y = y - base_size.y - (arrow_size.h * 0.5);
380              drag = (double)(y) / (double)(base_size.h - arrow_size.h);
381              edje_object_part_drag_value_set
382                 (wd->resize_obj,
383                  (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_left" :
384                   "elm.swallow.arrow_right"), 0, drag);
385           }
386         break;
387
388       case ELM_CTXPOPUP_DIRECTION_DOWN:
389         edje_object_signal_emit(sd->arrow, "elm,state,top", "elm");
390         edje_object_part_swallow
391           (wd->resize_obj, "elm.swallow.arrow_up",
392           sd->arrow);
393
394         if (base_size.w > 0)
395           {
396              if (x < ((arrow_size.w * 0.5) + base_size.x))
397                x = 0;
398              else if (x > (base_size.x + base_size.w - (arrow_size.w * 0.5)))
399                x = base_size.w - arrow_size.w;
400              else
401                x = x - base_size.x - (arrow_size.w * 0.5);
402              drag = (double)(x) / (double)(base_size.w - arrow_size.w);
403              edje_object_part_drag_value_set
404                (wd->resize_obj, "elm.swallow.arrow_up", drag,
405                1);
406           }
407         break;
408
409       case ELM_CTXPOPUP_DIRECTION_UP:
410         edje_object_signal_emit(sd->arrow, "elm,state,bottom", "elm");
411         edje_object_part_swallow
412           (wd->resize_obj, "elm.swallow.arrow_down",
413           sd->arrow);
414
415         if (base_size.w > 0)
416           {
417              if (x < ((arrow_size.w * 0.5) + base_size.x))
418                x = 0;
419              else if (x > (base_size.x + base_size.w - (arrow_size.w * 0.5)))
420                x = base_size.w - arrow_size.w;
421              else x = x - base_size.x - (arrow_size.w * 0.5);
422              drag = (double)(x) / (double)(base_size.w - arrow_size.w);
423              edje_object_part_drag_value_set
424                (wd->resize_obj, "elm.swallow.arrow_down",
425                drag, 0);
426           }
427         break;
428
429       default:
430         break;
431      }
432
433    //should be here for getting accurate geometry value
434    evas_object_smart_calculate(wd->resize_obj);
435 }
436
437 static void
438 _show_signals_emit(Evas_Object *obj,
439                    Elm_Ctxpopup_Direction dir)
440 {
441    ELM_CTXPOPUP_DATA_GET(obj, sd);
442
443    if (!sd->visible) return;
444    if ((sd->list) && (!sd->list_visible)) return;
445
446    switch (dir)
447      {
448       case ELM_CTXPOPUP_DIRECTION_UP:
449         elm_layout_signal_emit(obj, "elm,state,show,up", "elm");
450         break;
451
452       case ELM_CTXPOPUP_DIRECTION_LEFT:
453         elm_layout_signal_emit(obj,
454               (elm_widget_mirrored_get(obj) ? "elm,state,show,right" :
455                "elm,state,show,left"), "elm");
456         break;
457
458       case ELM_CTXPOPUP_DIRECTION_RIGHT:
459         elm_layout_signal_emit(obj,
460               (elm_widget_mirrored_get(obj) ? "elm,state,show,left" :
461                "elm,state,show,right"), "elm");
462         break;
463
464       case ELM_CTXPOPUP_DIRECTION_DOWN:
465         elm_layout_signal_emit(obj, "elm,state,show,down", "elm");
466         break;
467
468       default:
469         break;
470      }
471
472    edje_object_signal_emit(sd->bg, "elm,state,show", "elm");
473    elm_layout_signal_emit(obj, "elm,state,show", "elm");
474 }
475
476 static void
477 _hide_signals_emit(Evas_Object *obj,
478                    Elm_Ctxpopup_Direction dir)
479 {
480    ELM_CTXPOPUP_DATA_GET(obj, sd);
481
482    if (!sd->visible) return;
483
484    switch (dir)
485      {
486       case ELM_CTXPOPUP_DIRECTION_UP:
487         elm_layout_signal_emit(obj, "elm,state,hide,up", "elm");
488         break;
489
490       case ELM_CTXPOPUP_DIRECTION_LEFT:
491         elm_layout_signal_emit(obj,
492               (elm_widget_mirrored_get(obj) ? "elm,state,hide,right" :
493                "elm,state,hide,left"), "elm");
494         break;
495
496       case ELM_CTXPOPUP_DIRECTION_RIGHT:
497         elm_layout_signal_emit(obj,
498               (elm_widget_mirrored_get(obj) ? "elm,state,hide,left" :
499                "elm,state,hide,right"), "elm");
500         break;
501
502       case ELM_CTXPOPUP_DIRECTION_DOWN:
503         elm_layout_signal_emit(obj, "elm,state,hide,down", "elm");
504         break;
505
506       default:
507         break;
508      }
509
510    edje_object_signal_emit(sd->bg, "elm,state,hide", "elm");
511 }
512
513 static void
514 _base_shift_by_arrow(Evas_Object *arrow,
515                      Elm_Ctxpopup_Direction dir,
516                      Evas_Coord_Rectangle *rect)
517 {
518    Evas_Coord arrow_w, arrow_h;
519
520    evas_object_geometry_get(arrow, NULL, NULL, &arrow_w, &arrow_h);
521    switch (dir)
522      {
523       case ELM_CTXPOPUP_DIRECTION_RIGHT:
524         rect->x += arrow_w;
525         break;
526
527       case ELM_CTXPOPUP_DIRECTION_LEFT:
528         rect->x -= arrow_w;
529         break;
530
531       case ELM_CTXPOPUP_DIRECTION_DOWN:
532         rect->y += arrow_h;
533         break;
534
535       case ELM_CTXPOPUP_DIRECTION_UP:
536         rect->y -= arrow_h;
537         break;
538
539       default:
540         break;
541      }
542 }
543
544 static void
545 _elm_ctxpopup_smart_layout_sub_object_add_enable(Eo *obj EINA_UNUSED, void *_pd EINA_UNUSED, va_list *list)
546 {
547    Eina_Bool *enable = va_arg(*list, Eina_Bool *);
548    *enable = EINA_FALSE;
549 }
550
551 static void
552 _elm_ctxpopup_smart_sub_object_add(Eo *obj, void *_pd EINA_UNUSED, va_list *list)
553 {
554    Evas_Object *sobj = va_arg(*list, Evas_Object *);
555    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
556    Eina_Bool int_ret;
557
558    eo_do_super(obj, MY_CLASS, elm_wdg_sub_object_add(sobj, &int_ret));
559
560    if (ret) *ret = int_ret;
561 }
562
563 static void
564 _elm_ctxpopup_smart_sizing_eval(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
565 {
566    Evas_Coord_Rectangle rect = { 0, 0, 1, 1 };
567    Evas_Coord_Point list_size = { 0, 0 };
568
569    Elm_Ctxpopup_Smart_Data *sd = _pd;
570    Elm_Widget_Smart_Data *wd = eo_data_get(obj, ELM_OBJ_WIDGET_CLASS);
571
572    if (!sd->arrow) return;  /* simple way to flag "under deletion" */
573
574    //Base
575    sd->dir = _base_geometry_calc(obj, &rect);
576
577    _arrow_update(obj, sd->dir, rect);
578
579    _base_shift_by_arrow(sd->arrow, sd->dir, &rect);
580
581    if ((sd->list) && (sd->list_visible))
582      {
583         evas_object_geometry_get(sd->list, 0, 0, &list_size.x, &list_size.y);
584         if ((list_size.x >= rect.w) || (list_size.y >= rect.h))
585           {
586              elm_list_mode_set(sd->list, ELM_LIST_COMPRESS);
587              evas_object_size_hint_min_set(sd->box, rect.w, rect.h);
588              evas_object_size_hint_min_set(obj, rect.w, rect.h);
589           }
590      }
591
592    evas_object_move(wd->resize_obj, rect.x, rect.y);
593    evas_object_resize(wd->resize_obj, rect.w, rect.h);
594
595    _show_signals_emit(obj, sd->dir);
596 }
597
598 static void
599 _on_parent_del(void *data,
600                Evas *e __UNUSED__,
601                Evas_Object *obj __UNUSED__,
602                void *event_info __UNUSED__)
603 {
604    evas_object_del(data);
605 }
606
607 static void
608 _on_parent_move(void *data,
609                 Evas *e __UNUSED__,
610                 Evas_Object *obj __UNUSED__,
611                 void *event_info __UNUSED__)
612 {
613    ELM_CTXPOPUP_DATA_GET(data, sd);
614
615    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
616
617    if (sd->visible) elm_layout_sizing_eval(data);
618 }
619
620 static void
621 _on_parent_resize(void *data,
622                   Evas *e __UNUSED__,
623                   Evas_Object *obj __UNUSED__,
624                   void *event_info __UNUSED__)
625 {
626    ELM_CTXPOPUP_DATA_GET(data, sd);
627
628    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
629
630    evas_object_hide(data);
631    evas_object_smart_callback_call(data, SIG_DISMISSED, NULL);
632 }
633
634 static void
635 _parent_detach(Evas_Object *obj)
636 {
637    ELM_CTXPOPUP_DATA_GET(obj, sd);
638
639    if (!sd->parent) return;
640
641    evas_object_event_callback_del_full
642      (sd->parent, EVAS_CALLBACK_DEL, _on_parent_del, obj);
643    evas_object_event_callback_del_full
644      (sd->parent, EVAS_CALLBACK_MOVE, _on_parent_move, obj);
645    evas_object_event_callback_del_full
646      (sd->parent, EVAS_CALLBACK_RESIZE, _on_parent_resize, obj);
647 }
648
649 static void
650 _on_content_resized(void *data,
651                     Evas *e __UNUSED__,
652                     Evas_Object *obj __UNUSED__,
653                     void *event_info __UNUSED__)
654 {
655    ELM_CTXPOPUP_DATA_GET(data, sd);
656
657    elm_box_recalculate(sd->box);
658    elm_layout_sizing_eval(data);
659 }
660
661 //FIXME: lost the content size when theme hook is called.
662 static void
663 _elm_ctxpopup_smart_theme(Eo *obj, void *_pd, va_list *list)
664 {
665    Elm_Ctxpopup_Smart_Data *sd = _pd;
666
667    Eina_Bool int_ret;
668    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
669    if (ret) *ret = EINA_FALSE;
670
671    eo_do_super(obj, MY_CLASS, elm_wdg_theme(&int_ret));
672    if (!int_ret) return;
673
674    elm_widget_theme_object_set
675      (obj, sd->bg, "ctxpopup", "bg", elm_widget_style_get(obj));
676    elm_widget_theme_object_set
677      (obj, sd->arrow, "ctxpopup", "arrow", elm_widget_style_get(obj));
678
679    if (sd->list)
680      {
681         if (!strncmp(elm_object_style_get(obj), "default", strlen("default")))
682           elm_object_style_set(sd->list, "ctxpopup");
683         else
684           elm_object_style_set(sd->list, elm_object_style_get(obj));
685      }
686
687    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
688
689    if (sd->visible) elm_layout_sizing_eval(obj);
690
691    if (ret) *ret = EINA_TRUE;
692 }
693
694 /* kind of a big and tricky override here: an internal box will hold
695  * the actual content. content aliases won't be of much help here */
696 static void
697 _elm_ctxpopup_smart_content_set(Eo *obj, void *_pd, va_list *list)
698 {
699    Evas_Coord min_w = -1, min_h = -1;
700
701    Elm_Ctxpopup_Smart_Data *sd = _pd;
702    const char *part = va_arg(*list, const char *);
703    Evas_Object *content = va_arg(*list, Evas_Object *);
704    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
705    Eina_Bool int_ret = EINA_TRUE;
706
707    if ((part) && (strcmp(part, "default")))
708      {
709         eo_do_super(obj, MY_CLASS, elm_obj_container_content_set(part, content, &int_ret));
710         goto end;
711      }
712
713    if (!content)
714      {
715         int_ret = EINA_FALSE;
716         goto end;
717      }
718
719    if (content == sd->content) goto end;
720
721    if (sd->content) evas_object_del(sd->content);
722    if (sd->content == sd->list) sd->list = NULL;
723
724    evas_object_size_hint_weight_set
725      (content, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
726    evas_object_size_hint_fill_set
727      (content, EVAS_HINT_FILL, EVAS_HINT_FILL);
728
729    /* since it's going to be a box content, not a layout's... */
730    evas_object_show(content);
731
732    evas_object_size_hint_min_get(content, &min_w, &min_h);
733    evas_object_size_hint_min_set(sd->box, min_w, min_h);
734    elm_box_pack_end(sd->box, content);
735
736    sd->content = content;
737    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
738
739    if (sd->visible) elm_layout_sizing_eval(obj);
740
741 end:
742    if (ret) *ret = int_ret;
743 }
744
745 static void
746 _elm_ctxpopup_smart_content_get(Eo *obj, void *_pd, va_list *list)
747 {
748    const char *part = va_arg(*list, const char *);
749    Evas_Object **ret = va_arg(*list, Evas_Object **);
750    *ret = NULL;
751
752    if ((part) && (strcmp(part, "default")))
753      {
754         eo_do_super(obj, MY_CLASS, elm_obj_container_content_get(part, ret));
755         return;
756      }
757
758    Elm_Ctxpopup_Smart_Data *sd = _pd;
759
760    *ret = sd->content;
761 }
762
763 static void
764 _elm_ctxpopup_smart_content_unset(Eo *obj, void *_pd, va_list *list)
765 {
766    Evas_Object *content;
767
768    const char *part = va_arg(*list, const char *);
769    Evas_Object **ret = va_arg(*list, Evas_Object **);
770
771    if ((part) && (strcmp(part, "default")))
772      {
773         eo_do_super(obj, MY_CLASS, elm_obj_container_content_unset(part, &content));
774         goto end;
775      }
776
777    Elm_Ctxpopup_Smart_Data *sd = _pd;
778    content = sd->content;
779    if (!content) goto end;
780
781    sd->content = NULL;
782    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
783
784    if (sd->visible) elm_layout_sizing_eval(obj);
785
786 end:
787    if (ret) *ret = content;
788 }
789
790 static void
791 _item_text_set_hook(Elm_Object_Item *it,
792                     const char *part,
793                     const char *label)
794 {
795    Elm_Ctxpopup_Item *ctxpopup_it;
796
797    if ((part) && (strcmp(part, "default"))) return;
798
799    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
800
801    ELM_CTXPOPUP_DATA_GET(WIDGET(ctxpopup_it), sd);
802
803    elm_object_item_part_text_set(ctxpopup_it->list_item, "default", label);
804    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
805
806    if (sd->visible) elm_layout_sizing_eval(WIDGET(ctxpopup_it));
807 }
808
809 static const char *
810 _item_text_get_hook(const Elm_Object_Item *it,
811                     const char *part)
812 {
813    Elm_Ctxpopup_Item *ctxpopup_it;
814
815    if (part && strcmp(part, "default")) return NULL;
816
817    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
818    return elm_object_item_part_text_get(ctxpopup_it->list_item, "default");
819 }
820
821 static void
822 _item_content_set_hook(Elm_Object_Item *it,
823                        const char *part,
824                        Evas_Object *content)
825 {
826    Elm_Ctxpopup_Item *ctxpopup_it;
827
828    if ((part) && (strcmp(part, "icon"))
829        && (strcmp(part, "start"))
830        && (strcmp(part, "end"))) return;
831
832    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
833
834    ELM_CTXPOPUP_DATA_GET(WIDGET(ctxpopup_it), sd);
835
836    if ((part) && (!strcmp(part, "end")))
837      elm_object_item_part_content_set(ctxpopup_it->list_item, "end", content);
838    else
839      elm_object_item_part_content_set
840        (ctxpopup_it->list_item, "start", content);
841
842    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
843
844    if (sd->visible) elm_layout_sizing_eval(WIDGET(ctxpopup_it));
845 }
846
847 static Evas_Object *
848 _item_content_get_hook(const Elm_Object_Item *it,
849                        const char *part)
850 {
851    Elm_Ctxpopup_Item *ctxpopup_it;
852
853    if (part && strcmp(part, "icon") && strcmp(part, "start")
854        && strcmp(part, "end")) return NULL;
855
856    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
857
858    if (part && !strcmp(part, "end"))
859      return elm_object_item_part_content_get(ctxpopup_it->list_item, "end");
860    else
861      return elm_object_item_part_content_get(ctxpopup_it->list_item, "start");
862 }
863
864 static void
865 _item_disable_hook(Elm_Object_Item *it)
866 {
867    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
868
869    elm_object_item_disabled_set
870      (ctxpopup_it->list_item, elm_widget_item_disabled_get(ctxpopup_it));
871 }
872
873 static void
874 _item_signal_emit_hook(Elm_Object_Item *it,
875                        const char *emission,
876                        const char *source)
877 {
878    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
879
880    elm_object_item_signal_emit(ctxpopup_it->list_item, emission, source);
881 }
882
883 static void
884 _bg_clicked_cb(void *data,
885                Evas_Object *obj __UNUSED__,
886                const char *emission __UNUSED__,
887                const char *source __UNUSED__)
888 {
889    ELM_CTXPOPUP_DATA_GET(data, sd);
890
891    _hide_signals_emit(data, sd->dir);
892 }
893
894 static void
895 _on_show(void *data __UNUSED__,
896          Evas *e __UNUSED__,
897          Evas_Object *obj,
898          void *event_info __UNUSED__)
899 {
900    ELM_CTXPOPUP_DATA_GET(obj, sd);
901
902    if (sd->list)
903      {
904         elm_list_go(sd->list);
905         sd->visible = EINA_TRUE;
906         elm_object_focus_set(obj, EINA_TRUE);
907         return;
908      }
909
910    if (!sd->content) return;
911
912    sd->visible = EINA_TRUE;
913
914    evas_object_show(sd->bg);
915    evas_object_show(sd->arrow);
916
917    edje_object_signal_emit(sd->bg, "elm,state,show", "elm");
918    elm_layout_signal_emit(obj, "elm,state,show", "elm");
919
920    elm_layout_sizing_eval(obj);
921
922    elm_object_focus_set(obj, EINA_TRUE);
923 }
924
925 static void
926 _on_hide(void *data __UNUSED__,
927          Evas *e __UNUSED__,
928          Evas_Object *obj,
929          void *event_info __UNUSED__)
930 {
931    ELM_CTXPOPUP_DATA_GET(obj, sd);
932
933    if (!sd->visible) return;
934
935    evas_object_hide(sd->bg);
936    evas_object_hide(sd->arrow);
937
938    sd->visible = EINA_FALSE;
939    sd->list_visible = EINA_FALSE;
940 }
941
942 static void
943 _on_move(void *data __UNUSED__,
944          Evas *e __UNUSED__,
945          Evas_Object *obj,
946          void *event_info __UNUSED__)
947 {
948    ELM_CTXPOPUP_DATA_GET(obj, sd);
949
950    if (sd->visible) evas_object_show(sd->arrow);
951
952    elm_layout_sizing_eval(obj);
953 }
954
955 static void
956 _hide_finished_cb(void *data,
957                   Evas_Object *obj __UNUSED__,
958                   const char *emission __UNUSED__,
959                   const char *source __UNUSED__)
960 {
961    evas_object_hide(data);
962    evas_object_smart_callback_call(data, SIG_DISMISSED, NULL);
963 }
964
965 static void
966 _list_resize_cb(void *data,
967                 Evas *e __UNUSED__,
968                 Evas_Object *obj __UNUSED__,
969                 void *event_info __UNUSED__)
970 {
971    ELM_CTXPOPUP_DATA_GET(data, sd);
972
973    if (!sd->visible) return;
974    if (sd->list_visible) return;
975
976    sd->list_visible = EINA_TRUE;
977
978    evas_object_show(sd->bg);
979    evas_object_show(sd->arrow);
980
981    elm_layout_sizing_eval(data);
982 }
983
984 static void
985 _ctxpopup_restack_cb(void *data __UNUSED__,
986                      Evas *e __UNUSED__,
987                      Evas_Object *obj,
988                      void *event_info __UNUSED__)
989 {
990    ELM_CTXPOPUP_DATA_GET(obj, sd);
991    evas_object_stack_below(sd->bg, obj);
992 }
993
994 static void
995 _list_del(Elm_Ctxpopup_Smart_Data *sd)
996 {
997    if (!sd->list) return;
998
999    evas_object_del(sd->list);
1000    sd->list = NULL;
1001 }
1002
1003 static Eina_Bool
1004 _item_del_pre_hook(Elm_Object_Item *it)
1005 {
1006    Evas_Object *list;
1007    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1008
1009    ELM_CTXPOPUP_DATA_GET(WIDGET(ctxpopup_it), sd);
1010
1011    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1012
1013    list = elm_object_item_widget_get(ctxpopup_it->list_item);
1014
1015    if (eina_list_count(elm_list_items_get(list)) < 2)
1016      {
1017         elm_object_item_del(ctxpopup_it->list_item);
1018         sd->items = eina_list_remove(sd->items, ctxpopup_it);
1019         evas_object_hide(WIDGET(ctxpopup_it));
1020
1021         return EINA_TRUE;
1022      }
1023
1024    elm_object_item_del(ctxpopup_it->list_item);
1025    sd->items = eina_list_remove(sd->items, ctxpopup_it);
1026    if (sd->list_visible) elm_layout_sizing_eval(WIDGET(ctxpopup_it));
1027
1028    return EINA_TRUE;
1029 }
1030
1031 static void
1032 _elm_ctxpopup_smart_disable(Eo *obj, void *_pd, va_list *list)
1033 {
1034    Elm_Ctxpopup_Smart_Data *sd = _pd;
1035
1036    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
1037    if (ret) *ret = EINA_FALSE;
1038    Eina_Bool int_ret;
1039
1040    eo_do_super(obj, MY_CLASS, elm_wdg_disable(&int_ret));
1041    if (!int_ret) return;
1042
1043    elm_object_disabled_set(sd->list, elm_widget_disabled_get(obj));
1044
1045    if (ret) *ret = EINA_TRUE;
1046 }
1047
1048 static void
1049 _elm_ctxpopup_smart_add(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
1050 {
1051    Elm_Ctxpopup_Smart_Data *priv = _pd;
1052
1053    eo_do_super(obj, MY_CLASS, evas_obj_smart_add());
1054
1055    if (!elm_layout_theme_set
1056        (obj, "ctxpopup", "base", elm_widget_style_get(obj)))
1057      CRITICAL("Failed to set layout!");
1058
1059    elm_layout_signal_callback_add
1060      (obj, "elm,action,hide,finished", "", _hide_finished_cb, obj);
1061
1062    //Background
1063    priv->bg = edje_object_add(evas_object_evas_get(obj));
1064    elm_widget_theme_object_set(obj, priv->bg, "ctxpopup", "bg", "default");
1065    edje_object_signal_callback_add
1066      (priv->bg, "elm,action,click", "", _bg_clicked_cb, obj);
1067
1068    evas_object_stack_below(priv->bg, obj);
1069
1070    //Arrow
1071    priv->arrow = edje_object_add(evas_object_evas_get(obj));
1072    elm_widget_theme_object_set
1073      (obj, priv->arrow, "ctxpopup", "arrow", "default");
1074
1075    priv->dir_priority[0] = ELM_CTXPOPUP_DIRECTION_UP;
1076    priv->dir_priority[1] = ELM_CTXPOPUP_DIRECTION_LEFT;
1077    priv->dir_priority[2] = ELM_CTXPOPUP_DIRECTION_RIGHT;
1078    priv->dir_priority[3] = ELM_CTXPOPUP_DIRECTION_DOWN;
1079    priv->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1080
1081    evas_object_event_callback_add
1082      (obj, EVAS_CALLBACK_RESTACK, _ctxpopup_restack_cb, obj);
1083
1084    priv->box = elm_box_add(obj);
1085    evas_object_size_hint_weight_set
1086      (priv->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1087
1088    evas_object_event_callback_add
1089      (priv->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1090
1091    /* box will be our content placeholder, thus the parent's version call */
1092    Eina_Bool ret;
1093    eo_do_super(obj, MY_CLASS, elm_obj_container_content_set("elm.swallow.content", priv->box, &ret));
1094
1095    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show, NULL);
1096    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide, NULL);
1097    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _on_move, NULL);
1098
1099    elm_widget_can_focus_set(obj, EINA_TRUE);
1100 }
1101
1102 static void
1103 _elm_ctxpopup_smart_del(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
1104 {
1105    Elm_Ctxpopup_Smart_Data *sd = _pd;
1106    Elm_Ctxpopup_Item *it;
1107
1108    evas_object_event_callback_del_full
1109      (sd->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1110    _parent_detach(obj);
1111
1112    elm_ctxpopup_clear(obj);
1113    evas_object_del(sd->arrow);
1114    sd->arrow = NULL; /* stops _sizing_eval() from going on on deletion */
1115
1116    evas_object_del(sd->bg);
1117    sd->bg = NULL;
1118
1119    EINA_LIST_FREE(sd->items, it)
1120      elm_widget_item_free(it);
1121
1122    eo_do_super(obj, MY_CLASS, evas_obj_smart_del());
1123 }
1124
1125 static void
1126 _elm_ctxpopup_smart_parent_set(Eo *obj, void *_pd EINA_UNUSED, va_list *list)
1127 {
1128    Evas_Object *parent = va_arg(*list, Evas_Object *);
1129    //default parent is to be hover parent
1130    elm_ctxpopup_hover_parent_set(obj, parent);
1131 }
1132
1133 EAPI Evas_Object *
1134 elm_ctxpopup_add(Evas_Object *parent)
1135 {
1136    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1137    Evas_Object *obj = eo_add(MY_CLASS, parent);
1138    eo_unref(obj);
1139
1140    /* access: parent could be any object such as elm_list which does
1141       not know elc_ctxpopup as its child object in the focus_next(); */
1142    Elm_Widget_Smart_Data *wd = eo_data_get(obj, ELM_OBJ_WIDGET_CLASS);
1143    wd->highlight_root = EINA_TRUE;
1144
1145    return obj;
1146 }
1147
1148 static void
1149 _constructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
1150 {
1151    eo_do_super(obj, MY_CLASS, eo_constructor());
1152    eo_do(obj,
1153          evas_obj_type_set(MY_CLASS_NAME),
1154          evas_obj_smart_callbacks_descriptions_set(_smart_callbacks, NULL));
1155
1156    if (!elm_widget_sub_object_add(eo_parent_get(obj), obj))
1157      ERR("could not add %p as sub object of %p", obj, eo_parent_get(obj));
1158 }
1159
1160 EAPI void
1161 elm_ctxpopup_hover_parent_set(Evas_Object *obj,
1162                               Evas_Object *parent)
1163 {
1164    ELM_CTXPOPUP_CHECK(obj);
1165    eo_do(obj, elm_obj_ctxpopup_hover_parent_set(parent));
1166 }
1167
1168 static void
1169 _hover_parent_set(Eo *obj, void *_pd, va_list *list)
1170 {
1171    Evas_Coord x, y, w, h;
1172    Evas_Object *parent = va_arg(*list, Evas_Object *);
1173
1174    Elm_Ctxpopup_Smart_Data *sd = _pd;
1175
1176    if (!parent) return;
1177
1178    _parent_detach(obj);
1179
1180    evas_object_event_callback_add
1181      (parent, EVAS_CALLBACK_DEL, _on_parent_del, obj);
1182    evas_object_event_callback_add
1183      (parent, EVAS_CALLBACK_MOVE, _on_parent_move, obj);
1184    evas_object_event_callback_add
1185      (parent, EVAS_CALLBACK_RESIZE, _on_parent_resize, obj);
1186
1187    sd->parent = parent;
1188
1189    //Update Background
1190    evas_object_geometry_get(parent, &x, &y, &w, &h);
1191    if (!strcmp(evas_object_type_get(parent), "elm_win"))
1192      x = y = 0;
1193    evas_object_move(sd->bg, x, y);
1194    evas_object_resize(sd->bg, w, h);
1195
1196    if (sd->visible) elm_layout_sizing_eval(obj);
1197 }
1198
1199 EAPI Evas_Object *
1200 elm_ctxpopup_hover_parent_get(const Evas_Object *obj)
1201 {
1202    ELM_CTXPOPUP_CHECK(obj) NULL;
1203    Evas_Object *ret = NULL;
1204    eo_do((Eo *) obj, elm_obj_ctxpopup_hover_parent_get(&ret));
1205    return ret;
1206 }
1207
1208 static void
1209 _hover_parent_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
1210 {
1211    Evas_Object **ret = va_arg(*list, Evas_Object **);
1212    Elm_Ctxpopup_Smart_Data *sd = _pd;
1213    *ret = sd->parent;
1214 }
1215
1216 EAPI void
1217 elm_ctxpopup_clear(Evas_Object *obj)
1218 {
1219    ELM_CTXPOPUP_CHECK(obj);
1220    eo_do(obj, elm_obj_ctxpopup_clear());
1221 }
1222
1223 static void
1224 _clear(Eo *obj EINA_UNUSED, void *_pd, va_list *list EINA_UNUSED)
1225 {
1226    Elm_Ctxpopup_Smart_Data *sd = _pd;
1227
1228    _list_del(sd);
1229    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1230 }
1231
1232 EAPI void
1233 elm_ctxpopup_horizontal_set(Evas_Object *obj,
1234                             Eina_Bool horizontal)
1235 {
1236    ELM_CTXPOPUP_CHECK(obj);
1237    eo_do(obj, elm_obj_ctxpopup_horizontal_set(horizontal));
1238 }
1239
1240 static void
1241 _horizontal_set(Eo *obj, void *_pd, va_list *list)
1242 {
1243    Eina_Bool horizontal = va_arg(*list, int);
1244    Elm_Ctxpopup_Smart_Data *sd = _pd;
1245
1246    sd->horizontal = !!horizontal;
1247
1248    if (!sd->list) return;
1249
1250    elm_list_horizontal_set(sd->list, sd->horizontal);
1251
1252    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1253
1254    if (sd->visible) elm_layout_sizing_eval(obj);
1255 }
1256
1257 EAPI Eina_Bool
1258 elm_ctxpopup_horizontal_get(const Evas_Object *obj)
1259 {
1260    ELM_CTXPOPUP_CHECK(obj) EINA_FALSE;
1261    Eina_Bool ret = EINA_FALSE;
1262    eo_do((Eo *) obj, elm_obj_ctxpopup_horizontal_get(&ret));
1263    return ret;
1264 }
1265
1266 static void
1267 _horizontal_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
1268 {
1269    Eina_Bool *ret = va_arg(*list, Eina_Bool *);
1270    Elm_Ctxpopup_Smart_Data *sd = _pd;
1271
1272    *ret = sd->horizontal;
1273 }
1274
1275 EAPI Elm_Object_Item *
1276 elm_ctxpopup_item_append(Evas_Object *obj,
1277                          const char *label,
1278                          Evas_Object *icon,
1279                          Evas_Smart_Cb func,
1280                          const void *data)
1281 {
1282
1283    ELM_CTXPOPUP_CHECK(obj) NULL;
1284    Elm_Object_Item *ret = NULL;
1285    eo_do(obj, elm_obj_ctxpopup_item_append(label, icon, func, data, &ret));
1286    return ret;
1287 }
1288
1289 static void
1290 _item_wrap_cb(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
1291 {
1292    Elm_Ctxpopup_Item *item = data;
1293    item->wcb.org_func_cb((void *)item->wcb.org_data, item->wcb.cobj, item);
1294 }
1295
1296 static void
1297 _item_append(Eo *obj, void *_pd, va_list *list)
1298 {
1299    const char *label = va_arg(*list, const char *);
1300    Evas_Object *icon = va_arg(*list, Evas_Object *);
1301    Evas_Smart_Cb func = va_arg(*list, Evas_Smart_Cb);
1302    const void *data = va_arg(*list, const void *);
1303    Elm_Object_Item **ret = va_arg(*list, Elm_Object_Item **);
1304    *ret = NULL;
1305
1306    Elm_Ctxpopup_Item *item;
1307
1308    Elm_Ctxpopup_Smart_Data *sd = _pd;
1309
1310    item = elm_widget_item_new(obj, Elm_Ctxpopup_Item);
1311    if (!item) return;
1312
1313    elm_widget_item_del_pre_hook_set(item, _item_del_pre_hook);
1314    elm_widget_item_disable_hook_set(item, _item_disable_hook);
1315    elm_widget_item_text_set_hook_set(item, _item_text_set_hook);
1316    elm_widget_item_text_get_hook_set(item, _item_text_get_hook);
1317    elm_widget_item_content_set_hook_set(item, _item_content_set_hook);
1318    elm_widget_item_content_get_hook_set(item, _item_content_get_hook);
1319    elm_widget_item_signal_emit_hook_set(item, _item_signal_emit_hook);
1320
1321    if (!sd->list)
1322      {
1323         //The first item is appended.
1324         sd->list = elm_list_add(obj);
1325         elm_list_mode_set(sd->list, ELM_LIST_EXPAND);
1326         elm_list_horizontal_set(sd->list, sd->horizontal);
1327         evas_object_event_callback_add
1328           (sd->list, EVAS_CALLBACK_RESIZE, _list_resize_cb, obj);
1329         elm_layout_content_set(obj, "default", sd->list);
1330      }
1331
1332    item->wcb.org_func_cb = func;
1333    item->wcb.org_data = data;
1334    item->wcb.cobj = obj;
1335    item->list_item =
1336      elm_list_item_append(sd->list, label, icon, NULL, _item_wrap_cb, item);
1337    sd->items = eina_list_append(sd->items, item);
1338
1339    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1340
1341    if (sd->visible) elm_layout_sizing_eval(obj);
1342
1343    *ret = (Elm_Object_Item *)item;
1344 }
1345
1346 EAPI void
1347 elm_ctxpopup_direction_priority_set(Evas_Object *obj,
1348                                     Elm_Ctxpopup_Direction first,
1349                                     Elm_Ctxpopup_Direction second,
1350                                     Elm_Ctxpopup_Direction third,
1351                                     Elm_Ctxpopup_Direction fourth)
1352 {
1353    ELM_CTXPOPUP_CHECK(obj);
1354    eo_do(obj, elm_obj_ctxpopup_direction_priority_set(first, second, third, fourth));
1355 }
1356
1357 static void
1358 _direction_priority_set(Eo *obj, void *_pd, va_list *list)
1359 {
1360    Elm_Ctxpopup_Direction first = va_arg(*list, Elm_Ctxpopup_Direction);
1361    Elm_Ctxpopup_Direction second = va_arg(*list, Elm_Ctxpopup_Direction);
1362    Elm_Ctxpopup_Direction third = va_arg(*list, Elm_Ctxpopup_Direction);
1363    Elm_Ctxpopup_Direction fourth = va_arg(*list, Elm_Ctxpopup_Direction);
1364
1365    Elm_Ctxpopup_Smart_Data *sd = _pd;
1366
1367    sd->dir_priority[0] = first;
1368    sd->dir_priority[1] = second;
1369    sd->dir_priority[2] = third;
1370    sd->dir_priority[3] = fourth;
1371
1372    if (sd->visible) elm_layout_sizing_eval(obj);
1373 }
1374
1375 EAPI void
1376 elm_ctxpopup_direction_priority_get(Evas_Object *obj,
1377                                     Elm_Ctxpopup_Direction *first,
1378                                     Elm_Ctxpopup_Direction *second,
1379                                     Elm_Ctxpopup_Direction *third,
1380                                     Elm_Ctxpopup_Direction *fourth)
1381 {
1382    ELM_CTXPOPUP_CHECK(obj);
1383    eo_do(obj, elm_obj_ctxpopup_direction_priority_get(first, second, third, fourth));
1384 }
1385
1386 static void
1387 _direction_priority_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
1388 {
1389    Elm_Ctxpopup_Direction *first = va_arg(*list, Elm_Ctxpopup_Direction *);
1390    Elm_Ctxpopup_Direction *second = va_arg(*list, Elm_Ctxpopup_Direction *);
1391    Elm_Ctxpopup_Direction *third = va_arg(*list, Elm_Ctxpopup_Direction *);
1392    Elm_Ctxpopup_Direction *fourth = va_arg(*list, Elm_Ctxpopup_Direction *);
1393
1394    Elm_Ctxpopup_Smart_Data *sd = _pd;
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 EAPI Elm_Ctxpopup_Direction
1403 elm_ctxpopup_direction_get(const Evas_Object *obj)
1404 {
1405    ELM_CTXPOPUP_CHECK(obj) ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1406    Elm_Ctxpopup_Direction ret = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1407    eo_do((Eo *) obj, elm_obj_ctxpopup_direction_get(&ret));
1408    return ret;
1409 }
1410
1411 static void
1412 _direction_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
1413 {
1414    Elm_Ctxpopup_Direction *ret = va_arg(*list, Elm_Ctxpopup_Direction *);
1415    Elm_Ctxpopup_Smart_Data *sd = _pd;
1416
1417    *ret = sd->dir;
1418 }
1419
1420 EAPI void
1421 elm_ctxpopup_dismiss(Evas_Object *obj)
1422 {
1423    ELM_CTXPOPUP_CHECK(obj);
1424    eo_do(obj, elm_obj_ctxpopup_dismiss());
1425 }
1426
1427 static void
1428 _dismiss(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
1429 {
1430    Elm_Ctxpopup_Smart_Data *sd = _pd;
1431
1432    _hide_signals_emit(obj, sd->dir);
1433 }
1434
1435 static void
1436 _class_constructor(Eo_Class *klass)
1437 {
1438    const Eo_Op_Func_Description func_desc[] = {
1439         EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor),
1440
1441         EO_OP_FUNC(EVAS_OBJ_SMART_ID(EVAS_OBJ_SMART_SUB_ID_ADD), _elm_ctxpopup_smart_add),
1442         EO_OP_FUNC(EVAS_OBJ_SMART_ID(EVAS_OBJ_SMART_SUB_ID_DEL), _elm_ctxpopup_smart_del),
1443
1444         EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_PARENT_SET), _elm_ctxpopup_smart_parent_set),
1445         EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_DISABLE), _elm_ctxpopup_smart_disable),
1446         EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_EVENT), _elm_ctxpopup_smart_event),
1447         EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_THEME), _elm_ctxpopup_smart_theme),
1448         EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_SUB_OBJECT_ADD), _elm_ctxpopup_smart_sub_object_add),
1449         EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_FOCUS_NEXT_MANAGER_IS), _elm_ctxpopup_smart_focus_next_manager_is),
1450         EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_FOCUS_NEXT),  _elm_ctxpopup_smart_focus_next),
1451         EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_FOCUS_DIRECTION_MANAGER_IS), _elm_ctxpopup_smart_focus_direction_manager_is),
1452         EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_TRANSLATE), _elm_ctxpopup_smart_translate),
1453
1454         EO_OP_FUNC(ELM_OBJ_CONTAINER_ID(ELM_OBJ_CONTAINER_SUB_ID_CONTENT_SET), _elm_ctxpopup_smart_content_set),
1455         EO_OP_FUNC(ELM_OBJ_CONTAINER_ID(ELM_OBJ_CONTAINER_SUB_ID_CONTENT_GET), _elm_ctxpopup_smart_content_get),
1456         EO_OP_FUNC(ELM_OBJ_CONTAINER_ID(ELM_OBJ_CONTAINER_SUB_ID_CONTENT_UNSET), _elm_ctxpopup_smart_content_unset),
1457
1458         EO_OP_FUNC(ELM_OBJ_LAYOUT_ID(ELM_OBJ_LAYOUT_SUB_ID_SIZING_EVAL), _elm_ctxpopup_smart_sizing_eval),
1459         EO_OP_FUNC(ELM_OBJ_LAYOUT_ID(ELM_OBJ_LAYOUT_SUB_ID_SUB_OBJECT_ADD_ENABLE), _elm_ctxpopup_smart_layout_sub_object_add_enable),
1460
1461         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_HOVER_PARENT_SET), _hover_parent_set),
1462         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_HOVER_PARENT_GET), _hover_parent_get),
1463         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_CLEAR), _clear),
1464         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_HORIZONTAL_SET), _horizontal_set),
1465         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_HORIZONTAL_GET), _horizontal_get),
1466         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_ITEM_APPEND), _item_append),
1467         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_DIRECTION_PRIORITY_SET), _direction_priority_set),
1468         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_DIRECTION_PRIORITY_GET), _direction_priority_get),
1469         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_DIRECTION_GET), _direction_get),
1470         EO_OP_FUNC(ELM_OBJ_CTXPOPUP_ID(ELM_OBJ_CTXPOPUP_SUB_ID_DISMISS), _dismiss),
1471         EO_OP_FUNC_SENTINEL
1472    };
1473    eo_class_funcs_set(klass, func_desc);
1474
1475    evas_smart_legacy_type_register(MY_CLASS_NAME, klass);
1476 }
1477 static const Eo_Op_Description op_desc[] = {
1478      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_HOVER_PARENT_SET, "Set the Ctxpopup's parent."),
1479      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_HOVER_PARENT_GET, "Get the Ctxpopup's parent."),
1480      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_CLEAR, "Clear all items in the given ctxpopup object."),
1481      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_HORIZONTAL_SET, "Change the ctxpopup's orientation to horizontal or vertical."),
1482      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_HORIZONTAL_GET, "Get the value of current ctxpopup object's orientation."),
1483      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_ITEM_APPEND, "Add a new item to a ctxpopup object."),
1484      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_DIRECTION_PRIORITY_SET, "Set the direction priority of a ctxpopup."),
1485      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_DIRECTION_PRIORITY_GET, "Get the direction priority of a ctxpopup."),
1486      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_DIRECTION_GET, "Get the current direction of a ctxpopup."),
1487      EO_OP_DESCRIPTION(ELM_OBJ_CTXPOPUP_SUB_ID_DISMISS, "Dismiss a ctxpopup object."),
1488      EO_OP_DESCRIPTION_SENTINEL
1489 };
1490 static const Eo_Class_Description class_desc = {
1491      EO_VERSION,
1492      MY_CLASS_NAME,
1493      EO_CLASS_TYPE_REGULAR,
1494      EO_CLASS_DESCRIPTION_OPS(&ELM_OBJ_CTXPOPUP_BASE_ID, op_desc, ELM_OBJ_CTXPOPUP_SUB_ID_LAST),
1495      NULL,
1496      sizeof(Elm_Ctxpopup_Smart_Data),
1497      _class_constructor,
1498      NULL
1499 };
1500 EO_DEFINE_CLASS(elm_obj_ctxpopup_class_get, &class_desc, ELM_OBJ_LAYOUT_CLASS, NULL);