whoops, forgot to give transparent tooltip an alpha channel
[framework/uifw/elementary.git] / src / lib / els_tooltip.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Tooltips Tooltips
6  *
7  * The Tooltip is an (internal, for now) smart object used to show a
8  * content in a frame on mouse hover of objects(or widgets), with
9  * tips/information about them.
10  */
11
12 static const char _tooltip_key[] = "_elm_tooltip";
13
14 #define ELM_TOOLTIP_GET_OR_RETURN(tt, obj, ...)         \
15   Elm_Tooltip *tt;                                      \
16   do                                                    \
17     {                                                   \
18        if (!(obj))                                      \
19          {                                              \
20             CRITICAL("Null pointer: " #obj);            \
21             return __VA_ARGS__;                         \
22          }                                              \
23        tt = evas_object_data_get((obj), _tooltip_key);  \
24        if (!tt)                                         \
25          {                                              \
26             ERR("Object does not have tooltip: " #obj); \
27             return __VA_ARGS__;                         \
28          }                                              \
29     }                                                   \
30   while (0)
31
32 struct _Elm_Tooltip
33 {
34    Elm_Tooltip_Content_Cb   func;
35    Evas_Smart_Cb            del_cb;
36    const void              *data;
37    const char              *style;
38    Evas                    *evas, *tt_evas;
39    Evas_Object             *eventarea, *owner;
40    Evas_Object             *tooltip, *content;
41    Evas_Object             *tt_win;
42    Ecore_Timer             *show_timer;
43    Ecore_Timer             *hide_timer;
44    Ecore_Job               *reconfigure_job;
45    struct {
46       Evas_Coord            x, y, bx, by;
47    } pad;
48    struct {
49       double                x, y;
50    } rel_pos;
51    double                   hide_timeout; /* from theme */
52    Eina_Bool                visible_lock:1;
53    Eina_Bool                changed_style:1;
54    Eina_Bool                free_size : 1;
55 };
56
57 static void _elm_tooltip_reconfigure(Elm_Tooltip *tt);
58 static void _elm_tooltip_reconfigure_job_start(Elm_Tooltip *tt);
59 static void _elm_tooltip_reconfigure_job_stop(Elm_Tooltip *tt);
60 static void _elm_tooltip_hide_anim_start(Elm_Tooltip *tt);
61 static void _elm_tooltip_hide_anim_stop(Elm_Tooltip *tt);
62 static void _elm_tooltip_show_timer_stop(Elm_Tooltip *tt);
63 static void _elm_tooltip_hide(Elm_Tooltip *tt);
64 static void _elm_tooltip_data_clean(Elm_Tooltip *tt);
65
66 static void
67 _elm_tooltip_content_changed_hints_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
68 {
69    _elm_tooltip_reconfigure_job_start(data);
70 }
71
72 static void
73 _elm_tooltip_content_del_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
74 {
75    Elm_Tooltip *tt = data;
76    tt->content = NULL;
77    tt->visible_lock = EINA_FALSE;
78    _elm_tooltip_hide(tt);
79 }
80
81 static void
82 _elm_tooltip_obj_move_cb(void *data, Evas *e  __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info  __UNUSED__)
83 {
84    Elm_Tooltip *tt = data;
85    _elm_tooltip_reconfigure_job_start(tt);
86 }
87
88 static void
89 _elm_tooltip_obj_resize_cb(void *data, Evas *e  __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info  __UNUSED__)
90 {
91    Elm_Tooltip *tt = data;
92    _elm_tooltip_reconfigure_job_start(tt);
93 }
94
95 static void
96 _elm_tooltip_obj_mouse_move_cb(void *data, Evas *e  __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info  __UNUSED__)
97 {
98    Elm_Tooltip *tt = data;
99    _elm_tooltip_reconfigure_job_start(tt);
100 }
101
102 static void
103 _elm_tooltip_show(Elm_Tooltip *tt)
104 {
105    _elm_tooltip_show_timer_stop(tt);
106    _elm_tooltip_hide_anim_stop(tt);
107
108    if (tt->tooltip)
109      {
110         _elm_tooltip_reconfigure_job_start(tt);
111         return;
112      }
113    if (tt->free_size)
114      {
115         tt->tt_win = elm_win_add(NULL, "tooltip", ELM_WIN_BASIC);
116         elm_win_borderless_set(tt->tt_win, EINA_TRUE);
117         elm_win_override_set(tt->tt_win, EINA_TRUE);
118         tt->tt_evas = evas_object_evas_get(tt->tt_win);
119         tt->tooltip = edje_object_add(tt->tt_evas);
120         evas_object_pass_events_set(tt->tooltip, EINA_TRUE);
121         evas_object_move(tt->tooltip, 0, 0);
122         elm_win_resize_object_add(tt->tt_win, tt->tooltip);
123 #ifdef HAVE_ELEMENTARY_X
124         ecore_x_window_shape_input_rectangle_set(elm_win_xwindow_get(tt->tt_win), 0, 0, 0, 0);
125 #endif
126         evas_object_show(tt->tt_win);
127      }
128    else
129       tt->tooltip = edje_object_add(tt->evas);
130    if (!tt->tooltip) return;
131
132    evas_object_layer_set(tt->tt_win ?: tt->tooltip, ELM_OBJECT_LAYER_TOOLTIP);
133
134    evas_object_event_callback_add
135      (tt->eventarea, EVAS_CALLBACK_MOVE, _elm_tooltip_obj_move_cb, tt);
136    evas_object_event_callback_add
137      (tt->eventarea, EVAS_CALLBACK_RESIZE, _elm_tooltip_obj_resize_cb, tt);
138    evas_object_event_callback_add
139      (tt->eventarea, EVAS_CALLBACK_MOUSE_MOVE, _elm_tooltip_obj_mouse_move_cb, tt);
140
141    evas_object_pass_events_set(tt->tooltip, EINA_TRUE);
142    tt->changed_style = EINA_TRUE;
143    _elm_tooltip_reconfigure_job_start(tt);
144 }
145
146 static void
147 _elm_tooltip_content_del(Elm_Tooltip *tt)
148 {
149    if (!tt->content) return;
150
151    evas_object_event_callback_del_full
152      (tt->content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
153       _elm_tooltip_content_changed_hints_cb, tt);
154    evas_object_event_callback_del_full
155      (tt->content, EVAS_CALLBACK_DEL,
156       _elm_tooltip_content_del_cb, tt);
157    evas_object_hide(tt->content);
158    evas_object_del(tt->content);
159    tt->content = NULL;
160 }
161
162
163 static void
164 _elm_tooltip_hide(Elm_Tooltip *tt)
165 {
166    _elm_tooltip_show_timer_stop(tt);
167    _elm_tooltip_hide_anim_stop(tt);
168    _elm_tooltip_reconfigure_job_stop(tt);
169
170    if (!tt->tooltip) return;
171    if (tt->visible_lock) return;
172
173    _elm_tooltip_content_del(tt);
174
175    evas_object_event_callback_del_full
176      (tt->eventarea, EVAS_CALLBACK_MOVE, _elm_tooltip_obj_move_cb, tt);
177    evas_object_event_callback_del_full
178      (tt->eventarea, EVAS_CALLBACK_RESIZE, _elm_tooltip_obj_resize_cb, tt);
179    evas_object_event_callback_del_full
180      (tt->eventarea, EVAS_CALLBACK_MOUSE_MOVE, _elm_tooltip_obj_mouse_move_cb, tt);
181
182    if (tt->tt_win) evas_object_del(tt->tt_win);
183    else evas_object_del(tt->tooltip);
184
185    tt->tt_win = NULL;
186    tt->tt_evas = NULL;
187    tt->tooltip = NULL;
188 }
189
190 static void
191 _elm_tooltip_reconfigure_job(void *data)
192 {
193    Elm_Tooltip *tt = data;
194    tt->reconfigure_job = NULL;
195    _elm_tooltip_reconfigure(data);
196 }
197
198 static void
199 _elm_tooltip_reconfigure_job_stop(Elm_Tooltip *tt)
200 {
201    if (!tt->reconfigure_job) return;
202    ecore_job_del(tt->reconfigure_job);
203    tt->reconfigure_job = NULL;
204 }
205
206 static void
207 _elm_tooltip_reconfigure_job_start(Elm_Tooltip *tt)
208 {
209    if (!tt->reconfigure_job)
210      tt->reconfigure_job = ecore_job_add(_elm_tooltip_reconfigure_job, tt);
211 }
212
213 static Eina_Bool
214 _elm_tooltip_hide_anim_cb(void *data)
215 {
216    Elm_Tooltip *tt = data;
217    tt->hide_timer = NULL;
218    _elm_tooltip_hide(tt);
219    return EINA_FALSE;
220 }
221
222 static void
223 _elm_tooltip_hide_anim_start(Elm_Tooltip *tt)
224 {
225    double extra = 0;
226    if (tt->hide_timer) return;
227    /* hide slightly faster when in window mode to look less stupid */
228    if ((tt->hide_timeout > 0) && tt->tt_win) extra = 0.1;
229    edje_object_signal_emit(tt->tooltip, "elm,action,hide", "elm");
230    tt->hide_timer = ecore_timer_add
231      (tt->hide_timeout - extra, _elm_tooltip_hide_anim_cb, tt);
232 }
233
234 static void
235 _elm_tooltip_hide_anim_stop(Elm_Tooltip *tt)
236 {
237    if (!tt->hide_timer) return;
238    if (tt->tooltip)
239      edje_object_signal_emit(tt->tooltip, "elm,action,show", "elm");
240    ecore_timer_del(tt->hide_timer);
241    tt->hide_timer = NULL;
242 }
243
244 static void
245 _elm_tooltip_reconfigure(Elm_Tooltip *tt)
246 {
247    Evas_Coord ox, oy, ow, oh, px, py, tx, ty, tw, th, cw = 0, ch = 0;
248    Evas_Coord eminw, eminh, ominw, ominh;
249    double rel_x, rel_y;
250    Eina_Bool inside_eventarea;
251 #ifdef HAVE_ELEMENTARY_X
252    Ecore_X_Window xwin = 0;
253 #endif
254
255    _elm_tooltip_reconfigure_job_stop(tt);
256
257    if (tt->hide_timer) return;
258    if (!tt->tooltip) return;
259    if (tt->changed_style)
260      {
261         const char *style = tt->style ? tt->style : "default";
262         const char *str;
263         if (!_elm_theme_object_set(tt->owner, tt->tooltip, "tooltip", "base", style))
264           {
265              ERR("Could not apply the theme to the tooltip! style=%s", style);
266              if (tt->tt_win) evas_object_del(tt->tt_win);
267              else evas_object_del(tt->tooltip);
268              tt->tt_win = NULL;
269              tt->tt_evas = NULL;
270              tt->tooltip = NULL;
271              return;
272           }
273
274         tt->rel_pos.x = 0;
275         tt->rel_pos.y = 0;
276
277         tt->pad.x = 0;
278         tt->pad.y = 0;
279         tt->pad.bx = 0;
280         tt->pad.by = 0;
281         tt->hide_timeout = 0.0;
282         if (tt->tt_win)
283           {  /* FIXME: hardcoded here is bad */
284              if (!strcmp(style, "transparent"))
285                {
286                   elm_win_alpha_set(tt->tt_win, EINA_TRUE);
287                   elm_win_transparent_set(tt->tt_win, EINA_TRUE);
288                }
289              else
290                {
291                   elm_win_alpha_set(tt->tt_win, EINA_FALSE);
292                   elm_win_transparent_set(tt->tt_win, EINA_FALSE);
293                }
294           }
295
296         str = edje_object_data_get(tt->tooltip, "pad_x");
297         if (str) tt->pad.x = atoi(str);
298         str = edje_object_data_get(tt->tooltip, "pad_y");
299         if (str) tt->pad.y = atoi(str);
300
301         str = edje_object_data_get(tt->tooltip, "pad_border_x");
302         if (str) tt->pad.bx = atoi(str);
303         str = edje_object_data_get(tt->tooltip, "pad_border_y");
304         if (str) tt->pad.by = atoi(str);
305
306         str = edje_object_data_get(tt->tooltip, "hide_timeout");
307         if (str)
308           {
309              tt->hide_timeout = atof(str);
310              if (tt->hide_timeout < 0.0) tt->hide_timeout = 0.0;
311           }
312
313         evas_object_pass_events_set(tt->tooltip, EINA_TRUE);
314         tt->changed_style = EINA_FALSE;
315         if (tt->tooltip)
316           edje_object_part_swallow(tt->tooltip, "elm.swallow.content", 
317                                    tt->content);
318
319         edje_object_signal_emit(tt->tooltip, "elm,action,show", "elm");
320      }
321
322    if (!tt->content)
323      {
324         tt->content = tt->func((void *)tt->data, tt->owner, tt->tt_win ? : tt->owner);
325         if (!tt->content)
326           {
327              WRN("could not create tooltip content!");
328              if (tt->tt_win) evas_object_del(tt->tt_win);
329              else evas_object_del(tt->tooltip);
330
331              tt->tt_win = NULL;
332              tt->tt_evas = NULL;
333              tt->tooltip = NULL;
334              return;
335           }
336         evas_object_show(tt->content);
337         evas_object_layer_set(tt->content, ELM_OBJECT_LAYER_TOOLTIP);
338         evas_object_pass_events_set(tt->content, EINA_TRUE);
339         edje_object_part_swallow
340           (tt->tooltip, "elm.swallow.content", tt->content);
341         evas_object_event_callback_add(tt->content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
342            _elm_tooltip_content_changed_hints_cb, tt);
343         evas_object_event_callback_add(tt->content, EVAS_CALLBACK_DEL,
344            _elm_tooltip_content_del_cb, tt);
345
346      }
347
348    evas_object_size_hint_min_get(tt->content, &ominw, &ominh);
349    edje_object_size_min_get(tt->tooltip, &eminw, &eminh);
350
351    if (ominw < eminw) ominw = eminw;
352    if (ominh < eminh) ominh = eminh;
353
354    if (ominw < 1) ominw = 10; /* at least it is noticeable */
355    if (ominh < 1) ominh = 10; /* at least it is noticeable */
356
357    edje_object_size_min_restricted_calc(tt->tooltip, &tw, &th, ominw, ominh);
358
359 #ifdef HAVE_ELEMENTARY_X
360    if (tt->tt_win)
361      {
362         xwin = elm_win_xwindow_get(elm_object_top_widget_get(tt->owner));
363         if (xwin)
364           {
365              xwin = ecore_x_window_root_get(xwin);
366              ecore_x_randr_screen_current_size_get(xwin, &cw, &ch, NULL, NULL);
367           }
368      }
369    if (!cw)
370 #endif
371      evas_output_size_get(tt->tt_evas ?: tt->evas, &cw, &ch);
372
373    evas_object_geometry_get(tt->eventarea, &ox, &oy, &ow, &oh);
374
375    if (tt->tt_win)
376      {
377         int x, y;
378 #ifdef HAVE_ELEMENTARY_X
379         ecore_x_pointer_xy_get(xwin, &px, &py);
380 #endif
381         elm_win_screen_position_get(elm_object_top_widget_get(tt->owner), &x, &y);
382         ox += x;
383         oy += y;
384      }
385
386    else
387      evas_pointer_canvas_xy_get(tt->evas, &px, &py);
388
389    inside_eventarea = ((px >= ox) && (py >= oy) &&
390                        (px <= ox + ow) && (py <= oy + oh));
391    if (inside_eventarea)
392      {
393         tx = px;
394         ty = py;
395
396         if (tx + tw + tt->pad.x < cw) tx += tt->pad.x;
397         if (ty + th + tt->pad.y < ch) ty += tt->pad.y;
398      }
399    else
400      {
401         tx = ox + (ow / 2) - (tw / 2);
402         if (ch < (th + oy + oh)) ty = oy - th;
403         else ty = oy + oh;
404      }
405
406    if (tx + tw > cw)
407      {
408         if (abs(tx - tw) < (tx + tw) - cw)
409           tx -= tw;
410      }
411    else if ((tx < px) && (px < tx + tw))
412      {
413         if (0 < tx - tw)
414           tx -= tw;
415      }
416    if (ty + th > ch)
417      {
418         if (abs(ty - th) < (ty + th) - ch)
419           ty -= th;
420      }
421    else if ((ty < py) && (py < ty + th))
422      {
423         if (0 < ty - th)
424           ty -= th;
425      }
426
427    if (tt->pad.bx * 2 + tw < cw)
428      {
429         if (tx < tt->pad.bx) tx = tt->pad.bx;
430         else if (tx + tw >= cw - tt->pad.bx) tx = cw - tw - tt->pad.bx;
431      }
432
433    if (tt->pad.by * 2 + th < ch)
434      {
435         if (ty < tt->pad.by) ty = tt->pad.by;
436         else if (ty + th >= ch - tt->pad.by) ty = ch - th - tt->pad.by;
437      }
438
439    evas_object_move(tt->tt_win ? : tt->tooltip, tx, ty);
440    evas_object_resize(tt->tt_win ? : tt->tooltip, tw, th);
441    evas_object_show(tt->tooltip);
442
443    if (inside_eventarea)
444      {
445         rel_x = (px - tx) / (double)tw;
446         rel_y = (py - ty) / (double)th;
447      }
448    else
449      {
450         rel_x = (ox + (ow / 2) - tx) / (double)tw;
451         rel_y = (oy + (oh / 2) - ty) / (double)th;
452      }
453
454 #define FDIF(a, b) (fabs((a) - (b)) > 0.0001)
455    if ((FDIF(rel_x, tt->rel_pos.x)) || (FDIF(rel_y, tt->rel_pos.y)))
456      {
457         Edje_Message_Float_Set *msg;
458
459         msg = alloca(sizeof(Edje_Message_Float_Set) + sizeof(double));
460         msg->count = 2;
461         msg->val[0] = rel_x;
462         msg->val[1] = rel_y;
463         tt->rel_pos.x = rel_x;
464         tt->rel_pos.y = rel_y;
465
466         edje_object_message_send(tt->tooltip, EDJE_MESSAGE_FLOAT_SET, 1, msg);
467      }
468 #undef FDIF
469 }
470
471 static void
472 _elm_tooltip_show_timer_stop(Elm_Tooltip *tt)
473 {
474    if (!tt->show_timer) return;
475    ecore_timer_del(tt->show_timer);
476    tt->show_timer = NULL;
477 }
478
479 static Eina_Bool
480 _elm_tooltip_timer_show_cb(void *data)
481 {
482    Elm_Tooltip *tt = data;
483    tt->show_timer = NULL;
484    _elm_tooltip_show(tt);
485    return ECORE_CALLBACK_CANCEL;
486 }
487
488 static void
489 _elm_tooltip_obj_mouse_in_cb(void *data, Evas *e  __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info  __UNUSED__)
490 {
491    Elm_Tooltip *tt = data;
492
493    _elm_tooltip_hide_anim_stop(tt);
494
495    if ((tt->show_timer) || (tt->tooltip)) return;
496
497    tt->show_timer = ecore_timer_add
498      (_elm_config->tooltip_delay, _elm_tooltip_timer_show_cb, tt);
499 }
500
501 static void
502 _elm_tooltip_obj_mouse_out_cb(Elm_Tooltip *tt, Evas *e  __UNUSED__, Evas_Object *obj __UNUSED__, Evas_Event_Mouse_Out *event __UNUSED__)
503 {
504    if (tt->visible_lock) return;
505
506    if (!tt->tooltip)
507      {
508         _elm_tooltip_show_timer_stop(tt);
509         return;
510      }
511    _elm_tooltip_hide_anim_start(tt);
512 }
513
514 static void _elm_tooltip_obj_free_cb(void *data, Evas *e  __UNUSED__, Evas_Object *obj, void *event_info  __UNUSED__);
515
516 static void
517 _elm_tooltip_unset(Elm_Tooltip *tt)
518 {
519    tt->visible_lock = EINA_FALSE;
520    _elm_tooltip_hide(tt);
521    _elm_tooltip_data_clean(tt);
522
523    if (tt->eventarea)
524      {
525         evas_object_event_callback_del_full
526           (tt->eventarea, EVAS_CALLBACK_MOUSE_IN,
527            _elm_tooltip_obj_mouse_in_cb, tt);
528         evas_object_event_callback_del_full
529           (tt->eventarea, EVAS_CALLBACK_MOUSE_OUT,
530            (Evas_Object_Event_Cb)_elm_tooltip_obj_mouse_out_cb, tt);
531         evas_object_event_callback_del_full
532           (tt->eventarea, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
533
534         evas_object_data_del(tt->eventarea, _tooltip_key);
535      }
536    if (tt->owner)
537      {
538         evas_object_event_callback_del_full
539           (tt->owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
540         elm_widget_tooltip_del(tt->owner, tt);
541      }
542
543    eina_stringshare_del(tt->style);
544    free(tt);
545 }
546
547 static void
548 _elm_tooltip_obj_free_cb(void *data, Evas *e  __UNUSED__, Evas_Object *obj, void *event_info  __UNUSED__)
549 {
550    Elm_Tooltip *tt = data;
551    if (tt->eventarea == obj) tt->eventarea = NULL;
552    if (tt->owner == obj) tt->owner = NULL;
553    _elm_tooltip_unset(tt);
554 }
555
556 static Evas_Object *
557 _elm_tooltip_label_create(void *data, Evas_Object *obj __UNUSED__, Evas_Object *tooltip)
558 {
559    Evas_Object *label = elm_label_add(tooltip);
560    if (!label)
561      return NULL;
562    elm_object_style_set(label, "tooltip");
563    elm_object_text_set(label, data);
564    return label;
565 }
566
567 static void
568 _elm_tooltip_label_del_cb(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
569 {
570    eina_stringshare_del(data);
571 }
572
573 static void
574 _elm_tooltip_data_clean(Elm_Tooltip *tt)
575 {
576    if (tt->del_cb) tt->del_cb((void *)tt->data, tt->owner, NULL);
577
578    _elm_tooltip_content_del(tt);
579
580    tt->data = NULL;
581    tt->del_cb = NULL;
582 }
583
584 /**
585  * Notify tooltip should recalculate its theme.
586  * @internal
587  */
588 void
589 elm_tooltip_theme(Elm_Tooltip *tt)
590 {
591    if (!tt->tooltip) return;
592    tt->changed_style = EINA_TRUE;
593    _elm_tooltip_reconfigure_job_start(tt);
594 }
595
596
597 /**
598  * Set the content to be shown in the tooltip object for specific event area.
599  *
600  * Setup the tooltip to object. The object @a eventarea can have only
601  * one tooltip, so any previous tooltip data is removed. @p func(with
602  * @p data) will be called every time that need show the tooltip and
603  * it should return a valid Evas_Object. This object is then managed
604  * fully by tooltip system and is deleted when the tooltip is gone.
605  *
606  * This is an internal function that is used by objects with sub-items
607  * that want to provide different tooltips for each of them. The @a
608  * owner object should be an elm_widget and will be used to track
609  * theme changes and to feed @a func and @a del_cb. The @a eventarea
610  * may be any object and is the one that should be used later on with
611  * elm_object_tooltip apis, such as elm_object_tooltip_hide(),
612  * elm_object_tooltip_show() or elm_object_tooltip_unset().
613  *
614  * @param eventarea the object being attached a tooltip.
615  * @param owner the elm_widget that owns this object, will be used to
616  *        track theme changes and to be used in @a func or @a del_cb.
617  * @param func the function used to create the tooltip contents. The
618  *        @a Evas_Object parameters will receive @a owner as value.
619  * @param data what to provide to @a func as callback data/context.
620  * @param del_cb called when data is not needed anymore, either when
621  *        another callback replaces @func, the tooltip is unset with
622  *        elm_object_tooltip_unset() or the owner object @a obj
623  *        dies. This callback receives as the first parameter the
624  *        given @a data, and @c event_info is NULL.
625  *
626  * @internal
627  * @ingroup Tooltips
628  */
629 void
630 elm_object_sub_tooltip_content_cb_set(Evas_Object *eventarea, Evas_Object *owner, Elm_Tooltip_Content_Cb func, const void *data, Evas_Smart_Cb del_cb)
631 {
632    Elm_Tooltip *tt = NULL;
633    Eina_Bool just_created;
634
635    EINA_SAFETY_ON_NULL_GOTO(owner, error);
636    EINA_SAFETY_ON_NULL_GOTO(eventarea, error);
637
638    if (!func)
639      {
640         elm_object_tooltip_unset(eventarea);
641         return;
642      }
643
644    tt = evas_object_data_get(eventarea, _tooltip_key);
645    if (tt)
646      {
647         if (tt->owner != owner)
648           {
649              if (tt->owner != eventarea)
650                evas_object_event_callback_del_full
651                  (tt->owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
652
653              elm_widget_tooltip_del(tt->owner, tt);
654
655              if (owner != eventarea)
656                evas_object_event_callback_add
657                  (owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
658
659              elm_widget_tooltip_add(tt->owner, tt);
660           }
661
662         if ((tt->func == func) && (tt->data == data) &&
663             (tt->del_cb == del_cb))
664           return;
665         _elm_tooltip_data_clean(tt);
666         just_created = EINA_FALSE;
667      }
668    else
669      {
670         tt = ELM_NEW(Elm_Tooltip);
671         if (!tt) goto error;
672
673         tt->owner = owner;
674         tt->eventarea = eventarea;
675         tt->evas = evas_object_evas_get(eventarea);
676         evas_object_data_set(eventarea, _tooltip_key, tt);
677
678         just_created = EINA_TRUE;
679
680         evas_object_event_callback_add(eventarea, EVAS_CALLBACK_MOUSE_IN,
681            _elm_tooltip_obj_mouse_in_cb, tt);
682         evas_object_event_callback_add(eventarea, EVAS_CALLBACK_MOUSE_OUT,
683            (Evas_Object_Event_Cb)_elm_tooltip_obj_mouse_out_cb, tt);
684         evas_object_event_callback_add(eventarea, EVAS_CALLBACK_FREE,
685            _elm_tooltip_obj_free_cb, tt);
686
687         if (owner != eventarea)
688           evas_object_event_callback_add
689             (owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
690
691         elm_widget_tooltip_add(tt->owner, tt);
692      }
693
694    tt->func = func;
695    tt->data = data;
696    tt->del_cb = del_cb;
697
698    if (!just_created) _elm_tooltip_reconfigure_job_start(tt);
699    return;
700
701  error:
702    if (del_cb) del_cb((void *)data, owner, NULL);
703 }
704
705 /**
706  * Force show tooltip of object
707  *
708  * @param obj Target object
709  *
710  * Force show the tooltip and disable hide on mouse_out.
711  * If another content is set as tooltip, the visible tooltip will hididen and
712  * showed again with new content.
713  * This can force show more than one tooltip at a time.
714  *
715  * @ingroup Tooltips
716  */
717 EAPI void
718 elm_object_tooltip_show(Evas_Object *obj)
719 {
720    ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
721    tt->visible_lock = EINA_TRUE;
722    _elm_tooltip_show(tt);
723 }
724
725 /**
726  * Force hide tooltip of object
727  *
728  * @param obj Target object
729  *
730  * Force hide the tooltip and (re)enable future mouse interations.
731  *
732  * @ingroup Tooltips
733  */
734 EAPI void
735 elm_object_tooltip_hide(Evas_Object *obj)
736 {
737    ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
738    tt->visible_lock = EINA_FALSE;
739    _elm_tooltip_hide_anim_start(tt);
740 }
741
742 /**
743  * Set the text to be shown in the tooltip object
744  *
745  * @param obj Target object
746  * @param text The text to set in the content
747  *
748  * Setup the text as tooltip to object. The object can have only one tooltip,
749  * so any previous tooltip data is removed.
750  * This method call internaly the elm_tooltip_content_cb_set().
751  *
752  * @ingroup Tooltips
753  */
754 EAPI void
755 elm_object_tooltip_text_set(Evas_Object *obj, const char *text)
756 {
757    EINA_SAFETY_ON_NULL_RETURN(obj);
758    EINA_SAFETY_ON_NULL_RETURN(text);
759
760    text = eina_stringshare_add(text);
761    elm_object_tooltip_content_cb_set
762      (obj, _elm_tooltip_label_create, text, _elm_tooltip_label_del_cb);
763 }
764
765 /**
766  * Set the content to be shown in the tooltip object
767  *
768  * Setup the tooltip to object. The object can have only one tooltip,
769  * so any previous tooltip data is removed. @p func(with @p data) will
770  * be called every time that need show the tooltip and it should
771  * return a valid Evas_Object. This object is then managed fully by
772  * tooltip system and is deleted when the tooltip is gone.
773  *
774  * @param obj the object being attached a tooltip.
775  * @param func the function used to create the tooltip contents.
776  * @param data what to provide to @a func as callback data/context.
777  * @param del_cb called when data is not needed anymore, either when
778  *        another callback replaces @func, the tooltip is unset with
779  *        elm_object_tooltip_unset() or the owner object @a obj
780  *        dies. This callback receives as the first parameter the
781  *        given @a data, and @c event_info is NULL.
782  *
783  * @ingroup Tooltips
784  */
785 EAPI void
786 elm_object_tooltip_content_cb_set(Evas_Object *obj, Elm_Tooltip_Content_Cb func, const void *data, Evas_Smart_Cb del_cb)
787 {
788    elm_object_sub_tooltip_content_cb_set(obj, obj, func, data, del_cb);
789 }
790
791 /**
792  * Unset tooltip from object
793  *
794  * @param obj Target object
795  *
796  * Remove tooltip from object. The callback provided as del_cb to
797  * elm_object_tooltip_content_cb_set() will be called to notify it is
798  * not used anymore.
799  *
800  * @see elm_object_tooltip_content_cb_set()
801  *
802  * @ingroup Tooltips
803  */
804 EAPI void
805 elm_object_tooltip_unset(Evas_Object *obj)
806 {
807    ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
808    _elm_tooltip_unset(tt);
809 }
810
811 /**
812  * Sets a different style for this object tooltip.
813  *
814  * @note before you set a style you should define a tooltip with
815  *       elm_object_tooltip_content_cb_set() or
816  *       elm_object_tooltip_text_set().
817  *
818  * @param obj an object with tooltip already set.
819  * @param style the theme style to use (default, transparent, ...)
820  */
821 EAPI void
822 elm_object_tooltip_style_set(Evas_Object *obj, const char *style)
823 {
824    ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
825    if (!eina_stringshare_replace(&tt->style, style)) return;
826    elm_tooltip_theme(tt);
827 }
828
829 /**
830  * Get the style for this object tooltip.
831  *
832  * @param obj an object with tooltip already set.
833  * @return style the theme style in use, defaults to "default". If the
834  *         object does not have a tooltip set, then NULL is returned.
835  */
836 EAPI const char *
837 elm_object_tooltip_style_get(const Evas_Object *obj)
838 {
839    ELM_TOOLTIP_GET_OR_RETURN(tt, obj, NULL);
840    return tt->style ? tt->style : "default";
841 }
842
843 /**
844  * Get the configured tooltip delay
845  *
846  * This gets the globally configured tooltip delay in seconds
847  *
848  * @return The tooltip delay
849  * @ingroup Tooltips
850  */
851 EAPI double
852 elm_tooltip_delay_get(void)
853 {
854    return _elm_config->tooltip_delay;
855 }
856
857 /**
858  * Set the configured tooltip delay
859  *
860  * This sets the globally configured delay to tooltip
861  *
862  * @param delay The delay to show the tooltip
863  * @return EINA_TRUE if value is valid and setted
864  * @ingroup Tooltips
865  */
866 EAPI Eina_Bool
867 elm_tooltip_delay_set(double delay)
868 {
869    if (delay < 0.0) return EINA_FALSE;
870    _elm_config->tooltip_delay = delay;
871    return EINA_TRUE;
872 }
873
874 /**
875  * @brief Disable size restrictions on an object's tooltip
876  * @param obj The tooltip's anchor object
877  * @param disable If EINA_TRUE, size restrictions are disabled
878  * @return EINA_FALSE on failure, EINA_TRUE on success
879  *
880  * This function allows a tooltip to expand beyond its parant window's canvas.
881  * It will instead be limited only by the size of the display.
882  */
883 EAPI Eina_Bool
884 elm_tooltip_size_restrict_disable(Evas_Object *obj, Eina_Bool disable)
885 {
886    ELM_TOOLTIP_GET_OR_RETURN(tt, obj, EINA_FALSE);
887    return tt->free_size = disable;
888 }
889
890 /**
891  * @brief Retrieve size restriction state of an object's tooltip
892  * @param obj The tooltip's anchor object
893  * @return If EINA_TRUE, size restrictions are disabled
894  *
895  * This function returns whether a tooltip is allowed to expand beyond
896  * its parant window's canvas.
897  * It will instead be limited only by the size of the display.
898  */
899 EAPI Eina_Bool
900 elm_tooltip_size_restrict_disabled_get(const Evas_Object *obj)
901 {
902    ELM_TOOLTIP_GET_OR_RETURN(tt, obj, EINA_FALSE);
903    return tt->free_size;
904 }