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