elementary/ctxpopup - support more common apis
[platform/upstream/elementary.git] / src / lib / elc_ctxpopup.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5 typedef struct _Elm_Ctxpopup_Item Elm_Ctxpopup_Item;
6
7 struct _Elm_Ctxpopup_Item
8 {
9    ELM_WIDGET_ITEM;
10    const char *label;
11    Evas_Object *icon;
12    Evas_Smart_Cb func;
13 };
14
15 struct _Widget_Data
16 {
17    Evas_Object *parent;
18    Evas_Object *base;
19    Evas_Object *content;
20    Evas_Object *box;
21    Evas_Object *arrow;
22    Evas_Object *scr;
23    Evas_Object *bg;
24    Eina_List *items;
25    Elm_Ctxpopup_Direction dir;
26    Elm_Ctxpopup_Direction dir_priority[4];
27    Evas_Coord max_sc_w, max_sc_h;
28    Eina_Bool horizontal:1;
29    Eina_Bool visible:1;
30    Eina_Bool finished:1;
31 };
32
33 static const char *widtype = NULL;
34
35 static void _freeze_on(void *data, Evas_Object *obj, void *event_info);
36 static void _freeze_off(void *data, Evas_Object *obj, void *event_info);
37 static void _hold_on(void *data, Evas_Object *obj, void *event_info);
38 static void _hold_off(void *data, Evas_Object *obj, void *event_info);
39 static void _scroller_size_reset(Widget_Data *wd);
40 static void _on_focus_hook(void *data, Evas_Object *obj);
41 static Eina_Bool _event_hook(Evas_Object *obj,
42                              Evas_Object *src,
43                              Evas_Callback_Type type,
44                              void *event_info);
45 static void _parent_cut_off(Evas_Object *obj);
46 static void _parent_resize(void *data,
47                            Evas *e,
48                            Evas_Object *obj,
49                            void *event_info);
50 static void _parent_move(void *data,
51                          Evas *e,
52                          Evas_Object *obj,
53                          void *event_info);
54 static void _parent_del(void *data,
55                         Evas *e,
56                         Evas_Object *obj,
57                         void *event_info);
58 static void _item_sizing_eval(Elm_Ctxpopup_Item *item);
59 static void _adjust_pos_x(Evas_Coord_Point *pos,
60                           Evas_Coord_Point *base_size,
61                           Evas_Coord_Rectangle *hover_area);
62 static void _adjust_pos_y(Evas_Coord_Point *pos,
63                           Evas_Coord_Point *base_size,
64                           Evas_Coord_Rectangle *hover_area);
65 static Elm_Ctxpopup_Direction _calc_base_geometry(Evas_Object *obj,
66                                                   Evas_Coord_Rectangle *rect);
67 static void _update_arrow(Evas_Object *obj,
68                           Elm_Ctxpopup_Direction dir,
69                           Evas_Coord_Rectangle rect);
70 static void _sizing_eval(Evas_Object *obj);
71 static void _hide_signal_emit(Evas_Object *obj,
72                               Elm_Ctxpopup_Direction dir);
73 static void _show_signal_emit(Evas_Object *obj,
74                               Elm_Ctxpopup_Direction dir);
75 static void _shift_base_by_arrow(Evas_Object *arrow,
76                                  Elm_Ctxpopup_Direction dir,
77                                  Evas_Coord_Rectangle *rect);
78 static void _del_pre_hook(Evas_Object *obj);
79 static void _del_hook(Evas_Object *obj);
80 static void _theme_hook(Evas_Object *obj);
81 static void _content_set_hook(Evas_Object *obj,
82                               const char *part,
83                               Evas_Object *content);
84 static Evas_Object * _content_unset_hook(Evas_Object *obj,
85                                          const char *part__);
86 static Evas_Object * _content_get_hook(const Evas_Object *obj,
87                                        const char *part);
88 static void _item_text_set_hook(Elm_Object_Item *it,
89                                const char *part,
90                                const char *label);
91 static const char * _item_text_get_hook(const Elm_Object_Item *it,
92                                         const char *part);
93 static void _item_content_set_hook(Elm_Object_Item *it,
94                                    const char *part,
95                                    Evas_Object *content);
96 static Evas_Object * _item_content_get_hook(const Elm_Object_Item *it,
97                                             const char *part);
98 static void _item_disable_hook(Elm_Object_Item *it);
99 static void _item_signal_emit_hook(Elm_Object_Item *it,
100                                    const char *emission,
101                                    const char *source);
102 static void _bg_clicked_cb(void *data, Evas_Object *obj,
103                            const char *emission,
104                            const char *source);
105 static void _ctxpopup_show(void *data,
106                            Evas *e,
107                            Evas_Object *obj,
108                            void *event_info);
109 static void _hide_finished(void *data,
110                            Evas_Object *obj,
111                            const char *emission,
112                            const char *source __UNUSED__);
113 static void _ctxpopup_hide(void *data,
114                            Evas *e,
115                            Evas_Object *obj,
116                            void *event_info);
117 static void _content_resize(void *data,
118                             Evas *e,
119                             Evas_Object *obj,
120                             void *event_info);
121 static void _scroller_resize(void *data,
122                              Evas *e,
123                              Evas_Object *obj,
124                              void *event_info);
125 static void _ctxpopup_move(void *data,
126                            Evas *e,
127                            Evas_Object *obj,
128                            void *event_info);
129 static void _restack(void *data, Evas *e, Evas_Object *obj, void *event_info);
130 static void _item_select_cb(void *data, Evas_Object *obj,
131                             const char *emission,
132                             const char *source);
133 static void _item_icon_set(Elm_Ctxpopup_Item *item, Evas_Object *icon);
134 static void _item_label_set(Elm_Ctxpopup_Item *item, const char *label);
135 static void _item_new(Elm_Ctxpopup_Item *item, char *group_name);
136 static void _content_del(void *data,
137                          Evas *e,
138                          Evas_Object *obj,
139                          void *event_info);
140 static void _list_del(Widget_Data *wd);
141 static void _list_new(Evas_Object *obj);
142 static void _remove_items(Widget_Data * wd);
143 static void _disable_hook(Evas_Object *obj);
144 static void _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source);
145 static void _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data);
146 static void _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data);
147
148 static const char SIG_DISMISSED[] = "dismissed";
149
150 static const Evas_Smart_Cb_Description _signals[] = {
151    {SIG_DISMISSED, ""},
152    {NULL, NULL}
153 };
154
155 static void
156 _freeze_on(void *data __UNUSED__, Evas_Object *obj,
157            void *event_info __UNUSED__)
158 {
159    Widget_Data *wd = elm_widget_data_get(obj);
160
161    if ((!wd) || (!wd->scr)) return;
162    elm_object_scroll_freeze_push(wd->scr);
163 }
164
165 static void
166 _freeze_off(void *data __UNUSED__, Evas_Object *obj,
167             void *event_info __UNUSED__)
168 {
169    Widget_Data *wd = elm_widget_data_get(obj);
170
171    if ((!wd) || (!wd->scr)) return;
172    elm_object_scroll_freeze_pop(wd->scr);
173 }
174
175 static void
176 _hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
177 {
178    Widget_Data *wd = elm_widget_data_get(obj);
179
180    if ((!wd) || (!wd->scr)) return;
181    elm_object_scroll_hold_push(wd->scr);
182 }
183
184 static void
185 _hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
186 {
187    Widget_Data *wd = elm_widget_data_get(obj);
188
189    if ((!wd) || (!wd->scr)) return;
190    elm_object_scroll_hold_pop(wd->scr);
191 }
192
193 static void
194 _scroller_size_reset(Widget_Data *wd)
195 {
196    wd->finished = EINA_FALSE;
197    wd->max_sc_h = -1;
198    wd->max_sc_w = -1;
199 }
200
201 static void
202 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
203 {
204    Widget_Data *wd = elm_widget_data_get(obj);
205    if (!wd) return;
206
207    if (elm_widget_focus_get(obj))
208      {
209         //FIXME:
210      }
211    else
212      {
213         //FIXME:
214      }
215 }
216
217 static Eina_Bool
218 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__,
219             Evas_Callback_Type type, void *event_info)
220 {
221    Evas_Event_Key_Down *ev;
222    Widget_Data *wd;
223
224    if (type != EVAS_CALLBACK_KEY_DOWN)
225      return EINA_FALSE;
226    wd = elm_widget_data_get(obj);
227    if (!wd) return EINA_FALSE;
228
229    ev = event_info;
230    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
231    if (strcmp(ev->keyname, "Escape")) return EINA_FALSE;
232
233    evas_object_hide(obj);
234    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
235    return EINA_TRUE;
236 }
237
238 static void
239 _parent_cut_off(Evas_Object *obj)
240 {
241    Widget_Data *wd = elm_widget_data_get(obj);
242
243    if (!wd) return;
244
245    evas_object_event_callback_del_full(wd->parent,
246                                        EVAS_CALLBACK_DEL,
247                                        _parent_del,
248                                        obj);
249    evas_object_event_callback_del_full(wd->parent,
250                                        EVAS_CALLBACK_MOVE,
251                                        _parent_move,
252                                        obj);
253    evas_object_event_callback_del_full(wd->parent,
254                                        EVAS_CALLBACK_RESIZE,
255                                        _parent_resize,
256                                        obj);
257
258    elm_widget_sub_object_del(wd->parent, obj);
259 }
260
261 static void
262 _parent_resize(void *data,
263                Evas *e __UNUSED__,
264                Evas_Object *obj __UNUSED__,
265                void *event_info __UNUSED__)
266 {
267    Widget_Data *wd = elm_widget_data_get(data);
268    if (!wd) return;
269
270    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
271
272    evas_object_hide(data);
273 }
274
275 static void
276 _parent_move(void *data,
277              Evas *e __UNUSED__,
278              Evas_Object *obj __UNUSED__,
279              void *event_info __UNUSED__)
280 {
281    Widget_Data *wd = elm_widget_data_get(data);
282
283    if (!wd) return;
284
285    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
286
287    if (wd->visible)
288      {
289         _scroller_size_reset(wd);
290         _sizing_eval(obj);
291      }
292 }
293
294 static void
295 _parent_del(void *data,
296             Evas *e __UNUSED__,
297             Evas_Object *obj __UNUSED__,
298             void *event_info __UNUSED__)
299 {
300    evas_object_del(data);
301 }
302
303 static void
304 _item_sizing_eval(Elm_Ctxpopup_Item *item)
305 {
306    Evas_Coord min_w = -1, min_h = -1, max_w = -1, max_h = -1;
307
308    if (!item) return;
309
310    edje_object_size_min_restricted_calc(VIEW(item), &min_w, &min_h, min_w,
311                                         min_h);
312    evas_object_size_hint_min_set(VIEW(item), min_w, min_h);
313    evas_object_size_hint_max_set(VIEW(item), max_w, max_h);
314 }
315
316 static void
317 _adjust_pos_x(Evas_Coord_Point *pos, Evas_Coord_Point *base_size,
318               Evas_Coord_Rectangle *hover_area)
319 {
320    pos->x -= (base_size->x / 2);
321
322    if (pos->x < hover_area->x)
323      pos->x = hover_area->x;
324    else if ((pos->x + base_size->x) > (hover_area->x + hover_area->w))
325      pos->x = (hover_area->x + hover_area->w) - base_size->x;
326
327    if (base_size->x > hover_area->w)
328      base_size->x -= (base_size->x - hover_area->w);
329
330    if (pos->x < hover_area->x)
331      pos->x = hover_area->x;
332 }
333
334 static void
335 _adjust_pos_y(Evas_Coord_Point *pos, Evas_Coord_Point *base_size,
336               Evas_Coord_Rectangle *hover_area)
337 {
338    pos->y -= (base_size->y / 2);
339
340    if (pos->y < hover_area->y)
341      pos->y = hover_area->y;
342    else if ((pos->y + base_size->y) > (hover_area->y + hover_area->h))
343      pos->y = hover_area->y + hover_area->h - base_size->y;
344
345    if (base_size->y > hover_area->h)
346      base_size->y -= (base_size->y - hover_area->h);
347
348    if (pos->y < hover_area->y)
349      pos->y = hover_area->y;
350 }
351
352 static Elm_Ctxpopup_Direction
353 _calc_base_geometry(Evas_Object *obj, Evas_Coord_Rectangle *rect)
354 {
355    Widget_Data *wd;
356    Evas_Coord_Point pos = {0, 0};
357    Evas_Coord_Point base_size;
358    Evas_Coord_Point max_size;
359    Evas_Coord_Point min_size;
360    Evas_Coord_Rectangle hover_area;
361    Evas_Coord_Point arrow_size;
362    Elm_Ctxpopup_Direction dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
363    Evas_Coord_Point temp;
364    int idx;
365
366    wd = elm_widget_data_get(obj);
367
368    if ((!wd) || (!rect))
369      return ELM_CTXPOPUP_DIRECTION_DOWN;
370
371    edje_object_part_geometry_get(wd->arrow, "ctxpopup_arrow", NULL, NULL,
372                                  &arrow_size.x, &arrow_size.y);
373    evas_object_resize(wd->arrow, arrow_size.x, arrow_size.y);
374
375    //Initialize Area Rectangle.
376    evas_object_geometry_get(wd->parent,
377                             &hover_area.x,
378                             &hover_area.y,
379                             &hover_area.w,
380                             &hover_area.h);
381
382    evas_object_geometry_get(obj, &pos.x, &pos.y, NULL, NULL);
383
384    //recalc the edje
385    edje_object_size_min_calc(wd->base, &base_size.x, &base_size.y);
386    evas_object_smart_calculate(wd->base);
387
388    //Limit to Max Size
389    evas_object_size_hint_max_get(obj, &max_size.x, &max_size.y);
390
391    if ((max_size.y > 0) && (base_size.y > max_size.y))
392      base_size.y = max_size.y;
393
394    if ((max_size.x > 0) && (base_size.x > max_size.x))
395      base_size.x = max_size.x;
396
397    //Limit to Min Size
398    evas_object_size_hint_min_get(obj, &min_size.x, &min_size.y);
399
400    if ((min_size.y > 0) && (base_size.y < min_size.y))
401      base_size.y = min_size.y;
402
403    if ((min_size.x > 0) && (base_size.x < min_size.x))
404      base_size.x = min_size.x;
405
406    //Check the Which direction is available.
407    //If find a avaialble direction, it adjusts position and size.
408    for (idx = 0; idx < 4; idx++)
409      {
410         switch (wd->dir_priority[idx])
411           {
412            case ELM_CTXPOPUP_DIRECTION_UNKNOWN:
413            case ELM_CTXPOPUP_DIRECTION_UP:
414               temp.y = (pos.y - base_size.y);
415               if ((temp.y - arrow_size.y) < hover_area.y)
416                 continue;
417               _adjust_pos_x(&pos, &base_size, &hover_area);
418               pos.y -= base_size.y;
419               dir = ELM_CTXPOPUP_DIRECTION_UP;
420               break;
421            case ELM_CTXPOPUP_DIRECTION_LEFT:
422               temp.x = (pos.x - base_size.x);
423               if ((temp.x - arrow_size.x) < hover_area.x)
424                 continue;
425               _adjust_pos_y(&pos, &base_size, &hover_area);
426               pos.x -= base_size.x;
427               dir = ELM_CTXPOPUP_DIRECTION_LEFT;
428               break;
429            case ELM_CTXPOPUP_DIRECTION_RIGHT:
430               temp.x = (pos.x + base_size.x);
431               if ((temp.x + arrow_size.x) >
432                   (hover_area.x + hover_area.w))
433                 continue;
434               _adjust_pos_y(&pos, &base_size, &hover_area);
435               dir = ELM_CTXPOPUP_DIRECTION_RIGHT;
436               break;
437            case ELM_CTXPOPUP_DIRECTION_DOWN:
438               temp.y = (pos.y + base_size.y);
439               if ((temp.y + arrow_size.y) >
440                   (hover_area.y + hover_area.h))
441                 continue;
442               _adjust_pos_x(&pos, &base_size, &hover_area);
443               dir = ELM_CTXPOPUP_DIRECTION_DOWN;
444               break;
445            default:
446               break;
447           }
448         break;
449      }
450
451    //In this case, all directions are invalid because of lack of space.
452    if (idx == 4)
453      {
454         Evas_Coord length[2];
455
456         if(!wd->horizontal)
457           {
458              length[0] = pos.y - hover_area.y;
459              length[1] = (hover_area.y + hover_area.h) - pos.y;
460
461              // ELM_CTXPOPUP_DIRECTION_UP
462              if (length[0] > length[1])
463                {
464                   _adjust_pos_x(&pos, &base_size, &hover_area);
465                   pos.y -= base_size.y;
466                   dir = ELM_CTXPOPUP_DIRECTION_UP;
467                   if (pos.y < (hover_area.y + arrow_size.y))
468                     {
469                        base_size.y -= ((hover_area.y + arrow_size.y) - pos.y);
470                        pos.y = hover_area.y + arrow_size.y;
471                     }
472                }
473              //ELM_CTXPOPUP_DIRECTION_DOWN
474              else
475                {
476                   _adjust_pos_x(&pos, &base_size, &hover_area);
477                   dir = ELM_CTXPOPUP_DIRECTION_DOWN;
478                   if ((pos.y + arrow_size.y + base_size.y) >
479                       (hover_area.y + hover_area.h))
480                      base_size.y -=
481                         ((pos.y + arrow_size.y + base_size.y) -
482                          (hover_area.y + hover_area.h));
483                }
484           }
485         else
486           {
487              length[0] = pos.x - hover_area.x;
488              length[1] = (hover_area.x + hover_area.w) - pos.x;
489
490              //ELM_CTXPOPUP_DIRECTION_LEFT
491              if (length[0] > length[1])
492                {
493                   _adjust_pos_y(&pos, &base_size, &hover_area);
494                   pos.x -= base_size.x;
495                   dir = ELM_CTXPOPUP_DIRECTION_LEFT;
496                   if (pos.x < (hover_area.x + arrow_size.x))
497                     {
498                        base_size.x -= ((hover_area.x + arrow_size.x) - pos.x);
499                        pos.x = hover_area.x + arrow_size.x;
500                     }
501                }
502              //ELM_CTXPOPUP_DIRECTION_RIGHT
503              else
504                {
505                   _adjust_pos_y(&pos, &base_size, &hover_area);
506                   dir = ELM_CTXPOPUP_DIRECTION_RIGHT;
507                   if (pos.x + (arrow_size.x + base_size.x) >
508                       hover_area.x + hover_area.w)
509                      base_size.x -=
510                         ((pos.x + arrow_size.x + base_size.x) -
511                          (hover_area.x + hover_area.w));
512                }
513           }
514      }
515
516    //Final position and size.
517    rect->x = pos.x;
518    rect->y = pos.y;
519    rect->w = base_size.x;
520    rect->h = base_size.y;
521
522    return dir;
523 }
524
525 static void
526 _update_arrow(Evas_Object *obj, Elm_Ctxpopup_Direction dir,
527               Evas_Coord_Rectangle base_size)
528 {
529    Evas_Coord x, y;
530    Evas_Coord_Rectangle arrow_size;
531    Widget_Data *wd;
532    double drag;
533
534    wd = elm_widget_data_get(obj);
535    if (!wd) return;
536
537    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
538    evas_object_geometry_get(wd->arrow, NULL, NULL, &arrow_size.w,
539                             &arrow_size.h);
540
541    edje_object_part_unswallow(wd->base, wd->arrow);
542
543    switch (dir)
544      {
545       case ELM_CTXPOPUP_DIRECTION_RIGHT:
546          edje_object_signal_emit(wd->arrow, "elm,state,left", "elm");
547          edje_object_part_swallow(wd->base,
548                                   "elm.swallow.arrow_left",
549                                   wd->arrow);
550          if (base_size.h > 0)
551            {
552               if (y < ((arrow_size.h * 0.5) + base_size.y))
553                 y = 0;
554               else if (y > base_size.y + base_size.h - (arrow_size.h * 0.5))
555                 y = base_size.h - arrow_size.h;
556               else
557                 y = y - base_size.y - (arrow_size.h * 0.5);
558               drag = (double) (y) / (double) (base_size.h - arrow_size.h);
559               edje_object_part_drag_value_set(wd->base,
560                                               "elm.swallow.arrow_left",
561                                               1,
562                                               drag);
563            }
564          break;
565       case ELM_CTXPOPUP_DIRECTION_LEFT:
566          edje_object_signal_emit(wd->arrow, "elm,state,right", "elm");
567          edje_object_part_swallow(wd->base,
568                                   "elm.swallow.arrow_right",
569                                   wd->arrow);
570          if (base_size.h > 0)
571            {
572               if (y < ((arrow_size.h * 0.5) + base_size.y))
573                 y = 0;
574               else if (y > (base_size.y + base_size.h - (arrow_size.h * 0.5)))
575                 y = base_size.h - arrow_size.h;
576               else
577                 y = y - base_size.y - (arrow_size.h * 0.5);
578               drag = (double) (y) / (double) (base_size.h - arrow_size.h);
579               edje_object_part_drag_value_set(wd->base,
580                                               "elm.swallow.arrow_right",
581                                               0,
582                                               drag);
583            }
584          break;
585       case ELM_CTXPOPUP_DIRECTION_DOWN:
586          edje_object_signal_emit(wd->arrow, "elm,state,top", "elm");
587          edje_object_part_swallow(wd->base, "elm.swallow.arrow_up", wd->arrow);
588          if (base_size.w > 0)
589            {
590               if (x < ((arrow_size.w * 0.5) + base_size.x))
591                 x = 0;
592               else if (x > (base_size.x + base_size.w - (arrow_size.w * 0.5)))
593                 x = base_size.w - arrow_size.w;
594               else
595                 x = x - base_size.x - (arrow_size.w * 0.5);
596               drag = (double) (x) / (double) (base_size.w - arrow_size.w);
597               edje_object_part_drag_value_set(wd->base,
598                                               "elm.swallow.arrow_up",
599                                               drag,
600                                               1);
601            }
602          break;
603       case ELM_CTXPOPUP_DIRECTION_UP:
604          edje_object_signal_emit(wd->arrow, "elm,state,bottom", "elm");
605          edje_object_part_swallow(wd->base,
606                                   "elm.swallow.arrow_down",
607                                   wd->arrow);
608          if (base_size.w > 0)
609            {
610               if (x < ((arrow_size.w * 0.5) + base_size.x))
611                 x = 0;
612               else if (x > (base_size.x + base_size.w - (arrow_size.w * 0.5)))
613                 x = base_size.w - arrow_size.w;
614               else x = x - base_size.x - (arrow_size.w * 0.5);
615               drag = (double) (x) / (double) (base_size.w - arrow_size.w);
616               edje_object_part_drag_value_set(wd->base,
617                                               "elm.swallow.arrow_down",
618                                               drag,
619                                               0);
620            }
621          break;
622       default:
623          break;
624      }
625 }
626
627 static void
628 _hide_signal_emit(Evas_Object *obj, Elm_Ctxpopup_Direction dir)
629 {
630    Widget_Data *wd;
631
632    wd = elm_widget_data_get(obj);
633    if (!wd->visible) return;
634
635    switch (dir)
636      {
637         case ELM_CTXPOPUP_DIRECTION_UP:
638            edje_object_signal_emit(wd->base, "elm,state,hide,up", "elm");
639            break;
640         case ELM_CTXPOPUP_DIRECTION_LEFT:
641            edje_object_signal_emit(wd->base, "elm,state,hide,left", "elm");
642            break;
643         case ELM_CTXPOPUP_DIRECTION_RIGHT:
644            edje_object_signal_emit(wd->base, "elm,state,hide,right", "elm");
645            break;
646         case ELM_CTXPOPUP_DIRECTION_DOWN:
647            edje_object_signal_emit(wd->base, "elm,state,hide,down", "elm");
648            break;
649         default:
650            break;
651      }
652
653    edje_object_signal_emit(wd->bg, "elm,state,hide", "elm");
654 }
655
656 static void
657 _show_signal_emit(Evas_Object *obj, Elm_Ctxpopup_Direction dir)
658 {
659    Widget_Data *wd;
660
661    wd = elm_widget_data_get(obj);
662    if (wd->visible) return;
663
664    switch (dir)
665      {
666         case ELM_CTXPOPUP_DIRECTION_UP:
667            edje_object_signal_emit(wd->base, "elm,state,show,up", "elm");
668            break;
669         case ELM_CTXPOPUP_DIRECTION_LEFT:
670            edje_object_signal_emit(wd->base, "elm,state,show,left", "elm");
671            break;
672         case ELM_CTXPOPUP_DIRECTION_RIGHT:
673            edje_object_signal_emit(wd->base, "elm,state,show,right", "elm");
674            break;
675         case ELM_CTXPOPUP_DIRECTION_DOWN:
676            edje_object_signal_emit(wd->base, "elm,state,show,down", "elm");
677            break;
678         default:
679            break;
680      }
681 }
682
683 static void
684 _sizing_eval(Evas_Object *obj)
685 {
686    Widget_Data *wd;
687    Eina_List *elist;
688    Elm_Ctxpopup_Item *item;
689    Evas_Coord_Rectangle rect = { 0, 0, 1, 1 };
690    Evas_Coord_Point box_size = { 0, 0 };
691    Evas_Coord_Point _box_size = { 0, 0 };
692
693    wd = elm_widget_data_get(obj);
694    if (!wd) return;
695
696    //Box, Scroller
697    EINA_LIST_FOREACH(wd->items, elist, item)
698      {
699         _item_sizing_eval(item);
700         evas_object_size_hint_min_get(VIEW(item), &_box_size.x, &_box_size.y);
701         if (!wd->horizontal)
702           {
703              if (_box_size.x > box_size.x)
704                box_size.x = _box_size.x;
705              if (_box_size.y != -1)
706                box_size.y += _box_size.y;
707           }
708         else
709           {
710              if (_box_size.x != -1)
711                box_size.x += _box_size.x;
712              if (_box_size.y > box_size.y)
713                box_size.y = _box_size.y;
714           }
715      }
716
717    if (!wd->content)
718      {
719         evas_object_size_hint_min_set(wd->box, box_size.x, box_size.y);
720         evas_object_size_hint_min_set(wd->scr, box_size.x, box_size.y);
721      }
722
723    //Base
724    wd->dir = _calc_base_geometry(obj, &rect);
725    _show_signal_emit(obj, wd->dir);
726    _update_arrow(obj, wd->dir, rect);
727    _shift_base_by_arrow(wd->arrow, wd->dir, &rect);
728
729    //resize scroller according to final size.
730    if (!wd->content)
731      evas_object_smart_calculate(wd->scr);
732
733    evas_object_move(wd->base, rect.x, rect.y);
734    evas_object_resize(wd->base, rect.w, rect.h);
735 }
736
737 static void
738 _shift_base_by_arrow(Evas_Object *arrow, Elm_Ctxpopup_Direction dir,
739                      Evas_Coord_Rectangle *rect)
740 {
741    Evas_Coord arrow_w, arrow_h;
742
743    evas_object_geometry_get(arrow, NULL, NULL, &arrow_w, &arrow_h);
744
745    switch (dir)
746      {
747       case ELM_CTXPOPUP_DIRECTION_RIGHT:
748          rect->x += arrow_w;
749          break;
750       case ELM_CTXPOPUP_DIRECTION_LEFT:
751          rect->x -= arrow_w;
752          break;
753       case ELM_CTXPOPUP_DIRECTION_DOWN:
754          rect->y += arrow_h;
755          break;
756       case ELM_CTXPOPUP_DIRECTION_UP:
757          rect->y -= arrow_h;
758          break;
759       default:
760          break;
761      }
762 }
763
764 static void
765 _del_pre_hook(Evas_Object *obj)
766 {
767    Widget_Data *wd;
768
769    wd = elm_widget_data_get(obj);
770    if (!wd) return;
771
772    evas_object_event_callback_del_full(wd->box, EVAS_CALLBACK_RESIZE,
773                                        _content_resize, obj);
774    _parent_cut_off(obj);
775 }
776
777 static void
778 _del_hook(Evas_Object *obj)
779 {
780    Widget_Data *wd;
781
782    wd = elm_widget_data_get(obj);
783    if (!wd) return;
784
785    elm_ctxpopup_clear(obj);
786    evas_object_del(wd->arrow);
787    evas_object_del(wd->base);
788    free(wd);
789 }
790
791 //FIXME: lost the content size when theme hook is called.
792 static void
793 _theme_hook(Evas_Object *obj)
794 {
795    Widget_Data *wd;
796    Eina_List *elist;
797    Elm_Ctxpopup_Item *item;
798    Eina_Bool rtl;
799
800    wd = elm_widget_data_get(obj);
801    if (!wd) return;
802
803    _elm_widget_mirrored_reload(obj);
804    rtl = elm_widget_mirrored_get(obj);
805
806    _elm_theme_object_set(obj, wd->bg, "ctxpopup", "bg",
807                          elm_widget_style_get(obj));
808    _elm_theme_object_set(obj, wd->base, "ctxpopup", "base",
809                          elm_widget_style_get(obj));
810    _elm_theme_object_set(obj, wd->arrow, "ctxpopup", "arrow",
811                          elm_widget_style_get(obj));
812
813    //Items
814    EINA_LIST_FOREACH(wd->items, elist, item)
815      {
816         edje_object_mirrored_set(VIEW(item), rtl);
817
818         if (item->label && item->icon)
819           _elm_theme_object_set(obj, VIEW(item), "ctxpopup",
820                                 "icon_text_style_item",
821                                 elm_widget_style_get(obj));
822         else if (item->label)
823           _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "text_style_item",
824                                 elm_widget_style_get(obj));
825         else if (item->icon)
826           _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "icon_style_item",
827                                 elm_widget_style_get(obj));
828         if (item->label)
829           edje_object_part_text_set(VIEW(item), "elm.text", item->label);
830
831         if (elm_widget_item_disabled_get(item))
832           edje_object_signal_emit(VIEW(item), "elm,state,disabled", "elm");
833
834         edje_object_message_signal_process(VIEW(item));
835      }
836
837    if (wd->scr)
838      {
839         if (!strncmp(elm_object_style_get(obj), "default", strlen("default")))
840            elm_object_style_set(wd->scr, "ctxpopup");
841         else
842            elm_object_style_set(wd->scr, elm_object_style_get(obj));
843      }
844
845    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
846
847    if (wd->visible)
848      {
849         _scroller_size_reset(wd);
850         _sizing_eval(obj);
851      }
852 }
853
854 static void
855 _content_set_hook(Evas_Object *obj, const char *part, Evas_Object *content)
856 {
857    ELM_CHECK_WIDTYPE(obj, widtype);
858    Evas_Coord min_w = -1, min_h = -1;
859
860    Widget_Data *wd;
861
862    if (part && strcmp(part, "default")) return;
863
864    wd = elm_widget_data_get(obj);
865    if ((!wd) || (!content)) return;
866
867    if (wd->items) elm_ctxpopup_clear(obj);
868    if (wd->content) evas_object_del(wd->content);
869
870    //Use Box
871    wd->box = elm_box_add(obj);
872    evas_object_size_hint_weight_set(wd->box, EVAS_HINT_EXPAND,
873                                     EVAS_HINT_EXPAND);
874    evas_object_size_hint_weight_set(content, EVAS_HINT_EXPAND,
875                                     EVAS_HINT_EXPAND);
876    evas_object_size_hint_fill_set(content, EVAS_HINT_FILL,
877                                   EVAS_HINT_FILL);
878    evas_object_show(content);
879    evas_object_size_hint_min_get(content, &min_w, &min_h);
880    evas_object_size_hint_min_set(wd->box, min_w, min_h);
881    elm_box_pack_end(wd->box, content);
882
883    evas_object_event_callback_add(wd->box, EVAS_CALLBACK_RESIZE,
884                                   _content_resize, obj);
885    evas_object_event_callback_add(wd->box, EVAS_CALLBACK_DEL,
886                                   _content_del, obj);
887
888    elm_widget_sub_object_add(obj, wd->box);
889    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->box);
890
891    wd->content = content;
892    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
893
894    if (wd->visible)
895      _sizing_eval(obj);
896 }
897
898 static Evas_Object *
899 _content_unset_hook(Evas_Object *obj, const char *part)
900 {
901    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
902
903    Widget_Data *wd;
904    Evas_Object *content;
905    if (part && strcmp(part, "default")) return NULL;
906    wd = elm_widget_data_get(obj);
907    if (!wd) return NULL;
908
909    content = wd->content;
910    if (!content || !wd->box) return NULL;
911
912    edje_object_part_unswallow(wd->base, wd->box);
913    elm_widget_sub_object_del(obj, wd->box);
914    evas_object_event_callback_del(wd->box, EVAS_CALLBACK_DEL, _content_del);
915    edje_object_signal_emit(wd->base, "elm,state,content,disable", "elm");
916
917    evas_object_del(wd->box);
918    wd->box = NULL;
919    wd->content = NULL;
920    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
921
922    return content;
923 }
924
925 static Evas_Object *
926 _content_get_hook(const Evas_Object *obj, const char *part)
927 {
928    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
929    Widget_Data *wd;
930    if (part && strcmp(part, "default")) return NULL;
931    wd = elm_widget_data_get(obj);
932    if (!wd) return NULL;
933    return wd->content;
934 }
935
936 static void
937 _item_text_set_hook(Elm_Object_Item *it, const char *part, const char *label)
938 {
939    Widget_Data *wd;
940    Elm_Ctxpopup_Item *ctxpopup_it;
941
942    if (part && strcmp(part, "default")) return;
943
944    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
945
946    wd = elm_widget_data_get(WIDGET(ctxpopup_it));
947    if (!wd) return;
948
949    _item_label_set(ctxpopup_it, label);
950    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
951
952    if (wd->visible)
953      {
954         _scroller_size_reset(wd);
955         _sizing_eval(WIDGET(ctxpopup_it));
956      }
957 }
958
959 static const char *
960 _item_text_get_hook(const Elm_Object_Item *it, const char *part)
961 {
962    Elm_Ctxpopup_Item *ctxpopup_it;
963    if (part && strcmp(part, "default")) return NULL;
964    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
965    return ctxpopup_it->label;
966 }
967
968 static void
969 _item_content_set_hook(Elm_Object_Item *it,
970                        const char *part,
971                        Evas_Object *content)
972 {
973    Widget_Data *wd;
974    Elm_Ctxpopup_Item *ctxpopup_it;
975
976    if (part && strcmp(part, "icon")) return;
977
978    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
979
980    wd = elm_widget_data_get(WIDGET(ctxpopup_it));
981    if (!wd) return;
982
983    _item_icon_set(ctxpopup_it, content);
984    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
985
986    if (wd->visible)
987      {
988         _scroller_size_reset(wd);
989         _sizing_eval(WIDGET(ctxpopup_it));
990      }
991 }
992
993 static Evas_Object *
994 _item_content_get_hook(const Elm_Object_Item *it, const char *part)
995 {
996    Elm_Ctxpopup_Item *ctxpopup_it;
997    if (part && strcmp(part, "icon")) return NULL;
998    ctxpopup_it  = (Elm_Ctxpopup_Item *)it;
999    return ctxpopup_it->icon;
1000 }
1001
1002 static void
1003 _item_disable_hook(Elm_Object_Item *it)
1004 {
1005    Widget_Data *wd;
1006    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1007
1008    wd = elm_widget_data_get(WIDGET(ctxpopup_it));
1009    if (!wd) return;
1010
1011    if (elm_widget_item_disabled_get(it))
1012      edje_object_signal_emit(VIEW(ctxpopup_it), "elm,state,disabled", "elm");
1013    else
1014      edje_object_signal_emit(VIEW(ctxpopup_it), "elm,state,enabled", "elm");
1015 }
1016
1017 static void
1018 _item_signal_emit_hook(Elm_Object_Item *it, const char *emission,
1019                        const char *source)
1020 {
1021    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1022    edje_object_signal_emit(VIEW(ctxpopup_it), emission, source);
1023 }
1024
1025 static void
1026 _bg_clicked_cb(void *data, Evas_Object *obj __UNUSED__,
1027                const char *emission __UNUSED__, const char *source __UNUSED__)
1028 {
1029    Widget_Data *wd = elm_widget_data_get(data);
1030    if (!wd) return;
1031    _hide_signal_emit(data, wd->dir);
1032 }
1033
1034 static void
1035 _ctxpopup_show(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj,
1036                void *event_info __UNUSED__)
1037 {
1038    Widget_Data *wd;
1039
1040    wd = elm_widget_data_get(obj);
1041    if (!wd) return;
1042
1043    if ((!wd->items) && (!wd->content)) return;
1044
1045    wd->visible = EINA_TRUE;
1046
1047    evas_object_show(wd->bg);
1048    evas_object_show(wd->base);
1049    evas_object_show(wd->arrow);
1050
1051    edje_object_signal_emit(wd->bg, "elm,state,show", "elm");
1052    edje_object_signal_emit(wd->base, "elm,state,show", "elm");
1053
1054    _sizing_eval(obj);
1055
1056    elm_object_focus_set(obj, EINA_TRUE);
1057 }
1058
1059 static void
1060 _hide_finished(void *data, Evas_Object *obj __UNUSED__,
1061                const char *emission __UNUSED__, const char *source __UNUSED__)
1062 {
1063    evas_object_hide(data);
1064    evas_object_smart_callback_call(data, SIG_DISMISSED, NULL);
1065 }
1066
1067 static void
1068 _ctxpopup_hide(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj,
1069                void *event_info __UNUSED__)
1070 {
1071    Widget_Data *wd = elm_widget_data_get(obj);
1072    if ((!wd) || (!wd->visible)) return;
1073
1074    evas_object_hide(wd->bg);
1075    evas_object_hide(wd->arrow);
1076    evas_object_hide(wd->base);
1077
1078    _scroller_size_reset(wd);
1079
1080    wd->visible = EINA_FALSE;
1081 }
1082
1083 static void
1084 _content_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
1085                 void *event_info __UNUSED__)
1086 {
1087    Widget_Data *wd = elm_widget_data_get(data);
1088    if (!wd) return;
1089    elm_box_recalculate(wd->box);
1090    _sizing_eval(data);
1091 }
1092
1093 static void
1094 _scroller_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj,
1095                  void *event_info __UNUSED__)
1096 {
1097    Widget_Data *wd;
1098    Evas_Coord w, h;
1099
1100    wd = elm_widget_data_get(data);
1101    if (!wd) return;
1102    if (!wd->visible) return;
1103    if (wd->finished) return;
1104
1105    evas_object_geometry_get(obj, 0, 0, &w, &h);
1106
1107    if (w != 0 && h != 0)
1108      {
1109         if ((w <= wd->max_sc_w) && (h <= wd->max_sc_h))
1110           {
1111              _sizing_eval(data);
1112              wd->finished = EINA_TRUE;
1113              return;
1114           }
1115      }
1116
1117    if (wd->max_sc_w < w)
1118      wd->max_sc_w = w;
1119    if (wd->max_sc_h < h)
1120      wd->max_sc_h = h;
1121
1122    _sizing_eval(data);
1123 }
1124
1125 static void
1126 _ctxpopup_move(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj,
1127                void *event_info __UNUSED__)
1128 {
1129    Widget_Data *wd;
1130
1131    wd = elm_widget_data_get(obj);
1132
1133    if (!wd) return;
1134
1135    if (wd->visible)
1136      evas_object_show(wd->arrow);
1137
1138    _scroller_size_reset(wd);
1139    _sizing_eval(obj);
1140 }
1141
1142 static void
1143 _restack(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1144 {
1145    Widget_Data *wd = elm_widget_data_get(obj);
1146    if (!wd) return;
1147    evas_object_layer_set(wd->bg,
1148                          evas_object_layer_get(obj));
1149    evas_object_layer_set(wd->base,
1150                          evas_object_layer_get(obj));
1151 }
1152
1153 static void
1154 _item_select_cb(void *data, Evas_Object *obj __UNUSED__,
1155                 const char *emission __UNUSED__, const char *source __UNUSED__)
1156 {
1157    Elm_Ctxpopup_Item *item = data;
1158
1159    if (!item) return;
1160    if (elm_widget_item_disabled_get(item)) return;
1161
1162    if (item->func)
1163      item->func((void*)item->base.data, WIDGET(item), data);
1164 }
1165
1166 static void
1167 _item_icon_set(Elm_Ctxpopup_Item *item, Evas_Object *icon)
1168 {
1169    if (item->icon)
1170      evas_object_del(item->icon);
1171
1172    item->icon = icon;
1173    if (!icon) return;
1174
1175    edje_object_part_swallow(VIEW(item), "elm.swallow.icon", item->icon);
1176    edje_object_message_signal_process(VIEW(item));
1177 }
1178
1179 static void
1180 _item_label_set(Elm_Ctxpopup_Item *item, const char *label)
1181 {
1182    if (!eina_stringshare_replace(&item->label, label))
1183      return;
1184
1185    edje_object_part_text_set(VIEW(item), "elm.text", label);
1186    edje_object_message_signal_process(VIEW(item));
1187 }
1188
1189 static void
1190 _item_new(Elm_Ctxpopup_Item *item, char *group_name)
1191 {
1192    Widget_Data *wd;
1193
1194    wd = elm_widget_data_get(WIDGET(item));
1195    if (!wd) return;
1196
1197    VIEW(item) = edje_object_add(evas_object_evas_get(wd->base));
1198    edje_object_mirrored_set(VIEW(item), elm_widget_mirrored_get(WIDGET(item)));
1199    _elm_theme_object_set(WIDGET(item), VIEW(item), "ctxpopup", group_name,
1200                          elm_widget_style_get(WIDGET(item)));
1201    edje_object_signal_callback_add(VIEW(item), "elm,action,click", "",
1202                                    _item_select_cb, item);
1203    evas_object_size_hint_align_set(VIEW(item), EVAS_HINT_FILL, EVAS_HINT_FILL);
1204    evas_object_show(VIEW(item));
1205 }
1206
1207 static void
1208 _content_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
1209              void *event_info __UNUSED__)
1210 {
1211    elm_object_content_unset(data);
1212 }
1213
1214 static void
1215 _list_del(Widget_Data *wd)
1216 {
1217    if (!wd->scr) return;
1218
1219    edje_object_part_unswallow(wd->base, wd->scr);
1220    evas_object_del(wd->scr);
1221    wd->scr = NULL;
1222    wd->box = NULL;
1223 }
1224
1225 static void
1226 _list_new(Evas_Object *obj)
1227 {
1228    Widget_Data *wd;
1229    wd = elm_widget_data_get(obj);
1230    if (!wd) return;
1231
1232    //scroller
1233    wd->scr = elm_scroller_add(obj);
1234    elm_object_style_set(wd->scr, "ctxpopup");
1235    evas_object_size_hint_align_set(wd->scr, EVAS_HINT_FILL, EVAS_HINT_FILL);
1236    evas_object_event_callback_add(wd->scr, EVAS_CALLBACK_RESIZE,
1237                                   _scroller_resize, obj);
1238    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->scr);
1239
1240    //box
1241    wd->box = elm_box_add(obj);
1242    evas_object_size_hint_weight_set(wd->box, EVAS_HINT_EXPAND,
1243                                     EVAS_HINT_EXPAND);
1244
1245    elm_object_content_set(wd->scr, wd->box);
1246    elm_ctxpopup_horizontal_set(obj, wd->horizontal);
1247 }
1248
1249 static void
1250 _remove_items(Widget_Data *wd)
1251 {
1252    Eina_List *elist;
1253    Elm_Ctxpopup_Item *item;
1254
1255    if (!wd->items) return;
1256
1257    EINA_LIST_FOREACH(wd->items, elist, item)
1258      {
1259         if (item->label)
1260           eina_stringshare_del(item->label);
1261         if (item->icon)
1262           evas_object_del(item->icon);
1263         wd->items = eina_list_remove(wd->items, item);
1264         elm_widget_item_free(item);
1265      }
1266
1267    wd->items = NULL;
1268 }
1269
1270 static Eina_Bool
1271 _item_del_pre_hook(Elm_Object_Item *it)
1272 {
1273    Widget_Data *wd;
1274    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1275
1276    wd = elm_widget_data_get(WIDGET(ctxpopup_it));
1277    if (!wd) return EINA_FALSE;
1278
1279    if (ctxpopup_it->icon)
1280      evas_object_del(ctxpopup_it->icon);
1281    if (VIEW(ctxpopup_it))
1282      evas_object_del(VIEW(ctxpopup_it));
1283
1284    eina_stringshare_del(ctxpopup_it->label);
1285
1286    wd->items = eina_list_remove(wd->items, ctxpopup_it);
1287
1288    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1289
1290    if (eina_list_count(wd->items) < 1)
1291      {
1292         evas_object_hide(WIDGET(ctxpopup_it));
1293         return EINA_TRUE;
1294      }
1295
1296    if (wd->visible)
1297      _sizing_eval(WIDGET(ctxpopup_it));
1298
1299    return EINA_TRUE;
1300 }
1301
1302 static void
1303 _disable_hook(Evas_Object *obj)
1304 {
1305    Eina_List *l;
1306    Elm_Object_Item *it;
1307
1308    Widget_Data *wd = elm_widget_data_get(obj);
1309    if (!wd) return;
1310
1311    EINA_LIST_FOREACH(wd->items, l, it)
1312      elm_object_item_disabled_set(it, elm_widget_disabled_get(obj));
1313 }
1314
1315 static void
1316 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
1317 {
1318    Widget_Data *wd = elm_widget_data_get(obj);
1319    if (!wd) return;
1320    edje_object_signal_emit(wd->base, emission, source);
1321 }
1322
1323 static void
1324 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
1325 {
1326    Widget_Data *wd = elm_widget_data_get(obj);
1327    if (!wd) return;
1328    edje_object_signal_callback_add(wd->base, emission, source, func_cb, data);
1329 }
1330
1331 static void
1332 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
1333 {
1334    Widget_Data *wd = elm_widget_data_get(obj);
1335    if (!wd) return;
1336    edje_object_signal_callback_del_full(wd->base, emission, source, func_cb, data);
1337 }
1338
1339 EAPI Evas_Object *
1340 elm_ctxpopup_add(Evas_Object *parent)
1341 {
1342    Evas_Object *obj;
1343    Evas *e;
1344    Widget_Data *wd;
1345
1346    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
1347
1348    ELM_SET_WIDTYPE(widtype, "ctxpopup");
1349    elm_widget_type_set(obj, "ctxpopup");
1350    elm_widget_data_set(obj, wd);
1351    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
1352    elm_widget_del_hook_set(obj, _del_hook);
1353    elm_widget_theme_hook_set(obj, _theme_hook);
1354    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
1355    elm_widget_can_focus_set(obj, EINA_TRUE);
1356    elm_widget_event_hook_set(obj, _event_hook);
1357    elm_widget_content_set_hook_set(obj, _content_set_hook);
1358    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
1359    elm_widget_content_get_hook_set(obj, _content_get_hook);
1360    elm_widget_disable_hook_set(obj, _disable_hook);
1361    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
1362    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
1363    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
1364
1365    //Background
1366    wd->bg = edje_object_add(e);
1367    elm_widget_sub_object_add(obj, wd->bg);
1368    _elm_theme_object_set(obj, wd->bg, "ctxpopup", "bg", "default");
1369    edje_object_signal_callback_add(wd->bg,
1370                                    "elm,action,click",
1371                                    "",
1372                                    _bg_clicked_cb,
1373                                     obj);
1374    //Base
1375    wd->base = edje_object_add(e);
1376    elm_widget_sub_object_add(obj, wd->base);
1377    _elm_theme_object_set(obj, wd->base, "ctxpopup", "base", "default");
1378    edje_object_signal_callback_add(wd->base, "elm,action,hide,finished", "",
1379                                    _hide_finished, obj);
1380
1381    //Arrow
1382    wd->arrow = edje_object_add(e);
1383    elm_widget_sub_object_add(obj, wd->arrow);
1384    _elm_theme_object_set(obj, wd->arrow, "ctxpopup", "arrow", "default");
1385
1386    wd->dir_priority[0] = ELM_CTXPOPUP_DIRECTION_UP;
1387    wd->dir_priority[1] = ELM_CTXPOPUP_DIRECTION_LEFT;
1388    wd->dir_priority[2] = ELM_CTXPOPUP_DIRECTION_RIGHT;
1389    wd->dir_priority[3] = ELM_CTXPOPUP_DIRECTION_DOWN;
1390    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1391
1392    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _ctxpopup_show,
1393                                   NULL);
1394    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _ctxpopup_hide,
1395                                   NULL);
1396    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _ctxpopup_move,
1397                                   NULL);
1398    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESTACK, _restack, obj);
1399    evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj);
1400    evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj);
1401    evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj);
1402    evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj);
1403
1404    evas_object_smart_callbacks_descriptions_set(obj, _signals);
1405
1406    //default parent is to be hover parent
1407    elm_ctxpopup_hover_parent_set(obj, parent);
1408
1409    return obj;
1410 }
1411
1412 EAPI void
1413 elm_ctxpopup_hover_parent_set(Evas_Object *obj, Evas_Object *parent)
1414 {
1415    ELM_CHECK_WIDTYPE(obj, widtype);
1416
1417    Widget_Data *wd;
1418    Evas_Coord x, y, w, h;
1419
1420    wd = elm_widget_data_get(obj);
1421    if ((!wd) || (!parent)) return;
1422
1423    _parent_cut_off(obj);
1424
1425    if (parent)
1426      {
1427         evas_object_event_callback_add(parent,
1428                                        EVAS_CALLBACK_DEL,
1429                                        _parent_del,
1430                                        obj);
1431         evas_object_event_callback_add(parent,
1432                                        EVAS_CALLBACK_MOVE,
1433                                        _parent_move,
1434                                        obj);
1435         evas_object_event_callback_add(parent,
1436                                        EVAS_CALLBACK_RESIZE,
1437                                        _parent_resize,
1438                                        obj);
1439      }
1440
1441    elm_widget_sub_object_add(parent, obj);
1442    wd->parent = parent;
1443
1444    //Update Background
1445    evas_object_geometry_get(parent, &x, &y, &w, &h);
1446    evas_object_move(wd->bg, x, y);
1447    evas_object_resize(wd->bg, w, h);
1448
1449    if (wd->visible) _sizing_eval(obj);
1450 }
1451
1452 EAPI Evas_Object *
1453 elm_ctxpopup_hover_parent_get(const Evas_Object *obj)
1454 {
1455    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1456
1457    Widget_Data *wd;
1458
1459    wd = elm_widget_data_get(obj);
1460    if (!wd) return NULL;
1461
1462    return wd->parent;
1463 }
1464
1465 EAPI void
1466 elm_ctxpopup_clear(Evas_Object * obj)
1467 {
1468    ELM_CHECK_WIDTYPE(obj, widtype);
1469
1470    Widget_Data *wd = elm_widget_data_get(obj);
1471    if (!wd) return;
1472
1473    _remove_items(wd);
1474    _list_del(wd);
1475    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1476 }
1477
1478 EAPI void
1479 elm_ctxpopup_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
1480 {
1481    ELM_CHECK_WIDTYPE(obj, widtype);
1482
1483    Widget_Data *wd;
1484
1485    wd = elm_widget_data_get(obj);
1486    if (!wd) return;
1487
1488    wd->horizontal = !!horizontal;
1489
1490    if ((!wd->scr) && (!wd->box))
1491       return;
1492
1493    if (!horizontal)
1494      {
1495         elm_box_horizontal_set(wd->box, EINA_FALSE);
1496         elm_scroller_bounce_set(wd->scr, EINA_FALSE, EINA_TRUE);
1497      }
1498    else
1499      {
1500         elm_box_horizontal_set(wd->box, EINA_TRUE);
1501         elm_scroller_bounce_set(wd->scr, EINA_TRUE, EINA_FALSE);
1502      }
1503
1504    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1505
1506    if (wd->visible)
1507       _sizing_eval(obj);
1508 }
1509
1510 EAPI Eina_Bool
1511 elm_ctxpopup_horizontal_get(const Evas_Object *obj)
1512 {
1513    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1514
1515    Widget_Data *wd;
1516
1517    wd = elm_widget_data_get(obj);
1518    if (!wd) return EINA_FALSE;
1519
1520    return wd->horizontal;
1521 }
1522
1523 EAPI Elm_Object_Item *
1524 elm_ctxpopup_item_append(Evas_Object *obj, const char *label,
1525                          Evas_Object *icon, Evas_Smart_Cb func,
1526                          const void *data)
1527 {
1528    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1529
1530    Widget_Data *wd;
1531    Evas_Object *content;
1532    Elm_Ctxpopup_Item *item;
1533
1534    wd = elm_widget_data_get(obj);
1535    if (!wd) return NULL;
1536
1537    item = elm_widget_item_new(obj, Elm_Ctxpopup_Item);
1538    if (!item) return NULL;
1539
1540    elm_widget_item_del_pre_hook_set(item, _item_del_pre_hook);
1541    elm_widget_item_disable_hook_set(item, _item_disable_hook);
1542    elm_widget_item_text_set_hook_set(item, _item_text_set_hook);
1543    elm_widget_item_text_get_hook_set(item, _item_text_get_hook);
1544    elm_widget_item_content_set_hook_set(item, _item_content_set_hook);
1545    elm_widget_item_content_get_hook_set(item, _item_content_get_hook);
1546    elm_widget_item_signal_emit_hook_set(item, _item_signal_emit_hook);
1547
1548    //The first item is appended.
1549    content = elm_object_content_unset(obj);
1550    if (content) evas_object_del(content);
1551
1552    if (!wd->items)
1553      _list_new(obj);
1554
1555    item->func = func;
1556    item->base.data = data;
1557
1558    if (icon && label)
1559      _item_new(item, "icon_text_style_item");
1560    else if (label)
1561      _item_new(item, "text_style_item");
1562    else
1563      _item_new(item, "icon_style_item");
1564
1565    _item_icon_set(item, icon);
1566    _item_label_set(item, label);
1567    elm_box_pack_end(wd->box, VIEW(item));
1568    wd->items = eina_list_append(wd->items, item);
1569    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1570
1571    if (wd->visible)
1572      {
1573         _scroller_size_reset(wd);
1574         _sizing_eval(obj);
1575      }
1576
1577    return (Elm_Object_Item *)item;
1578 }
1579
1580 EAPI void
1581 elm_ctxpopup_direction_priority_set(Evas_Object *obj,
1582                                     Elm_Ctxpopup_Direction first,
1583                                     Elm_Ctxpopup_Direction second,
1584                                     Elm_Ctxpopup_Direction third,
1585                                     Elm_Ctxpopup_Direction fourth)
1586 {
1587    ELM_CHECK_WIDTYPE(obj, widtype);
1588    Widget_Data *wd;
1589
1590    wd = elm_widget_data_get(obj);
1591    if (!wd) return;
1592
1593    wd->dir_priority[0] = first;
1594    wd->dir_priority[1] = second;
1595    wd->dir_priority[2] = third;
1596    wd->dir_priority[3] = fourth;
1597
1598    if (wd->visible)
1599      _sizing_eval(obj);
1600 }
1601
1602 EAPI void
1603 elm_ctxpopup_direction_priority_get(Evas_Object *obj,
1604                                     Elm_Ctxpopup_Direction *first,
1605                                     Elm_Ctxpopup_Direction *second,
1606                                     Elm_Ctxpopup_Direction *third,
1607                                     Elm_Ctxpopup_Direction *fourth)
1608 {
1609    ELM_CHECK_WIDTYPE(obj, widtype);
1610    Widget_Data *wd;
1611
1612    wd = elm_widget_data_get(obj);
1613    if (!wd) return;
1614
1615    if (first) *first = wd->dir_priority[0];
1616    if (second) *second = wd->dir_priority[1];
1617    if (third) *third = wd->dir_priority[2];
1618    if (fourth) *fourth = wd->dir_priority[3];
1619 }
1620
1621 EAPI Elm_Ctxpopup_Direction
1622 elm_ctxpopup_direction_get(const Evas_Object *obj)
1623 {
1624    ELM_CHECK_WIDTYPE(obj, widtype) ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1625    Widget_Data *wd;
1626
1627    wd = elm_widget_data_get(obj);
1628    if (!wd) return ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1629    return wd->dir;
1630 }
1631
1632 EAPI void
1633 elm_ctxpopup_dismiss(Evas_Object *obj)
1634 {
1635    ELM_CHECK_WIDTYPE(obj, widtype);
1636    Widget_Data *wd = elm_widget_data_get(obj);
1637    if (!wd) return;
1638    _hide_signal_emit(obj, wd->dir);
1639 }