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