aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/elementary/src/lib/els_tooltip.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/elementary/src/lib/els_tooltip.c')
-rw-r--r--libraries/elementary/src/lib/els_tooltip.c951
1 files changed, 951 insertions, 0 deletions
diff --git a/libraries/elementary/src/lib/els_tooltip.c b/libraries/elementary/src/lib/els_tooltip.c
new file mode 100644
index 0000000..f1a451f
--- /dev/null
+++ b/libraries/elementary/src/lib/els_tooltip.c
@@ -0,0 +1,951 @@
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
12static 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
32struct _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
60static void _elm_tooltip_reconfigure(Elm_Tooltip *tt);
61static void _elm_tooltip_reconfigure_job_start(Elm_Tooltip *tt);
62static void _elm_tooltip_reconfigure_job_stop(Elm_Tooltip *tt);
63static void _elm_tooltip_hide_anim_start(Elm_Tooltip *tt);
64static void _elm_tooltip_hide_anim_stop(Elm_Tooltip *tt);
65static void _elm_tooltip_show_timer_stop(Elm_Tooltip *tt);
66static void _elm_tooltip_hide(Elm_Tooltip *tt);
67static void _elm_tooltip_data_clean(Elm_Tooltip *tt);
68
69static 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
76static 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
85static 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
93static 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
101static 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
119static 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
163static 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
181static 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
210static 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
218static 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
226static 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
233static 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
242static 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
255static 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
265static 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 = 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
509static 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
517static 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
526static 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
539static 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
553static void _elm_tooltip_obj_free_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__);
554
555static 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
586static 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
595static 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
606static 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
618static 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
624static 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
633static 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 */
648void
649elm_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 */
689void
690elm_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 */
777EAPI void
778elm_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 */
794EAPI void
795elm_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 */
814EAPI void
815elm_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 */
827EAPI void
828elm_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 */
863EAPI void
864elm_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 */
882EAPI void
883elm_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 */
899EAPI void
900elm_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 */
914EAPI const char *
915elm_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 */
930EAPI Eina_Bool
931elm_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 */
946EAPI Eina_Bool
947elm_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}