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