]> Pileus Git - ~andy/gtk/blob - gtk/gtktooltip.c
cleanup/refactor some leftover code.
[~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
501   gdk_event_get_coords (event, &dx, &dy);
502   tx = dx;
503   ty = dy;
504
505   tmp = find_widget_under_pointer (event->any.window, &tx, &ty);
506
507   if (x)
508     *x = tx;
509   if (y)
510     *y = ty;
511
512   return tmp;
513 }
514
515 static gint
516 tooltip_browse_mode_expired (gpointer data)
517 {
518   GtkTooltip *tooltip;
519
520   tooltip = GTK_TOOLTIP (data);
521
522   tooltip->browse_mode_enabled = FALSE;
523   tooltip->browse_mode_timeout_id = 0;
524
525   /* destroy tooltip */
526   g_object_set_data (G_OBJECT (gtk_widget_get_display (tooltip->window)),
527                      "gdk-display-current-tooltip", NULL);
528
529   return FALSE;
530 }
531
532 static void
533 gtk_tooltip_display_closed (GdkDisplay *display,
534                             gboolean    was_error,
535                             GtkTooltip *tooltip)
536 {
537   g_object_set (display, "gdk-display-current-tooltip", NULL);
538 }
539
540 static gboolean
541 gtk_tooltip_run_requery (GtkWidget  **widget,
542                          GtkTooltip  *tooltip,
543                          gint        *x,
544                          gint        *y)
545 {
546   gboolean has_tooltip = FALSE;
547   gboolean return_value = FALSE;
548
549   gtk_tooltip_reset (tooltip);
550
551   do
552     {
553       g_object_get (*widget,
554                     "has-tooltip", &has_tooltip,
555                     NULL);
556
557       if (has_tooltip)
558         g_signal_emit_by_name (*widget,
559                                "query-tooltip",
560                                *x, *y,
561                                tooltip->keyboard_mode_enabled,
562                                tooltip,
563                                &return_value);
564
565       if (!return_value)
566         {
567           GtkWidget *parent = (*widget)->parent;
568
569           if (parent)
570             gtk_widget_translate_coordinates (*widget, parent, *x, *y, x, y);
571
572           *widget = parent;
573         }
574       else
575         break;
576     }
577   while (*widget);
578
579   return return_value;
580 }
581
582 static void
583 gtk_tooltip_show_tooltip (GdkDisplay *display)
584 {
585   gint x, y;
586   GdkScreen *screen;
587
588   GdkWindow *window;
589   GtkWidget *tooltip_widget;
590   GtkWidget *pointer_widget;
591   GtkTooltip *tooltip;
592   gboolean has_tooltip;
593   gboolean return_value = FALSE;
594
595   tooltip = g_object_get_data (G_OBJECT (display),
596                                "gdk-display-current-tooltip");
597
598   if (tooltip->keyboard_mode_enabled)
599     {
600       pointer_widget = tooltip_widget = tooltip->keyboard_widget;
601     }
602   else
603     {
604       window = tooltip->last_window;
605
606       gdk_window_get_origin (window, &x, &y);
607       x = tooltip->last_x - x;
608       y = tooltip->last_y - y;
609
610       if (!window)
611         return;
612
613       pointer_widget = tooltip_widget = find_widget_under_pointer (window,
614                                                                    &x, &y);
615     }
616
617   if (!tooltip_widget)
618     return;
619
620   g_object_get (tooltip_widget, "has-tooltip", &has_tooltip, NULL);
621
622   g_assert (tooltip != NULL);
623
624   return_value = gtk_tooltip_run_requery (&tooltip_widget, tooltip, &x, &y);
625   if (!return_value)
626     return;
627
628   if (!tooltip->current_window)
629     {
630       if (gtk_widget_get_tooltip_window (tooltip_widget))
631         tooltip->current_window = gtk_widget_get_tooltip_window (tooltip_widget);
632       else
633         tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
634     }
635
636   /* Position the tooltip */
637   /* FIXME: should we swap this when RTL is enabled? */
638   if (tooltip->keyboard_mode_enabled)
639     {
640       gdk_window_get_origin (tooltip_widget->window, &x, &y);
641       if (GTK_WIDGET_NO_WINDOW (tooltip_widget))
642         {
643           x += tooltip_widget->allocation.x;
644           y += tooltip_widget->allocation.y;
645         }
646
647       /* For keyboard mode we position the tooltip below the widget,
648        * right of the center of the widget.
649        */
650       x += tooltip_widget->allocation.width / 2;
651       y += tooltip_widget->allocation.height + 4;
652     }
653   else
654     {
655       guint cursor_size;
656
657       x = tooltip->last_x;
658       y = tooltip->last_y;
659
660       /* For mouse mode, we position the tooltip right of the cursor,
661        * a little below the cursor's center.
662        */
663       cursor_size = gdk_display_get_default_cursor_size (display);
664       x += cursor_size / 2;
665       y += cursor_size / 2;
666     }
667
668   screen = gtk_widget_get_screen (tooltip_widget);
669
670   if (screen != gtk_widget_get_screen (tooltip->window))
671     {
672       g_signal_handlers_disconnect_by_func (display,
673                                             gtk_tooltip_display_closed,
674                                             tooltip);
675
676       gtk_window_set_screen (GTK_WINDOW (tooltip->window), screen);
677
678       g_signal_connect (display, "closed",
679                         G_CALLBACK (gtk_tooltip_display_closed), tooltip);
680     }
681
682   tooltip->tooltip_widget = tooltip_widget;
683
684   /* Show it */
685   if (tooltip->current_window)
686     {
687       gint monitor_num;
688       GdkRectangle monitor;
689       GtkRequisition requisition;
690
691       gtk_widget_size_request (GTK_WIDGET (tooltip->current_window),
692                                &requisition);
693
694       monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
695       gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
696
697       if (x + requisition.width > monitor.x + monitor.width)
698         x -= x - (monitor.x + monitor.width) + requisition.width;
699       else if (x < monitor.x)
700         x = monitor.x;
701
702       if (y + requisition.height > monitor.y + monitor.height)
703         y -= y - (monitor.y + monitor.height) + requisition.height;
704
705       gtk_window_move (GTK_WINDOW (tooltip->current_window), x, y);
706       gtk_widget_show (GTK_WIDGET (tooltip->current_window));
707     }
708
709   /* Now a tooltip is visible again on the display, make sure browse
710    * mode is enabled.
711    */
712   tooltip->browse_mode_enabled = TRUE;
713   if (tooltip->browse_mode_timeout_id)
714     {
715       g_source_remove (tooltip->browse_mode_timeout_id);
716       tooltip->browse_mode_timeout_id = 0;
717     }
718 }
719
720 static void
721 gtk_tooltip_hide_tooltip (GtkTooltip *tooltip)
722 {
723   if (!tooltip)
724     return;
725
726   if (tooltip->timeout_id)
727     {
728       g_source_remove (tooltip->timeout_id);
729       tooltip->timeout_id = 0;
730     }
731
732   if (!GTK_TOOLTIP_VISIBLE (tooltip))
733     return;
734
735   tooltip->tooltip_widget = NULL;
736
737   if (!tooltip->keyboard_mode_enabled)
738     {
739       guint timeout;
740       GtkSettings *settings;
741
742       settings = gtk_widget_get_settings (GTK_WIDGET (tooltip->window));
743
744       g_object_get (settings,
745                     "gtk-tooltip-browse-mode-timeout", &timeout,
746                     NULL);
747
748       /* The tooltip is gone, after (by default, should be configurable) 500ms
749        * we want to turn off browse mode
750        */
751       if (!tooltip->browse_mode_timeout_id)
752         tooltip->browse_mode_timeout_id =
753           gdk_threads_add_timeout_full (0, timeout,
754                                         tooltip_browse_mode_expired,
755                                         g_object_ref (tooltip),
756                                         g_object_unref);
757     }
758   else
759     {
760       if (tooltip->browse_mode_timeout_id)
761         {
762           g_source_remove (tooltip->browse_mode_timeout_id);
763           tooltip->browse_mode_timeout_id = 0;
764         }
765     }
766
767   if (tooltip->current_window)
768     {
769       gtk_widget_hide (GTK_WIDGET (tooltip->current_window));
770       tooltip->current_window = NULL;
771     }
772 }
773
774 static gint
775 tooltip_popup_timeout (gpointer data)
776 {
777   GdkDisplay *display;
778   GtkTooltip *tooltip;
779
780   display = GDK_DISPLAY_OBJECT (data);
781
782   gtk_tooltip_show_tooltip (display);
783
784   tooltip = g_object_get_data (G_OBJECT (display),
785                                "gdk-display-current-tooltip");
786   tooltip->timeout_id = 0;
787
788   return FALSE;
789 }
790
791 static void
792 gtk_tooltip_start_delay (GdkDisplay *display)
793 {
794   guint timeout;
795   GtkTooltip *tooltip;
796   GtkSettings *settings;
797
798   tooltip = g_object_get_data (G_OBJECT (display),
799                                "gdk-display-current-tooltip");
800
801   if (tooltip && GTK_TOOLTIP_VISIBLE (tooltip))
802     return;
803
804   if (tooltip->timeout_id)
805     g_source_remove (tooltip->timeout_id);
806
807   settings = gtk_widget_get_settings (GTK_WIDGET (tooltip->window));
808
809   if (tooltip->browse_mode_enabled)
810     g_object_get (settings, "gtk-tooltip-browse-timeout", &timeout, NULL);
811   else
812     g_object_get (settings, "gtk-tooltip-timeout", &timeout, NULL);
813
814   tooltip->timeout_id = gdk_threads_add_timeout_full (0, timeout,
815                                                       tooltip_popup_timeout,
816                                                       g_object_ref (display),
817                                                       g_object_unref);
818 }
819
820 void
821 _gtk_tooltip_focus_in (GtkWidget *widget)
822 {
823   gint x, y;
824   gboolean return_value = FALSE;
825   GdkDisplay *display;
826   GtkTooltip *tooltip;
827
828   /* Get current tooltip for this display */
829   display = gtk_widget_get_display (widget);
830   tooltip = g_object_get_data (G_OBJECT (display),
831                                "gdk-display-current-tooltip");
832
833   /* Check if keyboard mode is enabled at this moment */
834   if (!tooltip || !tooltip->keyboard_mode_enabled)
835     return;
836
837   if (tooltip->keyboard_widget)
838     g_object_unref (tooltip->keyboard_widget);
839
840   tooltip->keyboard_widget = g_object_ref (widget);
841
842   gdk_window_get_pointer (widget->window, &x, &y, NULL);
843
844   return_value = gtk_tooltip_run_requery (&widget, tooltip, &x, &y);
845   if (!return_value)
846     {
847       gtk_tooltip_hide_tooltip (tooltip);
848       return;
849     }
850
851   if (!tooltip->current_window)
852     {
853       if (gtk_widget_get_tooltip_window (widget))
854         tooltip->current_window = gtk_widget_get_tooltip_window (widget);
855       else
856         tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
857     }
858
859   gtk_tooltip_show_tooltip (display);
860 }
861
862 void
863 _gtk_tooltip_focus_out (GtkWidget *widget)
864 {
865   GdkDisplay *display;
866   GtkTooltip *tooltip;
867
868   /* Get current tooltip for this display */
869   display = gtk_widget_get_display (widget);
870   tooltip = g_object_get_data (G_OBJECT (display),
871                                "gdk-display-current-tooltip");
872
873   if (!tooltip || !tooltip->keyboard_mode_enabled)
874     return;
875
876   if (tooltip->keyboard_widget)
877     {
878       g_object_unref (tooltip->keyboard_widget);
879       tooltip->keyboard_widget = NULL;
880     }
881
882   gtk_tooltip_hide_tooltip (tooltip);
883 }
884
885 void
886 _gtk_tooltip_toggle_keyboard_mode (GtkWidget *widget)
887 {
888   GdkDisplay *display;
889   GtkTooltip *tooltip;
890
891   display = gtk_widget_get_display (widget);
892   tooltip = g_object_get_data (G_OBJECT (display),
893                                "gdk-display-current-tooltip");
894
895   if (!tooltip)
896     {
897       tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
898       g_object_set_data_full (G_OBJECT (display),
899                               "gdk-display-current-tooltip",
900                               tooltip, g_object_unref);
901     }
902
903   tooltip->keyboard_mode_enabled ^= 1;
904
905   if (tooltip->keyboard_mode_enabled)
906     {
907       tooltip->keyboard_widget = g_object_ref (widget);
908       _gtk_tooltip_focus_in (widget);
909     }
910   else
911     {
912       if (tooltip->keyboard_widget)
913         {
914           g_object_unref (tooltip->keyboard_widget);
915           tooltip->keyboard_widget = NULL;
916         }
917
918       gtk_tooltip_hide_tooltip (tooltip);
919     }
920 }
921
922 void
923 _gtk_tooltip_hide (GtkWidget *widget)
924 {
925   GtkWidget *toplevel;
926   GdkDisplay *display;
927   GtkTooltip *tooltip;
928
929   display = gtk_widget_get_display (widget);
930   tooltip = g_object_get_data (G_OBJECT (display),
931                                "gdk-display-current-tooltip");
932
933   if (!tooltip || !GTK_TOOLTIP_VISIBLE (tooltip) || !tooltip->tooltip_widget)
934     return;
935
936   toplevel = gtk_widget_get_toplevel (widget);
937
938   if (widget == tooltip->tooltip_widget
939       || toplevel->window == tooltip->toplevel_window)
940     gtk_tooltip_hide_tooltip (tooltip);
941 }
942
943 void
944 _gtk_tooltip_handle_event (GdkEvent *event)
945 {
946   gint x, y;
947   gboolean return_value = FALSE;
948   GtkWidget *has_tooltip_widget = NULL;
949   GdkDisplay *display;
950   GtkTooltip *current_tooltip;
951
952   has_tooltip_widget = find_topmost_widget_coords_from_event (event, &x, &y);
953   display = gdk_drawable_get_display (event->any.window);
954   current_tooltip = g_object_get_data (G_OBJECT (display),
955                                        "gdk-display-current-tooltip");
956
957   if (current_tooltip)
958     {
959       current_tooltip->last_window = event->any.window;
960       gdk_event_get_root_coords (event,
961                                 &current_tooltip->last_x,
962                                 &current_tooltip->last_y);
963     }
964
965   if (current_tooltip && current_tooltip->keyboard_mode_enabled)
966     {
967       has_tooltip_widget = current_tooltip->keyboard_widget;
968       if (!has_tooltip_widget)
969         return;
970
971       return_value = gtk_tooltip_run_requery (&has_tooltip_widget,
972                                               current_tooltip,
973                                               &x, &y);
974
975       if (!return_value)
976         gtk_tooltip_hide_tooltip (current_tooltip);
977       else
978         gtk_tooltip_start_delay (display);
979
980       return;
981     }
982
983   /* Always poll for a next motion event */
984   gdk_event_request_motions (&event->motion);
985
986   /* Hide the tooltip when there's no new tooltip widget */
987   if (!has_tooltip_widget)
988     {
989       if (current_tooltip && GTK_TOOLTIP_VISIBLE (current_tooltip))
990         gtk_tooltip_hide_tooltip (current_tooltip);
991
992       return;
993     }
994
995   switch (event->type)
996     {
997       case GDK_BUTTON_PRESS:
998       case GDK_2BUTTON_PRESS:
999       case GDK_3BUTTON_PRESS:
1000       case GDK_KEY_PRESS:
1001       case GDK_DRAG_ENTER:
1002       case GDK_GRAB_BROKEN:
1003         gtk_tooltip_hide_tooltip (current_tooltip);
1004         break;
1005
1006       case GDK_MOTION_NOTIFY:
1007       case GDK_ENTER_NOTIFY:
1008       case GDK_LEAVE_NOTIFY:
1009       case GDK_SCROLL:
1010         if (current_tooltip)
1011           {
1012             return_value = gtk_tooltip_run_requery (&has_tooltip_widget,
1013                                                     current_tooltip,
1014                                                     &x, &y);
1015
1016             if (!return_value)
1017               gtk_tooltip_hide_tooltip (current_tooltip);
1018             else
1019               gtk_tooltip_start_delay (display);
1020           }
1021         else
1022           {
1023             /* Need a new tooltip for this display */
1024             current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
1025             g_object_set_data_full (G_OBJECT (display),
1026                                     "gdk-display-current-tooltip",
1027                                     current_tooltip, g_object_unref);
1028
1029             current_tooltip->last_window = event->any.window;
1030             gdk_event_get_root_coords (event,
1031                                        &current_tooltip->last_x,
1032                                        &current_tooltip->last_y);
1033
1034             gtk_tooltip_start_delay (display);
1035           }
1036         break;
1037
1038       default:
1039         break;
1040     }
1041 }