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