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