Tooltips patch [take 1]



Hey,


So I finally managed to get the event handling and all in the new tooltips code
right.  Attached is a patch with a first attempt at an implementation.  Of
course there's also some TODO items left:

  * The tooltip handling code in gtktooltip.c probably needs to be
    modified to be per display.

  * What to do with gtk_tooltip_force_query_tooltip() and
    gtk_tooltip_force_query_tooltip_for_widget().  The former will look up
    the widget under the mouse pointer and immediately display that
    tooltip.  But I also see uses (and IIRC this has also been requested),
    to immediately show the tooltip of a specific widget.  This probably
    also needs to display the tooltip above/below the widget and not
    the mouse pointer.  Once we decide on a set of functions we should add
    those to the header file.

  * Also, do we want to be able to set a positioning function?

  * Some FIXMEs here and there: the positioning code needs work/polish,
    need to make the timeouts configurable, etc.

  * Right now the different things you can set on the GtkTooltip are
    packed in a box in this order: icon, markup, custom widget.  I guess
    people might want to change that order?  We could make it dependent on
    the order of the call to the _set functions, but I don't think I
    really like that idea :)

  * testtooltips.c still needs a license



thanks,

-kris.
Index: tests/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtk+/tests/Makefile.am,v
retrieving revision 1.64
diff -u -p -r1.64 Makefile.am
--- tests/Makefile.am	5 May 2006 16:08:12 -0000	1.64
+++ tests/Makefile.am	20 Jul 2006 10:08:01 -0000
@@ -81,7 +81,8 @@ noinst_PROGRAMS =			\
 	pixbuf-threads			\
 	testmerge			\
 	testactions			\
-	testgrouping
+	testgrouping			\
+	testtooltips
 
 autotestfilechooser_DEPENDENCIES = $(TEST_DEPS)
 simple_DEPENDENCIES = $(TEST_DEPS)
@@ -132,6 +133,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)
@@ -189,6 +191,7 @@ pixbuf_threads_LDADD = $(LDADDS) $(GLIB_
 testmerge_LDADD = $(LDADDS)
 testactions_LDADD = $(LDADDS)
 testgrouping_LDADD = $(LDADDS)
+testtooltips_LDADD = $(LDADDS)
 
 autotestfilechooser_SOURCES =	\
 	autotestfilechooser.c
@@ -262,6 +265,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	20 Jul 2006 10:08:02 -0000
@@ -0,0 +1,357 @@
+/* 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)));
+
+  return TRUE;
+}
+
+static gboolean
+query_tooltip_custom_cb (GtkWidget  *widget,
+			 gint        x,
+			 gint        y,
+			 gboolean    keyboard_tip,
+			 GtkTooltip *tooltip,
+			 gpointer    data)
+{
+  GtkWindow *window = gtk_widget_get_tooltip_window (widget);
+
+  /* ... can manipulate window here ... */
+
+  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;
+
+  char buffer[512];
+
+  if (keyboard_tip)
+    {
+      /* Keyboard mode */
+      gtk_tree_view_get_cursor (tree_view, &path, NULL);
+
+      if (!path)
+	return FALSE;
+    }
+  else
+    {
+      GtkWidget *header;
+
+      /* FIXME: need a tree view function for this */
+      header = (gtk_tree_view_get_column (tree_view, 0))->button;
+      y -= header->allocation.height;
+
+      /* 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);
+
+  snprintf (buffer, 511, "<b>Path %s:</b> %s",
+	    gtk_tree_path_to_string (path), tmp);
+  gtk_tooltip_set_markup (tooltip, buffer);
+
+  gtk_tree_path_free (path);
+  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_tooltip_force_query_tooltip_for_widget (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;
+}
Index: gtk/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/Makefile.am,v
retrieving revision 1.312
diff -u -p -r1.312 Makefile.am
--- gtk/Makefile.am	2 Jul 2006 14:40:43 -0000	1.312
+++ gtk/Makefile.am	20 Jul 2006 10:08:02 -0000
@@ -286,6 +286,7 @@ gtk_public_h_sources =          \
 	gtktoolbar.h		\
 	gtktoolbutton.h		\
 	gtktoolitem.h		\
+	gtktooltip.h		\
 	gtktooltips.h		\
 	gtktree.h		\
 	gtktreednd.h		\
@@ -557,6 +558,7 @@ gtk_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	20 Jul 2006 10:08:02 -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.128
diff -u -p -r1.128 gtk.symbols
--- gtk/gtk.symbols	10 Jul 2006 15:48:00 -0000	1.128
+++ gtk/gtk.symbols	20 Jul 2006 10:08:03 -0000
@@ -3943,6 +3943,16 @@ 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
+#endif
+#endif
+
 #if IN_HEADER(__GTK_TOOLTIPS_H__)
 #if IN_FILE(__GTK_TOOLTIPS_C__)
 gtk_tooltips_data_get
@@ -4483,6 +4493,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
@@ -4554,6 +4565,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
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	20 Jul 2006 10:08:03 -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/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	20 Jul 2006 10:08:03 -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,11 @@ 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);
+
 
 GType           gtk_requisition_get_type (void) G_GNUC_CONST;
 GtkRequisition *gtk_requisition_copy     (const GtkRequisition *requisition);
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.442
diff -u -p -r1.442 gtkwidget.c
--- gtk/gtkwidget.c	6 Jul 2006 05:14:03 -0000	1.442
+++ gtk/gtkwidget.c	20 Jul 2006 10:08:03 -0000
@@ -51,6 +51,7 @@
 #include "gtkintl.h"
 #include "gtkaccessible.h"
 #include "gtktooltips.h"
+#include "gtktooltip.h"
 #include "gtkinvisible.h"
 #include "gtkalias.h"
 
@@ -122,6 +123,7 @@ enum {
   CAN_ACTIVATE_ACCEL,
   GRAB_BROKEN,
   COMPOSITED_CHANGED,
+  QUERY_TOOLTIP,
   LAST_SIGNAL
 };
 
@@ -144,7 +146,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;
@@ -157,7 +161,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);
@@ -189,6 +192,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);
 
@@ -227,6 +235,9 @@ 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);
      
 static void gtk_widget_set_usize_internal (GtkWidget *widget,
 					   gint       width,
@@ -258,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;
 
@@ -333,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);
@@ -404,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;
   
@@ -545,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),
@@ -1388,6 +1421,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
@@ -1668,7 +1713,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));
@@ -1743,6 +1791,22 @@ 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));
+      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);
+
+      if (tooltip_markup)
+	g_free (tooltip_markup);
+      tooltip_markup = g_strdup (g_value_get_string (value));
+
+      g_object_set_qdata (object, quark_tooltip_markup, tooltip_markup);
+
+      tmp = (tooltip_window != NULL || (tooltip_markup ? strlen (tooltip_markup) : 0));
+      gtk_widget_set_has_tooltip (widget, tmp);
+      break;
     default:
       break;
     }
@@ -1759,6 +1823,7 @@ gtk_widget_get_property (GObject        
   switch (prop_id)
     {
       gint *eventp;
+      gboolean *has_tooltip;
       GdkExtensionMode *modep;
 
     case PROP_NAME:
@@ -1837,6 +1902,16 @@ 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:
+      has_tooltip = g_object_get_qdata (object, quark_has_tooltip);
+      if (has_tooltip)
+	g_value_set_boolean (value, *has_tooltip);
+      else
+	g_value_set_boolean (value, FALSE);
+      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;
@@ -2467,6 +2542,8 @@ gtk_widget_realize (GtkWidget *widget)
   
   if (!GTK_WIDGET_REALIZED (widget))
     {
+      gboolean *has_tooltip;
+
       /*
 	if (GTK_IS_CONTAINER (widget) && !GTK_WIDGET_NO_WINDOW (widget))
 	  g_message ("gtk_widget_realize(%s)", g_type_name (GTK_WIDGET_TYPE (widget)));
@@ -2484,6 +2561,22 @@ gtk_widget_realize (GtkWidget *widget)
       gtk_widget_ensure_style (widget);
       
       g_signal_emit (widget, widget_signals[REALIZE], 0);
+
+      has_tooltip = g_object_get_qdata (G_OBJECT (widget), quark_has_tooltip);
+      if (has_tooltip && *has_tooltip == TRUE)
+        {
+	  if (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);
+	  else
+	    gtk_widget_add_events (widget,
+				   GDK_LEAVE_NOTIFY_MASK |
+				   GDK_POINTER_MOTION_MASK |
+				   GDK_POINTER_MOTION_HINT_MASK);
+      }
       
       if (GTK_WIDGET_HAS_SHAPE_MASK (widget))
 	{
@@ -3658,6 +3751,8 @@ gtk_widget_real_focus_in_event (GtkWidge
 {
   gtk_widget_queue_shallow_draw (widget);
 
+  _gtk_tooltip_focus_in (widget);
+
   return FALSE;
 }
 
@@ -3667,6 +3762,8 @@ gtk_widget_real_focus_out_event (GtkWidg
 {
   gtk_widget_queue_shallow_draw (widget);
 
+  _gtk_tooltip_focus_out (widget);
+
   return FALSE;
 }
 
@@ -4289,12 +4386,34 @@ 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;
+
+  tooltip_markup = g_object_get_qdata (G_OBJECT (widget), quark_tooltip_markup);
+
+  if (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
@@ -6903,6 +7022,9 @@ gtk_widget_finalize (GObject *object)
   gint *events;
   GdkExtensionMode *mode;
   GtkAccessible *accessible;
+  gboolean *has_tooltip;
+  GtkWindow *tooltip_window;
+  gchar *tooltip_markup;
   
   gtk_grab_remove (widget);
 
@@ -6928,6 +7050,18 @@ gtk_widget_finalize (GObject *object)
   if (accessible)
     g_object_unref (accessible);
 
+  has_tooltip = g_object_get_qdata (G_OBJECT (widget), quark_has_tooltip);
+  if (has_tooltip)
+    g_slice_free (gboolean, has_tooltip);
+
+  tooltip_window = g_object_get_qdata (G_OBJECT (widget), quark_tooltip_window);
+  if (tooltip_window)
+    g_object_unref (tooltip_window);
+
+  tooltip_markup = g_object_get_qdata (G_OBJECT (widget), quark_tooltip_markup);
+  if (tooltip_markup)
+    g_free (tooltip_markup);
+
   G_OBJECT_CLASS (gtk_widget_parent_class)->finalize (object);
 }
 
@@ -8045,6 +8179,94 @@ 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 *priv_has_tooltip;
+
+  priv_has_tooltip = g_object_get_qdata (G_OBJECT (widget), quark_has_tooltip);
+
+  if (!priv_has_tooltip)
+    {
+      priv_has_tooltip = g_slice_new (gboolean);
+      *priv_has_tooltip = !has_tooltip;
+    }
+
+  if (*priv_has_tooltip != has_tooltip)
+    {
+      *priv_has_tooltip = has_tooltip;
+
+      if (*priv_has_tooltip)
+        {
+	  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, 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 (tooltip_window)
+    g_object_unref (tooltip_window);
+
+  if (custom_window)
+    g_object_ref (custom_window);
+
+  tooltip_window = custom_window;
+  g_object_set_qdata (G_OBJECT (widget), quark_tooltip_window, tooltip_window);
+
+  tmp = (tooltip_window != NULL || (tooltip_markup ? strlen (tooltip_markup) : 0));
+  gtk_widget_set_has_tooltip (widget, tmp);
+
+  /* FIXME: update current existing tooltip (if there is one) */
+}
+
+/**
+ * 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);
 }
 
 #define __GTK_WIDGET_C__
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.279
diff -u -p -r1.279 gtkmain.c
--- gtk/gtkmain.c	21 Jun 2006 18:16:55 -0000	1.279
+++ gtk/gtkmain.c	20 Jul 2006 10:08:04 -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"
@@ -1274,6 +1275,13 @@ gtk_main_do_event (GdkEvent *event)
     {
       _gtk_clipboard_handle_event (&event->owner_change);
       return;
+    }
+
+  if (event->type == GDK_LEAVE_NOTIFY
+      || event->type == GDK_MOTION_NOTIFY
+      || event->type == GDK_SCROLL)
+    {
+      _gtk_tooltip_handle_event (event);
     }
 
   /* Find the widget which got the event. We store the widget
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	20 Jul 2006 10:08:04 -0000
@@ -0,0 +1,52 @@
+/* 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_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);
+
+G_END_DECLS
+
+#endif /* __GTK_TOOLTIP__ */
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	20 Jul 2006 10:08:04 -0000
@@ -0,0 +1,734 @@
+/* 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"
+
+
+#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))
+#define GTK_TOOLTIP_GET_PRIVATE(obj)     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TOOLTIP, GtkTooltipPrivate))
+
+typedef struct _GtkTooltipClass   GtkTooltipClass;
+typedef struct _GtkTooltipPrivate GtkTooltipPrivate;
+
+struct _GtkTooltip
+{
+  GObject parent_instance;
+
+  /*< opaque >*/
+};
+
+struct _GtkTooltipClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GtkTooltipPrivate
+{
+  GtkWidget *window;
+  GtkWidget *alignment;
+  GtkWidget *box;
+  GtkWidget *image;
+  GtkWidget *label;
+  GtkWidget *custom_widget;
+
+  guint timeout_id;
+};
+
+
+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);
+
+  g_type_class_add_private (object_class, sizeof (GtkTooltipPrivate));
+}
+
+static gboolean
+gtk_tooltip_paint_window (GtkTooltip *tooltip)
+{
+  GtkRequisition req;
+  GtkTooltipPrivate *priv = GTK_TOOLTIP_GET_PRIVATE (tooltip);
+
+  gtk_widget_size_request (priv->window, &req);
+  gtk_paint_flat_box (priv->window->style,
+		      priv->window->window,
+		      GTK_STATE_NORMAL,
+		      GTK_SHADOW_OUT,
+		      NULL,
+		      priv->window,
+		      "tooltip",
+		      0, 0,
+		      req.width, req.height);
+
+  return FALSE;
+}
+
+static void
+gtk_tooltip_init (GtkTooltip *tooltip)
+{
+  GtkTooltipPrivate *priv = GTK_TOOLTIP_GET_PRIVATE (tooltip);
+
+  priv->timeout_id = 0;
+
+  priv->window = gtk_window_new (GTK_WINDOW_POPUP);
+  gtk_window_set_type_hint (GTK_WINDOW (priv->window),
+			    GDK_WINDOW_TYPE_HINT_TOOLTIP);
+  gtk_widget_set_app_paintable (priv->window, TRUE);
+  gtk_window_set_resizable (GTK_WINDOW (priv->window), FALSE);
+  gtk_widget_set_name (priv->window, "gtk-tooltips");
+
+  priv->alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+  gtk_alignment_set_padding (GTK_ALIGNMENT (priv->alignment),
+			     priv->window->style->ythickness,
+			     priv->window->style->ythickness,
+			     priv->window->style->xthickness,
+			     priv->window->style->xthickness);
+  gtk_container_add (GTK_CONTAINER (priv->window), priv->alignment);
+  gtk_widget_show (priv->alignment);
+
+  g_signal_connect_swapped (priv->window, "expose_event",
+			    G_CALLBACK (gtk_tooltip_paint_window), tooltip);
+
+  priv->box = gtk_hbox_new (FALSE, priv->window->style->xthickness);
+  gtk_container_add (GTK_CONTAINER (priv->alignment), priv->box);
+  gtk_widget_show (priv->box);
+
+  priv->image = gtk_image_new ();
+  gtk_box_pack_start (GTK_BOX (priv->box), priv->image,
+		      FALSE, FALSE, 0);
+
+  priv->label = gtk_label_new ("");
+  gtk_box_pack_start (GTK_BOX (priv->box), priv->label,
+		      FALSE, FALSE, 0);
+
+  priv->custom_widget = NULL;
+}
+
+
+
+void
+gtk_tooltip_set_markup (GtkTooltip  *tooltip,
+			const gchar *markup)
+{
+  GtkTooltipPrivate *priv;
+
+  g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
+
+  priv = GTK_TOOLTIP_GET_PRIVATE (tooltip);
+
+  gtk_label_set_markup (GTK_LABEL (priv->label), markup);
+
+  if (markup && strlen (markup) > 0)
+    gtk_widget_show (priv->label);
+  else
+    gtk_widget_hide (priv->label);
+}
+
+void
+gtk_tooltip_set_icon (GtkTooltip *tooltip,
+		      GdkPixbuf  *pixbuf)
+{
+  GtkTooltipPrivate *priv;
+
+  g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
+  if (pixbuf)
+    g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
+
+  priv = GTK_TOOLTIP_GET_PRIVATE (tooltip);
+
+  gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
+
+  if (pixbuf)
+    gtk_widget_show (priv->image);
+  else
+    gtk_widget_hide (priv->image);
+}
+
+void
+gtk_tooltip_set_icon_from_stock (GtkTooltip  *tooltip,
+				 const gchar *stock_id,
+				 GtkIconSize  size)
+{
+  GtkTooltipPrivate *priv;
+
+  g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
+
+  priv = GTK_TOOLTIP_GET_PRIVATE (tooltip);
+
+  gtk_image_set_from_stock (GTK_IMAGE (priv->image), stock_id, size);
+
+  if (stock_id)
+    gtk_widget_show (priv->image);
+  else
+    gtk_widget_hide (priv->image);
+}
+
+void
+gtk_tooltip_set_custom (GtkTooltip *tooltip,
+			GtkWidget  *custom_widget)
+{
+  GtkTooltipPrivate *priv;
+
+  g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
+  g_return_if_fail (GTK_IS_WIDGET (custom_widget));
+
+  priv = GTK_TOOLTIP_GET_PRIVATE (tooltip);
+
+  if (priv->custom_widget)
+    gtk_container_remove (GTK_CONTAINER (priv->box), priv->custom_widget);
+
+  priv->custom_widget = custom_widget;
+
+  if (custom_widget)
+    {
+      gtk_container_add (GTK_CONTAINER (priv->box), custom_widget);
+      gtk_widget_show (custom_widget);
+    }
+}
+
+
+/**
+ * event handling, etc
+ */
+
+/* variables */
+static GtkWidget *current_tooltip_widget = NULL;
+static GtkWindow *current_window = NULL;
+static GtkTooltip *current_tooltip = NULL;
+static gboolean tooltip_visible = FALSE;
+static gboolean tooltip_browse_mode_enabled = FALSE;
+static gboolean tooltip_keyboard_mode_enabled = FALSE;
+static guint tooltip_timeout_id = 0;
+static guint tooltip_browse_mode_timeout_id = 0;
+
+
+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)
+    {
+      if (GTK_WIDGET_NO_WINDOW (child))
+        {
+	  if (child_loc->x >= child->allocation.x &&
+	      child_loc->y >= child->allocation.y &&
+	      child_loc->x < child->allocation.x + child->allocation.width &&
+	      child_loc->y < child->allocation.y + child->allocation.height)
+	    {
+	      child_loc->child = child;
+	    }
+	}
+      else
+        {
+	  gint x, y;
+
+	  gtk_widget_translate_coordinates (child_loc->container, child,
+					    child_loc->x, child_loc->y,
+					    &x, &y);
+
+	  if (x >= child_loc->container->allocation.x &&
+	      y >= child_loc->container->allocation.y &&
+	      x < child_loc->container->allocation.x + child_loc->container->allocation.width &&
+	      y < child_loc->container->allocation.y + child_loc->container->allocation.height)
+	    {
+	      child_loc->child = child;
+	      child_loc->x = x;
+	      child_loc->y = y;
+	    }
+	}
+    }
+}
+
+static GtkWidget *
+find_widget_under_pointer (GdkEvent *event,
+			   gint     *x,
+			   gint     *y)
+{
+  GtkWidget *event_widget;
+  struct ChildLocation child_loc = { NULL, NULL, 0, 0 };
+
+  switch (event->type)
+    {
+      case GDK_LEAVE_NOTIFY:
+	child_loc.x = event->crossing.x;
+	child_loc.y = event->crossing.y;
+	break;
+
+      case GDK_MOTION_NOTIFY:
+	child_loc.x = event->motion.x;
+	child_loc.y = event->motion.y;
+	break;
+
+      case GDK_SCROLL:
+	child_loc.x = event->scroll.x;
+	child_loc.y = event->scroll.y;
+	break;
+
+      default:
+	break;
+    }
+
+  event_widget = gtk_get_event_widget (event);
+  if (GTK_IS_CONTAINER (event_widget))
+    {
+      GdkWindow *window;
+
+      child_loc.child = event_widget;
+
+      window = event->any.window;
+      while (window != child_loc.child->window)
+        {
+	  gint wx, wy;
+
+	  gdk_window_get_position (window, &wx, &wy);
+	  child_loc.x += wx;
+	  child_loc.y += wy;
+
+	  window = gdk_window_get_parent (window);
+
+	  if (!window)
+	    return NULL;
+	}
+
+      do
+        {
+	  event_widget = child_loc.container = child_loc.child;
+	  child_loc.child = NULL;
+
+	  gtk_container_foreach (GTK_CONTAINER (event_widget),
+				 child_location_foreach, &child_loc);
+	}
+      while (child_loc.child && GTK_IS_CONTAINER (child_loc.child));
+
+      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 gint
+tooltip_browse_mode_timeout (gpointer data)
+{
+  GDK_THREADS_ENTER ();
+
+  tooltip_browse_mode_enabled = FALSE;
+  tooltip_browse_mode_timeout_id = 0;
+
+  GDK_THREADS_LEAVE ();
+
+  return FALSE;
+}
+
+static void
+gtk_tooltip_show_tooltip (void)
+{
+  gint x, y;
+  gint w, h;
+  gint monitor_num, px, py;
+  GdkScreen *screen;
+  GdkScreen *pointer_screen;
+  GdkRectangle monitor;
+
+  GtkWidget *widget = current_tooltip_widget;
+
+  /* Position the tooltip */
+  if (tooltip_keyboard_mode_enabled)
+    {
+      gdk_window_get_origin (widget->window, &x, &y);
+      if (GTK_WIDGET_NO_WINDOW (widget))
+        {
+	  x += widget->allocation.x;
+	  y += widget->allocation.y;
+	}
+
+      /* FIXME: want to get this right */
+      x += widget->allocation.width / 2;
+      y += widget->allocation.height + 4;
+    }
+  else
+    {
+      gdk_window_get_pointer (gdk_screen_get_root_window (gtk_widget_get_screen (widget)), &x, &y, NULL);
+
+      /* FIXME: take RTL into account */
+      /* FIXME: maybe take size of cursor into account */
+      x += 8;
+      y += 8;
+    }
+
+  if (current_window)
+    {
+      GtkRequisition requisition;
+
+      gtk_widget_size_request (GTK_WIDGET (current_window), &requisition);
+      w = requisition.width;
+      h = requisition.height;
+    }
+
+  screen = gtk_widget_get_screen (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 (current_window)
+    {
+      gtk_window_move (GTK_WINDOW (current_window), x, y);
+      gtk_widget_show (GTK_WIDGET (current_window));
+    }
+
+  tooltip_visible = TRUE;
+}
+
+static void
+gtk_tooltip_hide_tooltip (GtkWidget *widget)
+{
+  if (tooltip_timeout_id)
+    {
+      g_source_remove (tooltip_timeout_id);
+      tooltip_timeout_id = 0;
+    }
+
+  /* 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 (500,
+						    tooltip_browse_mode_timeout,
+						    NULL);
+
+  if (current_window)
+    gtk_widget_hide (GTK_WIDGET (current_window));
+
+  tooltip_visible = FALSE;
+}
+
+static gint
+tooltip_timeout (gpointer data)
+{
+  GDK_THREADS_ENTER ();
+
+  gtk_tooltip_show_tooltip ();
+  tooltip_timeout_id = 0;
+
+  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;
+    }
+
+  GDK_THREADS_LEAVE ();
+
+  return FALSE;
+}
+
+static void
+gtk_tooltip_start_delay (GtkWidget *widget)
+{
+  guint timeout;
+
+  if (tooltip_visible || tooltip_timeout_id)
+    return;
+
+  /* FIXME: make this configurable */
+  if (tooltip_browse_mode_enabled)
+    timeout = 100;
+  else
+    timeout = 1500;
+
+  tooltip_timeout_id = g_timeout_add (timeout,
+				      tooltip_timeout,
+				      widget);
+}
+
+void
+_gtk_tooltip_focus_in (GtkWidget *widget)
+{
+  gint x, y;
+  gboolean has_tooltip;
+  gboolean return_value;
+
+  g_object_get (widget, "has-tooltip", &has_tooltip, NULL);
+
+  if (!tooltip_keyboard_mode_enabled || !has_tooltip)
+    return;
+
+  gdk_window_get_pointer (widget->window, &x, &y, NULL);
+
+  if (!current_window)
+    {
+      current_window = gtk_widget_get_tooltip_window (widget);
+      if (!current_window)
+        {
+	  current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
+	  current_window = GTK_WINDOW (GTK_TOOLTIP_GET_PRIVATE (current_tooltip)->window);
+	}
+      current_tooltip_widget = widget;
+    }
+
+  g_signal_emit_by_name (widget, "query-tooltip",
+			 x, y,
+			 TRUE,
+			 current_tooltip,
+			 &return_value);
+
+  if (!return_value)
+    gtk_tooltip_hide_tooltip (widget);
+  else
+    gtk_tooltip_show_tooltip ();
+}
+
+void
+_gtk_tooltip_focus_out (GtkWidget *widget)
+{
+  gboolean has_tooltip;
+
+  g_object_get (widget, "has-tooltip", &has_tooltip, NULL);
+
+  if (!tooltip_keyboard_mode_enabled || !has_tooltip
+      || current_tooltip_widget != widget)
+    return;
+
+  /* Hide tooltip */
+  gtk_tooltip_hide_tooltip (widget);
+
+  if (current_tooltip)
+    g_object_unref (current_tooltip);
+  current_tooltip = NULL;
+  current_window = NULL;
+  current_tooltip_widget = NULL;
+}
+
+void
+_gtk_tooltip_toggle_keyboard_mode (GtkWidget *widget)
+{
+  tooltip_keyboard_mode_enabled ^= 1;
+
+  if (tooltip_keyboard_mode_enabled)
+    _gtk_tooltip_focus_in (widget);
+  else
+    {
+      gtk_tooltip_hide_tooltip (current_tooltip_widget);
+
+      if (current_tooltip)
+	g_object_unref (current_tooltip);
+      current_tooltip = NULL;
+      current_window = NULL;
+      current_tooltip_widget = NULL;
+    }
+}
+
+void
+_gtk_tooltip_handle_event (GdkEvent *event)
+{
+  gint x, y;
+  gboolean has_tooltip = FALSE;
+  gboolean return_value;
+  GtkWidget *tooltip_widget;
+  GtkWidget *old_widget;
+
+  if (tooltip_keyboard_mode_enabled)
+    return;
+
+  old_widget = tooltip_widget = find_widget_under_pointer (event, &x, &y);
+  if (!tooltip_widget)
+    return;
+
+  g_object_get (tooltip_widget, "has-tooltip", &has_tooltip, NULL);
+
+  if (event->type == GDK_MOTION_NOTIFY && event->motion.is_hint)
+    {
+      gdk_window_get_pointer (tooltip_widget->window, NULL, NULL, 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 (current_tooltip_widget && current_tooltip_widget != tooltip_widget)
+    {
+      gtk_tooltip_hide_tooltip (tooltip_widget);
+      if (current_tooltip)
+	g_object_unref (current_tooltip);
+      current_tooltip = NULL;
+      current_window = NULL;
+      current_tooltip_widget = NULL;
+    }
+
+  if (!tooltip_widget || !has_tooltip)
+    return;
+
+  /* Handle tooltip event for this widget */
+#if 0
+  g_print ("%p (%s)  has tooltip=%d\n",
+	   tooltip_widget, gtk_widget_get_name (tooltip_widget), has_tooltip);
+#endif
+
+  switch (event->type)
+    {
+      case GDK_LEAVE_NOTIFY:
+	gtk_tooltip_hide_tooltip (tooltip_widget);
+	if (current_tooltip)
+	  g_object_unref (current_tooltip);
+	current_tooltip = NULL;
+	current_window = NULL;
+	current_tooltip_widget = NULL;
+	break;
+
+      case GDK_SCROLL:
+      case GDK_MOTION_NOTIFY:
+	if (!current_window)
+	  {
+	    current_window = gtk_widget_get_tooltip_window (tooltip_widget);
+	    if (!current_window)
+	      {
+		current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
+		current_window = GTK_WINDOW (GTK_TOOLTIP_GET_PRIVATE (current_tooltip)->window);
+	      }
+	    current_tooltip_widget = tooltip_widget;
+	  }
+
+	g_signal_emit_by_name (tooltip_widget, "query-tooltip",
+			       x, y,
+			       FALSE,
+			       current_tooltip,
+			       &return_value);
+
+	if (!return_value)
+	  gtk_tooltip_hide_tooltip (tooltip_widget);
+	else
+	  gtk_tooltip_start_delay (tooltip_widget);
+	break;
+
+      default:
+	break;
+    }
+}
+
+void
+gtk_tooltip_force_query_tooltip_for_widget (GtkWidget *widget)
+{
+  gint x, y;
+  gboolean return_value;
+
+  if (!current_window)
+    {
+      current_window = gtk_widget_get_tooltip_window (widget);
+      if (!current_window)
+        {
+	  current_tooltip = g_object_new (GTK_TYPE_TOOLTIP, NULL);
+	  current_window = GTK_WINDOW (GTK_TOOLTIP_GET_PRIVATE (current_tooltip)->window);
+	}
+      current_tooltip_widget = widget;
+    }
+
+  g_signal_emit_by_name (widget, "query-tooltip",
+			 x, y,
+			 tooltip_keyboard_mode_enabled,
+			 current_tooltip,
+			 &return_value);
+
+  if (!return_value)
+    gtk_tooltip_hide_tooltip (widget);
+  else
+    gtk_tooltip_show_tooltip ();
+}
+
+void
+gtk_tooltip_force_query_tooltip (GdkDisplay *display)
+{
+  gint x, y;
+  GdkWindow *window;
+  GdkEvent event;
+  GtkWidget *widget;
+
+  if (!display)
+    display = gdk_screen_get_display (gdk_screen_get_default ());
+
+  window = gdk_display_get_window_at_pointer (display, &x, &y);
+
+  if (!window)
+    return;
+
+  /* evil */
+  event.type = GDK_MOTION_NOTIFY;
+  event.any.window = window;
+
+  widget = find_widget_under_pointer (&event, &x, &y);
+
+  gtk_tooltip_force_query_tooltip_for_widget (widget);
+}
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	20 Jul 2006 10:08:04 -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


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