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