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