Elementary migration revision 70375
[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 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 //TODO: compress item - different from opensource
628 static void
629 _compress_item(Evas_Object *obj)
630 {
631    Widget_Data *wd;
632    Eina_List *elist;
633    Elm_Ctxpopup_Item *item;
634
635    wd = elm_widget_data_get(obj);
636    if (!wd) return;
637
638    EINA_LIST_FOREACH(wd->items, elist, item)
639      {
640            edje_object_signal_emit(item->base.view, "elm,state,compress", "elm");
641      }
642 }
643
644 static void
645 _hide_signal_emit(Evas_Object *obj, Elm_Ctxpopup_Direction dir)
646 {
647    Widget_Data *wd;
648
649    wd = elm_widget_data_get(obj);
650    if (!wd->visible) return;
651
652    switch (dir)
653      {
654         case ELM_CTXPOPUP_DIRECTION_UP:
655            edje_object_signal_emit(wd->base, "elm,state,hide,up", "elm");
656            break;
657         case ELM_CTXPOPUP_DIRECTION_LEFT:
658            edje_object_signal_emit(wd->base, "elm,state,hide,left", "elm");
659            break;
660         case ELM_CTXPOPUP_DIRECTION_RIGHT:
661            edje_object_signal_emit(wd->base, "elm,state,hide,right", "elm");
662            break;
663         case ELM_CTXPOPUP_DIRECTION_DOWN:
664            edje_object_signal_emit(wd->base, "elm,state,hide,down", "elm");
665            break;
666         default:
667            break;
668      }
669
670    edje_object_signal_emit(wd->bg, "elm,state,hide", "elm");
671 }
672
673 static void
674 _show_signal_emit(Evas_Object *obj, Elm_Ctxpopup_Direction dir)
675 {
676    Widget_Data *wd;
677
678    wd = elm_widget_data_get(obj);
679    if (wd->visible) return;
680
681    switch (dir)
682      {
683         case ELM_CTXPOPUP_DIRECTION_UP:
684            edje_object_signal_emit(wd->base, "elm,state,show,up", "elm");
685            break;
686         case ELM_CTXPOPUP_DIRECTION_LEFT:
687            edje_object_signal_emit(wd->base, "elm,state,show,left", "elm");
688            break;
689         case ELM_CTXPOPUP_DIRECTION_RIGHT:
690            edje_object_signal_emit(wd->base, "elm,state,show,right", "elm");
691            break;
692         case ELM_CTXPOPUP_DIRECTION_DOWN:
693            edje_object_signal_emit(wd->base, "elm,state,show,down", "elm");
694            break;
695         default:
696            break;
697      }
698 }
699
700 static void
701 _sizing_eval(Evas_Object *obj)
702 {
703    Widget_Data *wd;
704    Eina_List *elist;
705    Elm_Ctxpopup_Item *item;
706    Evas_Coord_Rectangle rect = { 0, 0, 1, 1 };
707    Evas_Coord_Point box_size = { 0, 0 };
708    Evas_Coord_Point _box_size = { 0, 0 };
709
710    wd = elm_widget_data_get(obj);
711    if (!wd) return;
712    if (!wd->parent) return;
713
714    //Box, Scroller
715    EINA_LIST_FOREACH(wd->items, elist, item)
716      {
717         _item_sizing_eval(item);
718         evas_object_size_hint_min_get(VIEW(item), &_box_size.x, &_box_size.y);
719         if (!wd->horizontal)
720           {
721              if (_box_size.x > box_size.x)
722                box_size.x = _box_size.x;
723              if (_box_size.y != -1)
724                box_size.y += _box_size.y;
725           }
726         else
727           {
728              if (_box_size.x != -1)
729                box_size.x += _box_size.x;
730              if (_box_size.y > box_size.y)
731                box_size.y = _box_size.y;
732           }
733      }
734
735    if (!wd->content)
736      {
737         evas_object_size_hint_min_set(wd->box, box_size.x, box_size.y);
738         evas_object_size_hint_min_set(wd->scr, box_size.x, box_size.y);
739      }
740
741    //Base
742    wd->dir = _calc_base_geometry(obj, &rect);
743
744    //TODO: compress item - different from opensource
745    if (!wd->horizontal && !wd->content)
746      _compress_item(obj);
747
748    _show_signal_emit(obj, wd->dir);
749    _update_arrow(obj, wd->dir, rect);
750    _shift_base_by_arrow(wd->arrow, wd->dir, &rect);
751
752    //resize scroller according to final size.
753    if (!wd->content)
754      evas_object_smart_calculate(wd->scr);
755
756    evas_object_move(wd->base, rect.x, rect.y);
757    evas_object_resize(wd->base, rect.w, rect.h);
758 }
759
760 static void
761 _shift_base_by_arrow(Evas_Object *arrow, Elm_Ctxpopup_Direction dir,
762                      Evas_Coord_Rectangle *rect)
763 {
764    Evas_Coord arrow_w, arrow_h;
765
766    evas_object_geometry_get(arrow, NULL, NULL, &arrow_w, &arrow_h);
767
768    switch (dir)
769      {
770       case ELM_CTXPOPUP_DIRECTION_RIGHT:
771          rect->x += arrow_w;
772          break;
773       case ELM_CTXPOPUP_DIRECTION_LEFT:
774          rect->x -= arrow_w;
775          break;
776       case ELM_CTXPOPUP_DIRECTION_DOWN:
777          rect->y += arrow_h;
778          break;
779       case ELM_CTXPOPUP_DIRECTION_UP:
780          rect->y -= arrow_h;
781          break;
782       default:
783          break;
784      }
785 }
786
787 static void
788 _del_pre_hook(Evas_Object *obj)
789 {
790    Widget_Data *wd;
791
792    wd = elm_widget_data_get(obj);
793    if (!wd) return;
794
795    evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE,
796                                        _parent_resize, obj);
797    evas_object_event_callback_del_full(wd->box, EVAS_CALLBACK_RESIZE,
798                                        _content_resize, obj);
799
800    _parent_cut_off(obj);
801 }
802
803 static void
804 _del_hook(Evas_Object *obj)
805 {
806    Widget_Data *wd;
807
808    wd = elm_widget_data_get(obj);
809    if (!wd) return;
810
811    elm_ctxpopup_clear(obj);
812    evas_object_del(wd->arrow);
813    evas_object_del(wd->base);
814    free(wd);
815 }
816
817 //FIXME: lost the content size when theme hook is called.
818 static void
819 _theme_hook(Evas_Object *obj)
820 {
821    Widget_Data *wd;
822    Eina_List *elist;
823    Elm_Ctxpopup_Item *item;
824    int idx = 0;
825    Eina_Bool rtl;
826
827    wd = elm_widget_data_get(obj);
828    if (!wd) return;
829
830    _elm_widget_mirrored_reload(obj);
831    rtl = elm_widget_mirrored_get(obj);
832
833    _elm_theme_object_set(obj, wd->bg, "ctxpopup", "bg",
834                          elm_widget_style_get(obj));
835    _elm_theme_object_set(obj, wd->base, "ctxpopup", "base",
836                          elm_widget_style_get(obj));
837    _elm_theme_object_set(obj, wd->arrow, "ctxpopup", "arrow",
838                          elm_widget_style_get(obj));
839
840    //Items
841    EINA_LIST_FOREACH(wd->items, elist, item)
842      {
843         edje_object_mirrored_set(VIEW(item), rtl);
844
845         if (item->label && item->icon)
846           _elm_theme_object_set(obj, VIEW(item), "ctxpopup",
847                                 "icon_text_style_item",
848                                 elm_widget_style_get(obj));
849         else if (item->label)
850           _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "text_style_item",
851                                 elm_widget_style_get(obj));
852         else if (item->icon)
853           _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "icon_style_item",
854                                 elm_widget_style_get(obj));
855         if (item->label)
856           edje_object_part_text_set(VIEW(item), "elm.text", item->label);
857
858         if (elm_widget_item_disabled_get(item))
859           edje_object_signal_emit(VIEW(item), "elm,state,disabled", "elm");
860
861        /*
862         *  For separator, if the first item has visible separator,
863         *  then it should be aligned with edge of the base part.
864         *  In some cases, it gives improper display. Ex) rounded corner
865         *  So the first item separator should be invisible.
866         */
867        if ((idx++) == 0)
868          edje_object_signal_emit(VIEW(item), "elm,state,default", "elm");
869        else
870          {
871            if(!wd->horizontal)
872              edje_object_signal_emit(VIEW(item), "elm,state,vertical", "elm");
873            else
874              edje_object_signal_emit(VIEW(item), "elm,state,horizontal", "elm");
875          }
876
877         edje_object_message_signal_process(VIEW(item));
878      }
879
880    if (evas_object_visible_get(wd->bg))
881      edje_object_signal_emit(wd->bg, "elm,state,show", "elm");
882
883    if (wd->scr)
884      {
885         if (!strncmp(elm_object_style_get(obj), "default", strlen("default")))
886            elm_object_style_set(wd->scr, "ctxpopup");
887         else
888            elm_object_style_set(wd->scr, elm_object_style_get(obj));
889      }
890
891    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
892
893    if (wd->visible)
894      {
895         _scroller_size_reset(wd);
896         _sizing_eval(obj);
897      }
898 }
899
900 static void
901 _content_set_hook(Evas_Object *obj, const char *part, Evas_Object *content)
902 {
903    ELM_CHECK_WIDTYPE(obj, widtype);
904    Evas_Coord min_w = -1, min_h = -1;
905
906    Widget_Data *wd;
907
908    if (part && strcmp(part, "default")) return;
909
910    wd = elm_widget_data_get(obj);
911    if ((!wd) || (!content)) return;
912
913    if (wd->items) elm_ctxpopup_clear(obj);
914    if (wd->content) evas_object_del(wd->content);
915
916    //Use Box
917    wd->box = elm_box_add(obj);
918    evas_object_size_hint_weight_set(wd->box, EVAS_HINT_EXPAND,
919                                     EVAS_HINT_EXPAND);
920    evas_object_size_hint_weight_set(content, EVAS_HINT_EXPAND,
921                                     EVAS_HINT_EXPAND);
922    evas_object_size_hint_fill_set(content, EVAS_HINT_FILL,
923                                   EVAS_HINT_FILL);
924    evas_object_show(content);
925    evas_object_size_hint_min_get(content, &min_w, &min_h);
926    evas_object_size_hint_min_set(wd->box, min_w, min_h);
927    elm_box_pack_end(wd->box, content);
928
929    evas_object_event_callback_add(wd->box, EVAS_CALLBACK_RESIZE,
930                                   _content_resize, obj);
931    evas_object_event_callback_add(wd->box, EVAS_CALLBACK_DEL,
932                                   _content_del, obj);
933
934    elm_widget_sub_object_add(obj, wd->box);
935    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->box);
936
937    wd->content = content;
938    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
939
940    if (wd->visible)
941      _sizing_eval(obj);
942 }
943
944 static Evas_Object *
945 _content_unset_hook(Evas_Object *obj, const char *part)
946 {
947    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
948
949    Widget_Data *wd;
950    Evas_Object *content;
951    if (part && strcmp(part, "default")) return NULL;
952    wd = elm_widget_data_get(obj);
953    if (!wd) return NULL;
954
955    content = wd->content;
956    if (!content || !wd->box) return NULL;
957
958    edje_object_part_unswallow(wd->base, wd->box);
959    elm_widget_sub_object_del(obj, wd->box);
960    evas_object_event_callback_del(wd->box, EVAS_CALLBACK_DEL, _content_del);
961    edje_object_signal_emit(wd->base, "elm,state,content,disable", "elm");
962
963    evas_object_del(wd->box);
964    wd->box = NULL;
965    wd->content = NULL;
966    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
967
968    return content;
969 }
970
971 static Evas_Object *
972 _content_get_hook(const Evas_Object *obj, const char *part)
973 {
974    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
975    Widget_Data *wd;
976    if (part && strcmp(part, "default")) return NULL;
977    wd = elm_widget_data_get(obj);
978    if (!wd) return NULL;
979    return wd->content;
980 }
981
982 static void
983 _item_text_set_hook(Elm_Object_Item *it, const char *part, const char *label)
984 {
985    Widget_Data *wd;
986    Elm_Ctxpopup_Item *ctxpopup_it;
987
988    if (part && strcmp(part, "default")) return;
989
990    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
991
992    wd = elm_widget_data_get(WIDGET(ctxpopup_it));
993    if (!wd) return;
994
995    _item_label_set(ctxpopup_it, label);
996    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
997
998    if (wd->visible)
999      {
1000         _scroller_size_reset(wd);
1001         _sizing_eval(WIDGET(ctxpopup_it));
1002      }
1003 }
1004
1005 static const char *
1006 _item_text_get_hook(const Elm_Object_Item *it, const char *part)
1007 {
1008    Elm_Ctxpopup_Item *ctxpopup_it;
1009    if (part && strcmp(part, "default")) return NULL;
1010    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1011    return ctxpopup_it->label;
1012 }
1013
1014 static void
1015 _item_content_set_hook(Elm_Object_Item *it,
1016                        const char *part,
1017                        Evas_Object *content)
1018 {
1019    Widget_Data *wd;
1020    Elm_Ctxpopup_Item *ctxpopup_it;
1021
1022    if (part && strcmp(part, "icon")) return;
1023
1024    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1025
1026    wd = elm_widget_data_get(WIDGET(ctxpopup_it));
1027    if (!wd) return;
1028
1029    _item_icon_set(ctxpopup_it, content);
1030    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1031
1032    if (wd->visible)
1033      {
1034         _scroller_size_reset(wd);
1035         _sizing_eval(WIDGET(ctxpopup_it));
1036      }
1037 }
1038
1039 static Evas_Object *
1040 _item_content_get_hook(const Elm_Object_Item *it, const char *part)
1041 {
1042    Elm_Ctxpopup_Item *ctxpopup_it;
1043    if (part && strcmp(part, "icon")) return NULL;
1044    ctxpopup_it  = (Elm_Ctxpopup_Item *)it;
1045    return ctxpopup_it->icon;
1046 }
1047
1048 static void
1049 _item_disable_hook(Elm_Object_Item *it)
1050 {
1051    Widget_Data *wd;
1052    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1053
1054    wd = elm_widget_data_get(WIDGET(ctxpopup_it));
1055    if (!wd) return;
1056
1057    if (elm_widget_item_disabled_get(it))
1058      edje_object_signal_emit(VIEW(ctxpopup_it), "elm,state,disabled", "elm");
1059    else
1060      edje_object_signal_emit(VIEW(ctxpopup_it), "elm,state,enabled", "elm");
1061 }
1062
1063 static void
1064 _item_signal_emit_hook(Elm_Object_Item *it, const char *emission,
1065                        const char *source)
1066 {
1067    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1068    edje_object_signal_emit(VIEW(ctxpopup_it), emission, source);
1069 }
1070
1071 static void
1072 _bg_clicked_cb(void *data, Evas_Object *obj __UNUSED__,
1073                const char *emission __UNUSED__, const char *source __UNUSED__)
1074 {
1075    Widget_Data *wd = elm_widget_data_get(data);
1076    if (!wd) return;
1077    _hide_signal_emit(data, wd->dir);
1078 }
1079
1080 static void
1081 _ctxpopup_show(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj,
1082                void *event_info __UNUSED__)
1083 {
1084    Widget_Data *wd;
1085    Eina_List *elist;
1086    Elm_Ctxpopup_Item *item;
1087    int idx = 0;
1088
1089    wd = elm_widget_data_get(obj);
1090    if (!wd) return;
1091
1092    if ((!wd->items) && (!wd->content)) return;
1093
1094    wd->visible = EINA_TRUE;
1095
1096    evas_object_show(wd->bg);
1097    evas_object_show(wd->base);
1098    evas_object_show(wd->arrow);
1099
1100    edje_object_signal_emit(wd->bg, "elm,state,show", "elm");
1101
1102    EINA_LIST_FOREACH(wd->items, elist, item)
1103      {
1104        if (idx++ == 0)
1105          edje_object_signal_emit(VIEW(item), "elm,state,default", "elm");
1106        else
1107          {
1108            if(!wd->horizontal)
1109              edje_object_signal_emit(VIEW(item), "elm,state,vertical", "elm");
1110            else
1111              edje_object_signal_emit(VIEW(item), "elm,state,horizontal", "elm");
1112          }
1113      }
1114
1115    edje_object_signal_emit(wd->base, "elm,state,show", "elm");
1116
1117    _sizing_eval(obj);
1118
1119    elm_object_focus_set(obj, EINA_TRUE);
1120 }
1121
1122 static void
1123 _hide_finished(void *data, Evas_Object *obj __UNUSED__,
1124                const char *emission __UNUSED__, const char *source __UNUSED__)
1125 {
1126    evas_object_hide(data);
1127    evas_object_smart_callback_call(data, SIG_DISMISSED, NULL);
1128 }
1129
1130 static void
1131 _ctxpopup_hide(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj,
1132                void *event_info __UNUSED__)
1133 {
1134    Widget_Data *wd = elm_widget_data_get(obj);
1135    if ((!wd) || (!wd->visible)) return;
1136
1137    evas_object_hide(wd->bg);
1138    evas_object_hide(wd->arrow);
1139    evas_object_hide(wd->base);
1140
1141    _scroller_size_reset(wd);
1142
1143    wd->visible = EINA_FALSE;
1144 }
1145
1146 static void
1147 _content_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
1148                 void *event_info __UNUSED__)
1149 {
1150    Widget_Data *wd = elm_widget_data_get(data);
1151    if (!wd) return;
1152    elm_box_recalculate(wd->box);
1153    _sizing_eval(data);
1154 }
1155
1156 static void
1157 _scroller_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj,
1158                  void *event_info __UNUSED__)
1159 {
1160    Widget_Data *wd;
1161    Evas_Coord w, h;
1162
1163    wd = elm_widget_data_get(data);
1164    if (!wd) return;
1165    if (!wd->visible) return;
1166    if (wd->finished) return;
1167
1168    evas_object_geometry_get(obj, 0, 0, &w, &h);
1169
1170    if (w != 0 && h != 0)
1171      {
1172         if ((w <= wd->max_sc_w) && (h <= wd->max_sc_h))
1173           {
1174              _sizing_eval(data);
1175              wd->finished = EINA_TRUE;
1176              return;
1177           }
1178      }
1179
1180    if (wd->max_sc_w < w)
1181      wd->max_sc_w = w;
1182    if (wd->max_sc_h < h)
1183      wd->max_sc_h = h;
1184
1185    _sizing_eval(data);
1186 }
1187
1188 static void
1189 _ctxpopup_move(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj,
1190                void *event_info __UNUSED__)
1191 {
1192    Widget_Data *wd;
1193
1194    wd = elm_widget_data_get(obj);
1195
1196    if (!wd) return;
1197
1198    if (wd->visible)
1199      evas_object_show(wd->arrow);
1200
1201    _scroller_size_reset(wd);
1202    _sizing_eval(obj);
1203 }
1204
1205 static void
1206 _restack(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1207 {
1208    Widget_Data *wd = elm_widget_data_get(obj);
1209    if (!wd) return;
1210    evas_object_layer_set(wd->bg,
1211                          evas_object_layer_get(obj));
1212    evas_object_layer_set(wd->base,
1213                          evas_object_layer_get(obj));
1214 }
1215
1216 static void
1217 _item_select_cb(void *data, Evas_Object *obj __UNUSED__,
1218                 const char *emission __UNUSED__, const char *source __UNUSED__)
1219 {
1220    Elm_Ctxpopup_Item *item = data;
1221
1222    if (!item) return;
1223    if (elm_widget_item_disabled_get(item)) return;
1224
1225    if (item->func)
1226      item->func((void*)item->base.data, WIDGET(item), data);
1227 }
1228
1229 static void
1230 _item_icon_set(Elm_Ctxpopup_Item *item, Evas_Object *icon)
1231 {
1232    if (item->icon)
1233      evas_object_del(item->icon);
1234
1235    item->icon = icon;
1236    if (!icon) return;
1237
1238    edje_object_part_swallow(VIEW(item), "elm.swallow.icon", item->icon);
1239    edje_object_message_signal_process(VIEW(item));
1240 }
1241
1242 static void
1243 _item_label_set(Elm_Ctxpopup_Item *item, const char *label)
1244 {
1245    if (!eina_stringshare_replace(&item->label, label))
1246      return;
1247
1248    edje_object_part_text_set(VIEW(item), "elm.text", label);
1249    edje_object_message_signal_process(VIEW(item));
1250 }
1251
1252 static void
1253 _item_new(Elm_Ctxpopup_Item *item, char *group_name)
1254 {
1255    Widget_Data *wd;
1256
1257    wd = elm_widget_data_get(WIDGET(item));
1258    if (!wd) return;
1259
1260    VIEW(item) = edje_object_add(evas_object_evas_get(wd->base));
1261    edje_object_mirrored_set(VIEW(item), elm_widget_mirrored_get(WIDGET(item)));
1262    _elm_theme_object_set(WIDGET(item), VIEW(item), "ctxpopup", group_name,
1263                          elm_widget_style_get(WIDGET(item)));
1264    edje_object_signal_callback_add(VIEW(item), "elm,action,click", "",
1265                                    _item_select_cb, item);
1266    evas_object_size_hint_align_set(VIEW(item), EVAS_HINT_FILL, EVAS_HINT_FILL);
1267    evas_object_show(VIEW(item));
1268 }
1269
1270 static void
1271 _content_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
1272              void *event_info __UNUSED__)
1273 {
1274    elm_object_content_unset(data);
1275 }
1276
1277 static void
1278 _list_del(Widget_Data *wd)
1279 {
1280    if (!wd->scr) return;
1281
1282    edje_object_part_unswallow(wd->base, wd->scr);
1283    evas_object_del(wd->scr);
1284    wd->scr = NULL;
1285    wd->box = NULL;
1286 }
1287
1288 static void
1289 _list_new(Evas_Object *obj)
1290 {
1291    Widget_Data *wd;
1292    wd = elm_widget_data_get(obj);
1293    if (!wd) return;
1294
1295    //scroller
1296    wd->scr = elm_scroller_add(obj);
1297    elm_object_style_set(wd->scr, "ctxpopup");
1298    evas_object_size_hint_align_set(wd->scr, EVAS_HINT_FILL, EVAS_HINT_FILL);
1299    evas_object_event_callback_add(wd->scr, EVAS_CALLBACK_RESIZE,
1300                                   _scroller_resize, obj);
1301    edje_object_part_swallow(wd->base, "elm.swallow.content", wd->scr);
1302
1303    //box
1304    wd->box = elm_box_add(obj);
1305    evas_object_size_hint_weight_set(wd->box, EVAS_HINT_EXPAND,
1306                                     EVAS_HINT_EXPAND);
1307
1308    elm_object_content_set(wd->scr, wd->box);
1309    elm_ctxpopup_horizontal_set(obj, wd->horizontal);
1310 }
1311
1312 static void
1313 _remove_items(Widget_Data *wd)
1314 {
1315    Eina_List *elist;
1316    Elm_Ctxpopup_Item *item;
1317
1318    if (!wd->items) return;
1319
1320    EINA_LIST_FOREACH(wd->items, elist, item)
1321      {
1322         if (item->label)
1323           eina_stringshare_del(item->label);
1324         if (item->icon)
1325           evas_object_del(item->icon);
1326         wd->items = eina_list_remove(wd->items, item);
1327         elm_widget_item_free(item);
1328      }
1329
1330    wd->items = NULL;
1331 }
1332
1333 static Eina_Bool
1334 _item_del_pre_hook(Elm_Object_Item *it)
1335 {
1336    Widget_Data *wd;
1337    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1338
1339    wd = elm_widget_data_get(WIDGET(ctxpopup_it));
1340    if (!wd) return EINA_FALSE;
1341
1342    if (ctxpopup_it->icon)
1343      evas_object_del(ctxpopup_it->icon);
1344    if (VIEW(ctxpopup_it))
1345      evas_object_del(VIEW(ctxpopup_it));
1346
1347    eina_stringshare_del(ctxpopup_it->label);
1348
1349    wd->items = eina_list_remove(wd->items, ctxpopup_it);
1350
1351    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1352
1353    if (eina_list_count(wd->items) < 1)
1354      {
1355         evas_object_hide(WIDGET(ctxpopup_it));
1356         return EINA_TRUE;
1357      }
1358
1359    if (wd->visible)
1360      _sizing_eval(WIDGET(ctxpopup_it));
1361
1362    return EINA_TRUE;
1363 }
1364
1365 static void
1366 _disable_hook(Evas_Object *obj)
1367 {
1368    Eina_List *l;
1369    Elm_Object_Item *it;
1370
1371    Widget_Data *wd = elm_widget_data_get(obj);
1372    if (!wd) return;
1373
1374    EINA_LIST_FOREACH(wd->items, l, it)
1375      elm_object_item_disabled_set(it, elm_widget_disabled_get(obj));
1376 }
1377
1378 static void
1379 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
1380 {
1381    Widget_Data *wd = elm_widget_data_get(obj);
1382    if (!wd) return;
1383    edje_object_signal_emit(wd->base, emission, source);
1384 }
1385
1386 static void
1387 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
1388 {
1389    Widget_Data *wd = elm_widget_data_get(obj);
1390    if (!wd) return;
1391    edje_object_signal_callback_add(wd->base, emission, source, func_cb, data);
1392 }
1393
1394 static void
1395 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
1396 {
1397    Widget_Data *wd = elm_widget_data_get(obj);
1398    if (!wd) return;
1399    edje_object_signal_callback_del_full(wd->base, emission, source, func_cb, data);
1400 }
1401
1402 EAPI Evas_Object *
1403 elm_ctxpopup_add(Evas_Object *parent)
1404 {
1405    Evas_Object *obj;
1406    Evas *e;
1407    Widget_Data *wd;
1408
1409    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
1410
1411    ELM_SET_WIDTYPE(widtype, "ctxpopup");
1412    elm_widget_type_set(obj, "ctxpopup");
1413    elm_widget_data_set(obj, wd);
1414    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
1415    elm_widget_del_hook_set(obj, _del_hook);
1416    elm_widget_theme_hook_set(obj, _theme_hook);
1417    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
1418    elm_widget_can_focus_set(obj, EINA_TRUE);
1419    elm_widget_event_hook_set(obj, _event_hook);
1420    elm_widget_content_set_hook_set(obj, _content_set_hook);
1421    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
1422    elm_widget_content_get_hook_set(obj, _content_get_hook);
1423    elm_widget_disable_hook_set(obj, _disable_hook);
1424    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
1425    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
1426    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
1427
1428    //Background
1429    wd->bg = edje_object_add(e);
1430    elm_widget_sub_object_add(obj, wd->bg);
1431    _elm_theme_object_set(obj, wd->bg, "ctxpopup", "bg", "default");
1432    edje_object_signal_callback_add(wd->bg,
1433                                    "elm,action,click",
1434                                    "",
1435                                    _bg_clicked_cb,
1436                                     obj);
1437    //Base
1438    wd->base = edje_object_add(e);
1439    elm_widget_sub_object_add(obj, wd->base);
1440    _elm_theme_object_set(obj, wd->base, "ctxpopup", "base", "default");
1441    edje_object_signal_callback_add(wd->base, "elm,action,hide,finished", "",
1442                                    _hide_finished, obj);
1443
1444    //Arrow
1445    wd->arrow = edje_object_add(e);
1446    elm_widget_sub_object_add(obj, wd->arrow);
1447    _elm_theme_object_set(obj, wd->arrow, "ctxpopup", "arrow", "default");
1448
1449    wd->dir_priority[0] = ELM_CTXPOPUP_DIRECTION_UP;
1450    wd->dir_priority[1] = ELM_CTXPOPUP_DIRECTION_LEFT;
1451    wd->dir_priority[2] = ELM_CTXPOPUP_DIRECTION_RIGHT;
1452    wd->dir_priority[3] = ELM_CTXPOPUP_DIRECTION_DOWN;
1453    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1454
1455    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _ctxpopup_show,
1456                                   NULL);
1457    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _ctxpopup_hide,
1458                                   NULL);
1459    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _ctxpopup_move,
1460                                   NULL);
1461    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESTACK, _restack, obj);
1462    evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj);
1463    evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj);
1464    evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj);
1465    evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj);
1466
1467    evas_object_smart_callbacks_descriptions_set(obj, _signals);
1468
1469    //default parent is to be hover parent
1470    elm_ctxpopup_hover_parent_set(obj, parent);
1471
1472    return obj;
1473 }
1474
1475 EAPI void
1476 elm_ctxpopup_hover_parent_set(Evas_Object *obj, Evas_Object *parent)
1477 {
1478    ELM_CHECK_WIDTYPE(obj, widtype);
1479
1480    Widget_Data *wd;
1481    Evas_Coord x, y, w, h;
1482
1483    wd = elm_widget_data_get(obj);
1484    if ((!wd) || (!parent)) return;
1485
1486    _parent_cut_off(obj);
1487
1488    if (parent)
1489      {
1490         evas_object_event_callback_add(parent,
1491                                        EVAS_CALLBACK_DEL,
1492                                        _parent_del,
1493                                        obj);
1494         evas_object_event_callback_add(parent,
1495                                        EVAS_CALLBACK_MOVE,
1496                                        _parent_move,
1497                                        obj);
1498         evas_object_event_callback_add(parent,
1499                                        EVAS_CALLBACK_RESIZE,
1500                                        _parent_resize,
1501                                        obj);
1502      }
1503
1504    elm_widget_sub_object_add(parent, obj);
1505    wd->parent = parent;
1506
1507    //Update Background
1508    evas_object_geometry_get(parent, &x, &y, &w, &h);
1509    evas_object_move(wd->bg, x, y);
1510    evas_object_resize(wd->bg, w, h);
1511
1512    if (wd->visible) _sizing_eval(obj);
1513 }
1514
1515 EAPI Evas_Object *
1516 elm_ctxpopup_hover_parent_get(const Evas_Object *obj)
1517 {
1518    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1519
1520    Widget_Data *wd;
1521
1522    wd = elm_widget_data_get(obj);
1523    if (!wd) return NULL;
1524
1525    return wd->parent;
1526 }
1527
1528 EAPI void
1529 elm_ctxpopup_clear(Evas_Object * obj)
1530 {
1531    ELM_CHECK_WIDTYPE(obj, widtype);
1532
1533    Widget_Data *wd = elm_widget_data_get(obj);
1534    if (!wd) return;
1535
1536    _remove_items(wd);
1537    _list_del(wd);
1538    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1539 }
1540
1541 EAPI void
1542 elm_ctxpopup_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
1543 {
1544    ELM_CHECK_WIDTYPE(obj, widtype);
1545
1546    Widget_Data *wd;
1547    Eina_List *elist;
1548    Elm_Ctxpopup_Item *item;
1549    int idx = 0;
1550
1551    wd = elm_widget_data_get(obj);
1552    if (!wd) return;
1553
1554    wd->horizontal = !!horizontal;
1555
1556    if ((!wd->scr) && (!wd->box))
1557       return;
1558
1559    if (!horizontal)
1560      {
1561         elm_box_horizontal_set(wd->box, EINA_FALSE);
1562         elm_scroller_bounce_set(wd->scr, EINA_FALSE, EINA_TRUE);
1563
1564         EINA_LIST_FOREACH(wd->items, elist, item)
1565           {
1566              if (idx++ == 0)
1567                edje_object_signal_emit(VIEW(item), "elm,state,default", "elm");
1568              else
1569                edje_object_signal_emit(VIEW(item), "elm,state,vertical", "elm");
1570           }
1571      }
1572    else
1573      {
1574         elm_box_horizontal_set(wd->box, EINA_TRUE);
1575         elm_scroller_bounce_set(wd->scr, EINA_TRUE, EINA_FALSE);
1576
1577         EINA_LIST_FOREACH(wd->items, elist, item)
1578           {
1579              if (idx++ == 0)
1580                edje_object_signal_emit(VIEW(item), "elm,state,default", "elm");
1581              else
1582                edje_object_signal_emit(VIEW(item), "elm,state,horizontal", "elm");
1583           }
1584      }
1585
1586    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1587
1588    if (wd->visible)
1589       _sizing_eval(obj);
1590 }
1591
1592 EAPI Eina_Bool
1593 elm_ctxpopup_horizontal_get(const Evas_Object *obj)
1594 {
1595    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1596
1597    Widget_Data *wd;
1598
1599    wd = elm_widget_data_get(obj);
1600    if (!wd) return EINA_FALSE;
1601
1602    return wd->horizontal;
1603 }
1604
1605 EAPI Elm_Object_Item *
1606 elm_ctxpopup_item_append(Evas_Object *obj, const char *label,
1607                          Evas_Object *icon, Evas_Smart_Cb func,
1608                          const void *data)
1609 {
1610    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1611
1612    Widget_Data *wd;
1613    Evas_Object *content;
1614    Elm_Ctxpopup_Item *item;
1615
1616    wd = elm_widget_data_get(obj);
1617    if (!wd) return NULL;
1618
1619    item = elm_widget_item_new(obj, Elm_Ctxpopup_Item);
1620    if (!item) return NULL;
1621
1622    elm_widget_item_del_pre_hook_set(item, _item_del_pre_hook);
1623    elm_widget_item_disable_hook_set(item, _item_disable_hook);
1624    elm_widget_item_text_set_hook_set(item, _item_text_set_hook);
1625    elm_widget_item_text_get_hook_set(item, _item_text_get_hook);
1626    elm_widget_item_content_set_hook_set(item, _item_content_set_hook);
1627    elm_widget_item_content_get_hook_set(item, _item_content_get_hook);
1628    elm_widget_item_signal_emit_hook_set(item, _item_signal_emit_hook);
1629
1630    //The first item is appended.
1631    content = elm_object_content_unset(obj);
1632    if (content) evas_object_del(content);
1633
1634    if (!wd->items)
1635      _list_new(obj);
1636
1637    item->func = func;
1638    item->base.data = data;
1639
1640    if (icon && label)
1641      _item_new(item, "icon_text_style_item");
1642    else if (label)
1643      _item_new(item, "text_style_item");
1644    else
1645      _item_new(item, "icon_style_item");
1646
1647    _item_icon_set(item, icon);
1648    _item_label_set(item, label);
1649    elm_box_pack_end(wd->box, VIEW(item));
1650    wd->items = eina_list_append(wd->items, item);
1651    wd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1652
1653    if (wd->visible)
1654      {
1655         _scroller_size_reset(wd);
1656         _sizing_eval(obj);
1657      }
1658
1659    return (Elm_Object_Item *)item;
1660 }
1661
1662 EAPI void
1663 elm_ctxpopup_direction_priority_set(Evas_Object *obj,
1664                                     Elm_Ctxpopup_Direction first,
1665                                     Elm_Ctxpopup_Direction second,
1666                                     Elm_Ctxpopup_Direction third,
1667                                     Elm_Ctxpopup_Direction fourth)
1668 {
1669    ELM_CHECK_WIDTYPE(obj, widtype);
1670    Widget_Data *wd;
1671
1672    wd = elm_widget_data_get(obj);
1673    if (!wd) return;
1674
1675    wd->dir_priority[0] = first;
1676    wd->dir_priority[1] = second;
1677    wd->dir_priority[2] = third;
1678    wd->dir_priority[3] = fourth;
1679
1680    if (wd->visible)
1681      _sizing_eval(obj);
1682 }
1683
1684 EAPI void
1685 elm_ctxpopup_direction_priority_get(Evas_Object *obj,
1686                                     Elm_Ctxpopup_Direction *first,
1687                                     Elm_Ctxpopup_Direction *second,
1688                                     Elm_Ctxpopup_Direction *third,
1689                                     Elm_Ctxpopup_Direction *fourth)
1690 {
1691    ELM_CHECK_WIDTYPE(obj, widtype);
1692    Widget_Data *wd;
1693
1694    wd = elm_widget_data_get(obj);
1695    if (!wd) return;
1696
1697    if (first) *first = wd->dir_priority[0];
1698    if (second) *second = wd->dir_priority[1];
1699    if (third) *third = wd->dir_priority[2];
1700    if (fourth) *fourth = wd->dir_priority[3];
1701 }
1702
1703 EAPI Elm_Ctxpopup_Direction
1704 elm_ctxpopup_direction_get(const Evas_Object *obj)
1705 {
1706    ELM_CHECK_WIDTYPE(obj, widtype) ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1707    Widget_Data *wd;
1708
1709    wd = elm_widget_data_get(obj);
1710    if (!wd) return ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1711    return wd->dir;
1712 }
1713
1714 EAPI void
1715 elm_ctxpopup_dismiss(Evas_Object *obj)
1716 {
1717    ELM_CHECK_WIDTYPE(obj, widtype);
1718    Widget_Data *wd = elm_widget_data_get(obj);
1719    if (!wd) return;
1720    _hide_signal_emit(obj, wd->dir);
1721 }