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