--- gtk/gtkmain.c	(revision 17027)
+++ gtk/gtkmain.c	(working copy)
@@ -1567,6 +1567,7 @@
 	  
 	  g_object_unref (event_widget);
 	}
+      GTK_PRIVATE_SET_FLAG (event_widget, GTK_SYNTH_CROSSING);
       break;
       
     case GDK_LEAVE_NOTIFY:
@@ -1577,6 +1578,7 @@
 	}
       else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
 	gtk_widget_event (grab_widget, event);
+      GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_SYNTH_CROSSING);
       break;
       
     case GDK_DRAG_STATUS:
--- gtk/gtkprivate.h	(revision 17027)
+++ gtk/gtkprivate.h	(working copy)
@@ -49,7 +49,8 @@
   PRIVATE_GTK_CHILD_VISIBLE     = 1 <<  10,  /* If widget should be mapped when parent is mapped */
   PRIVATE_GTK_REDRAW_ON_ALLOC   = 1 <<  11,  /* If we should queue a draw on the entire widget when it is reallocated */
   PRIVATE_GTK_ALLOC_NEEDED      = 1 <<  12,  /* If we we should allocate even if the allocation is the same */
-  PRIVATE_GTK_REQUEST_NEEDED    = 1 <<  13   /* Whether we need to call gtk_widget_size_request */
+  PRIVATE_GTK_REQUEST_NEEDED    = 1 <<  13,  /* Whether we need to call gtk_widget_size_request */
+  PRIVATE_GTK_SYNTH_CROSSING    = 1 <<  14   /* Whether to synthesize enter/leave events on making the widget (in)sensitive */
 } GtkPrivateFlags;
 
 /* Macros for extracting a widgets private_flags from GtkWidget.
@@ -67,6 +68,7 @@
 #define GTK_WIDGET_REDRAW_ON_ALLOC(obj)   ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_REDRAW_ON_ALLOC) != 0)
 #define GTK_WIDGET_ALLOC_NEEDED(obj)      ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_ALLOC_NEEDED) != 0)
 #define GTK_WIDGET_REQUEST_NEEDED(obj)    ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_REQUEST_NEEDED) != 0)
+#define GTK_WIDGET_SYNTH_CROSSING(obj)    ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_SYNTH_CROSSING) != 0)
 
 /* Macros for setting and clearing private widget flags.
  * we use a preprocessor string concatenation here for a clear
--- gtk/gtktypebuiltins.c	(revision 17027)
+++ gtk/gtktypebuiltins.c	(working copy)
@@ -1501,6 +1501,7 @@ gtk_private_flags_get_type (void)
       { PRIVATE_GTK_REDRAW_ON_ALLOC, "PRIVATE_GTK_REDRAW_ON_ALLOC", "redraw-on-alloc" },
       { PRIVATE_GTK_ALLOC_NEEDED, "PRIVATE_GTK_ALLOC_NEEDED", "alloc-needed" },
       { PRIVATE_GTK_REQUEST_NEEDED, "PRIVATE_GTK_REQUEST_NEEDED", "request-needed" },
+      { PRIVATE_GTK_SYNTH_CROSSING, "PRIVATE_GTK_SYNTH_CROSSING", "synth-crossing" },
       { 0, NULL, NULL }
     };
     etype = g_flags_register_static (g_intern_static_string ("GtkPrivateFlags"), values);
--- gtk/gtkwidget.c	(revision 17027)
+++ gtk/gtkwidget.c	(working copy)
@@ -4248,6 +4248,10 @@
 			 gboolean   was_grabbed)
 {
   g_signal_emit (widget, widget_signals[GRAB_NOTIFY], 0, was_grabbed);
+
+  if (!GTK_WIDGET_NO_WINDOW (widget) && widget->window)
+    gtk_widget_synthesize_crossing_from_grab_notify (widget, widget->window, 
+						     was_grabbed);
 }
 
 /**
@@ -7248,6 +7252,146 @@
 }
 
 static void
+gtk_widget_synthesize_crossing_event (GtkWidget      *widget,
+			              GdkWindow      *window,
+				      gboolean        enter)
+{
+  GdkEvent *event;
+
+  event = gdk_event_new (enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY);
+
+  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.state = 0;
+  event->crossing.mode = GDK_CROSSING_SYNTHESIZED;
+  event->crossing.detail = GDK_NOTIFY_UNKNOWN;
+  event->crossing.focus = FALSE;
+
+  g_object_ref (widget);
+  gtk_widget_event_internal (widget, event);
+  gdk_event_free (event);
+
+  if (enter)
+    GTK_PRIVATE_SET_FLAG (widget, GTK_LEAVE_PENDING);
+  else
+    GTK_PRIVATE_UNSET_FLAG (widget, GTK_LEAVE_PENDING);
+  g_object_unref (widget);
+}
+
+/**
+ * gtk_widget_synthesize_crossing_from_grab_notify:
+ * @widget: a #GtkWidget
+ * @window: the #GdkWindow to synthesize events for
+ * @was_grabbed: the previous grab state
+ *
+ * Synthesize enter/leave events upon changing the global 
+ * grab state of a widget. 
+ *
+ * GTK+ handles this automatically for window widgets and 
+ * for #GtkButton<!-- -->s. Third-party no-window widgets 
+ * which need to track enter/leave events need to call this 
+ * function in their grab-notify handler:
+ *
+ * <informalexample><programlisting>
+ * static void
+ * gtk_button_grab_notify (GtkWidget *widget,
+ *                         gboolean   was_grabbed)
+ * {
+ *   GtkButton *button = GTK_BUTTON (widget);
+ *   
+ *   /<!-- -->* Handle grab *<!-- -->/
+ *    
+ *   if (button-&gt;event_window)
+ *     gtk_widget_synthesize_crossing_from_grab_notify (widget,
+ *                                                      button-&gt;event_window,
+ *                                                      was_grabbed);
+ * }
+ * </programlisting></informalexample>
+ *
+ * Since: 2.12
+ */
+void
+gtk_widget_synthesize_crossing_from_grab_notify (GtkWidget *widget,
+					         GdkWindow *window,
+					         gboolean   was_grabbed)
+{
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GTK_WIDGET_SYNTH_CROSSING (widget) && GTK_WIDGET_IS_SENSITIVE (widget))
+    gtk_widget_synthesize_crossing_event (widget, window, was_grabbed);
+}
+
+static gboolean
+is_shadowed (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+  GtkWindowGroup *group;
+  GtkWidget *grab_widget;
+  
+  toplevel = gtk_widget_get_toplevel (widget);
+  group = gtk_window_get_group ((toplevel && GTK_IS_WINDOW (toplevel))
+				? (GtkWindow *) toplevel : NULL);
+  grab_widget = _gtk_window_group_get_current_grab (group);
+  return (grab_widget != NULL && grab_widget != widget &&
+	  !gtk_widget_is_ancestor (widget, grab_widget));
+}
+
+/**
+ * gtk_widget_synthesize_crossing_from_state_changed:
+ * @widget: a #GtkWidget
+ * @window: the #GdkWindow to synthesize events for
+ * @previous_state: the previous state
+ *
+ * Synthesize enter/leave events upon changing the 
+ * sensitivity of a widget.
+ *
+ * GTK+ handles this automatically for window widgets and 
+ * for #GtkButton<!-- -->s. Third-party no-window widgets 
+ * which need to track enter/leave events need to call this 
+ * function in their state-changed handler:
+ *
+ * <informalexample><programlisting>
+ * static void
+ * gtk_button_state_changed (GtkWidget    *widget,
+ *                           GtkStateType  previous_state)
+ * {
+ *   GtkButton *button = GTK_BUTTON (widget);
+ *      
+ *   /<!-- -->* Handle state change *<!-- -->/
+ *   
+ *   if (button-&gt;event_window)
+ *     gtk_widget_synthesize_crossing_from_state_changed (widget,
+ *                                                        button-&gt;event_window,
+ *                                                        previous_state);
+ * } 
+ * </programlisting></informalexample>
+ *
+ * Since: 2.12
+ */
+void
+gtk_widget_synthesize_crossing_from_state_changed (GtkWidget    *widget,
+					           GdkWindow    *window,
+						   GtkStateType  previous_state)
+{
+  gboolean sensitive;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
+
+  if (GTK_WIDGET_SYNTH_CROSSING (widget) &&
+      (!sensitive || previous_state == GTK_STATE_INSENSITIVE) &&
+      !is_shadowed (widget))
+    gtk_widget_synthesize_crossing_event (widget, window, sensitive);
+}
+
+static void
 gtk_widget_propagate_state (GtkWidget           *widget,
 			    GtkStateData        *data)
 {
@@ -7308,6 +7452,11 @@
 	gtk_grab_remove (widget);
       
       g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state);
+
+      if (!GTK_WIDGET_NO_WINDOW (widget) && widget->window)
+	gtk_widget_synthesize_crossing_from_state_changed (widget, 
+						           widget->window, 
+							   old_state);
       
       if (GTK_IS_CONTAINER (widget))
 	{
--- gtk/gtkwidget.h	(revision 17027)
+++ gtk/gtkwidget.h	(working copy)
@@ -781,6 +781,13 @@
 GtkRequisition *gtk_requisition_copy     (const GtkRequisition *requisition);
 void            gtk_requisition_free     (GtkRequisition       *requisition);
 
+void gtk_widget_synthesize_crossing_from_grab_notify (GtkWidget      *widget,
+						      GdkWindow      *window,
+						      gboolean        was_grabbed);
+void gtk_widget_synthesize_crossing_from_state_changed (GtkWidget    *widget,
+							GdkWindow    *window,
+							GtkStateType  previous_state);
+
 #if	defined (GTK_TRACE_OBJECTS) && defined (__GNUC__)
 #  define gtk_widget_ref gtk_object_ref
 #  define gtk_widget_unref gtk_object_unref
--- gtk/gtk.symbols	(revision 17027)
+++ gtk/gtk.symbols	(working copy)
@@ -4476,6 +4476,8 @@
 gtk_widget_ensure_style
 gtk_widget_error_bell
 gtk_widget_event
+gtk_widget_synthesize_crossing_from_grab_notify
+gtk_widget_synthesize_crossing_from_state_changed
 gtk_widget_freeze_child_notify
 gtk_widget_get_accessible
 gtk_widget_get_ancestor
--- gtk/gtkbutton.c	(revision 17027)
+++ gtk/gtkbutton.c	(working copy)
@@ -1925,6 +1925,11 @@
       button->in_button = FALSE;
       gtk_real_button_released (button);
     }
+
+  if (button->event_window)
+    gtk_widget_synthesize_crossing_from_state_changed (widget, 
+						       button->event_window, 
+						       previous_state);
 }
 
 static void
@@ -1945,6 +1950,11 @@
           gtk_button_update_state (button);
         }
     }
+
+  if (button->event_window)
+    gtk_widget_synthesize_crossing_from_grab_notify (widget, 
+						     button->event_window, 
+						     was_grabbed);
 }
 
 /**
--- gdk/gdkevents.h	(revision 17027)
+++ gdk/gdkevents.h	(working copy)
@@ -215,12 +215,14 @@ typedef enum
  *   NotifyNormal
  *   NotifyGrab
  *   NotifyUngrab
+ *   Synthesized: synthesized in response to a widget becoming (in)sensitive.
  */
 typedef enum
 {
   GDK_CROSSING_NORMAL,
   GDK_CROSSING_GRAB,
-  GDK_CROSSING_UNGRAB
+  GDK_CROSSING_UNGRAB,
+  GDK_CROSSING_SYNTHESIZED
 } GdkCrossingMode;
 
 typedef enum
--- docs/reference/gdk/tmpl/event_structs.sgml	(revision 17027)
+++ docs/reference/gdk/tmpl/event_structs.sgml	(working copy)
@@ -239,7 +239,9 @@
 
 <!-- ##### STRUCT GdkEventCrossing ##### -->
 <para>
-Generated when the pointer enters or leaves a window.
+Generated when the pointer enters or leaves a window. If @mode is 
+%GDK_CROSSING_SYNTHESIZED, the @x, @y, @x_root, @y_root and @state 
+members will be unset.
 </para>
 
 @type: the type of the event (%GDK_ENTER_NOTIFY or %GDK_LEAVE_NOTIFY).
@@ -248,18 +251,23 @@
 <function>XSendEvent</function>).
 @subwindow: the window that was entered or left.
 @time: the time of the event in milliseconds.
-@x: the x coordinate of the pointer relative to the window.
-@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).
+@x: the x coordinate of the pointer relative to the window, not set if 
+  @mode is %GDK_CROSSING_SYNTHESIZED.
+@y: the y coordinate of the pointer relative to the window, not set if 
+  @mode is %GDK_CROSSING_SYNTHESIZED.
+@x_root: the x coordinate of the pointer relative to the root of the 
+  screen, not set if @mode is %GDK_CROSSING_SYNTHESIZED.
+@y_root: the y coordinate of the pointer relative to the root of the 
+  screen, not set if @mode is %GDK_CROSSING_SYNTHESIZED.
+@mode: the crossing mode (%GDK_CROSSING_NORMAL, %GDK_CROSSING_GRAB,
+  %GDK_CROSSING_UNGRAB or %GDK_CROSSING_SYNTHESIZED).
 @detail: the kind of crossing that happened (%GDK_NOTIFY_INFERIOR,
-  %GDK_NOTIFY_ANCESTOR, %GDK_NOTIFY_VIRTUAL, %GDK_NOTIFY_NONLINEAR or
-  %GDK_NOTIFY_NONLINEAR_VIRTUAL).
+  %GDK_NOTIFY_ANCESTOR, %GDK_NOTIFY_VIRTUAL, %GDK_NOTIFY_NONLINEAR,
+  %GDK_NOTIFY_NONLINEAR_VIRTUAL, or %GDK_NOTIFY_UNKNOWN).
 @focus: %TRUE if @window is the focus window or an inferior.
 @state: a bit-mask representing the state of the modifier keys (e.g. Control,
-  Shift and Alt) and the pointer buttons. See #GdkModifierType.
+  Shift and Alt) and the pointer buttons. See #GdkModifierType.  Not set if 
+  @mode is %GDK_CROSSING_SYNTHESIZED.
 
 <!-- ##### STRUCT GdkEventFocus ##### -->
 <para>
@@ -474,6 +473,8 @@
 @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_SYNTHESIZED: synthesized by GTK+ in response to a widget 
+   becoming sensitive or insensitive. Since 2.12.
 
 <!-- ##### ENUM GdkNotifyType ##### -->
 <para>
