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