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