Index: gtk/gtkbutton.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkbutton.c,v
retrieving revision 1.118
diff -u -p -r1.118 gtkbutton.c
--- gtk/gtkbutton.c	2 Jan 2005 03:14:34 -0000	1.118
+++ gtk/gtkbutton.c	7 Jan 2005 16:57:26 -0000
@@ -109,6 +109,8 @@ static gint gtk_button_button_press   (G
 				       GdkEventButton   *event);
 static gint gtk_button_button_release (GtkWidget        *widget,
 				       GdkEventButton   *event);
+static gint gtk_button_grab_broken    (GtkWidget        *widget,
+				       GdkEventAny      *event);
 static gint gtk_button_key_release    (GtkWidget        *widget,
 				       GdkEventKey      *event);
 static gint gtk_button_enter_notify   (GtkWidget        *widget,
@@ -198,6 +200,7 @@ gtk_button_class_init (GtkButtonClass *k
   widget_class->expose_event = gtk_button_expose;
   widget_class->button_press_event = gtk_button_button_press;
   widget_class->button_release_event = gtk_button_button_release;
+  widget_class->grab_broken_event = gtk_button_grab_broken;
   widget_class->key_release_event = gtk_button_key_release;
   widget_class->enter_notify_event = gtk_button_enter_notify;
   widget_class->leave_notify_event = gtk_button_leave_notify;
@@ -1208,6 +1211,29 @@ gtk_button_button_release (GtkWidget    
     {
       button = GTK_BUTTON (widget);
       gtk_button_released (button);
+    }
+
+  return TRUE;
+}
+
+static gboolean
+gtk_button_grab_broken (GtkWidget   *widget,
+			GdkEventAny *event)
+{
+  GtkButton *button = GTK_BUTTON (widget);
+  gboolean save_in;
+  
+  /* Simulate a button release without the pointer in the button */
+  if (button->button_down)
+    {
+      save_in = button->in_button;
+      button->in_button = FALSE;
+      gtk_button_released (button);
+      if (save_in != button->in_button)
+	{
+	  button->in_button = save_in;
+	  gtk_button_update_state (button);
+	}
     }
 
   return TRUE;
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.255
diff -u -p -r1.255 gtkmain.c
--- gtk/gtkmain.c	27 Dec 2004 05:25:15 -0000	1.255
+++ gtk/gtkmain.c	7 Jan 2005 16:57:28 -0000
@@ -1358,6 +1358,7 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_CLIENT_EVENT:
     case GDK_VISIBILITY_NOTIFY:
     case GDK_WINDOW_STATE:
+    case GDK_GRAB_BROKEN:
       gtk_widget_event (event_widget, event);
       break;
 
Index: gtk/gtkmenushell.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.c,v
retrieving revision 1.87
diff -u -p -r1.87 gtkmenushell.c
--- gtk/gtkmenushell.c	28 Dec 2004 05:33:27 -0000	1.87
+++ gtk/gtkmenushell.c	7 Jan 2005 16:57:28 -0000
@@ -140,6 +140,8 @@ static gint gtk_menu_shell_leave_notify 
 					      GdkEventCrossing  *event);
 static void gtk_menu_shell_screen_changed    (GtkWidget         *widget,
 					      GdkScreen         *previous_screen);
+static gint gtk_menu_shell_grab_broken       (GtkWidget         *widget,
+					      GdkEventAny       *event);
 static void gtk_menu_shell_add               (GtkContainer      *container,
 					      GtkWidget         *widget);
 static void gtk_menu_shell_remove            (GtkContainer      *container,
@@ -226,6 +228,7 @@ gtk_menu_shell_class_init (GtkMenuShellC
   widget_class->realize = gtk_menu_shell_realize;
   widget_class->button_press_event = gtk_menu_shell_button_press;
   widget_class->button_release_event = gtk_menu_shell_button_release;
+  widget_class->grab_broken_event = gtk_menu_shell_grab_broken;
   widget_class->key_press_event = gtk_menu_shell_key_press;
   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
@@ -601,6 +604,27 @@ gtk_menu_shell_button_release (GtkWidget
 	  gtk_menu_shell_deactivate (menu_shell);
 	  g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
 	}
+    }
+
+  return TRUE;
+}
+
+static gint
+gtk_menu_shell_grab_broken (GtkWidget      *widget,
+			    GdkEventButton *event)
+{
+  GtkMenuShell *menu_shell;
+  GtkWidget *menu_item;
+  gint deactivate;
+
+  g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  menu_shell = GTK_MENU_SHELL (widget);
+  if (menu_shell->active)
+    {
+      gtk_menu_shell_deactivate (menu_shell);
+      g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
     }
 
   return TRUE;
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.386
diff -u -p -r1.386 gtkwidget.c
--- gtk/gtkwidget.c	13 Dec 2004 13:50:34 -0000	1.386
+++ gtk/gtkwidget.c	7 Jan 2005 16:57:36 -0000
@@ -120,6 +120,7 @@ enum {
   ACCEL_CLOSURES_CHANGED,
   SCREEN_CHANGED,
   CAN_ACTIVATE_ACCEL,
+  GRAB_BROKEN,
   LAST_SIGNAL
 };
 
@@ -396,6 +397,7 @@ gtk_widget_class_init (GtkWidgetClass *k
   klass->drag_data_received = NULL;
   klass->screen_changed = NULL;
   klass->can_activate_accel = gtk_widget_real_can_activate_accel;
+  klass->grab_broken_event = NULL;
 
   klass->show_help = gtk_widget_real_show_help;
   
@@ -1312,6 +1314,15 @@ gtk_widget_class_init (GtkWidgetClass *k
 		  _gtk_marshal_BOOLEAN__BOXED,
 		  G_TYPE_BOOLEAN, 1,
 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+  widget_signals[GRAB_BROKEN] =
+    g_signal_new ("grab_broken_event",
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (GtkWidgetClass, grab_broken_event),
+		  _gtk_boolean_handled_accumulator, NULL,
+		  _gtk_marshal_BOOLEAN__BOXED,
+		  G_TYPE_BOOLEAN, 1,
+		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 /**
  * GtkWidget::popup-menu
  * @widget: the object which received the signal
@@ -3606,6 +3617,9 @@ gtk_widget_event_internal (GtkWidget *wi
 	  break;
 	case GDK_VISIBILITY_NOTIFY:
 	  signal_num = VISIBILITY_NOTIFY_EVENT;
+	  break;
+	case GDK_GRAB_BROKEN:
+	  signal_num = GRAB_BROKEN;
 	  break;
 	default:
 	  g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
Index: gtk/gtkwidget.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.h,v
retrieving revision 1.152
diff -u -p -r1.152 gtkwidget.h
--- gtk/gtkwidget.h	28 Nov 2004 07:02:27 -0000	1.152
+++ gtk/gtkwidget.h	7 Jan 2005 16:57:37 -0000
@@ -406,8 +406,11 @@ struct _GtkWidgetClass
   gboolean     (*can_activate_accel) (GtkWidget *widget,
                                       guint      signal_id);
 
+  /* Sent when a grab is broken. */
+  gboolean (*grab_broken_event) (GtkWidget	     *widget,
+                                 GdkEventAny         *event);
+
   /* Padding for future expansion */
-  void (*_gtk_reserved2) (void);
   void (*_gtk_reserved3) (void);
   void (*_gtk_reserved4) (void);
   void (*_gtk_reserved5) (void);
Index: gdk/gdkevents.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkevents.h,v
retrieving revision 1.23
diff -u -p -r1.23 gdkevents.h
--- gdk/gdkevents.h	28 Nov 2004 06:45:52 -0000	1.23
+++ gdk/gdkevents.h	7 Jan 2005 16:57:38 -0000
@@ -1,3 +1,4 @@
+
 #ifndef __GDK_EVENTS_H__
 #define __GDK_EVENTS_H__
 
@@ -120,7 +121,8 @@ typedef enum
   GDK_SCROLL            = 31,
   GDK_WINDOW_STATE      = 32,
   GDK_SETTING           = 33,
-  GDK_OWNER_CHANGE      = 34
+  GDK_OWNER_CHANGE      = 34,
+  GDK_GRAB_BROKEN       = 35
 } GdkEventType;
 
 /* Event masks. (Used to select what types of events a window
Index: gdk/win32/gdkevents-win32.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/win32/gdkevents-win32.c,v
retrieving revision 1.141
diff -u -p -r1.141 gdkevents-win32.c
--- gdk/win32/gdkevents-win32.c	16 Nov 2004 17:32:15 -0000	1.141
+++ gdk/win32/gdkevents-win32.c	7 Jan 2005 16:57:40 -0000
@@ -2900,8 +2782,25 @@ gdk_event_translate (GdkDisplay *display
 	 }
        break;
 
-    case WM_SETFOCUS:
     case WM_KILLFOCUS:
+      if (p_grab_window != NULL && !GDK_WINDOW_DESTROYED (p_grab_window))
+	{
+	  event = gdk_event_new (GDK_GRAB_BROKEN);
+	  event->any.window = p_grab_window;
+
+          append_event (display, event);
+	}
+      if (k_grab_window != NULL && !GDK_WINDOW_DESTROYED (k_grab_window) 
+	  && k_grab_window != p_grab_window)
+	{
+	  event = gdk_event_new (GDK_GRAB_BROKEN);
+	  event->any.window = k_grab_window;
+
+          append_event (display, event);
+	}
+
+      /* fallthrough */
+    case WM_SETFOCUS:
       if (k_grab_window != NULL && !k_grab_owner_events)
 	break;
 
