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