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