First draft of interface property patch



Hi Tim,

The attached is a first draft of an implementation of interface
properties along the lines you suggested. 

(I used OVERRIDE as a paramspec flag name rather than TRANSLUCENT,
because I really don't like TRANSLUCENT. OVERRIDE isn't all that
wonderful, but it fits in well with 
g_object_property_find_overridden())

The interface additions are:

===
  G_PARAM_OVERRIDE            = 1 << 6

void        g_object_interface_install_property (GType        iface_type,
                                                 GParamSpec  *pspec);
GParamSpec* g_object_interface_find_property    (GType        iface_type,
                                                 const gchar *property_name);
GParamSpec**g_object_interface_list_properties  (GType        iface_type,
                                                 guint       *n_properties_p);
GParamSpec* g_object_property_find_overridden   (GParamSpec  *pspec);
===

Comments:
 
 * Doc comments are inline until the interfaces are finalized to 
   keep things simple. I'll move them later.

 * Currently, there is no way of removing properties from interfaces
   once added because:

   a) We don't really have a class_init/class_finalize steps
      for interfaces.

   b) There is no way of hooking into class_finalize in any case.

   If we think this capability is important, then one simple
   possibility is to add a:

    g_object_interface_remove_properties ()

   That the user calls itself.

 * interface_install_property() takes a GType rather than a class   
   pointer (like object_class_install_property()) because there is no
   class for an interface, only vtables for implementations of that
   interface.

 * It's not clear to me when the checks for an object class
   implementing all necessary properties should happen. The
   patch currently does it in g_object_newv(), but:

    a) that's pretty clearly too expensive (with some thread 
       locking trouble, you could manage to cache the info)
    b) you can add interfaces after an object already exists.
       
   The right point seems to be right after interface_init is 
   called for the class/interface pair, but there is no
   way for gobject.c to hook into that.

 * What are the constraints on parameter spec validation here?
   It's a little interesting:

    G_PARAM_READABLE:

     The set of valid values for the interface property must
     be a superset of the set of valid values for the implementation
     property.

    G_PARAM_WRITABLE:
  
     The set of valid values for the interface property must
     be a subset of the set of valid values for the implementation
     property.

   So, for the common case of READWRITE properties, these two
   sets of valid values have to be identical. Changing the value 
   type of a GParamSpecObject property or the range of a 
   GParamSpecInt isn't a valid operation.

   This doesn't have much affect on my patch, since this isn't
   particularly enforceable, but I do check that the value types 
   are the same. (For conceptual simplicity, always, instead
   of checking the appropriate derivation based on READABLE/WRITABLE)

 * I expect some objects to the inclusion of gobject.h in gparam.h
   to make g_param_spec_get_nick/blurb to magically look up
   the overridden property.

   I don't see a good alternative that maintains the same 
   degree of simplicity.

 * I'm really not very happy with the need to copy ranges and
   default values ... it's a pain and it's bugs waiting to happen.
   
   Noting the above comments about changing the ranges, it
   would be possible to add:

    g_param_spec_new_override (gchar *name, 
                               GType  value_type,
                               GParamFlags flags);
    g_param_spec_override_set_default (GParamSpecOverride *pspec);

   That looked up the overriden paramspec and chained 
   value_validate() and (when default isn't set) value_set_default()
   to that paramspec, without changing anything else.

   The only possible problem with this is efficiency... looking
   up the overriden paramspec is moderately expensive.

Regards,
                                          Owen


Index: gobject.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gobject.c,v
retrieving revision 1.57
diff -u -p -r1.57 gobject.c
--- gobject.c	7 Feb 2003 22:04:24 -0000	1.57
+++ gobject.c	17 Mar 2003 20:06:55 -0000
@@ -258,6 +258,25 @@ g_object_do_class_init (GObjectClass *cl
 		  1, G_TYPE_PARAM);
 }
 
+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 (G_STRLOC ": 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
 g_object_class_install_property (GObjectClass *class,
 				 guint	       property_id,
@@ -276,18 +295,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,6 +308,33 @@ g_object_class_install_property (GObject
     class->construct_properties = g_slist_remove (class->construct_properties, pspec);
 }
 
+/**
+ * g_object_interface_install_property:
+ * @iface_type: an interface type
+ * @pspec: the #GParamSpec for the new property
+ * 
+ * Add a property to an interface; this will only be useful
+ * if the interface is subsequently added to a GObject-derived
+ * type. Adding a property to an interface has two primary
+ * purposes. First, all object types with that interface will
+ * be required by GObject to implement a property with the
+ * same name and type. Second, the %G_PARAM_OVERRIDE flag can be
+ * used on the implementation properties to indicate that 
+ * the nick and blurb should be looked up from the interface
+ * property when missing from the instance property.
+ * (See g_object_property_find_overridden ())
+ **/
+void
+g_object_interface_install_property (GType         iface_type,
+				     GParamSpec   *pspec)
+{
+  g_return_if_fail (G_TYPE_IS_INTERFACE (iface_type));
+  g_return_if_fail (G_IS_PARAM_SPEC (pspec));
+  g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);	/* paranoid */
+		    
+  install_property_internal (iface_type, 0, pspec);
+}
+
 GParamSpec*
 g_object_class_find_property (GObjectClass *class,
 			      const gchar  *property_name)
@@ -312,6 +348,104 @@ g_object_class_find_property (GObjectCla
 				   TRUE);
 }
 
+/**
+ * g_object_interface_find_property:
+ * @iface_type: an interface type
+ * @property_name: name of a property to lookup.
+ *
+ * Find the #GParamSpec with the given name for an
+ * interface.
+ * 
+ * Return value: the #GParamSpec for the property of the
+ *  interface with the name @property_name, or %NULL
+ *  if no such property exists.
+ **/
+GParamSpec*
+g_object_interface_find_property (GType         iface_type,
+				  const gchar  *property_name)
+{
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_type), NULL);
+  g_return_val_if_fail (property_name != NULL, NULL);
+  
+  return g_param_spec_pool_lookup (pspec_pool,
+				   property_name,
+				   iface_type,
+				   FALSE);
+}
+
+/**
+ * g_object_property_find_overridden:
+ * @pspec: a #GParamSpec
+ * 
+ * Given a property, finds the property that it overrides.
+ * Only properties with %G_PARAM_OVERRIDE flag set are
+ * ever considered to override another property. To
+ * find the property that such a property overrides,
+ * first the parent types of the property's owner
+ * type are checked, then the interfaces that the
+ * owner type implements.
+ * 
+ * Return value: a #GParamSpec, or %NULL if this paramspec
+ *   is not overriding another paramspec.
+ **/
+GParamSpec*
+g_object_property_find_overridden (GParamSpec *pspec)
+{
+  GParamSpec *result;
+  GType owner_type;
+  GType parent_type;
+  GType *ifaces;
+  guint n_ifaces;
+  
+  g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
+
+  if (!(pspec->flags & G_PARAM_OVERRIDE))
+    return NULL;
+
+  owner_type = pspec->owner_type;
+  if (!owner_type || G_TYPE_IS_INTERFACE (owner_type))
+    return NULL;
+
+  if (!G_TYPE_IS_OBJECT (owner_type))
+    {
+      g_warning ("Owner type in g_object_property_find_overridden() "
+		 "does not derive from GObject\n");
+      return NULL;
+    }
+
+  result = NULL;
+  
+  /* First check parent types
+   */
+  parent_type = g_type_parent (owner_type);
+  if (parent_type != G_TYPE_NONE)
+    result = g_param_spec_pool_lookup (pspec_pool,
+				       pspec->name,
+				       parent_type,
+				       TRUE);
+
+  if (result)
+    return result;
+
+  /* Now check interfaces
+   */
+  ifaces = g_type_interfaces (owner_type, &n_ifaces);
+  while (n_ifaces-- && !result)
+    {
+      result = g_param_spec_pool_lookup (pspec_pool,
+					 pspec->name,
+					 ifaces[n_ifaces],
+					 FALSE);
+      
+      if (result)
+	break;
+    }
+
+  g_free (ifaces);
+
+  return result;
+}
+		    
 GParamSpec** /* free result */
 g_object_class_list_properties (GObjectClass *class,
 				guint        *n_properties_p)
@@ -330,6 +464,38 @@ g_object_class_list_properties (GObjectC
   return pspecs;
 }
 
+/**
+ * g_object_interface_list_properties:
+ * @iface_type: an interface type
+ * @n_properties_p: location to store number of properties
+ *                  returned.
+ * 
+ * Lists the properties of a given interface.
+ * 
+ * Return value: a pointer to an array of pointers to
+ *               #GParamSpec structures. The structures
+ *               are owned by GLib, but the array should
+ *               be freed with g_free() when you are
+ *               done with it.
+ **/
+GParamSpec** /* free result */
+g_object_interface_list_properties (GType         iface_type,
+				    guint        *n_properties_p)
+{
+  GParamSpec **pspecs;
+  guint n;
+
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_type), NULL);
+
+  pspecs = g_param_spec_pool_list (pspec_pool,
+				   iface_type,
+				   &n);
+  if (n_properties_p)
+    *n_properties_p = n;
+
+  return pspecs;
+}
+
 static void
 g_object_init (GObject *object)
 {
@@ -591,6 +757,99 @@ g_object_new (GType	   object_type,
   return object;
 }
 
+static gboolean
+g_object_class_check_iface_properties (GObjectClass *class)
+{
+  GType *ifaces;
+  guint n_ifaces;
+  gboolean result;
+
+  ifaces = g_type_interfaces (G_OBJECT_CLASS_TYPE (class), &n_ifaces);
+
+  /* The behavior will be undefined if multiple interfaces
+   * define the same property, and the OVERRIDE flag is used; we
+   * don't try to check for that here
+   */
+
+  result = TRUE;
+  while (n_ifaces--)
+    {
+      GParamSpec **pspecs;
+      guint n;
+
+      pspecs = g_param_spec_pool_list (pspec_pool, ifaces[n_ifaces], &n);
+
+      while (n--)
+	{
+	  GParamSpec *class_pspec = g_object_class_find_property (class, pspecs[n]->name);
+	  
+	  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 (ifaces[n_ifaces]));
+	      result = FALSE;
+	    }
+
+	  /* The implementation paramspec must have a less
+	   * restrictive type than the interface parameter
+	   * spec for set() and a more restrictive type for
+	   * get(), so we just require equality. (Though we
+	   * could theoretically check the READABLE and
+	   * WRITABLE flags) Should we also check
+	   * 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 (ifaces[n_ifaces]));
+
+	      result = FALSE;
+	    }
+
+#define SUBSET(a,b,mask) (((a) & ~(b) & (mask)) == 0)
+
+	  /* CONSTRUCT and CONSTRUCT_ONLY add restrictions.
+	   * READABLE and WRITABLE remove restrictions
+	   */
+	  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 (ifaces[n_ifaces]));
+
+	      result = FALSE;
+	    }
+#undef SUBSET	  
+	}
+
+      g_free (pspecs);
+    }
+
+  g_free (ifaces);
+
+  return result;
+}
+
 gpointer
 g_object_newv (GType       object_type,
 	       guint       n_parameters,
@@ -609,6 +868,10 @@ g_object_newv (GType       object_type,
   g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL);
 
   class = g_type_class_ref (object_type);
+  
+  if (!g_object_class_check_iface_properties (class))
+    return NULL;
+
   for (slist = class->construct_properties; slist; slist = slist->next)
     {
       clist = g_list_prepend (clist, slist->data);
Index: gobject.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gobject.h,v
retrieving revision 1.26
diff -u -p -r1.26 gobject.h
--- gobject.h	21 Mar 2002 00:34:04 -0000	1.26
+++ gobject.h	17 Mar 2003 20:06:55 -0000
@@ -116,6 +116,15 @@ GParamSpec* g_object_class_find_property
 					       const gchar    *property_name);
 GParamSpec**g_object_class_list_properties    (GObjectClass   *oclass,
 					       guint	      *n_properties);
+
+void        g_object_interface_install_property (GType        iface_type,
+						 GParamSpec  *pspec);
+GParamSpec* g_object_interface_find_property    (GType        iface_type,
+						 const gchar *property_name);
+GParamSpec**g_object_interface_list_properties  (GType        iface_type,
+						 guint       *n_properties_p);
+GParamSpec* g_object_property_find_overridden   (GParamSpec  *pspec);
+
 gpointer    g_object_new                      (GType           object_type,
 					       const gchar    *first_property_name,
 					       ...);
Index: gparam.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gparam.c,v
retrieving revision 1.27
diff -u -p -r1.27 gparam.c
--- gparam.c	7 Feb 2003 22:04:24 -0000	1.27
+++ gparam.c	17 Mar 2003 20:06:55 -0000
@@ -21,6 +21,7 @@
  * MT safe
  */
 
+#include        "gobject.h"
 #include	"gparam.h"
 
 
@@ -246,7 +247,20 @@ 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 if (pspec->flags & G_PARAM_OVERRIDE &&
+	   pspec->owner_type != G_TYPE_NONE &&
+	   G_TYPE_IS_OBJECT (pspec->owner_type))
+    {
+      GParamSpec *overridden;
+
+      overridden = g_object_property_find_overridden (pspec);
+      if (overridden && overridden->_nick)
+	return overridden->_nick;
+    }
+
+  return pspec->name;
 }
 
 G_CONST_RETURN gchar*
@@ -254,7 +268,20 @@ 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 if (pspec->flags & G_PARAM_OVERRIDE &&
+	   pspec->owner_type != G_TYPE_NONE &&
+	   G_TYPE_IS_OBJECT (pspec->owner_type))
+    {
+      GParamSpec *overridden;
+
+      overridden = g_object_property_find_overridden (pspec);
+      if (overridden && overridden->_blurb)
+	return overridden->_blurb;
+    }
+
+  return NULL;
 }
 
 static void
Index: gparam.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gparam.h,v
retrieving revision 1.22
diff -u -p -r1.22 gparam.h
--- gparam.h	3 Mar 2002 03:14:43 -0000	1.22
+++ gparam.h	17 Mar 2003 20:06:55 -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_OVERRIDE            = 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;
Index: testgobject.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/testgobject.c,v
retrieving revision 1.7
diff -u -p -r1.7 testgobject.c
--- testgobject.c	10 Mar 2003 16:41:28 -0000	1.7
+++ testgobject.c	17 Mar 2003 20:06:55 -0000
@@ -40,6 +40,16 @@ static void	iface_base_init		(TestIfaceC
 static void	iface_base_finalize	(TestIfaceClass	*iface);
 static void	print_foo		(TestIface	*tiobj,
 					 const gchar	*string);
+
+enum {
+  PROP_0,
+  PROP_TESTINT1
+};
+
+
+#define PROP_TESTINT1_NICK "Test Int 1"
+#define PROP_TESTINT1_BLURB "First test integer"
+
 GType
 test_iface_get_type (void)
 {
@@ -64,11 +74,35 @@ static guint iface_base_init_count = 0;
 static void
 iface_base_init (TestIfaceClass *iface)
 {
+  static gboolean initialized = FALSE;
+  
   iface_base_init_count++;
   if (iface_base_init_count == 1)
     {
       /* add signals here */
     }
+
+  if (!initialized)
+    {
+      /* We can't remove properties, so we have to make sure
+       * to add them only _once_.
+       */
+      initialized = TRUE;
+
+      /* In the implementation, we'll add the READABLE flag
+       * remove the CONSTRUCT_ONLY flag ...  both changes makes
+       * things less restrictive, so are OK.
+       */
+      g_object_interface_install_property (G_TYPE_FROM_INTERFACE (iface),
+					   g_param_spec_int ("testint1",
+							     PROP_TESTINT1_NICK,
+							     PROP_TESTINT1_BLURB,
+							     G_MININT, /* min */
+							     G_MAXINT, /* max */
+							     0,	       /* default */
+							     (G_PARAM_WRITABLE |
+							      G_PARAM_CONSTRUCT_ONLY)));
+    }
 }
 static void
 iface_base_finalize (TestIfaceClass *iface)
@@ -149,6 +183,14 @@ struct _TestObjectPrivate
 };
 static void	test_object_class_init	(TestObjectClass	*class);
 static void	test_object_init	(TestObject		*tobject);
+static void    test_object_set_property (GObject                *object,
+					 guint                   prop_id,
+					 const GValue           *value,
+					 GParamSpec             *pspec);
+static void    test_object_get_property (GObject                *object,
+					 guint                   prop_id,
+					 GValue                 *value,
+					 GParamSpec             *pspec);
 static gboolean	test_signal_accumulator	(GSignalInvocationHint	*ihint,
 					 GValue            	*return_accu,
 					 const GValue       	*handler_return,
@@ -186,8 +228,11 @@ test_object_get_type (void)
 static void
 test_object_class_init (TestObjectClass *class)
 {
-  /*  GObjectClass *gobject_class = G_OBJECT_CLASS (class); */
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
 
+  gobject_class->set_property = test_object_set_property;
+  gobject_class->get_property = test_object_get_property;
+  
   class->test_signal = test_object_test_signal;
 
   g_signal_new ("test-signal",
@@ -198,6 +243,17 @@ test_object_class_init (TestObjectClass 
 		g_cclosure_marshal_STRING__OBJECT_POINTER,
 		G_TYPE_STRING, 2, TEST_TYPE_IFACE, G_TYPE_POINTER);
 
+  g_object_class_install_property (gobject_class,
+				   PROP_TESTINT1,
+				   g_param_spec_int ("testint1",
+						     NULL, 
+						     NULL,
+						     G_MININT,
+						     G_MAXINT,
+						     0,
+						     G_PARAM_READWRITE |
+						     G_PARAM_OVERRIDE));
+
   g_type_class_add_private (class, sizeof (TestObjectPrivate));
 }
 static void
@@ -210,6 +266,46 @@ test_object_init (TestObject *tobject)
   g_assert (priv);
   g_assert ((gchar *)priv >= (gchar *)tobject + sizeof (TestObject));
 }
+static void
+test_object_set_property (GObject                *object,
+			  guint                   prop_id,
+			  const GValue           *value,
+			  GParamSpec             *pspec)
+{
+  TestObjectPrivate *priv;
+
+  priv = TEST_OBJECT_GET_PRIVATE (object);
+  
+  switch (prop_id)
+    {
+    case PROP_TESTINT1:
+      priv->dummy1 = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+static void
+test_object_get_property (GObject                *object,
+			  guint                   prop_id,
+			  GValue                 *value,
+			  GParamSpec             *pspec)
+{
+  TestObjectPrivate *priv;
+
+  priv = TEST_OBJECT_GET_PRIVATE (object);
+  
+  switch (prop_id)
+    {
+    case PROP_TESTINT1:
+      g_value_set_int (value, priv->dummy1);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
 static gboolean
 test_signal_accumulator (GSignalInvocationHint *ihint,
 			 GValue                *return_accu,
@@ -360,6 +456,9 @@ main (int   argc,
   TestObject *sigarg;
   DerivedObject *dobject;
   gchar *string = NULL;
+  TestObjectClass *test_class;
+  GParamSpec *pspec;
+  gint int_result;
 
   g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
 			  G_LOG_LEVEL_WARNING |
@@ -376,10 +475,23 @@ main (int   argc,
   g_assert (g_type_from_name ("FooShadow2") == G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_USER_FIRST + 1));
 
   /* to test past class initialization interface setups, create the class here */
-  g_type_class_ref (TEST_TYPE_OBJECT);
+  test_class = g_type_class_ref (TEST_TYPE_OBJECT);
 
+  /* Test redirection of nick/blurb from overriding properties
+   */
+  pspec = g_object_class_find_property (G_OBJECT_CLASS (test_class),
+					"testint1");
+  g_assert (strcmp (g_param_spec_get_nick (pspec), PROP_TESTINT1_NICK) == 0);
+  g_assert (strcmp (g_param_spec_get_blurb (pspec), PROP_TESTINT1_BLURB) == 0);
+  
   dobject = g_object_new (DERIVED_TYPE_OBJECT, NULL);
   sigarg = g_object_new (TEST_TYPE_OBJECT, NULL);
+
+  /* Test setting and getting properties
+   */
+  g_object_set (dobject, "testint1", 42, NULL);
+  g_object_get (dobject, "testint1", &int_result, NULL);
+  g_assert (int_result == 42);
 
   g_print ("MAIN: emit test-signal:\n");
   g_signal_emit_by_name (dobject, "test-signal", sigarg, NULL, &string);


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