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