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