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