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