Elementary: Added on-the-fly UI-mirroing support to all of the widgets
[framework/uifw/elementary.git] / src / lib / elm_hover.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Hover Hover
6  *
7  * A Hover object will over its @p parent object at the @p target
8  * location. Anything in the background will be given a darker
9  * coloring to indicate that the hover object is on top (at the
10  * default theme).
11  *
12  * @note The hover object will take up the entire space of @p target
13  * object.
14  */
15
16 typedef struct _Widget_Data Widget_Data;
17 typedef struct _Content_Info Content_Info;
18
19 #ifndef MAX
20 # define MAX(a, b) (((a) > (b)) ? (a) : (b))
21 #endif
22
23 #define ELM_HOVER_PARTS_FOREACH unsigned int i = 0; \
24   for (i = 0; i < sizeof(wd->subs) / sizeof(wd->subs[0]); i++)
25
26 static const char *_directions[] = {
27   "left",
28   "top-left",
29   "top",
30   "top-right",
31   "right",
32   "bottom-right",
33   "bottom",
34   "bottom-left",
35   "middle"
36 };
37
38 #define _HOV_LEFT (_directions[0])
39 #define _HOV_TOP_LEFT (_directions[1])
40 #define _HOV_TOP (_directions[2])
41 #define _HOV_TOP_RIGHT (_directions[2])
42 #define _HOV_RIGHT (_directions[4])
43 #define _HOV_BOTTOM_RIGHT (_directions[5])
44 #define _HOV_BOTTOM (_directions[6])
45 #define _HOV_BOTTOM_LEFT (_directions[7])
46 #define _HOV_MIDDLE (_directions[8])
47
48 struct _Content_Info
49 {
50    const char *swallow;
51    Evas_Object *obj;
52 };
53
54 struct _Widget_Data
55 {
56    Evas_Object *hov, *cov;
57    Evas_Object *offset, *size;
58    Evas_Object *parent, *target;
59    Evas_Object *smt_sub;
60    Content_Info subs[sizeof(_directions)/sizeof(_directions[0])];
61 };
62
63 static const char *widtype = NULL;
64 static void _del_pre_hook(Evas_Object *obj);
65 static void _del_hook(Evas_Object *obj);
66 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
67 static void _theme_hook(Evas_Object *obj);
68 static void _sizing_eval(Evas_Object *obj);
69 static void _reval_content(Evas_Object *obj);
70 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
71 static void _hov_show_do(Evas_Object *obj);
72 static void _hov_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
73 static void _hov_resize(void *data, Evas *e, Evas_Object *obj, void *event_info);
74 static void _hov_show(void *data, Evas *e, Evas_Object *obj, void *event_info);
75 static void _hov_hide(void *data, Evas *e, Evas_Object *obj, void *event_info);
76 static void _on_focus_hook(void *data, Evas_Object *obj);
77 static void _elm_hover_sub_obj_placement_eval_cb(void *data, Evas *e, Evas_Object *obj, void *event_info);
78 static void _elm_hover_sub_obj_placement_eval(Evas_Object *obj);
79
80 static const char SIG_CLICKED[] = "clicked";
81 static const char SIG_SMART_LOCATION_CHANGED[] = "smart,changed";
82 static const Evas_Smart_Cb_Description _signals[] = {
83   {SIG_CLICKED, ""},
84   {SIG_SMART_LOCATION_CHANGED, ""},
85   {NULL, NULL}
86 };
87
88 static void
89 _del_pre_hook(Evas_Object *obj)
90 {
91    Widget_Data *wd = elm_widget_data_get(obj);
92    if (!wd)
93      return;
94
95    if (evas_object_visible_get(obj))
96      evas_object_smart_callback_call(obj, SIG_CLICKED, NULL);
97    elm_hover_target_set(obj, NULL);
98    elm_hover_parent_set(obj, NULL);
99    evas_object_event_callback_del_full(wd->hov, EVAS_CALLBACK_MOVE, _hov_move, obj);
100    evas_object_event_callback_del_full(wd->hov, EVAS_CALLBACK_RESIZE, _hov_resize, obj);
101    evas_object_event_callback_del_full(wd->hov, EVAS_CALLBACK_SHOW, _hov_show, obj);
102    evas_object_event_callback_del_full(wd->hov, EVAS_CALLBACK_HIDE, _hov_hide, obj);
103 }
104
105 static void
106 _del_hook(Evas_Object *obj)
107 {
108    Widget_Data *wd = elm_widget_data_get(obj);
109    if (!wd) return;
110    free(wd);
111 }
112
113 static void
114 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
115 {
116    Widget_Data *wd = elm_widget_data_get(obj);
117    if (!wd) return;
118    if (elm_widget_focus_get(obj))
119      {
120         edje_object_signal_emit(wd->cov, "elm,action,focus", "elm");
121         evas_object_focus_set(wd->cov, EINA_TRUE);
122      }
123    else
124      {
125         edje_object_signal_emit(wd->cov, "elm,action,unfocus", "elm");
126         evas_object_focus_set(wd->cov, EINA_FALSE);
127      }
128 }
129
130 static void
131 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
132 {
133    Widget_Data *wd = elm_widget_data_get(obj);
134    if (!wd) return;
135    edje_object_mirrored_set(wd->cov, rtl);
136 }
137
138 static void
139 _theme_hook(Evas_Object *obj)
140 {
141    Widget_Data *wd = elm_widget_data_get(obj);
142    if (!wd) return;
143    _elm_widget_mirrored_reload(obj);
144    _mirrored_set(obj, elm_widget_mirrored_get(obj));
145    // FIXME: hover contents doesn't seem to propagate resizes properly
146    _elm_theme_object_set(obj, wd->cov, "hover", "base", elm_widget_style_get(obj));
147    edje_object_scale_set(wd->cov, elm_widget_scale_get(obj) *
148                          _elm_config->scale);
149
150    if (wd->smt_sub)
151       _elm_hover_sub_obj_placement_eval(obj);
152    else
153       _reval_content(obj);
154    _sizing_eval(obj);
155    if (evas_object_visible_get(wd->cov)) _hov_show_do(obj);
156 }
157
158 static void
159 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
160 {
161    Widget_Data *wd;
162
163    wd = elm_widget_data_get(obj);
164    if (!wd)
165      return;
166
167    edje_object_signal_emit(wd->cov, emission, source);
168 }
169
170 static void
171 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
172 {
173    Widget_Data *wd;
174
175    wd = elm_widget_data_get(obj);
176    if (!wd)
177      return;
178
179    edje_object_signal_callback_add(wd->hov, emission, source, func_cb, data);
180 }
181
182 static void
183 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
184 {
185    Widget_Data *wd;
186
187    wd = elm_widget_data_get(obj);
188
189    edje_object_signal_callback_del_full(wd->hov, emission, source, func_cb,
190                                         data);
191 }
192
193 static void
194 _elm_hover_left_space_calc(Widget_Data *wd, Evas_Coord *spc_l, Evas_Coord *spc_t, Evas_Coord *spc_r, Evas_Coord *spc_b)
195 {
196    Evas_Coord x = 0, y = 0, w = 0, h = 0, x2 = 0, y2 = 0, w2 = 0, h2 = 0;
197
198    if (wd->parent)
199      evas_object_geometry_get(wd->parent, &x, &y, &w, &h);
200    if (wd->target)
201      evas_object_geometry_get(wd->target, &x2, &y2, &w2, &h2);
202
203    *spc_l = x2 - x;
204    *spc_r = (x + w) - (x2 + w2);
205    if (*spc_l < 0)
206      *spc_l = 0;
207    if (*spc_r < 0)
208      *spc_r = 0;
209
210    *spc_t = y2 - y;
211    *spc_b = (y + h) - (y2 + h2);
212    if (*spc_t < 0)
213      *spc_t = 0;
214    if (*spc_b < 0)
215      *spc_b = 0;
216 }
217
218 static void
219 _sizing_eval(Evas_Object *obj)
220 {
221    Widget_Data *wd = elm_widget_data_get(obj);
222    Evas_Coord ofs_x, x = 0, y = 0, w = 0, h = 0, x2 = 0, y2 = 0, w2 = 0, h2 = 0;
223    if (!wd) return;
224    if (wd->parent) evas_object_geometry_get(wd->parent, &x, &y, &w, &h);
225    if (wd->hov) evas_object_geometry_get(wd->hov, &x2, &y2, &w2, &h2);
226
227    if (elm_widget_mirrored_get(obj))
228      ofs_x = w - (x2 - x) - w2;
229    else
230      ofs_x = x2 - x;
231
232    evas_object_move(wd->cov, x, y);
233    evas_object_resize(wd->cov, w, h);
234    evas_object_size_hint_min_set(wd->offset, ofs_x, y2 - y);
235    evas_object_size_hint_min_set(wd->size, w2, h2);
236    edje_object_part_swallow(wd->cov, "elm.swallow.offset", wd->offset);
237    edje_object_part_swallow(wd->cov, "elm.swallow.size", wd->size);
238 }
239
240 static void
241 _reval_content(Evas_Object *obj)
242 {
243    Widget_Data *wd = elm_widget_data_get(obj);
244    if (!wd)
245      return;
246
247    ELM_HOVER_PARTS_FOREACH
248      {
249         char buf[1024];
250         snprintf(buf, sizeof(buf), "elm.swallow.slot.%s", wd->subs[i].swallow);
251         edje_object_part_swallow(wd->cov, buf, wd->subs[i].obj);
252      }
253 }
254
255 static const char *
256 _elm_hover_smart_content_location_get(Widget_Data *wd,  Evas_Coord spc_l, Evas_Coord spc_t, Evas_Coord spc_r, Evas_Coord spc_b)
257 {
258    Evas_Coord c_w = 0, c_h = 0, mid_w, mid_h;
259    int max;
260
261    evas_object_size_hint_min_get(wd->smt_sub, &c_w, &c_h);
262    mid_w = c_w / 2;
263    mid_h = c_h / 2;
264
265    if (spc_l > spc_r)
266      goto left;
267
268    max = MAX(spc_t, spc_r);
269    max = MAX(max, spc_b);
270
271    if (max == spc_t)
272      {
273         if (mid_w > spc_l)
274           return _HOV_TOP_RIGHT;
275
276         return _HOV_TOP;
277      }
278
279    if (max == spc_r)
280      {
281         if (mid_h > spc_t)
282           return _HOV_BOTTOM_RIGHT;
283         else if (mid_h > spc_b)
284           return _HOV_TOP_RIGHT;
285
286         return _HOV_RIGHT;
287      }
288
289    if (mid_h > spc_l)
290      return _HOV_BOTTOM_RIGHT;
291
292    return _HOV_BOTTOM;
293
294 left:
295    max = MAX(spc_t, spc_l);
296    max = MAX(max, spc_b);
297
298    if (max == spc_t)
299      {
300         if (mid_w > spc_r)
301           return _HOV_TOP_LEFT;
302
303         return _HOV_TOP;
304      }
305
306    if (max == spc_l)
307      {
308         if (mid_h > spc_t)
309           return _HOV_BOTTOM_LEFT;
310         else if (mid_h > spc_b)
311           return _HOV_TOP_LEFT;
312
313         return _HOV_LEFT;
314      }
315
316    if (mid_h > spc_r)
317      return _HOV_BOTTOM_LEFT;
318
319    return _HOV_BOTTOM;
320 }
321
322 static void
323 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
324 {
325    Widget_Data *wd;
326    Evas_Object *sub;
327
328    sub = event_info;
329    wd = elm_widget_data_get(obj);
330    if (!wd)
331      return;
332
333    if (wd->smt_sub)
334      {
335         if (wd->smt_sub == sub)
336            wd->smt_sub = NULL;
337      }
338    else
339      {
340         ELM_HOVER_PARTS_FOREACH
341           {
342              if (wd->subs[i].obj == sub)
343                {
344                   wd->subs[i].obj = NULL;
345                   break;
346                }
347           }
348      }
349 }
350
351 static void
352 _hov_show_do(Evas_Object *obj)
353 {
354    Widget_Data *wd = elm_widget_data_get(obj);
355    if (!wd)
356      return;
357
358    if (wd->cov)
359      {
360         evas_object_show(wd->cov);
361         edje_object_signal_emit(wd->cov, "elm,action,show", "elm");
362      }
363
364    ELM_HOVER_PARTS_FOREACH
365      {
366         char buf[1024];
367
368         if (wd->subs[i].obj)
369           {
370              snprintf(buf, sizeof(buf), "elm,action,slot,%s,show",
371                       wd->subs[i].swallow);
372              edje_object_signal_emit(wd->cov, buf, "elm");
373           }
374      }
375 }
376
377 static void
378 _hov_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
379 {
380    _sizing_eval(data);
381 }
382
383 static void
384 _hov_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
385 {
386    _sizing_eval(data);
387 }
388
389 static void
390 _hov_show(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
391 {
392    _hov_show_do(data);
393 }
394
395 static void
396 _hov_hide(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
397 {
398    Widget_Data *wd = elm_widget_data_get(data);
399    if (!wd) return;
400    if (wd->cov)
401      {
402         edje_object_signal_emit(wd->cov, "elm,action,hide", "elm");
403         evas_object_hide(wd->cov);
404      }
405
406    ELM_HOVER_PARTS_FOREACH
407      {
408         char buf[1024];
409
410         if (wd->subs[i].obj)
411           {
412              snprintf(buf, sizeof(buf), "elm,action,slot,%s,hide",
413                       wd->subs[i].swallow);
414              edje_object_signal_emit(wd->cov, buf, "elm");
415           }
416      }
417 }
418
419 static void
420 _target_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
421 {
422    Widget_Data *wd = elm_widget_data_get(data);
423    if (!wd) return;
424    wd->target = NULL;
425 }
426
427 static void
428 _target_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
429 {
430    Widget_Data *wd = elm_widget_data_get(data);
431    if (!wd)
432      return;
433
434    _sizing_eval(data);
435    _elm_hover_sub_obj_placement_eval(data);
436 }
437
438 static void
439 _signal_dismiss(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
440 {
441    Widget_Data *wd = elm_widget_data_get(data);
442    if (!wd) return;
443    evas_object_hide(data);
444    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
445 }
446
447 static void
448 _parent_move(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
449 {
450    _sizing_eval(data);
451 }
452
453 static void
454 _parent_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
455 {
456    _sizing_eval(data);
457 }
458
459 static void
460 _parent_show(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
461 {
462 }
463
464 static void
465 _parent_hide(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
466 {
467    Widget_Data *wd = elm_widget_data_get(data);
468    if (!wd) return;
469    evas_object_hide(wd->cov);
470 }
471
472 static void
473 _parent_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
474 {
475    Widget_Data *wd = elm_widget_data_get(data);
476    if (!wd) return;
477    elm_hover_parent_set(data, NULL);
478    _sizing_eval(data);
479 }
480
481 /**
482  * Adds a hover object to @p parent
483  *
484  * @param parent The parent object
485  * @return The hover object or NULL if one could not be created
486  *
487  * @ingroup Hover
488  */
489 EAPI Evas_Object *
490 elm_hover_add(Evas_Object *parent)
491 {
492    Evas_Object *obj;
493    Evas *e;
494    Widget_Data *wd;
495
496    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
497
498    wd = ELM_NEW(Widget_Data);
499
500    ELM_HOVER_PARTS_FOREACH
501      wd->subs[i].swallow = _directions[i];
502
503    e = evas_object_evas_get(parent);
504    if (!e) return NULL;
505    obj = elm_widget_add(e);
506    ELM_SET_WIDTYPE(widtype, "hover");
507    elm_widget_type_set(obj, "hover");
508    elm_widget_sub_object_add(parent, obj);
509    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
510    elm_widget_data_set(obj, wd);
511    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
512    elm_widget_theme_hook_set(obj, _theme_hook);
513    elm_widget_del_hook_set(obj, _del_hook);
514    elm_widget_can_focus_set(obj, EINA_TRUE);
515    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
516    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
517    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
518
519    wd->hov = evas_object_rectangle_add(e);
520    evas_object_pass_events_set(wd->hov, EINA_TRUE);
521    evas_object_color_set(wd->hov, 0, 0, 0, 0);
522    elm_widget_resize_object_set(obj, wd->hov);
523    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_MOVE, _hov_move, obj);
524    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_RESIZE, _hov_resize, obj);
525    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_SHOW, _hov_show, obj);
526    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_HIDE, _hov_hide, obj);
527
528    wd->cov = edje_object_add(e);
529    _elm_theme_object_set(obj, wd->cov, "hover", "base", "default");
530    elm_widget_sub_object_add(obj, wd->cov);
531    edje_object_signal_callback_add(wd->cov, "elm,action,dismiss", "",
532                                    _signal_dismiss, obj);
533
534    wd->offset = evas_object_rectangle_add(e);
535    evas_object_pass_events_set(wd->offset, EINA_TRUE);
536    evas_object_color_set(wd->offset, 0, 0, 0, 0);
537    elm_widget_sub_object_add(obj, wd->offset);
538
539    wd->size = evas_object_rectangle_add(e);
540    evas_object_pass_events_set(wd->size, EINA_TRUE);
541    evas_object_color_set(wd->size, 0, 0, 0, 0);
542    elm_widget_sub_object_add(obj, wd->size);
543
544    edje_object_part_swallow(wd->cov, "elm.swallow.offset", wd->offset);
545    edje_object_part_swallow(wd->cov, "elm.swallow.size", wd->size);
546
547    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
548
549    elm_hover_parent_set(obj, parent);
550    evas_object_smart_callbacks_descriptions_set(obj, _signals);
551
552    _mirrored_set(obj, elm_widget_mirrored_get(obj));
553    _sizing_eval(obj);
554    return obj;
555 }
556
557 /**
558  * Sets the target object for the hover.
559  *
560  * @param obj The hover object
561  * @param target The object to center the hover onto. The hover
562  * will take up the entire space that the target object fills.
563  *
564  * @ingroup Hover
565  */
566 EAPI void
567 elm_hover_target_set(Evas_Object *obj, Evas_Object *target)
568 {
569    ELM_CHECK_WIDTYPE(obj, widtype);
570    Widget_Data *wd = elm_widget_data_get(obj);
571
572    if (wd->target)
573      {
574         evas_object_event_callback_del_full(wd->target, EVAS_CALLBACK_DEL,
575                                             _target_del, obj);
576         evas_object_event_callback_del_full(wd->target, EVAS_CALLBACK_MOVE,
577                                             _target_move, obj);
578      }
579    wd->target = target;
580    if (wd->target)
581      {
582         evas_object_event_callback_add(wd->target, EVAS_CALLBACK_DEL,
583                                        _target_del, obj);
584         evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOVE,
585                                        _target_move, obj);
586         elm_widget_hover_object_set(target, obj);
587         _sizing_eval(obj);
588      }
589 }
590
591
592 /**
593  * Sets the parent object for the hover.
594  *
595  * @param obj The hover object
596  * @param parent The object to locate the hover over.
597  *
598  * @ingroup Hover
599  */
600 EAPI void
601 elm_hover_parent_set(Evas_Object *obj, Evas_Object *parent)
602 {
603    ELM_CHECK_WIDTYPE(obj, widtype);
604    Widget_Data *wd = elm_widget_data_get(obj);
605    if (!wd) return;
606    if (wd->parent)
607      {
608         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_MOVE,
609                                        _parent_move, obj);
610         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE,
611                                        _parent_resize, obj);
612         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_SHOW,
613                                        _parent_show, obj);
614         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_HIDE,
615                                        _parent_hide, obj);
616         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_DEL,
617                                        _parent_del, obj);
618      }
619    wd->parent = parent;
620    if (wd->parent)
621      {
622         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_MOVE,
623                                        _parent_move, obj);
624         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_RESIZE,
625                                        _parent_resize, obj);
626         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_SHOW,
627                                        _parent_show, obj);
628         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_HIDE,
629                                        _parent_hide, obj);
630         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_DEL,
631                                        _parent_del, obj);
632 //      elm_widget_sub_object_add(parent, obj);
633      }
634    _sizing_eval(obj);
635 }
636
637 /**
638  * Gets the target object for the hover.
639  *
640  * @param obj The hover object
641  * @return The target object of the hover.
642  *
643  * @ingroup Hover
644  */
645 EAPI Evas_Object *
646 elm_hover_target_get(const Evas_Object *obj)
647 {
648    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
649    Widget_Data *wd = elm_widget_data_get(obj);
650    if (!wd) return NULL;
651
652    return wd->target;
653 }
654
655 /**
656  * Gets the parent object for the hover.
657  *
658  * @param obj The hover object
659  * @return The parent object to locate the hover over.
660  *
661  * @ingroup Hover
662  */
663 EAPI Evas_Object *
664 elm_hover_parent_get(const Evas_Object *obj)
665 {
666    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
667    Widget_Data *wd = elm_widget_data_get(obj);
668    if (!wd) return NULL;
669
670    return wd->parent;
671 }
672
673 static void
674 _elm_hover_subs_del(Widget_Data *wd)
675 {
676    ELM_HOVER_PARTS_FOREACH
677      {
678         if (wd->subs[i].obj)
679           {
680              evas_object_del(wd->subs[i].obj);
681              wd->subs[i].obj = NULL;
682           }
683      }
684 }
685
686 static void
687 _elm_hover_sub_obj_placement_eval(Evas_Object *obj)
688 {
689    Evas_Coord spc_l, spc_r, spc_t, spc_b;
690    const char *smart_dir;
691    Widget_Data *wd;
692    char buf[1024];
693
694    wd = elm_widget_data_get(obj);
695    if (!wd->smt_sub)
696      return;
697
698    _elm_hover_left_space_calc(wd, &spc_l, &spc_t, &spc_r, &spc_b);
699
700    edje_object_part_unswallow(wd->cov, wd->smt_sub);
701
702    smart_dir = _elm_hover_smart_content_location_get(wd, spc_l, spc_t, spc_r,
703                                                      spc_b);
704    evas_object_smart_callback_call(obj, SIG_SMART_LOCATION_CHANGED,
705                                    (void *)smart_dir);
706
707    if (elm_widget_mirrored_get(obj))
708      {
709         if (smart_dir == _HOV_BOTTOM_LEFT)
710           smart_dir = _HOV_BOTTOM_RIGHT;
711         else if (smart_dir == _HOV_BOTTOM_RIGHT)
712           smart_dir = _HOV_BOTTOM_LEFT;
713         else if (smart_dir == _HOV_RIGHT)
714           smart_dir = _HOV_LEFT;
715         else if (smart_dir == _HOV_LEFT)
716           smart_dir = _HOV_RIGHT;
717         else if (smart_dir == _HOV_TOP_RIGHT)
718           smart_dir = _HOV_TOP_LEFT;
719         else if (smart_dir == _HOV_TOP_LEFT)
720           smart_dir = _HOV_TOP_RIGHT;
721      }
722    snprintf(buf, sizeof(buf), "elm.swallow.slot.%s", smart_dir);
723    edje_object_part_swallow(wd->cov, buf, wd->smt_sub);
724 }
725
726 static void
727 _elm_hover_sub_obj_placement_eval_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
728 {
729    _elm_hover_sub_obj_placement_eval(data);
730 }
731
732 /**
733  * Sets the content of the hover object and the direction in which
734  * it will pop out.
735  *
736  * Once the content object is set for a given direction, a previously
737  * set one (on the same direction) will be deleted. If you want to
738  * keep that old content object, use the elm_hover_content_unset()
739  * function.
740  *
741  * @param obj The hover object
742  * @param swallow The direction that the object will be displayed
743  * at. Accepted values are "left", "top-left", "top", "top-right",
744  * "right", "bottom-right", "bottom", "bottom-left", "middle" and
745  * "smart".
746  * @param content The content to place at @p swallow
747  *
748  * All directions may have contents at the same time, except for
749  * "smart". This is a special placement hint and its use case
750  * independs of the calculations coming from
751  * elm_hover_best_content_location_get(). Its use is for cases when
752  * one desires only one hover content, but with a dinamic special
753  * placement within the hover area. The content's geometry, whenever
754  * it changes, will be used to decide on a best location not
755  * extrapolating the hover's parent object view to show it in (still
756  * being the hover's target determinant of its medium part -- move and
757  * resize it to simulate finger sizes, for example). If one of the
758  * directions other than "smart" are used, a previously content set
759  * using it will be deleted, and vice-versa.
760  *
761  * @ingroup Hover
762  */
763 EAPI void
764 elm_hover_content_set(Evas_Object *obj, const char *swallow, Evas_Object *content)
765 {
766    ELM_CHECK_WIDTYPE(obj, widtype);
767
768    Widget_Data *wd;
769
770    wd = elm_widget_data_get(obj);
771    if (!wd)
772      return;
773
774    if (!strcmp(swallow, "smart"))
775      {
776         if (wd->smt_sub != content)
777           {
778              _elm_hover_subs_del(wd);
779              wd->smt_sub = content;
780           }
781
782         if (content)
783           {
784              elm_widget_sub_object_add(obj, content);
785              evas_object_event_callback_add(wd->smt_sub,
786                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
787                                             _elm_hover_sub_obj_placement_eval_cb,
788                                             obj);
789
790              _elm_hover_sub_obj_placement_eval(obj);
791           }
792
793         goto end;
794      }
795
796    if (wd->smt_sub)
797      {
798         evas_object_del(wd->smt_sub);
799         wd->smt_sub = NULL;
800      }
801
802    ELM_HOVER_PARTS_FOREACH
803      {
804         if (!strcmp(swallow, wd->subs[i].swallow))
805           {
806              if (content == wd->subs[i].obj)
807                return;
808              evas_object_del(wd->subs[i].obj);
809              wd->subs[i].obj = NULL;
810
811              if (content)
812                {
813                   char buf[1024];
814
815                   snprintf(buf, sizeof(buf), "elm.swallow.slot.%s", swallow);
816                   elm_widget_sub_object_add(obj, content);
817                   edje_object_part_swallow(wd->cov, buf, content);
818                   wd->subs[i].obj = content;
819                }
820              break;
821           }
822      }
823
824  end:
825    _sizing_eval(obj);
826 }
827
828 /**
829  * Get the content of the hover object, in a given direction.
830  *
831  * Return the content object which was set for this widget in the
832  * given direction.
833  *
834  * @param obj The hover object
835  * @param swallow The direction that the object was display at.
836  * @return The content that was being used
837  *
838  * @note See elm_hover_content_set() for more information.
839  *
840  * @ingroup Hover
841  */
842 EAPI Evas_Object *
843 elm_hover_content_get(const Evas_Object *obj, const char *swallow)
844 {
845    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
846
847    Widget_Data *wd;
848
849    wd = elm_widget_data_get(obj);
850    if (!wd)
851      return NULL;
852
853    if (!strcmp(swallow, "smart"))
854      return wd->smt_sub;
855
856    ELM_HOVER_PARTS_FOREACH
857      if (!strcmp(swallow, wd->subs[i].swallow))
858        return wd->subs[i].obj;
859
860    return NULL;
861 }
862
863 static void
864 _elm_hover_sub_obj_unparent(Evas_Object *obj)
865 {
866    Widget_Data *wd;
867
868    wd = elm_widget_data_get(obj);
869
870    elm_widget_sub_object_del(obj, wd->smt_sub);
871    evas_object_event_callback_del_full(wd->smt_sub,
872                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
873                                        _elm_hover_sub_obj_placement_eval_cb,
874                                        obj);
875    edje_object_part_unswallow(wd->cov, wd->smt_sub);
876    wd->smt_sub = NULL;
877 }
878
879 /**
880  * Unset the content of the hover object, in a given direction.
881  *
882  * Unparent and return the content object set at that direction.
883  *
884  * @param obj The hover object
885  * @param swallow The direction that the object was display at.
886  * @return The content that was being used.
887  *
888  * @note See elm_hover_content_set() for more information.
889  *
890  * @ingroup Hover
891  */
892 EAPI Evas_Object *
893 elm_hover_content_unset(Evas_Object *obj, const char *swallow)
894 {
895    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
896
897    Widget_Data *wd;
898
899    wd = elm_widget_data_get(obj);
900    if (!wd)
901      return NULL;
902
903    if (!strcmp(swallow, "smart"))
904      {
905         Evas_Object *content;
906
907         if (!wd->smt_sub)
908           return NULL;
909
910         content = wd->smt_sub;
911         _elm_hover_sub_obj_unparent(obj);
912         return content;
913      }
914
915    ELM_HOVER_PARTS_FOREACH
916      {
917         if (!strcmp(swallow, wd->subs[i].swallow))
918           {
919              Evas_Object *content;
920
921              if (!wd->subs[i].obj)
922                return NULL;
923
924              content = wd->subs[i].obj;
925              elm_widget_sub_object_del(obj, wd->subs[i].obj);
926              edje_object_part_unswallow(wd->cov, wd->subs[i].obj);
927              wd->subs[i].obj = NULL;
928
929              return content;
930           }
931      }
932
933    return NULL;
934 }
935
936 /**
937  * Returns the best swallow location for content in the hover.
938  *
939  * @param obj The hover object
940  * @param pref_axis The preferred orientation axis for the hover object to use
941  * @return The edje location to place content into the hover or @c
942  *         NULL, on errors.
943  *
944  * @p pref_axis may be one of
945  * <ul>
946  * - @c ELM_HOVER_AXIS_NONE -- no prefered orientation
947  * - @c ELM_HOVER_AXIS_HORIZONTAL -- horizontal
948  * - @c ELM_HOVER_AXIS_VERTICAL -- vertical
949  * - @c ELM_HOVER_AXIS_BOTH -- both
950  * </ul>
951  *
952  * See also elm_hover_content_set().
953  *
954  * @ingroup Hover
955  */
956 EAPI const char *
957 elm_hover_best_content_location_get(const Evas_Object *obj, Elm_Hover_Axis pref_axis)
958 {
959    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
960
961    Evas_Coord spc_l, spc_r, spc_t, spc_b;
962    Widget_Data *wd;
963
964    wd = elm_widget_data_get(obj);
965    if (!wd)
966      return NULL;
967
968    _elm_hover_left_space_calc(wd, &spc_l, &spc_t, &spc_r, &spc_b);
969
970    if (pref_axis == ELM_HOVER_AXIS_HORIZONTAL)
971      {
972         if (spc_l < spc_r) return _HOV_RIGHT;
973         else return _HOV_LEFT;
974      }
975    else if (pref_axis == ELM_HOVER_AXIS_VERTICAL)
976      {
977         if (spc_t < spc_b) return _HOV_BOTTOM;
978         else return _HOV_TOP;
979      }
980
981    if (spc_l < spc_r)
982      {
983         if (spc_t > spc_r) return _HOV_TOP;
984         else if (spc_b > spc_r) return _HOV_BOTTOM;
985         return _HOV_RIGHT;
986      }
987    if (spc_t > spc_r) return _HOV_TOP;
988    else if (spc_b > spc_r) return _HOV_BOTTOM;
989    return _HOV_LEFT;
990 }