]> Pileus Git - ~andy/gtk/blob - gtk/gtktooltip.c
use new gdk_threads API.
[~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 #include "gtktooltip.h"
24 #include "gtkintl.h"
25 #include "gtkwindow.h"
26 #include "gtkmain.h"
27 #include "gtklabel.h"
28 #include "gtkimage.h"
29 #include "gtkhbox.h"
30 #include "gtkalignment.h"
31
32 #include <string.h>
33
34
35 #define GTK_TOOLTIP(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TOOLTIP, GtkTooltip))
36 #define GTK_TOOLTIP_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TOOLTIP, GtkTooltipClass))
37 #define GTK_IS_TOOLTIP(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TOOLTIP))
38 #define GTK_IS_TOOLTIP_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TOOLTIP))
39 #define GTK_TOOLTIP_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TOOLTIP, GtkTooltipClass))
40
41 typedef struct _GtkTooltipClass   GtkTooltipClass;
42
43 struct _GtkTooltip
44 {
45   GObject parent_instance;
46
47   GtkWidget *window;
48   GtkWidget *alignment;
49   GtkWidget *box;
50   GtkWidget *image;
51   GtkWidget *label;
52   GtkWidget *custom_widget;
53
54   GtkWindow *current_window;
55   GtkWidget *keyboard_widget;
56
57   GtkWidget *tooltip_widget;
58   GdkWindow *toplevel_window;
59
60   gdouble last_x;
61   gdouble last_y;
62   GdkWindow *last_window;
63
64   guint timeout_id;
65   guint browse_mode_timeout_id;
66
67   guint browse_mode_enabled : 1;
68   guint keyboard_mode_enabled : 1;
69 };
70
71 struct _GtkTooltipClass
72 {
73   GObjectClass parent_class;
74 };
75
76 #define GTK_TOOLTIP_VISIBLE(tooltip) ((tooltip)->current_window && GTK_WIDGET_VISIBLE ((tooltip)->current_window))
77
78
79 static void       gtk_tooltip_class_init           (GtkTooltipClass *klass);
80 static void       gtk_tooltip_init                 (GtkTooltip      *tooltip);
81 static void       gtk_tooltip_finalize             (GObject         *object);
82
83 static gboolean   gtk_tooltip_paint_window         (GtkTooltip      *tooltip);
84 static void       gtk_tooltip_window_hide          (GtkWidget       *widget,
85                                                     gpointer         user_data);
86 static void       gtk_tooltip_display_closed       (GdkDisplay      *display,
87                                                     gboolean         was_error,
88                                                     GtkTooltip      *tooltip);
89
90
91 G_DEFINE_TYPE (GtkTooltip, gtk_tooltip, G_TYPE_OBJECT);
92
93 static void
94 gtk_tooltip_class_init (GtkTooltipClass *klass)
95 {
96   GObjectClass *object_class;
97
98   object_class = G_OBJECT_CLASS (klass);
99
100   object_class->finalize = gtk_tooltip_finalize;
101 }
102
103 static void
104 gtk_tooltip_init (GtkTooltip *tooltip)
105 {
106   tooltip->timeout_id = 0;
107   tooltip->browse_mode_timeout_id = 0;
108
109   tooltip->browse_mode_enabled = FALSE;
110   tooltip->keyboard_mode_enabled = FALSE;
111
112   tooltip->current_window = NULL;
113   tooltip->keyboard_widget = NULL;
114
115   tooltip->tooltip_widget = NULL;
116   tooltip->toplevel_window = NULL;
117
118   tooltip->last_window = NULL;
119
120   tooltip->window = g_object_ref (gtk_window_new (GTK_WINDOW_POPUP));
121   gtk_window_set_type_hint (GTK_WINDOW (tooltip->window),
122                             GDK_WINDOW_TYPE_HINT_TOOLTIP);
123   gtk_widget_set_app_paintable (tooltip->window, TRUE);
124   gtk_window_set_resizable (GTK_WINDOW (tooltip->window), FALSE);
125   gtk_widget_set_name (tooltip->window, "gtk-tooltip");
126   g_signal_connect (tooltip->window, "hide",
127                     G_CALLBACK (gtk_tooltip_window_hide), tooltip);
128
129   tooltip->alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
130   gtk_alignment_set_padding (GTK_ALIGNMENT (tooltip->alignment),
131                              tooltip->window->style->ythickness,
132                              tooltip->window->style->ythickness,
133                              tooltip->window->style->xthickness,
134                              tooltip->window->style->xthickness);
135   gtk_container_add (GTK_CONTAINER (tooltip->window), tooltip->alignment);
136   gtk_widget_show (tooltip->alignment);
137
138   g_signal_connect_swapped (tooltip->window, "expose_event",
139                             G_CALLBACK (gtk_tooltip_paint_window), tooltip);
140
141   tooltip->box = gtk_hbox_new (FALSE, tooltip->window->style->xthickness);
142   gtk_container_add (GTK_CONTAINER (tooltip->alignment), tooltip->box);
143   gtk_widget_show (tooltip->box);
144
145   tooltip->image = gtk_image_new ();
146   gtk_box_pack_start (GTK_BOX (tooltip->box), tooltip->image,
147                       FALSE, FALSE, 0);
148
149   tooltip->label = gtk_label_new ("");
150   gtk_box_pack_start (GTK_BOX (tooltip->box), tooltip->label,
151                       FALSE, FALSE, 0);
152
153   tooltip->custom_widget = NULL;
154 }
155
156 static void
157 gtk_tooltip_finalize (GObject *object)
158 {
159   GtkTooltip *tooltip = GTK_TOOLTIP (object);
160
161   if (tooltip->timeout_id)
162     {
163       g_source_remove (tooltip->timeout_id);
164       tooltip->timeout_id = 0;
165     }
166
167   if (tooltip->browse_mode_timeout_id)
168     {
169       g_source_remove (tooltip->browse_mode_timeout_id);
170       tooltip->browse_mode_timeout_id = 0;
171     }
172
173   if (tooltip->window)
174     {
175       GdkDisplay *display;
176
177       display = gtk_widget_get_display (tooltip->window);
178       g_signal_handlers_disconnect_by_func (display,
179                                             gtk_tooltip_display_closed,
180                                             tooltip);
181       gtk_widget_destroy (tooltip->window);
182     }
183
184   G_OBJECT_CLASS (gtk_tooltip_parent_class)->finalize (object);
185 }
186
187 /* public API */
188
189 /**
190  * gtk_tooltip_set_markup:
191  * @label: a #GtkTooltip
192  * @markup: a markup string (see <link linkend="PangoMarkupFormat">Pango markup format</link>) or %NULL
193  *
194  * Sets the text of the tooltip to be @str, which is marked up
195  * with the <link
196  * linkend="PangoMarkupFormat">Pango text markup language</link>.
197  * If @markup is %NULL, the label will be hidden.
198  *
199  * Since: 2.12
200  */
201 void
202 gtk_tooltip_set_markup (GtkTooltip  *tooltip,
203                         const gchar *markup)
204 {
205   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
206
207   gtk_label_set_markup (GTK_LABEL (tooltip->label), markup);
208
209   if (markup)
210     gtk_widget_show (tooltip->label);
211   else
212     gtk_widget_hide (tooltip->label);
213 }
214
215 /**
216  * gtk_tooltip_set_icon:
217  * @tooltip: a #GtkTooltip
218  * @pixbuf: a #GdkPixbuf, or %NULL
219  *
220  * Sets the icon of the tooltip (which is in front of the text) to be
221  * @pixbuf.  If @pixbuf is %NULL, the image will be hidden.
222  *
223  * Since: 2.12
224  */
225 void
226 gtk_tooltip_set_icon (GtkTooltip *tooltip,
227                       GdkPixbuf  *pixbuf)
228 {
229   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
230   if (pixbuf)
231     g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
232
233   gtk_image_set_from_pixbuf (GTK_IMAGE (tooltip->image), pixbuf);
234
235   if (pixbuf)
236     gtk_widget_show (tooltip->image);
237   else
238     gtk_widget_hide (tooltip->image);
239 }
240
241 /**
242  * gtk_tooltip_set_icon_from_stock:
243  * @tooltip: a #GtkTooltip
244  * @stock_id: a stock icon name, or %NULL
245  * @size: a stock icon size
246  *
247  * Sets the icon of the tooltip (which is in front of the text) to be
248  * the stock item indicated by @stock_id with the size indicated
249  * by @size.  If @stock_id is %NULL, the image will be hidden.
250  *
251  * Since: 2.12
252  */
253 void
254 gtk_tooltip_set_icon_from_stock (GtkTooltip  *tooltip,
255                                  const gchar *stock_id,
256                                  GtkIconSize  size)
257 {
258   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
259
260   gtk_image_set_from_stock (GTK_IMAGE (tooltip->image), stock_id, size);
261
262   if (stock_id)
263     gtk_widget_show (tooltip->image);
264   else
265     gtk_widget_hide (tooltip->image);
266 }
267
268 /**
269  * gtk_tooltip_set_custom:
270  * tooltip: a #GtkTooltip
271  * custom_widget: a #GtkWidget
272  *
273  * Replaces the widget packed into the tooltip with @custom_widget.  By
274  * default a box with a #GtkImage and #GtkLabel is embedded in the tooltip,
275  * which can be configured using gtk_tooltip_set_markup() and
276  * gtk_tooltip_set_icon().
277  *
278  * Since: 2.12
279  */
280 void
281 gtk_tooltip_set_custom (GtkTooltip *tooltip,
282                         GtkWidget  *custom_widget)
283 {
284   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
285   if (custom_widget)
286     g_return_if_fail (GTK_IS_WIDGET (custom_widget));
287
288   if (tooltip->custom_widget)
289     {
290       gtk_container_remove (GTK_CONTAINER (tooltip->box),
291                             tooltip->custom_widget);
292       g_object_unref (tooltip->custom_widget);
293     }
294
295   if (custom_widget)
296     {
297       tooltip->custom_widget = g_object_ref (custom_widget);
298
299       gtk_container_add (GTK_CONTAINER (tooltip->box), custom_widget);
300       gtk_widget_show (custom_widget);
301     }
302   else
303     tooltip->custom_widget = NULL;
304 }
305
306 /**
307  * gtk_tooltip_trigger_tooltip_query:
308  * @display: a #GtkDisplay
309  *
310  * Triggers a new tooltip query on @display, in order to update the current
311  * visible tooltip, or to show/hide the current tooltip.  This function is
312  * useful to call when, for example, the state of the widget changed by a
313  * key press.
314  *
315  * Since: 2.12
316  */
317 void
318 gtk_tooltip_trigger_tooltip_query (GdkDisplay *display)
319 {
320   gint x, y;
321   GdkWindow *window;
322   GdkEvent event;
323
324   /* Trigger logic as if the mouse moved */
325   window = gdk_display_get_window_at_pointer (display, &x, &y);
326   if (!window)
327     return;
328
329   event.type = GDK_MOTION_NOTIFY;
330   event.motion.window = window;
331   event.motion.x = x;
332   event.motion.y = y;
333   event.motion.is_hint = FALSE;
334
335   _gtk_tooltip_handle_event (&event);
336 }
337
338 /* private functions */
339
340 static void
341 gtk_tooltip_reset (GtkTooltip *tooltip)
342 {
343   gtk_tooltip_set_markup (tooltip, NULL);
344   gtk_tooltip_set_icon (tooltip, NULL);
345   gtk_tooltip_set_custom (tooltip, NULL);
346 }
347
348 static gboolean
349 gtk_tooltip_paint_window (GtkTooltip *tooltip)
350 {
351   GtkRequisition req;
352
353   gtk_widget_size_request (tooltip->window, &req);
354   gtk_paint_flat_box (tooltip->window->style,
355                       tooltip->window->window,
356                       GTK_STATE_NORMAL,
357                       GTK_SHADOW_OUT,
358                       NULL,
359                       tooltip->window,
360                       "tooltip",
361                       0, 0,
362                       tooltip->window->allocation.width,
363                       tooltip->window->allocation.height);
364
365   return FALSE;
366 }
367
368 static void
369 gtk_tooltip_window_hide (GtkWidget *widget,
370                          gpointer   user_data)
371 {
372   GtkTooltip *tooltip = GTK_TOOLTIP (user_data);
373
374   if (tooltip->custom_widget)
375     gtk_tooltip_set_custom (tooltip, NULL);
376 }
377
378 /* event handling, etc */
379
380 struct ChildLocation
381 {
382   GtkWidget *child;
383   GtkWidget *container;
384
385   gint x;
386   gint y;
387 };
388
389 static void
390 child_location_foreach (GtkWidget *child,
391                         gpointer   data)
392 {
393   gint x, y;
394   struct ChildLocation *child_loc = data;
395
396   if (!child_loc->child &&
397       gtk_widget_translate_coordinates (child_loc->container, child,
398                                         child_loc->x, child_loc->y,
399                                         &x, &y))
400     {
401       if (x >= 0 && x < child->allocation.width
402           && y >= 0 && y < child->allocation.height)
403         {
404           if (GTK_IS_CONTAINER (child))
405             {
406               struct ChildLocation tmp = { NULL, NULL, 0, 0 };
407
408               tmp.x = x;
409               tmp.y = y;
410               tmp.container = child;
411
412               gtk_container_foreach (GTK_CONTAINER (child),
413                                      child_location_foreach, &tmp);
414
415               if (tmp.child)
416                 child_loc->child = tmp.child;
417               else
418                 child_loc->child = child;
419             }
420           else
421             child_loc->child = child;
422         }
423     }
424 }
425
426 static void
427 window_to_alloc (GtkWidget *dest_widget,
428                  gint       src_x,
429                  gint       src_y,
430                  gint      *dest_x,
431                  gint      *dest_y)
432 {
433   /* Translate from window relative to allocation relative */
434   if (!GTK_WIDGET_NO_WINDOW (dest_widget) && dest_widget->parent)
435     {
436       gint wx, wy;
437       gdk_window_get_position (dest_widget->window, &wx, &wy);
438
439       src_x += wx - dest_widget->allocation.x;
440       src_y += wy - dest_widget->allocation.y;
441     }
442   else
443     {
444       src_x -= dest_widget->allocation.x;
445       src_y -= dest_widget->allocation.y;
446     }
447
448   if (dest_x)
449     *dest_x = src_x;
450   if (dest_y)
451     *dest_y = src_y;
452 }
453
454 static GtkWidget *
455 find_widget_under_pointer (GdkWindow *window,
456                            gint      *x,
457                            gint      *y)
458 {
459   GtkWidget *event_widget;
460   struct ChildLocation child_loc = { NULL, NULL, 0, 0 };
461
462   child_loc.x = *x;
463   child_loc.y = *y;
464
465   gdk_window_get_user_data (window, (void **)&event_widget);
466   if (GTK_IS_CONTAINER (event_widget))
467     {
468       window_to_alloc (event_widget,
469                        child_loc.x, child_loc.y,
470                        &child_loc.x, &child_loc.y);
471
472       child_loc.container = event_widget;
473       child_loc.child = NULL;
474
475       gtk_container_foreach (GTK_CONTAINER (event_widget),
476                              child_location_foreach, &child_loc);
477
478       if (child_loc.child)
479         event_widget = child_loc.child;
480       else if (child_loc.container)
481         event_widget = child_loc.container;
482     }
483
484   if (x)
485     *x = child_loc.x;
486   if (y)
487     *y = child_loc.y;
488
489   return event_widget;
490 }
491
492 static GtkWidget *
493 find_topmost_widget_coords_from_event (GdkEvent *event,
494                                        gint     *x,
495                                        gint     *y)
496 {
497   gint tx, ty;
498   gdouble dx, dy;
499   GtkWidget *tmp;
500   GtkWidget *orig;
501
502   gdk_event_get_coords (event, &dx, &dy);
503   tx = dx;
504   ty = dy;
505
506   orig = tmp = find_widget_under_pointer (event->any.window, &tx, &ty);
507
508   if (tmp && (x != NULL || y != NULL))
509     {
510       if (tmp != orig)
511         gtk_widget_translate_coordinates (orig, tmp, tx, ty, x, y);
512       else
513         {
514           if (x)
515             *x = tx;
516           if (y)
517             *y = ty;
518         }
519     }
520
521   return tmp;
522 }
523
524 static gint
525 tooltip_browse_mode_expired (gpointer data)
526 {
527   GtkTooltip *tooltip;
528
529   tooltip = GTK_TOOLTIP (data);
530
531   tooltip->browse_mode_enabled = FALSE;
532   tooltip->browse_mode_timeout_id = 0;
533
534   /* destroy tooltip */
535   g_object_set_data (G_OBJECT (gtk_widget_get_display (tooltip->window)),
536                      "gdk-display-current-tooltip", NULL);
537
538   return FALSE;
539 }
540
541 static void
542 gtk_tooltip_display_closed (GdkDisplay *display,
543                             gboolean    was_error,
544                             GtkTooltip *tooltip)
545 {
546   g_object_set (display, "gdk-display-current-tooltip", NULL);
547 }
548
549 static gboolean
550 gtk_tooltip_run_requery (GtkWidget  **widget,
551                          GtkTooltip  *tooltip,
552                          gint        *x,
553                          gint        *y)
554 {
555   gboolean has_tooltip = FALSE;
556   gboolean return_value = FALSE;
557
558   gtk_tooltip_reset (tooltip);
559
560   do
561     {
562       g_object_get (*widget,
563                     "has-tooltip", &has_tooltip,
564                     NULL);
565
566       if (has_tooltip)
567         g_signal_emit_by_name (*widget,
568                                "query-tooltip",
569                                *x, *y,
570                                tooltip->keyboard_mode_enabled,
571                                tooltip,
572                                &return_value);
573
574       if (!return_value)
575         {
576           GtkWidget *parent = (*widget)->parent;
577
578           if (parent)
579             gtk_widget_translate_coordinates (*widget, parent, *x, *y, x, y);
580
581           *widget = parent;
582         }
583       else
584         break;
585     }
586   while (*widget);
587
588   return return_value;
589 }
590
591 static void
592 gtk_tooltip_show_tooltip (GdkDisplay *display)
593 {
594   gint x, y;
595   GdkScreen *screen;
596
597   GdkWindow *window;
598   GtkWidget *tooltip_widget;
599   GtkWidget *pointer_widget;
600   GtkTooltip *tooltip;
601   gboolean has_tooltip;
602   gboolean return_value = FALSE;
603
604   tooltip = g_object_get_data (G_OBJECT (display),
605                                "gdk-display-current-tooltip");
606
607   if (tooltip->keyboard_mode_enabled)
608     {
609       pointer_widget = tooltip_widget = tooltip->keyboard_widget;
610     }
611   else
612     {
613       window = tooltip->last_window;
614
615       gdk_window_get_origin (window, &x, &y);
616       x = tooltip->last_x - x;
617       y = tooltip->last_y - y;
618
619       if (!window)
620         return;
621
622       pointer_widget = tooltip_widget = find_widget_under_pointer (window,
623                                                                    &x, &y);
624     }
625
626   if (!tooltip_widget)
627     return;
628
629   g_object_get (tooltip_widget, "has-tooltip", &has_tooltip, NULL);
630
631   g_assert (tooltip != NULL);
632
633   return_value = gtk_tooltip_run_requery (&tooltip_widget, tooltip, &x, &y);
634   if (!return_value)
635     return;
636
637   if (!tooltip->current_window)
638     {
639       if (gtk_widget_get_tooltip_window (tooltip_widget))
640         tooltip->current_window = gtk_widget_get_tooltip_window (tooltip_widget);
641       else
642         tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
643     }
644
645   /* Position the tooltip */
646   /* FIXME: should we swap this when RTL is enabled? */
647   if (tooltip->keyboard_mode_enabled)
648     {
649       gdk_window_get_origin (tooltip_widget->window, &x, &y);
650       if (GTK_WIDGET_NO_WINDOW (tooltip_widget))
651         {
652           x += tooltip_widget->allocation.x;
653           y += tooltip_widget->allocation.y;
654         }
655
656       /* For keyboard mode we position the tooltip below the widget,
657        * right of the center of the widget.
658        */
659       x += tooltip_widget->allocation.width / 2;
660       y += tooltip_widget->allocation.height + 4;
661     }
662   else
663     {
664       guint cursor_size;
665
666       x = tooltip->last_x;
667       y = tooltip->last_y;
668
669       /* For mouse mode, we position the tooltip right of the cursor,
670        * a little below the cursor's center.
671        */
672       cursor_size = gdk_display_get_default_cursor_size (display);
673       x += cursor_size / 2;
674       y += cursor_size / 2;
675     }
676
677   screen = gtk_widget_get_screen (tooltip_widget);
678
679   if (screen != gtk_widget_get_screen (tooltip->window))
680     {
681       g_signal_handlers_disconnect_by_func (display,
682                                             gtk_tooltip_display_closed,
683                                             tooltip);
684
685       gtk_window_set_screen (GTK_WINDOW (tooltip->window), screen);
686
687       g_signal_connect (display, "closed",
688                         G_CALLBACK (gtk_tooltip_display_closed), tooltip);
689     }
690
691   tooltip->tooltip_widget = tooltip_widget;
692
693   /* Show it */
694   if (tooltip->current_window)
695     {
696       gint monitor_num;
697       GdkRectangle monitor;
698       GtkRequisition requisition;
699
700       gtk_widget_size_request (GTK_WIDGET (tooltip->current_window),
701                                &requisition);
702
703       monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
704       gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
705
706       if (x + requisition.width > monitor.x + monitor.width)
707         x -= x - (monitor.x + monitor.width) + requisition.width;
708       else if (x < monitor.x)
709         x = monitor.x;
710
711       if (y + requisition.height > monitor.y + monitor.height)
712         y -= y - (monitor.y + monitor.height) + requisition.height;
713
714       gtk_window_move (GTK_WINDOW (tooltip->current_window), x, y);
715       gtk_widget_show (GTK_WIDGET (tooltip->current_window));
716     }
717
718   /* Now a tooltip is visible again on the display, make sure browse
719    * mode is enabled.
720    */
721   tooltip->browse_mode_enabled = TRUE;
722   if (tooltip->browse_mode_timeout_id)
723     {
724       g_source_remove (tooltip->browse_mode_timeout_id);
725       tooltip->browse_mode_timeout_id = 0;
726     }
727 }
728
729 static void
730 gtk_tooltip_hide_tooltip (GtkTooltip *tooltip)
731 {
732   if (!tooltip)
733     return;
734
735   if (tooltip->timeout_id)
736     {
737       g_source_remove (tooltip->timeout_id);
738       tooltip->timeout_id = 0;
739     }
740
741   if (!GTK_TOOLTIP_VISIBLE (tooltip))
742     return;
743
744   tooltip->tooltip_widget = NULL;
745
746   if (!tooltip->keyboard_mode_enabled)
747     {
748       guint timeout;
749       GtkSettings *settings;
750
751       settings = gtk_widget_get_settings (GTK_WIDGET (tooltip->window));
752
753       g_object_get (settings,
754                     "gtk-tooltip-browse-mode-timeout", &timeout,
755                     NULL);
756
757       /* The tooltip is gone, after (by default, should be configurable) 500ms
758        * we want to turn off browse mode
759        */
760       if (!tooltip->browse_mode_timeout_id)
761         tooltip->browse_mode_timeout_id =
762           gdk_threads_add_timeout_full (0, timeout,
763                                         tooltip_browse_mode_expired,
764                                         g_object_ref (tooltip),
765                                         g_object_unref);
766     }
767   else
768     {
769       if (tooltip->browse_mode_timeout_id)
770         {
771           g_source_remove (tooltip->browse_mode_timeout_id);
772           tooltip->browse_mode_timeout_id = 0;
773         }
774     }
775
776   if (tooltip->current_window)
777     {
778       gtk_widget_hide (GTK_WIDGET (tooltip->current_window));
779       tooltip->current_window = NULL;
780     }
781 }
782
783 static gint
784 tooltip_popup_timeout (gpointer data)
785 {
786   GdkDisplay *display;
787   GtkTooltip *tooltip;
788
789   display = GDK_DISPLAY_OBJECT (data);
790
791   gtk_tooltip_show_tooltip (display);
792
793   tooltip = g_object_get_data (G_OBJECT (display),
794                                "gdk-display-current-tooltip");
795   tooltip->timeout_id = 0;
796
797   return FALSE;
798 }
799
800 static void
801 gtk_tooltip_start_delay (GdkDisplay *display)
802 {
803   guint timeout;
804   GtkTooltip *tooltip;
805   GtkSettings *settings;
806
807   tooltip = g_object_get_data (G_OBJECT (display),
808                                "gdk-display-current-tooltip");
809
810   if (tooltip && GTK_TOOLTIP_VISIBLE (tooltip))
811     return;
812
813   if (tooltip->timeout_id)
814     g_source_remove (tooltip->timeout_id);
815
816   settings = gtk_widget_get_settings (GTK_WIDGET (tooltip->window));
817
818   if (tooltip->browse_mode_enabled)
819     g_object_get (settings, "gtk-tooltip-browse-timeout", &timeout, NULL);
820   else
821     g_object_get (settings, "gtk-tooltip-timeout", &timeout, NULL);
822
823   tooltip->timeout_id = gdk_threads_add_timeout_full (0, timeout,
824                                                       tooltip_popup_timeout,
825                                                       g_object_ref (display),
826                                                       g_object_unref);
827 }
828
829 void
830 _gtk_tooltip_focus_in (GtkWidget *widget)
831 {
832   gint x, y;
833   gboolean return_value = FALSE;
834   GdkDisplay *display;
835   GtkTooltip *tooltip;
836
837   /* Get current tooltip for this display */
838   display = gtk_widget_get_display (widget);
839   tooltip = g_object_get_data (G_OBJECT (display),
840                                "gdk-display-current-tooltip");
841
842   /* Check if keyboard mode is enabled at this moment */
843   if (!tooltip || !tooltip->keyboard_mode_enabled)
844     return;
845
846   if (tooltip->keyboard_widget)
847     g_object_unref (tooltip->keyboard_widget);
848
849   tooltip->keyboard_widget = g_object_ref (widget);
850
851   gdk_window_get_pointer (widget->window, &x, &y, NULL);
852
853   return_value = gtk_tooltip_run_requery (&widget, tooltip, &x, &y);
854   if (!return_value)
855     {
856       gtk_tooltip_hide_tooltip (tooltip);
857       return;
858     }
859
860   if (!tooltip->current_window)
861     {
862       if (gtk_widget_get_tooltip_window (widget))
863         tooltip->current_window = gtk_widget_get_tooltip_window (widget);
864       else
865         tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
866     }
867
868   gtk_tooltip_show_tooltip (display);
869 }
870
871 void
872 _gtk_tooltip_focus_out (GtkWidget *widget)
873 {
874   GdkDisplay *display;
875   GtkTooltip *tooltip;
876
877   /* Get current tooltip for this display */
878   display = gtk_widget_get_display (widget);
879   tooltip = g_object_get_data (G_OBJECT (display),
880                                "gdk-display-current-tooltip");
881
882   if (!tooltip || !tooltip->keyboard_mode_enabled)
883     return;
884
885   if (tooltip->keyboard_widget)
886     {
887       g_object_unref (tooltip->keyboard_widget);
888       tooltip->keyboard_widget = NULL;
889     }
890
891   gtk_tooltip_hide_tooltip (tooltip);
892 }
893
894 void
895 _gtk_tooltip_toggle_keyboard_mode (GtkWidget *widget)
896 {
897   GdkDisplay *display;
898   GtkTooltip *tooltip;
899
900   display = gtk_widget_get_display (widget);
901   tooltip = g_object_get_data (G_OBJECT (display),
902                                "gdk-display-current-tooltip");
903
904   if (!tooltip)
905     {
906       tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
907       g_object_set_data_full (G_OBJECT (display),
908                               "gdk-display-current-tooltip",
909                               tooltip, g_object_unref);
910     }
911
912   tooltip->keyboard_mode_enabled ^= 1;
913
914   if (tooltip->keyboard_mode_enabled)
915     {
916       tooltip->keyboard_widget = g_object_ref (widget);
917       _gtk_tooltip_focus_in (widget);
918     }
919   else
920     {
921       if (tooltip->keyboard_widget)
922         {
923           g_object_unref (tooltip->keyboard_widget);
924           tooltip->keyboard_widget = NULL;
925         }
926
927       gtk_tooltip_hide_tooltip (tooltip);
928     }
929 }
930
931 void
932 _gtk_tooltip_hide (GtkWidget *widget)
933 {
934   GtkWidget *toplevel;
935   GdkDisplay *display;
936   GtkTooltip *tooltip;
937
938   display = gtk_widget_get_display (widget);
939   tooltip = g_object_get_data (G_OBJECT (display),
940                                "gdk-display-current-tooltip");
941
942   if (!tooltip || !GTK_TOOLTIP_VISIBLE (tooltip) || !tooltip->tooltip_widget)
943     return;
944
945   toplevel = gtk_widget_get_toplevel (widget);
946
947   if (widget == tooltip->tooltip_widget
948       || toplevel->window == tooltip->toplevel_window)
949     gtk_tooltip_hide_tooltip (tooltip);
950 }
951
952 void
953 _gtk_tooltip_handle_event (GdkEvent *event)
954 {
955   gint x, y;
956   gboolean return_value = FALSE;
957   GtkWidget *has_tooltip_widget = NULL;
958   GdkDisplay *display;
959   GtkTooltip *current_tooltip;
960
961   has_tooltip_widget = find_topmost_widget_coords_from_event (event, &x, &y);
962   display = gdk_drawable_get_display (event->any.window);
963   current_tooltip = g_object_get_data (G_OBJECT (display),
964                                        "gdk-display-current-tooltip");
965
966   if (current_tooltip)
967     {
968       current_tooltip->last_window = event->any.window;
969       gdk_event_get_root_coords (event,
970                                 &current_tooltip->last_x,
971                                 &current_tooltip->last_y);
972     }
973
974   if (current_tooltip && current_tooltip->keyboard_mode_enabled)
975     {
976       has_tooltip_widget = current_tooltip->keyboard_widget;
977       if (!has_tooltip_widget)
978         return;
979
980       return_value = gtk_tooltip_run_requery (&has_tooltip_widget,
981                                               current_tooltip,
982                                               &x, &y);
983
984       if (!return_value)
985         gtk_tooltip_hide_tooltip (current_tooltip);
986       else
987         gtk_tooltip_start_delay (display);
988
989       return;
990     }
991
992   /* Always poll for a next motion event */
993   gdk_event_request_motions (&event->motion);
994
995   /* Hide the tooltip when there's no new tooltip widget */
996   if (!has_tooltip_widget)
997     {
998       if (current_tooltip && GTK_TOOLTIP_VISIBLE (current_tooltip))
999         gtk_tooltip_hide_tooltip (current_tooltip);
1000
1001       return;
1002     }
1003
1004   switch (event->type)
1005     {
1006       case GDK_BUTTON_PRESS:
1007       case GDK_2BUTTON_PRESS:
1008       case GDK_3BUTTON_PRESS:
1009       case GDK_KEY_PRESS:
1010       case GDK_DRAG_ENTER:
1011       case GDK_GRAB_BROKEN:
1012         gtk_tooltip_hide_tooltip (current_tooltip);
1013         break;
1014
1015       case GDK_MOTION_NOTIFY:
1016       case GDK_ENTER_NOTIFY:
1017       case GDK_LEAVE_NOTIFY:
1018       case GDK_SCROLL:
1019         if (current_tooltip)
1020           {
1021             return_value = gtk_tooltip_run_requery (&has_tooltip_widget,
1022                                                     current_tooltip,
1023                                                     &x, &y);
1024
1025             if (!return_value)
1026               gtk_tooltip_hide_tooltip (current_tooltip);
1027             else
1028               gtk_tooltip_start_delay (display);
1029           }
1030         else
1031           {
1032             /* Need a new tooltip for this display */
1033             current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
1034             g_object_set_data_full (G_OBJECT (display),
1035                                     "gdk-display-current-tooltip",
1036                                     current_tooltip, g_object_unref);
1037
1038             current_tooltip->last_window = event->any.window;
1039             gdk_event_get_root_coords (event,
1040                                        &current_tooltip->last_x,
1041                                        &current_tooltip->last_y);
1042
1043             gtk_tooltip_start_delay (display);
1044           }
1045         break;
1046
1047       default:
1048         break;
1049     }
1050 }