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