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