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