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