--- gtk/gtkmain.c	2007/01/02 18:03:54	1.1
+++ gtk/gtkmain.c	2007/01/02 18:04:11
@@ -1433,6 +1433,7 @@ gtk_main_do_event (GdkEvent *event)
 	  
 	  g_object_unref (event_widget);
 	}
+      GTK_PRIVATE_SET_FLAG (event_widget, GTK_SYNTH_CROSSING);
       break;
       
     case GDK_LEAVE_NOTIFY:
@@ -1443,6 +1444,7 @@ gtk_main_do_event (GdkEvent *event)
 	}
       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	2007/01/02 18:03:54	1.1
+++ gtk/gtkprivate.h	2007/01/02 18:05:13
@@ -49,7 +49,8 @@ typedef enum
   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 @@ typedef enum
 #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	2007/01/02 18:03:54	1.1
+++ gtk/gtktypebuiltins.c	2007/01/02 18:04:11
@@ -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	2007/01/02 18:03:54	1.1
+++ gtk/gtkwidget.c	2007/01/02 21:22:41
@@ -4192,6 +4192,11 @@ _gtk_widget_grab_notify (GtkWidget *widg
 			 gboolean   was_grabbed)
 {
   g_signal_emit (widget, widget_signals[GRAB_NOTIFY], 0, was_grabbed);
+
+  if (GTK_WIDGET_SYNTH_CROSSING (widget) && 
+      !GTK_WIDGET_NO_WINDOW (widget) && widget->window)
+    _gtk_widget_synthesize_crossing_from_grab_notify (widget, widget->window, 
+						      was_grabbed);
 }
 
 /**
@@ -7076,6 +7081,116 @@ _gtk_widget_peek_colormap (void)
   return NULL;
 }
 
+/**
+ * _gtk_widget_synthesize_crossing_event:
+ * @widget: a #GtkWidget
+ * @window: the #GdkWindow to synthesize events for
+ * @enter: %TRUE for an enter event, %FALSE for a leave event
+ * @mode: the #GdkCrossingMode of the crossing event
+ *
+ * Synthesize enter/leave events upon changing the sensitivity or global grab
+ * state of a widget.
+ **/
+static void
+_gtk_widget_synthesize_crossing_event (GtkWidget      *widget,
+				       GdkWindow      *window,
+				       gboolean        enter,
+				       GdkCrossingMode mode)
+{
+  GdkEvent *event;
+  GdkEventType type = enter ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
+
+  g_return_if_fail (widget);
+  g_return_if_fail (window);
+
+  event = gdk_event_new (type);
+  event->crossing.type = type;
+  event->crossing.window = window;
+  event->crossing.send_event = FALSE;
+  event->crossing.subwindow = window;
+  event->crossing.time = GDK_CURRENT_TIME;
+  if (mode == GDK_CROSSING_SYNTHESIZED) {
+    event->crossing.x = event->crossing.y = 0;
+    event->crossing.x_root = event->crossing.y_root = 0;
+    event->crossing.state = 0;
+  } else {
+    gint x_root, y_root, x, y;
+    gdk_display_get_pointer (gdk_drawable_get_display (window), NULL, 
+			     &x_root, &y_root, &event->crossing.state);
+    event->crossing.x_root = (gdouble) x_root;
+    event->crossing.y_root = (gdouble) y_root;
+    gdk_window_get_origin (window, &x, &y);
+    event->crossing.x = (gdouble) (x_root - x);
+    event->crossing.y = (gdouble) (y_root - y);
+  }
+  event->crossing.mode = mode;
+  event->crossing.detail = GDK_NOTIFY_UNKNOWN;
+  event->crossing.focus = FALSE;
+
+  g_object_ref (widget);
+  gtk_widget_event (widget, 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.
+ **/
+void
+_gtk_widget_synthesize_crossing_from_grab_notify (GtkWidget      *widget,
+						  GdkWindow      *window,
+						  gboolean        was_grabbed)
+{
+  if (GTK_WIDGET_IS_SENSITIVE (widget))
+    _gtk_widget_synthesize_crossing_event (widget, window, was_grabbed, 
+					   GDK_CROSSING_SYNTHESIZED);
+}
+
+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_event_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.
+ **/
+void
+_gtk_widget_synthesize_crossing_from_state_changed (GtkWidget    *widget,
+						    GdkWindow    *window,
+						    GtkStateType  previous_state)
+{
+  gboolean sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
+
+  if ((!sensitive || previous_state == GTK_STATE_INSENSITIVE) &&
+      !is_shadowed (widget))
+    _gtk_widget_synthesize_crossing_event (widget, window, sensitive, 
+					   GDK_CROSSING_SYNTHESIZED);
+}
+
 static void
 gtk_widget_propagate_state (GtkWidget           *widget,
 			    GtkStateData        *data)
@@ -7137,6 +7252,12 @@ gtk_widget_propagate_state (GtkWidget   
 	gtk_grab_remove (widget);
       
       g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state);
+
+      if (GTK_WIDGET_SYNTH_CROSSING (widget) && 
+	  !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	2007/01/02 18:03:54	1.1
+++ gtk/gtkwidget.h	2007/01/02 18:06:08
@@ -794,6 +794,13 @@ void              _gtk_widget_propagate_
 							   GdkScreen    *previous_screen);
 void		  _gtk_widget_propagate_composited_changed (GtkWidget    *widget);
 
+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);
+
 GdkColormap* _gtk_widget_peek_colormap (void);
 
 G_END_DECLS
--- gtk/gtkbutton.c	2007/01/02 18:03:54	1.1
+++ gtk/gtkbutton.c	2007/01/02 18:06:17
@@ -1929,6 +1929,11 @@ gtk_button_state_changed (GtkWidget    *
       button->in_button = FALSE;
       gtk_real_button_released (button);
     }
+
+  if (GTK_WIDGET_SYNTH_CROSSING (widget) && button->event_window)
+    _gtk_widget_synthesize_crossing_from_state_changed (widget, 
+							button->event_window, 
+							previous_state);
 }
 
 static void
@@ -1942,6 +1947,11 @@ gtk_button_grab_notify (GtkWidget *widge
       button->in_button = FALSE;
       gtk_real_button_released (button);
     }
+
+  if (GTK_WIDGET_SYNTH_CROSSING (widget) && button->event_window)
+    _gtk_widget_synthesize_crossing_from_grab_notify (widget, 
+						      button->event_window, 
+						      was_grabbed);
 }
 
 /**
--- gdk/gdkevents.h	2007/01/02 18:07:03	1.1
+++ gdk/gdkevents.h	2007/01/02 21:20:21
@@ -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	2007/01/02 18:12:35	1.1
+++ docs/reference/gdk/tmpl/event_structs.sgml	2007/01/02 21:22:11
@@ -246,7 +246,9 @@ Generated when the window visibility sta
 
 <!-- ##### 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).
@@ -255,18 +257,23 @@ Generated when the pointer enters or lea
 <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 +481,8 @@ 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_SYNTHESIZED: synthesized by GTK+ in response to a widget 
+   becoming sensitive or insensitive.
 
 <!-- ##### ENUM GdkNotifyType ##### -->
 <para>
