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