Initialize Tizen 2.3
[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_TOOLTIP);
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 = 0, py = 0, 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,
279                                   "tooltip", "base", style))
280           {
281              ERR("Could not apply the theme to the tooltip! style=%s", style);
282              if (tt->tt_win) evas_object_del(tt->tt_win);
283              else evas_object_del(tt->tooltip);
284              tt->tt_win = NULL;
285              tt->tt_evas = NULL;
286              tt->tooltip = NULL;
287              return;
288           }
289
290         tt->rel_pos.x = 0;
291         tt->rel_pos.y = 0;
292
293         tt->pad.x = 0;
294         tt->pad.y = 0;
295         tt->pad.bx = 0;
296         tt->pad.by = 0;
297         tt->hide_timeout = 0.0;
298
299         str = edje_object_data_get(tt->tooltip, "transparent");
300         if (tt->tt_win)
301           {  /* FIXME: hardcoded here is bad */
302              if (str && (!strcmp(str, "enabled")))
303                {
304                   evas_object_hide(tt->tt_win);
305                   elm_win_alpha_set(tt->tt_win, EINA_TRUE);
306                }
307              else
308                {
309                   evas_object_hide(tt->tt_win);
310                   elm_win_alpha_set(tt->tt_win, EINA_FALSE);
311                }
312 #ifdef HAVE_ELEMENTARY_X
313              Ecore_X_Window win;
314              win = elm_win_xwindow_get(tt->tt_win);
315              if (win)
316                ecore_x_window_shape_input_rectangle_set(win, 0, 0, 0, 0);
317 #endif
318              evas_object_show(tt->tt_win);
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         if (xwin)
401           ecore_x_pointer_xy_get(xwin, &px, &py);
402 #endif
403         elm_win_screen_position_get(win, &x, &y);
404         ox += x;
405         if (px) px += x;
406         oy += y;
407         if (py) py += y;
408      }
409    else
410      evas_pointer_canvas_xy_get(tt->evas, &px, &py);
411    TTDBG("POINTER:  px=%d,py=%d\n", px, py);
412    inside_eventarea = ((px >= ox) && (py >= oy) &&
413                        (px <= ox + ow) && (py <= oy + oh));
414    if (inside_eventarea)
415      {
416         /* try to position bottom right corner at pointer */
417         tx = px - tw;
418         ty = py - th;
419         TTDBG("INIT (EVENTAREA)\n");
420      }
421    else
422      {
423         /* try centered on middle of eventarea */
424         tx = ox + (ow / 2) - (tw / 2);
425         if (0 > (th - oy - oh)) ty = oy + th;
426         else ty = oy - oh;
427         TTDBG("INIT (INTERPRETED)\n");
428      }
429    TTDBG("ADJUST (POINTER):  tx=%d,ty=%d\n", tx, ty);
430    if (tx < 0)
431      {
432         /* if we're offscreen, try to flip over the Y axis */
433         if (abs((tx + 2 * tw) - cw) < abs(tx))
434           tx += tw;
435      }
436    else if ((tx > px) && (px > tw))
437      {
438         if (tx + tw < cw)
439           tx += tw;
440      }
441    if (ty < 0)
442      {
443         /* if we're offscreen, try to flip over the X axis */
444         if (abs((ty + 2 * th) - ch) < abs(ty))
445           ty += th;
446      }
447    else if ((ty > py) && (py > th))
448      {
449         if (ty + th < ch)
450           ty += th;
451      }
452    TTDBG("ADJUST (FLIP):  tx=%d,ty=%d\n", tx, ty);
453    if (inside_eventarea)
454      {
455         if ((tx == px) && ((tx + tw + tt->pad.x < cw) || (tx + tw > cw))) tx += tt->pad.x;
456         else if ((tx - tt->pad.x > 0) || (tx < 0)) tx -= tt->pad.x;
457         if ((ty == py) && ((ty + th + tt->pad.y < ch) || (ty + th > ch))) ty += tt->pad.y;
458         else if ((ty - tt->pad.y > 0) || (ty < 0)) ty -= tt->pad.y;
459      }
460    TTDBG("PAD:  tx=%d,ty=%d\n", tx, ty);
461    if (tt->pad.bx * 2 + tw < cw)
462      {
463         if (tx < tt->pad.bx) tx = tt->pad.bx;
464         else if ((tx >= tw) && (tx + tt->pad.bx <= cw)) tx += tt->pad.bx;
465         else if (tx - tt->pad.bx >= 0) tx -= tt->pad.bx;
466      }
467    else if (tx < 0) tx -= tt->pad.bx;
468    else if (tx > cw) tx += tt->pad.bx;
469    if (tt->pad.by * 2 + th < ch)
470      {
471         if (ty < tt->pad.by) ty = tt->pad.by;
472         else if ((ty >= th) && (ty + tt->pad.by <= ch)) ty += tt->pad.by;
473         else if (ty - tt->pad.by >= 0) ty -= tt->pad.by;
474      }
475    else if (ty < 0) ty -= tt->pad.by;
476    else if (ty > ch) ty += tt->pad.by;
477    TTDBG("PAD (BORDER):  tx=%d,ty=%d\n", tx, ty);
478    if (((tx < 0) && (tw < cw)) || ((ty < 0) && (th < ch)))
479      {
480         TTDBG("POSITIONING FAILED! THIS IS A BUG SOMEWHERE!\n");
481         abort();
482         return;
483      }
484    evas_object_move(tt->tt_win ? : tt->tooltip, tx, ty);
485    evas_object_resize(tt->tt_win ? : tt->tooltip, tw, th);
486    TTDBG("FINAL: tx=%d,ty=%d,tw=%d,th=%d\n", tx, ty, tw, th);
487    evas_object_show(tt->tooltip);
488
489    if (inside_eventarea)
490      {
491         rel_x = (px - tx) / (double)tw;
492         rel_y = (py - ty) / (double)th;
493      }
494    else
495      {
496         rel_x = (ox + (ow / 2) - tx) / (double)tw;
497         rel_y = (oy + (oh / 2) - ty) / (double)th;
498      }
499
500 #define FDIF(a, b) (fabs((a) - (b)) > 0.0001)
501    if ((FDIF(rel_x, tt->rel_pos.x)) || (FDIF(rel_y, tt->rel_pos.y)))
502      {
503         Edje_Message_Float_Set *msg;
504
505         msg = alloca(sizeof(Edje_Message_Float_Set) + sizeof(double));
506         msg->count = 2;
507         msg->val[0] = rel_x;
508         msg->val[1] = rel_y;
509         tt->rel_pos.x = rel_x;
510         tt->rel_pos.y = rel_y;
511
512         edje_object_message_send(tt->tooltip, EDJE_MESSAGE_FLOAT_SET, 1, msg);
513      }
514 #undef FDIF
515 }
516
517 static void
518 _elm_tooltip_show_timer_stop(Elm_Tooltip *tt)
519 {
520    if (!tt->show_timer) return;
521    ecore_timer_del(tt->show_timer);
522    tt->show_timer = NULL;
523 }
524
525 static Eina_Bool
526 _elm_tooltip_timer_show_cb(void *data)
527 {
528    Elm_Tooltip *tt = data;
529    tt->show_timer = NULL;
530    _elm_tooltip_show(tt);
531    return ECORE_CALLBACK_CANCEL;
532 }
533
534 static void
535 _elm_tooltip_obj_mouse_in_cb(void *data, Evas *e  __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info  __UNUSED__)
536 {
537    Elm_Tooltip *tt = data;
538
539    _elm_tooltip_hide_anim_stop(tt);
540
541    if ((tt->show_timer) || (tt->tooltip)) return;
542
543    tt->show_timer = ecore_timer_add(_elm_config->tooltip_delay, _elm_tooltip_timer_show_cb, tt);
544    TTDBG("MOUSE IN\n");
545 }
546
547 static void
548 _elm_tooltip_obj_mouse_out_cb(Elm_Tooltip *tt, Evas *e  __UNUSED__, Evas_Object *obj __UNUSED__, Evas_Event_Mouse_Out *event __UNUSED__)
549 {
550    if (tt->visible_lock) return;
551
552    if (!tt->tooltip)
553      {
554         _elm_tooltip_show_timer_stop(tt);
555         return;
556      }
557    _elm_tooltip_hide_anim_start(tt);
558    TTDBG("MOUSE OUT\n");
559 }
560
561 static void _elm_tooltip_obj_free_cb(void *data, Evas *e  __UNUSED__, Evas_Object *obj, void *event_info  __UNUSED__);
562
563 static void
564 _elm_tooltip_unset(Elm_Tooltip *tt)
565 {
566    tt->visible_lock = EINA_FALSE;
567    _elm_tooltip_hide(tt);
568    _elm_tooltip_data_clean(tt);
569
570    if (tt->eventarea)
571      {
572         evas_object_event_callback_del_full
573           (tt->eventarea, EVAS_CALLBACK_MOUSE_IN,
574            _elm_tooltip_obj_mouse_in_cb, tt);
575         evas_object_event_callback_del_full
576           (tt->eventarea, EVAS_CALLBACK_MOUSE_OUT,
577            (Evas_Object_Event_Cb)_elm_tooltip_obj_mouse_out_cb, tt);
578         evas_object_event_callback_del_full
579           (tt->eventarea, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
580
581         evas_object_data_del(tt->eventarea, _tooltip_key);
582      }
583    if (tt->owner)
584      {
585         evas_object_event_callback_del_full
586           (tt->owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
587         elm_widget_tooltip_del(tt->owner, tt);
588      }
589
590    eina_stringshare_del(tt->style);
591    free(tt);
592 }
593
594 static void
595 _elm_tooltip_obj_free_cb(void *data, Evas *e  __UNUSED__, Evas_Object *obj, void *event_info  __UNUSED__)
596 {
597    Elm_Tooltip *tt = data;
598    if (tt->eventarea == obj) tt->eventarea = NULL;
599    if (tt->owner == obj) tt->owner = NULL;
600    _elm_tooltip_unset(tt);
601 }
602
603 static Evas_Object *
604 _elm_tooltip_label_create(void *data, Evas_Object *obj __UNUSED__, Evas_Object *tooltip)
605 {
606    Evas_Object *label = elm_label_add(tooltip);
607    if (!label)
608      return NULL;
609    elm_object_style_set(label, "tooltip");
610    elm_object_text_set(label, data);
611    return label;
612 }
613
614 static Evas_Object *
615 _elm_tooltip_trans_label_create(void *data, Evas_Object *obj __UNUSED__, Evas_Object *tooltip)
616 {
617    Evas_Object *label = elm_label_add(tooltip);
618    const char **text = data;
619    if (!label)
620      return NULL;
621    elm_object_style_set(label, "tooltip");
622    elm_object_domain_translatable_text_set(label, text[0], text[1]);
623    return label;
624 }
625
626 static void
627 _elm_tooltip_label_del_cb(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
628 {
629    eina_stringshare_del(data);
630 }
631
632 static void
633 _elm_tooltip_trans_label_del_cb(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
634 {
635    const char **text = data;
636    eina_stringshare_del(text[0]);
637    eina_stringshare_del(text[1]);
638    free(text);
639 }
640
641 static void
642 _elm_tooltip_data_clean(Elm_Tooltip *tt)
643 {
644    if (tt->del_cb) tt->del_cb((void *)tt->data, tt->owner, NULL);
645
646    _elm_tooltip_content_del(tt);
647
648    tt->data = NULL;
649    tt->del_cb = NULL;
650 }
651
652 /**
653  * Notify tooltip should recalculate its theme.
654  * @internal
655  */
656 void
657 elm_tooltip_theme(Elm_Tooltip *tt)
658 {
659    if (!tt->tooltip) return;
660    tt->changed_style = EINA_TRUE;
661    _elm_tooltip_reconfigure_job_start(tt);
662 }
663
664
665 /**
666  * Set the content to be shown in the tooltip object for specific event area.
667  *
668  * Setup the tooltip to object. The object @a eventarea can have only
669  * one tooltip, so any previous tooltip data is removed. @p func(with
670  * @p data) will be called every time that need show the tooltip and
671  * it should return a valid Evas_Object. This object is then managed
672  * fully by tooltip system and is deleted when the tooltip is gone.
673  *
674  * This is an internal function that is used by objects with sub-items
675  * that want to provide different tooltips for each of them. The @a
676  * owner object should be an elm_widget and will be used to track
677  * theme changes and to feed @a func and @a del_cb. The @a eventarea
678  * may be any object and is the one that should be used later on with
679  * elm_object_tooltip apis, such as elm_object_tooltip_hide(),
680  * elm_object_tooltip_show() or elm_object_tooltip_unset().
681  *
682  * @param eventarea the object being attached a tooltip.
683  * @param owner the elm_widget that owns this object, will be used to
684  *        track theme changes and to be used in @a func or @a del_cb.
685  * @param func the function used to create the tooltip contents. The
686  *        @a Evas_Object parameters will receive @a owner as value.
687  * @param data what to provide to @a func as callback data/context.
688  * @param del_cb called when data is not needed anymore, either when
689  *        another callback replaces @p func, the tooltip is unset with
690  *        elm_object_tooltip_unset() or the owner object @a obj
691  *        dies. This callback receives as the first parameter the
692  *        given @a data, and @c event_info is NULL.
693  *
694  * @internal
695  * @ingroup Tooltips
696  */
697 void
698 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)
699 {
700    Elm_Tooltip *tt = NULL;
701    Eina_Bool just_created;
702
703    EINA_SAFETY_ON_NULL_GOTO(owner, error);
704    EINA_SAFETY_ON_NULL_GOTO(eventarea, error);
705
706    if (!func)
707      {
708         elm_object_tooltip_unset(eventarea);
709         return;
710      }
711
712    tt = evas_object_data_get(eventarea, _tooltip_key);
713    if (tt)
714      {
715         if (tt->owner != owner)
716           {
717              if (tt->owner != eventarea)
718                evas_object_event_callback_del_full
719                  (tt->owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
720
721              elm_widget_tooltip_del(tt->owner, tt);
722
723              if (owner != eventarea)
724                evas_object_event_callback_add
725                  (owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
726
727              elm_widget_tooltip_add(tt->owner, tt);
728           }
729
730         if ((tt->func == func) && (tt->data == data) &&
731             (tt->del_cb == del_cb))
732           return;
733         _elm_tooltip_data_clean(tt);
734         just_created = EINA_FALSE;
735      }
736    else
737      {
738         tt = ELM_NEW(Elm_Tooltip);
739         if (!tt) goto error;
740
741         tt->owner = owner;
742         tt->eventarea = eventarea;
743         tt->evas = evas_object_evas_get(eventarea);
744         evas_object_data_set(eventarea, _tooltip_key, tt);
745
746         just_created = EINA_TRUE;
747
748         evas_object_event_callback_add(eventarea, EVAS_CALLBACK_MOUSE_IN,
749            _elm_tooltip_obj_mouse_in_cb, tt);
750         evas_object_event_callback_add(eventarea, EVAS_CALLBACK_MOUSE_OUT,
751            (Evas_Object_Event_Cb)_elm_tooltip_obj_mouse_out_cb, tt);
752         evas_object_event_callback_add(eventarea, EVAS_CALLBACK_FREE,
753            _elm_tooltip_obj_free_cb, tt);
754
755         if (owner != eventarea)
756           evas_object_event_callback_add
757             (owner, EVAS_CALLBACK_FREE, _elm_tooltip_obj_free_cb, tt);
758
759         elm_widget_tooltip_add(tt->owner, tt);
760      }
761
762    tt->func = func;
763    tt->data = data;
764    tt->del_cb = del_cb;
765
766    if (!just_created) _elm_tooltip_reconfigure_job_start(tt);
767    return;
768
769  error:
770    if (del_cb) del_cb((void *)data, owner, NULL);
771 }
772
773 /**
774  * Force show tooltip of object
775  *
776  * @param obj Target object
777  *
778  * Force show the tooltip and disable hide on mouse_out.
779  * If another content is set as tooltip, the visible tooltip will hididen and
780  * showed again with new content.
781  * This can force show more than one tooltip at a time.
782  *
783  * @ingroup Tooltips
784  */
785 EAPI void
786 elm_object_tooltip_show(Evas_Object *obj)
787 {
788    ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
789    tt->visible_lock = EINA_TRUE;
790    _elm_tooltip_show(tt);
791 }
792
793 /**
794  * Force hide tooltip of object
795  *
796  * @param obj Target object
797  *
798  * Force hide the tooltip and (re)enable future mouse interations.
799  *
800  * @ingroup Tooltips
801  */
802 EAPI void
803 elm_object_tooltip_hide(Evas_Object *obj)
804 {
805    ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
806    tt->visible_lock = EINA_FALSE;
807    _elm_tooltip_hide_anim_start(tt);
808 }
809
810 /**
811  * Set the text to be shown in the tooltip object
812  *
813  * @param obj Target object
814  * @param text The text to set in the content
815  *
816  * Setup the text as tooltip to object. The object can have only one tooltip,
817  * so any previous tooltip data is removed.
818  * This method call internaly the elm_tooltip_content_cb_set().
819  *
820  * @ingroup Tooltips
821  */
822 EAPI void
823 elm_object_tooltip_text_set(Evas_Object *obj, const char *text)
824 {
825    EINA_SAFETY_ON_NULL_RETURN(obj);
826    EINA_SAFETY_ON_NULL_RETURN(text);
827
828    text = eina_stringshare_add(text);
829    elm_object_tooltip_content_cb_set
830      (obj, _elm_tooltip_label_create, text, _elm_tooltip_label_del_cb);
831 }
832
833 /**
834  */
835 EAPI void
836 elm_object_tooltip_domain_translatable_text_set(Evas_Object *obj, const char *domain, const char *text)
837 {
838    const char **data;
839    EINA_SAFETY_ON_NULL_RETURN(obj);
840    EINA_SAFETY_ON_NULL_RETURN(text);
841
842    data = malloc(2 * sizeof(char *));
843    if (!data) return;
844    data[0] = eina_stringshare_add(domain);
845    data[1] = eina_stringshare_add(text);
846    elm_object_tooltip_content_cb_set
847      (obj, _elm_tooltip_trans_label_create, data,
848       _elm_tooltip_trans_label_del_cb);
849 }
850
851 /**
852  * Set the content to be shown in the tooltip object
853  *
854  * Setup the tooltip to object. The object can have only one tooltip,
855  * so any previous tooltip data is removed. @p func(with @p data) will
856  * be called every time that need show the tooltip and it should
857  * return a valid Evas_Object. This object is then managed fully by
858  * tooltip system and is deleted when the tooltip is gone.
859  *
860  * @param obj the object being attached a tooltip.
861  * @param func the function used to create the tooltip contents.
862  * @param data what to provide to @a func as callback data/context.
863  * @param del_cb called when data is not needed anymore, either when
864  *        another callback replaces @p func, the tooltip is unset with
865  *        elm_object_tooltip_unset() or the owner object @a obj
866  *        dies. This callback receives as the first parameter the
867  *        given @a data, and @c event_info is NULL.
868  *
869  * @ingroup Tooltips
870  */
871 EAPI void
872 elm_object_tooltip_content_cb_set(Evas_Object *obj, Elm_Tooltip_Content_Cb func, const void *data, Evas_Smart_Cb del_cb)
873 {
874    elm_object_sub_tooltip_content_cb_set(obj, obj, func, data, del_cb);
875 }
876
877 /**
878  * Unset tooltip from object
879  *
880  * @param obj Target object
881  *
882  * Remove tooltip from object. The callback provided as del_cb to
883  * elm_object_tooltip_content_cb_set() will be called to notify it is
884  * not used anymore.
885  *
886  * @see elm_object_tooltip_content_cb_set()
887  *
888  * @ingroup Tooltips
889  */
890 EAPI void
891 elm_object_tooltip_unset(Evas_Object *obj)
892 {
893    ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
894    _elm_tooltip_unset(tt);
895 }
896
897 /**
898  * Sets a different style for this object tooltip.
899  *
900  * @note before you set a style you should define a tooltip with
901  *       elm_object_tooltip_content_cb_set() or
902  *       elm_object_tooltip_text_set().
903  *
904  * @param obj an object with tooltip already set.
905  * @param style the theme style to use (default, transparent, ...)
906  */
907 EAPI void
908 elm_object_tooltip_style_set(Evas_Object *obj, const char *style)
909 {
910    ELM_TOOLTIP_GET_OR_RETURN(tt, obj);
911    if (!eina_stringshare_replace(&tt->style, style)) return;
912    elm_tooltip_theme(tt);
913 }
914
915 /**
916  * Get the style for this object tooltip.
917  *
918  * @param obj an object with tooltip already set.
919  * @return style the theme style in use, defaults to "default". If the
920  *         object does not have a tooltip set, then NULL is returned.
921  */
922 EAPI const char *
923 elm_object_tooltip_style_get(const Evas_Object *obj)
924 {
925    ELM_TOOLTIP_GET_OR_RETURN(tt, obj, NULL);
926    return tt->style ? tt->style : "default";
927 }
928
929 /**
930  * @brief Disable size restrictions on an object's tooltip
931  * @param obj The tooltip's anchor object
932  * @param disable If EINA_TRUE, size restrictions are disabled
933  * @return EINA_FALSE on failure, EINA_TRUE on success
934  *
935  * This function allows a tooltip to expand beyond its parent window's canvas.
936  * It will instead be limited only by the size of the display.
937  */
938 EAPI Eina_Bool
939 elm_object_tooltip_window_mode_set(Evas_Object *obj, Eina_Bool disable)
940 {
941    ELM_TOOLTIP_GET_OR_RETURN(tt, obj, EINA_FALSE);
942    return tt->free_size = disable;
943 }
944
945 /**
946  * @brief Retrieve size restriction state of an object's tooltip
947  * @param obj The tooltip's anchor object
948  * @return If EINA_TRUE, size restrictions are disabled
949  *
950  * This function returns whether a tooltip is allowed to expand beyond
951  * its parent window's canvas.
952  * It will instead be limited only by the size of the display.
953  */
954 EAPI Eina_Bool
955 elm_object_tooltip_window_mode_get(const Evas_Object *obj)
956 {
957    ELM_TOOLTIP_GET_OR_RETURN(tt, obj, EINA_FALSE);
958    return tt->free_size;
959 }