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