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