New version of the grab group patch



Here's a new version of the patch to add grab groups and
notification of grab shadowing that I sent to the list
a few months ago.

The big change from the last version is that there is now
a GtkWindowGroup structure, instead of the grouping being
implicitely defined by a "leader window".

This works a lot like GtkSizeGroup in that you have:

void gtk_window_group_add_window    (GtkWindowGroup     *window_group,
                		     GtkWindow          *window);
void gtk_window_group_remove_window (GtkWindowGroup     *window_group,
				     GtkWindow          *window);

Rather than a gtk_window_set_group ().

One interesting question is whether the code should 
automatically or optionally make the windows in a window 
group an ICCCM window group by setting a group leader... I'm
leaving this undone for now.

I consider this to close:

 1579: Setting Gtk wwidget insensitive can lock GUI
 51746: Notification of shadowing by gtk_grab_add()

Regards,
                                        Owen

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gtk+/ChangeLog,v
retrieving revision 1.2030
diff -u -r1.2030 ChangeLog
--- ChangeLog	2001/06/08 16:06:50	1.2030
+++ ChangeLog	2001/06/08 22:02:09
@@ -1,3 +1,17 @@
+Fri Jun  8 17:56:52 2001  Owen Taylor  <otaylor redhat com>
+
+	* gtk/gtkwindow.[ch] gtk/gtkmain.c: Add a GtkWindowGroup struct
+	that allows grouping together multiple windows so that grabs
+	within that set of windows only affect those windows.
+
+	* gtk/gtkmain.c gtk/gtkwidget.[ch]: Add a "grab_notify"
+	signal for notification when a widget becomes shadowed
+	by a grab or is no longer shadowed by a grab.
+
+	* gtk/gtkwidget.c (gtk_widget_propagate_state)
+	  gtk/gtkmain.c: (gtk_widget_grab_add): Don't allow
+	insenstive widgets to maintain a grab.
+
 Fri Jun  8 12:03:07 2001  Owen Taylor  <otaylor redhat com>
 
 	* gdk/gdkkeys.[ch]: Add a direction-changed signal,
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.161
diff -u -r1.161 gtkmain.c
--- gtk/gtkmain.c	2001/05/12 13:16:40	1.161
+++ gtk/gtkmain.c	2001/06/08 22:02:09
@@ -110,6 +110,8 @@
 static void  gtk_print			 (gchar		     *str);
 #endif
 
+static GtkWindowGroup *gtk_main_get_window_group (GtkWidget   *widget);
+
 const guint gtk_major_version = GTK_MAJOR_VERSION;
 const guint gtk_minor_version = GTK_MINOR_VERSION;
 const guint gtk_micro_version = GTK_MICRO_VERSION;
@@ -122,9 +124,6 @@
 
 static GSList *main_loops = NULL;      /* stack of currently executing main loops */
 
-static GSList *grabs = NULL;		   /* A stack of unique grabs. The grabbing
-					    *  widget is the first one on the list.
-					    */
 static GList *init_functions = NULL;	   /* A list of init functions.
 					    */
 static GList *quit_functions = NULL;	   /* A list of quit functions.
@@ -750,6 +749,7 @@
 {
   GtkWidget *event_widget;
   GtkWidget *grab_widget;
+  GtkWindowGroup *window_group;
   GdkEvent *next_event;
   GList *tmp_list;
 
@@ -814,12 +814,14 @@
    * gtk_current_event_get().
    */
   current_events = g_list_prepend (current_events, event);
+
+  window_group = gtk_main_get_window_group (event_widget);
   
   /* If there is a grab in effect...
    */
-  if (grabs)
+  if (window_group->grabs)
     {
-      grab_widget = grabs->data;
+      grab_widget = window_group->grabs->data;
       
       /* If the grab widget is an ancestor of the event widget
        *  then we send the event to the original event widget.
@@ -851,7 +853,7 @@
       
     case GDK_DELETE:
       gtk_widget_ref (event_widget);
-      if ((!grabs || gtk_widget_get_toplevel (grabs->data) == event_widget) &&
+      if ((!window_group->grabs || gtk_widget_get_toplevel (window_group->grabs->data) == event_widget) &&
 	  !gtk_widget_event (event_widget, event))
 	gtk_widget_destroy (event_widget);
       gtk_widget_unref (event_widget);
@@ -970,39 +972,131 @@
   return FALSE;
 }
 
+static GtkWindowGroup *
+gtk_main_get_window_group (GtkWidget   *widget)
+{
+  GtkWidget *toplevel = NULL;
+
+  if (widget)
+    toplevel = gtk_widget_get_toplevel (widget);
+
+  if (toplevel && GTK_IS_WINDOW (toplevel))
+    return _gtk_window_get_group (GTK_WINDOW (toplevel));
+  else
+    return _gtk_window_get_group (NULL);
+}
+
+typedef struct
+{
+  gboolean was_grabbed;
+  GtkWidget *grab_widget;
+} GrabNotifyInfo;
+
+static void
+gtk_grab_notify_foreach (GtkWidget *child,
+			 gpointer   data)
+                        
+{
+  GrabNotifyInfo *info = data;
+
+  if (child != info->grab_widget)
+    {
+      g_object_ref (G_OBJECT (child));
+
+      gtk_signal_emit_by_name (GTK_OBJECT (child), "grab_notify", info->was_grabbed);
+
+      if (GTK_IS_CONTAINER (child))
+       gtk_container_foreach (GTK_CONTAINER (child), gtk_grab_notify_foreach, info);
+      
+      g_object_unref (G_OBJECT (child));
+    }
+}
+
+static void
+gtk_grab_notify (GtkWindowGroup *group,
+		 GtkWidget      *grab_widget,
+		 gboolean        was_grabbed)
+{
+  GList *toplevels;
+  GrabNotifyInfo info;
+
+  info.grab_widget = grab_widget;
+  info.was_grabbed = was_grabbed;
+
+  g_object_ref (group);
+  g_object_ref (grab_widget);
+
+  toplevels = gtk_window_list_toplevels ();
+  g_list_foreach (toplevels, (GFunc)g_object_ref, NULL);
+			    
+  while (toplevels)
+    {
+      GtkWindow *toplevel = toplevels->data;
+      toplevels = g_list_delete_link (toplevels, toplevels);
+
+      if (group == toplevel->group)
+	gtk_container_foreach (GTK_CONTAINER (toplevel), gtk_grab_notify_foreach, &info);
+      g_object_unref (toplevel);
+    }
+
+  g_object_unref (group);
+  g_object_unref (grab_widget);
+}
+
 void
 gtk_grab_add (GtkWidget *widget)
 {
+  GtkWindowGroup *group;
+  gboolean was_grabbed;
+  
   g_return_if_fail (widget != NULL);
   
-  if (!GTK_WIDGET_HAS_GRAB (widget))
+  if (!GTK_WIDGET_HAS_GRAB (widget) && GTK_WIDGET_IS_SENSITIVE (widget))
     {
       GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
       
-      grabs = g_slist_prepend (grabs, widget);
+      group = gtk_main_get_window_group (widget);
+
+      was_grabbed = (group->grabs != NULL);
+      
       gtk_widget_ref (widget);
+      group->grabs = g_slist_prepend (group->grabs, widget);
+
+      if (!was_grabbed)
+	gtk_grab_notify (group, widget, FALSE);
     }
 }
 
 GtkWidget*
 gtk_grab_get_current (void)
 {
-  if (grabs)
-    return GTK_WIDGET (grabs->data);
+  GtkWindowGroup *group;
+
+  group = gtk_main_get_window_group (NULL);
+
+  if (group->grabs)
+    return GTK_WIDGET (group->grabs->data);
   return NULL;
 }
 
 void
 gtk_grab_remove (GtkWidget *widget)
 {
+  GtkWindowGroup *group;
+  
   g_return_if_fail (widget != NULL);
   
   if (GTK_WIDGET_HAS_GRAB (widget))
     {
       GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
+
+      group = gtk_main_get_window_group (widget);
+      group->grabs = g_slist_remove (group->grabs, widget);
       
-      grabs = g_slist_remove (grabs, widget);
       gtk_widget_unref (widget);
+
+      if (!group->grabs)
+	gtk_grab_notify (group, widget, TRUE);
     }
 }
 
Index: gtk/gtkplug.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkplug.c,v
retrieving revision 1.25
diff -u -r1.25 gtkplug.c
--- gtk/gtkplug.c	2001/06/08 18:09:34	1.25
+++ gtk/gtkplug.c	2001/06/08 22:02:09
@@ -119,10 +119,6 @@
 
   window->type = GTK_WINDOW_TOPLEVEL;
   window->auto_shrink = TRUE;
-
-#if 0  
-  gtk_window_set_grab_group (window, window);
-#endif  
 }
 
 void
@@ -168,10 +164,11 @@
       plug->socket_window = NULL;
     }
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.221
diff -u -r1.221 gtkwidget.c
--- gtk/gtkwidget.c	2001/06/08 18:09:34	1.221
+++ gtk/gtkwidget.c	2001/06/08 22:02:10
@@ -65,6 +65,7 @@
   HIERARCHY_CHANGED,
   STYLE_SET,
   DIRECTION_CHANGED,
+  GRAB_NOTIFY,
   ADD_ACCELERATOR,
   REMOVE_ACCELERATOR,
   ACTIVATE_MNEMONIC,
@@ -632,6 +633,14 @@
 		    gtk_marshal_VOID__ENUM,
 		    GTK_TYPE_NONE, 1,
 		    GTK_TYPE_TEXT_DIRECTION);
+  widget_signals[GRAB_NOTIFY] =
+    gtk_signal_new ("grab_notify",
+		    GTK_RUN_FIRST,
+		    GTK_CLASS_TYPE (object_class),
+                    GTK_SIGNAL_OFFSET (GtkWidgetClass, grab_notify),
+		    gtk_marshal_VOID__BOOLEAN,
+		    GTK_TYPE_NONE, 1,
+		    GTK_TYPE_BOOL);
   widget_signals[ADD_ACCELERATOR] =
     gtk_accel_group_create_add (GTK_CLASS_TYPE (object_class), GTK_RUN_LAST,
 				GTK_SIGNAL_OFFSET (GtkWidgetClass, add_accelerator));
@@ -5287,7 +5296,12 @@
   if (old_state != GTK_WIDGET_STATE (widget))
     {
       gtk_widget_ref (widget);
+      
+      if (!GTK_WIDGET_IS_SENSITIVE (widget) && GTK_HAS_GRAB (widget))
+	gtk_grab_remove (widget);
+      
       gtk_signal_emit (GTK_OBJECT (widget), widget_signals[STATE_CHANGED], old_state);
+      
       
       if (GTK_IS_CONTAINER (widget))
 	{
Index: gtk/gtkwidget.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.h,v
retrieving revision 1.109
diff -u -r1.109 gtkwidget.h
--- gtk/gtkwidget.h	2001/06/08 18:09:34	1.109
+++ gtk/gtkwidget.h	2001/06/08 22:02:10
@@ -258,6 +258,8 @@
 				GtkStyle         *previous_style);
   void (* direction_changed)   (GtkWidget        *widget,
 				GtkTextDirection  previous_direction);
+  void (* grab_notify)         (GtkWidget        *widget,
+				gboolean          was_grabbed);
   
   /* accelerators */
   void (* add_accelerator)     (GtkWidget      *widget,
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.121
diff -u -r1.121 gtkwindow.c
--- gtk/gtkwindow.c	2001/06/08 18:09:34	1.121
+++ gtk/gtkwindow.c	2001/06/08 22:02:10
@@ -1599,6 +1599,9 @@
       gtk_widget_unref (GTK_WIDGET (window));
     }
 
+  if (window->group)
+    gtk_window_group_remove_window (window->group, window);
+
   GTK_OBJECT_CLASS (parent_class)->destroy (object);
 }
 
@@ -3159,8 +3162,6 @@
     }
 }
 
-
-
 /**
  * gtk_window_present:
  * @window: a #GtkWindow
@@ -3606,4 +3607,146 @@
                               button,
                               root_x, root_y,
                               timestamp);
+}
+
+
+static void
+gtk_window_group_class_init (GtkWindowGroupClass *klass)
+{
+}
+
+GtkType
+gtk_window_group_get_type (void)
+{
+  static GtkType window_group_type = 0;
+
+  if (!window_group_type)
+    {
+      static const GTypeInfo window_group_info =
+      {
+	sizeof (GtkWindowGroupClass),
+	NULL,		/* base_init */
+	NULL,		/* base_finalize */
+	(GClassInitFunc) gtk_window_group_class_init,
+	NULL,		/* class_finalize */
+	NULL,		/* class_data */
+	sizeof (GtkWindowGroup),
+	16,		/* n_preallocs */
+	(GInstanceInitFunc) NULL,
+      };
+
+      window_group_type = g_type_register_static (G_TYPE_OBJECT, "GtkWindowGroup", &window_group_info, 0);
+    }
+
+  return window_group_type;
+}
+
+/**
+ * gtk_window_group_new:
+ * 
+ * Create a new #GtkWindowGroup object. Grabs added with
+ * gtk_window_grab_add() only affect windows within the
+ * same #GtkWindowGroup
+ * 
+ * Return value: 
+ **/
+GtkWindowGroup *
+gtk_window_group_new (void)
+{
+  return g_object_new (GTK_TYPE_WINDOW_GROUP, NULL);
+}
+
+static void
+window_group_cleanup_grabs (GtkWindowGroup *group,
+			    GtkWindow      *window)
+{
+  GSList *tmp_list;
+  GSList *to_remove = NULL;
+
+  tmp_list = window_group->grabs;
+  while (tmp_list)
+    {
+      if (gtk_widget_get_toplevel (tmp_list->data) == window)
+	to_remove = g_slist_prepend (to_remove, g_object_ref (tmp_list->data));
+      tmp_list = tmp_list->next;
+    }
+
+  while (to_remove)
+    {
+      gtk_grab_remove (to_remove->data);
+      g_object_unref (to_remove->data);
+      to_remove = g_slist_delete_link (to_remove, to_remove);
+    }
+}
+
+/**
+ * gtk_window_group_add_widget:
+ * @window_group: a #GtkWindowGroup
+ * @window: the #GtkWindow to add
+ * 
+ * Add a window to a #GtkWindowGroup. 
+ **/
+void
+gtk_window_group_add_window (GtkWindowGroup *window_group,
+			     GtkWindow      *window)
+{
+  g_return_if_fail (GTK_IS_WINDOW_GROUP (window_group));
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  if (window->group != window_group)
+    {
+      g_object_ref (window);
+      g_object_ref (window_group);
+      
+      if (window->group)
+	gtk_window_group_remove_window (window->group, window);
+      else
+	window_group_cleanup_grabs (_gtk_window_get_group (NULL), window);
+
+      window->group = window_group;
+
+      g_object_unref (window);
+    }
+}
+
+/**
+ * gtk_window_group_remove_window:
+ * @window_group: a #GtkWindowGroup
+ * @window: the #GtkWindow to remove
+ * 
+ * Removes a window from a #GtkWindowGroup.
+ **/
+void
+gtk_window_group_remove_window (GtkWindowGroup *window_group,
+				GtkWindow      *window)
+{
+  g_return_if_fail (GTK_IS_WINDOW_GROUP (window_group));
+  g_return_if_fail (GTK_IS_WIDGET (window));
+  g_return_if_fail (window->group == window_group);
+
+  g_object_ref (window);
+
+  window_group_cleanup_grabs (window_group, window);
+  window->group = NULL;
+  
+  g_object_unref (G_OBJECT (window_group));
+  g_object_unref (window);
+}
+
+/* Return the group for the window or the default group
+ */
+GtkWindowGroup *
+_gtk_window_get_group (GtkWindow *window)
+{
+  if (window && window->group)
+    return window->group;
+  else
+    {
+      static GtkWindowGroup *default_group = NULL;
+
+      if (!default_group)
+	default_group = gtk_window_group_new ();
+
+      return default_group;
+    }
 }
Index: gtk/gtkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v
retrieving revision 1.40
diff -u -r1.40 gtkwindow.h
--- gtk/gtkwindow.h	2001/04/26 14:42:35	1.40
+++ gtk/gtkwindow.h	2001/06/08 22:02:10
@@ -50,6 +50,8 @@
 typedef struct _GtkWindow             GtkWindow;
 typedef struct _GtkWindowClass        GtkWindowClass;
 typedef struct _GtkWindowGeometryInfo GtkWindowGeometryInfo;
+typedef struct _GtkWindowGroup        GtkWindowGroup;
+typedef struct _GtkWindowGroupClass   GtkWindowGroupClass;
 
 struct _GtkWindow
 {
@@ -65,6 +67,7 @@
   GtkWindow *transient_parent;
   GtkWindowGeometryInfo *geometry_info;
   GdkWindow *frame;
+  GtkWindowGroup *group;
 
   guint16 resize_count;
 
@@ -123,7 +126,25 @@
                                         GtkDirectionType direction);  
 };
 
+#define GTK_TYPE_WINDOW_GROUP             (gtk_window_group_get_type ())
+#define GTK_WINDOW_GROUP(object)          (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_WINDOW_GROUP, GtkWindowGroup))
+#define GTK_WINDOW_GROUP_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_WINDOW_GROUP, GtkWindowGroupClass))
+#define GTK_IS_WINDOW_GROUP(object)       (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_WINDOW_GROUP))
+#define GTK_IS_WINDOW_GROUP_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_WINDOW_GROUP))
+#define GTK_WINDOW_GET_CLASS(obj)         (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_WINDOW_GROUP, GtkWindowGroupClass))
 
+struct _GtkWindowGroup
+{
+  GObject parent_instance;
+
+  GSList *grabs;
+};
+
+struct _GtkWindowGroupClass
+{
+  GObjectClass parent_class;
+};
+
 GtkType    gtk_window_get_type                 (void) G_GNUC_CONST;
 GtkWidget* gtk_window_new                      (GtkWindowType        type);
 void       gtk_window_set_title                (GtkWindow           *window,
@@ -175,9 +196,9 @@
                                                gboolean   setting);
 
 /* If window is set modal, input will be grabbed when show and released when hide */
-void       gtk_window_set_modal                (GtkWindow           *window,
-                                                gboolean             modal);
-GList*	   gtk_window_list_toplevels           (void);
+void       gtk_window_set_modal      (GtkWindow *window,
+				      gboolean   modal);
+GList*     gtk_window_list_toplevels (void);
 
 void     gtk_window_add_mnemonic          (GtkWindow       *window,
 					   guint            keyval,
@@ -225,23 +248,34 @@
 						gint                 width,
 						gint                 height);
 
+/* Window groups
+ */
+GType            gtk_window_group_get_type      (void) G_GNUC_CONST;;
+
+GtkWindowGroup * gtk_window_group_new           (void);
+void             gtk_window_group_add_window    (GtkWindowGroup     *window_group,
+						 GtkWindow          *window);
+void             gtk_window_group_remove_window (GtkWindowGroup     *window_group,
+					         GtkWindow          *window);
+
 /* --- internal functions --- */
-void       gtk_window_set_focus                (GtkWindow           *window,
-						GtkWidget           *focus);
-void       gtk_window_set_default              (GtkWindow           *window,
-						GtkWidget           *defaultw);
-void       gtk_window_remove_embedded_xid      (GtkWindow           *window,
-				                guint                xid);
-void       gtk_window_add_embedded_xid         (GtkWindow           *window,
-						guint                xid);
-void       _gtk_window_reposition              (GtkWindow           *window,
-						gint                 x,
-						gint                 y);
-void       _gtk_window_constrain_size          (GtkWindow           *window,
-						gint                 width,
-						gint                 height,
-						gint                *new_width,
-						gint                *new_height);
+void            gtk_window_set_focus           (GtkWindow *window,
+						GtkWidget *focus);
+void            gtk_window_set_default         (GtkWindow *window,
+						GtkWidget *defaultw);
+void            gtk_window_remove_embedded_xid (GtkWindow *window,
+						guint      xid);
+void            gtk_window_add_embedded_xid    (GtkWindow *window,
+						guint      xid);
+void            _gtk_window_reposition         (GtkWindow *window,
+						gint       x,
+						gint       y);
+void            _gtk_window_constrain_size     (GtkWindow *window,
+						gint       width,
+						gint       height,
+						gint      *new_width,
+						gint      *new_height);
+GtkWindowGroup *_gtk_window_get_group          (GtkWindow *window);
 
 #ifdef __cplusplus
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]