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