elementary - entry, genlist, gengrid, image, icon, hover, frame, diskselector, index...
[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    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
497
498    ELM_SET_WIDTYPE(widtype, "hover");
499    elm_widget_type_set(obj, "hover");
500    elm_widget_sub_object_add(parent, obj);
501    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
502    elm_widget_data_set(obj, wd);
503    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
504    elm_widget_theme_hook_set(obj, _theme_hook);
505    elm_widget_del_hook_set(obj, _del_hook);
506    elm_widget_can_focus_set(obj, EINA_TRUE);
507    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
508    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
509    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
510
511    ELM_HOVER_PARTS_FOREACH
512       wd->subs[i].swallow = _directions[i];
513
514    wd->hov = evas_object_rectangle_add(e);
515    evas_object_pass_events_set(wd->hov, EINA_TRUE);
516    evas_object_color_set(wd->hov, 0, 0, 0, 0);
517    elm_widget_resize_object_set(obj, wd->hov);
518    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_MOVE, _hov_move, obj);
519    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_RESIZE, _hov_resize, obj);
520    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_SHOW, _hov_show, obj);
521    evas_object_event_callback_add(wd->hov, EVAS_CALLBACK_HIDE, _hov_hide, obj);
522
523    wd->cov = edje_object_add(e);
524    _elm_theme_object_set(obj, wd->cov, "hover", "base", "default");
525    elm_widget_sub_object_add(obj, wd->cov);
526    edje_object_signal_callback_add(wd->cov, "elm,action,dismiss", "",
527                                    _signal_dismiss, obj);
528
529    wd->offset = evas_object_rectangle_add(e);
530    evas_object_pass_events_set(wd->offset, EINA_TRUE);
531    evas_object_color_set(wd->offset, 0, 0, 0, 0);
532    elm_widget_sub_object_add(obj, wd->offset);
533
534    wd->size = evas_object_rectangle_add(e);
535    evas_object_pass_events_set(wd->size, EINA_TRUE);
536    evas_object_color_set(wd->size, 0, 0, 0, 0);
537    elm_widget_sub_object_add(obj, wd->size);
538
539    edje_object_part_swallow(wd->cov, "elm.swallow.offset", wd->offset);
540    edje_object_part_swallow(wd->cov, "elm.swallow.size", wd->size);
541
542    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
543
544    elm_hover_parent_set(obj, parent);
545    evas_object_smart_callbacks_descriptions_set(obj, _signals);
546
547    _mirrored_set(obj, elm_widget_mirrored_get(obj));
548    _sizing_eval(obj);
549    return obj;
550 }
551
552 /**
553  * Sets the target object for the hover.
554  *
555  * @param obj The hover object
556  * @param target The object to center the hover onto. The hover
557  * will take up the entire space that the target object fills.
558  *
559  * @ingroup Hover
560  */
561 EAPI void
562 elm_hover_target_set(Evas_Object *obj, Evas_Object *target)
563 {
564    ELM_CHECK_WIDTYPE(obj, widtype);
565    Widget_Data *wd = elm_widget_data_get(obj);
566
567    if (wd->target)
568      {
569         evas_object_event_callback_del_full(wd->target, EVAS_CALLBACK_DEL,
570                                             _target_del, obj);
571         evas_object_event_callback_del_full(wd->target, EVAS_CALLBACK_MOVE,
572                                             _target_move, obj);
573      }
574    wd->target = target;
575    if (wd->target)
576      {
577         evas_object_event_callback_add(wd->target, EVAS_CALLBACK_DEL,
578                                        _target_del, obj);
579         evas_object_event_callback_add(wd->target, EVAS_CALLBACK_MOVE,
580                                        _target_move, obj);
581         elm_widget_hover_object_set(target, obj);
582         _sizing_eval(obj);
583      }
584 }
585
586
587 /**
588  * Sets the parent object for the hover.
589  *
590  * @param obj The hover object
591  * @param parent The object to locate the hover over.
592  *
593  * @ingroup Hover
594  */
595 EAPI void
596 elm_hover_parent_set(Evas_Object *obj, Evas_Object *parent)
597 {
598    ELM_CHECK_WIDTYPE(obj, widtype);
599    Widget_Data *wd = elm_widget_data_get(obj);
600    if (!wd) return;
601    if (wd->parent)
602      {
603         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_MOVE,
604                                             _parent_move, obj);
605         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_RESIZE,
606                                             _parent_resize, obj);
607         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_SHOW,
608                                             _parent_show, obj);
609         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_HIDE,
610                                             _parent_hide, obj);
611         evas_object_event_callback_del_full(wd->parent, EVAS_CALLBACK_DEL,
612                                             _parent_del, obj);
613      }
614    wd->parent = parent;
615    if (wd->parent)
616      {
617         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_MOVE,
618                                        _parent_move, obj);
619         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_RESIZE,
620                                        _parent_resize, obj);
621         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_SHOW,
622                                        _parent_show, obj);
623         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_HIDE,
624                                        _parent_hide, obj);
625         evas_object_event_callback_add(wd->parent, EVAS_CALLBACK_DEL,
626                                        _parent_del, obj);
627         //      elm_widget_sub_object_add(parent, obj);
628      }
629    _sizing_eval(obj);
630 }
631
632 /**
633  * Gets the target object for the hover.
634  *
635  * @param obj The hover object
636  * @return The target object of the hover.
637  *
638  * @ingroup Hover
639  */
640 EAPI Evas_Object *
641 elm_hover_target_get(const Evas_Object *obj)
642 {
643    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
644    Widget_Data *wd = elm_widget_data_get(obj);
645    if (!wd) return NULL;
646
647    return wd->target;
648 }
649
650 /**
651  * Gets the parent object for the hover.
652  *
653  * @param obj The hover object
654  * @return The parent object to locate the hover over.
655  *
656  * @ingroup Hover
657  */
658 EAPI Evas_Object *
659 elm_hover_parent_get(const Evas_Object *obj)
660 {
661    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
662    Widget_Data *wd = elm_widget_data_get(obj);
663    if (!wd) return NULL;
664
665    return wd->parent;
666 }
667
668 static void
669 _elm_hover_subs_del(Widget_Data *wd)
670 {
671    ELM_HOVER_PARTS_FOREACH
672      {
673         if (wd->subs[i].obj)
674           {
675              evas_object_del(wd->subs[i].obj);
676              wd->subs[i].obj = NULL;
677           }
678      }
679 }
680
681 static void
682 _elm_hover_sub_obj_placement_eval(Evas_Object *obj)
683 {
684    Evas_Coord spc_l, spc_r, spc_t, spc_b;
685    const char *smart_dir;
686    Widget_Data *wd;
687    char buf[1024];
688
689    wd = elm_widget_data_get(obj);
690    if (!wd->smt_sub)
691      return;
692
693    _elm_hover_left_space_calc(wd, &spc_l, &spc_t, &spc_r, &spc_b);
694
695    edje_object_part_unswallow(wd->cov, wd->smt_sub);
696
697    smart_dir = _elm_hover_smart_content_location_get(wd, spc_l, spc_t, spc_r,
698                                                      spc_b);
699    evas_object_smart_callback_call(obj, SIG_SMART_LOCATION_CHANGED,
700                                    (void *)smart_dir);
701
702    if (elm_widget_mirrored_get(obj))
703      {
704         if (smart_dir == _HOV_BOTTOM_LEFT)
705           smart_dir = _HOV_BOTTOM_RIGHT;
706         else if (smart_dir == _HOV_BOTTOM_RIGHT)
707           smart_dir = _HOV_BOTTOM_LEFT;
708         else if (smart_dir == _HOV_RIGHT)
709           smart_dir = _HOV_LEFT;
710         else if (smart_dir == _HOV_LEFT)
711           smart_dir = _HOV_RIGHT;
712         else if (smart_dir == _HOV_TOP_RIGHT)
713           smart_dir = _HOV_TOP_LEFT;
714         else if (smart_dir == _HOV_TOP_LEFT)
715           smart_dir = _HOV_TOP_RIGHT;
716      }
717    snprintf(buf, sizeof(buf), "elm.swallow.slot.%s", smart_dir);
718    edje_object_part_swallow(wd->cov, buf, wd->smt_sub);
719 }
720
721 static void
722 _elm_hover_sub_obj_placement_eval_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
723 {
724    _elm_hover_sub_obj_placement_eval(data);
725 }
726
727 /**
728  * Sets the content of the hover object and the direction in which
729  * it will pop out.
730  *
731  * Once the content object is set for a given direction, a previously
732  * set one (on the same direction) will be deleted. If you want to
733  * keep that old content object, use the elm_hover_content_unset()
734  * function.
735  *
736  * @param obj The hover object
737  * @param swallow The direction that the object will be displayed
738  * at. Accepted values are "left", "top-left", "top", "top-right",
739  * "right", "bottom-right", "bottom", "bottom-left", "middle" and
740  * "smart".
741  * @param content The content to place at @p swallow
742  *
743  * All directions may have contents at the same time, except for
744  * "smart". This is a special placement hint and its use case
745  * independs of the calculations coming from
746  * elm_hover_best_content_location_get(). Its use is for cases when
747  * one desires only one hover content, but with a dinamic special
748  * placement within the hover area. The content's geometry, whenever
749  * it changes, will be used to decide on a best location not
750  * extrapolating the hover's parent object view to show it in (still
751  * being the hover's target determinant of its medium part -- move and
752  * resize it to simulate finger sizes, for example). If one of the
753  * directions other than "smart" are used, a previously content set
754  * using it will be deleted, and vice-versa.
755  *
756  * @ingroup Hover
757  */
758 EAPI void
759 elm_hover_content_set(Evas_Object *obj, const char *swallow, Evas_Object *content)
760 {
761    ELM_CHECK_WIDTYPE(obj, widtype);
762
763    Widget_Data *wd;
764
765    wd = elm_widget_data_get(obj);
766    if (!wd)
767      return;
768
769    if (!strcmp(swallow, "smart"))
770      {
771         if (wd->smt_sub != content)
772           {
773              _elm_hover_subs_del(wd);
774              wd->smt_sub = content;
775           }
776
777         if (content)
778           {
779              elm_widget_sub_object_add(obj, content);
780              evas_object_event_callback_add(wd->smt_sub,
781                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
782                                             _elm_hover_sub_obj_placement_eval_cb,
783                                             obj);
784
785              _elm_hover_sub_obj_placement_eval(obj);
786           }
787
788         goto end;
789      }
790
791    if (wd->smt_sub)
792      {
793         evas_object_del(wd->smt_sub);
794         wd->smt_sub = NULL;
795      }
796
797    ELM_HOVER_PARTS_FOREACH
798      {
799         if (!strcmp(swallow, wd->subs[i].swallow))
800           {
801              if (content == wd->subs[i].obj)
802                return;
803              evas_object_del(wd->subs[i].obj);
804              wd->subs[i].obj = NULL;
805
806              if (content)
807                {
808                   char buf[1024];
809
810                   snprintf(buf, sizeof(buf), "elm.swallow.slot.%s", swallow);
811                   elm_widget_sub_object_add(obj, content);
812                   edje_object_part_swallow(wd->cov, buf, content);
813                   wd->subs[i].obj = content;
814                }
815              break;
816           }
817      }
818
819 end:
820    _sizing_eval(obj);
821 }
822
823 /**
824  * Get the content of the hover object, in a given direction.
825  *
826  * Return the content object which was set for this widget in the
827  * given direction.
828  *
829  * @param obj The hover object
830  * @param swallow The direction that the object was display at.
831  * @return The content that was being used
832  *
833  * @note See elm_hover_content_set() for more information.
834  *
835  * @ingroup Hover
836  */
837 EAPI Evas_Object *
838 elm_hover_content_get(const Evas_Object *obj, const char *swallow)
839 {
840    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
841
842    Widget_Data *wd;
843
844    wd = elm_widget_data_get(obj);
845    if (!wd)
846      return NULL;
847
848    if (!strcmp(swallow, "smart"))
849      return wd->smt_sub;
850
851    ELM_HOVER_PARTS_FOREACH
852       if (!strcmp(swallow, wd->subs[i].swallow))
853         return wd->subs[i].obj;
854
855    return NULL;
856 }
857
858 static void
859 _elm_hover_sub_obj_unparent(Evas_Object *obj)
860 {
861    Widget_Data *wd;
862
863    wd = elm_widget_data_get(obj);
864
865    elm_widget_sub_object_del(obj, wd->smt_sub);
866    evas_object_event_callback_del_full(wd->smt_sub,
867                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
868                                        _elm_hover_sub_obj_placement_eval_cb,
869                                        obj);
870    edje_object_part_unswallow(wd->cov, wd->smt_sub);
871    wd->smt_sub = NULL;
872 }
873
874 /**
875  * Unset the content of the hover object, in a given direction.
876  *
877  * Unparent and return the content object set at that direction.
878  *
879  * @param obj The hover object
880  * @param swallow The direction that the object was display at.
881  * @return The content that was being used.
882  *
883  * @note See elm_hover_content_set() for more information.
884  *
885  * @ingroup Hover
886  */
887 EAPI Evas_Object *
888 elm_hover_content_unset(Evas_Object *obj, const char *swallow)
889 {
890    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
891
892    Widget_Data *wd;
893
894    wd = elm_widget_data_get(obj);
895    if (!wd)
896      return NULL;
897
898    if (!strcmp(swallow, "smart"))
899      {
900         Evas_Object *content;
901
902         if (!wd->smt_sub)
903           return NULL;
904
905         content = wd->smt_sub;
906         _elm_hover_sub_obj_unparent(obj);
907         return content;
908      }
909
910    ELM_HOVER_PARTS_FOREACH
911      {
912         if (!strcmp(swallow, wd->subs[i].swallow))
913           {
914              Evas_Object *content;
915
916              if (!wd->subs[i].obj)
917                return NULL;
918
919              content = wd->subs[i].obj;
920              elm_widget_sub_object_del(obj, wd->subs[i].obj);
921              edje_object_part_unswallow(wd->cov, wd->subs[i].obj);
922              wd->subs[i].obj = NULL;
923
924              return content;
925           }
926      }
927
928    return NULL;
929 }
930
931 /**
932  * Returns the best swallow location for content in the hover.
933  *
934  * @param obj The hover object
935  * @param pref_axis The preferred orientation axis for the hover object to use
936  * @return The edje location to place content into the hover or @c
937  *         NULL, on errors.
938  *
939  * @p pref_axis may be one of
940  * <ul>
941  * - @c ELM_HOVER_AXIS_NONE -- no prefered orientation
942  * - @c ELM_HOVER_AXIS_HORIZONTAL -- horizontal
943  * - @c ELM_HOVER_AXIS_VERTICAL -- vertical
944  * - @c ELM_HOVER_AXIS_BOTH -- both
945  * </ul>
946  *
947  * See also elm_hover_content_set().
948  *
949  * @ingroup Hover
950  */
951 EAPI const char *
952 elm_hover_best_content_location_get(const Evas_Object *obj, Elm_Hover_Axis pref_axis)
953 {
954    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
955
956    Evas_Coord spc_l, spc_r, spc_t, spc_b;
957    Widget_Data *wd;
958
959    wd = elm_widget_data_get(obj);
960    if (!wd)
961      return NULL;
962
963    _elm_hover_left_space_calc(wd, &spc_l, &spc_t, &spc_r, &spc_b);
964
965    if (pref_axis == ELM_HOVER_AXIS_HORIZONTAL)
966      {
967         if (spc_l < spc_r) return _HOV_RIGHT;
968         else return _HOV_LEFT;
969      }
970    else if (pref_axis == ELM_HOVER_AXIS_VERTICAL)
971      {
972         if (spc_t < spc_b) return _HOV_BOTTOM;
973         else return _HOV_TOP;
974      }
975
976    if (spc_l < spc_r)
977      {
978         if (spc_t > spc_r) return _HOV_TOP;
979         else if (spc_b > spc_r) return _HOV_BOTTOM;
980         return _HOV_RIGHT;
981      }
982    if (spc_t > spc_r) return _HOV_TOP;
983    else if (spc_b > spc_r) return _HOV_BOTTOM;
984    return _HOV_LEFT;
985 }