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