1 /* gtktooltip.c
2 *
3 * Copyright (C) 2006-2007 Imendio AB
4 * Contact: Kristian Rietveld <kris@imendio.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21
22 #include "gtktooltip.h"
23 #include "gtktooltipprivate.h"
24
25 #include <math.h>
26 #include <string.h>
27
28 #include "gtkintl.h"
29 #include "gtkwindow.h"
30 #include "gtkmain.h"
31 #include "gtklabel.h"
32 #include "gtkimage.h"
33 #include "gtkbox.h"
34 #include "gtksettings.h"
35 #include "gtksizerequest.h"
36 #include "gtkstylecontext.h"
37 #include "gtktooltipwindowprivate.h"
38 #include "gtkwindowprivate.h"
39 #include "gtkwidgetprivate.h"
40 #include "gtkaccessible.h"
41
42 #ifdef GDK_WINDOWING_WAYLAND
43 #include "wayland/gdkwayland.h"
44 #endif
45
46
47 /**
48 * SECTION:gtktooltip
49 * @Short_description: Add tips to your widgets
50 * @Title: GtkTooltip
51 *
52 * Basic tooltips can be realized simply by using gtk_widget_set_tooltip_text()
53 * or gtk_widget_set_tooltip_markup() without any explicit tooltip object.
54 *
55 * When you need a tooltip with a little more fancy contents, like adding an
56 * image, or you want the tooltip to have different contents per #GtkTreeView
57 * row or cell, you will have to do a little more work:
58 *
59 * - Set the #GtkWidget:has-tooltip property to %TRUE, this will make GTK+
60 * monitor the widget for motion and related events which are needed to
61 * determine when and where to show a tooltip.
62 *
63 * - Connect to the #GtkWidget::query-tooltip signal. This signal will be
64 * emitted when a tooltip is supposed to be shown. One of the arguments passed
65 * to the signal handler is a GtkTooltip object. This is the object that we
66 * are about to display as a tooltip, and can be manipulated in your callback
67 * using functions like gtk_tooltip_set_icon(). There are functions for setting
68 * the tooltip’s markup, setting an image from a named icon, or even putting in
69 * a custom widget.
70 *
71 * Return %TRUE from your query-tooltip handler. This causes the tooltip to be
72 * show. If you return %FALSE, it will not be shown.
73 *
74 * In the probably rare case where you want to have even more control over the
75 * tooltip that is about to be shown, you can set your own #GtkWindow which
76 * will be used as tooltip window. This works as follows:
77 *
78 * - Set #GtkWidget:has-tooltip and connect to #GtkWidget::query-tooltip as before.
79 * Use gtk_widget_set_tooltip_window() to set a #GtkWindow created by you as
80 * tooltip window.
81 *
82 * - In the #GtkWidget::query-tooltip callback you can access your window using
83 * gtk_widget_get_tooltip_window() and manipulate as you wish. The semantics of
84 * the return value are exactly as before, return %TRUE to show the window,
85 * %FALSE to not show it.
86 */
87
88
89 #define HOVER_TIMEOUT 500
90 #define BROWSE_TIMEOUT 60
91 #define BROWSE_DISABLE_TIMEOUT 500
92
93 #define GTK_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TOOLTIP, GtkTooltipClass))
94 #define GTK_IS_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TOOLTIP))
95 #define GTK_TOOLTIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TOOLTIP, GtkTooltipClass))
96
97 typedef struct _GtkTooltipClass GtkTooltipClass;
98
99 struct _GtkTooltip
100 {
101 GObject parent_instance;
102
103 GtkWidget *window;
104 GtkWidget *box;
105 GtkWidget *image;
106 GtkWidget *label;
107 GtkWidget *custom_widget;
108
109 GtkWindow *current_window;
110 GtkWidget *keyboard_widget;
111
112 GtkWidget *tooltip_widget;
113
114 GdkWindow *last_window;
115
116 guint timeout_id;
117 guint browse_mode_timeout_id;
118
119 GdkRectangle tip_area;
120
121 guint browse_mode_enabled : 1;
122 guint keyboard_mode_enabled : 1;
123 guint tip_area_set : 1;
124 guint custom_was_reset : 1;
125 };
126
127 struct _GtkTooltipClass
128 {
129 GObjectClass parent_class;
130 };
131
132 #define GTK_TOOLTIP_VISIBLE(tooltip) ((tooltip)->current_window && gtk_widget_get_visible (GTK_WIDGET((tooltip)->current_window)))
133
134
135 static void gtk_tooltip_class_init (GtkTooltipClass *klass);
136 static void gtk_tooltip_init (GtkTooltip *tooltip);
137 static void gtk_tooltip_dispose (GObject *object);
138
139 static void gtk_tooltip_window_hide (GtkWidget *widget,
140 gpointer user_data);
141 static void gtk_tooltip_display_closed (GdkDisplay *display,
142 gboolean was_error,
143 GtkTooltip *tooltip);
144 static void gtk_tooltip_set_last_window (GtkTooltip *tooltip,
145 GdkWindow *window);
146
147 static void gtk_tooltip_handle_event_internal (GdkEvent *event);
148
tooltip_quark(void)149 static inline GQuark tooltip_quark (void)
150 {
151 static GQuark quark;
152
153 if G_UNLIKELY (quark == 0)
154 quark = g_quark_from_static_string ("gdk-display-current-tooltip");
155 return quark;
156 }
157
158 #define quark_current_tooltip tooltip_quark()
159
160 G_DEFINE_TYPE (GtkTooltip, gtk_tooltip, G_TYPE_OBJECT);
161
162 static void
gtk_tooltip_class_init(GtkTooltipClass * klass)163 gtk_tooltip_class_init (GtkTooltipClass *klass)
164 {
165 GObjectClass *object_class;
166
167 object_class = G_OBJECT_CLASS (klass);
168
169 object_class->dispose = gtk_tooltip_dispose;
170 }
171
172 static void
gtk_tooltip_init(GtkTooltip * tooltip)173 gtk_tooltip_init (GtkTooltip *tooltip)
174 {
175 tooltip->timeout_id = 0;
176 tooltip->browse_mode_timeout_id = 0;
177
178 tooltip->browse_mode_enabled = FALSE;
179 tooltip->keyboard_mode_enabled = FALSE;
180
181 tooltip->current_window = NULL;
182 tooltip->keyboard_widget = NULL;
183
184 tooltip->tooltip_widget = NULL;
185
186 tooltip->last_window = NULL;
187
188 tooltip->window = gtk_tooltip_window_new ();
189 g_signal_connect (tooltip->window, "hide",
190 G_CALLBACK (gtk_tooltip_window_hide),
191 tooltip);
192 }
193
194 static void
gtk_tooltip_dispose(GObject * object)195 gtk_tooltip_dispose (GObject *object)
196 {
197 GtkTooltip *tooltip = GTK_TOOLTIP (object);
198
199 if (tooltip->timeout_id)
200 {
201 g_source_remove (tooltip->timeout_id);
202 tooltip->timeout_id = 0;
203 }
204
205 if (tooltip->browse_mode_timeout_id)
206 {
207 g_source_remove (tooltip->browse_mode_timeout_id);
208 tooltip->browse_mode_timeout_id = 0;
209 }
210
211 gtk_tooltip_set_custom (tooltip, NULL);
212 gtk_tooltip_set_last_window (tooltip, NULL);
213
214 if (tooltip->window)
215 {
216 GdkDisplay *display;
217
218 display = gtk_widget_get_display (tooltip->window);
219 g_signal_handlers_disconnect_by_func (display,
220 gtk_tooltip_display_closed,
221 tooltip);
222 gtk_widget_destroy (tooltip->window);
223 tooltip->window = NULL;
224 }
225
226 G_OBJECT_CLASS (gtk_tooltip_parent_class)->dispose (object);
227 }
228
229 /* public API */
230
231 /**
232 * gtk_tooltip_set_markup:
233 * @tooltip: a #GtkTooltip
234 * @markup: (allow-none): a markup string (see [Pango markup format][PangoMarkupFormat]) or %NULL
235 *
236 * Sets the text of the tooltip to be @markup, which is marked up
237 * with the [Pango text markup language][PangoMarkupFormat].
238 * If @markup is %NULL, the label will be hidden.
239 *
240 * Since: 2.12
241 */
242 void
gtk_tooltip_set_markup(GtkTooltip * tooltip,const gchar * markup)243 gtk_tooltip_set_markup (GtkTooltip *tooltip,
244 const gchar *markup)
245 {
246 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
247
248 gtk_tooltip_window_set_label_markup (GTK_TOOLTIP_WINDOW (tooltip->window), markup);
249 }
250
251 /**
252 * gtk_tooltip_set_text:
253 * @tooltip: a #GtkTooltip
254 * @text: (allow-none): a text string or %NULL
255 *
256 * Sets the text of the tooltip to be @text. If @text is %NULL, the label
257 * will be hidden. See also gtk_tooltip_set_markup().
258 *
259 * Since: 2.12
260 */
261 void
gtk_tooltip_set_text(GtkTooltip * tooltip,const gchar * text)262 gtk_tooltip_set_text (GtkTooltip *tooltip,
263 const gchar *text)
264 {
265 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
266
267 gtk_tooltip_window_set_label_text (GTK_TOOLTIP_WINDOW (tooltip->window), text);
268 }
269
270 /**
271 * gtk_tooltip_set_icon:
272 * @tooltip: a #GtkTooltip
273 * @pixbuf: (allow-none): a #GdkPixbuf, or %NULL
274 *
275 * Sets the icon of the tooltip (which is in front of the text) to be
276 * @pixbuf. If @pixbuf is %NULL, the image will be hidden.
277 *
278 * Since: 2.12
279 */
280 void
gtk_tooltip_set_icon(GtkTooltip * tooltip,GdkPixbuf * pixbuf)281 gtk_tooltip_set_icon (GtkTooltip *tooltip,
282 GdkPixbuf *pixbuf)
283 {
284 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
285 g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
286
287 gtk_tooltip_window_set_image_icon (GTK_TOOLTIP_WINDOW (tooltip->window), pixbuf);
288 }
289
290 /**
291 * gtk_tooltip_set_icon_from_stock:
292 * @tooltip: a #GtkTooltip
293 * @stock_id: (allow-none): a stock id, or %NULL
294 * @size: (type int): a stock icon size (#GtkIconSize)
295 *
296 * Sets the icon of the tooltip (which is in front of the text) to be
297 * the stock item indicated by @stock_id with the size indicated
298 * by @size. If @stock_id is %NULL, the image will be hidden.
299 *
300 * Since: 2.12
301 *
302 * Deprecated: 3.10: Use gtk_tooltip_set_icon_from_icon_name() instead.
303 */
304 void
gtk_tooltip_set_icon_from_stock(GtkTooltip * tooltip,const gchar * stock_id,GtkIconSize size)305 gtk_tooltip_set_icon_from_stock (GtkTooltip *tooltip,
306 const gchar *stock_id,
307 GtkIconSize size)
308 {
309 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
310
311 gtk_tooltip_window_set_image_icon_from_stock (GTK_TOOLTIP_WINDOW (tooltip->window),
312 stock_id,
313 size);
314 }
315
316 /**
317 * gtk_tooltip_set_icon_from_icon_name:
318 * @tooltip: a #GtkTooltip
319 * @icon_name: (allow-none): an icon name, or %NULL
320 * @size: (type int): a stock icon size (#GtkIconSize)
321 *
322 * Sets the icon of the tooltip (which is in front of the text) to be
323 * the icon indicated by @icon_name with the size indicated
324 * by @size. If @icon_name is %NULL, the image will be hidden.
325 *
326 * Since: 2.14
327 */
328 void
gtk_tooltip_set_icon_from_icon_name(GtkTooltip * tooltip,const gchar * icon_name,GtkIconSize size)329 gtk_tooltip_set_icon_from_icon_name (GtkTooltip *tooltip,
330 const gchar *icon_name,
331 GtkIconSize size)
332 {
333 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
334
335 gtk_tooltip_window_set_image_icon_from_name (GTK_TOOLTIP_WINDOW (tooltip->window),
336 icon_name,
337 size);
338 }
339
340 /**
341 * gtk_tooltip_set_icon_from_gicon:
342 * @tooltip: a #GtkTooltip
343 * @gicon: (allow-none): a #GIcon representing the icon, or %NULL
344 * @size: (type int): a stock icon size (#GtkIconSize)
345 *
346 * Sets the icon of the tooltip (which is in front of the text)
347 * to be the icon indicated by @gicon with the size indicated
348 * by @size. If @gicon is %NULL, the image will be hidden.
349 *
350 * Since: 2.20
351 */
352 void
gtk_tooltip_set_icon_from_gicon(GtkTooltip * tooltip,GIcon * gicon,GtkIconSize size)353 gtk_tooltip_set_icon_from_gicon (GtkTooltip *tooltip,
354 GIcon *gicon,
355 GtkIconSize size)
356 {
357 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
358
359 gtk_tooltip_window_set_image_icon_from_gicon (GTK_TOOLTIP_WINDOW (tooltip->window),
360 gicon,
361 size);
362 }
363
364 /**
365 * gtk_tooltip_set_custom:
366 * @tooltip: a #GtkTooltip
367 * @custom_widget: (allow-none): a #GtkWidget, or %NULL to unset the old custom widget.
368 *
369 * Replaces the widget packed into the tooltip with
370 * @custom_widget. @custom_widget does not get destroyed when the tooltip goes
371 * away.
372 * By default a box with a #GtkImage and #GtkLabel is embedded in
373 * the tooltip, which can be configured using gtk_tooltip_set_markup()
374 * and gtk_tooltip_set_icon().
375
376 *
377 * Since: 2.12
378 */
379 void
gtk_tooltip_set_custom(GtkTooltip * tooltip,GtkWidget * custom_widget)380 gtk_tooltip_set_custom (GtkTooltip *tooltip,
381 GtkWidget *custom_widget)
382 {
383 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
384 g_return_if_fail (custom_widget == NULL || GTK_IS_WIDGET (custom_widget));
385
386 /* The custom widget has been updated from the query-tooltip
387 * callback, so we do not want to reset the custom widget later on.
388 */
389 tooltip->custom_was_reset = TRUE;
390
391 gtk_tooltip_window_set_custom_widget (GTK_TOOLTIP_WINDOW (tooltip->window), custom_widget);
392 }
393
394 /**
395 * gtk_tooltip_set_tip_area:
396 * @tooltip: a #GtkTooltip
397 * @rect: a #GdkRectangle
398 *
399 * Sets the area of the widget, where the contents of this tooltip apply,
400 * to be @rect (in widget coordinates). This is especially useful for
401 * properly setting tooltips on #GtkTreeView rows and cells, #GtkIconViews,
402 * etc.
403 *
404 * For setting tooltips on #GtkTreeView, please refer to the convenience
405 * functions for this: gtk_tree_view_set_tooltip_row() and
406 * gtk_tree_view_set_tooltip_cell().
407 *
408 * Since: 2.12
409 */
410 void
gtk_tooltip_set_tip_area(GtkTooltip * tooltip,const GdkRectangle * rect)411 gtk_tooltip_set_tip_area (GtkTooltip *tooltip,
412 const GdkRectangle *rect)
413 {
414 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
415
416 if (!rect)
417 tooltip->tip_area_set = FALSE;
418 else
419 {
420 tooltip->tip_area_set = TRUE;
421 tooltip->tip_area = *rect;
422 }
423 }
424
425 /**
426 * gtk_tooltip_trigger_tooltip_query:
427 * @display: a #GdkDisplay
428 *
429 * Triggers a new tooltip query on @display, in order to update the current
430 * visible tooltip, or to show/hide the current tooltip. This function is
431 * useful to call when, for example, the state of the widget changed by a
432 * key press.
433 *
434 * Since: 2.12
435 */
436 void
gtk_tooltip_trigger_tooltip_query(GdkDisplay * display)437 gtk_tooltip_trigger_tooltip_query (GdkDisplay *display)
438 {
439 gint x, y;
440 GdkWindow *window;
441 GdkEvent event;
442 GdkDevice *device;
443
444 /* Trigger logic as if the mouse moved */
445 device = gdk_seat_get_pointer (gdk_display_get_default_seat (display));
446 window = gdk_device_get_window_at_position (device, &x, &y);
447 if (!window)
448 return;
449
450 event.type = GDK_MOTION_NOTIFY;
451 event.motion.window = window;
452 event.motion.x = x;
453 event.motion.y = y;
454 event.motion.is_hint = FALSE;
455
456 gdk_window_get_root_coords (window, x, y, &x, &y);
457 event.motion.x_root = x;
458 event.motion.y_root = y;
459
460 gtk_tooltip_handle_event_internal (&event);
461 }
462
463 /* private functions */
464
465 static void
gtk_tooltip_reset(GtkTooltip * tooltip)466 gtk_tooltip_reset (GtkTooltip *tooltip)
467 {
468 gtk_tooltip_set_markup (tooltip, NULL);
469 gtk_tooltip_set_icon (tooltip, NULL);
470 gtk_tooltip_set_tip_area (tooltip, NULL);
471
472 /* See if the custom widget is again set from the query-tooltip
473 * callback.
474 */
475 tooltip->custom_was_reset = FALSE;
476 }
477
478 static void
gtk_tooltip_window_hide(GtkWidget * widget,gpointer user_data)479 gtk_tooltip_window_hide (GtkWidget *widget,
480 gpointer user_data)
481 {
482 GtkTooltip *tooltip = GTK_TOOLTIP (user_data);
483
484 gtk_tooltip_set_custom (tooltip, NULL);
485 }
486
487 /* event handling, etc */
488
489 struct ChildLocation
490 {
491 GtkWidget *child;
492 GtkWidget *container;
493
494 gint x;
495 gint y;
496 };
497
498 static void
prepend_and_ref_widget(GtkWidget * widget,gpointer data)499 prepend_and_ref_widget (GtkWidget *widget,
500 gpointer data)
501 {
502 GSList **slist_p = data;
503
504 *slist_p = g_slist_prepend (*slist_p, g_object_ref (widget));
505 }
506
507 static void
child_location_foreach(GtkWidget * child,gpointer data)508 child_location_foreach (GtkWidget *child,
509 gpointer data)
510 {
511 GtkAllocation child_allocation;
512 gint x, y;
513 struct ChildLocation *child_loc = data;
514
515 /* Ignore invisible widgets */
516 if (!gtk_widget_is_drawable (child))
517 return;
518
519 gtk_widget_get_allocation (child, &child_allocation);
520
521 x = 0;
522 y = 0;
523
524 /* (child_loc->x, child_loc->y) are relative to
525 * child_loc->container's allocation.
526 */
527
528 if (!child_loc->child &&
529 gtk_widget_translate_coordinates (child_loc->container, child,
530 child_loc->x, child_loc->y,
531 &x, &y))
532 {
533 /* (x, y) relative to child's allocation. */
534 if (x >= 0 && x < child_allocation.width
535 && y >= 0 && y < child_allocation.height)
536 {
537 if (GTK_IS_CONTAINER (child))
538 {
539 struct ChildLocation tmp = { NULL, NULL, 0, 0 };
540 GSList *children = NULL, *tmp_list;
541
542 /* Take (x, y) relative the child's allocation and
543 * recurse.
544 */
545 tmp.x = x;
546 tmp.y = y;
547 tmp.container = child;
548
549 gtk_container_forall (GTK_CONTAINER (child),
550 prepend_and_ref_widget, &children);
551
552 for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
553 {
554 child_location_foreach (tmp_list->data, &tmp);
555 g_object_unref (tmp_list->data);
556 }
557
558 if (tmp.child)
559 child_loc->child = tmp.child;
560 else
561 child_loc->child = child;
562
563 g_slist_free (children);
564 }
565 else
566 child_loc->child = child;
567 }
568 }
569 }
570
571 /* Translates coordinates from dest_widget->window relative (src_x, src_y),
572 * to allocation relative (dest_x, dest_y) of dest_widget.
573 */
574 static void
window_to_alloc(GtkWidget * dest_widget,gint src_x,gint src_y,gint * dest_x,gint * dest_y)575 window_to_alloc (GtkWidget *dest_widget,
576 gint src_x,
577 gint src_y,
578 gint *dest_x,
579 gint *dest_y)
580 {
581 GtkAllocation allocation;
582
583 gtk_widget_get_allocation (dest_widget, &allocation);
584
585 /* Translate from window relative to allocation relative */
586 if (gtk_widget_get_has_window (dest_widget) &&
587 gtk_widget_get_parent (dest_widget))
588 {
589 gint wx, wy;
590 gdk_window_get_position (gtk_widget_get_window (dest_widget),
591 &wx, &wy);
592
593 /* Offset coordinates if widget->window is smaller than
594 * widget->allocation.
595 */
596 src_x += wx - allocation.x;
597 src_y += wy - allocation.y;
598 }
599 else
600 {
601 src_x -= allocation.x;
602 src_y -= allocation.y;
603 }
604
605 if (dest_x)
606 *dest_x = src_x;
607 if (dest_y)
608 *dest_y = src_y;
609 }
610
611 /* Translates coordinates from window relative (x, y) to
612 * allocation relative (x, y) of the returned widget.
613 */
614 GtkWidget *
_gtk_widget_find_at_coords(GdkWindow * window,gint window_x,gint window_y,gint * widget_x,gint * widget_y)615 _gtk_widget_find_at_coords (GdkWindow *window,
616 gint window_x,
617 gint window_y,
618 gint *widget_x,
619 gint *widget_y)
620 {
621 GtkWidget *event_widget;
622 struct ChildLocation child_loc = { NULL, NULL, 0, 0 };
623
624 g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
625
626 gdk_window_get_user_data (window, (void **)&event_widget);
627
628 if (!event_widget)
629 return NULL;
630
631 /* Coordinates are relative to event window */
632 child_loc.x = window_x;
633 child_loc.y = window_y;
634
635 /* We go down the window hierarchy to the widget->window,
636 * coordinates stay relative to the current window.
637 * We end up with window == widget->window, coordinates relative to that.
638 */
639 while (window && window != gtk_widget_get_window (event_widget))
640 {
641 gdouble px, py;
642
643 gdk_window_coords_to_parent (window,
644 child_loc.x, child_loc.y,
645 &px, &py);
646 child_loc.x = px;
647 child_loc.y = py;
648
649 window = gdk_window_get_effective_parent (window);
650 }
651
652 /* Failing to find widget->window can happen for e.g. a detached handle box;
653 * chaining ::query-tooltip up to its parent probably makes little sense,
654 * and users better implement tooltips on handle_box->child.
655 * so we simply ignore the event for tooltips here.
656 */
657 if (!window)
658 return NULL;
659
660 /* Convert the window relative coordinates to allocation
661 * relative coordinates.
662 */
663 window_to_alloc (event_widget,
664 child_loc.x, child_loc.y,
665 &child_loc.x, &child_loc.y);
666
667 if (GTK_IS_CONTAINER (event_widget))
668 {
669 GtkWidget *container = event_widget;
670
671 child_loc.container = event_widget;
672 child_loc.child = NULL;
673
674 gtk_container_forall (GTK_CONTAINER (event_widget),
675 child_location_foreach, &child_loc);
676
677 /* Here we have a widget, with coordinates relative to
678 * child_loc.container's allocation.
679 */
680
681 if (child_loc.child)
682 event_widget = child_loc.child;
683 else if (child_loc.container)
684 event_widget = child_loc.container;
685
686 /* Translate to event_widget's allocation */
687 gtk_widget_translate_coordinates (container, event_widget,
688 child_loc.x, child_loc.y,
689 &child_loc.x, &child_loc.y);
690 }
691
692 /* We return (x, y) relative to the allocation of event_widget. */
693 if (widget_x)
694 *widget_x = child_loc.x;
695 if (widget_y)
696 *widget_y = child_loc.y;
697
698 return event_widget;
699 }
700
701 /* Ignores (x, y) on input, translates event coordinates to
702 * allocation relative (x, y) of the returned widget.
703 */
704 static GtkWidget *
find_topmost_widget_coords_from_event(GdkEvent * event,gint * x,gint * y)705 find_topmost_widget_coords_from_event (GdkEvent *event,
706 gint *x,
707 gint *y)
708 {
709 GtkAllocation allocation;
710 gint tx, ty;
711 gdouble dx, dy;
712 GtkWidget *tmp;
713
714 gdk_event_get_coords (event, &dx, &dy);
715
716 /* Returns coordinates relative to tmp's allocation. */
717 tmp = _gtk_widget_find_at_coords (event->any.window, dx, dy, &tx, &ty);
718
719 if (!tmp)
720 return NULL;
721
722 /* Make sure the pointer can actually be on the widget returned. */
723 gtk_widget_get_allocation (tmp, &allocation);
724 allocation.x = 0;
725 allocation.y = 0;
726 if (GTK_IS_WINDOW (tmp))
727 {
728 GtkBorder border;
729 _gtk_window_get_shadow_width (GTK_WINDOW (tmp), &border);
730 allocation.x = border.left;
731 allocation.y = border.top;
732 allocation.width -= border.left + border.right;
733 allocation.height -= border.top + border.bottom;
734 }
735
736 if (tx < allocation.x || tx >= allocation.width ||
737 ty < allocation.y || ty >= allocation.height)
738 return NULL;
739
740 if (x)
741 *x = tx;
742 if (y)
743 *y = ty;
744
745 return tmp;
746 }
747
748 static gint
tooltip_browse_mode_expired(gpointer data)749 tooltip_browse_mode_expired (gpointer data)
750 {
751 GtkTooltip *tooltip;
752 GdkDisplay *display;
753
754 tooltip = GTK_TOOLTIP (data);
755
756 tooltip->browse_mode_enabled = FALSE;
757 tooltip->browse_mode_timeout_id = 0;
758
759 if (tooltip->timeout_id)
760 {
761 g_source_remove (tooltip->timeout_id);
762 tooltip->timeout_id = 0;
763 }
764
765 /* destroy tooltip */
766 display = gtk_widget_get_display (tooltip->window);
767 g_object_set_qdata (G_OBJECT (display), quark_current_tooltip, NULL);
768
769 return FALSE;
770 }
771
772 static void
gtk_tooltip_display_closed(GdkDisplay * display,gboolean was_error,GtkTooltip * tooltip)773 gtk_tooltip_display_closed (GdkDisplay *display,
774 gboolean was_error,
775 GtkTooltip *tooltip)
776 {
777 if (tooltip->timeout_id)
778 {
779 g_source_remove (tooltip->timeout_id);
780 tooltip->timeout_id = 0;
781 }
782
783 g_object_set_qdata (G_OBJECT (display), quark_current_tooltip, NULL);
784 }
785
786 static void
gtk_tooltip_set_last_window(GtkTooltip * tooltip,GdkWindow * window)787 gtk_tooltip_set_last_window (GtkTooltip *tooltip,
788 GdkWindow *window)
789 {
790 GtkWidget *window_widget = NULL;
791
792 if (tooltip->last_window == window)
793 return;
794
795 if (tooltip->last_window)
796 g_object_remove_weak_pointer (G_OBJECT (tooltip->last_window),
797 (gpointer *) &tooltip->last_window);
798
799 tooltip->last_window = window;
800
801 if (tooltip->last_window)
802 g_object_add_weak_pointer (G_OBJECT (tooltip->last_window),
803 (gpointer *) &tooltip->last_window);
804
805 if (window)
806 gdk_window_get_user_data (window, (gpointer *) &window_widget);
807
808 if (window_widget)
809 window_widget = gtk_widget_get_toplevel (window_widget);
810
811 if (window_widget &&
812 window_widget != tooltip->window &&
813 gtk_widget_is_toplevel (window_widget) &&
814 GTK_IS_WINDOW (window_widget))
815 gtk_window_set_transient_for (GTK_WINDOW (tooltip->window),
816 GTK_WINDOW (window_widget));
817 else
818 gtk_window_set_transient_for (GTK_WINDOW (tooltip->window), NULL);
819 }
820
821 static gboolean
gtk_tooltip_run_requery(GtkWidget ** widget,GtkTooltip * tooltip,gint * x,gint * y)822 gtk_tooltip_run_requery (GtkWidget **widget,
823 GtkTooltip *tooltip,
824 gint *x,
825 gint *y)
826 {
827 gboolean has_tooltip = FALSE;
828 gboolean return_value = FALSE;
829
830 gtk_tooltip_reset (tooltip);
831
832 do
833 {
834 has_tooltip = gtk_widget_get_has_tooltip (*widget);
835
836 if (has_tooltip)
837 return_value = gtk_widget_query_tooltip (*widget, *x, *y, tooltip->keyboard_mode_enabled, tooltip);
838
839 if (!return_value)
840 {
841 GtkWidget *parent = gtk_widget_get_parent (*widget);
842
843 if (parent)
844 gtk_widget_translate_coordinates (*widget, parent, *x, *y, x, y);
845
846 *widget = parent;
847 }
848 else
849 break;
850 }
851 while (*widget);
852
853 /* If the custom widget was not reset in the query-tooltip
854 * callback, we clear it here.
855 */
856 if (!tooltip->custom_was_reset)
857 gtk_tooltip_set_custom (tooltip, NULL);
858
859 return return_value;
860 }
861
862 static void
gtk_tooltip_position(GtkTooltip * tooltip,GdkDisplay * display,GtkWidget * new_tooltip_widget,GdkDevice * device)863 gtk_tooltip_position (GtkTooltip *tooltip,
864 GdkDisplay *display,
865 GtkWidget *new_tooltip_widget,
866 GdkDevice *device)
867 {
868 GdkScreen *screen;
869 GtkSettings *settings;
870 GdkRectangle anchor_rect;
871 GdkWindow *window;
872 GdkWindow *widget_window;
873 GdkWindow *effective_toplevel;
874 GtkWidget *toplevel;
875 int rect_anchor_dx = 0;
876 int cursor_size;
877 int anchor_rect_padding;
878
879 gtk_widget_realize (GTK_WIDGET (tooltip->current_window));
880 window = _gtk_widget_get_window (GTK_WIDGET (tooltip->current_window));
881
882 tooltip->tooltip_widget = new_tooltip_widget;
883
884 toplevel = _gtk_widget_get_toplevel (new_tooltip_widget);
885 gtk_widget_translate_coordinates (new_tooltip_widget, toplevel,
886 0, 0,
887 &anchor_rect.x, &anchor_rect.y);
888
889 anchor_rect.width = gtk_widget_get_allocated_width (new_tooltip_widget);
890 anchor_rect.height = gtk_widget_get_allocated_height (new_tooltip_widget);
891
892 screen = gdk_window_get_screen (window);
893 settings = gtk_settings_get_for_screen (screen);
894 g_object_get (settings,
895 "gtk-cursor-theme-size", &cursor_size,
896 NULL);
897
898 if (cursor_size == 0)
899 cursor_size = gdk_display_get_default_cursor_size (display);
900
901 if (device)
902 anchor_rect_padding = MAX (4, cursor_size - 32);
903 else
904 anchor_rect_padding = 4;
905
906 anchor_rect.x -= anchor_rect_padding;
907 anchor_rect.y -= anchor_rect_padding;
908 anchor_rect.width += anchor_rect_padding * 2;
909 anchor_rect.height += anchor_rect_padding * 2;
910
911 if (device)
912 {
913 const int max_x_distance = 32;
914 /* Max 48x48 icon + default padding */
915 const int max_anchor_rect_height = 48 + 8;
916 int pointer_x, pointer_y;
917
918 /*
919 * For pointer position triggered tooltips, implement the following
920 * semantics:
921 *
922 * If the anchor rectangle is too tall (meaning if we'd be constrained
923 * and flip, it'd flip too far away), rely only on the pointer position
924 * to position the tooltip. The approximate pointer cursorrectangle is
925 * used as a anchor rectantgle.
926 *
927 * If the anchor rectangle isn't to tall, make sure the tooltip isn't too
928 * far away from the pointer position.
929 */
930 widget_window = _gtk_widget_get_window (new_tooltip_widget);
931 effective_toplevel = gdk_window_get_effective_toplevel (widget_window);
932 gdk_window_get_device_position (effective_toplevel,
933 device,
934 &pointer_x, &pointer_y, NULL);
935
936 if (anchor_rect.height > max_anchor_rect_height)
937 {
938 anchor_rect.x = pointer_x - 4;
939 anchor_rect.y = pointer_y - 4;
940 anchor_rect.width = cursor_size;
941 anchor_rect.height = cursor_size;
942 }
943 else
944 {
945 int anchor_point_x;
946 int x_distance;
947
948 anchor_point_x = anchor_rect.x + anchor_rect.width / 2;
949 x_distance = pointer_x - anchor_point_x;
950
951 if (x_distance > max_x_distance)
952 rect_anchor_dx = x_distance - max_x_distance;
953 else if (x_distance < -max_x_distance)
954 rect_anchor_dx = x_distance + max_x_distance;
955 }
956 }
957
958 gtk_window_set_transient_for (GTK_WINDOW (tooltip->current_window),
959 GTK_WINDOW (toplevel));
960
961 gdk_window_move_to_rect (window,
962 &anchor_rect,
963 GDK_GRAVITY_SOUTH,
964 GDK_GRAVITY_NORTH,
965 GDK_ANCHOR_FLIP_Y | GDK_ANCHOR_SLIDE_X,
966 rect_anchor_dx, 0);
967 gtk_widget_show (GTK_WIDGET (tooltip->current_window));
968 }
969
970 static void
gtk_tooltip_show_tooltip(GdkDisplay * display)971 gtk_tooltip_show_tooltip (GdkDisplay *display)
972 {
973 gint x, y;
974 GdkScreen *screen;
975 GdkDevice *device;
976 GdkWindow *window;
977 GtkWidget *tooltip_widget;
978 GtkTooltip *tooltip;
979 gboolean return_value = FALSE;
980
981 tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
982
983 if (tooltip->keyboard_mode_enabled)
984 {
985 x = y = -1;
986 tooltip_widget = tooltip->keyboard_widget;
987 device = NULL;
988 }
989 else
990 {
991 gint tx, ty;
992
993 window = tooltip->last_window;
994
995 if (!GDK_IS_WINDOW (window))
996 return;
997
998 device = gdk_seat_get_pointer (gdk_display_get_default_seat (display));
999
1000 gdk_window_get_device_position (window, device, &x, &y, NULL);
1001
1002 gdk_window_get_root_coords (window, x, y, &tx, &ty);
1003
1004 tooltip_widget = _gtk_widget_find_at_coords (window, x, y, &x, &y);
1005 }
1006
1007 if (!tooltip_widget)
1008 return;
1009
1010 return_value = gtk_tooltip_run_requery (&tooltip_widget, tooltip, &x, &y);
1011 if (!return_value)
1012 return;
1013
1014 if (!tooltip->current_window)
1015 {
1016 if (gtk_widget_get_tooltip_window (tooltip_widget))
1017 tooltip->current_window = gtk_widget_get_tooltip_window (tooltip_widget);
1018 else
1019 tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
1020 }
1021
1022 screen = gtk_widget_get_screen (tooltip_widget);
1023
1024 /* FIXME: should use tooltip->current_window iso tooltip->window */
1025 if (screen != gtk_widget_get_screen (tooltip->window))
1026 {
1027 g_signal_handlers_disconnect_by_func (display,
1028 gtk_tooltip_display_closed,
1029 tooltip);
1030
1031 gtk_window_set_screen (GTK_WINDOW (tooltip->window), screen);
1032
1033 g_signal_connect (display, "closed",
1034 G_CALLBACK (gtk_tooltip_display_closed), tooltip);
1035 }
1036
1037 gtk_tooltip_position (tooltip, display, tooltip_widget, device);
1038
1039 /* Now a tooltip is visible again on the display, make sure browse
1040 * mode is enabled.
1041 */
1042 tooltip->browse_mode_enabled = TRUE;
1043 if (tooltip->browse_mode_timeout_id)
1044 {
1045 g_source_remove (tooltip->browse_mode_timeout_id);
1046 tooltip->browse_mode_timeout_id = 0;
1047 }
1048 }
1049
1050 static void
gtk_tooltip_hide_tooltip(GtkTooltip * tooltip)1051 gtk_tooltip_hide_tooltip (GtkTooltip *tooltip)
1052 {
1053 if (!tooltip)
1054 return;
1055
1056 if (tooltip->timeout_id)
1057 {
1058 g_source_remove (tooltip->timeout_id);
1059 tooltip->timeout_id = 0;
1060 }
1061
1062 if (!GTK_TOOLTIP_VISIBLE (tooltip))
1063 return;
1064
1065 tooltip->tooltip_widget = NULL;
1066
1067 if (!tooltip->keyboard_mode_enabled)
1068 {
1069 guint timeout = BROWSE_DISABLE_TIMEOUT;
1070
1071 /* The tooltip is gone, after (by default, should be configurable) 500ms
1072 * we want to turn off browse mode
1073 */
1074 if (!tooltip->browse_mode_timeout_id)
1075 {
1076 tooltip->browse_mode_timeout_id =
1077 gdk_threads_add_timeout_full (0, timeout,
1078 tooltip_browse_mode_expired,
1079 g_object_ref (tooltip),
1080 g_object_unref);
1081 g_source_set_name_by_id (tooltip->browse_mode_timeout_id, "[gtk+] tooltip_browse_mode_expired");
1082 }
1083 }
1084 else
1085 {
1086 if (tooltip->browse_mode_timeout_id)
1087 {
1088 g_source_remove (tooltip->browse_mode_timeout_id);
1089 tooltip->browse_mode_timeout_id = 0;
1090 }
1091 }
1092
1093 if (tooltip->current_window)
1094 {
1095 gtk_widget_hide (GTK_WIDGET (tooltip->current_window));
1096 tooltip->current_window = NULL;
1097 }
1098 }
1099
1100 static gint
tooltip_popup_timeout(gpointer data)1101 tooltip_popup_timeout (gpointer data)
1102 {
1103 GdkDisplay *display;
1104 GtkTooltip *tooltip;
1105
1106 display = GDK_DISPLAY (data);
1107 tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
1108
1109 /* This usually does not happen. However, it does occur in language
1110 * bindings were reference counting of objects behaves differently.
1111 */
1112 if (!tooltip)
1113 return FALSE;
1114
1115 gtk_tooltip_show_tooltip (display);
1116
1117 tooltip->timeout_id = 0;
1118
1119 return FALSE;
1120 }
1121
1122 static void
gtk_tooltip_start_delay(GdkDisplay * display)1123 gtk_tooltip_start_delay (GdkDisplay *display)
1124 {
1125 guint timeout;
1126 GtkTooltip *tooltip;
1127
1128 tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
1129
1130 if (!tooltip || GTK_TOOLTIP_VISIBLE (tooltip))
1131 return;
1132
1133 if (tooltip->timeout_id)
1134 g_source_remove (tooltip->timeout_id);
1135
1136 if (tooltip->browse_mode_enabled)
1137 timeout = BROWSE_TIMEOUT;
1138 else
1139 timeout = HOVER_TIMEOUT;
1140
1141 tooltip->timeout_id = gdk_threads_add_timeout_full (0, timeout,
1142 tooltip_popup_timeout,
1143 g_object_ref (display),
1144 g_object_unref);
1145 g_source_set_name_by_id (tooltip->timeout_id, "[gtk+] tooltip_popup_timeout");
1146 }
1147
1148 void
_gtk_tooltip_focus_in(GtkWidget * widget)1149 _gtk_tooltip_focus_in (GtkWidget *widget)
1150 {
1151 gint x, y;
1152 gboolean return_value = FALSE;
1153 GdkDisplay *display;
1154 GtkTooltip *tooltip;
1155 GdkDevice *device;
1156
1157 /* Get current tooltip for this display */
1158 display = gtk_widget_get_display (widget);
1159 tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
1160
1161 /* Check if keyboard mode is enabled at this moment */
1162 if (!tooltip || !tooltip->keyboard_mode_enabled)
1163 return;
1164
1165 device = gtk_get_current_event_device ();
1166
1167 if (device && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
1168 device = gdk_device_get_associated_device (device);
1169
1170 /* This function should be called by either a focus in event,
1171 * or a key binding. In either case there should be a device.
1172 */
1173 if (!device)
1174 return;
1175
1176 if (tooltip->keyboard_widget)
1177 g_object_unref (tooltip->keyboard_widget);
1178
1179 tooltip->keyboard_widget = g_object_ref (widget);
1180
1181 gdk_window_get_device_position (gtk_widget_get_window (widget),
1182 device, &x, &y, NULL);
1183
1184 return_value = gtk_tooltip_run_requery (&widget, tooltip, &x, &y);
1185 if (!return_value)
1186 {
1187 gtk_tooltip_hide_tooltip (tooltip);
1188 return;
1189 }
1190
1191 if (!tooltip->current_window)
1192 {
1193 if (gtk_widget_get_tooltip_window (widget))
1194 tooltip->current_window = gtk_widget_get_tooltip_window (widget);
1195 else
1196 tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
1197 }
1198
1199 gtk_tooltip_show_tooltip (display);
1200 }
1201
1202 void
_gtk_tooltip_focus_out(GtkWidget * widget)1203 _gtk_tooltip_focus_out (GtkWidget *widget)
1204 {
1205 GdkDisplay *display;
1206 GtkTooltip *tooltip;
1207
1208 /* Get current tooltip for this display */
1209 display = gtk_widget_get_display (widget);
1210 tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
1211
1212 if (!tooltip || !tooltip->keyboard_mode_enabled)
1213 return;
1214
1215 if (tooltip->keyboard_widget)
1216 {
1217 g_object_unref (tooltip->keyboard_widget);
1218 tooltip->keyboard_widget = NULL;
1219 }
1220
1221 gtk_tooltip_hide_tooltip (tooltip);
1222 }
1223
1224 void
_gtk_tooltip_toggle_keyboard_mode(GtkWidget * widget)1225 _gtk_tooltip_toggle_keyboard_mode (GtkWidget *widget)
1226 {
1227 GdkDisplay *display;
1228 GtkTooltip *tooltip;
1229
1230 display = gtk_widget_get_display (widget);
1231 tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
1232
1233 if (!tooltip)
1234 {
1235 tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
1236 g_object_set_qdata_full (G_OBJECT (display),
1237 quark_current_tooltip,
1238 tooltip,
1239 g_object_unref);
1240 g_signal_connect (display, "closed",
1241 G_CALLBACK (gtk_tooltip_display_closed),
1242 tooltip);
1243 }
1244
1245 tooltip->keyboard_mode_enabled ^= 1;
1246
1247 if (tooltip->keyboard_mode_enabled)
1248 {
1249 tooltip->keyboard_widget = g_object_ref (widget);
1250 _gtk_tooltip_focus_in (widget);
1251 }
1252 else
1253 {
1254 if (tooltip->keyboard_widget)
1255 {
1256 g_object_unref (tooltip->keyboard_widget);
1257 tooltip->keyboard_widget = NULL;
1258 }
1259
1260 gtk_tooltip_hide_tooltip (tooltip);
1261 }
1262 }
1263
1264 void
_gtk_tooltip_hide(GtkWidget * widget)1265 _gtk_tooltip_hide (GtkWidget *widget)
1266 {
1267 GdkDisplay *display;
1268 GtkTooltip *tooltip;
1269
1270 display = gtk_widget_get_display (widget);
1271 tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
1272
1273 if (!tooltip || !GTK_TOOLTIP_VISIBLE (tooltip) || !tooltip->tooltip_widget)
1274 return;
1275
1276 if (widget == tooltip->tooltip_widget)
1277 gtk_tooltip_hide_tooltip (tooltip);
1278 }
1279
1280 void
_gtk_tooltip_hide_in_display(GdkDisplay * display)1281 _gtk_tooltip_hide_in_display (GdkDisplay *display)
1282 {
1283 GtkTooltip *tooltip;
1284
1285 if (!display)
1286 return;
1287
1288 tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
1289
1290 if (!tooltip || !GTK_TOOLTIP_VISIBLE (tooltip))
1291 return;
1292
1293 gtk_tooltip_hide_tooltip (tooltip);
1294 }
1295
1296 static gboolean
tooltips_enabled(GdkEvent * event)1297 tooltips_enabled (GdkEvent *event)
1298 {
1299 GdkDevice *source_device;
1300 GdkInputSource source;
1301
1302 source_device = gdk_event_get_source_device (event);
1303
1304 if (!source_device)
1305 return FALSE;
1306
1307 source = gdk_device_get_source (source_device);
1308
1309 if (source != GDK_SOURCE_TOUCHSCREEN)
1310 return TRUE;
1311
1312 return FALSE;
1313 }
1314
1315 void
_gtk_tooltip_handle_event(GdkEvent * event)1316 _gtk_tooltip_handle_event (GdkEvent *event)
1317 {
1318 if (!tooltips_enabled (event))
1319 return;
1320
1321 gtk_tooltip_handle_event_internal (event);
1322 }
1323
1324 static void
gtk_tooltip_handle_event_internal(GdkEvent * event)1325 gtk_tooltip_handle_event_internal (GdkEvent *event)
1326 {
1327 gint x, y;
1328 GtkWidget *has_tooltip_widget;
1329 GdkDisplay *display;
1330 GtkTooltip *current_tooltip;
1331
1332 /* Returns coordinates relative to has_tooltip_widget's allocation. */
1333 has_tooltip_widget = find_topmost_widget_coords_from_event (event, &x, &y);
1334 display = gdk_window_get_display (event->any.window);
1335 current_tooltip = g_object_get_qdata (G_OBJECT (display), quark_current_tooltip);
1336
1337 if (current_tooltip)
1338 {
1339 gtk_tooltip_set_last_window (current_tooltip, event->any.window);
1340 }
1341
1342 if (current_tooltip && current_tooltip->keyboard_mode_enabled)
1343 {
1344 gboolean return_value;
1345
1346 has_tooltip_widget = current_tooltip->keyboard_widget;
1347 if (!has_tooltip_widget)
1348 return;
1349
1350 return_value = gtk_tooltip_run_requery (&has_tooltip_widget,
1351 current_tooltip,
1352 &x, &y);
1353
1354 if (!return_value)
1355 gtk_tooltip_hide_tooltip (current_tooltip);
1356 else
1357 gtk_tooltip_start_delay (display);
1358
1359 return;
1360 }
1361
1362 /* Always poll for a next motion event */
1363 gdk_event_request_motions (&event->motion);
1364
1365 /* Hide the tooltip when there's no new tooltip widget */
1366 if (!has_tooltip_widget)
1367 {
1368 if (current_tooltip)
1369 gtk_tooltip_hide_tooltip (current_tooltip);
1370
1371 return;
1372 }
1373
1374 switch (event->type)
1375 {
1376 case GDK_BUTTON_PRESS:
1377 case GDK_2BUTTON_PRESS:
1378 case GDK_3BUTTON_PRESS:
1379 case GDK_KEY_PRESS:
1380 case GDK_DRAG_ENTER:
1381 case GDK_GRAB_BROKEN:
1382 case GDK_SCROLL:
1383 gtk_tooltip_hide_tooltip (current_tooltip);
1384 break;
1385
1386 case GDK_MOTION_NOTIFY:
1387 case GDK_ENTER_NOTIFY:
1388 case GDK_LEAVE_NOTIFY:
1389 if (current_tooltip)
1390 {
1391 gboolean tip_area_set;
1392 GdkRectangle tip_area;
1393 gboolean hide_tooltip;
1394
1395 tip_area_set = current_tooltip->tip_area_set;
1396 tip_area = current_tooltip->tip_area;
1397
1398 gtk_tooltip_run_requery (&has_tooltip_widget,
1399 current_tooltip,
1400 &x, &y);
1401
1402 /* Leave notify should override the query function */
1403 hide_tooltip = (event->type == GDK_LEAVE_NOTIFY);
1404
1405 /* Is the pointer above another widget now? */
1406 if (GTK_TOOLTIP_VISIBLE (current_tooltip))
1407 hide_tooltip |= has_tooltip_widget != current_tooltip->tooltip_widget;
1408
1409 /* Did the pointer move out of the previous "context area"? */
1410 if (tip_area_set)
1411 hide_tooltip |= (x <= tip_area.x
1412 || x >= tip_area.x + tip_area.width
1413 || y <= tip_area.y
1414 || y >= tip_area.y + tip_area.height);
1415
1416 if (hide_tooltip)
1417 gtk_tooltip_hide_tooltip (current_tooltip);
1418 else
1419 gtk_tooltip_start_delay (display);
1420 }
1421 else
1422 {
1423 /* Need a new tooltip for this display */
1424 current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
1425 g_object_set_qdata_full (G_OBJECT (display),
1426 quark_current_tooltip,
1427 current_tooltip,
1428 g_object_unref);
1429 g_signal_connect (display, "closed",
1430 G_CALLBACK (gtk_tooltip_display_closed),
1431 current_tooltip);
1432
1433 gtk_tooltip_set_last_window (current_tooltip, event->any.window);
1434
1435 gtk_tooltip_start_delay (display);
1436 }
1437 break;
1438
1439 default:
1440 break;
1441 }
1442 }
1443