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