Index: gtk/gtkwidget.c
===================================================================
--- gtk/gtkwidget.c	(revision 18972)
+++ gtk/gtkwidget.c	(working copy)
@@ -293,6 +293,7 @@
 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;
@@ -380,6 +381,7 @@
   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");
@@ -8043,18 +8045,185 @@
   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.
+ **/
+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);
+
+  if (widget)
+    gtk_widget_event_internal (widget, event);
+
+  gdk_event_free (event);
+}
+
+/**
+ * _gtk_widget_is_pointer_widget:
+ * @widget: a #GtkWidget
+ *
+ * Returns %TRUE if the pointer window belongs to @widget.
+ *
+ */
+gboolean
+_gtk_widget_is_pointer_widget (GtkWidget *widget)
+{
+  if (GTK_WIDGET_HAS_POINTER (widget))
+    { 
+      GdkWindow *win; 
+      GtkWidget *wid;
+
+      win = _gtk_widget_get_pointer_window (widget);
+      if (win)
+        { 
+          gdk_window_get_user_data (win, &wid);
+          if (wid == widget)
+            return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+gboolean _effective_pointer_window_is_root;
+
+/**
+ * _gtk_widget_synthesize_crossing:
+ * @widget: the #GtkWidget the virtual pointer is leaving or entering
+ * @leave: %TRUE if the pointer is leaving
+ * @event: the event that triggered this change, or %NULL
+ *
+ * Generate crossing events for moving the pointer between @widget
+ * and the root window. If @leave is %TRUE, generate events for going
+ * from @widget to root, else the other way around. 
+ * The synthesized events are made to match @event.
+ */
+void
+_gtk_widget_synthesize_crossing (GtkWidget      *widget,
+				 gboolean        leave,
+                                 GdkEvent       *event)
+{
+  GdkWindow *window = NULL;
+  GList *ancestors = NULL,  *list;
+  GdkWindow *ancestor;
+  GdkNotifyType end_detail, inner_detail;
+
+  _effective_pointer_window_is_root = leave;
+
+  if (_gtk_widget_is_pointer_widget (widget))
+    window = _gtk_widget_get_pointer_window (widget);
+  else   
+    window = widget->window;
+
+  ancestor = window;
+  while (TRUE)
+    {
+      ancestor = gdk_window_get_parent (ancestor);
+      if (ancestor == NULL)
+        break;
+      ancestors = g_list_prepend (ancestors, ancestor);
+    }
+
+  inner_detail = GDK_NOTIFY_VIRTUAL;
+  end_detail = GDK_NOTIFY_ANCESTOR;
+
+  if (event)
+    {
+       if (event->crossing.detail == GDK_NOTIFY_NONLINEAR ||
+           event->crossing.detail == GDK_NOTIFY_NONLINEAR_VIRTUAL)
+        {
+          inner_detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
+          end_detail = GDK_NOTIFY_NONLINEAR;
+        }
+
+      if (event->crossing.detail == GDK_NOTIFY_VIRTUAL ||
+          event->crossing.detail == GDK_NOTIFY_NONLINEAR_VIRTUAL)
+        end_detail = inner_detail;
+    }
+ 
+  if (leave)
+    {
+      synth_crossing (NULL, GDK_LEAVE_NOTIFY, window,
+                      GDK_CROSSING_NORMAL, end_detail);
+      for (list = g_list_last (ancestors); list; list = list->prev)
+	synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data, 
+			GDK_CROSSING_NORMAL, inner_detail);
+    }
+  else
+    {
+      for (list = ancestors; list; list = list->next)
+        synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
+			GDK_CROSSING_NORMAL, inner_detail);
+      synth_crossing (NULL, GDK_ENTER_NOTIFY, window,
+                      GDK_CROSSING_NORMAL, end_detail);
+    }
+  g_list_free (ancestors);
+}
+
+static void
 gtk_widget_propagate_state (GtkWidget           *widget,
 			    GtkStateData        *data)
 {
   guint8 old_state = GTK_WIDGET_STATE (widget);
   guint8 old_saved_state = GTK_WIDGET_SAVED_STATE (widget);
-
+  guint8 new_state;
+  
   /* don't call this function with state==GTK_STATE_INSENSITIVE,
    * parent_sensitive==TRUE on a sensitive widget
    */
 
-
   if (data->parent_sensitive)
     GTK_WIDGET_SET_FLAGS (widget, GTK_PARENT_SENSITIVE);
   else
@@ -8088,6 +8257,8 @@
 	gtk_window_set_focus (GTK_WINDOW (window), NULL);
     }
 
+  new_state = GTK_WIDGET_STATE (widget);
+
   if (old_state != GTK_WIDGET_STATE (widget) ||
       old_saved_state != GTK_WIDGET_SAVED_STATE (widget))
     {
@@ -8098,6 +8269,14 @@
 
       g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state);
 
+      if (GTK_WIDGET_HAS_POINTER (widget) && !GTK_WIDGET_SHADOWED (widget))
+        { 
+          if (new_state == GTK_STATE_INSENSITIVE)
+            _gtk_widget_synthesize_crossing (widget, TRUE, NULL);
+          else if (old_state == GTK_STATE_INSENSITIVE)
+	    _gtk_widget_synthesize_crossing (widget, FALSE, NULL);
+        }
+      
       if (GTK_IS_CONTAINER (widget))
 	{
 	  data->parent_sensitive = (GTK_WIDGET_IS_SENSITIVE (widget) != FALSE);
Index: gtk/gtkwidget.h
===================================================================
--- gtk/gtkwidget.h	(revision 18972)
+++ gtk/gtkwidget.h	(working copy)
@@ -821,6 +821,14 @@
 							   GdkScreen    *previous_screen);
 void		  _gtk_widget_propagate_composited_changed (GtkWidget    *widget);
 
+void	   _gtk_widget_set_pointer_window  (GtkWidget      *widget,
+					    GdkWindow      *pointer_window);
+GdkWindow *_gtk_widget_get_pointer_window  (GtkWidget      *widget);
+gboolean   _gtk_widget_is_pointer_widget   (GtkWidget      *widget);
+void       _gtk_widget_synthesize_crossing (GtkWidget      *widget,
+					    gboolean        leave,
+                                            GdkEvent       *event); 
+
 GdkColormap* _gtk_widget_peek_colormap (void);
 
 G_END_DECLS
Index: gtk/gtkwindow.c
===================================================================
--- gtk/gtkwindow.c	(revision 18972)
+++ gtk/gtkwindow.c	(working copy)
@@ -2029,10 +2029,6 @@
   
   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);
@@ -2050,7 +2046,13 @@
         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);
+	}
     }
 }
 
Index: gtk/gtkmain.c
===================================================================
--- gtk/gtkmain.c	(revision 18972)
+++ gtk/gtkmain.c	(working copy)
@@ -1373,6 +1373,8 @@
     return NULL;
 }
 
+extern gboolean _effective_pointer_window_is_root;
+
 void 
 gtk_main_do_event (GdkEvent *event)
 {
@@ -1540,28 +1542,40 @@
       break;
       
     case GDK_ENTER_NOTIFY:
-      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);
-	}
+      {
+        gboolean can_has_pointer;
+
+        GTK_PRIVATE_SET_FLAG (event_widget, GTK_HAS_POINTER);
+        _gtk_widget_set_pointer_window (event_widget, event->any.window);
+
+        can_has_pointer = GTK_WIDGET_IS_SENSITIVE (event_widget) && !GTK_WIDGET_SHADOWED (event_widget);
+  
+        if (_effective_pointer_window_is_root)
+          {
+            if (can_has_pointer)
+              _gtk_widget_synthesize_crossing (event_widget, FALSE, event); 
+          }
+        else
+          {
+            if (can_has_pointer)
+              gtk_widget_event (event_widget, event);
+            else
+	      _gtk_widget_synthesize_crossing (gtk_widget_get_parent (event_widget), TRUE, event);
+          }
+       }
+       break;
+	      
+     case GDK_LEAVE_NOTIFY:
+       GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_HAS_POINTER);
+       _gtk_widget_set_pointer_window (event_widget, NULL);
+
+       if (!_effective_pointer_window_is_root)
+         {
+           if (GTK_WIDGET_IS_SENSITIVE (grab_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_widget_event (grab_widget, event);
-      break;
-      
+
     case GDK_DRAG_STATUS:
     case GDK_DROP_FINISHED:
       _gtk_drag_source_handle_event (event_widget, event);
@@ -1631,6 +1645,7 @@
   GtkWidget *new_grab_widget;
   gboolean   was_grabbed;
   gboolean   is_grabbed;
+  gboolean   from_grab;
 } GrabNotifyInfo;
 
 static void
@@ -1653,6 +1668,21 @@
 
   g_object_ref (child);
   
+  if (is_shadowed)
+    {
+      GTK_PRIVATE_SET_FLAG (child, GTK_SHADOWED);
+      if (!was_shadowed && _gtk_widget_is_pointer_widget (child) &&
+	  GTK_WIDGET_IS_SENSITIVE (child))
+	_gtk_widget_synthesize_crossing (child, TRUE, NULL);
+    }
+  else
+    {
+      GTK_PRIVATE_UNSET_FLAG (child, GTK_SHADOWED);
+      if (was_shadowed && _gtk_widget_is_pointer_widget (child) &&
+	  GTK_WIDGET_IS_SENSITIVE (child))
+	_gtk_widget_synthesize_crossing (child, FALSE, NULL);
+    }
+
   if (was_shadowed != is_shadowed)
     _gtk_widget_grab_notify (child, was_shadowed);
   
@@ -1668,7 +1698,8 @@
 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;
@@ -1678,6 +1709,7 @@
 
   info.old_grab_widget = old_grab_widget;
   info.new_grab_widget = new_grab_widget;
+  info.from_grab = from_grab;
 
   g_object_ref (group);
 
@@ -1722,7 +1754,7 @@
       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);
     }
 }
 
@@ -1758,7 +1790,7 @@
       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);
     }
Index: gtk/gtkbutton.c
===================================================================
--- gtk/gtkbutton.c	(revision 18972)
+++ gtk/gtkbutton.c	(working copy)
@@ -1843,7 +1843,8 @@
     new_state = depressed ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL;
 
   _gtk_button_set_depressed (button, depressed); 
-  gtk_widget_set_state (GTK_WIDGET (button), new_state);
+  if (GTK_WIDGET_STATE (button) != GTK_STATE_INSENSITIVE)
+    gtk_widget_set_state (GTK_WIDGET (button), new_state);
 }
 
 static void 
Index: gtk/gtkprivate.h
===================================================================
--- gtk/gtkprivate.h	(revision 18972)
+++ gtk/gtkprivate.h	(working copy)
@@ -40,7 +40,8 @@
 {
   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 @@
 #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)
