--- gtk/gtkmain.c	2007/02/12 13:33:13	1.1
+++ gtk/gtkmain.c	2007/02/12 13:33:27
@@ -1570,25 +1570,15 @@ gtk_main_do_event (GdkEvent *event)
       break;
       
     case GDK_ENTER_NOTIFY:
+      GTK_PRIVATE_SET_FLAG (event_widget, GTK_HAS_POINTER);
+      _gtk_widget_set_pointer_window (event_widget, event->any.window);
       if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
-	{
-	  g_object_ref (event_widget);
-	  
-	  gtk_widget_event (grab_widget, event);
-	  if (event_widget == grab_widget)
-	    GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
-	  
-	  g_object_unref (event_widget);
-	}
+	gtk_widget_event (grab_widget, event);
       break;
       
     case GDK_LEAVE_NOTIFY:
-      if (GTK_WIDGET_LEAVE_PENDING (event_widget))
-	{
-	  GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
-	  gtk_widget_event (event_widget, event);
-	}
-      else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
+      GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_HAS_POINTER);
+      if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
 	gtk_widget_event (grab_widget, event);
       break;
       
@@ -1647,6 +1637,7 @@ typedef struct
   GtkWidget *new_grab_widget;
   gboolean   was_grabbed;
   gboolean   is_grabbed;
+  gboolean   from_grab;
 } GrabNotifyInfo;
 
 static void
@@ -1669,6 +1660,24 @@ gtk_grab_notify_foreach (GtkWidget *chil
 
   g_object_ref (child);
   
+  if (is_shadowed)
+    {
+      GTK_PRIVATE_SET_FLAG (child, GTK_SHADOWED);
+      if (!was_shadowed && GTK_WIDGET_HAS_POINTER (child)
+	  && GTK_WIDGET_IS_SENSITIVE (child))
+	_gtk_widget_synthesize_crossing (child, info->new_grab_widget,
+					 GDK_CROSSING_GTK_GRAB);
+    }
+  else
+    {
+      GTK_PRIVATE_UNSET_FLAG (child, GTK_SHADOWED);
+      if (was_shadowed && GTK_WIDGET_HAS_POINTER (child)
+	  && GTK_WIDGET_IS_SENSITIVE (child))
+	_gtk_widget_synthesize_crossing (info->old_grab_widget, child,
+					 info->from_grab ? GDK_CROSSING_GTK_GRAB
+					 : GDK_CROSSING_GTK_UNGRAB);
+    }
+
   if (was_shadowed != is_shadowed)
     _gtk_widget_grab_notify (child, was_shadowed);
   
@@ -1684,7 +1693,8 @@ gtk_grab_notify_foreach (GtkWidget *chil
 static void
 gtk_grab_notify (GtkWindowGroup *group,
 		 GtkWidget      *old_grab_widget,
-		 GtkWidget      *new_grab_widget)
+		 GtkWidget      *new_grab_widget,
+		 gboolean        from_grab)
 {
   GList *toplevels;
   GrabNotifyInfo info;
@@ -1694,6 +1704,7 @@ gtk_grab_notify (GtkWindowGroup *group,
 
   info.old_grab_widget = old_grab_widget;
   info.new_grab_widget = new_grab_widget;
+  info.from_grab = from_grab;
 
   g_object_ref (group);
 
@@ -1738,7 +1749,7 @@ gtk_grab_add (GtkWidget *widget)
       g_object_ref (widget);
       group->grabs = g_slist_prepend (group->grabs, widget);
 
-      gtk_grab_notify (group, old_grab_widget, widget);
+      gtk_grab_notify (group, old_grab_widget, widget, TRUE);
     }
 }
 
@@ -1774,7 +1785,7 @@ gtk_grab_remove (GtkWidget *widget)
       else
 	new_grab_widget = NULL;
 
-      gtk_grab_notify (group, widget, new_grab_widget);
+      gtk_grab_notify (group, widget, new_grab_widget, FALSE);
       
       g_object_unref (widget);
     }
--- gtk/gtkprivate.h	2007/02/12 13:33:13	1.1
+++ gtk/gtkprivate.h	2007/02/12 13:33:27
@@ -40,7 +40,8 @@ typedef enum
 {
   PRIVATE_GTK_USER_STYLE	= 1 <<  0,
   PRIVATE_GTK_RESIZE_PENDING	= 1 <<  2,
-  PRIVATE_GTK_LEAVE_PENDING	= 1 <<  4,
+  PRIVATE_GTK_HAS_POINTER	= 1 <<  3,   /* If the pointer is above a window belonging to the widget */
+  PRIVATE_GTK_SHADOWED		= 1 <<  4,   /* If there is a grab in effect shadowing the widget */
   PRIVATE_GTK_HAS_SHAPE_MASK	= 1 <<  5,
   PRIVATE_GTK_IN_REPARENT       = 1 <<  6,
   PRIVATE_GTK_DIRECTION_SET     = 1 <<  7,   /* If the reading direction is not DIR_NONE */
@@ -57,7 +58,8 @@ typedef enum
 #define GTK_PRIVATE_FLAGS(wid)            (GTK_WIDGET (wid)->private_flags)
 #define GTK_WIDGET_USER_STYLE(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_USER_STYLE) != 0)
 #define GTK_CONTAINER_RESIZE_PENDING(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_RESIZE_PENDING) != 0)
-#define GTK_WIDGET_LEAVE_PENDING(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_LEAVE_PENDING) != 0)
+#define GTK_WIDGET_HAS_POINTER(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_HAS_POINTER) != 0)
+#define GTK_WIDGET_SHADOWED(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_SHADOWED) != 0)
 #define GTK_WIDGET_HAS_SHAPE_MASK(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_HAS_SHAPE_MASK) != 0)
 #define GTK_WIDGET_IN_REPARENT(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_IN_REPARENT) != 0)
 #define GTK_WIDGET_DIRECTION_SET(obj)	  ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_DIRECTION_SET) != 0)
--- gtk/gtktooltips.c	2007/02/12 13:33:13	1.1
+++ gtk/gtktooltips.c	2007/02/12 13:33:27
@@ -692,9 +692,6 @@ gtk_tooltips_event_handler (GtkWidget *w
 	   */
 	  if (GTK_IS_MENU_ITEM (widget) && !GTK_MENU_ITEM (widget)->submenu)
 	    {
-	      /* Completely evil hack to make sure we get the LEAVE_NOTIFY
-	       */
-	      GTK_PRIVATE_SET_FLAG (widget, GTK_LEAVE_PENDING);
 	      gtk_tooltips_set_active_widget (tooltips, NULL);
 	      gtk_tooltips_start_delay (tooltips, widget);
 	      break;
--- gtk/gtktypebuiltins.c	2007/02/12 13:33:13	1.1
+++ gtk/gtktypebuiltins.c	2007/02/12 13:33:27
@@ -1491,7 +1491,8 @@ gtk_private_flags_get_type (void)
     static const GFlagsValue values[] = {
       { PRIVATE_GTK_USER_STYLE, "PRIVATE_GTK_USER_STYLE", "user-style" },
       { PRIVATE_GTK_RESIZE_PENDING, "PRIVATE_GTK_RESIZE_PENDING", "resize-pending" },
-      { PRIVATE_GTK_LEAVE_PENDING, "PRIVATE_GTK_LEAVE_PENDING", "leave-pending" },
+      { PRIVATE_GTK_HAS_POINTER, "PRIVATE_GTK_HAS_POINTER", "has-pointer" },
+      { PRIVATE_GTK_SHADOWED, "PRIVATE_GTK_SHADOWED", "shadowed" },
       { PRIVATE_GTK_HAS_SHAPE_MASK, "PRIVATE_GTK_HAS_SHAPE_MASK", "has-shape-mask" },
       { PRIVATE_GTK_IN_REPARENT, "PRIVATE_GTK_IN_REPARENT", "in-reparent" },
       { PRIVATE_GTK_DIRECTION_SET, "PRIVATE_GTK_DIRECTION_SET", "direction-set" },
--- gtk/gtkwidget.c	2007/02/12 13:33:13	1.1
+++ gtk/gtkwidget.c	2007/02/12 13:33:27
@@ -251,6 +251,7 @@ static GQuark		quark_accel_closures = 0;
 static GQuark		quark_event_mask = 0;
 static GQuark		quark_extension_event_mode = 0;
 static GQuark		quark_parent_window = 0;
+static GQuark		quark_pointer_window = 0;
 static GQuark		quark_shape_info = 0;
 static GQuark		quark_input_shape_info = 0;
 static GQuark		quark_colormap = 0;
@@ -326,6 +327,7 @@ gtk_widget_class_init (GtkWidgetClass *k
   quark_event_mask = g_quark_from_static_string ("gtk-event-mask");
   quark_extension_event_mode = g_quark_from_static_string ("gtk-extension-event-mode");
   quark_parent_window = g_quark_from_static_string ("gtk-parent-window");
+  quark_pointer_window = g_quark_from_static_string ("gtk-pointer-window");
   quark_shape_info = g_quark_from_static_string ("gtk-shape-info");
   quark_input_shape_info = g_quark_from_static_string ("gtk-input-shape-info");
   quark_colormap = g_quark_from_static_string ("gtk-colormap");
@@ -7080,6 +7082,170 @@ _gtk_widget_peek_colormap (void)
   return NULL;
 }
 
+/**
+ * _gtk_widget_set_pointer_window:
+ * @widget: a #GtkWidget.
+ * @pointer_window: the new pointer window.
+ *  
+ * Sets pointer window for @widget.  Does not ref @pointer_window.
+ * Actually stores it on the #GdkScreen, but you don't need to know that.
+ **/
+void
+_gtk_widget_set_pointer_window   (GtkWidget *widget,
+				  GdkWindow *pointer_window)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (widget->window));
+  g_object_set_qdata (G_OBJECT (screen), quark_pointer_window, pointer_window);
+}
+
+/**
+ * _gtk_widget_get_pointer_window:
+ * @widget: a #GtkWidget.
+ *
+ * Return value: the pointer window set on the #GdkScreen @widget is attached
+ * to, or %NULL.
+ **/
+static GdkWindow *
+_gtk_widget_get_pointer_window   (GtkWidget *widget)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (widget->window));
+  return g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
+}
+
+static void
+synth_crossing (GtkWidget      *widget,
+		GdkEventType    type,
+		GdkWindow      *window,
+		GdkCrossingMode mode,
+		GdkNotifyType   detail)
+{
+  GdkEvent *event;
+  
+  event = gdk_event_new (type);
+
+  event->crossing.window = g_object_ref (window);
+  event->crossing.send_event = TRUE;
+  event->crossing.subwindow = g_object_ref (window);
+  event->crossing.time = GDK_CURRENT_TIME;
+  event->crossing.x = event->crossing.y = 0;
+  event->crossing.x_root = event->crossing.y_root = 0;
+  event->crossing.mode = mode;
+  event->crossing.detail = detail;
+  event->crossing.focus = FALSE;
+  event->crossing.state = 0;
+
+  if (!widget)
+    widget = gtk_get_event_widget (event);
+  gtk_widget_event_internal (widget, event);
+  gdk_event_free (event);
+}
+
+
+/**
+ * _gtk_widget_synthesize_crossing:
+ * @from: the #GtkWidget the virtual pointer is leaving.
+ * @to: the #GtkWidget the virtual pointer is moving to.
+ * @mode: the #GdkCrossingMode to place on the synthesized events.
+ *
+ * Generate crossing event(s) on widget state (sensitivity) or GTK+ grab change.
+ */
+void
+_gtk_widget_synthesize_crossing (GtkWidget      *from,
+				 GtkWidget      *to,
+				 GdkCrossingMode mode)
+{
+  GdkWindow *from_window = NULL, *to_window = NULL;
+
+  g_return_if_fail (from != NULL || to != NULL);
+
+  if (from != NULL)
+    from_window = GTK_WIDGET_HAS_POINTER (from)
+      ? _gtk_widget_get_pointer_window (from) : from->window;
+  if (to != NULL)
+    to_window = GTK_WIDGET_HAS_POINTER (to)
+      ? _gtk_widget_get_pointer_window (to) : to->window;
+
+  if (from_window == NULL && to_window == NULL)
+    ;
+  else if (from_window != NULL && to_window == NULL)
+    synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
+		    mode, GDK_NOTIFY_UNKNOWN);
+  else if (from_window == NULL && to_window != NULL)
+    synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
+		    mode, GDK_NOTIFY_UNKNOWN);
+  else if (from_window == to_window)
+    ;
+  else
+    {
+      GList *from_ancestors = NULL, *to_ancestors = NULL, *list;
+      GdkWindow *from_ancestor = from_window, *to_ancestor = to_window;
+
+      while (from_ancestor != NULL || to_ancestor != NULL)
+	{
+	  if (from_ancestor != NULL)
+	    {
+	      from_ancestor = gdk_window_get_parent (from_ancestor);
+	      if (from_ancestor == to_window)
+		break;
+	      from_ancestors = g_list_prepend (from_ancestors, from_ancestor);
+	    }
+	  if (to_ancestor != NULL)
+	    {
+	      to_ancestor = gdk_window_get_parent (to_ancestor);
+	      if (to_ancestor == from_window)
+		break;
+	      to_ancestors = g_list_prepend (to_ancestors, to_ancestor);
+	    }
+	}
+      if (to_ancestor == from_window)
+	{
+	  synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
+			  mode, GDK_NOTIFY_INFERIOR);
+	  for (list = to_ancestors; list; list = list->next)
+	    synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data, 
+			    mode, GDK_NOTIFY_VIRTUAL);
+	  synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
+			  mode, GDK_NOTIFY_ANCESTOR);
+	}
+      else if (from_ancestor == to_window)
+	{
+	  synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
+			  mode, GDK_NOTIFY_ANCESTOR);
+	  for (list = g_list_last (from_ancestors); list; list = list->prev)
+	    synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
+			    mode, GDK_NOTIFY_VIRTUAL);
+	  synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
+			  mode, GDK_NOTIFY_INFERIOR);
+	}
+      else
+	{
+	  while (from_ancestors != NULL && to_ancestors != NULL 
+		 && from_ancestors->data == to_ancestors->data)
+	    {
+	      from_ancestors = g_list_delete_link (from_ancestors, 
+						   from_ancestors);
+	      to_ancestors = g_list_delete_link (to_ancestors, to_ancestors);
+	    }
+	  synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
+			  mode, GDK_NOTIFY_NONLINEAR);
+	  for (list = g_list_last (from_ancestors); list; list = list->prev)
+	    synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
+			    mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
+	  for (list = to_ancestors; list; list = list->next)
+	    synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
+			    mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
+	  synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
+			  mode, GDK_NOTIFY_NONLINEAR);
+	}
+      g_list_free (from_ancestors);
+      g_list_free (to_ancestors);
+    }
+}
+
 static void
 gtk_widget_propagate_state (GtkWidget           *widget,
 			    GtkStateData        *data)
@@ -7141,6 +7307,16 @@ gtk_widget_propagate_state (GtkWidget   
 	gtk_grab_remove (widget);
       
       g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state);
+
+      if (GTK_WIDGET_HAS_POINTER (widget) && !GTK_WIDGET_SHADOWED (widget))
+	{
+	  if (!GTK_WIDGET_IS_SENSITIVE (widget))
+	    _gtk_widget_synthesize_crossing (widget, NULL, 
+					     GDK_CROSSING_STATE_CHANGED);
+	  else if (old_state == GTK_STATE_INSENSITIVE)
+	    _gtk_widget_synthesize_crossing (NULL, widget, 
+					     GDK_CROSSING_STATE_CHANGED);
+	}
       
       if (GTK_IS_CONTAINER (widget))
 	{
--- gtk/gtkwidget.h	2007/02/12 13:33:13	1.1
+++ gtk/gtkwidget.h	2007/02/12 13:33:27
@@ -794,6 +794,12 @@ void              _gtk_widget_propagate_
 							   GdkScreen    *previous_screen);
 void		  _gtk_widget_propagate_composited_changed (GtkWidget    *widget);
 
+void	   _gtk_widget_set_pointer_window  (GtkWidget      *widget,
+					    GdkWindow      *pointer_window);
+void       _gtk_widget_synthesize_crossing (GtkWidget      *from,
+					    GtkWidget      *to,
+					    GdkCrossingMode mode);
+
 GdkColormap* _gtk_widget_peek_colormap (void);
 
 G_END_DECLS
--- gdk/gdkevents.h	2007/02/12 13:33:13	1.1
+++ gdk/gdkevents.h	2007/02/12 13:33:27
@@ -220,7 +220,10 @@ typedef enum
 {
   GDK_CROSSING_NORMAL,
   GDK_CROSSING_GRAB,
-  GDK_CROSSING_UNGRAB
+  GDK_CROSSING_UNGRAB,
+  GDK_CROSSING_GTK_GRAB,
+  GDK_CROSSING_GTK_UNGRAB,
+  GDK_CROSSING_STATE_CHANGED
 } GdkCrossingMode;
 
 typedef enum
--- docs/reference/gdk/tmpl/event_structs.sgml	2007/02/12 13:33:13	1.1
+++ docs/reference/gdk/tmpl/event_structs.sgml	2007/02/12 13:33:27
@@ -259,8 +259,9 @@ Generated when the pointer enters or lea
 @y: the y coordinate of the pointer relative to the window.
 @x_root: the x coordinate of the pointer relative to the root of the screen.
 @y_root: the y coordinate of the pointer relative to the root of the screen.
-@mode: the crossing mode (%GDK_CROSSING_NORMAL, %GDK_CROSSING_GRAB or
-  %GDK_CROSSING_UNGRAB).
+@mode: the crossing mode (%GDK_CROSSING_NORMAL, %GDK_CROSSING_GRAB, 
+  %GDK_CROSSING_UNGRAB, %GDK_CROSSING_GTK_GRAB, %GDK_CROSSING_GTK_UNGRAB or
+  %GDK_CROSSING_STATE_CHANGED).
 @detail: the kind of crossing that happened (%GDK_NOTIFY_INFERIOR,
   %GDK_NOTIFY_ANCESTOR, %GDK_NOTIFY_VIRTUAL, %GDK_NOTIFY_NONLINEAR or
   %GDK_NOTIFY_NONLINEAR_VIRTUAL).
@@ -474,6 +475,10 @@ Specifies the crossing mode for #GdkEven
 @GDK_CROSSING_NORMAL: crossing because of pointer motion.
 @GDK_CROSSING_GRAB: crossing because a grab is activated.
 @GDK_CROSSING_UNGRAB: crossing because a grab is deactivated.
+@GDK_CROSSING_GTK_GRAB: crossing because a GTK+ grab is activated.
+@GDK_CROSSING_GTK_UNGRAB: crossing because a GTK+ grab is deactivated.
+@GDK_CROSSING_STATE_CHANGED: crossing because a GTK+ widget changed state (e.g.
+   sensitivity).
 
 <!-- ##### ENUM GdkNotifyType ##### -->
 <para>
--- gtk/gtkwindow.c	2007/02/12 13:33:13	1.1
+++ gtk/gtkwindow.c	2007/02/12 16:05:11
@@ -1855,10 +1855,6 @@ gtk_window_unset_transient_for  (GtkWind
   
   if (window->transient_parent)
     {
-      if (priv->transient_parent_group)
-	gtk_window_group_remove_window (window->group,
-					window);
-
       g_signal_handlers_disconnect_by_func (window->transient_parent,
 					    gtk_window_transient_parent_realized,
 					    window);
@@ -1876,7 +1872,13 @@ gtk_window_unset_transient_for  (GtkWind
         disconnect_parent_destroyed (window);
       
       window->transient_parent = NULL;
-      priv->transient_parent_group = FALSE;
+
+      if (priv->transient_parent_group)
+	{
+	  priv->transient_parent_group = FALSE;
+	  gtk_window_group_remove_window (window->group,
+					  window);
+	}
     }
 }
 
