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