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