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