]> Pileus Git - ~andy/gtk/blob - gtk/gtktooltip.c
Make the GTK_TOOLTIP and GTK_IS_TOOLTIP macros public.
[~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 "gtkalias.h"
33
34 #include <string.h>
35
36
37 #define GTK_TOOLTIP_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TOOLTIP, GtkTooltipClass))
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  * @tooltip: 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 @markup, 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_text:
217  * @tooltip: a #GtkTooltip
218  * @text: a text string or %NULL
219  *
220  * Sets the text of the tooltip to be @text. If @text is %NULL, the label
221  * will be hidden. See also gtk_tooltip_set_markup().
222  *
223  * Since: 2.12
224  */
225 void
226 gtk_tooltip_set_text (GtkTooltip  *tooltip,
227                       const gchar *text)
228 {
229   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
230
231   gtk_label_set_text (GTK_LABEL (tooltip->label), text);
232
233   if (text)
234     gtk_widget_show (tooltip->label);
235   else
236     gtk_widget_hide (tooltip->label);
237 }
238
239 /**
240  * gtk_tooltip_set_icon:
241  * @tooltip: a #GtkTooltip
242  * @pixbuf: a #GdkPixbuf, or %NULL
243  *
244  * Sets the icon of the tooltip (which is in front of the text) to be
245  * @pixbuf.  If @pixbuf is %NULL, the image will be hidden.
246  *
247  * Since: 2.12
248  */
249 void
250 gtk_tooltip_set_icon (GtkTooltip *tooltip,
251                       GdkPixbuf  *pixbuf)
252 {
253   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
254   if (pixbuf)
255     g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
256
257   gtk_image_set_from_pixbuf (GTK_IMAGE (tooltip->image), pixbuf);
258
259   if (pixbuf)
260     gtk_widget_show (tooltip->image);
261   else
262     gtk_widget_hide (tooltip->image);
263 }
264
265 /**
266  * gtk_tooltip_set_icon_from_stock:
267  * @tooltip: a #GtkTooltip
268  * @stock_id: a stock icon name, or %NULL
269  * @size: a stock icon size
270  *
271  * Sets the icon of the tooltip (which is in front of the text) to be
272  * the stock item indicated by @stock_id with the size indicated
273  * by @size.  If @stock_id is %NULL, the image will be hidden.
274  *
275  * Since: 2.12
276  */
277 void
278 gtk_tooltip_set_icon_from_stock (GtkTooltip  *tooltip,
279                                  const gchar *stock_id,
280                                  GtkIconSize  size)
281 {
282   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
283
284   gtk_image_set_from_stock (GTK_IMAGE (tooltip->image), stock_id, size);
285
286   if (stock_id)
287     gtk_widget_show (tooltip->image);
288   else
289     gtk_widget_hide (tooltip->image);
290 }
291
292 /**
293  * gtk_tooltip_set_custom:
294  * @tooltip: a #GtkTooltip
295  * @custom_widget: a #GtkWidget
296  *
297  * Replaces the widget packed into the tooltip with @custom_widget.  
298  * By default a box with a #GtkImage and #GtkLabel is embedded in 
299  * the tooltip, which can be configured using gtk_tooltip_set_markup() 
300  * and gtk_tooltip_set_icon().
301  *
302  * Since: 2.12
303  */
304 void
305 gtk_tooltip_set_custom (GtkTooltip *tooltip,
306                         GtkWidget  *custom_widget)
307 {
308   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
309   if (custom_widget)
310     g_return_if_fail (GTK_IS_WIDGET (custom_widget));
311
312   if (tooltip->custom_widget)
313     {
314       gtk_container_remove (GTK_CONTAINER (tooltip->box),
315                             tooltip->custom_widget);
316       g_object_unref (tooltip->custom_widget);
317     }
318
319   if (custom_widget)
320     {
321       tooltip->custom_widget = g_object_ref (custom_widget);
322
323       gtk_container_add (GTK_CONTAINER (tooltip->box), custom_widget);
324       gtk_widget_show (custom_widget);
325     }
326   else
327     tooltip->custom_widget = NULL;
328 }
329
330 /**
331  * gtk_tooltip_trigger_tooltip_query:
332  * @display: a #GtkDisplay
333  *
334  * Triggers a new tooltip query on @display, in order to update the current
335  * visible tooltip, or to show/hide the current tooltip.  This function is
336  * useful to call when, for example, the state of the widget changed by a
337  * key press.
338  *
339  * Since: 2.12
340  */
341 void
342 gtk_tooltip_trigger_tooltip_query (GdkDisplay *display)
343 {
344   gint x, y;
345   GdkWindow *window;
346   GdkEvent event;
347
348   /* Trigger logic as if the mouse moved */
349   window = gdk_display_get_window_at_pointer (display, &x, &y);
350   if (!window)
351     return;
352
353   event.type = GDK_MOTION_NOTIFY;
354   event.motion.window = window;
355   event.motion.x = x;
356   event.motion.y = y;
357   event.motion.is_hint = FALSE;
358
359   _gtk_tooltip_handle_event (&event);
360 }
361
362 /* private functions */
363
364 static void
365 gtk_tooltip_reset (GtkTooltip *tooltip)
366 {
367   gtk_tooltip_set_markup (tooltip, NULL);
368   gtk_tooltip_set_icon (tooltip, NULL);
369   gtk_tooltip_set_custom (tooltip, NULL);
370 }
371
372 static gboolean
373 gtk_tooltip_paint_window (GtkTooltip *tooltip)
374 {
375   gtk_paint_flat_box (tooltip->window->style,
376                       tooltip->window->window,
377                       GTK_STATE_NORMAL,
378                       GTK_SHADOW_OUT,
379                       NULL,
380                       tooltip->window,
381                       "tooltip",
382                       0, 0,
383                       tooltip->window->allocation.width,
384                       tooltip->window->allocation.height);
385
386   return FALSE;
387 }
388
389 static void
390 gtk_tooltip_window_hide (GtkWidget *widget,
391                          gpointer   user_data)
392 {
393   GtkTooltip *tooltip = GTK_TOOLTIP (user_data);
394
395   if (tooltip->custom_widget)
396     gtk_tooltip_set_custom (tooltip, NULL);
397 }
398
399 /* event handling, etc */
400
401 struct ChildLocation
402 {
403   GtkWidget *child;
404   GtkWidget *container;
405
406   gint x;
407   gint y;
408 };
409
410 static void
411 child_location_foreach (GtkWidget *child,
412                         gpointer   data)
413 {
414   gint x, y;
415   struct ChildLocation *child_loc = data;
416
417   if (!child_loc->child &&
418       gtk_widget_translate_coordinates (child_loc->container, child,
419                                         child_loc->x, child_loc->y,
420                                         &x, &y))
421     {
422       if (x >= 0 && x < child->allocation.width
423           && y >= 0 && y < child->allocation.height)
424         {
425           if (GTK_IS_CONTAINER (child))
426             {
427               struct ChildLocation tmp = { NULL, NULL, 0, 0 };
428
429               tmp.x = x;
430               tmp.y = y;
431               tmp.container = child;
432
433               gtk_container_foreach (GTK_CONTAINER (child),
434                                      child_location_foreach, &tmp);
435
436               if (tmp.child)
437                 child_loc->child = tmp.child;
438               else
439                 child_loc->child = child;
440             }
441           else
442             child_loc->child = child;
443         }
444     }
445 }
446
447 static void
448 window_to_alloc (GtkWidget *dest_widget,
449                  gint       src_x,
450                  gint       src_y,
451                  gint      *dest_x,
452                  gint      *dest_y)
453 {
454   /* Translate from window relative to allocation relative */
455   if (!GTK_WIDGET_NO_WINDOW (dest_widget) && dest_widget->parent)
456     {
457       gint wx, wy;
458       gdk_window_get_position (dest_widget->window, &wx, &wy);
459
460       src_x += wx - dest_widget->allocation.x;
461       src_y += wy - dest_widget->allocation.y;
462     }
463   else
464     {
465       src_x -= dest_widget->allocation.x;
466       src_y -= dest_widget->allocation.y;
467     }
468
469   if (dest_x)
470     *dest_x = src_x;
471   if (dest_y)
472     *dest_y = src_y;
473 }
474
475 static GtkWidget *
476 find_widget_under_pointer (GdkWindow *window,
477                            gint      *x,
478                            gint      *y)
479 {
480   GtkWidget *event_widget;
481   struct ChildLocation child_loc = { NULL, NULL, 0, 0 };
482
483   gdk_window_get_user_data (window, (void **)&event_widget);
484
485   if (!event_widget)
486     return NULL;
487
488   child_loc.x = *x;
489   child_loc.y = *y;
490
491   while (window && window != event_widget->window)
492     {
493       gint px, py;
494
495       gdk_window_get_position (window, &px, &py);
496       child_loc.x += px;
497       child_loc.y += py;
498
499       window = gdk_window_get_parent (window);
500     }
501
502   if (GTK_WIDGET_NO_WINDOW (event_widget))
503     {
504       child_loc.x += event_widget->allocation.x;
505       child_loc.y += event_widget->allocation.y;
506     }
507
508   /* Failing to find widget->window can happen for e.g. a detached handle box;
509    * chaining ::query-tooltip up to its parent probably makes little sense,
510    * and users better implement tooltips on handle_box->child.
511    * so we simply ignore the event for tooltips here.
512    */
513   if (!window)
514     return NULL;
515
516   if (GTK_IS_CONTAINER (event_widget))
517     {
518       window_to_alloc (event_widget,
519                        child_loc.x, child_loc.y,
520                        &child_loc.x, &child_loc.y);
521
522       child_loc.container = event_widget;
523       child_loc.child = NULL;
524
525       gtk_container_foreach (GTK_CONTAINER (event_widget),
526                              child_location_foreach, &child_loc);
527
528       if (child_loc.child)
529         event_widget = child_loc.child;
530       else if (child_loc.container)
531         event_widget = child_loc.container;
532     }
533
534   if (x)
535     *x = child_loc.x;
536   if (y)
537     *y = child_loc.y;
538
539   return event_widget;
540 }
541
542 static GtkWidget *
543 find_topmost_widget_coords_from_event (GdkEvent *event,
544                                        gint     *x,
545                                        gint     *y)
546 {
547   gint tx, ty;
548   gdouble dx, dy;
549   GtkWidget *tmp;
550
551   gdk_event_get_coords (event, &dx, &dy);
552   tx = dx;
553   ty = dy;
554
555   tmp = find_widget_under_pointer (event->any.window, &tx, &ty);
556
557   if (x)
558     *x = tx;
559   if (y)
560     *y = ty;
561
562   return tmp;
563 }
564
565 static gint
566 tooltip_browse_mode_expired (gpointer data)
567 {
568   GtkTooltip *tooltip;
569
570   tooltip = GTK_TOOLTIP (data);
571
572   tooltip->browse_mode_enabled = FALSE;
573   tooltip->browse_mode_timeout_id = 0;
574
575   /* destroy tooltip */
576   g_object_set_data (G_OBJECT (gtk_widget_get_display (tooltip->window)),
577                      "gdk-display-current-tooltip", NULL);
578
579   return FALSE;
580 }
581
582 static void
583 gtk_tooltip_display_closed (GdkDisplay *display,
584                             gboolean    was_error,
585                             GtkTooltip *tooltip)
586 {
587   g_object_set (display, "gdk-display-current-tooltip", NULL);
588 }
589
590 static gboolean
591 gtk_tooltip_run_requery (GtkWidget  **widget,
592                          GtkTooltip  *tooltip,
593                          gint        *x,
594                          gint        *y)
595 {
596   gboolean has_tooltip = FALSE;
597   gboolean return_value = FALSE;
598
599   gtk_tooltip_reset (tooltip);
600
601   do
602     {
603       g_object_get (*widget,
604                     "has-tooltip", &has_tooltip,
605                     NULL);
606
607       if (has_tooltip)
608         g_signal_emit_by_name (*widget,
609                                "query-tooltip",
610                                *x, *y,
611                                tooltip->keyboard_mode_enabled,
612                                tooltip,
613                                &return_value);
614
615       if (!return_value)
616         {
617           GtkWidget *parent = (*widget)->parent;
618
619           if (parent)
620             gtk_widget_translate_coordinates (*widget, parent, *x, *y, x, y);
621
622           *widget = parent;
623         }
624       else
625         break;
626     }
627   while (*widget);
628
629   return return_value;
630 }
631
632 static void
633 gtk_tooltip_show_tooltip (GdkDisplay *display)
634 {
635   gint x, y;
636   GdkScreen *screen;
637
638   GdkWindow *window;
639   GtkWidget *tooltip_widget;
640   GtkWidget *pointer_widget;
641   GtkTooltip *tooltip;
642   gboolean has_tooltip;
643   gboolean return_value = FALSE;
644
645   tooltip = g_object_get_data (G_OBJECT (display),
646                                "gdk-display-current-tooltip");
647
648   if (tooltip->keyboard_mode_enabled)
649     {
650       pointer_widget = tooltip_widget = tooltip->keyboard_widget;
651     }
652   else
653     {
654       window = tooltip->last_window;
655
656       if (!GDK_IS_WINDOW (window))
657         return;
658
659       gdk_window_get_origin (window, &x, &y);
660       x = tooltip->last_x - x;
661       y = tooltip->last_y - y;
662
663       pointer_widget = tooltip_widget = find_widget_under_pointer (window,
664                                                                    &x, &y);
665     }
666
667   if (!tooltip_widget)
668     return;
669
670   g_object_get (tooltip_widget, "has-tooltip", &has_tooltip, NULL);
671
672   g_assert (tooltip != NULL);
673
674   return_value = gtk_tooltip_run_requery (&tooltip_widget, tooltip, &x, &y);
675   if (!return_value)
676     return;
677
678   if (!tooltip->current_window)
679     {
680       if (gtk_widget_get_tooltip_window (tooltip_widget))
681         tooltip->current_window = gtk_widget_get_tooltip_window (tooltip_widget);
682       else
683         tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
684     }
685
686   /* Position the tooltip */
687   /* FIXME: should we swap this when RTL is enabled? */
688   if (tooltip->keyboard_mode_enabled)
689     {
690       gdk_window_get_origin (tooltip_widget->window, &x, &y);
691       if (GTK_WIDGET_NO_WINDOW (tooltip_widget))
692         {
693           x += tooltip_widget->allocation.x;
694           y += tooltip_widget->allocation.y;
695         }
696
697       /* For keyboard mode we position the tooltip below the widget,
698        * right of the center of the widget.
699        */
700       x += tooltip_widget->allocation.width / 2;
701       y += tooltip_widget->allocation.height + 4;
702     }
703   else
704     {
705       guint cursor_size;
706
707       x = tooltip->last_x;
708       y = tooltip->last_y;
709
710       /* For mouse mode, we position the tooltip right of the cursor,
711        * a little below the cursor's center.
712        */
713       cursor_size = gdk_display_get_default_cursor_size (display);
714       x += cursor_size / 2;
715       y += cursor_size / 2;
716     }
717
718   screen = gtk_widget_get_screen (tooltip_widget);
719
720   if (screen != gtk_widget_get_screen (tooltip->window))
721     {
722       g_signal_handlers_disconnect_by_func (display,
723                                             gtk_tooltip_display_closed,
724                                             tooltip);
725
726       gtk_window_set_screen (GTK_WINDOW (tooltip->window), screen);
727
728       g_signal_connect (display, "closed",
729                         G_CALLBACK (gtk_tooltip_display_closed), tooltip);
730     }
731
732   tooltip->tooltip_widget = tooltip_widget;
733
734   /* Show it */
735   if (tooltip->current_window)
736     {
737       gint monitor_num;
738       GdkRectangle monitor;
739       GtkRequisition requisition;
740
741       gtk_widget_size_request (GTK_WIDGET (tooltip->current_window),
742                                &requisition);
743
744       monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
745       gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
746
747       if (x + requisition.width > monitor.x + monitor.width)
748         x -= x - (monitor.x + monitor.width) + requisition.width;
749       else if (x < monitor.x)
750         x = monitor.x;
751
752       if (y + requisition.height > monitor.y + monitor.height)
753         y -= y - (monitor.y + monitor.height) + requisition.height;
754
755       gtk_window_move (GTK_WINDOW (tooltip->current_window), x, y);
756       gtk_widget_show (GTK_WIDGET (tooltip->current_window));
757     }
758
759   /* Now a tooltip is visible again on the display, make sure browse
760    * mode is enabled.
761    */
762   tooltip->browse_mode_enabled = TRUE;
763   if (tooltip->browse_mode_timeout_id)
764     {
765       g_source_remove (tooltip->browse_mode_timeout_id);
766       tooltip->browse_mode_timeout_id = 0;
767     }
768 }
769
770 static void
771 gtk_tooltip_hide_tooltip (GtkTooltip *tooltip)
772 {
773   if (!tooltip)
774     return;
775
776   if (tooltip->timeout_id)
777     {
778       g_source_remove (tooltip->timeout_id);
779       tooltip->timeout_id = 0;
780     }
781
782   if (!GTK_TOOLTIP_VISIBLE (tooltip))
783     return;
784
785   tooltip->tooltip_widget = NULL;
786
787   if (!tooltip->keyboard_mode_enabled)
788     {
789       guint timeout;
790       GtkSettings *settings;
791
792       settings = gtk_widget_get_settings (GTK_WIDGET (tooltip->window));
793
794       g_object_get (settings,
795                     "gtk-tooltip-browse-mode-timeout", &timeout,
796                     NULL);
797
798       /* The tooltip is gone, after (by default, should be configurable) 500ms
799        * we want to turn off browse mode
800        */
801       if (!tooltip->browse_mode_timeout_id)
802         tooltip->browse_mode_timeout_id =
803           gdk_threads_add_timeout_full (0, timeout,
804                                         tooltip_browse_mode_expired,
805                                         g_object_ref (tooltip),
806                                         g_object_unref);
807     }
808   else
809     {
810       if (tooltip->browse_mode_timeout_id)
811         {
812           g_source_remove (tooltip->browse_mode_timeout_id);
813           tooltip->browse_mode_timeout_id = 0;
814         }
815     }
816
817   if (tooltip->current_window)
818     {
819       gtk_widget_hide (GTK_WIDGET (tooltip->current_window));
820       tooltip->current_window = NULL;
821     }
822 }
823
824 static gint
825 tooltip_popup_timeout (gpointer data)
826 {
827   GdkDisplay *display;
828   GtkTooltip *tooltip;
829
830   display = GDK_DISPLAY_OBJECT (data);
831
832   gtk_tooltip_show_tooltip (display);
833
834   tooltip = g_object_get_data (G_OBJECT (display),
835                                "gdk-display-current-tooltip");
836   tooltip->timeout_id = 0;
837
838   return FALSE;
839 }
840
841 static void
842 gtk_tooltip_start_delay (GdkDisplay *display)
843 {
844   guint timeout;
845   GtkTooltip *tooltip;
846   GtkSettings *settings;
847
848   tooltip = g_object_get_data (G_OBJECT (display),
849                                "gdk-display-current-tooltip");
850
851   if (tooltip && GTK_TOOLTIP_VISIBLE (tooltip))
852     return;
853
854   if (tooltip->timeout_id)
855     g_source_remove (tooltip->timeout_id);
856
857   settings = gtk_widget_get_settings (GTK_WIDGET (tooltip->window));
858
859   if (tooltip->browse_mode_enabled)
860     g_object_get (settings, "gtk-tooltip-browse-timeout", &timeout, NULL);
861   else
862     g_object_get (settings, "gtk-tooltip-timeout", &timeout, NULL);
863
864   tooltip->timeout_id = gdk_threads_add_timeout_full (0, timeout,
865                                                       tooltip_popup_timeout,
866                                                       g_object_ref (display),
867                                                       g_object_unref);
868 }
869
870 void
871 _gtk_tooltip_focus_in (GtkWidget *widget)
872 {
873   gint x, y;
874   gboolean return_value = FALSE;
875   GdkDisplay *display;
876   GtkTooltip *tooltip;
877
878   /* Get current tooltip for this display */
879   display = gtk_widget_get_display (widget);
880   tooltip = g_object_get_data (G_OBJECT (display),
881                                "gdk-display-current-tooltip");
882
883   /* Check if keyboard mode is enabled at this moment */
884   if (!tooltip || !tooltip->keyboard_mode_enabled)
885     return;
886
887   if (tooltip->keyboard_widget)
888     g_object_unref (tooltip->keyboard_widget);
889
890   tooltip->keyboard_widget = g_object_ref (widget);
891
892   gdk_window_get_pointer (widget->window, &x, &y, NULL);
893
894   return_value = gtk_tooltip_run_requery (&widget, tooltip, &x, &y);
895   if (!return_value)
896     {
897       gtk_tooltip_hide_tooltip (tooltip);
898       return;
899     }
900
901   if (!tooltip->current_window)
902     {
903       if (gtk_widget_get_tooltip_window (widget))
904         tooltip->current_window = gtk_widget_get_tooltip_window (widget);
905       else
906         tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
907     }
908
909   gtk_tooltip_show_tooltip (display);
910 }
911
912 void
913 _gtk_tooltip_focus_out (GtkWidget *widget)
914 {
915   GdkDisplay *display;
916   GtkTooltip *tooltip;
917
918   /* Get current tooltip for this display */
919   display = gtk_widget_get_display (widget);
920   tooltip = g_object_get_data (G_OBJECT (display),
921                                "gdk-display-current-tooltip");
922
923   if (!tooltip || !tooltip->keyboard_mode_enabled)
924     return;
925
926   if (tooltip->keyboard_widget)
927     {
928       g_object_unref (tooltip->keyboard_widget);
929       tooltip->keyboard_widget = NULL;
930     }
931
932   gtk_tooltip_hide_tooltip (tooltip);
933 }
934
935 void
936 _gtk_tooltip_toggle_keyboard_mode (GtkWidget *widget)
937 {
938   GdkDisplay *display;
939   GtkTooltip *tooltip;
940
941   display = gtk_widget_get_display (widget);
942   tooltip = g_object_get_data (G_OBJECT (display),
943                                "gdk-display-current-tooltip");
944
945   if (!tooltip)
946     {
947       tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
948       g_object_set_data_full (G_OBJECT (display),
949                               "gdk-display-current-tooltip",
950                               tooltip, g_object_unref);
951     }
952
953   tooltip->keyboard_mode_enabled ^= 1;
954
955   if (tooltip->keyboard_mode_enabled)
956     {
957       tooltip->keyboard_widget = g_object_ref (widget);
958       _gtk_tooltip_focus_in (widget);
959     }
960   else
961     {
962       if (tooltip->keyboard_widget)
963         {
964           g_object_unref (tooltip->keyboard_widget);
965           tooltip->keyboard_widget = NULL;
966         }
967
968       gtk_tooltip_hide_tooltip (tooltip);
969     }
970 }
971
972 void
973 _gtk_tooltip_hide (GtkWidget *widget)
974 {
975   GtkWidget *toplevel;
976   GdkDisplay *display;
977   GtkTooltip *tooltip;
978
979   display = gtk_widget_get_display (widget);
980   tooltip = g_object_get_data (G_OBJECT (display),
981                                "gdk-display-current-tooltip");
982
983   if (!tooltip || !GTK_TOOLTIP_VISIBLE (tooltip) || !tooltip->tooltip_widget)
984     return;
985
986   toplevel = gtk_widget_get_toplevel (widget);
987
988   if (widget == tooltip->tooltip_widget
989       || toplevel->window == tooltip->toplevel_window)
990     gtk_tooltip_hide_tooltip (tooltip);
991 }
992
993 void
994 _gtk_tooltip_handle_event (GdkEvent *event)
995 {
996   gint x, y;
997   gboolean return_value = FALSE;
998   GtkWidget *has_tooltip_widget = NULL;
999   GdkDisplay *display;
1000   GtkTooltip *current_tooltip;
1001
1002   has_tooltip_widget = find_topmost_widget_coords_from_event (event, &x, &y);
1003   display = gdk_drawable_get_display (event->any.window);
1004   current_tooltip = g_object_get_data (G_OBJECT (display),
1005                                        "gdk-display-current-tooltip");
1006
1007   if (current_tooltip)
1008     {
1009       current_tooltip->last_window = event->any.window;
1010       gdk_event_get_root_coords (event,
1011                                 &current_tooltip->last_x,
1012                                 &current_tooltip->last_y);
1013     }
1014
1015   if (current_tooltip && current_tooltip->keyboard_mode_enabled)
1016     {
1017       has_tooltip_widget = current_tooltip->keyboard_widget;
1018       if (!has_tooltip_widget)
1019         return;
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       return;
1031     }
1032
1033   /* Always poll for a next motion event */
1034   gdk_event_request_motions (&event->motion);
1035
1036   /* Hide the tooltip when there's no new tooltip widget */
1037   if (!has_tooltip_widget)
1038     {
1039       if (current_tooltip && GTK_TOOLTIP_VISIBLE (current_tooltip))
1040         gtk_tooltip_hide_tooltip (current_tooltip);
1041
1042       return;
1043     }
1044
1045   switch (event->type)
1046     {
1047       case GDK_BUTTON_PRESS:
1048       case GDK_2BUTTON_PRESS:
1049       case GDK_3BUTTON_PRESS:
1050       case GDK_KEY_PRESS:
1051       case GDK_DRAG_ENTER:
1052       case GDK_GRAB_BROKEN:
1053         gtk_tooltip_hide_tooltip (current_tooltip);
1054         break;
1055
1056       case GDK_MOTION_NOTIFY:
1057       case GDK_ENTER_NOTIFY:
1058       case GDK_LEAVE_NOTIFY:
1059       case GDK_SCROLL:
1060         if (current_tooltip)
1061           {
1062             return_value = gtk_tooltip_run_requery (&has_tooltip_widget,
1063                                                     current_tooltip,
1064                                                     &x, &y);
1065
1066             if (!return_value)
1067               gtk_tooltip_hide_tooltip (current_tooltip);
1068             else
1069               gtk_tooltip_start_delay (display);
1070           }
1071         else
1072           {
1073             /* Need a new tooltip for this display */
1074             current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
1075             g_object_set_data_full (G_OBJECT (display),
1076                                     "gdk-display-current-tooltip",
1077                                     current_tooltip, g_object_unref);
1078
1079             current_tooltip->last_window = event->any.window;
1080             gdk_event_get_root_coords (event,
1081                                        &current_tooltip->last_x,
1082                                        &current_tooltip->last_y);
1083
1084             gtk_tooltip_start_delay (display);
1085           }
1086         break;
1087
1088       default:
1089         break;
1090     }
1091 }
1092
1093
1094 #define __GTK_TOOLTIP_C__
1095 #include "gtkaliasdef.c"