Re: Interface properties (Re: 2.4 schedule)



On Mon, 2003-10-13 at 23:51, Owen Taylor wrote: 
> 
> On Mon, 2003-10-13 at 19:05, Owen Taylor wrote:
> 
> > What I'm going to do is produce a version of my patch that:
> >  
> >  - Changes g_object_interface_find/list_properties to take
> > 
> >  - Has the docs moved out of the source files
> > 
> >  - Removes the g_param_spec_set_redirect_target() in favor
> >    of storing the data in GParamSpecOverride
> > 
> >  - Leaves GParamSpecOverride fully functional in terms of
> >    get_name/get_nick/validate/etc.
> > 
> >  - Leaves the GParamSpecOverrides visible in constructor(),
> >    notify, since I don't think it's worth the expense of fully
> >    hiding them, and since I think that keeping the right
> >    paramspec ID's is important.
> > 
> > This is what I believe should go into GObject. If you disagree
> > after seeing the patch, we can then discuss ways we can get
> > something more to your taste in expeditiously.
> 
> Here's the patch as promised. I don't know of any outstanding issues
> with it. (You could argue that g_param_spec_override() shouldn't
> take a name, but I think the consistency with the other 
> g_param_spec_* functions makes it worth keeping.)

New version of the patch. As discussed on IRC, I changed it so
the pspec passed to set_property(), get_property(), and ::notify
is now the redirected property.

The pspecs passed to ->constructor() stay GParamSpecOverride.

Regards,
					Owen

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/glib/ChangeLog,v
retrieving revision 1.1418
diff -u -p -u -r1.1418 ChangeLog
--- ChangeLog	8 Oct 2003 21:44:03 -0000	1.1418
+++ ChangeLog	14 Oct 2003 22:02:10 -0000
@@ -1,3 +1,8 @@
+Tue Oct 14 17:44:38 2003  Owen Taylor  <otaylor redhat com>
+
+	* tests/gobject/ifaceproperties.c: Test for interface
+	properties and GParamSpecOverride.
+
 Wed Oct  8 23:40:26 2003  Matthias Clasen  <maclas gmx de>
 
 	* glib/gmarkup.c (g_markup_printf_escaped): 
Index: docs/reference/ChangeLog
===================================================================
RCS file: /cvs/gnome/glib/docs/reference/ChangeLog,v
retrieving revision 1.220
diff -u -p -u -r1.220 ChangeLog
--- docs/reference/ChangeLog	5 Oct 2003 21:32:05 -0000	1.220
+++ docs/reference/ChangeLog	14 Oct 2003 22:02:10 -0000
@@ -1,3 +1,9 @@
+Tue Oct 14 17:45:33 2003  Owen Taylor  <otaylor redhat com>
+
+	* gobject/gobject-sections.txt gobject/tmpl/objects.sgml
+	gobject/tmpl/param_value_types.sgml gobject/tmpl/gparamspec.sgml:
+	Document interface properties and GParamSpecOverride.
+
 Sun Oct  5 23:23:53 2003  Matthias Clasen  <maclas gmx de>
 
 	* gobject/glib-sections.txt: 
Index: docs/reference/gobject/gobject-sections.txt
===================================================================
RCS file: /cvs/gnome/glib/docs/reference/gobject/gobject-sections.txt,v
retrieving revision 1.30
diff -u -p -u -r1.30 gobject-sections.txt
--- docs/reference/gobject/gobject-sections.txt	5 Oct 2003 21:32:07 -0000	1.30
+++ docs/reference/gobject/gobject-sections.txt	14 Oct 2003 22:02:10 -0000
@@ -208,6 +208,10 @@ G_OBJECT_CLASS_NAME
 g_object_class_install_property
 g_object_class_find_property
 g_object_class_list_properties
+g_object_class_override_property
+g_object_interface_install_property
+g_object_interface_find_property
+g_object_interface_list_properties
 g_object_new
 g_object_newv
 GParameter
@@ -377,6 +381,7 @@ g_param_spec_get_qdata
 g_param_spec_set_qdata
 g_param_spec_set_qdata_full
 g_param_spec_steal_qdata
+g_param_spec_get_redirect_target
 g_param_spec_internal
 GParamSpecTypeInfo
 g_param_type_register_static
@@ -601,6 +606,13 @@ G_PARAM_SPEC_VALUE_ARRAY
 G_TYPE_PARAM_VALUE_ARRAY
 GParamSpecValueArray
 g_param_spec_value_array
+
+<SUBSECTION Override>
+G_IS_PARAM_SPEC_OVERRIDE
+G_PARAM_SPEC_OVERRIDE
+G_TYPE_PARAM_OVERRIDE
+GParamSpecOverride
+g_param_spec_override
 
 <SUBSECTION Private>
 g_value_set_instance
Index: docs/reference/gobject/tmpl/gparamspec.sgml
===================================================================
RCS file: /cvs/gnome/glib/docs/reference/gobject/tmpl/gparamspec.sgml,v
retrieving revision 1.9
diff -u -p -u -r1.9 gparamspec.sgml
--- docs/reference/gobject/tmpl/gparamspec.sgml	29 Sep 2003 22:48:05 -0000	1.9
+++ docs/reference/gobject/tmpl/gparamspec.sgml	14 Oct 2003 22:02:10 -0000
@@ -128,6 +128,13 @@ can be configured.
 @G_PARAM_LAX_VALIDATION: upon parameter conversion (see g_param_value_convert())
                          strict validation is not required
 @G_PARAM_PRIVATE: 
+ G_PARAM_REDIRECT:       indicates that the parameter redirects to
+                         another parameter; this flag is always set for
+                         #GParamSpecOverride, and must not be set for
+                         other paramspec types.
+
+
+
 
 <!-- ##### MACRO G_PARAM_READWRITE ##### -->
 <para>
@@ -332,6 +339,22 @@ user data pointers with a destroy notifi
 @pspec: the #GParamSpec to get a stored user data pointer from
 @quark: a #GQuark, naming the user data pointer
 @Returns: the user data pointer set, or %NULL
+
+
+<!-- ##### FUNCTION g_param_spec_get_redirect_target ##### -->
+<para>
+If the paramspec redirects operations to another paramspec,
+returns that paramspec. Redirect is used typically for
+providing a new implementation of a property in a derived
+type while perserving all the properties from the parent
+type. Redirection is established by creating a property
+of type #GParamSpecOverride. See g_object_override_property()
+for an example of the use of this capability.
+</para>
+
+ pspec: a #GParamSpec
+ Returns: paramspec to which requests on this paramspec should
+  be redirected, or %NULL if none.
 
 
 <!-- ##### FUNCTION g_param_spec_internal ##### -->
Index: docs/reference/gobject/tmpl/objects.sgml
===================================================================
RCS file: /cvs/gnome/glib/docs/reference/gobject/tmpl/objects.sgml,v
retrieving revision 1.21
diff -u -p -u -r1.21 objects.sgml
--- docs/reference/gobject/tmpl/objects.sgml	11 Jun 2003 21:18:55 -0000	1.21
+++ docs/reference/gobject/tmpl/objects.sgml	14 Oct 2003 22:02:11 -0000
@@ -181,6 +181,92 @@ Returns an array of #GParamSpec* for all
 @Returns: an array of #GParamSpec* which should be freed after use
 
 
+<!-- ##### FUNCTION g_object_class_override_property ##### -->
+<para>
+Registers @property_id as referring to a property with the
+name @name in a parent class or in an interface implemented
+by @oclass. This allows this class to <firstterm>override</firstterm>
+a property implementation in a parent class or to provide
+the implementation of a property from an interface.
+</para>
+<note>
+<para>
+Internally, overriding is implemented by creating a property of type
+#GParamSpecOverride; generally operations that query the properties of
+the object class, such as g_object_class_find_property() or
+g_object_class_list_properties() will return the overridden
+property. However, in one case, the @construct_properties argument of
+the @constructor virtual function, the #GParamSpecOverride is passed
+instead, so that the @param_id field of the #GParamSpec will be
+correct.  For virtually all uses, this makes no difference. If you
+need to get the overridden property, you can call
+g_param_spec_get_redirect_target().
+</para>
+</note>
+
+ oclass: a #GObjectClass
+ property_id: the new property ID
+ name: the name of a property registered in a parent class or
+       in an interface of this class.
+
+
+<!-- ##### FUNCTION g_object_interface_install_property ##### -->
+<para>
+Add a property to an interface; this is only useful for interfaces
+that are added to GObject-derived types. Adding a property to an
+interface forces all objects classes with that interface to have a
+compatible property. The compatible property could be a newly
+created #GParamSpec, but normally
+g_object_class_override_property() will be used so that the object
+class only needs to provide an implementation and inherits the
+property description, default value, bounds, and so forth from the
+interface property.
+</para>
+<para>
+This function is meant to be called from the interface's default
+vtable initialization function (the @class_init member of
+#GTypeInfo.) It must not be called after after @class_init has
+been called for any object types implementing this interface.
+</para>
+
+ g_iface: any interface vtable for the interface, or the default
+ vtable for the interface.
+ pspec: the #GParamSpec for the new property
+
+
+<!-- ##### FUNCTION g_object_interface_find_property ##### -->
+<para>
+Find the #GParamSpec with the given name for an
+interface. Generally, the interface vtable passed in as @g_iface
+will be the default vtable from g_type_default_interface_ref(), or,
+if you know the interface has already been loaded,
+g_type_default_interface_peek().
+</para>
+
+ g_iface: any interface vtable for the interface, or the default
+  vtable for the interface
+ property_name: name of a property to lookup.
+ Returns: the #GParamSpec for the property of the
+  interface with the name @property_name, or %NULL
+  if no such property exists.
+
+
+<!-- ##### FUNCTION g_object_interface_list_properties ##### -->
+<para>
+Lists the properties of an interface.Generally, the interface
+vtable passed in as @g_iface will be the default vtable from
+g_type_default_interface_ref(), or, if you know the interface has
+already been loaded, g_type_default_interface_peek().
+</para>
+
+ g_iface: any interface vtable for the interface, or the default
+ vtable for the interface
+ n_properties_p: location to store number of properties returned.
+ Returns: a pointer to an array of pointers to #GParamSpec structures.
+  The paramspecs are owned by GLib, but the array should
+  be freed with g_free() when you are done with it.
+
+
 <!-- ##### FUNCTION g_object_new ##### -->
 <para>
 Creates a new instance of a #GObject subtype and sets its properties.
Index: docs/reference/gobject/tmpl/param_value_types.sgml
===================================================================
RCS file: /cvs/gnome/glib/docs/reference/gobject/tmpl/param_value_types.sgml,v
retrieving revision 1.14
diff -u -p -u -r1.14 param_value_types.sgml
--- docs/reference/gobject/tmpl/param_value_types.sgml	12 Sep 2003 20:11:38 -0000	1.14
+++ docs/reference/gobject/tmpl/param_value_types.sgml	14 Oct 2003 22:02:11 -0000
@@ -1640,3 +1640,51 @@ See g_param_spec_internal() for details 
 @Returns: a newly created parameter specification
 
 
+<!-- ##### MACRO G_IS_PARAM_SPEC_OVERRIDE ##### -->
+<para>
+Returns whether the given #GParamSpec is of type %G_TYPE_PARAM_OBJECT.
+</para>
+
+ pspec: a #GParamSpec
+
+
+<!-- ##### MACRO G_PARAM_SPEC_OVERRIDE ##### -->
+<para>
+Casts a #GParamSpec into a #GParamSpecObject.
+</para>
+
+ pspec:  a #GParamSpec
+
+
+<!-- ##### MACRO G_TYPE_PARAM_OVERRIDE ##### -->
+<para>
+The #GType of #GParamSpecOverride.
+</para>
+
+
+
+<!-- ##### STRUCT GParamSpecOverride ##### -->
+<para>
+This is a type of #GParamSpec type that simply redirects operations to
+another paramspec.  All operations other than getting or
+setting the value are redirected, including accessing the nick and
+blurb, validating a value, and so forth. See
+g_param_spec_get_redirect_target() for retrieving the overidden
+property. #GParamSpecOverride is used in implementing
+g_object_class_override_property(), and will not be directly useful
+unless you are implementing a new base type similar to GObject.
+</para>
+
+
+<!-- ##### FUNCTION g_param_spec_override ##### -->
+<para>
+Creates a new property of type #GParamSpecOverride. This is used
+to direct operations to another paramspec, and will not be directly
+useful unless you are implementing a new base type similar to GObject.
+</para>
+
+ name: the name of the property.
+ overridden: The property that is being overridden
+ Returns: the newly created #GParamSpec
+
+
Index: gobject/ChangeLog
===================================================================
RCS file: /cvs/gnome/glib/gobject/ChangeLog,v
retrieving revision 1.268
diff -u -p -u -r1.268 ChangeLog
--- gobject/ChangeLog	2 Oct 2003 05:41:04 -0000	1.268
+++ gobject/ChangeLog	14 Oct 2003 22:02:11 -0000
@@ -1,3 +1,21 @@
+Tue Oct 14 17:40:19 2003  Owen Taylor  <otaylor redhat com>
+
+	* gparamspecs.[ch]: Add a new GParamSpecOverride type
+	that is a pointer to a different paramspec in a parent
+	class or interface.
+
+	* gparam.[ch]: Add g_paramspec_get_redirect_target()
+	which follows GParamSpecOverride to the real property.
+	Make g_param_spec_pool_list() hand redirections, 
+	properties on interfaces.
+
+	* gobject.[ch]: Add g_object_interface_install_property,
+	g_object_interface_find_property,
+	g_object_interface_list_properties(). Redirect virtually
+	all publically exposed GParamSpec's to the redirect
+	target if any. (->constructor is the exception.)
+	(#105894)
+
 Thu Oct  2 07:37:12 2003  Tim Janik  <timj gtk org>
 
 	* gtype.c: fix post class_init interface initialization logic
Index: gobject/gobject.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gobject.c,v
retrieving revision 1.61
diff -u -p -u -r1.61 gobject.c
--- gobject/gobject.c	12 Sep 2003 20:11:36 -0000	1.61
+++ gobject/gobject.c	14 Oct 2003 22:02:11 -0000
@@ -97,6 +97,9 @@ static inline void	   object_set_propert
 							 const GValue   *value,
 							 GObjectNotifyQueue *nqueue);
 
+static void object_interface_check_properties           (gpointer        func_data,
+							 gpointer        g_iface);
+
 
 /* --- variables --- */
 static GQuark	            quark_closure_array = 0;
@@ -256,6 +259,30 @@ g_object_do_class_init (GObjectClass *cl
 		  g_cclosure_marshal_VOID__PARAM,
 		  G_TYPE_NONE,
 		  1, G_TYPE_PARAM);
+
+  /* Install a check function that we'll use to verify that classes that
+   * implement an interface implement all properties for that interface
+   */
+  g_type_add_interface_check (NULL, object_interface_check_properties);
+}
+
+static void
+install_property_internal (GType       g_type,
+			   guint       property_id,
+			   GParamSpec *pspec)
+{
+  if (g_param_spec_pool_lookup (pspec_pool, pspec->name, g_type, FALSE))
+    {
+      g_warning ("When installing property: type `%s' already has a property named `%s'",
+		 g_type_name (g_type),
+		 pspec->name);
+      return;
+    }
+
+  g_param_spec_ref (pspec);
+  g_param_spec_sink (pspec);
+  PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
+  g_param_spec_pool_insert (pspec_pool, pspec, g_type);
 }
 
 void
@@ -276,18 +303,8 @@ g_object_class_install_property (GObject
   if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
     g_return_if_fail (pspec->flags & G_PARAM_WRITABLE);
 
-  if (g_param_spec_pool_lookup (pspec_pool, pspec->name, G_OBJECT_CLASS_TYPE (class), FALSE))
-    {
-      g_warning (G_STRLOC ": class `%s' already contains a property named `%s'",
-		 G_OBJECT_CLASS_NAME (class),
-		 pspec->name);
-      return;
-    }
+  install_property_internal (G_OBJECT_CLASS_TYPE (class), property_id, pspec);
 
-  g_param_spec_ref (pspec);
-  g_param_spec_sink (pspec);
-  PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
-  g_param_spec_pool_insert (pspec_pool, pspec, G_OBJECT_CLASS_TYPE (class));
   if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
     class->construct_properties = g_slist_prepend (class->construct_properties, pspec);
 
@@ -299,17 +316,111 @@ g_object_class_install_property (GObject
     class->construct_properties = g_slist_remove (class->construct_properties, pspec);
 }
 
+void
+g_object_interface_install_property (gpointer      g_iface,
+				     GParamSpec   *pspec)
+{
+  GTypeInterface *iface_class = g_iface;
+	
+  g_return_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type));
+  g_return_if_fail (G_IS_PARAM_SPEC (pspec));
+  g_return_if_fail ((pspec->flags & G_PARAM_REDIRECT) == 0); /* paranoid */
+  g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);	/* paranoid */
+		    
+  install_property_internal (iface_class->g_type, 0, pspec);
+}
+
 GParamSpec*
 g_object_class_find_property (GObjectClass *class,
 			      const gchar  *property_name)
 {
+  GParamSpec *pspec;
+  GParamSpec *redirect;
+	
   g_return_val_if_fail (G_IS_OBJECT_CLASS (class), NULL);
   g_return_val_if_fail (property_name != NULL, NULL);
   
+  pspec = g_param_spec_pool_lookup (pspec_pool,
+				    property_name,
+				    G_OBJECT_CLASS_TYPE (class),
+				    TRUE);
+
+  if (pspec)
+    {
+      redirect = g_param_spec_get_redirect_target (pspec);
+      if (redirect)
+	return redirect;
+      else
+	return pspec;
+    }
+  else
+    return NULL;
+}
+
+GParamSpec*
+g_object_interface_find_property (gpointer      g_iface,
+				  const gchar  *property_name)
+{
+  GTypeInterface *iface_class = g_iface;
+	
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type), NULL);
+  g_return_val_if_fail (property_name != NULL, NULL);
+  
   return g_param_spec_pool_lookup (pspec_pool,
 				   property_name,
-				   G_OBJECT_CLASS_TYPE (class),
-				   TRUE);
+				   iface_class->g_type,
+				   FALSE);
+}
+
+void
+g_object_class_override_property (GObjectClass *oclass,
+				  guint         property_id,
+				  const gchar  *name)
+{
+  GParamSpec *overridden = NULL;
+  GParamSpec *new;
+  GType parent_type;
+  
+  g_return_if_fail (G_IS_OBJECT_CLASS (oclass));
+  g_return_if_fail (property_id > 0);
+  g_return_if_fail (name != NULL);
+
+  /* Find the overridden property; first check parent types
+   */
+  parent_type = g_type_parent (G_OBJECT_CLASS_TYPE (oclass));
+  if (parent_type != G_TYPE_NONE)
+    overridden = g_param_spec_pool_lookup (pspec_pool,
+					   name,
+					   parent_type,
+					   TRUE);
+  if (!overridden)
+    {
+      GType *ifaces;
+      guint n_ifaces;
+      
+      /* Now check interfaces
+       */
+      ifaces = g_type_interfaces (G_OBJECT_CLASS_TYPE (oclass), &n_ifaces);
+      while (n_ifaces-- && !overridden)
+	{
+	  overridden = g_param_spec_pool_lookup (pspec_pool,
+						 name,
+						 ifaces[n_ifaces],
+						 FALSE);
+	}
+      
+      g_free (ifaces);
+    }
+
+  if (!overridden)
+    {
+      g_warning ("%s: Can't find property to override for '%s::%s'",
+		 G_STRLOC, G_OBJECT_CLASS_NAME (oclass), name);
+      return;
+    }
+
+  new = g_param_spec_override (name, overridden);
+  g_object_class_install_property (oclass, property_id, new);
 }
 
 GParamSpec** /* free result */
@@ -330,6 +441,25 @@ g_object_class_list_properties (GObjectC
   return pspecs;
 }
 
+GParamSpec** /* free result */
+g_object_interface_list_properties (gpointer      g_iface,
+				    guint        *n_properties_p)
+{
+  GTypeInterface *iface_class = g_iface;
+  GParamSpec **pspecs;
+  guint n;
+
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_class->g_type), NULL);
+
+  pspecs = g_param_spec_pool_list (pspec_pool,
+				   iface_class->g_type,
+				   &n);
+  if (n_properties_p)
+    *n_properties_p = n;
+
+  return pspecs;
+}
+
 static void
 g_object_init (GObject *object)
 {
@@ -491,10 +621,8 @@ g_object_notify (GObject     *object,
     return;
   
   g_object_ref (object);
-  pspec = g_param_spec_pool_lookup (pspec_pool,
-				    property_name,
-				    G_OBJECT_TYPE (object),
-				    TRUE);
+  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name);
+
   if (!pspec)
     g_warning ("%s: object class `%s' has no property named `%s'",
 	       G_STRLOC,
@@ -535,8 +663,14 @@ object_get_property (GObject     *object
 		     GValue      *value)
 {
   GObjectClass *class = g_type_class_peek (pspec->owner_type);
+  guint param_id = PARAM_SPEC_PARAM_ID (pspec);
+  GParamSpec *redirect;
+
+  redirect = g_param_spec_get_redirect_target (pspec);
+  if (redirect)
+    pspec = redirect;    
   
-  class->get_property (object, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
+  class->get_property (object, param_id, value, pspec);
 }
 
 static inline void
@@ -547,6 +681,12 @@ object_set_property (GObject            
 {
   GValue tmp_value = { 0, };
   GObjectClass *class = g_type_class_peek (pspec->owner_type);
+  guint param_id = PARAM_SPEC_PARAM_ID (pspec);
+  GParamSpec *redirect;
+
+  redirect = g_param_spec_get_redirect_target (pspec);
+  if (redirect)
+    pspec = redirect;
 
   /* provide a copy to work from, convert (if necessary) and validate */
   g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
@@ -568,10 +708,91 @@ object_set_property (GObject            
     }
   else
     {
-      class->set_property (object, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
+      class->set_property (object, param_id, &tmp_value, pspec);
       g_object_notify_queue_add (object, nqueue, pspec);
     }
   g_value_unset (&tmp_value);
+}
+
+static void
+object_interface_check_properties (gpointer func_data,
+				   gpointer g_iface)
+{
+  GTypeInterface *iface_class = g_iface;
+  GObjectClass *class = g_type_class_peek (iface_class->g_instance_type);
+  GType iface_type = iface_class->g_type;
+  GParamSpec **pspecs;
+  guint n;
+
+  if (!G_IS_OBJECT_CLASS (class))
+    return;
+
+  pspecs = g_param_spec_pool_list (pspec_pool, iface_type, &n);
+
+  while (n--)
+    {
+      GParamSpec *class_pspec = g_param_spec_pool_lookup (pspec_pool,
+							  pspecs[n]->name,
+							  G_OBJECT_CLASS_TYPE (class),
+							  TRUE);
+      
+      if (!class_pspec)
+	{
+	  g_critical ("Object class %s doesn't implement property "
+		      "'%s' from interface '%s'",
+		      g_type_name (G_OBJECT_CLASS_TYPE (class)),
+		      pspecs[n]->name,
+		      g_type_name (iface_type));
+
+	  continue;
+	}
+
+      /* The implementation paramspec must have a less restrictive
+       * type than the interface parameter spec for set() and a
+       * more restrictive type for get(). We just require equality,
+       * rather than doing something more complicated checking
+       * the READABLE and WRITABLE flags. We also simplify here
+       * by only checking the value type, not the G_PARAM_SPEC_TYPE.
+       */
+      if (class_pspec &&
+	  !g_type_is_a (G_PARAM_SPEC_VALUE_TYPE (pspecs[n]),
+			G_PARAM_SPEC_VALUE_TYPE (class_pspec)))
+	{
+	  g_critical ("Property '%s' on class '%s' has type '%s' "
+		      "which is different from the type '%s', "
+		      "of the property on interface '%s'\n",
+		      pspecs[n]->name,
+		      g_type_name (G_OBJECT_CLASS_TYPE (class)),
+		      g_type_name (G_PARAM_SPEC_VALUE_TYPE (class_pspec)),
+		      g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspecs[n])),
+		      g_type_name (iface_type));
+	}
+      
+#define SUBSET(a,b,mask) (((a) & ~(b) & (mask)) == 0)
+      
+      /* CONSTRUCT and CONSTRUCT_ONLY add restrictions.
+       * READABLE and WRITABLE remove restrictions. The implementation
+       * paramspec must have less restrictive flags.
+       */
+      if (class_pspec &&
+	  (!SUBSET (class_pspec->flags,
+		    pspecs[n]->flags,
+		    G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY) ||
+	   !SUBSET (pspecs[n]->flags,
+		    class_pspec->flags,
+		    G_PARAM_READABLE | G_PARAM_WRITABLE)))
+	{
+	  g_critical ("Flags for property '%s' on class '%s' "
+		      "are not compatible with the property on"
+		      "interface '%s'\n",
+		      pspecs[n]->name,
+		      g_type_name (G_OBJECT_CLASS_TYPE (class)),
+		      g_type_name (iface_type));
+	}
+#undef SUBSET	  
+    }
+  
+  g_free (pspecs);
 }
 
 gpointer
Index: gobject/gobject.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gobject.h,v
retrieving revision 1.27
diff -u -p -u -r1.27 gobject.h
--- gobject/gobject.h	12 Sep 2003 20:11:36 -0000	1.27
+++ gobject/gobject.h	14 Oct 2003 22:02:11 -0000
@@ -116,6 +116,17 @@ GParamSpec* g_object_class_find_property
 					       const gchar    *property_name);
 GParamSpec**g_object_class_list_properties    (GObjectClass   *oclass,
 					       guint	      *n_properties);
+void        g_object_class_override_property  (GObjectClass   *oclass,
+					       guint           property_id,
+					       const gchar    *name);
+
+void        g_object_interface_install_property (gpointer     g_iface,
+						 GParamSpec  *pspec);
+GParamSpec* g_object_interface_find_property    (gpointer     g_iface,
+						 const gchar *property_name);
+GParamSpec**g_object_interface_list_properties  (gpointer     g_iface,
+						 guint       *n_properties_p);
+
 gpointer    g_object_new                      (GType           object_type,
 					       const gchar    *first_property_name,
 					       ...);
Index: gobject/gparam.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gparam.c,v
retrieving revision 1.28
diff -u -p -u -r1.28 gparam.c
--- gobject/gparam.c	12 Sep 2003 20:11:36 -0000	1.28
+++ gobject/gparam.c	14 Oct 2003 22:02:11 -0000
@@ -22,7 +22,7 @@
  */
 
 #include	"gparam.h"
-
+#include        "gparamspecs.h"
 
 #include	"gvaluecollector.h"
 #include	<string.h>
@@ -246,7 +246,18 @@ g_param_spec_get_nick (GParamSpec *pspec
 {
   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
 
-  return pspec->_nick ? pspec->_nick : pspec->name;
+  if (pspec->_nick)
+    return pspec->_nick;
+  else
+    {
+      GParamSpec *redirect_target;
+
+      redirect_target = g_param_spec_get_redirect_target (pspec);
+      if (redirect_target && redirect_target->_nick)
+	return redirect_target->_nick;
+    }
+
+  return pspec->name;
 }
 
 G_CONST_RETURN gchar*
@@ -254,7 +265,18 @@ g_param_spec_get_blurb (GParamSpec *pspe
 {
   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
 
-  return pspec->_blurb;
+  if (pspec->_blurb)
+    return pspec->_blurb;
+  else
+    {
+      GParamSpec *redirect_target;
+
+      redirect_target = g_param_spec_get_redirect_target (pspec);
+      if (redirect_target && redirect_target->_blurb)
+	return redirect_target->_blurb;
+    }
+
+  return NULL;
 }
 
 static void
@@ -339,6 +361,25 @@ g_param_spec_steal_qdata (GParamSpec *ps
   return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
 }
 
+GParamSpec*
+g_param_spec_get_redirect_target (GParamSpec *pspec)
+{
+  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
+
+  if ((pspec->flags & G_PARAM_REDIRECT) != 0)
+    {
+      GParamSpecOverride *ospec;
+      
+      g_return_val_if_fail (G_IS_PARAM_SPEC_OVERRIDE (pspec), NULL);
+
+      ospec = G_PARAM_SPEC_OVERRIDE (pspec);
+
+      return ospec->overridden;
+    }
+  else
+    return NULL;
+}
+
 void
 g_param_value_set_default (GParamSpec *pspec,
 			   GValue     *value)
@@ -793,10 +834,10 @@ pspec_compare_id (gconstpointer a,
 }
 
 static inline GSList*
-pspec_list_remove_overridden (GSList     *plist,
-			      GHashTable *ht,
-			      GType       owner_type,
-			      guint      *n_p)
+pspec_list_remove_overridden_and_redirected (GSList     *plist,
+					     GHashTable *ht,
+					     GType       owner_type,
+					     guint      *n_p)
 {
   GSList *rlist = NULL;
 
@@ -804,9 +845,31 @@ pspec_list_remove_overridden (GSList    
     {
       GSList *tmp = plist->next;
       GParamSpec *pspec = plist->data;
+      GParamSpec *found;
+      gboolean remove = FALSE;
+
+      /* Remove paramspecs that are redirected, and also paramspecs
+       * that have are overridden by non-redirected properties.
+       * The idea is to get the single paramspec for each name that
+       * best corresponds to what the application sees.
+       */
+      if (pspec->flags & G_PARAM_REDIRECT)
+	remove = TRUE;
+      else
+	{
+	  found = param_spec_ht_lookup (ht, pspec->name, owner_type, TRUE);
+	  if (found != pspec)
+	    {
+	      GParamSpec *redirect = g_param_spec_get_redirect_target (found);
+	      if (redirect != pspec)
+		remove = TRUE;
+	    }
+	}
 
-      if (param_spec_ht_lookup (ht, pspec->name, owner_type, TRUE) != pspec)
-	g_slist_free_1 (plist);
+      if (remove)
+	{
+	  g_slist_free_1 (plist);
+	}
       else
 	{
 	  plist->next = rlist;
@@ -830,12 +893,42 @@ pool_depth_list (gpointer key,
 
   if (g_type_is_a (owner_type, pspec->owner_type))
     {
-      guint d = g_type_depth (pspec->owner_type);
+      if (G_TYPE_IS_INTERFACE (pspec->owner_type))
+	{
+	  slists[0] = g_slist_prepend (slists[0], pspec);
+	}
+      else
+	{
+	  guint d = g_type_depth (pspec->owner_type);
 
-      slists[d - 1] = g_slist_prepend (slists[d - 1], pspec);
+	  slists[d - 1] = g_slist_prepend (slists[d - 1], pspec);
+	}
     }
 }
 
+/* We handle interfaces specially since we don't want to
+ * count interface prerequsites like normal inheritance;
+ * the property comes from the direct inheritance from
+ * the prerequisite class, not from the interface that
+ * prerequires it.
+ * 
+ * also 'depth' isn't a meaningful concept for interface
+ * prerequites.
+ */
+static void
+pool_depth_list_for_interface (gpointer key,
+			       gpointer value,
+			       gpointer user_data)
+{
+  GParamSpec *pspec = value;
+  gpointer *data = user_data;
+  GSList **slists = data[0];
+  GType owner_type = (GType) data[1];
+
+  if (pspec->owner_type == owner_type)
+    slists[0] = g_slist_prepend (slists[0], pspec);
+}
+
 GParamSpec** /* free result */
 g_param_spec_pool_list (GParamSpecPool *pool,
 			GType           owner_type,
@@ -856,10 +949,15 @@ g_param_spec_pool_list (GParamSpecPool *
   slists = g_new0 (GSList*, d);
   data[0] = slists;
   data[1] = (gpointer) owner_type;
-  g_hash_table_foreach (pool->hash_table, pool_depth_list, &data);
-  for (i = 0; i < d - 1; i++)
-    slists[i] = pspec_list_remove_overridden (slists[i], pool->hash_table, owner_type, n_pspecs_p);
-  *n_pspecs_p += g_slist_length (slists[i]);
+
+  g_hash_table_foreach (pool->hash_table,
+			G_TYPE_IS_INTERFACE (owner_type) ?
+			   pool_depth_list_for_interface :
+			   pool_depth_list,
+			&data);
+  
+  for (i = 0; i < d; i++)
+    slists[i] = pspec_list_remove_overridden_and_redirected (slists[i], pool->hash_table, owner_type, n_pspecs_p);
   pspecs = g_new (GParamSpec*, *n_pspecs_p + 1);
   p = pspecs;
   for (i = 0; i < d; i++)
Index: gobject/gparam.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gparam.h,v
retrieving revision 1.23
diff -u -p -u -r1.23 gparam.h
--- gobject/gparam.h	12 Sep 2003 20:11:36 -0000	1.23
+++ gobject/gparam.h	14 Oct 2003 22:02:11 -0000
@@ -53,7 +53,8 @@ typedef enum
   G_PARAM_CONSTRUCT	      = 1 << 2,
   G_PARAM_CONSTRUCT_ONLY      = 1 << 3,
   G_PARAM_LAX_VALIDATION      = 1 << 4,
-  G_PARAM_PRIVATE	      = 1 << 5
+  G_PARAM_PRIVATE	      = 1 << 5,
+  G_PARAM_REDIRECT            = 1 << 6
 } GParamFlags;
 #define	G_PARAM_READWRITE	(G_PARAM_READABLE | G_PARAM_WRITABLE)
 #define	G_PARAM_MASK		(0x000000ff)
@@ -73,7 +74,7 @@ struct _GParamSpec
   gchar         *name;
   GParamFlags    flags;
   GType		 value_type;
-  GType		 owner_type;	/* class using this property */
+  GType		 owner_type;	/* class or interface using this property */
 
   /*< private >*/
   gchar         *_nick;
@@ -122,6 +123,8 @@ void            g_param_spec_set_qdata_f
 						 GDestroyNotify destroy);
 gpointer        g_param_spec_steal_qdata	(GParamSpec    *pspec,
 						 GQuark         quark);
+GParamSpec*     g_param_spec_get_redirect_target (GParamSpec   *pspec);
+
 void		g_param_value_set_default	(GParamSpec    *pspec,
 						 GValue	       *value);
 gboolean	g_param_value_defaults		(GParamSpec    *pspec,
Index: gobject/gparamspecs.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gparamspecs.c,v
retrieving revision 1.25
diff -u -p -u -r1.25 gparamspecs.c
--- gobject/gparamspecs.c	7 Feb 2003 22:04:24 -0000	1.25
+++ gobject/gparamspecs.c	14 Oct 2003 22:02:11 -0000
@@ -966,6 +966,54 @@ param_object_values_cmp (GParamSpec   *p
   return p1 < p2 ? -1 : p1 > p2;
 }
 
+static void
+param_override_init (GParamSpec *pspec)
+{
+  /* GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec); */
+}
+
+static void
+param_override_finalize (GParamSpec *pspec)
+{
+  GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec);
+  GParamSpecClass *parent_class = g_type_class_peek (g_type_parent (G_TYPE_PARAM_OVERRIDE));
+  
+  if (ospec->overridden)
+    {
+      g_param_spec_unref (ospec->overridden);
+      ospec->overridden = NULL;
+    }
+  
+  parent_class->finalize (pspec);
+}
+
+static void
+param_override_set_default (GParamSpec *pspec,
+			    GValue     *value)
+{
+  GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec);
+
+  g_param_value_set_default (ospec->overridden, value);
+}
+
+static gboolean
+param_override_validate (GParamSpec *pspec,
+			 GValue     *value)
+{
+  GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec);
+  
+  return g_param_value_validate (ospec->overridden, value);
+}
+
+static gint
+param_override_values_cmp (GParamSpec   *pspec,
+			   const GValue *value1,
+			   const GValue *value2)
+{
+  GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec);
+
+  return g_param_values_cmp (ospec->overridden, value1, value2);
+}
 
 /* --- type initialization --- */
 GType *g_param_spec_types = NULL;
@@ -973,7 +1021,7 @@ GType *g_param_spec_types = NULL;
 void
 g_param_spec_types_init (void)	/* sync with gtype.c */
 {
-  const guint n_types = 20;
+  const guint n_types = 21;
   GType type, *spec_types, *spec_types_bound;
 
   g_param_spec_types = g_new0 (GType, n_types);
@@ -1341,6 +1389,24 @@ g_param_spec_types_init (void)	/* sync w
     g_assert (type == G_TYPE_PARAM_OBJECT);
   }
 
+  /* G_TYPE_PARAM_OVERRIDE
+   */
+  {
+    static const GParamSpecTypeInfo pspec_info = {
+      sizeof (GParamSpecOverride), /* instance_size */
+      16,                        /* n_preallocs */
+      param_override_init,	 /* instance_init */
+      G_TYPE_NONE,		 /* value_type */
+      param_override_finalize,	 /* finalize */
+      param_override_set_default, /* value_set_default */
+      param_override_validate,	  /* value_validate */
+      param_override_values_cmp,  /* values_cmp */
+    };
+    type = g_param_type_register_static ("GParamOverride", &pspec_info);
+    *spec_types++ = type;
+    g_assert (type == G_TYPE_PARAM_OVERRIDE);
+  }
+
   g_assert (spec_types == spec_types_bound);
 }
 
@@ -1830,4 +1896,34 @@ g_param_spec_object (const gchar *name,
   G_PARAM_SPEC (ospec)->value_type = object_type;
   
   return G_PARAM_SPEC (ospec);
+}
+
+GParamSpec*
+g_param_spec_override (const gchar *name,
+		       GParamSpec  *overridden)
+{
+  GParamSpec *pspec;
+  
+  g_return_val_if_fail (name != NULL, NULL);
+  g_return_val_if_fail (G_IS_PARAM_SPEC (overridden), NULL);
+  
+  /* Dereference further redirections for property that was passed in
+   */
+  while (TRUE)
+    {
+      GParamSpec *indirect = g_param_spec_get_redirect_target (overridden);
+      if (indirect)
+	overridden = indirect;
+      else
+	break;
+    }
+
+  pspec = g_param_spec_internal (G_TYPE_PARAM_OVERRIDE,
+				 name, NULL, NULL,
+				 overridden->flags | G_PARAM_REDIRECT);
+  
+  pspec->value_type = G_PARAM_SPEC_VALUE_TYPE (overridden);
+  G_PARAM_SPEC_OVERRIDE (pspec)->overridden = g_param_spec_ref (overridden);
+
+  return pspec;
 }
Index: gobject/gparamspecs.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gparamspecs.h,v
retrieving revision 1.21
diff -u -p -u -r1.21 gparamspecs.h
--- gobject/gparamspecs.h	22 Nov 2001 18:55:05 -0000	1.21
+++ gobject/gparamspecs.h	14 Oct 2003 22:02:11 -0000
@@ -93,6 +93,9 @@ G_BEGIN_DECLS
 #define	G_TYPE_PARAM_OBJECT		   (g_param_spec_types[19])
 #define G_IS_PARAM_SPEC_OBJECT(pspec)      (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), G_TYPE_PARAM_OBJECT))
 #define G_PARAM_SPEC_OBJECT(pspec)         (G_TYPE_CHECK_INSTANCE_CAST ((pspec), G_TYPE_PARAM_OBJECT, GParamSpecObject))
+#define	G_TYPE_PARAM_OVERRIDE		   (g_param_spec_types[20])
+#define G_IS_PARAM_SPEC_OVERRIDE(pspec)    (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), G_TYPE_PARAM_OVERRIDE))
+#define G_PARAM_SPEC_OVERRIDE(pspec)       (G_TYPE_CHECK_INSTANCE_CAST ((pspec), G_TYPE_PARAM_OVERRIDE, GParamSpecOverride))
 
 
 /* --- typedefs & structures --- */
@@ -116,6 +119,7 @@ typedef struct _GParamSpecBoxed      GPa
 typedef struct _GParamSpecPointer    GParamSpecPointer;
 typedef struct _GParamSpecValueArray GParamSpecValueArray;
 typedef struct _GParamSpecObject     GParamSpecObject;
+typedef struct _GParamSpecOverride   GParamSpecOverride;
 
 struct _GParamSpecChar
 {
@@ -258,6 +262,12 @@ struct _GParamSpecObject
 {
   GParamSpec    parent_instance;
 };
+struct _GParamSpecOverride
+{
+  /*< private >*/
+  GParamSpec    parent_instance;
+  GParamSpec   *overridden;
+};
 
 /* --- GParamSpec prototypes --- */
 GParamSpec*	g_param_spec_char	 (const gchar	 *name,
@@ -382,6 +392,8 @@ GParamSpec*	g_param_spec_object	 (const 
 					  GType		  object_type,
 					  GParamFlags	  flags);
 
+GParamSpec*     g_param_spec_override    (const gchar    *name,
+					  GParamSpec     *overridden);
 
 /* --- internal --- */
 /* We prefix variable declarations so they can
Index: tests/gobject/Makefile.am
===================================================================
RCS file: /cvs/gnome/glib/tests/gobject/Makefile.am,v
retrieving revision 1.4
diff -u -p -u -r1.4 Makefile.am
--- tests/gobject/Makefile.am	2 Oct 2003 05:28:00 -0000	1.4
+++ tests/gobject/Makefile.am	14 Oct 2003 22:02:11 -0000
@@ -50,6 +50,7 @@ test_programs =					\
 	ifacecheck				\
 	ifaceinit				\
 	ifaceinherit				\
+	ifaceproperties				\
 	override
 
 check_PROGRAMS = $(test_programs)
Index: tests/gobject/ifaceproperties.c
===================================================================
RCS file: tests/gobject/ifaceproperties.c
diff -N tests/gobject/ifaceproperties.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/gobject/ifaceproperties.c	14 Oct 2003 22:02:11 -0000
@@ -0,0 +1,505 @@
+/* GObject - GLib Type, Object, Parameter and Signal Library
+ * Copyright (C) 2001, 2003 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#undef	G_LOG_DOMAIN
+#define	G_LOG_DOMAIN "TestIfaceProperties"
+
+#undef G_DISABLE_ASSERT
+#undef G_DISABLE_CHECKS
+#undef G_DISABLE_CAST_CHECKS
+
+#include <string.h>
+
+#include <glib-object.h>
+
+#include "testcommon.h"
+
+/* This test tests interface properties, implementing interface
+ * properties and #GParamSpecOverride.
+ *
+ * Four properties are tested:
+ *
+ * prop1: Defined in TestIface, Implemented in BaseObject with a GParamSpecOverride
+ * prop2: Defined in TestIface, Implemented in BaseObject with a new property
+ * prop3: Defined in TestIface, Implemented in BaseObject, Overridden in DerivedObject
+ * prop4: Defined in BaseObject, Overridden in DerivedObject
+ */
+   
+static GType base_object_get_type ();
+static GType derived_object_get_type ();
+
+enum {
+  BASE_PROP_0,
+  BASE_PROP1,
+  BASE_PROP2,
+  BASE_PROP3,
+  BASE_PROP4
+};
+
+enum {
+  DERIVED_PROP_0,
+  DERIVED_PROP3,
+  DERIVED_PROP4
+};
+
+/*
+ * BaseObject, a parent class for DerivedObject
+ */
+#define BASE_TYPE_OBJECT          (base_object_get_type ())
+#define BASE_OBJECT(obj)	  (G_TYPE_CHECK_INSTANCE_CAST ((obj), BASE_TYPE_OBJECT, BaseObject))
+typedef struct _BaseObject        BaseObject;
+typedef struct _BaseObjectClass   BaseObjectClass;
+
+struct _BaseObject
+{
+  GObject parent_instance;
+
+  gint val1;
+  gint val2;
+  gint val3;
+  gint val4;
+};
+struct _BaseObjectClass
+{
+  GObjectClass parent_class;
+};
+
+GObjectClass *base_parent_class;
+
+/*
+ * DerivedObject, the child class of DerivedObject
+ */
+#define DERIVED_TYPE_OBJECT          (derived_object_get_type ())
+typedef struct _DerivedObject        DerivedObject;
+typedef struct _DerivedObjectClass   DerivedObjectClass;
+
+struct _DerivedObject
+{
+  BaseObject parent_instance;
+};
+struct _DerivedObjectClass
+{
+  BaseObjectClass parent_class;
+};
+
+/*
+ * The interface
+ */
+typedef struct _TestIfaceClass TestIfaceClass;
+
+struct _TestIfaceClass
+{
+  GTypeInterface base_iface;
+};
+
+#define TEST_TYPE_IFACE (test_iface_get_type ())
+
+/* The paramspecs installed on our interface
+ */
+static GParamSpec *iface_spec1, *iface_spec2, *iface_spec3;
+
+/* The paramspecs inherited by our derived object
+ */
+static GParamSpec *inherited_spec1, *inherited_spec2, *inherited_spec3, *inherited_spec4;
+
+static void
+test_iface_default_init (TestIfaceClass *iface_vtable)
+{
+  inherited_spec1 = iface_spec1 = g_param_spec_int ("prop1",
+						    "Prop1",
+						    "Property 1",
+						    G_MININT, /* min */
+						    0xFFFF,  /* max */
+						    42,       /* default */
+						    G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+  g_object_interface_install_property (iface_vtable, iface_spec1);
+
+  iface_spec2 = g_param_spec_int ("prop2",
+				  "Prop2",
+				  "Property 2",
+				  G_MININT, /* min */
+				  G_MAXINT, /* max */
+				  0,	       /* default */
+				  G_PARAM_WRITABLE);
+  g_object_interface_install_property (iface_vtable, iface_spec2);
+    
+  inherited_spec3 = iface_spec3 = g_param_spec_int ("prop3",
+						    "Prop3",
+						    "Property 3",
+						    G_MININT, /* min */
+						    G_MAXINT, /* max */
+						    0,	       /* default */
+						    G_PARAM_READWRITE);
+  g_object_interface_install_property (iface_vtable, iface_spec3);
+}
+
+static DEFINE_IFACE (TestIface, test_iface, NULL, test_iface_default_init)
+
+
+static GObject*
+base_object_constructor  (GType                  type,
+			  guint                  n_construct_properties,
+			  GObjectConstructParam *construct_properties)
+{
+  /* The constructor is the one place where a GParamSpecOverride is visible
+   * to the outside world, so we do a bunch of checks here
+   */
+  GValue value1 = { 0, };
+  GValue value2 = { 0, };
+  GParamSpec *pspec;
+
+  g_assert (n_construct_properties == 1);
+
+  pspec = construct_properties->pspec;
+
+  /* Check we got the param spec we expected
+   */
+  g_assert (G_IS_PARAM_SPEC_OVERRIDE (pspec));
+  g_assert (pspec->param_id == BASE_PROP1);
+  g_assert (strcmp (g_param_spec_get_name (pspec), "prop1") == 0);
+  g_assert (g_param_spec_get_redirect_target (pspec) == iface_spec1);
+
+  /* Test redirection of the nick and blurb to the redirect target
+   */
+  g_assert (strcmp (g_param_spec_get_nick (pspec), "Prop1") == 0);
+  g_assert (strcmp (g_param_spec_get_blurb (pspec), "Property 1") == 0);
+
+  /* Test forwarding of the various GParamSpec methods to the redirect target
+   */
+  g_value_init (&value1, G_TYPE_INT);
+  g_value_init (&value2, G_TYPE_INT);
+  
+  g_param_value_set_default (pspec, &value1);
+  g_assert (g_value_get_int (&value1) == 42);
+
+  g_value_reset (&value1);
+  g_value_set_int (&value1, 0x10000);
+  g_assert (g_param_value_validate (pspec, &value1));
+  g_assert (g_value_get_int (&value1) == 0xFFFF);
+  g_assert (!g_param_value_validate (pspec, &value1));
+  
+  g_value_reset (&value1);
+  g_value_set_int (&value1, 1);
+  g_value_set_int (&value2, 2);
+  g_assert (g_param_values_cmp (pspec, &value1, &value2) < 0);
+  g_assert (g_param_values_cmp (pspec, &value2, &value1) > 0);
+  
+  g_value_unset (&value1);
+  g_value_unset (&value2);
+
+  return base_parent_class->constructor (type,
+					 n_construct_properties,
+					 construct_properties);
+}
+
+static void
+base_object_set_property (GObject      *object,
+			  guint         prop_id,
+			  const GValue *value,
+			  GParamSpec   *pspec)
+{
+  BaseObject *base_object = BASE_OBJECT (object);
+  
+  switch (prop_id)
+    {
+    case BASE_PROP1:
+      g_assert (pspec == inherited_spec1);
+      base_object->val1 = g_value_get_int (value);
+      break;
+    case BASE_PROP2:
+      g_assert (pspec == inherited_spec2);
+      base_object->val2 = g_value_get_int (value);
+      break;
+    case BASE_PROP3:
+      g_assert_not_reached ();
+      break;
+    case BASE_PROP4:
+      g_assert_not_reached ();
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+base_object_get_property (GObject                *object,
+			  guint                   prop_id,
+			  GValue                 *value,
+			  GParamSpec             *pspec)
+{
+  BaseObject *base_object = BASE_OBJECT (object);
+
+  switch (prop_id)
+    {
+    case BASE_PROP1:
+      g_assert (pspec == inherited_spec1);
+      g_value_set_int (value, base_object->val1);
+      break;
+    case BASE_PROP2:
+      g_assert (pspec == inherited_spec2);
+      g_value_set_int (value, base_object->val2);
+      break;
+    case BASE_PROP3:
+      g_assert_not_reached ();
+      break;
+    case BASE_PROP4:
+      g_assert_not_reached ();
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+base_object_notify (GObject    *object,
+		    GParamSpec *pspec)
+{
+  /* The property passed to notify is the redirect target, not the
+   * GParamSpecOverride
+   */
+  g_assert (pspec == inherited_spec1 ||
+	    pspec == inherited_spec2 ||
+	    pspec == inherited_spec3 ||
+	    pspec == inherited_spec4);
+}
+
+static void
+base_object_class_init (BaseObjectClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  base_parent_class= g_type_class_peek_parent (class);
+
+  object_class->constructor = base_object_constructor;
+  object_class->set_property = base_object_set_property;
+  object_class->get_property = base_object_get_property;
+  object_class->notify = base_object_notify;
+
+  g_object_class_override_property (object_class, BASE_PROP1, "prop1");
+
+  /* We override this one using a real property, not GParamSpecOverride
+   * We change the flags from READONLY to READWRITE to show that we
+   * can make the flags less restrictive
+   */
+  inherited_spec2 = g_param_spec_int ("prop2",
+				      "Prop2",
+				      "Property 2",
+				      G_MININT, /* min */
+				      G_MAXINT, /* max */
+				      0,	       /* default */
+				      G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, BASE_PROP2, inherited_spec2);
+
+  g_object_class_override_property (object_class, BASE_PROP3, "prop3");
+  
+  inherited_spec4 = g_param_spec_int ("prop4",
+				      "Prop4",
+				      "Property 4",
+				      G_MININT, /* min */
+				      G_MAXINT, /* max */
+				      0,	       /* default */
+				      G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, BASE_PROP4, inherited_spec4);
+}
+
+static void
+base_object_init (BaseObject *base_object)
+{
+  base_object->val1 = 42;
+}
+
+static DEFINE_TYPE_FULL (BaseObject, base_object,
+			 base_object_class_init, NULL, base_object_init,
+			 G_TYPE_OBJECT,
+			 INTERFACE (NULL, TEST_TYPE_IFACE))
+
+static void
+derived_object_set_property (GObject      *object,
+			     guint         prop_id,
+			     const GValue *value,
+			     GParamSpec   *pspec)
+{
+  BaseObject *base_object = BASE_OBJECT (object);
+
+  switch (prop_id)
+    {
+    case DERIVED_PROP3:
+      g_assert (pspec == inherited_spec3);
+      base_object->val3 = g_value_get_int (value);
+      break;
+    case DERIVED_PROP4:
+      g_assert (pspec == inherited_spec4);
+      base_object->val4 = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+derived_object_get_property (GObject                *object,
+			     guint                   prop_id,
+			     GValue                 *value,
+			     GParamSpec             *pspec)
+{
+  BaseObject *base_object = BASE_OBJECT (object);
+
+  switch (prop_id)
+    {
+    case DERIVED_PROP3:
+      g_assert (pspec == inherited_spec3);
+      g_value_set_int (value, base_object->val3);
+      break;
+    case DERIVED_PROP4:
+      g_assert (pspec == inherited_spec4);
+      g_value_set_int (value, base_object->val4);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+derived_object_class_init (DerivedObjectClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->set_property = derived_object_set_property;
+  object_class->get_property = derived_object_get_property;
+
+  /* Overriding a property that is itself overridding an interface property */
+  g_object_class_override_property (object_class, DERIVED_PROP3, "prop3");
+
+  /* Overriding a property not from an interface */
+  g_object_class_override_property (object_class, DERIVED_PROP4, "prop4");
+}
+
+static DEFINE_TYPE (DerivedObject, derived_object,
+		    derived_object_class_init, NULL, NULL,
+		    BASE_TYPE_OBJECT)
+
+/* Helper function for testing ...list_properties()
+ */
+static void
+assert_in_properties (GParamSpec  *param_spec,
+		      GParamSpec **properties,
+		      gint         n_properties)
+{
+  gint i;
+  gboolean found = FALSE;
+
+  for (i = 0; i < n_properties; i++)
+    {
+      if (properties[i] == param_spec)
+	found = TRUE;
+    }
+
+  g_assert (found);
+}
+
+int
+main (gint   argc,
+      gchar *argv[])
+{
+  BaseObject *object;
+  GObjectClass *object_class;
+  TestIfaceClass *iface_vtable;
+  GParamSpec **properties;
+  gint n_properties;
+  
+  gint val1, val2, val3, val4;
+	
+  g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
+			  G_LOG_LEVEL_WARNING |
+			  G_LOG_LEVEL_CRITICAL);
+  g_type_init ();
+
+  object = g_object_new (DERIVED_TYPE_OBJECT, NULL);
+
+  /* Test setting and getting the properties
+   */
+  g_object_set (object,
+		"prop1", 0x0101,
+		"prop2", 0x0202,
+		"prop3", 0x0303,
+		"prop4", 0x0404);
+  g_object_get (object,
+		"prop1", &val1,
+		"prop2", &val2,
+		"prop3", &val3,
+		"prop4", &val4);
+
+  g_assert (val1 == 0x0101);
+  g_assert (val2 == 0x0202);
+  g_assert (val3 == 0x0303);
+  g_assert (val4 == 0x0404);
+
+  /* Test that the right spec is passed on explicit notifications
+   */
+  g_object_freeze_notify (G_OBJECT (object));
+  g_object_notify (G_OBJECT (object), "prop1");
+  g_object_notify (G_OBJECT (object), "prop2");
+  g_object_notify (G_OBJECT (object), "prop3");
+  g_object_notify (G_OBJECT (object), "prop4");
+  g_object_thaw_notify (G_OBJECT (object));
+
+  /* Test g_object_class_find_property() for overridden properties
+   */
+  object_class = G_OBJECT_GET_CLASS (object);
+
+  g_assert (g_object_class_find_property (object_class, "prop1") == inherited_spec1);
+  g_assert (g_object_class_find_property (object_class, "prop2") == inherited_spec2);
+  g_assert (g_object_class_find_property (object_class, "prop3") == inherited_spec3);
+  g_assert (g_object_class_find_property (object_class, "prop4") == inherited_spec4);
+
+  /* Test g_object_class_list_properties() for overridden properties
+   */
+  properties = g_object_class_list_properties (object_class, &n_properties);
+  g_assert (n_properties == 4);
+  assert_in_properties (inherited_spec1, properties, n_properties);
+  assert_in_properties (inherited_spec2, properties, n_properties);
+  assert_in_properties (inherited_spec3, properties, n_properties);
+  assert_in_properties (inherited_spec4, properties, n_properties);
+  g_free (properties);
+
+  /* Test g_object_interface_find_property()
+   */
+  iface_vtable = g_type_default_interface_peek (TEST_TYPE_IFACE);
+
+  g_assert (g_object_interface_find_property (iface_vtable, "prop1") == iface_spec1);
+  g_assert (g_object_interface_find_property (iface_vtable, "prop2") == iface_spec2);
+  g_assert (g_object_interface_find_property (iface_vtable, "prop3") == iface_spec3);
+
+  /* Test g_object_interface_list_properties()
+   */
+  properties = g_object_interface_list_properties (iface_vtable, &n_properties);
+  g_assert (n_properties == 3);
+  assert_in_properties (iface_spec1, properties, n_properties);
+  assert_in_properties (iface_spec2, properties, n_properties);
+  assert_in_properties (iface_spec3, properties, n_properties);
+  g_free (properties);
+
+  g_object_unref (object);
+
+  return 0;
+}


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