[ctxpopup] Add focus_direction.
[framework/uifw/elementary.git] / src / lib / elc_ctxpopup.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "elm_widget_ctxpopup.h"
4
5 EAPI const char ELM_CTXPOPUP_SMART_NAME[] = "elm_ctxpopup";
6
7 static const char SIG_DISMISSED[] = "dismissed";
8 static const char SIG_LANG_CHANGED[] = "language,changed";
9
10 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
11    {SIG_DISMISSED, ""},
12    {SIG_LANG_CHANGED, ""},
13    {NULL, NULL}
14 };
15
16 EVAS_SMART_SUBCLASS_NEW
17   (ELM_CTXPOPUP_SMART_NAME, _elm_ctxpopup, Elm_Ctxpopup_Smart_Class,
18    Elm_Layout_Smart_Class, elm_layout_smart_class_get, _smart_callbacks);
19
20 static Eina_Bool
21 _elm_ctxpopup_smart_translate(Evas_Object *obj)
22 {
23    evas_object_hide(obj);
24
25    return EINA_TRUE;
26 }
27
28 static Eina_Bool
29 _elm_ctxpopup_smart_focus_next(const Evas_Object *obj,
30                                Elm_Focus_Direction dir,
31                                Evas_Object **next)
32 {
33    Eina_List *items = NULL;
34    Eina_List *elist = NULL;
35    Elm_Ctxpopup_Item *it;
36
37    ELM_CTXPOPUP_DATA_GET(obj, sd);
38
39    if (!sd)
40      return EINA_FALSE;
41
42    if (eina_list_count(sd->items))
43      {
44         EINA_LIST_FOREACH(sd->items, elist, it)
45           items = eina_list_append(items, it->base.access_obj);
46
47         return elm_widget_focus_list_next_get
48                  (obj, items, eina_list_data_get, dir, next);
49      }
50    else
51      {
52         return elm_widget_focus_next_get(sd->box, dir, next);
53      }
54 }
55
56 static Eina_Bool
57 _elm_ctxpopup_smart_focus_direction(const Evas_Object *obj,
58                                     const Evas_Object *base,
59                                     double degree,
60                                     Evas_Object **direction,
61                                     double *weight)
62 {
63    Eina_Bool ret;
64    Eina_List *l = NULL;
65    void *(*list_data_get)(const Eina_List *list);
66
67    ELM_CTXPOPUP_DATA_GET(obj, sd);
68
69    if (!sd)
70      return EINA_FALSE;
71
72    list_data_get = eina_list_data_get;
73
74    l = eina_list_append(l, sd->box);
75    ret = elm_widget_focus_list_direction_get
76       (obj, base, l, list_data_get, degree, direction, weight);
77    eina_list_free(l);
78
79    return ret;
80 }
81
82 static Eina_Bool
83 _elm_ctxpopup_smart_event(Evas_Object *obj,
84                           Evas_Object *src __UNUSED__,
85                           Evas_Callback_Type type,
86                           void *event_info)
87 {
88    Evas_Event_Key_Down *ev = event_info;
89
90    ELM_CTXPOPUP_DATA_GET(obj, sd);
91
92    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
93    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
94    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
95
96    if (!strcmp(ev->keyname, "Tab"))
97      {
98         if (evas_key_modifier_is_set(ev->modifiers, "Shift"))
99           elm_widget_focus_cycle(sd->box, ELM_FOCUS_PREVIOUS);
100         else
101           elm_widget_focus_cycle(sd->box, ELM_FOCUS_NEXT);
102         return EINA_TRUE;
103      }
104
105    if (strcmp(ev->keyname, "Escape")) return EINA_FALSE;
106
107    evas_object_hide(obj);
108    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
109    return EINA_TRUE;
110 }
111
112 static void
113 _freeze_on_cb(void *data __UNUSED__,
114               Evas_Object *obj,
115               void *event_info __UNUSED__)
116 {
117    ELM_CTXPOPUP_DATA_GET(obj, sd);
118
119    if ((!sd) || (!sd->scr)) return;
120    elm_object_scroll_freeze_push(sd->scr);
121 }
122
123 static void
124 _freeze_off_cb(void *data __UNUSED__,
125                Evas_Object *obj,
126                void *event_info __UNUSED__)
127 {
128    ELM_CTXPOPUP_DATA_GET(obj, sd);
129
130    if ((!sd) || (!sd->scr)) return;
131    elm_object_scroll_freeze_pop(sd->scr);
132 }
133
134 static void
135 _hold_on_cb(void *data __UNUSED__,
136             Evas_Object *obj,
137             void *event_info __UNUSED__)
138 {
139    ELM_CTXPOPUP_DATA_GET(obj, sd);
140
141    if ((!sd) || (!sd->scr)) return;
142    elm_object_scroll_hold_push(sd->scr);
143 }
144
145 static void
146 _hold_off_cb(void *data __UNUSED__,
147              Evas_Object *obj,
148              void *event_info __UNUSED__)
149 {
150    ELM_CTXPOPUP_DATA_GET(obj, sd);
151
152    if ((!sd) || (!sd->scr)) return;
153    elm_object_scroll_hold_pop(sd->scr);
154 }
155
156 static void
157 _x_pos_adjust(Evas_Coord_Point *pos,
158               Evas_Coord_Point *base_size,
159               Evas_Coord_Rectangle *hover_area)
160 {
161    pos->x -= (base_size->x / 2);
162
163    if (pos->x < hover_area->x)
164      pos->x = hover_area->x;
165    else if ((pos->x + base_size->x) > (hover_area->x + hover_area->w))
166      pos->x = (hover_area->x + hover_area->w) - base_size->x;
167
168    if (base_size->x > hover_area->w)
169      base_size->x -= (base_size->x - hover_area->w);
170
171    if (pos->x < hover_area->x)
172      pos->x = hover_area->x;
173 }
174
175 static void
176 _y_pos_adjust(Evas_Coord_Point *pos,
177               Evas_Coord_Point *base_size,
178               Evas_Coord_Rectangle *hover_area)
179 {
180    pos->y -= (base_size->y / 2);
181
182    if (pos->y < hover_area->y)
183      pos->y = hover_area->y;
184    else if ((pos->y + base_size->y) > (hover_area->y + hover_area->h))
185      pos->y = hover_area->y + hover_area->h - base_size->y;
186
187    if (base_size->y > hover_area->h)
188      base_size->y -= (base_size->y - hover_area->h);
189
190    if (pos->y < hover_area->y)
191      pos->y = hover_area->y;
192 }
193
194 static void
195 _item_select_cb(void *data, Evas_Object *obj __UNUSED__,
196                 const char *emission __UNUSED__,
197                 const char *source __UNUSED__)
198 {
199    Elm_Ctxpopup_Item *item = data;
200
201    if (!item) return;
202    if (elm_widget_item_disabled_get(item)) return;
203
204    if (item->func)
205      item->func((void*)item->base.data, WIDGET(item), data);
206 }
207
208 static char *
209 _access_info_cb(void *data, Evas_Object *obj __UNUSED__)
210 {
211    Elm_Ctxpopup_Item *it = (Elm_Ctxpopup_Item *)data;
212    const char *txt = NULL;
213    Evas_Object *icon = NULL;
214    Eina_Strbuf *buf = NULL;
215    char *str = NULL;
216
217    if (!it) return NULL;
218
219    if ((!txt) && (!icon))
220      {
221         txt = it->label;
222         icon = it->icon;
223      }
224    else if (!txt) txt = it->label;
225    else if (!icon) icon = it->icon;
226
227    if (txt && icon)
228      {
229         buf = eina_strbuf_new();
230         eina_strbuf_append(buf, E_("icon "));
231         eina_strbuf_append(buf, txt);
232         str = eina_strbuf_string_steal(buf);
233         eina_strbuf_free(buf);
234         return str;
235      }
236    else if ((!txt) && icon) return strdup(E_("icon"));
237    else if (txt && (!icon)) return strdup(txt);
238
239    return NULL;
240 }
241
242 static char *
243 _access_state_cb(void *data, Evas_Object *obj __UNUSED__)
244 {
245    Elm_Ctxpopup_Item *it = (Elm_Ctxpopup_Item *)data;
246    if (!it) return NULL;
247
248    if (it->base.disabled)
249      return strdup(E_("State: Disabled"));
250
251    return NULL;
252 }
253
254 static void
255 _access_activate_cb(void *data __UNUSED__,
256                     Evas_Object *part_obj __UNUSED__,
257                     Elm_Object_Item *item)
258 {
259    _item_select_cb(item, NULL, NULL, NULL);
260 }
261
262 static void
263 _access_widget_item_register(Elm_Ctxpopup_Item *it, Eina_Bool is_access)
264 {
265    Elm_Access_Info *ai;
266
267    if (!is_access) _elm_access_widget_item_unregister((Elm_Widget_Item *)it);
268    else
269      {
270         _elm_access_widget_item_register((Elm_Widget_Item *)it);
271
272         ai = _elm_access_object_get(it->base.access_obj);
273
274         _elm_access_callback_set(ai, ELM_ACCESS_INFO, _access_info_cb, it);
275         _elm_access_callback_set(ai, ELM_ACCESS_STATE, _access_state_cb, it);
276         _elm_access_text_set(ai, ELM_ACCESS_TYPE, E_("Contextual Popup"));
277         _elm_access_activate_callback_set(ai, _access_activate_cb, it);
278      }
279 }
280
281 static void
282 _item_new(Elm_Ctxpopup_Item *item,
283           char *group_name)
284 {
285    ELM_CTXPOPUP_DATA_GET(WIDGET(item), sd);
286    if (!sd) return;
287
288    VIEW(item) = edje_object_add(evas_object_evas_get(sd->box));
289    edje_object_mirrored_set(VIEW(item), elm_widget_mirrored_get(WIDGET(item)));
290    _elm_theme_object_set(WIDGET(item), VIEW(item), "ctxpopup", group_name,
291                          elm_widget_style_get(WIDGET(item)));
292    edje_object_signal_callback_add(VIEW(item), "elm,action,click", "",
293                                    _item_select_cb, item);
294    evas_object_size_hint_align_set(VIEW(item), EVAS_HINT_FILL, EVAS_HINT_FILL);
295    evas_object_show(VIEW(item));
296
297    /* access */
298    if (_elm_config->access_mode) _access_widget_item_register(item, EINA_TRUE);
299 }
300
301 static void
302 _item_icon_set(Elm_Ctxpopup_Item *item,
303                Evas_Object *icon)
304 {
305    if (item->icon)
306      evas_object_del(item->icon);
307
308    item->icon = icon;
309    if (!icon) return;
310
311    edje_object_part_swallow(VIEW(item), "elm.swallow.icon", item->icon);
312    edje_object_message_signal_process(VIEW(item));
313 }
314
315 static void
316 _item_label_set(Elm_Ctxpopup_Item *item,
317                 const char *label)
318 {
319    if (!eina_stringshare_replace(&item->label, label))
320      return;
321
322    edje_object_part_text_set(VIEW(item), "elm.text", label);
323    edje_object_message_signal_process(VIEW(item));
324 }
325
326 static Eina_Bool
327 _item_del_pre_hook(Elm_Object_Item *it)
328 {
329    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
330
331    ELM_CTXPOPUP_DATA_GET(WIDGET(ctxpopup_it), sd);
332    if (!sd) return EINA_FALSE;
333
334    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
335
336    if (ctxpopup_it->icon)
337      evas_object_del(ctxpopup_it->icon);
338    if (VIEW(ctxpopup_it))
339      evas_object_del(VIEW(ctxpopup_it));
340
341    eina_stringshare_del(ctxpopup_it->label);
342    sd->items = eina_list_remove(sd->items, ctxpopup_it);
343
344    if (eina_list_count(sd->items) < 1)
345      {
346         evas_object_hide(WIDGET(ctxpopup_it));
347         return EINA_TRUE;
348      }
349    if (sd->visible) elm_layout_sizing_eval(WIDGET(ctxpopup_it));
350
351    return EINA_TRUE;
352 }
353
354 static void
355 _items_remove(Elm_Ctxpopup_Smart_Data *sd)
356 {
357    Eina_List *elist;
358    Elm_Ctxpopup_Item *item;
359
360    if (!sd->items) return;
361
362    EINA_LIST_FOREACH(sd->items, elist, item)
363      {
364          _item_del_pre_hook((Elm_Object_Item *)item);
365      }
366
367    sd->items = NULL;
368 }
369
370 static void
371 _scroller_size_reset(Elm_Ctxpopup_Smart_Data *sd)
372 {
373    sd->finished = EINA_FALSE;
374    sd->max_sc_h = -1;
375    sd->max_sc_w = -1;
376 }
377
378 static void
379 _item_sizing_eval(Elm_Ctxpopup_Item *item)
380 {
381    Evas_Coord min_w = -1, min_h = -1, max_w = -1, max_h = -1;
382
383    if (!item) return;
384
385    edje_object_size_min_restricted_calc(VIEW(item), &min_w, &min_h, min_w,
386                                         min_h);
387    evas_object_size_hint_min_set(VIEW(item), min_w, min_h);
388    evas_object_size_hint_max_set(VIEW(item), max_w, max_h);
389 }
390
391 static Elm_Ctxpopup_Direction
392 _base_geometry_calc(Evas_Object *obj,
393                     Evas_Coord_Rectangle *rect)
394 {
395    Elm_Ctxpopup_Direction dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
396    Evas_Coord_Rectangle hover_area;
397    Evas_Coord_Point pos = {0, 0};
398    Evas_Coord_Point arrow_size;
399    Evas_Coord_Point base_size;
400    Evas_Coord_Point max_size;
401    Evas_Coord_Point min_size;
402    Evas_Coord_Point temp;
403    int idx;
404
405    ELM_CTXPOPUP_DATA_GET(obj, sd);
406
407    if (!rect) return ELM_CTXPOPUP_DIRECTION_DOWN;
408
409    edje_object_part_geometry_get
410      (sd->arrow, "ctxpopup_arrow", NULL, NULL, &arrow_size.x, &arrow_size.y);
411    evas_object_resize(sd->arrow, arrow_size.x, arrow_size.y);
412
413    //Initialize Area Rectangle.
414    evas_object_geometry_get
415      (sd->parent, &hover_area.x, &hover_area.y, &hover_area.w,
416      &hover_area.h);
417
418    evas_object_geometry_get(obj, &pos.x, &pos.y, NULL, NULL);
419
420    //recalc the edje
421    edje_object_size_min_calc
422      (ELM_WIDGET_DATA(sd)->resize_obj, &base_size.x, &base_size.y);
423    evas_object_smart_calculate(ELM_WIDGET_DATA(sd)->resize_obj);
424
425    //Limit to Max Size
426    evas_object_size_hint_max_get(obj, &max_size.x, &max_size.y);
427    if ((max_size.x == -1) || (max_size.y == -1))
428      edje_object_size_max_get(sd->layout, &max_size.x, &max_size.y);
429
430    if ((max_size.y > 0) && (base_size.y > max_size.y))
431      base_size.y = max_size.y;
432
433    if ((max_size.x > 0) && (base_size.x > max_size.x))
434      base_size.x = max_size.x;
435
436    //Limit to Min Size
437    evas_object_size_hint_min_get(obj, &min_size.x, &min_size.y);
438    if ((min_size.x == 0) || (min_size.y == 0))
439      edje_object_size_min_get(sd->layout, &min_size.x, &min_size.y);
440
441    if ((min_size.y > 0) && (base_size.y < min_size.y))
442      base_size.y = min_size.y;
443
444    if ((min_size.x > 0) && (base_size.x < min_size.x))
445      base_size.x = min_size.x;
446
447    //Check the Which direction is available.
448    //If find a avaialble direction, it adjusts position and size.
449    for (idx = 0; idx < 4; idx++)
450      {
451         switch (sd->dir_priority[idx])
452           {
453            case ELM_CTXPOPUP_DIRECTION_UNKNOWN:
454
455            case ELM_CTXPOPUP_DIRECTION_UP:
456              temp.y = (pos.y - base_size.y);
457              if ((temp.y - arrow_size.y) < hover_area.y)
458                continue;
459
460              _x_pos_adjust(&pos, &base_size, &hover_area);
461              pos.y -= base_size.y;
462              dir = ELM_CTXPOPUP_DIRECTION_UP;
463              break;
464
465            case ELM_CTXPOPUP_DIRECTION_LEFT:
466              temp.x = (pos.x - base_size.x);
467              if ((temp.x - arrow_size.x) < hover_area.x)
468                continue;
469
470              _y_pos_adjust(&pos, &base_size, &hover_area);
471              pos.x -= base_size.x;
472              dir = ELM_CTXPOPUP_DIRECTION_LEFT;
473              break;
474
475            case ELM_CTXPOPUP_DIRECTION_RIGHT:
476              temp.x = (pos.x + base_size.x);
477              if ((temp.x + arrow_size.x) >
478                  (hover_area.x + hover_area.w))
479                continue;
480
481              _y_pos_adjust(&pos, &base_size, &hover_area);
482              dir = ELM_CTXPOPUP_DIRECTION_RIGHT;
483              break;
484
485            case ELM_CTXPOPUP_DIRECTION_DOWN:
486              temp.y = (pos.y + base_size.y);
487              if ((temp.y + arrow_size.y) >
488                  (hover_area.y + hover_area.h))
489                continue;
490
491              _x_pos_adjust(&pos, &base_size, &hover_area);
492              dir = ELM_CTXPOPUP_DIRECTION_DOWN;
493              break;
494
495            default:
496              continue;
497           }
498         break;
499      }
500
501    //In this case, all directions are invalid because of lack of space.
502    if (idx == 4)
503      {
504         Evas_Coord length[2];
505
506         if (!sd->horizontal)
507           {
508              length[0] = pos.y - hover_area.y;
509              length[1] = (hover_area.y + hover_area.h) - pos.y;
510
511              // ELM_CTXPOPUP_DIRECTION_UP
512              if (length[0] > length[1])
513                {
514                   _x_pos_adjust(&pos, &base_size, &hover_area);
515                   pos.y -= base_size.y;
516                   dir = ELM_CTXPOPUP_DIRECTION_UP;
517                   if (pos.y < (hover_area.y + arrow_size.y))
518                     {
519                        base_size.y -= ((hover_area.y + arrow_size.y) - pos.y);
520                        pos.y = hover_area.y + arrow_size.y;
521                     }
522                }
523              //ELM_CTXPOPUP_DIRECTION_DOWN
524              else
525                {
526                   _x_pos_adjust(&pos, &base_size, &hover_area);
527                   dir = ELM_CTXPOPUP_DIRECTION_DOWN;
528                   if ((pos.y + arrow_size.y + base_size.y) >
529                       (hover_area.y + hover_area.h))
530                     base_size.y -=
531                       ((pos.y + arrow_size.y + base_size.y) -
532                        (hover_area.y + hover_area.h));
533                }
534           }
535         else
536           {
537              length[0] = pos.x - hover_area.x;
538              length[1] = (hover_area.x + hover_area.w) - pos.x;
539
540              //ELM_CTXPOPUP_DIRECTION_LEFT
541              if (length[0] > length[1])
542                {
543                   _y_pos_adjust(&pos, &base_size, &hover_area);
544                   pos.x -= base_size.x;
545                   dir = ELM_CTXPOPUP_DIRECTION_LEFT;
546                   if (pos.x < (hover_area.x + arrow_size.x))
547                     {
548                        base_size.x -= ((hover_area.x + arrow_size.x) - pos.x);
549                        pos.x = hover_area.x + arrow_size.x;
550                     }
551                }
552              //ELM_CTXPOPUP_DIRECTION_RIGHT
553              else
554                {
555                   _y_pos_adjust(&pos, &base_size, &hover_area);
556                   dir = ELM_CTXPOPUP_DIRECTION_RIGHT;
557                   if (pos.x + (arrow_size.x + base_size.x) >
558                       hover_area.x + hover_area.w)
559                     base_size.x -=
560                       ((pos.x + arrow_size.x + base_size.x) -
561                        (hover_area.x + hover_area.w));
562                }
563           }
564      }
565
566    //Final position and size.
567    rect->x = pos.x;
568    rect->y = pos.y;
569    rect->w = base_size.x;
570    rect->h = base_size.y;
571
572    return dir;
573 }
574
575 static void
576 _arrow_update(Evas_Object *obj,
577               Elm_Ctxpopup_Direction dir,
578               Evas_Coord_Rectangle base_size)
579 {
580    Evas_Coord_Rectangle arrow_size;
581    Evas_Coord x, y;
582    double drag;
583    Evas_Coord_Rectangle shadow_left_top, shadow_right_bottom, arrow_padding;
584
585    ELM_CTXPOPUP_DATA_GET(obj, sd);
586
587    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
588    evas_object_geometry_get
589      (sd->arrow, NULL, NULL, &arrow_size.w, &arrow_size.h);
590
591    /* tizen only : since ctxpopup of tizen has shadow, start and end padding of arrow, it should be put together when updating arrow
592     * so there are some differences between open source and tizen */
593    edje_object_part_geometry_get(ELM_WIDGET_DATA(sd)->resize_obj, "frame_shadow_left_top_padding", NULL, NULL, &shadow_left_top.w, &shadow_left_top.h);
594    edje_object_part_geometry_get(ELM_WIDGET_DATA(sd)->resize_obj, "frame_shadow_right_bottom_padding", NULL, NULL, &shadow_right_bottom.w, &shadow_right_bottom.h);
595    edje_object_part_geometry_get(ELM_WIDGET_DATA(sd)->resize_obj, "ctxpopup_frame_left_top", NULL, NULL, &arrow_padding.w, &arrow_padding.h);
596
597    /* arrow is not being kept as sub-object on purpose, here. the
598     * design of the widget does not help with the contrary */
599
600    switch (dir)
601      {
602       case ELM_CTXPOPUP_DIRECTION_RIGHT:
603         edje_object_signal_emit(sd->arrow, "elm,state,left", "elm");
604         edje_object_part_swallow
605            (ELM_WIDGET_DATA(sd)->resize_obj,
606             (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_right" :
607              "elm.swallow.arrow_left"), sd->arrow);
608
609         if (base_size.h > 0)
610           {
611              if (y <= ((arrow_size.h * 0.5) + base_size.y + shadow_left_top.h + arrow_padding.h))
612                y = 0;
613              else if (y >= (base_size.y + base_size.h - ((arrow_size.h * 0.5) + shadow_right_bottom.h + arrow_padding.h)))
614                y = base_size.h - (arrow_size.h + shadow_right_bottom.h + shadow_left_top.h + (arrow_padding.h * 2));
615              else
616                y = y - base_size.y - ((arrow_size.h * 0.5) + shadow_left_top.h + arrow_padding.h);
617              drag = (double)(y) / (double)(base_size.h - (arrow_size.h + shadow_right_bottom.h + shadow_left_top.h + (arrow_padding.h * 2)));
618              edje_object_part_drag_value_set
619                 (ELM_WIDGET_DATA(sd)->resize_obj,
620                  (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_right" :
621                   "elm.swallow.arrow_left"), 1, drag);
622           }
623         break;
624
625       case ELM_CTXPOPUP_DIRECTION_LEFT:
626         edje_object_signal_emit(sd->arrow, "elm,state,right", "elm");
627         edje_object_part_swallow
628            (ELM_WIDGET_DATA(sd)->resize_obj,
629             (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_left" :
630              "elm.swallow.arrow_right"), sd->arrow);
631
632         if (base_size.h > 0)
633           {
634              if (y <= ((arrow_size.h * 0.5) + base_size.y + shadow_left_top.h + arrow_padding.h))
635                y = 0;
636              else if (y >= (base_size.y + base_size.h - ((arrow_size.h * 0.5) + shadow_right_bottom.h + arrow_padding.h)))
637                y = base_size.h - (arrow_size.h + shadow_right_bottom.h + shadow_left_top.h + (arrow_padding.h * 2));
638              else
639                y = y - base_size.y - ((arrow_size.h * 0.5) + shadow_left_top.h + arrow_padding.h);
640              drag = (double)(y) / (double)(base_size.h - (arrow_size.h + shadow_right_bottom.h + shadow_left_top.h + (arrow_padding.h * 2)));
641              edje_object_part_drag_value_set
642                 (ELM_WIDGET_DATA(sd)->resize_obj,
643                  (elm_widget_mirrored_get(obj) ? "elm.swallow.arrow_left" :
644                   "elm.swallow.arrow_right"), 0, drag);
645           }
646         break;
647
648       case ELM_CTXPOPUP_DIRECTION_DOWN:
649         edje_object_signal_emit(sd->arrow, "elm,state,top", "elm");
650         edje_object_part_swallow
651           (ELM_WIDGET_DATA(sd)->resize_obj, "elm.swallow.arrow_up",
652           sd->arrow);
653
654         if (base_size.w > 0)
655           {
656              if (x <= ((arrow_size.w * 0.5) + base_size.x + shadow_left_top.w + arrow_padding.w))
657                x = 0;
658              else if (x >= (base_size.x + base_size.w - ((arrow_size.w * 0.5) + shadow_right_bottom.w + arrow_padding.w)))
659                x = base_size.w - (arrow_size.w + shadow_right_bottom.w + shadow_left_top.w + (arrow_padding.w * 2));
660              else
661                x = x - base_size.x - ((arrow_size.w * 0.5) + shadow_left_top.w + arrow_padding.w);
662              drag = (double)(x) / (double)(base_size.w - (arrow_size.w + shadow_right_bottom.w + shadow_left_top.w + (arrow_padding.w * 2)));
663              edje_object_part_drag_value_set
664                (ELM_WIDGET_DATA(sd)->resize_obj, "elm.swallow.arrow_up",
665                drag, 1);
666           }
667         break;
668
669       case ELM_CTXPOPUP_DIRECTION_UP:
670         edje_object_signal_emit(sd->arrow, "elm,state,bottom", "elm");
671         edje_object_part_swallow
672           (ELM_WIDGET_DATA(sd)->resize_obj, "elm.swallow.arrow_down",
673           sd->arrow);
674
675         if (base_size.w > 0)
676           {
677              if (x <= ((arrow_size.w * 0.5) + base_size.x + shadow_left_top.w + arrow_padding.w))
678                x = 0;
679              else if (x >= (base_size.x + base_size.w - ((arrow_size.w * 0.5) + shadow_right_bottom.w + arrow_padding.w)))
680                x = base_size.w - (arrow_size.w + shadow_right_bottom.w + shadow_left_top.w + (arrow_padding.w * 2));
681              else
682                x = x - base_size.x - ((arrow_size.w * 0.5) + shadow_left_top.w + arrow_padding.w);
683              drag = (double)(x) / (double)(base_size.w - (arrow_size.w + shadow_right_bottom.w + shadow_left_top.w + (arrow_padding.w * 2)));
684              edje_object_part_drag_value_set
685                (ELM_WIDGET_DATA(sd)->resize_obj, "elm.swallow.arrow_down",
686                drag, 0);
687           }
688         break;
689
690       default:
691         break;
692      }
693
694    //should be here for getting accurate geometry value
695    evas_object_smart_calculate(ELM_WIDGET_DATA(sd)->resize_obj);
696 }
697
698 static void
699 _show_signals_emit(Evas_Object *obj,
700                    Elm_Ctxpopup_Direction dir)
701 {
702    ELM_CTXPOPUP_DATA_GET(obj, sd);
703
704    if (!sd->visible) return;
705
706    switch (dir)
707      {
708       case ELM_CTXPOPUP_DIRECTION_UP:
709         edje_object_signal_emit(sd->layout, "elm,state,show,up", "elm");
710         break;
711
712       case ELM_CTXPOPUP_DIRECTION_LEFT:
713         edje_object_signal_emit(sd->layout, (elm_widget_mirrored_get(obj) ? "elm,state,show,right" :
714                "elm,state,show,left"), "elm");
715         break;
716
717       case ELM_CTXPOPUP_DIRECTION_RIGHT:
718         edje_object_signal_emit(sd->layout, (elm_widget_mirrored_get(obj) ? "elm,state,show,left" :
719                "elm,state,show,right"), "elm");
720         break;
721
722       case ELM_CTXPOPUP_DIRECTION_DOWN:
723         edje_object_signal_emit(sd->layout, "elm,state,show,down", "elm");
724         break;
725
726       default:
727         break;
728      }
729
730    edje_object_signal_emit(sd->bg, "elm,state,show", "elm");
731 //   elm_layout_signal_emit(obj, "elm,state,show", "elm");
732 }
733
734 static void
735 _hide_signals_emit(Evas_Object *obj,
736                    Elm_Ctxpopup_Direction dir)
737 {
738    ELM_CTXPOPUP_DATA_GET(obj, sd);
739
740    if (!sd->visible) return;
741
742    switch (dir)
743      {
744       case ELM_CTXPOPUP_DIRECTION_UP:
745         edje_object_signal_emit(sd->layout, "elm,state,hide,up", "elm");
746         break;
747
748       case ELM_CTXPOPUP_DIRECTION_LEFT:
749         edje_object_signal_emit(sd->layout, (elm_widget_mirrored_get(obj) ? "elm,state,hide,right" :
750                "elm,state,hide,left"), "elm");
751         break;
752
753       case ELM_CTXPOPUP_DIRECTION_RIGHT:
754         edje_object_signal_emit(sd->layout, (elm_widget_mirrored_get(obj) ? "elm,state,hide,left" :
755                "elm,state,hide,right"), "elm");
756         break;
757
758       case ELM_CTXPOPUP_DIRECTION_DOWN:
759         edje_object_signal_emit(sd->layout, "elm,state,hide,down", "elm");
760         break;
761
762       default:
763         break;
764      }
765
766    edje_object_signal_emit(sd->bg, "elm,state,hide", "elm");
767 }
768
769 static void
770 _base_shift_by_arrow(Evas_Object *arrow,
771                      Elm_Ctxpopup_Direction dir,
772                      Evas_Coord_Rectangle *rect)
773 {
774    Evas_Coord arrow_w, arrow_h;
775
776    evas_object_geometry_get(arrow, NULL, NULL, &arrow_w, &arrow_h);
777
778    switch (dir)
779      {
780       case ELM_CTXPOPUP_DIRECTION_RIGHT:
781         rect->x += arrow_w;
782         break;
783
784       case ELM_CTXPOPUP_DIRECTION_LEFT:
785         rect->x -= arrow_w;
786         break;
787
788       case ELM_CTXPOPUP_DIRECTION_DOWN:
789         rect->y += arrow_h;
790         break;
791
792       case ELM_CTXPOPUP_DIRECTION_UP:
793         rect->y -= arrow_h;
794         break;
795
796       default:
797          break;
798      }
799 }
800
801 //TODO: compress item - different from opensource
802 static void
803 _compress_item(Evas_Object *obj)
804 {
805    Eina_List *elist;
806    Elm_Ctxpopup_Item *item;
807
808    ELM_CTXPOPUP_DATA_GET(obj, sd);
809    if (!sd) return;
810
811    EINA_LIST_FOREACH(sd->items, elist, item)
812      {
813         edje_object_signal_emit(item->base.view, "elm,state,compress", "elm");
814      }
815 }
816
817 static Eina_Bool
818 _elm_ctxpopup_smart_sub_object_add(Evas_Object *obj,
819                                    Evas_Object *sobj)
820 {
821    Elm_Widget_Smart_Class *parent_parent;
822
823    parent_parent = (Elm_Widget_Smart_Class *)((Evas_Smart_Class *)
824                                               _elm_ctxpopup_parent_sc)->parent;
825
826    /* skipping layout's code, which registers size hint changing
827     * callback on sub objects. a hack to make ctxpopup live, as it is,
828     * on the new classing schema. this widget needs a total
829     * rewrite. */
830    if (!parent_parent->sub_object_add(obj, sobj))
831      return EINA_FALSE;
832
833    return EINA_TRUE;
834 }
835
836 static void
837 _elm_ctxpopup_smart_sizing_eval(Evas_Object *obj)
838 {
839    Eina_List *elist;
840    Elm_Ctxpopup_Item *item;
841    Evas_Coord_Rectangle rect = { 0, 0, 1, 1 };
842    Evas_Coord_Point box_size = { 0, 0 };
843    Evas_Coord_Point _box_size = { 0, 0 };
844    Evas_Coord maxw = 0;
845    const char *str;
846
847    ELM_CTXPOPUP_DATA_GET(obj, sd);
848
849    //Box, Scroller
850    EINA_LIST_FOREACH(sd->items, elist, item)
851      {
852         _item_sizing_eval(item);
853         evas_object_size_hint_min_get(VIEW(item), &_box_size.x, &_box_size.y);
854
855         str = edje_object_data_get(VIEW(item), "item_max_size");
856         if (str)
857           {
858              maxw = atoi(str);
859              maxw = maxw * elm_widget_scale_get(obj) * elm_config_scale_get();
860
861              if (_box_size.x > maxw)
862                {
863                   edje_object_signal_emit(VIEW(item), "elm,state,text,ellipsis", "elm");
864                   edje_object_message_signal_process(VIEW(item));
865                }
866           }
867
868         if (!sd->horizontal)
869           {
870              if (_box_size.x > box_size.x)
871                box_size.x = _box_size.x;
872              if (_box_size.y != -1)
873                box_size.y += _box_size.y;
874           }
875         else
876           {
877              if (_box_size.x != -1)
878                box_size.x += _box_size.x;
879              if (_box_size.y > box_size.y)
880                box_size.y = _box_size.y;
881           }
882      }
883
884    if (!sd->arrow) return;  /* simple way to flag "under deletion" */
885
886    if ((!sd->content) && (sd->scr))
887      {
888         evas_object_size_hint_min_set(sd->box, box_size.x, box_size.y);
889         elm_scroller_content_min_limit(sd->scr, EINA_TRUE, EINA_TRUE);
890         evas_object_size_hint_min_set(sd->scr, box_size.x, box_size.y);
891      }
892
893    //Base
894    sd->dir = _base_geometry_calc(obj, &rect);
895
896    //TODO: compress item - different from opensource
897    if (!sd->horizontal && !sd->content)
898      _compress_item(obj);
899
900    _arrow_update(obj, sd->dir, rect);
901    _base_shift_by_arrow(sd->arrow, sd->dir, &rect);
902
903    //resize scroller according to final size
904    if ((!sd->content) && (sd->scr))
905      {
906         elm_scroller_content_min_limit(sd->scr, EINA_FALSE, EINA_FALSE);
907         evas_object_smart_calculate(sd->scr);
908      }
909
910    evas_object_size_hint_min_set(ELM_WIDGET_DATA(sd)->resize_obj, rect.w, rect.h);
911    evas_object_resize(ELM_WIDGET_DATA(sd)->resize_obj, rect.w, rect.h);
912
913    evas_object_move(sd->layout, rect.x, rect.y);
914    evas_object_resize(sd->layout, rect.w, rect.h);
915
916    _show_signals_emit(obj, sd->dir);
917 }
918
919 static void
920 _on_parent_del(void *data,
921                Evas *e __UNUSED__,
922                Evas_Object *obj __UNUSED__,
923                void *event_info __UNUSED__)
924 {
925    evas_object_del(data);
926 }
927
928 static void
929 _on_parent_move(void *data,
930                 Evas *e __UNUSED__,
931                 Evas_Object *obj __UNUSED__,
932                 void *event_info __UNUSED__)
933 {
934    ELM_CTXPOPUP_DATA_GET(data, sd);
935
936
937    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
938
939    if (sd->visible)
940      {
941         _scroller_size_reset(sd);
942         elm_layout_sizing_eval(data);
943      }
944 }
945
946 static void
947 _on_parent_resize(void *data,
948                   Evas *e __UNUSED__,
949                   Evas_Object *obj __UNUSED__,
950                   void *event_info __UNUSED__)
951 {
952    ELM_CTXPOPUP_DATA_GET(data, sd);
953
954    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
955
956    evas_object_hide(data);
957    evas_object_smart_callback_call(data, SIG_DISMISSED, NULL);
958 }
959
960 static void
961 _parent_detach(Evas_Object *obj)
962 {
963    ELM_CTXPOPUP_DATA_GET(obj, sd);
964
965    if (!sd->parent) return;
966
967    evas_object_event_callback_del_full
968      (sd->parent, EVAS_CALLBACK_DEL, _on_parent_del, obj);
969    evas_object_event_callback_del_full
970      (sd->parent, EVAS_CALLBACK_MOVE, _on_parent_move, obj);
971    evas_object_event_callback_del_full
972      (sd->parent, EVAS_CALLBACK_RESIZE, _on_parent_resize, obj);
973 }
974
975 static void
976 _on_content_resized(void *data,
977                     Evas *e __UNUSED__,
978                     Evas_Object *obj __UNUSED__,
979                     void *event_info __UNUSED__)
980 {
981    ELM_CTXPOPUP_DATA_GET(data, sd);
982
983    elm_box_recalculate(sd->box);
984    elm_layout_sizing_eval(data);
985 }
986
987 //FIXME: lost the content size when theme hook is called.
988 static Eina_Bool
989 _elm_ctxpopup_smart_theme(Evas_Object *obj)
990 {
991    Eina_List *elist;
992    Elm_Ctxpopup_Item *item;
993    int idx = 0;
994    Eina_Bool rtl;
995
996    ELM_CTXPOPUP_DATA_GET(obj, sd);
997
998    if (!ELM_WIDGET_CLASS(_elm_ctxpopup_parent_sc)->theme(obj))
999      return EINA_FALSE;
1000
1001    rtl = elm_widget_mirrored_get(obj);
1002
1003    elm_widget_theme_object_set
1004      (obj, sd->bg, "ctxpopup", "bg", elm_widget_style_get(obj));
1005    elm_widget_theme_object_set
1006      (obj, sd->arrow, "ctxpopup", "arrow", elm_widget_style_get(obj));
1007    elm_widget_theme_object_set
1008      (obj, sd->layout, "ctxpopup", "layout", elm_widget_style_get(obj));
1009
1010    //Items
1011    EINA_LIST_FOREACH(sd->items, elist, item)
1012      {
1013         edje_object_mirrored_set(VIEW(item), rtl);
1014
1015         if (item->label && item->icon)
1016           _elm_theme_object_set
1017              (obj, VIEW(item), "ctxpopup", "icon_text_style_item", elm_widget_style_get(obj));
1018         else if (item->label)
1019           {
1020              if(!sd->horizontal)
1021                _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "text_style_item",
1022                                      elm_widget_style_get(obj));
1023              else
1024                _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "text_style_item_horizontal",
1025                                      elm_widget_style_get(obj));
1026           }
1027         else if (item->icon)
1028           _elm_theme_object_set
1029              (obj, VIEW(item), "ctxpopup", "icon_style_item", elm_widget_style_get(obj));
1030         if (item->label)
1031           edje_object_part_text_set(VIEW(item), "elm.text", item->label);
1032
1033         if (elm_widget_item_disabled_get(item))
1034           edje_object_signal_emit(VIEW(item), "elm,state,disabled", "elm");
1035
1036        /*
1037         *  For separator, if the first item has visible separator,
1038         *  then it should be aligned with edge of the base part.
1039         *  In some cases, it gives improper display. Ex) rounded corner
1040         *  So the first item separator should be invisible.
1041         */
1042         if ((idx++) == 0)
1043           edje_object_signal_emit(VIEW(item), "elm,state,default", "elm");
1044         else
1045           edje_object_signal_emit(VIEW(item), "elm,state,separator", "elm");
1046
1047         edje_object_message_signal_process(VIEW(item));
1048      }
1049
1050    if (evas_object_visible_get(sd->bg))
1051      edje_object_signal_emit(sd->bg, "elm,state,show", "elm");
1052
1053    if (sd->scr)
1054      {
1055         if (!strncmp(elm_object_style_get(obj), "default", strlen("default")))
1056            elm_object_style_set(sd->scr, "ctxpopup");
1057         else
1058            elm_object_style_set(sd->scr, elm_object_style_get(obj));
1059      }
1060
1061    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1062
1063    if (sd->visible)
1064      {
1065         _scroller_size_reset(sd);
1066         _elm_ctxpopup_smart_sizing_eval(obj);
1067      }
1068
1069    return EINA_TRUE;
1070 }
1071
1072 /* kind of a big and tricky override here: an internal box will hold
1073  * the actual content. content aliases won't be of much help here */
1074 static Eina_Bool
1075 _elm_ctxpopup_smart_content_set(Evas_Object *obj,
1076                                 const char *part,
1077                                 Evas_Object *content)
1078 {
1079    Evas_Coord min_w = -1, min_h = -1;
1080
1081    ELM_CTXPOPUP_DATA_GET(obj, sd);
1082
1083    if ((part) && (strcmp(part, "default")))
1084      return ELM_CONTAINER_CLASS(_elm_ctxpopup_parent_sc)->content_set
1085               (obj, part, content);
1086
1087    if (!content) return EINA_FALSE;
1088
1089    if (content == sd->content) return EINA_TRUE;
1090
1091    if (sd->items) elm_ctxpopup_clear(obj);
1092    if (sd->content) evas_object_del(sd->content);
1093
1094    //box
1095    if (!sd->box)
1096      {
1097         sd->box = elm_box_add(obj);
1098         evas_object_size_hint_weight_set(sd->box, EVAS_HINT_EXPAND,
1099                                          EVAS_HINT_EXPAND);
1100         evas_object_event_callback_add
1101            (sd->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1102         ELM_CONTAINER_CLASS(_elm_ctxpopup_parent_sc)->content_set
1103            (obj, "elm.swallow.content", sd->box);
1104      }
1105
1106    evas_object_size_hint_weight_set
1107      (content, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1108    evas_object_size_hint_fill_set
1109      (content, EVAS_HINT_FILL, EVAS_HINT_FILL);
1110
1111    /* since it's going to be a box content, not a layout's... */
1112    evas_object_show(content);
1113
1114    evas_object_size_hint_min_get(content, &min_w, &min_h);
1115    evas_object_size_hint_min_set(sd->box, min_w, min_h);
1116    elm_box_pack_end(sd->box, content);
1117
1118    sd->content = content;
1119    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1120
1121    if (sd->visible) elm_layout_sizing_eval(obj);
1122
1123    return EINA_TRUE;
1124 }
1125
1126 static Evas_Object *
1127 _elm_ctxpopup_smart_content_get(const Evas_Object *obj,
1128                                 const char *part)
1129 {
1130    ELM_CTXPOPUP_DATA_GET(obj, sd);
1131
1132    if ((part) && (strcmp(part, "default")))
1133      return ELM_CONTAINER_CLASS(_elm_ctxpopup_parent_sc)->content_get
1134               (obj, part);
1135
1136    return sd->content;
1137 }
1138
1139 static Evas_Object *
1140 _elm_ctxpopup_smart_content_unset(Evas_Object *obj,
1141                                   const char *part)
1142 {
1143    Evas_Object *content;
1144
1145    ELM_CTXPOPUP_DATA_GET(obj, sd);
1146
1147    if ((part) && (strcmp(part, "default")))
1148      return ELM_CONTAINER_CLASS(_elm_ctxpopup_parent_sc)->content_unset
1149               (obj, part);
1150
1151    content = sd->content;
1152    if (!content) return NULL;
1153
1154    evas_object_del(sd->box);
1155    sd->box = NULL;
1156    sd->content = NULL;
1157    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1158
1159    if (sd->visible) elm_layout_sizing_eval(obj);
1160
1161    return content;
1162 }
1163
1164 static void
1165 _item_text_set_hook(Elm_Object_Item *it,
1166                     const char *part,
1167                     const char *label)
1168 {
1169    Elm_Ctxpopup_Item *ctxpopup_it;
1170
1171    if ((part) && (strcmp(part, "default"))) return;
1172
1173    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1174
1175    _item_label_set(ctxpopup_it, label);
1176 }
1177
1178 static const char *
1179 _item_text_get_hook(const Elm_Object_Item *it,
1180                     const char *part)
1181 {
1182    Elm_Ctxpopup_Item *ctxpopup_it;
1183
1184    if (part && strcmp(part, "default")) return NULL;
1185
1186    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1187    return ctxpopup_it->label;
1188 }
1189
1190 static void
1191 _item_content_set_hook(Elm_Object_Item *it,
1192                        const char *part,
1193                        Evas_Object *content)
1194 {
1195    Elm_Ctxpopup_Item *ctxpopup_it;
1196
1197    if ((part) && (strcmp(part, "icon"))) return;
1198
1199    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1200
1201    ELM_CTXPOPUP_DATA_GET(WIDGET(ctxpopup_it), sd);
1202
1203    _item_icon_set(ctxpopup_it, content);
1204    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1205
1206    if (sd->visible)
1207      {
1208         _scroller_size_reset(sd);
1209         _elm_ctxpopup_smart_sizing_eval(WIDGET(ctxpopup_it));
1210      }
1211 }
1212
1213 static Evas_Object *
1214 _item_content_get_hook(const Elm_Object_Item *it,
1215                        const char *part)
1216 {
1217    Elm_Ctxpopup_Item *ctxpopup_it;
1218
1219    if (part && strcmp(part, "icon")) return NULL;
1220
1221    ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1222    return ctxpopup_it->icon;
1223 }
1224
1225 static void
1226 _item_disable_hook(Elm_Object_Item *it)
1227 {
1228    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1229
1230    ELM_CTXPOPUP_DATA_GET(WIDGET(it), sd);
1231    if (!sd) return;
1232
1233    if (elm_widget_item_disabled_get(it))
1234      edje_object_signal_emit(VIEW(ctxpopup_it), "elm,state,disabled", "elm");
1235    else
1236      edje_object_signal_emit(VIEW(ctxpopup_it), "elm,state,enabled", "elm");
1237 }
1238
1239 static void
1240 _item_signal_emit_hook(Elm_Object_Item *it,
1241                        const char *emission,
1242                        const char *source)
1243 {
1244    Elm_Ctxpopup_Item *ctxpopup_it = (Elm_Ctxpopup_Item *)it;
1245
1246    edje_object_signal_emit(VIEW(ctxpopup_it), emission, source);
1247 }
1248
1249 static void
1250 _bg_clicked_cb(void *data,
1251                Evas_Object *obj __UNUSED__,
1252                const char *emission __UNUSED__,
1253                const char *source __UNUSED__)
1254 {
1255    ELM_CTXPOPUP_DATA_GET(data, sd);
1256
1257    _hide_signals_emit(data, sd->dir);
1258 }
1259
1260 static void
1261 _on_show(void *data __UNUSED__,
1262          Evas *e __UNUSED__,
1263          Evas_Object *obj,
1264          void *event_info __UNUSED__)
1265 {
1266    Eina_List *elist;
1267    Elm_Ctxpopup_Item *item;
1268    int idx = 0;
1269
1270    ELM_CTXPOPUP_DATA_GET(obj, sd);
1271
1272    if ((!sd->items) && (!sd->content)) return;
1273
1274    sd->visible = EINA_TRUE;
1275
1276    evas_object_show(sd->bg);
1277    evas_object_show(sd->arrow);
1278    evas_object_show(sd->layout);
1279
1280    edje_object_signal_emit(sd->bg, "elm,state,show", "elm");
1281    elm_layout_signal_emit(obj, "elm,state,show", "elm");
1282
1283    EINA_LIST_FOREACH(sd->items, elist, item)
1284      {
1285         if (item->label && !item->icon)
1286           {
1287              if(!sd->horizontal)
1288                _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "text_style_item",
1289                                      elm_widget_style_get(obj));
1290              else
1291                _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "text_style_item_horizontal",
1292                                      elm_widget_style_get(obj));
1293           }
1294
1295         if (idx++ == 0)
1296           edje_object_signal_emit(VIEW(item), "elm,state,default", "elm");
1297         else
1298           edje_object_signal_emit(VIEW(item), "elm,state,separator", "elm");
1299      }
1300
1301    elm_layout_sizing_eval(obj);
1302
1303    elm_object_focus_set(obj, EINA_TRUE);
1304 }
1305
1306 static void
1307 _on_hide(void *data __UNUSED__,
1308          Evas *e __UNUSED__,
1309          Evas_Object *obj,
1310          void *event_info __UNUSED__)
1311 {
1312    ELM_CTXPOPUP_DATA_GET(obj, sd);
1313
1314    if (!sd->visible) return;
1315
1316    evas_object_hide(sd->bg);
1317    evas_object_hide(sd->arrow);
1318    evas_object_hide(sd->layout);
1319
1320    _scroller_size_reset(sd);
1321
1322    sd->visible = EINA_FALSE;
1323 }
1324
1325 static void
1326 _on_move(void *data __UNUSED__,
1327          Evas *e __UNUSED__,
1328          Evas_Object *obj,
1329          void *event_info __UNUSED__)
1330 {
1331    ELM_CTXPOPUP_DATA_GET(obj, sd);
1332
1333    if (sd->visible) evas_object_show(sd->arrow);
1334
1335    _scroller_size_reset(sd);
1336    _elm_ctxpopup_smart_sizing_eval(obj);
1337 }
1338
1339 static void
1340 _hide_finished_cb(void *data,
1341                   Evas_Object *obj __UNUSED__,
1342                   const char *emission __UNUSED__,
1343                   const char *source __UNUSED__)
1344 {
1345    evas_object_hide(data);
1346    evas_object_smart_callback_call(data, SIG_DISMISSED, NULL);
1347 }
1348
1349 static void
1350 _scroller_resize(void *data, Evas *e __UNUSED__,
1351                  Evas_Object *obj,
1352                  void *event_info __UNUSED__)
1353 {
1354    Evas_Coord w, h;
1355
1356    ELM_CTXPOPUP_DATA_GET(data, sd);
1357    if (!sd) return;
1358    if (!sd->visible) return;
1359    if (sd->finished) return;
1360
1361    evas_object_geometry_get(obj, 0, 0, &w, &h);
1362
1363    if (w != 0 && h != 0)
1364      {
1365         if ((w <= sd->max_sc_w) && (h <= sd->max_sc_h))
1366           {
1367              _elm_ctxpopup_smart_sizing_eval(data);
1368              sd->finished = EINA_TRUE;
1369              return;
1370           }
1371      }
1372
1373    if (sd->max_sc_w < w)
1374      sd->max_sc_w = w;
1375    if (sd->max_sc_h < h)
1376      sd->max_sc_h = h;
1377
1378    _elm_ctxpopup_smart_sizing_eval(data);
1379 }
1380
1381 static void
1382 _ctxpopup_restack_cb(void *data __UNUSED__,
1383                      Evas *e __UNUSED__,
1384                      Evas_Object *obj,
1385                      void *event_info __UNUSED__)
1386 {
1387    ELM_CTXPOPUP_DATA_GET(obj, sd);
1388    evas_object_stack_below(sd->bg, obj);
1389    evas_object_stack_below(sd->layout, obj);
1390 }
1391
1392 static void
1393 _list_del(Elm_Ctxpopup_Smart_Data *sd)
1394 {
1395    if (!sd->scr) return;
1396
1397    edje_object_part_unswallow(ELM_WIDGET_DATA(sd)->resize_obj, sd->scr);
1398    evas_object_del(sd->scr);
1399    sd->scr = NULL;
1400    evas_object_del(sd->box);
1401    sd->box = NULL;
1402 }
1403
1404 static void
1405 _list_new(Evas_Object *obj)
1406 {
1407    ELM_CTXPOPUP_DATA_GET(obj, sd);
1408    if (!sd) return;
1409
1410    //scroller
1411    sd->scr = elm_scroller_add(obj);
1412    elm_object_style_set(sd->scr, "ctxpopup");
1413    evas_object_size_hint_align_set(sd->scr, EVAS_HINT_FILL, EVAS_HINT_FILL);
1414    evas_object_event_callback_add(sd->scr, EVAS_CALLBACK_RESIZE,
1415                                   _scroller_resize, obj);
1416    edje_object_part_swallow(ELM_WIDGET_DATA(sd)->resize_obj, "elm.swallow.content", sd->scr);
1417
1418    //box
1419    if (!sd->box)
1420      {
1421         sd->box = elm_box_add(obj);
1422         evas_object_size_hint_weight_set(sd->box, EVAS_HINT_EXPAND,
1423                                          EVAS_HINT_EXPAND);
1424         evas_object_event_callback_add
1425            (sd->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1426         ELM_CONTAINER_CLASS(_elm_ctxpopup_parent_sc)->content_set
1427            (obj, "elm.swallow.content", sd->box);
1428      }
1429
1430    elm_object_content_set(sd->scr, sd->box);
1431    elm_ctxpopup_horizontal_set(obj, sd->horizontal);
1432 }
1433
1434 static Eina_Bool
1435 _elm_ctxpopup_smart_disable(Evas_Object *obj)
1436 {
1437    Eina_List *l;
1438    Elm_Object_Item *it;
1439
1440    ELM_CTXPOPUP_DATA_GET(obj, sd);
1441
1442    if (!ELM_WIDGET_CLASS(_elm_ctxpopup_parent_sc)->disable(obj))
1443      return EINA_FALSE;
1444
1445    EINA_LIST_FOREACH(sd->items, l, it)
1446      elm_object_item_disabled_set(it, elm_widget_disabled_get(obj));
1447
1448    return EINA_TRUE;
1449 }
1450
1451 static void
1452 _elm_ctxpopup_smart_add(Evas_Object *obj)
1453 {
1454    EVAS_SMART_DATA_ALLOC(obj, Elm_Ctxpopup_Smart_Data);
1455    ELM_CTXPOPUP_DATA_GET(obj, sd);
1456
1457    ELM_WIDGET_CLASS(_elm_ctxpopup_parent_sc)->base.add(obj);
1458
1459    elm_layout_theme_set(obj, "ctxpopup", "base", elm_widget_style_get(obj));
1460    elm_layout_signal_callback_add
1461      (obj, "elm,action,hide,finished", "", _hide_finished_cb, obj);
1462
1463    //Background
1464    priv->bg = edje_object_add(evas_object_evas_get(obj));
1465    elm_widget_theme_object_set(obj, priv->bg, "ctxpopup", "bg", "default");
1466    edje_object_signal_callback_add
1467      (priv->bg, "elm,action,click", "", _bg_clicked_cb, obj);
1468
1469    evas_object_stack_below(priv->bg, obj);
1470
1471    //Arrow
1472    priv->arrow = edje_object_add(evas_object_evas_get(obj));
1473    elm_widget_theme_object_set
1474      (obj, priv->arrow, "ctxpopup", "arrow", "default");
1475
1476    priv->dir_priority[0] = ELM_CTXPOPUP_DIRECTION_UP;
1477    priv->dir_priority[1] = ELM_CTXPOPUP_DIRECTION_LEFT;
1478    priv->dir_priority[2] = ELM_CTXPOPUP_DIRECTION_RIGHT;
1479    priv->dir_priority[3] = ELM_CTXPOPUP_DIRECTION_DOWN;
1480    priv->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1481
1482    evas_object_event_callback_add
1483       (obj, EVAS_CALLBACK_RESTACK, _ctxpopup_restack_cb, obj);
1484
1485    evas_object_smart_callback_add
1486       (obj, "scroll-freeze-on", _freeze_on_cb, obj);
1487    evas_object_smart_callback_add
1488       (obj, "scroll-freeze-off", _freeze_off_cb, obj);
1489    evas_object_smart_callback_add
1490       (obj, "scroll-hold-on", _hold_on_cb, obj);
1491    evas_object_smart_callback_add
1492       (obj, "scroll-hold-off", _hold_off_cb, obj);
1493
1494    priv->box = elm_box_add(obj);
1495    evas_object_size_hint_weight_set
1496      (priv->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1497
1498    evas_object_event_callback_add
1499      (priv->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1500
1501    /* box will be our content placeholder, thus the parent's version call */
1502    ELM_CONTAINER_CLASS(_elm_ctxpopup_parent_sc)->content_set
1503      (obj, "elm.swallow.content", priv->box);
1504
1505    priv->layout = edje_object_add(evas_object_evas_get(obj));
1506    elm_widget_theme_object_set(obj, priv->layout, "ctxpopup", "layout", "default");
1507
1508    edje_object_signal_callback_add
1509      (priv->layout, "elm,action,hide,finished", "", _hide_finished_cb, obj);
1510    edje_object_part_swallow(priv->layout, "swallow", ELM_WIDGET_DATA(sd)->resize_obj);
1511    evas_object_size_hint_weight_set
1512      (priv->layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
1513
1514    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _on_show, NULL);
1515    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _on_hide, NULL);
1516    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _on_move, NULL);
1517
1518    elm_widget_can_focus_set(obj, EINA_TRUE);
1519 }
1520
1521 static void
1522 _elm_ctxpopup_smart_del(Evas_Object *obj)
1523 {
1524    ELM_CTXPOPUP_DATA_GET(obj, sd);
1525
1526    evas_object_event_callback_del_full
1527      (sd->box, EVAS_CALLBACK_RESIZE, _on_content_resized, obj);
1528    _parent_detach(obj);
1529
1530    elm_ctxpopup_clear(obj);
1531    evas_object_del(sd->arrow);
1532    sd->arrow = NULL; /* stops _sizing_eval() from going on on deletion */
1533
1534    evas_object_del(sd->bg);
1535    sd->bg = NULL;
1536
1537    evas_object_del(sd->layout);
1538    sd->layout = NULL;
1539
1540    ELM_WIDGET_CLASS(_elm_ctxpopup_parent_sc)->base.del(obj);
1541 }
1542
1543 static void
1544 _elm_ctxpopup_smart_parent_set(Evas_Object *obj,
1545                                Evas_Object *parent)
1546 {
1547    //default parent is to be hover parent
1548    elm_ctxpopup_hover_parent_set(obj, parent);
1549 }
1550
1551 static void
1552 _elm_ctxpopup_smart_access(Evas_Object *obj, Eina_Bool is_access)
1553 {
1554    Eina_List *elist = NULL;
1555    Elm_Ctxpopup_Item *it;
1556
1557    ELM_CTXPOPUP_CHECK(obj);
1558    ELM_CTXPOPUP_DATA_GET(obj, sd);
1559
1560    EINA_LIST_FOREACH(sd->items, elist, it)
1561      _access_widget_item_register(it, is_access);
1562 }
1563
1564 static void
1565 _elm_ctxpopup_smart_set_user(Elm_Ctxpopup_Smart_Class *sc)
1566 {
1567    ELM_WIDGET_CLASS(sc)->base.add = _elm_ctxpopup_smart_add;
1568    ELM_WIDGET_CLASS(sc)->base.del = _elm_ctxpopup_smart_del;
1569
1570    ELM_WIDGET_CLASS(sc)->parent_set = _elm_ctxpopup_smart_parent_set;
1571    ELM_WIDGET_CLASS(sc)->disable = _elm_ctxpopup_smart_disable;
1572    ELM_WIDGET_CLASS(sc)->event = _elm_ctxpopup_smart_event;
1573    ELM_WIDGET_CLASS(sc)->theme = _elm_ctxpopup_smart_theme;
1574    ELM_WIDGET_CLASS(sc)->sub_object_add = _elm_ctxpopup_smart_sub_object_add;
1575    ELM_WIDGET_CLASS(sc)->focus_next = _elm_ctxpopup_smart_focus_next;
1576    ELM_WIDGET_CLASS(sc)->focus_direction = _elm_ctxpopup_smart_focus_direction;
1577    ELM_WIDGET_CLASS(sc)->translate = _elm_ctxpopup_smart_translate;
1578
1579    ELM_CONTAINER_CLASS(sc)->content_get = _elm_ctxpopup_smart_content_get;
1580    ELM_CONTAINER_CLASS(sc)->content_set = _elm_ctxpopup_smart_content_set;
1581    ELM_CONTAINER_CLASS(sc)->content_unset = _elm_ctxpopup_smart_content_unset;
1582
1583    ELM_LAYOUT_CLASS(sc)->sizing_eval = _elm_ctxpopup_smart_sizing_eval;
1584
1585    ELM_WIDGET_CLASS(sc)->access = _elm_ctxpopup_smart_access;
1586 }
1587
1588 EAPI const Elm_Ctxpopup_Smart_Class *
1589 elm_ctxpopup_smart_class_get(void)
1590 {
1591    static Elm_Ctxpopup_Smart_Class _sc =
1592      ELM_CTXPOPUP_SMART_CLASS_INIT_NAME_VERSION(ELM_CTXPOPUP_SMART_NAME);
1593    static const Elm_Ctxpopup_Smart_Class *class = NULL;
1594    Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
1595
1596    if (class)
1597      return class;
1598
1599    _elm_ctxpopup_smart_set(&_sc);
1600    esc->callbacks = _smart_callbacks;
1601    class = &_sc;
1602
1603    return class;
1604 }
1605
1606 EAPI Evas_Object *
1607 elm_ctxpopup_add(Evas_Object *parent)
1608 {
1609    Evas_Object *obj;
1610
1611    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1612
1613    obj = elm_widget_add(_elm_ctxpopup_smart_class_new(), parent);
1614    if (!obj) return NULL;
1615
1616    if (!elm_widget_sub_object_add(parent, obj))
1617      ERR("could not add %p as sub object of %p", obj, parent);
1618
1619    /* access: parent could be any object such as elm_list which does
1620       not know elc_ctxpopup as its child object in the focus_next(); */
1621    ELM_WIDGET_DATA_GET(obj, sd);
1622    sd->highlight_root = EINA_TRUE;
1623
1624    return obj;
1625 }
1626
1627 EAPI void
1628 elm_ctxpopup_hover_parent_set(Evas_Object *obj,
1629                               Evas_Object *parent)
1630 {
1631    Evas_Coord x, y, w, h;
1632
1633    ELM_CTXPOPUP_CHECK(obj);
1634    ELM_CTXPOPUP_DATA_GET(obj, sd);
1635
1636    if (!parent) return;
1637
1638    _parent_detach(obj);
1639
1640    evas_object_event_callback_add
1641      (parent, EVAS_CALLBACK_DEL, _on_parent_del, obj);
1642    evas_object_event_callback_add
1643      (parent, EVAS_CALLBACK_MOVE, _on_parent_move, obj);
1644    evas_object_event_callback_add
1645      (parent, EVAS_CALLBACK_RESIZE, _on_parent_resize, obj);
1646
1647    sd->parent = parent;
1648
1649    //Update Background
1650    evas_object_geometry_get(parent, &x, &y, &w, &h);
1651    evas_object_move(sd->bg, x, y);
1652    evas_object_resize(sd->bg, w, h);
1653
1654    if (sd->visible) elm_layout_sizing_eval(obj);
1655 }
1656
1657 EAPI Evas_Object *
1658 elm_ctxpopup_hover_parent_get(const Evas_Object *obj)
1659 {
1660    ELM_CTXPOPUP_CHECK(obj) NULL;
1661    ELM_CTXPOPUP_DATA_GET(obj, sd);
1662
1663    return sd->parent;
1664 }
1665
1666 EAPI void
1667 elm_ctxpopup_clear(Evas_Object *obj)
1668 {
1669    ELM_CTXPOPUP_CHECK(obj);
1670    ELM_CTXPOPUP_DATA_GET(obj, sd);
1671
1672    _items_remove(sd);
1673    _list_del(sd);
1674    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1675 }
1676
1677 EAPI void
1678 elm_ctxpopup_horizontal_set(Evas_Object *obj,
1679                             Eina_Bool horizontal)
1680 {
1681    Eina_List *elist;
1682    Elm_Ctxpopup_Item *item;
1683    int idx = 0;
1684
1685    ELM_CTXPOPUP_CHECK(obj);
1686    ELM_CTXPOPUP_DATA_GET(obj, sd);
1687
1688    sd->horizontal = !!horizontal;
1689
1690    if (!sd->scr)
1691       return;
1692
1693   if (!horizontal)
1694      {
1695         elm_box_horizontal_set(sd->box, EINA_FALSE);
1696         elm_scroller_bounce_set(sd->scr, EINA_FALSE, EINA_TRUE);
1697      }
1698    else
1699      {
1700         elm_box_horizontal_set(sd->box, EINA_TRUE);
1701         elm_scroller_bounce_set(sd->scr, EINA_TRUE, EINA_FALSE);
1702      }
1703
1704    EINA_LIST_FOREACH(sd->items, elist, item)
1705      {
1706         if (item->label && !item->icon && !horizontal)
1707           _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "text_style_item",
1708                                 elm_widget_style_get(obj));
1709         else if (item->label && !item->icon && horizontal)
1710           _elm_theme_object_set(obj, VIEW(item), "ctxpopup", "text_style_item_horizontal",
1711                                 elm_widget_style_get(obj));
1712
1713         if (idx++ == 0)
1714           edje_object_signal_emit(VIEW(item), "elm,state,default", "elm");
1715         else
1716           edje_object_signal_emit(VIEW(item), "elm,state,separator", "elm");
1717
1718         _item_disable_hook((Elm_Object_Item *)item);
1719      }
1720
1721    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1722
1723    if (sd->visible) elm_layout_sizing_eval(obj);
1724 }
1725
1726 EAPI Eina_Bool
1727 elm_ctxpopup_horizontal_get(const Evas_Object *obj)
1728 {
1729    ELM_CTXPOPUP_CHECK(obj) EINA_FALSE;
1730    ELM_CTXPOPUP_DATA_GET(obj, sd);
1731
1732    return sd->horizontal;
1733 }
1734
1735 EAPI Elm_Object_Item *
1736 elm_ctxpopup_item_append(Evas_Object *obj,
1737                          const char *label,
1738                          Evas_Object *icon,
1739                          Evas_Smart_Cb func,
1740                          const void *data)
1741 {
1742    Elm_Ctxpopup_Item *item;
1743    Evas_Object *content;
1744
1745    ELM_CTXPOPUP_CHECK(obj) NULL;
1746    ELM_CTXPOPUP_DATA_GET(obj, sd);
1747
1748    item = elm_widget_item_new(obj, Elm_Ctxpopup_Item);
1749    if (!item) return NULL;
1750
1751    elm_widget_item_del_pre_hook_set(item, _item_del_pre_hook);
1752    elm_widget_item_disable_hook_set(item, _item_disable_hook);
1753    elm_widget_item_text_set_hook_set(item, _item_text_set_hook);
1754    elm_widget_item_text_get_hook_set(item, _item_text_get_hook);
1755    elm_widget_item_content_set_hook_set(item, _item_content_set_hook);
1756    elm_widget_item_content_get_hook_set(item, _item_content_get_hook);
1757    elm_widget_item_signal_emit_hook_set(item, _item_signal_emit_hook);
1758
1759    //The first item is appended.
1760    content = elm_object_content_unset(obj);
1761    if (content) evas_object_del(content);
1762
1763    if (!sd->items)
1764      _list_new(obj);
1765
1766    item->func = func;
1767    item->base.data = data;
1768
1769    if (icon && label)
1770      _item_new(item, "icon_text_style_item");
1771    else if (label)
1772      {
1773         if (!sd->horizontal)
1774           _item_new(item, "text_style_item");
1775         else
1776           _item_new(item, "text_style_item_horizontal");
1777      }
1778    else
1779      _item_new(item, "icon_style_item");
1780
1781    _item_icon_set(item, icon);
1782    _item_label_set(item, label);
1783    elm_box_pack_end(sd->box, VIEW(item));
1784    sd->items = eina_list_append(sd->items, item);
1785
1786    sd->dir = ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1787
1788    if (sd->visible)
1789      {
1790         _scroller_size_reset(sd);
1791         _elm_ctxpopup_smart_sizing_eval(obj);
1792      }
1793
1794
1795    return (Elm_Object_Item *)item;
1796 }
1797
1798 EAPI void
1799 elm_ctxpopup_direction_priority_set(Evas_Object *obj,
1800                                     Elm_Ctxpopup_Direction first,
1801                                     Elm_Ctxpopup_Direction second,
1802                                     Elm_Ctxpopup_Direction third,
1803                                     Elm_Ctxpopup_Direction fourth)
1804 {
1805    ELM_CTXPOPUP_CHECK(obj);
1806    ELM_CTXPOPUP_DATA_GET(obj, sd);
1807
1808    sd->dir_priority[0] = first;
1809    sd->dir_priority[1] = second;
1810    sd->dir_priority[2] = third;
1811    sd->dir_priority[3] = fourth;
1812
1813    if (sd->visible) elm_layout_sizing_eval(obj);
1814 }
1815
1816 EAPI void
1817 elm_ctxpopup_direction_priority_get(Evas_Object *obj,
1818                                     Elm_Ctxpopup_Direction *first,
1819                                     Elm_Ctxpopup_Direction *second,
1820                                     Elm_Ctxpopup_Direction *third,
1821                                     Elm_Ctxpopup_Direction *fourth)
1822 {
1823    ELM_CTXPOPUP_CHECK(obj);
1824    ELM_CTXPOPUP_DATA_GET(obj, sd);
1825
1826    if (first) *first = sd->dir_priority[0];
1827    if (second) *second = sd->dir_priority[1];
1828    if (third) *third = sd->dir_priority[2];
1829    if (fourth) *fourth = sd->dir_priority[3];
1830 }
1831
1832 EAPI Elm_Ctxpopup_Direction
1833 elm_ctxpopup_direction_get(const Evas_Object *obj)
1834 {
1835    ELM_CTXPOPUP_CHECK(obj) ELM_CTXPOPUP_DIRECTION_UNKNOWN;
1836    ELM_CTXPOPUP_DATA_GET(obj, sd);
1837
1838    return sd->dir;
1839 }
1840
1841 EAPI void
1842 elm_ctxpopup_dismiss(Evas_Object *obj)
1843 {
1844    ELM_CTXPOPUP_CHECK(obj);
1845    ELM_CTXPOPUP_DATA_GET(obj, sd);
1846
1847    _hide_signals_emit(obj, sd->dir);
1848 }