Adding a ::screen-changed property



After resisting it for a long time, I've come to the conclusion that
we really need a ::screen-changed property on GtkWidget.

It does add one more signal when adding a widget to a widget
heirarchy, but a no-op signal is only about 3000 cycles (100,000 a
second on a 300mhz machine) so that isn't a _huge_ concern.

The reason for it is that you need to track the quantity fairly often
when writing multihead-safe code (See GtkToolbar and GtkFontSelection)
and that it is a big pain to do with only the primitives we have
currently (See GtkToolbar and GtkFontSelection).

The other reason I was inspired to add it was that I needed
the screen-tracking machinery to fix 
http://bugzilla.gnome.org/show_bug.cgi?id=85709.

The attach patch implements this; note that the quantity that
the signal tracks is not the result of gtk_widget_get_screen()
but rather the result of:

 if (gtk_widget_has_screen (widget))
   return gtk_widget_get_screen (widget);
 else
   return NULL;

That is, unanchored widgets are considered to have a screen
of NULL. 

[ It definitely sucks that this distinction is necessary, but 
  I'm hesistant to change gtk_widget_get_screen() now ]

Regards,
                                        Owen

? gtk/gtkcombo.c.owt
Index: gtk/gtkfontsel.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkfontsel.c,v
retrieving revision 1.77
diff -u -p -r1.77 gtkfontsel.c
--- gtk/gtkfontsel.c	9 Oct 2002 22:11:07 -0000	1.77
+++ gtk/gtkfontsel.c	5 Nov 2002 22:02:05 -0000
@@ -118,8 +118,8 @@ static void    gtk_font_selection_get_pr
 						      GParamSpec      *pspec);
 static void    gtk_font_selection_init		     (GtkFontSelection      *fontsel);
 static void    gtk_font_selection_finalize	     (GObject               *object);
-static void    gtk_font_selection_hierarchy_changed  (GtkWidget		    *widget,
-						      GtkWidget             *previous_toplevel);
+static void    gtk_font_selection_screen_changed     (GtkWidget		    *widget,
+						      GdkScreen             *previous_screen);
 
 /* These are the callbacks & related functions. */
 static void     gtk_font_selection_select_font           (GtkTreeSelection *selection,
@@ -200,7 +200,7 @@ gtk_font_selection_class_init (GtkFontSe
   gobject_class->set_property = gtk_font_selection_set_property;
   gobject_class->get_property = gtk_font_selection_get_property;
 
-  widget_class->hierarchy_changed = gtk_font_selection_hierarchy_changed;
+  widget_class->screen_changed = gtk_font_selection_screen_changed;
    
   g_object_class_install_property (gobject_class,
                                    PROP_FONT_NAME,
@@ -522,60 +522,17 @@ gtk_font_selection_finalize (GObject *ob
 }
 
 static void
-fontsel_screen_changed (GtkFontSelection *fontsel)
+gtk_font_selection_screen_changed (GtkWidget *widget,
+				   GdkScreen *previous_screen)
 {
-  GdkScreen *old_screen = g_object_get_data (G_OBJECT (fontsel), "gtk-font-selection-screen");
-  GdkScreen *screen;
+  GtkFontSelection *fontsel = GTK_FONT_SELECTION (widget);
 
   if (gtk_widget_has_screen (GTK_WIDGET (fontsel)))
-    screen = gtk_widget_get_screen (GTK_WIDGET (fontsel));
-  else
-    screen = NULL;
-
-  if (screen == old_screen)
-    return;
-
-  if (fontsel->font)
-    {
-      gdk_font_unref (fontsel->font);
-      fontsel->font = NULL;
-    }
-
-  if (old_screen)
-    g_object_unref (old_screen);
-  
-  if (screen)
     {
-      g_object_ref (screen);
-      g_object_set_data (G_OBJECT (fontsel), "gtk-font-selection-screen", screen);
-
       gtk_font_selection_show_available_fonts (fontsel);
       gtk_font_selection_show_available_sizes (fontsel, TRUE);
       gtk_font_selection_show_available_styles (fontsel);
     }
-  else
-    g_object_set_data (G_OBJECT (fontsel), "gtk-font-selection-screen", NULL);
-}
-
-static void
-gtk_font_selection_hierarchy_changed (GtkWidget *widget,
-				      GtkWidget *previous_toplevel)
-{
-  GtkWidget *toplevel;
-  
-  if (previous_toplevel)
-    g_signal_handlers_disconnect_by_func (previous_toplevel,
-					  fontsel_screen_changed,
-					  widget);
-  
-  toplevel = gtk_widget_get_toplevel (widget);
-  if (GTK_WIDGET_TOPLEVEL (toplevel))
-    g_signal_connect_swapped (toplevel,
-			      "notify::screen",
-			      G_CALLBACK (fontsel_screen_changed),
-			      widget);
-  
-  fontsel_screen_changed (GTK_FONT_SELECTION (widget));
 }
 
 static void
Index: gtk/gtkinvisible.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkinvisible.c,v
retrieving revision 1.19
diff -u -p -r1.19 gtkinvisible.c
--- gtk/gtkinvisible.c	10 Oct 2002 22:00:09 -0000	1.19
+++ gtk/gtkinvisible.c	5 Nov 2002 22:02:05 -0000
@@ -189,6 +189,8 @@ void
 gtk_invisible_set_screen (GtkInvisible *invisible,
 			  GdkScreen    *screen)
 {
+  GtkWidget *widget;
+  GdkScreen *previous_screen;
   gboolean was_realized;
   
   g_return_if_fail (GTK_IS_INVISIBLE (invisible));
@@ -197,16 +199,21 @@ gtk_invisible_set_screen (GtkInvisible *
   if (screen == invisible->screen)
     return;
 
+  widget = GTK_WIDGET (invisible);
+
+  previous_screen = invisible->screen;
   was_realized = GTK_WIDGET_REALIZED (invisible);
 
   if (was_realized)
-    gtk_widget_unrealize (GTK_WIDGET (invisible));
+    gtk_widget_unrealize (widget);
   
   invisible->screen = screen;
+  if (screen != previous_screen)
+    _gtk_widget_propagate_screen_changed (widget, previous_screen);
   g_object_notify (G_OBJECT (invisible), "screen");
   
   if (was_realized)
-    gtk_widget_realize (GTK_WIDGET (invisible));
+    gtk_widget_realize (widget);
 }
 
 /**
Index: gtk/gtktoolbar.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktoolbar.c,v
retrieving revision 1.82
diff -u -p -r1.82 gtktoolbar.c
--- gtk/gtktoolbar.c	2 Nov 2002 05:37:03 -0000	1.82
+++ gtk/gtktoolbar.c	5 Nov 2002 22:02:05 -0000
@@ -94,8 +94,8 @@ static void gtk_toolbar_style_set       
                                                   GtkStyle        *prev_style);
 static gboolean gtk_toolbar_focus                (GtkWidget       *widget,
                                                   GtkDirectionType dir);
-static void gtk_toolbar_hierarchy_changed        (GtkWidget       *widget,
-						  GtkWidget       *previous_toplevel);
+static void gtk_toolbar_screen_changed           (GtkWidget       *widget,
+						  GdkScreen       *previous_screen);
 static void gtk_toolbar_show_all                 (GtkWidget       *widget);
 static void gtk_toolbar_add                      (GtkContainer    *container,
 				                  GtkWidget       *widget);
@@ -195,7 +195,7 @@ gtk_toolbar_class_init (GtkToolbarClass 
   widget_class->style_set = gtk_toolbar_style_set;
   widget_class->show_all = gtk_toolbar_show_all;
   widget_class->focus = gtk_toolbar_focus;
-  widget_class->hierarchy_changed = gtk_toolbar_hierarchy_changed;
+  widget_class->screen_changed = gtk_toolbar_screen_changed;
   
   container_class->add = gtk_toolbar_add;
   container_class->remove = gtk_toolbar_remove;
@@ -329,8 +329,10 @@ toolbar_get_settings (GtkToolbar *toolba
 }
 
 static void
-toolbar_screen_changed (GtkToolbar *toolbar)
+gtk_toolbar_screen_changed (GtkWidget *widget,
+			    GdkScreen *previous_screen)
 {
+  GtkToolbar *toolbar = GTK_TOOLBAR (widget);
   GtkSettings *old_settings = toolbar_get_settings (toolbar);
   GtkSettings *settings;
 
@@ -373,27 +375,6 @@ toolbar_screen_changed (GtkToolbar *tool
 
   style_change_notify (toolbar);
   icon_size_change_notify (toolbar);
-}
-
-static void
-gtk_toolbar_hierarchy_changed (GtkWidget *widget,
-			       GtkWidget *previous_toplevel)
-{
-  GtkWidget *toplevel;
-  
-  if (previous_toplevel)
-    g_signal_handlers_disconnect_by_func (previous_toplevel,
-					  toolbar_screen_changed,
-					  widget);
-
-  toplevel = gtk_widget_get_toplevel (widget);
-  if (GTK_WIDGET_TOPLEVEL (toplevel))
-    g_signal_connect_swapped (toplevel,
-			      "notify::screen",
-			      G_CALLBACK (toolbar_screen_changed),
-			      widget);
-  
-  toolbar_screen_changed (GTK_TOOLBAR (widget));
 }
 
 static void
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.330
diff -u -p -r1.330 gtkwidget.c
--- gtk/gtkwidget.c	13 Oct 2002 20:44:17 -0000	1.330
+++ gtk/gtkwidget.c	5 Nov 2002 22:02:05 -0000
@@ -116,6 +116,7 @@ enum {
   POPUP_MENU,
   SHOW_HELP,
   ACCEL_CLOSURES_CHANGED,
+  SCREEN_CHANGED,
   LAST_SIGNAL
 };
 
@@ -379,6 +380,7 @@ gtk_widget_class_init (GtkWidgetClass *k
   klass->drag_motion = NULL;
   klass->drag_drop = NULL;
   klass->drag_data_received = NULL;
+  klass->screen_changed = NULL;
 
   klass->show_help = gtk_widget_real_show_help;
   
@@ -1053,6 +1055,15 @@ gtk_widget_class_init (GtkWidgetClass *k
 		  NULL, NULL,
 		  _gtk_marshal_NONE__NONE,
 		  G_TYPE_NONE, 0);
+  widget_signals[SCREEN_CHANGED] =
+    g_signal_new ("screen_changed",
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (GtkWidgetClass, hierarchy_changed),
+		  NULL, NULL,
+		  _gtk_marshal_VOID__OBJECT,
+		  G_TYPE_NONE, 1,
+		  GDK_TYPE_SCREEN);
   
   binding_set = gtk_binding_set_by_class (klass);
   gtk_binding_entry_add_signal (binding_set, GDK_F10, GDK_SHIFT_MASK,
@@ -4289,11 +4300,27 @@ gtk_widget_set_style_internal (GtkWidget
   g_object_unref (widget);
 }
 
+typedef struct {
+  GtkWidget *previous_toplevel;
+  GdkScreen *previous_screen;
+  GdkScreen *new_screen;
+} HierarchyChangedInfo;
+
+static void
+do_screen_change (GtkWidget *widget,
+		  GdkScreen *old_screen,
+		  GdkScreen *new_screen)
+{
+  if (old_screen != new_screen)
+    g_signal_emit (widget, widget_signals[SCREEN_CHANGED], 0, old_screen);
+}
+
 static void
 gtk_widget_propagate_hierarchy_changed_recurse (GtkWidget *widget,
 						gpointer   client_data)
 {
   gboolean new_anchored;
+  HierarchyChangedInfo *info = client_data;
 
   new_anchored = GTK_WIDGET_TOPLEVEL (widget) ||
                  (widget->parent && GTK_WIDGET_ANCHORED (widget->parent));
@@ -4307,8 +4334,9 @@ gtk_widget_propagate_hierarchy_changed_r
       else
 	GTK_PRIVATE_UNSET_FLAG (widget, GTK_ANCHORED);
       
-      g_signal_emit (widget, widget_signals[HIERARCHY_CHANGED], 0, client_data);
-  
+      g_signal_emit (widget, widget_signals[HIERARCHY_CHANGED], 0, info->previous_toplevel);
+      do_screen_change (widget, info->previous_screen, info->new_screen);
+      
       if (GTK_IS_CONTAINER (widget))
 	gtk_container_forall (GTK_CONTAINER (widget),
 			      gtk_widget_propagate_hierarchy_changed_recurse,
@@ -4331,13 +4359,72 @@ void
 _gtk_widget_propagate_hierarchy_changed (GtkWidget    *widget,
 					 GtkWidget    *previous_toplevel)
 {
+  HierarchyChangedInfo info;
+
+  info.previous_toplevel = previous_toplevel;
+  info.previous_screen = previous_toplevel ? gtk_widget_get_screen (previous_toplevel) : NULL;
+
+  if (GTK_WIDGET_TOPLEVEL (widget) ||
+      (widget->parent && GTK_WIDGET_ANCHORED (widget->parent)))
+    info.new_screen = gtk_widget_get_screen (widget);
+  else
+    info.new_screen = NULL;
+
+  if (info.previous_screen)
+    g_object_ref (info.previous_screen);
   if (previous_toplevel)
     g_object_ref (previous_toplevel);
 
-  gtk_widget_propagate_hierarchy_changed_recurse (widget, previous_toplevel);
-  
+  gtk_widget_propagate_hierarchy_changed_recurse (widget, &info);
+
   if (previous_toplevel)
     g_object_unref (previous_toplevel);
+  if (info.previous_screen)
+    g_object_unref (info.previous_screen);
+}
+
+static void
+gtk_widget_propagate_screen_changed_recurse (GtkWidget *widget,
+					     gpointer   client_data)
+{
+  HierarchyChangedInfo *info = client_data;
+
+  g_object_ref (widget);
+  
+  do_screen_change (widget, info->previous_screen, info->new_screen);
+  
+  if (GTK_IS_CONTAINER (widget))
+    gtk_container_forall (GTK_CONTAINER (widget),
+			  gtk_widget_propagate_hierarchy_changed_recurse,
+			  client_data);
+  
+  g_object_unref (widget);
+}
+
+/**
+ * _gtk_widget_propagate_screen_changed:
+ * @widget: a #GtkWidget
+ * @previous_screen: Previous screen
+ * 
+ * Propagates changes in the screen for a widget to all
+ * children, emitting ::screen_changed.
+ **/
+void
+_gtk_widget_propagate_screen_changed (GtkWidget    *widget,
+				      GdkScreen    *previous_screen)
+{
+  HierarchyChangedInfo info;
+
+  info.previous_screen = previous_screen;
+  info.new_screen = gtk_widget_get_screen (widget);
+
+  if (previous_screen)
+    g_object_ref (previous_screen);
+
+  gtk_widget_propagate_screen_changed_recurse (widget, &info);
+
+  if (previous_screen)
+    g_object_unref (previous_screen);
 }
 
 static void
@@ -4666,7 +4753,7 @@ gtk_widget_get_screen_unchecked (GtkWidg
  * 
  * Get the #GdkScreen from the toplevel window associated with
  * this widget. This function can only be called after the widget
- * has been added to a widget heirarchy with a #GtkWindow
+ * has been added to a widget hierarchy with a #GtkWindow
  * at the top.
  *
  * In general, you should only create screen specific
Index: gtk/gtkwidget.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.h,v
retrieving revision 1.138
diff -u -p -r1.138 gtkwidget.h
--- gtk/gtkwidget.h	13 Oct 2002 20:44:18 -0000	1.138
+++ gtk/gtkwidget.h	5 Nov 2002 22:02:05 -0000
@@ -398,6 +398,8 @@ struct _GtkWidgetClass
    */
   AtkObject*   (* get_accessible)  (GtkWidget          *widget);
 
+  void (* screen_changed) (GtkWidget *widget,
+			   GdkScreen *previous_screen);
   /* Padding for future expansion */
   void (*_gtk_reserved1) (void);
   void (*_gtk_reserved2) (void);
@@ -761,6 +763,8 @@ GtkWidgetAuxInfo *_gtk_widget_get_aux_in
 							   gboolean      create);
 void              _gtk_widget_propagate_hierarchy_changed (GtkWidget    *widget,
 							   GtkWidget    *previous_toplevel);
+void              _gtk_widget_propagate_screen_changed    (GtkWidget    *widget,
+							   GdkScreen    *previous_screen);
 
 GdkColormap* _gtk_widget_peek_colormap (void);
 
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.230
diff -u -p -r1.230 gtkwindow.c
--- gtk/gtkwindow.c	2 Nov 2002 05:37:03 -0000	1.230
+++ gtk/gtkwindow.c	5 Nov 2002 22:02:05 -0000
@@ -5982,6 +5982,8 @@ gtk_window_set_screen (GtkWindow *window
 		       GdkScreen *screen)
 {
   GtkWidget *widget;
+  GdkScreen *previous_screen;
+  GdkScreen *new_screen;
   gboolean was_mapped;
   
   g_return_if_fail (GTK_IS_WINDOW (window));
@@ -5991,17 +5993,20 @@ gtk_window_set_screen (GtkWindow *window
     return;
 
   widget = GTK_WIDGET (window);
-  
+
+  previous_screen = window->screen;
   was_mapped = GTK_WIDGET_MAPPED (widget);
 
   if (was_mapped)
     gtk_widget_unmap (widget);
   if (GTK_WIDGET_REALIZED (widget))
     gtk_widget_unrealize (widget);
-  
+      
   gtk_window_free_key_hash (window);
   window->screen = screen;
   gtk_widget_reset_rc_styles (widget);
+  if (screen != previous_screen)
+    _gtk_widget_propagate_screen_changed (widget, previous_screen);
   g_object_notify (G_OBJECT (window), "screen");
 
   if (was_mapped)


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