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