Re: Tooltips patch [take 1]



On Wed, Oct 11, 2006 at 04:42:02PM +0200, Kristian Rietveld wrote:
> I am almost done processing all of your comments.  I think it's a good
> idea to post a patch here once more and then commit on HEAD and continue
> from there.

I think I finally managed to process Tim's comments, so here is another
iteration of the tooltips patch.  Biggest changes are:

 - Event handling and "widget finder" have basically been rewritten
   using Tim's comments.

 - Code to handle screen-changed and display closure has been added.

 - All timeouts are now configurable through GtkSettings.

 - gtk-tooltip RC style, and lots of small changes.


Some thoughts:

 - The positioning code might need some more love, for example I don't fully
   like the behaviour in tree view yet.  Though a tooltip following
   the mouse pointer on the tree view is probably pretty annoying.
   Also the header and row tooltips show up at the same location
   when still in browse mode (we can fix this to determine the location
   based on GdkWindow instead of GtkWidget, but that breaks other stuff
   again).

 - I will fill in the doc comments as soon as this code hits CVS.



thanks,

-kris.
Index: gtk/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/Makefile.am,v
retrieving revision 1.321
diff -u -p -r1.321 Makefile.am
--- gtk/Makefile.am	5 Oct 2006 15:51:40 -0000	1.321
+++ gtk/Makefile.am	26 Oct 2006 11:04:23 -0000
@@ -292,6 +292,7 @@ gtk_public_h_sources =          \
 	gtktoolbar.h		\
 	gtktoolbutton.h		\
 	gtktoolitem.h		\
+	gtktooltip.h		\
 	gtktooltips.h		\
 	gtktree.h		\
 	gtktreednd.h		\
@@ -561,6 +562,7 @@ gtk_base_c_sources =            \
 	gtktoolbar.c		\
 	gtktoolbutton.c		\
 	gtktoolitem.c		\
+	gtktooltip.c		\
 	gtktooltips.c		\
 	gtktree.c		\
 	gtktreedatalist.c	\
Index: gtk/gtk.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtk.h,v
retrieving revision 1.84
diff -u -p -r1.84 gtk.h
--- gtk/gtk.h	21 Apr 2006 15:09:28 -0000	1.84
+++ gtk/gtk.h	26 Oct 2006 11:04:23 -0000
@@ -177,6 +177,7 @@
 #include <gtk/gtktoolbar.h>
 #include <gtk/gtktoolbutton.h>
 #include <gtk/gtktoolitem.h>
+#include <gtk/gtktooltip.h>
 #include <gtk/gtktooltips.h>
 #include <gtk/gtktree.h>
 #include <gtk/gtktreednd.h>
Index: gtk/gtk.symbols
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtk.symbols,v
retrieving revision 1.131
diff -u -p -r1.131 gtk.symbols
--- gtk/gtk.symbols	10 Oct 2006 21:55:29 -0000	1.131
+++ gtk/gtk.symbols	26 Oct 2006 11:04:24 -0000
@@ -3947,6 +3947,17 @@ gtk_tool_item_set_visible_vertical
 #endif
 #endif
 
+#if IN_HEADER(__GTK_TOOLTIP_H__)
+#if IN_FILE(__GTK_TOOLTIP_C__)
+gtk_tooltip_get_type G_GNUC_CONST
+gtk_tooltip_set_custom
+gtk_tooltip_set_icon
+gtk_tooltip_set_icon_from_stock
+gtk_tooltip_set_markup
+gtk_tooltip_trigger_tooltip_query
+#endif
+#endif
+
 #if IN_HEADER(__GTK_TOOLTIPS_H__)
 #if IN_FILE(__GTK_TOOLTIPS_C__)
 gtk_tooltips_data_get
@@ -4489,6 +4500,7 @@ gtk_widget_get_screen
 gtk_widget_get_settings
 gtk_widget_get_size_request
 gtk_widget_get_style
+gtk_widget_get_tooltip_window
 gtk_widget_get_toplevel
 gtk_widget_get_type G_GNUC_CONST
 gtk_widget_get_visual
@@ -4560,6 +4572,7 @@ gtk_widget_set_sensitive
 gtk_widget_set_size_request
 gtk_widget_set_state
 gtk_widget_set_style
+gtk_widget_set_tooltip_window
 gtk_widget_shape_combine_mask
 gtk_widget_input_shape_combine_mask
 gtk_widget_show
@@ -4572,6 +4585,7 @@ gtk_widget_style_get_property
 gtk_widget_style_get_valist
 gtk_widget_thaw_child_notify
 gtk_widget_translate_coordinates
+gtk_widget_trigger_tooltip_query
 gtk_widget_unmap
 gtk_widget_unparent
 gtk_widget_unrealize
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.281
diff -u -p -r1.281 gtkmain.c
--- gtk/gtkmain.c	10 Oct 2006 21:51:11 -0000	1.281
+++ gtk/gtkmain.c	26 Oct 2006 11:04:24 -0000
@@ -65,6 +65,7 @@
 #include "gtksettings.h"
 #include "gtkwidget.h"
 #include "gtkwindow.h"
+#include "gtktooltip.h"
 #include "gtkprivate.h"
 #include "gtkdebug.h"
 #include "gtkalias.h"
@@ -1605,6 +1606,20 @@ gtk_main_do_event (GdkEvent *event)
     default:
       g_assert_not_reached ();
       break;
+    }
+
+  if (event->type == GDK_ENTER_NOTIFY
+      || event->type == GDK_LEAVE_NOTIFY
+      || event->type == GDK_BUTTON_PRESS
+      || event->type == GDK_2BUTTON_PRESS
+      || event->type == GDK_3BUTTON_PRESS
+      || event->type == GDK_KEY_PRESS
+      || event->type == GDK_DRAG_ENTER
+      || event->type == GDK_GRAB_BROKEN
+      || event->type == GDK_MOTION_NOTIFY
+      || event->type == GDK_SCROLL)
+    {
+      _gtk_tooltip_handle_event (event);
     }
   
   tmp_list = current_events;
Index: gtk/gtkmarshalers.list
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmarshalers.list,v
retrieving revision 1.69
diff -u -p -r1.69 gtkmarshalers.list
--- gtk/gtkmarshalers.list	11 Jun 2006 00:32:38 -0000	1.69
+++ gtk/gtkmarshalers.list	26 Oct 2006 11:04:24 -0000
@@ -37,6 +37,7 @@ BOOLEAN:OBJECT,STRING,STRING
 BOOLEAN:INT
 BOOLEAN:INT,INT
 BOOLEAN:INT,INT,INT
+BOOLEAN:INT,INT,BOOLEAN,OBJECT
 BOOLEAN:UINT
 BOOLEAN:VOID
 BOOLEAN:BOOLEAN
Index: gtk/gtkrc.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkrc.c,v
retrieving revision 1.187
diff -u -p -r1.187 gtkrc.c
--- gtk/gtkrc.c	5 Oct 2006 14:48:47 -0000	1.187
+++ gtk/gtkrc.c	26 Oct 2006 11:04:24 -0000
@@ -887,7 +887,7 @@ _gtk_rc_init (void)
 		       "\n"
 		       "class \"GtkProgressBar\" style : gtk \"gtk-default-progress-bar-style\"\n"
 		       "class \"GtkTrayIcon\" style : gtk \"gtk-default-tray-icon-style\"\n"
-		       "widget \"gtk-tooltips*\" style : gtk \"gtk-default-tooltips-style\"\n"
+		       "widget \"gtk-tooltip*\" style : gtk \"gtk-default-tooltips-style\"\n"
 		       "widget_class \"*<GtkMenuItem>*\" style : gtk \"gtk-default-menu-item-style\"\n"
 		       "widget_class \"*<GtkMenuBar>*<GtkMenuItem>\" style : gtk \"gtk-default-menu-bar-item-style\"\n"
       );
Index: gtk/gtksettings.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtksettings.c,v
retrieving revision 1.102
diff -u -p -r1.102 gtksettings.c
--- gtk/gtksettings.c	9 Sep 2006 05:22:16 -0000	1.102
+++ gtk/gtksettings.c	26 Oct 2006 11:04:25 -0000
@@ -93,6 +93,9 @@ enum {
   PROP_COLOR_SCHEME,
   PROP_ENABLE_ANIMATIONS,
   PROP_TOUCHSCREEN_MODE,
+  PROP_TOOLTIP_TIMEOUT,
+  PROP_TOOLTIP_BROWSE_TIMEOUT,
+  PROP_TOOLTIP_BROWSE_MODE_TIMEOUT,
   PROP_COLOR_HASH
 };
 
@@ -522,6 +525,39 @@ gtk_settings_class_init (GtkSettingsClas
                                              NULL);
 
   g_assert (result == PROP_TOUCHSCREEN_MODE);
+
+  result = settings_install_property_parser (class,
+					     g_param_spec_int ("gtk-tooltip-timeout",
+ 							       P_("Tooltip timeout"),
+ 							       P_("Timeout before tooltip is shown"),
+ 							       0, G_MAXINT,
+							       1500,
+ 							       GTK_PARAM_READWRITE),
+					     NULL);
+
+  g_assert (result == PROP_TOOLTIP_TIMEOUT);
+
+  result = settings_install_property_parser (class,
+					     g_param_spec_int ("gtk-tooltip-browse-timeout",
+ 							       P_("Tooltip browse timeout"),
+ 							       P_("Timeout before tooltip is shown when browse mode is enabled"),
+ 							       0, G_MAXINT,
+							       100,
+ 							       GTK_PARAM_READWRITE),
+					     NULL);
+
+  g_assert (result == PROP_TOOLTIP_BROWSE_TIMEOUT);
+
+  result = settings_install_property_parser (class,
+					     g_param_spec_int ("gtk-tooltip-browse-mode-timeout",
+ 							       P_("Tooltip browse mode timeout"),
+ 							       P_("Timeout after which browse mode is disabled"),
+ 							       0, G_MAXINT,
+							       500,
+ 							       GTK_PARAM_READWRITE),
+					     NULL);
+
+  g_assert (result == PROP_TOOLTIP_BROWSE_MODE_TIMEOUT);
 
   /**
    * GtkSettings:color-hash:
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.446
diff -u -p -r1.446 gtkwidget.c
--- gtk/gtkwidget.c	8 Oct 2006 05:07:45 -0000	1.446
+++ gtk/gtkwidget.c	26 Oct 2006 11:04:26 -0000
@@ -50,6 +50,7 @@
 #include "gdk/gdkkeysyms.h"
 #include "gtkaccessible.h"
 #include "gtktooltips.h"
+#include "gtktooltip.h"
 #include "gtkinvisible.h"
 #include "gtkalias.h"
 
@@ -121,6 +122,7 @@ enum {
   CAN_ACTIVATE_ACCEL,
   GRAB_BROKEN,
   COMPOSITED_CHANGED,
+  QUERY_TOOLTIP,
   LAST_SIGNAL
 };
 
@@ -143,7 +145,9 @@ enum {
   PROP_STYLE,
   PROP_EVENTS,
   PROP_EXTENSION_EVENTS,
-  PROP_NO_SHOW_ALL
+  PROP_NO_SHOW_ALL,
+  PROP_HAS_TOOLTIP,
+  PROP_TOOLTIP_MARKUP
 };
 
 typedef	struct	_GtkStateData	 GtkStateData;
@@ -156,7 +160,6 @@ struct _GtkStateData
   guint		use_forall : 1;
 };
 
-
 /* --- prototypes --- */
 static void	gtk_widget_class_init		(GtkWidgetClass     *klass);
 static void	gtk_widget_base_class_finalize	(GtkWidgetClass     *klass);
@@ -188,6 +191,11 @@ static void	gtk_widget_direction_changed
 						  GtkTextDirection   previous_direction);
 
 static void	gtk_widget_real_grab_focus	 (GtkWidget         *focus_widget);
+static gboolean gtk_widget_real_query_tooltip    (GtkWidget         *widget,
+						  gint               x,
+						  gint               y,
+						  gboolean           keyboard_tip,
+						  GtkTooltip        *tooltip);
 static gboolean gtk_widget_real_show_help        (GtkWidget         *widget,
                                                   GtkWidgetHelpType  help_type);
 
@@ -226,6 +234,10 @@ static GdkScreen *      gtk_widget_get_s
 static void		gtk_widget_queue_shallow_draw		(GtkWidget        *widget);
 static gboolean         gtk_widget_real_can_activate_accel      (GtkWidget *widget,
                                                                  guint      signal_id);
+
+static void             gtk_widget_set_has_tooltip              (GtkWidget *widget,
+								 gboolean   has_tooltip,
+								 gboolean   force);
      
 static void gtk_widget_set_usize_internal (GtkWidget *widget,
 					   gint       width,
@@ -257,6 +269,9 @@ static GQuark		quark_pango_context = 0;
 static GQuark		quark_rc_style = 0;
 static GQuark		quark_accessible_object = 0;
 static GQuark		quark_mnemonic_labels = 0;
+static GQuark		quark_tooltip_markup = 0;
+static GQuark		quark_has_tooltip = 0;
+static GQuark		quark_tooltip_window = 0;
 GParamSpecPool         *_gtk_widget_child_property_pool = NULL;
 GObjectNotifyContext   *_gtk_widget_child_property_notify_context = NULL;
 
@@ -332,6 +347,9 @@ gtk_widget_class_init (GtkWidgetClass *k
   quark_rc_style = g_quark_from_static_string ("gtk-rc-style");
   quark_accessible_object = g_quark_from_static_string ("gtk-accessible-object");
   quark_mnemonic_labels = g_quark_from_static_string ("gtk-mnemonic-labels");
+  quark_tooltip_markup = g_quark_from_static_string ("gtk-tooltip-markup");
+  quark_has_tooltip = g_quark_from_static_string ("gtk-has-tooltip");
+  quark_tooltip_window = g_quark_from_static_string ("gtk-tooltip-window");
 
   style_property_spec_pool = g_param_spec_pool_new (FALSE);
   _gtk_widget_child_property_pool = g_param_spec_pool_new (TRUE);
@@ -403,6 +421,7 @@ gtk_widget_class_init (GtkWidgetClass *k
   klass->screen_changed = NULL;
   klass->can_activate_accel = gtk_widget_real_can_activate_accel;
   klass->grab_broken_event = NULL;
+  klass->query_tooltip = gtk_widget_real_query_tooltip;
 
   klass->show_help = gtk_widget_real_show_help;
   
@@ -544,6 +563,21 @@ gtk_widget_class_init (GtkWidgetClass *k
  							 P_("Whether gtk_widget_show_all() should not affect this widget"),
  							 FALSE,
  							 GTK_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+				   PROP_HAS_TOOLTIP,
+				   g_param_spec_boolean ("has-tooltip",
+ 							 P_("Has tooltip"),
+ 							 P_("Whether this widget has a tooltip"),
+ 							 FALSE,
+ 							 GTK_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+				   PROP_TOOLTIP_MARKUP,
+				   g_param_spec_string ("tooltip-markup",
+ 							P_("Tooltip markup"),
+							P_("The contents of the tooltip for this widget"),
+							NULL,
+							GTK_PARAM_READWRITE));
+
   widget_signals[SHOW] =
     g_signal_new (I_("show"),
 		  G_TYPE_FROM_CLASS (gobject_class),
@@ -1397,6 +1431,18 @@ gtk_widget_class_init (GtkWidgetClass *k
 		  _gtk_marshal_BOOLEAN__BOXED,
 		  G_TYPE_BOOLEAN, 1,
 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+  widget_signals[QUERY_TOOLTIP] =
+    g_signal_new (I_("query-tooltip"),
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (GtkWidgetClass, query_tooltip),
+		  _gtk_boolean_handled_accumulator, NULL,
+		  _gtk_marshal_BOOLEAN__INT_INT_BOOLEAN_OBJECT,
+		  G_TYPE_BOOLEAN, 4,
+		  G_TYPE_INT,
+		  G_TYPE_INT,
+		  G_TYPE_BOOLEAN,
+		  GTK_TYPE_TOOLTIP);
 /**
  * GtkWidget::popup-menu
  * @widget: the object which received the signal
@@ -1677,7 +1723,10 @@ gtk_widget_set_property (GObject        
 
   switch (prop_id)
     {
+      gboolean tmp;
       guint32 saved_flags;
+      gchar *tooltip_markup;
+      GtkWindow *tooltip_window;
       
     case PROP_NAME:
       gtk_widget_set_name (widget, g_value_get_string (value));
@@ -1752,6 +1801,21 @@ gtk_widget_set_property (GObject        
     case PROP_NO_SHOW_ALL:
       gtk_widget_set_no_show_all (widget, g_value_get_boolean (value));
       break;
+    case PROP_HAS_TOOLTIP:
+      gtk_widget_set_has_tooltip (widget, g_value_get_boolean (value), FALSE);
+      break;
+    case PROP_TOOLTIP_MARKUP:
+      tooltip_markup = g_object_get_qdata (object, quark_tooltip_markup);
+      tooltip_window = g_object_get_qdata (object, quark_tooltip_window);
+
+      tooltip_markup = g_value_dup_string (value);
+
+      g_object_set_qdata_full (object, quark_tooltip_markup,
+			       tooltip_markup, g_free);
+
+      tmp = (tooltip_window != NULL || tooltip_markup != NULL);
+      gtk_widget_set_has_tooltip (widget, tmp, FALSE);
+      break;
     default:
       break;
     }
@@ -1846,6 +1910,12 @@ gtk_widget_get_property (GObject        
     case PROP_NO_SHOW_ALL:
       g_value_set_boolean (value, gtk_widget_get_no_show_all (widget));
       break;
+    case PROP_HAS_TOOLTIP:
+      g_value_set_boolean (value, GPOINTER_TO_UINT (g_object_get_qdata (object, quark_has_tooltip)));
+      break;
+    case PROP_TOOLTIP_MARKUP:
+      g_value_set_string (value, g_object_get_qdata (object, quark_tooltip_markup));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -2435,6 +2505,7 @@ gtk_widget_unmap (GtkWidget *widget)
     {
       if (GTK_WIDGET_NO_WINDOW (widget))
 	gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
+      _gtk_tooltip_hide (widget);
       g_signal_emit (widget, widget_signals[UNMAP], 0);
     }
 }
@@ -2493,7 +2564,9 @@ gtk_widget_realize (GtkWidget *widget)
       gtk_widget_ensure_style (widget);
       
       g_signal_emit (widget, widget_signals[REALIZE], 0);
-      
+
+      gtk_widget_set_has_tooltip (widget, GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (widget), quark_has_tooltip)), TRUE);
+
       if (GTK_WIDGET_HAS_SHAPE_MASK (widget))
 	{
 	  shape_info = g_object_get_qdata (G_OBJECT (widget), quark_shape_info);
@@ -2546,6 +2619,7 @@ gtk_widget_unrealize (GtkWidget *widget)
   if (GTK_WIDGET_REALIZED (widget))
     {
       g_object_ref (widget);
+      _gtk_tooltip_hide (widget);
       g_signal_emit (widget, widget_signals[UNREALIZE], 0);
       GTK_WIDGET_UNSET_FLAGS (widget, GTK_REALIZED | GTK_MAPPED);
       g_object_unref (widget);
@@ -3840,6 +3914,7 @@ gtk_widget_event_internal (GtkWidget *wi
 	  break;
 	case GDK_DESTROY:
 	  signal_num = DESTROY_EVENT;
+	  _gtk_tooltip_hide (widget);
 	  break;
 	case GDK_KEY_PRESS:
 	  signal_num = KEY_PRESS_EVENT;
@@ -3855,6 +3930,10 @@ gtk_widget_event_internal (GtkWidget *wi
 	  break;
 	case GDK_FOCUS_CHANGE:
 	  signal_num = event->focus_change.in ? FOCUS_IN_EVENT : FOCUS_OUT_EVENT;
+	  if (event->focus_change.in)
+	    _gtk_tooltip_focus_in (widget);
+	  else
+	    _gtk_tooltip_focus_out (widget);
 	  break;
 	case GDK_CONFIGURE:
 	  signal_num = CONFIGURE_EVENT;
@@ -4298,12 +4377,36 @@ gtk_widget_real_grab_focus (GtkWidget *f
 }
 
 static gboolean
+gtk_widget_real_query_tooltip (GtkWidget  *widget,
+			       gint        x,
+			       gint        y,
+			       gboolean    keyboard_tip,
+			       GtkTooltip *tooltip)
+{
+  gchar *tooltip_markup;
+  gboolean has_tooltip;
+
+  tooltip_markup = g_object_get_qdata (G_OBJECT (widget), quark_tooltip_markup);
+  has_tooltip = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (widget), quark_has_tooltip));
+
+  if (has_tooltip && tooltip_markup)
+    {
+      gtk_tooltip_set_markup (tooltip, tooltip_markup);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
 gtk_widget_real_show_help (GtkWidget        *widget,
                            GtkWidgetHelpType help_type)
 {
   if (help_type == GTK_WIDGET_HELP_TOOLTIP)
     {
       _gtk_tooltips_toggle_keyboard_mode (widget);
+      _gtk_tooltip_toggle_keyboard_mode (widget);
+
       return TRUE;
     }
   else
@@ -5179,6 +5282,7 @@ do_screen_change (GtkWidget *widget,
 	    g_object_set_qdata (G_OBJECT (widget), quark_pango_context, NULL);
 	}
       
+      _gtk_tooltip_hide (widget);
       g_signal_emit (widget, widget_signals[SCREEN_CHANGED], 0, old_screen);
     }
 }
@@ -8054,6 +8158,106 @@ gtk_widget_set_no_show_all (GtkWidget *w
     GTK_WIDGET_UNSET_FLAGS (widget, GTK_NO_SHOW_ALL);
   
   g_object_notify (G_OBJECT (widget), "no-show-all");
+}
+
+
+static void
+gtk_widget_set_has_tooltip (GtkWidget *widget,
+			    gboolean   has_tooltip,
+			    gboolean   force)
+{
+  gboolean priv_has_tooltip;
+
+  priv_has_tooltip = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (widget),
+				       quark_has_tooltip));
+
+  if (priv_has_tooltip != has_tooltip || force)
+    {
+      priv_has_tooltip = has_tooltip;
+
+      if (priv_has_tooltip)
+        {
+	  /* FIXME: do we need to walk the GdkWindow children of
+	   * widget->window?
+	   */
+	  if (GTK_WIDGET_REALIZED (widget) && GTK_WIDGET_NO_WINDOW (widget))
+	    gdk_window_set_events (widget->window,
+				   gdk_window_get_events (widget->window) |
+				   GDK_LEAVE_NOTIFY_MASK |
+				   GDK_POINTER_MOTION_MASK |
+				   GDK_POINTER_MOTION_HINT_MASK);
+
+	  if (!GTK_WIDGET_NO_WINDOW (widget))
+	      gtk_widget_add_events (widget,
+				     GDK_LEAVE_NOTIFY_MASK |
+				     GDK_POINTER_MOTION_MASK |
+				     GDK_POINTER_MOTION_HINT_MASK);
+	}
+
+      g_object_set_qdata (G_OBJECT (widget), quark_has_tooltip,
+			  GUINT_TO_POINTER (priv_has_tooltip));
+    }
+}
+
+/**
+ * gtk_widget_set_tooltip_window:
+ *
+ *
+ * Since: 2.12
+ */
+void
+gtk_widget_set_tooltip_window (GtkWidget *widget,
+			       GtkWindow *custom_window)
+{
+  gboolean tmp;
+  gchar *tooltip_markup;
+  GtkWindow *tooltip_window;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  if (custom_window)
+    g_return_if_fail (GTK_IS_WINDOW (custom_window));
+
+  tooltip_window = g_object_get_qdata (G_OBJECT (widget), quark_tooltip_window);
+  tooltip_markup = g_object_get_qdata (G_OBJECT (widget), quark_tooltip_markup);
+
+  if (custom_window)
+    g_object_ref (custom_window);
+
+  if (tooltip_window)
+    g_object_unref (tooltip_window);
+
+  tooltip_window = custom_window;
+  g_object_set_qdata_full (G_OBJECT (widget), quark_tooltip_window,
+			   tooltip_window, g_object_unref);
+
+  tmp = (tooltip_window != NULL || tooltip_markup != NULL);
+  gtk_widget_set_has_tooltip (widget, tmp, FALSE);
+
+  gtk_widget_trigger_tooltip_query (widget);
+}
+
+/**
+ * gtk_widget_get_tooltip_window:
+ *
+ * Since: 2.12
+ */
+GtkWindow *
+gtk_widget_get_tooltip_window (GtkWidget *widget)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  return g_object_get_qdata (G_OBJECT (widget), quark_tooltip_window);
+}
+
+/**
+ * gtk_widget_trigger_tooltip_query:
+ *
+ * Since: 2.12
+ */
+void
+gtk_widget_trigger_tooltip_query (GtkWidget *widget)
+{
+  gtk_tooltip_trigger_tooltip_query (gtk_widget_get_display (widget));
 }
 
 #define __GTK_WIDGET_C__
Index: gtk/gtkwidget.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.h,v
retrieving revision 1.159
diff -u -p -r1.159 gtkwidget.h
--- gtk/gtkwidget.h	25 Apr 2006 14:27:31 -0000	1.159
+++ gtk/gtkwidget.h	26 Oct 2006 11:04:26 -0000
@@ -140,6 +140,8 @@ typedef struct _GtkWidgetClass	   GtkWid
 typedef struct _GtkWidgetAuxInfo   GtkWidgetAuxInfo;
 typedef struct _GtkWidgetShapeInfo GtkWidgetShapeInfo;
 typedef struct _GtkClipboard	   GtkClipboard;
+typedef struct _GtkTooltip         GtkTooltip;
+typedef struct _GtkWindow          GtkWindow;
 typedef void     (*GtkCallback)        (GtkWidget        *widget,
 					gpointer	  data);
 
@@ -408,9 +410,14 @@ struct _GtkWidgetClass
                                  GdkEventGrabBroken  *event);
 
   void         (* composited_changed) (GtkWidget *widget);
+
+  gboolean     (* query_tooltip)      (GtkWidget  *widget,
+				       gint        x,
+				       gint        y,
+				       gboolean    keyboard_tooltip,
+				       GtkTooltip *tooltip);
 	
   /* Padding for future expansion */
-  void (*_gtk_reserved4) (void);
   void (*_gtk_reserved5) (void);
   void (*_gtk_reserved6) (void);
   void (*_gtk_reserved7) (void);
@@ -773,6 +780,12 @@ void   gtk_widget_add_mnemonic_label    
 					 GtkWidget *label);
 void   gtk_widget_remove_mnemonic_label (GtkWidget *widget,
 					 GtkWidget *label);
+
+void            gtk_widget_set_tooltip_window    (GtkWidget *widget,
+					          GtkWindow *custom_window);
+GtkWindow      *gtk_widget_get_tooltip_window    (GtkWidget *widget);
+void            gtk_widget_trigger_tooltip_query (GtkWidget *widget);
+
 
 GType           gtk_requisition_get_type (void) G_GNUC_CONST;
 GtkRequisition *gtk_requisition_copy     (const GtkRequisition *requisition);
Index: gtk/gtkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v
retrieving revision 1.85
diff -u -p -r1.85 gtkwindow.h
--- gtk/gtkwindow.h	25 Apr 2006 14:27:31 -0000	1.85
+++ gtk/gtkwindow.h	26 Oct 2006 11:04:26 -0000
@@ -44,7 +44,6 @@ G_BEGIN_DECLS
 #define GTK_WINDOW_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WINDOW, GtkWindowClass))
 
 
-typedef struct _GtkWindow             GtkWindow;
 typedef struct _GtkWindowClass        GtkWindowClass;
 typedef struct _GtkWindowGeometryInfo GtkWindowGeometryInfo;
 typedef struct _GtkWindowGroup        GtkWindowGroup;
Index: gtk/gtktooltip.c
===================================================================
RCS file: gtk/gtktooltip.c
diff -N gtk/gtktooltip.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtk/gtktooltip.c	26 Oct 2006 11:04:26 -0000
@@ -0,0 +1,1039 @@
+/* gtktooltip.c
+ *
+ * Copyright (C) 2006 Imendio AB
+ * Contact: Kristian Rietveld <kris imendio com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "gtktooltip.h"
+#include "gtkintl.h"
+#include "gtkwindow.h"
+#include "gtkmain.h"
+#include "gtklabel.h"
+#include "gtkimage.h"
+#include "gtkhbox.h"
+#include "gtkalignment.h"
+
+#include <string.h>
+
+
+#define GTK_TOOLTIP(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TOOLTIP, GtkTooltip))
+#define GTK_TOOLTIP_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TOOLTIP, GtkTooltipClass))
+#define GTK_IS_TOOLTIP(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TOOLTIP))
+#define GTK_IS_TOOLTIP_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TOOLTIP))
+#define GTK_TOOLTIP_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TOOLTIP, GtkTooltipClass))
+
+typedef struct _GtkTooltipClass   GtkTooltipClass;
+
+struct _GtkTooltip
+{
+  GObject parent_instance;
+
+  GtkWidget *window;
+  GtkWidget *alignment;
+  GtkWidget *box;
+  GtkWidget *image;
+  GtkWidget *label;
+  GtkWidget *custom_widget;
+
+  GtkWindow *current_window;
+  GtkWidget *keyboard_widget;
+
+  GtkWidget *tooltip_widget;
+  GdkWindow *toplevel_window;
+
+  guint timeout_id;
+  guint browse_mode_timeout_id;
+
+  guint visible : 1;
+  guint browse_mode_enabled : 1;
+  guint keyboard_mode_enabled : 1;
+};
+
+struct _GtkTooltipClass
+{
+  GObjectClass parent_class;
+};
+
+
+static void       gtk_tooltip_class_init           (GtkTooltipClass *klass);
+static void       gtk_tooltip_init                 (GtkTooltip      *tooltip);
+static void       gtk_tooltip_finalize             (GObject         *object);
+
+static gboolean   gtk_tooltip_paint_window         (GtkTooltip      *tooltip);
+static void       gtk_tooltip_window_hide          (GtkWidget       *widget,
+						    gpointer         user_data);
+static void       gtk_tooltip_display_closed       (GdkDisplay      *display,
+						    gboolean         was_error,
+						    GtkTooltip      *tooltip);
+
+
+G_DEFINE_TYPE (GtkTooltip, gtk_tooltip, G_TYPE_OBJECT);
+
+static void
+gtk_tooltip_class_init (GtkTooltipClass *klass)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_tooltip_finalize;
+}
+
+static void
+gtk_tooltip_init (GtkTooltip *tooltip)
+{
+  tooltip->timeout_id = 0;
+  tooltip->browse_mode_timeout_id = 0;
+
+  tooltip->visible = FALSE;
+  tooltip->browse_mode_enabled = FALSE;
+  tooltip->keyboard_mode_enabled = FALSE;
+
+  tooltip->current_window = NULL;
+  tooltip->keyboard_widget = NULL;
+
+  tooltip->tooltip_widget = NULL;
+  tooltip->toplevel_window = NULL;
+
+  tooltip->window = g_object_ref (gtk_window_new (GTK_WINDOW_POPUP));
+  gtk_window_set_type_hint (GTK_WINDOW (tooltip->window),
+			    GDK_WINDOW_TYPE_HINT_TOOLTIP);
+  gtk_widget_set_app_paintable (tooltip->window, TRUE);
+  gtk_window_set_resizable (GTK_WINDOW (tooltip->window), FALSE);
+  gtk_widget_set_name (tooltip->window, "gtk-tooltip");
+  g_signal_connect (tooltip->window, "hide",
+		    G_CALLBACK (gtk_tooltip_window_hide), tooltip);
+
+  tooltip->alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+  gtk_alignment_set_padding (GTK_ALIGNMENT (tooltip->alignment),
+			     tooltip->window->style->ythickness,
+			     tooltip->window->style->ythickness,
+			     tooltip->window->style->xthickness,
+			     tooltip->window->style->xthickness);
+  gtk_container_add (GTK_CONTAINER (tooltip->window), tooltip->alignment);
+  gtk_widget_show (tooltip->alignment);
+
+  g_signal_connect_swapped (tooltip->window, "expose_event",
+			    G_CALLBACK (gtk_tooltip_paint_window), tooltip);
+
+  tooltip->box = gtk_hbox_new (FALSE, tooltip->window->style->xthickness);
+  gtk_container_add (GTK_CONTAINER (tooltip->alignment), tooltip->box);
+  gtk_widget_show (tooltip->box);
+
+  tooltip->image = gtk_image_new ();
+  gtk_box_pack_start (GTK_BOX (tooltip->box), tooltip->image,
+		      FALSE, FALSE, 0);
+
+  tooltip->label = gtk_label_new ("");
+  gtk_box_pack_start (GTK_BOX (tooltip->box), tooltip->label,
+		      FALSE, FALSE, 0);
+
+  tooltip->custom_widget = NULL;
+}
+
+static void
+gtk_tooltip_finalize (GObject *object)
+{
+  GtkTooltip *tooltip = GTK_TOOLTIP (object);
+
+  if (tooltip->timeout_id)
+    {
+      g_source_remove (tooltip->timeout_id);
+      tooltip->timeout_id = 0;
+    }
+
+  if (tooltip->browse_mode_timeout_id)
+    {
+      g_source_remove (tooltip->browse_mode_timeout_id);
+      tooltip->browse_mode_timeout_id = 0;
+    }
+
+  if (tooltip->window)
+    {
+      GdkDisplay *display;
+
+      display = gtk_widget_get_display (tooltip->window);
+      g_signal_handlers_disconnect_by_func (display,
+					    gtk_tooltip_display_closed,
+					    tooltip);
+      gtk_widget_destroy (tooltip->window);
+    }
+}
+
+/* public API */
+
+void
+gtk_tooltip_set_markup (GtkTooltip  *tooltip,
+			const gchar *markup)
+{
+  g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
+
+  gtk_label_set_markup (GTK_LABEL (tooltip->label), markup);
+
+  if (markup)
+    gtk_widget_show (tooltip->label);
+  else
+    gtk_widget_hide (tooltip->label);
+}
+
+void
+gtk_tooltip_set_icon (GtkTooltip *tooltip,
+		      GdkPixbuf  *pixbuf)
+{
+  g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
+  if (pixbuf)
+    g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+
+  gtk_image_set_from_pixbuf (GTK_IMAGE (tooltip->image), pixbuf);
+
+  if (pixbuf)
+    gtk_widget_show (tooltip->image);
+  else
+    gtk_widget_hide (tooltip->image);
+}
+
+void
+gtk_tooltip_set_icon_from_stock (GtkTooltip  *tooltip,
+				 const gchar *stock_id,
+				 GtkIconSize  size)
+{
+  g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
+
+  gtk_image_set_from_stock (GTK_IMAGE (tooltip->image), stock_id, size);
+
+  if (stock_id)
+    gtk_widget_show (tooltip->image);
+  else
+    gtk_widget_hide (tooltip->image);
+}
+
+void
+gtk_tooltip_set_custom (GtkTooltip *tooltip,
+			GtkWidget  *custom_widget)
+{
+  g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
+  if (custom_widget)
+    g_return_if_fail (GTK_IS_WIDGET (custom_widget));
+
+  if (tooltip->custom_widget)
+    gtk_container_remove (GTK_CONTAINER (tooltip->box), tooltip->custom_widget);
+
+  if (custom_widget)
+    {
+      tooltip->custom_widget = g_object_ref (custom_widget);
+
+      gtk_container_add (GTK_CONTAINER (tooltip->box), custom_widget);
+      gtk_widget_show (custom_widget);
+    }
+  else
+    tooltip->custom_widget = NULL;
+}
+
+void
+gtk_tooltip_trigger_tooltip_query (GdkDisplay *display)
+{
+  gint x, y;
+  GdkWindow *window;
+  GdkEvent event;
+
+  /* Trigger logic as if the mouse moved */
+  window = gdk_display_get_window_at_pointer (display, &x, &y);
+  if (!window)
+    return;
+
+  event.type = GDK_MOTION_NOTIFY;
+  event.motion.window = window;
+  event.motion.x = x;
+  event.motion.y = y;
+  event.motion.is_hint = FALSE;
+
+  _gtk_tooltip_handle_event (&event);
+}
+
+/* private functions */
+
+static void
+gtk_tooltip_reset (GtkTooltip *tooltip)
+{
+  gtk_tooltip_set_markup (tooltip, NULL);
+  gtk_tooltip_set_icon (tooltip, NULL);
+  gtk_tooltip_set_custom (tooltip, NULL);
+}
+
+static gboolean
+gtk_tooltip_paint_window (GtkTooltip *tooltip)
+{
+  GtkRequisition req;
+
+  gtk_widget_size_request (tooltip->window, &req);
+  gtk_paint_flat_box (tooltip->window->style,
+		      tooltip->window->window,
+		      GTK_STATE_NORMAL,
+		      GTK_SHADOW_OUT,
+		      NULL,
+		      tooltip->window,
+		      "tooltip",
+		      0, 0,
+		      req.width, req.height);
+
+  return FALSE;
+}
+
+static void
+gtk_tooltip_window_hide (GtkWidget *widget,
+			 gpointer   user_data)
+{
+  GtkTooltip *tooltip = GTK_TOOLTIP (user_data);
+
+  if (tooltip->custom_widget)
+    {
+      g_object_unref (tooltip->custom_widget);
+      tooltip->custom_widget = NULL;
+    }
+}
+
+/* event handling, etc */
+
+struct ChildLocation
+{
+  GtkWidget *child;
+  GtkWidget *container;
+
+  gint x;
+  gint y;
+};
+
+static void
+child_location_foreach (GtkWidget *child,
+			gpointer   data)
+{
+  struct ChildLocation *child_loc = data;
+
+  if (!child_loc->child)
+    {
+      gint x, y;
+
+      gtk_widget_translate_coordinates (child_loc->container, child,
+					child_loc->x, child_loc->y,
+					&x, &y);
+
+      if (x >= 0 && x < child->allocation.width
+	  && y >= 0 && y < child->allocation.height)
+        {
+	  if (GTK_IS_CONTAINER (child))
+	    {
+	      struct ChildLocation tmp = { NULL, NULL, 0, 0 };
+
+	      tmp.x = x;
+	      tmp.y = y;
+	      tmp.container = child;
+
+	      gtk_container_foreach (GTK_CONTAINER (child),
+				     child_location_foreach, &tmp);
+
+	      if (tmp.child)
+		child_loc->child = tmp.child;
+	      else
+		child_loc->child = child;
+	    }
+	  else
+	    child_loc->child = child;
+	}
+    }
+}
+
+static void
+window_to_alloc (GtkWidget *dest_widget,
+		 gint       src_x,
+		 gint       src_y,
+		 gint      *dest_x,
+		 gint      *dest_y)
+{
+  /* Translate from window relative to allocation relative */
+  if (!GTK_WIDGET_NO_WINDOW (dest_widget) && dest_widget->parent)
+    {
+      gint wx, wy;
+      gdk_window_get_position (dest_widget->window, &wx, &wy);
+
+      src_x += wx - dest_widget->allocation.x;
+      src_y += wy - dest_widget->allocation.y;
+    }
+  else
+    {
+      src_x -= dest_widget->allocation.x;
+      src_y -= dest_widget->allocation.y;
+    }
+
+  if (dest_x)
+    *dest_x = src_x;
+  if (dest_y)
+    *dest_y = src_y;
+}
+
+static GtkWidget *
+find_widget_under_pointer (GdkWindow *window,
+			   gint      *x,
+			   gint      *y)
+{
+  GtkWidget *event_widget;
+  struct ChildLocation child_loc = { NULL, NULL, 0, 0 };
+
+  child_loc.x = *x;
+  child_loc.y = *y;
+
+  gdk_window_get_user_data (window, (void **)&event_widget);
+  if (GTK_IS_CONTAINER (event_widget))
+    {
+      child_loc.child = event_widget;
+
+      window_to_alloc (event_widget,
+		       child_loc.x, child_loc.y,
+		       &child_loc.x, &child_loc.y);
+
+      event_widget = child_loc.container = child_loc.child;
+      child_loc.child = NULL;
+
+      gtk_container_foreach (GTK_CONTAINER (event_widget),
+			     child_location_foreach, &child_loc);
+
+      if (child_loc.child)
+	event_widget = child_loc.child;
+      else if (child_loc.container)
+	event_widget = child_loc.container;
+    }
+
+  if (x)
+    *x = child_loc.x;
+  if (y)
+    *y = child_loc.y;
+
+  return event_widget;
+}
+
+static GtkWidget *
+find_has_tooltip_widget (GdkEvent *event,
+			 gint     *x,
+			 gint     *y)
+{
+  gint tx, ty;
+  gdouble dx, dy;
+  GtkWidget *tmp;
+  GtkWidget *orig;
+  gboolean has_tooltip;
+
+  gdk_event_get_coords (event, &dx, &dy);
+  tx = dx;
+  ty = dy;
+
+  orig = tmp = find_widget_under_pointer (event->any.window, &tx, &ty);
+
+  g_object_get (tmp, "has-tooltip", &has_tooltip, NULL);
+
+  /* We now have the toplevel widget under the mouse pointer, now find the
+   * widget with the tooltip in the stack
+   */
+  while (tmp && !has_tooltip)
+    {
+      tmp = tmp->parent;
+      if (tmp)
+	g_object_get (tmp, "has-tooltip", &has_tooltip, NULL);
+    }
+
+  if (tmp && (x != NULL || y != NULL))
+    {
+      if (tmp != orig)
+	gtk_widget_translate_coordinates (orig, tmp, tx, ty, x, y);
+      else
+        {
+	  if (x)
+	    *x = tx;
+	  if (y)
+	    *y = ty;
+	}
+    }
+
+  return tmp;
+}
+
+static gint
+tooltip_browse_mode_timeout (gpointer data)
+{
+  GtkTooltip *tooltip;
+
+  GDK_THREADS_ENTER ();
+
+  tooltip = GTK_TOOLTIP (data);
+
+  tooltip->browse_mode_enabled = FALSE;
+  tooltip->browse_mode_timeout_id = 0;
+
+  /* destroy tooltip */
+  g_object_set_data (G_OBJECT (gtk_widget_get_display (tooltip->window)),
+		     "gdk-display-current-tooltip", NULL);
+
+  GDK_THREADS_LEAVE ();
+
+  return FALSE;
+}
+
+static void
+gtk_tooltip_display_closed (GdkDisplay *display,
+			    gboolean    was_error,
+			    GtkTooltip *tooltip)
+{
+  g_object_set (display, "gdk-display-current-tooltip", NULL);
+}
+
+static gboolean
+gtk_tooltip_run_requery (GtkWidget  **widget,
+			 GtkTooltip  *tooltip,
+			 gint        *x,
+			 gint        *y)
+{
+  gboolean has_tooltip = FALSE;
+  gboolean return_value = FALSE;
+
+  gtk_tooltip_reset (tooltip);
+
+  do
+    {
+      g_object_get (*widget,
+		    "has-tooltip", &has_tooltip,
+		    NULL);
+
+      if (has_tooltip)
+	g_signal_emit_by_name (*widget,
+			       "query-tooltip",
+			       *x, *y,
+			       tooltip->keyboard_mode_enabled,
+			       tooltip,
+			       &return_value);
+
+      if (!return_value)
+        {
+	  GtkWidget *parent = (*widget)->parent;
+
+	  if (parent)
+	    gtk_widget_translate_coordinates (*widget, parent, *x, *y, x, y);
+
+	  *widget = parent;
+	}
+      else
+	break;
+    }
+  while (*widget);
+
+  return return_value;
+}
+
+static void
+gtk_tooltip_show_tooltip (GdkDisplay *display)
+{
+  gint x, y;
+  gint w, h;
+  gint monitor_num, px, py;
+  GdkScreen *screen;
+  GdkScreen *pointer_screen;
+  GdkRectangle monitor;
+
+  GdkWindow *window;
+  GtkWidget *tooltip_widget;
+  GtkWidget *pointer_widget;
+  GtkTooltip *tooltip;
+  gboolean has_tooltip;
+  gboolean return_value = FALSE;
+
+  tooltip = g_object_get_data (G_OBJECT (display),
+			       "gdk-display-current-tooltip");
+
+  if (tooltip->keyboard_mode_enabled)
+    {
+      pointer_widget = tooltip_widget = tooltip->keyboard_widget;
+    }
+  else
+    {
+      window = gdk_display_get_window_at_pointer (display, &x, &y);
+      if (!window)
+	return;
+
+      pointer_widget = tooltip_widget = find_widget_under_pointer (window,
+								   &x, &y);
+    }
+
+  if (!tooltip_widget)
+    return;
+
+  g_object_get (tooltip_widget, "has-tooltip", &has_tooltip, NULL);
+
+  /* We now have the toplevel widget under the mouse pointer, now find the
+   * widget with the tooltip in the stack
+   */
+  while (tooltip_widget && !has_tooltip)
+    {
+      tooltip_widget = tooltip_widget->parent;
+      if (tooltip_widget)
+	g_object_get (tooltip_widget, "has-tooltip", &has_tooltip, NULL);
+    }
+
+  if (!tooltip_widget || !has_tooltip)
+    return;
+
+  g_assert (tooltip != NULL);
+
+  if (tooltip->keyboard_mode_enabled)
+    {
+      if (tooltip_widget != pointer_widget)
+	gtk_widget_translate_coordinates (pointer_widget, tooltip_widget,
+					  x, y, &x, &y);
+    }
+
+  if (!tooltip->current_window)
+    {
+      if (gtk_widget_get_tooltip_window (tooltip_widget))
+	tooltip->current_window = gtk_widget_get_tooltip_window (tooltip_widget);
+      else
+	tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
+    }
+
+  return_value = gtk_tooltip_run_requery (&tooltip_widget, tooltip, &x, &y);
+
+  if (!return_value)
+    return;
+
+  /* Position the tooltip */
+  /* FIXME: should we swap this when RTL is enabled? */
+  if (tooltip->keyboard_mode_enabled)
+    {
+      gdk_window_get_origin (tooltip_widget->window, &x, &y);
+      if (GTK_WIDGET_NO_WINDOW (tooltip_widget))
+        {
+	  x += tooltip_widget->allocation.x;
+	  y += tooltip_widget->allocation.y;
+	}
+
+      /* For keyboard mode we position the tooltip below the widget,
+       * right of the center of the widget.
+       */
+      x += tooltip_widget->allocation.width / 2;
+      y += tooltip_widget->allocation.height + 4;
+    }
+  else
+    {
+      guint cursor_size;
+
+      gdk_window_get_pointer (gdk_screen_get_root_window (gtk_widget_get_screen (tooltip_widget)), &x, &y, NULL);
+
+      /* For mouse mode, we position the tooltip right of the cursor,
+       * a little below the cursor's center.
+       */
+      cursor_size = gdk_display_get_default_cursor_size (display);
+      x += cursor_size / 2;
+      y += cursor_size / 2;
+    }
+
+  if (tooltip->current_window)
+    {
+      GtkRequisition requisition;
+
+      gtk_widget_size_request (GTK_WIDGET (tooltip->current_window), &requisition);
+      w = requisition.width;
+      h = requisition.height;
+    }
+
+  screen = gtk_widget_get_screen (tooltip_widget);
+
+  if (screen != gtk_widget_get_screen (tooltip->window))
+    {
+      g_signal_handlers_disconnect_by_func (display,
+					    gtk_tooltip_display_closed,
+					    tooltip);
+
+      gtk_window_set_screen (GTK_WINDOW (tooltip->window), screen);
+
+      g_signal_connect (display, "closed",
+			G_CALLBACK (gtk_tooltip_display_closed), tooltip);
+    }
+
+  tooltip->tooltip_widget = tooltip_widget;
+
+  gdk_display_get_pointer (gdk_screen_get_display (screen),
+			   &pointer_screen, &px, &py, NULL);
+  if (pointer_screen != screen)
+    {
+      px = x;
+      py = y;
+    }
+  monitor_num = gdk_screen_get_monitor_at_point (screen, px, py);
+  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+  if (x > monitor.x + monitor.width)
+    x -= x - (monitor.x + monitor.width + w);
+  else if (x < monitor.x)
+    x = monitor.x;
+
+  if (y > monitor.y + monitor.height)
+    y -= y - (monitor.y + monitor.height + h);
+
+  /* Show it */
+  if (tooltip->current_window)
+    {
+      gtk_window_move (GTK_WINDOW (tooltip->current_window), x, y);
+      gtk_widget_show (GTK_WIDGET (tooltip->current_window));
+    }
+
+  /* Now a tooltip is visible again on the display, make sure browse
+   * mode is enabled.
+   */
+  tooltip->browse_mode_enabled = TRUE;
+  if (tooltip->browse_mode_timeout_id)
+    {
+      g_source_remove (tooltip->browse_mode_timeout_id);
+      tooltip->browse_mode_timeout_id = 0;
+    }
+
+  tooltip->visible = TRUE;
+}
+
+static void
+gtk_tooltip_hide_tooltip (GtkTooltip *tooltip)
+{
+  if (!tooltip || !tooltip->visible)
+    return;
+
+  tooltip->visible = FALSE;
+  tooltip->tooltip_widget = NULL;
+
+  if (tooltip->timeout_id)
+    {
+      g_source_remove (tooltip->timeout_id);
+      tooltip->timeout_id = 0;
+    }
+
+  if (!tooltip->keyboard_mode_enabled)
+    {
+      guint timeout;
+      GtkSettings *settings;
+
+      settings = gtk_widget_get_settings (GTK_WIDGET (tooltip->window));
+
+      g_object_get (settings,
+		    "gtk-tooltip-browse-mode-timeout", &timeout,
+		    NULL);
+
+      /* The tooltip is gone, after (by default, should be configurable) 500ms
+       * we want to turn off browse mode
+       */
+      if (!tooltip->browse_mode_timeout_id)
+	tooltip->browse_mode_timeout_id =
+	  g_timeout_add_full (0, timeout,
+			      tooltip_browse_mode_timeout,
+			      g_object_ref (tooltip),
+			      g_object_unref);
+    }
+  else
+    {
+      if (tooltip->browse_mode_timeout_id)
+        {
+	  g_source_remove (tooltip->browse_mode_timeout_id);
+	  tooltip->browse_mode_timeout_id = 0;
+	}
+    }
+
+  if (tooltip->current_window)
+    {
+      gtk_widget_hide (GTK_WIDGET (tooltip->current_window));
+      tooltip->current_window = NULL;
+    }
+}
+
+static gint
+tooltip_timeout (gpointer data)
+{
+  GdkDisplay *display;
+  GtkTooltip *tooltip;
+
+  GDK_THREADS_ENTER ();
+
+  display = GDK_DISPLAY_OBJECT (data);
+
+  gtk_tooltip_show_tooltip (display);
+
+  tooltip = g_object_get_data (G_OBJECT (display),
+			       "gdk-display-current-tooltip");
+  tooltip->timeout_id = 0;
+
+  GDK_THREADS_LEAVE ();
+
+  return FALSE;
+}
+
+static void
+gtk_tooltip_start_delay (GdkDisplay *display)
+{
+  guint timeout;
+  GtkTooltip *tooltip;
+  GtkSettings *settings;
+
+  tooltip = g_object_get_data (G_OBJECT (display),
+			       "gdk-display-current-tooltip");
+
+  if (tooltip && tooltip->visible)
+    return;
+
+  if (tooltip->timeout_id)
+    g_source_remove (tooltip->timeout_id);
+
+  settings = gtk_widget_get_settings (GTK_WIDGET (tooltip->window));
+
+  if (tooltip->browse_mode_enabled)
+    g_object_get (settings, "gtk-tooltip-browse-timeout", &timeout, NULL);
+  else
+    g_object_get (settings, "gtk-tooltip-timeout", &timeout, NULL);
+
+  tooltip->timeout_id = g_timeout_add_full (0, timeout,
+					    tooltip_timeout,
+					    g_object_ref (display),
+					    g_object_unref);
+}
+
+void
+_gtk_tooltip_focus_in (GtkWidget *widget)
+{
+  gint x, y;
+  gboolean has_tooltip = FALSE;
+  gboolean return_value = FALSE;
+  GdkDisplay *display;
+  GtkTooltip *tooltip;
+
+  /* Get current tooltip for this display */
+  display = gtk_widget_get_display (widget);
+  tooltip = g_object_get_data (G_OBJECT (display),
+			       "gdk-display-current-tooltip");
+
+  g_object_get (widget, "has-tooltip", &has_tooltip, NULL);
+
+  /* Check if keyboard mode is enabled at this moment */
+  if (!tooltip || !tooltip->keyboard_mode_enabled || !has_tooltip)
+    return;
+
+  if (tooltip->keyboard_widget)
+    g_object_unref (tooltip->keyboard_widget);
+
+  tooltip->keyboard_widget = g_object_ref (widget);
+
+  gdk_window_get_pointer (widget->window, &x, &y, NULL);
+
+  if (!tooltip->current_window)
+    {
+      if (gtk_widget_get_tooltip_window (widget))
+	tooltip->current_window = gtk_widget_get_tooltip_window (widget);
+      else
+	tooltip->current_window = GTK_WINDOW (GTK_TOOLTIP (tooltip)->window);
+    }
+
+  return_value = gtk_tooltip_run_requery (&widget, tooltip, &x, &y);
+
+  if (!return_value)
+    gtk_tooltip_hide_tooltip (tooltip);
+  else
+    gtk_tooltip_show_tooltip (display);
+}
+
+void
+_gtk_tooltip_focus_out (GtkWidget *widget)
+{
+  gboolean has_tooltip;
+  GdkDisplay *display;
+  GtkTooltip *tooltip;
+
+  /* Get current tooltip for this display */
+  display = gtk_widget_get_display (widget);
+  tooltip = g_object_get_data (G_OBJECT (display),
+			       "gdk-display-current-tooltip");
+
+  g_object_get (widget, "has-tooltip", &has_tooltip, NULL);
+
+  if (!tooltip)
+    return;
+
+  /* Handle case where mouse pointer moves out of the window */
+  if (!tooltip->keyboard_mode_enabled && tooltip->tooltip_widget != widget)
+    {
+      gtk_tooltip_hide_tooltip (tooltip);
+      return;
+    }
+
+  if (!tooltip || !tooltip->keyboard_mode_enabled || !has_tooltip)
+    return;
+
+  if (tooltip->keyboard_widget)
+    {
+      g_object_unref (tooltip->keyboard_widget);
+      tooltip->keyboard_widget = NULL;
+    }
+
+  /* Hide tooltip */
+  gtk_tooltip_hide_tooltip (tooltip);
+}
+
+void
+_gtk_tooltip_toggle_keyboard_mode (GtkWidget *widget)
+{
+  GdkDisplay *display;
+  GtkTooltip *tooltip;
+
+  display = gtk_widget_get_display (widget);
+  tooltip = g_object_get_data (G_OBJECT (display),
+			       "gdk-display-current-tooltip");
+
+  if (!tooltip)
+    {
+      tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
+      g_object_set_data_full (G_OBJECT (display),
+			      "gdk-display-current-tooltip",
+			      tooltip, g_object_unref);
+    }
+
+  tooltip->keyboard_mode_enabled ^= 1;
+
+  if (tooltip->keyboard_mode_enabled)
+    {
+      tooltip->keyboard_widget = g_object_ref (widget);
+      _gtk_tooltip_focus_in (widget);
+    }
+  else
+    {
+      if (tooltip->keyboard_widget)
+        {
+	  g_object_unref (tooltip->keyboard_widget);
+	  tooltip->keyboard_widget = NULL;
+	}
+
+      gtk_tooltip_hide_tooltip (tooltip);
+    }
+}
+
+void
+_gtk_tooltip_hide (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+  GdkDisplay *display;
+  GtkTooltip *tooltip;
+
+  display = gtk_widget_get_display (widget);
+  tooltip = g_object_get_data (G_OBJECT (display),
+			       "gdk-display-current-tooltip");
+
+  if (!tooltip || !tooltip->visible || !tooltip->tooltip_widget)
+    return;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+
+  if (widget == tooltip->tooltip_widget
+      || toplevel->window == tooltip->toplevel_window)
+    gtk_tooltip_hide_tooltip (tooltip);
+}
+
+void
+_gtk_tooltip_handle_event (GdkEvent *event)
+{
+  gint x, y;
+  gboolean return_value = FALSE;
+  GtkWidget *has_tooltip_widget = NULL;
+  GdkDisplay *display;
+  GtkTooltip *current_tooltip;
+
+  has_tooltip_widget = find_has_tooltip_widget (event, &x, &y);
+  display = gdk_drawable_get_display (event->any.window);
+  current_tooltip = g_object_get_data (G_OBJECT (display),
+				       "gdk-display-current-tooltip");
+
+  if (current_tooltip && current_tooltip->keyboard_mode_enabled)
+    {
+      has_tooltip_widget = current_tooltip->keyboard_widget;
+      if (!has_tooltip_widget)
+	return;
+
+      return_value = gtk_tooltip_run_requery (&has_tooltip_widget,
+					      current_tooltip,
+					      &x, &y);
+
+      if (!return_value)
+	gtk_tooltip_hide_tooltip (current_tooltip);
+      else
+	gtk_tooltip_start_delay (display);
+
+      return;
+    }
+
+  /* Always poll for a next motion event */
+  if (event->motion.is_hint)
+    gdk_window_get_pointer (event->any.window, NULL, NULL, NULL);
+
+  /* Hide the tooltip when there's no new tooltip widget */
+  if (!has_tooltip_widget)
+    {
+      if (current_tooltip && current_tooltip->visible)
+	gtk_tooltip_hide_tooltip (current_tooltip);
+
+      return;
+    }
+
+  switch (event->type)
+    {
+      case GDK_BUTTON_PRESS:
+      case GDK_2BUTTON_PRESS:
+      case GDK_3BUTTON_PRESS:
+      case GDK_KEY_PRESS:
+      case GDK_DRAG_ENTER:
+      case GDK_GRAB_BROKEN:
+	gtk_tooltip_hide_tooltip (current_tooltip);
+	break;
+
+      case GDK_MOTION_NOTIFY:
+      case GDK_ENTER_NOTIFY:
+      case GDK_LEAVE_NOTIFY:
+      case GDK_SCROLL:
+	if (current_tooltip)
+	  {
+	    return_value = gtk_tooltip_run_requery (&has_tooltip_widget,
+						    current_tooltip,
+						    &x, &y);
+
+	    if (!return_value)
+	      gtk_tooltip_hide_tooltip (current_tooltip);
+	    else
+	      gtk_tooltip_start_delay (display);
+	  }
+	else
+	  {
+	    /* Need a new tooltip for this display */
+	    current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
+	    g_object_set_data_full (G_OBJECT (display),
+				    "gdk-display-current-tooltip",
+				    current_tooltip, g_object_unref);
+
+	    gtk_tooltip_start_delay (display);
+	  }
+	break;
+
+      default:
+	break;
+    }
+}
Index: gtk/gtktooltip.h
===================================================================
RCS file: gtk/gtktooltip.h
diff -N gtk/gtktooltip.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtk/gtktooltip.h	26 Oct 2006 11:04:26 -0000
@@ -0,0 +1,55 @@
+/* gtktooltip.h
+ *
+ * Copyright (C) 2006 Imendio AB
+ * Contact: Kristian Rietveld <kris imendio com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TOOLTIP__
+#define __GTK_TOOLTIP__
+
+#include "gtkwidget.h"
+#include "gtkwindow.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_TOOLTIP                 (gtk_tooltip_get_type ())
+
+GType gtk_tooltip_get_type (void);
+
+void gtk_tooltip_set_markup            (GtkTooltip  *tooltip,
+			                const gchar *markup);
+void gtk_tooltip_set_icon              (GtkTooltip  *tooltip,
+				        GdkPixbuf   *pixbuf);
+void gtk_tooltip_set_icon_from_stock   (GtkTooltip  *tooltip,
+				        const gchar *stock_id,
+				        GtkIconSize  size);
+void gtk_tooltip_set_custom	       (GtkTooltip  *tooltip,
+				        GtkWidget   *custom_widget);
+
+void gtk_tooltip_trigger_tooltip_query (GdkDisplay  *display);
+
+
+void _gtk_tooltip_focus_in             (GtkWidget   *widget);
+void _gtk_tooltip_focus_out            (GtkWidget   *widget);
+void _gtk_tooltip_toggle_keyboard_mode (GtkWidget   *widget);
+void _gtk_tooltip_handle_event         (GdkEvent    *event);
+void _gtk_tooltip_hide                 (GtkWidget   *widget);
+
+G_END_DECLS
+
+#endif /* __GTK_TOOLTIP__ */
Index: tests/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtk+/tests/Makefile.am,v
retrieving revision 1.65
diff -u -p -r1.65 Makefile.am
--- tests/Makefile.am	28 Aug 2006 16:14:39 -0000	1.65
+++ tests/Makefile.am	26 Oct 2006 11:04:26 -0000
@@ -82,7 +82,8 @@ noinst_PROGRAMS =			\
 	pixbuf-threads			\
 	testmerge			\
 	testactions			\
-	testgrouping
+	testgrouping			\
+	testtooltips
 
 autotestfilechooser_DEPENDENCIES = $(TEST_DEPS)
 simple_DEPENDENCIES = $(TEST_DEPS)
@@ -134,6 +135,7 @@ testxinerama_DEPENDENCIES = $(TEST_DEPS)
 testmerge_DEPENDENCIES = $(TEST_DEPS)
 testactions_DEPENDENCIES = $(TEST_DEPS)
 testgrouping_DEPENDENCIES = $(TEST_DEPS)
+testtooltips_DEPENDENCIES = $(TEST_DEPS)
 
 autotestfilechooser_LDADD = $(LDADDS)
 simple_LDADD = $(LDADDS)
@@ -192,6 +194,7 @@ pixbuf_threads_LDADD = $(LDADDS) $(GLIB_
 testmerge_LDADD = $(LDADDS)
 testactions_LDADD = $(LDADDS)
 testgrouping_LDADD = $(LDADDS)
+testtooltips_LDADD = $(LDADDS)
 
 autotestfilechooser_SOURCES =	\
 	autotestfilechooser.c
@@ -265,6 +268,9 @@ testrecentchooser_SOURCES = 	\
 
 testgrouping_SOURCES =		\
 	testgrouping.c
+
+testtoooltips_SOURCES =		\
+	testtooltips.c
 
 EXTRA_DIST = 			\
 	prop-editor.h		\
Index: tests/testtooltips.c
===================================================================
RCS file: tests/testtooltips.c
diff -N tests/testtooltips.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/testtooltips.c	26 Oct 2006 11:04:26 -0000
@@ -0,0 +1,379 @@
+/* testtooltips.c: Test application for GTK+ >= 2.12 tooltips code
+ *
+ * Copyright (C) 2006  Imendio AB
+ * Contact: Kristian Rietveld <kris imendio com>
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+/* vim:sw=2
+ */
+#include <gtk/gtk.h>
+
+static gboolean
+query_tooltip_cb (GtkWidget  *widget,
+		  gint        x,
+		  gint        y,
+		  gboolean    keyboard_tip,
+		  GtkTooltip *tooltip,
+		  gpointer    data)
+{
+  gtk_tooltip_set_markup (tooltip, gtk_button_get_label (GTK_BUTTON (widget)));
+  gtk_tooltip_set_icon_from_stock (tooltip, GTK_STOCK_DELETE,
+				   GTK_ICON_SIZE_MENU);
+
+  return TRUE;
+}
+
+static gboolean
+query_tooltip_custom_cb (GtkWidget  *widget,
+			 gint        x,
+			 gint        y,
+			 gboolean    keyboard_tip,
+			 GtkTooltip *tooltip,
+			 gpointer    data)
+{
+  GdkColor color = { 0, 0, 65535 };
+  GtkWindow *window = gtk_widget_get_tooltip_window (widget);
+
+  gtk_widget_modify_bg (GTK_WIDGET (window), GTK_STATE_NORMAL, &color);
+
+  return TRUE;
+}
+
+static gboolean
+query_tooltip_text_view_cb (GtkWidget  *widget,
+			    gint        x,
+			    gint        y,
+			    gboolean    keyboard_tip,
+			    GtkTooltip *tooltip,
+			    gpointer    data)
+{
+  gint bx, by, trailing;
+  GtkTextTag *tag = data;
+  GtkTextIter iter;
+  GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+
+  gtk_text_view_window_to_buffer_coords (text_view, GTK_TEXT_WINDOW_TEXT,
+					 x, y, &bx, &by);
+  gtk_text_view_get_iter_at_position (text_view, &iter, &trailing, bx, by);
+
+  if (gtk_text_iter_has_tag (&iter, tag))
+    gtk_tooltip_set_markup (tooltip, "Tooltip on text tag");
+  else
+   return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+query_tooltip_tree_view_cb (GtkWidget  *widget,
+			    gint        x,
+			    gint        y,
+			    gboolean    keyboard_tip,
+			    GtkTooltip *tooltip,
+			    gpointer    data)
+{
+  GtkTreeIter iter;
+  GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+  GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+  GtkTreePath *path = NULL;
+  gchar *tmp;
+  gchar *pathstring;
+
+  char buffer[512];
+
+  if (keyboard_tip)
+    {
+      /* Keyboard mode */
+      gtk_tree_view_get_cursor (tree_view, &path, NULL);
+
+      if (!path)
+	return FALSE;
+    }
+  else
+    {
+      /* Mouse mode */
+      if (!gtk_tree_view_get_path_at_pos (tree_view, x, y, &path, NULL, NULL, NULL))
+        return FALSE;
+    }
+
+  gtk_tree_model_get_iter (model, &iter, path);
+  gtk_tree_model_get (model, &iter, 0, &tmp, -1);
+  pathstring = gtk_tree_path_to_string (path);
+
+  snprintf (buffer, 511, "<b>Path %s:</b> %s", pathstring, tmp);
+  gtk_tooltip_set_markup (tooltip, buffer);
+
+  gtk_tree_path_free (path);
+  g_free (pathstring);
+  g_free (tmp);
+
+  return TRUE;
+}
+
+static GtkTreeModel *
+create_model (void)
+{
+  GtkTreeStore *store;
+  GtkTreeIter iter;
+
+  store = gtk_tree_store_new (1, G_TYPE_STRING);
+
+  /* A tree store with some random words ... */
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
+				     0, "File Manager", -1);
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
+				     0, "Gossip", -1);
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
+				     0, "System Settings", -1);
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
+				     0, "The GIMP", -1);
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
+				     0, "Terminal", -1);
+  gtk_tree_store_insert_with_values (store, &iter, NULL, 0,
+				     0, "Word Processor", -1);
+
+  return GTK_TREE_MODEL (store);
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *selection,
+		      GtkWidget        *tree_view)
+{
+  gtk_widget_trigger_tooltip_query (tree_view);
+}
+
+static struct Rectangle
+{
+  gint x;
+  gint y;
+  gfloat r;
+  gfloat g;
+  gfloat b;
+  const char *tooltip;
+}
+rectangles[] =
+{
+  { 10, 10, 0.0, 0.0, 0.9, "Blue box!" },
+  { 200, 170, 1.0, 0.0, 0.0, "Red thing" },
+  { 100, 50, 0.8, 0.8, 0.0, "Yellow thing" }
+};
+
+static gboolean
+query_tooltip_drawing_area_cb (GtkWidget  *widget,
+			       gint        x,
+			       gint        y,
+			       gboolean    keyboard_tip,
+			       GtkTooltip *tooltip,
+			       gpointer    data)
+{
+  gint i;
+
+  if (keyboard_tip)
+    return FALSE;
+
+  for (i = 0; i < G_N_ELEMENTS (rectangles); i++)
+    {
+      struct Rectangle *r = &rectangles[i];
+
+      if (r->x < x && x < r->x + 50
+	  && r->y < y && y < r->y + 50)
+        {
+	  gtk_tooltip_set_markup (tooltip, r->tooltip);
+	  return TRUE;
+	}
+    }
+
+  return FALSE;
+}
+
+static gboolean
+drawing_area_expose (GtkWidget      *drawing_area,
+		     GdkEventExpose *event,
+		     gpointer        data)
+{
+  gint i;
+  cairo_t *cr;
+
+  gdk_window_get_pointer (drawing_area->window, NULL, NULL, NULL);
+
+  cr = gdk_cairo_create (drawing_area->window);
+
+  cairo_rectangle (cr, 0, 0,
+		   drawing_area->allocation.width,
+		   drawing_area->allocation.height);
+  cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+  cairo_fill (cr);
+
+  for (i = 0; i < G_N_ELEMENTS (rectangles); i++)
+    {
+      struct Rectangle *r = &rectangles[i];
+
+      cairo_rectangle (cr, r->x, r->y, 50, 50);
+      cairo_set_source_rgb (cr, r->r, r->g, r->b);
+      cairo_stroke (cr);
+
+      cairo_rectangle (cr, r->x, r->y, 50, 50);
+      cairo_set_source_rgba (cr, r->r, r->g, r->b, 0.5);
+      cairo_fill (cr);
+    }
+
+  cairo_destroy (cr);
+
+  return FALSE;
+}
+
+int
+main (int argc, char *argv[])
+{
+  GtkWidget *window;
+  GtkWidget *box;
+  GtkWidget *drawing_area;
+  GtkWidget *button;
+
+  GtkWidget *tooltip_window;
+  GtkWidget *tooltip_button;
+
+  GtkWidget *tree_view;
+  GtkTreeViewColumn *column;
+
+  GtkWidget *text_view;
+  GtkTextBuffer *buffer;
+  GtkTextIter iter;
+  GtkTextTag *tag;
+
+  gtk_init (&argc, &argv);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title (GTK_WINDOW (window), "Tooltips test");
+  gtk_container_set_border_width (GTK_CONTAINER (window), 10);
+  g_signal_connect (window, "delete_event",
+		    G_CALLBACK (gtk_main_quit), NULL);
+
+  box = gtk_vbox_new (FALSE, 3);
+  gtk_container_add (GTK_CONTAINER (window), box);
+
+  /* A check button using the tooltip-markup property */
+  button = gtk_check_button_new_with_label ("This one uses the tooltip-markup property");
+  g_object_set (button, "tooltip-markup", "Hello, I am a static tooltip.", NULL);
+  gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+  /* A check button using the query-tooltip signal */
+  button = gtk_check_button_new_with_label ("I use the query-tooltip signal");
+  g_object_set (button, "has-tooltip", TRUE, NULL);
+  g_signal_connect (button, "query-tooltip",
+		    G_CALLBACK (query_tooltip_cb), NULL);
+  gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+  /* A label */
+  button = gtk_label_new ("I am just a label");
+  gtk_label_set_selectable (GTK_LABEL (button), FALSE);
+  g_object_set (button, "tooltip-markup", "Label tooltip", NULL);
+  gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+  /* A selectable label */
+  button = gtk_label_new ("I am a selectable label");
+  gtk_label_set_selectable (GTK_LABEL (button), TRUE);
+  g_object_set (button, "tooltip-markup", "Another Label tooltip", NULL);
+  gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+  /* Another one, with a custom tooltip window */
+  button = gtk_check_button_new_with_label ("This one has a custom tooltip window!");
+  gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+  tooltip_window = gtk_window_new (GTK_WINDOW_POPUP);
+  tooltip_button = gtk_label_new ("blaat!");
+  gtk_container_add (GTK_CONTAINER (tooltip_window), tooltip_button);
+  gtk_widget_show (tooltip_button);
+
+  gtk_widget_set_tooltip_window (button, GTK_WINDOW (tooltip_window));
+  g_signal_connect (button, "query-tooltip",
+		    G_CALLBACK (query_tooltip_custom_cb), NULL);
+  g_object_set (button, "has-tooltip", TRUE, NULL);
+
+  /* An insensitive button */
+  button = gtk_button_new_with_label ("This one is insensitive");
+  gtk_widget_set_sensitive (button, FALSE);
+  g_object_set (button, "tooltip-markup", "Insensitive!", NULL);
+  gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
+
+  /* Testcases from Kris without a tree view don't exist. */
+  tree_view = gtk_tree_view_new_with_model (create_model ());
+  gtk_widget_set_size_request (tree_view, 200, 240);
+
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view),
+					       0, "Test",
+					       gtk_cell_renderer_text_new (),
+					       "text", 0,
+					       NULL);
+
+  g_object_set (tree_view, "has-tooltip", TRUE, NULL);
+  g_signal_connect (tree_view, "query-tooltip",
+		    G_CALLBACK (query_tooltip_tree_view_cb), NULL);
+  g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)),
+		    "changed", G_CALLBACK (selection_changed_cb), tree_view);
+
+  /* Set a tooltip on the column */
+  column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 0);
+  gtk_tree_view_column_set_clickable (column, TRUE);
+  g_object_set (column->button, "tooltip-markup", "Header", NULL);
+
+  gtk_box_pack_start (GTK_BOX (box), tree_view, FALSE, FALSE, 2);
+
+  /* And a text view for Matthias */
+  buffer = gtk_text_buffer_new (NULL);
+
+  gtk_text_buffer_get_end_iter (buffer, &iter);
+  gtk_text_buffer_insert (buffer, &iter, "Hello, the text ", -1);
+
+  tag = gtk_text_buffer_create_tag (buffer, "bold", NULL);
+  g_object_set (tag, "weight", PANGO_WEIGHT_BOLD, NULL);
+
+  gtk_text_buffer_get_end_iter (buffer, &iter);
+  gtk_text_buffer_insert_with_tags (buffer, &iter, "in bold", -1, tag, NULL);
+
+  gtk_text_buffer_get_end_iter (buffer, &iter);
+  gtk_text_buffer_insert (buffer, &iter, " has a tooltip!", -1);
+
+  text_view = gtk_text_view_new_with_buffer (buffer);
+  gtk_widget_set_size_request (text_view, 200, 50);
+
+  g_object_set (text_view, "has-tooltip", TRUE, NULL);
+  g_signal_connect (text_view, "query-tooltip",
+		    G_CALLBACK (query_tooltip_text_view_cb), tag);
+
+  gtk_box_pack_start (GTK_BOX (box), text_view, FALSE, FALSE, 2);
+
+  /* Drawing area */
+  drawing_area = gtk_drawing_area_new ();
+  gtk_widget_set_size_request (drawing_area, 320, 240);
+  g_object_set (drawing_area, "has-tooltip", TRUE, NULL);
+  g_signal_connect (drawing_area, "expose_event",
+		    G_CALLBACK (drawing_area_expose), NULL);
+  g_signal_connect (drawing_area, "query-tooltip",
+		    G_CALLBACK (query_tooltip_drawing_area_cb), NULL);
+  gtk_box_pack_start (GTK_BOX (box), drawing_area, FALSE, FALSE, 2);
+
+  /* Done! */
+  gtk_widget_show_all (window);
+
+  gtk_main ();
+
+  return 0;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]