Interfaces on properties, 2002-08-27



Here's another version of interfaces on properties, modified
to more closely follow what you sketched out in:

http://mail.gnome.org/archives/gtk-devel-list/2003-July/msg00162.html

The main changes are:

 - Added g_param_spec_set/get_redirect_property()

 - Added g_object_override_property() that looks up the overridden
   property immediately, thus requiring the base-init reordering
   change.

 - Made g_param_spec_pool_list() g_object_class_find_property()
   respect property redirections.

Notes:

 - The main open question in my mind is whether we should keep
   g_param_spec_set_redirect_target() and the REDIRECT flag as
   a separate concept from g_param_spec_override() - if we
   simply made g_param_spec_get_redirect_target know about
   G_PARAM_SPEC_OVERRIDE, we could get rid of the need to 
   store the redirect target in qdata.

   Right now, you can do a couple of things with 
   g_param_spec_set_redirect_target() you can't do with
   a hidden g_param_spec_override():

    - You can have your own implementations of set_default(),
      validate() that don't just chain up.  '
    - You can set nick/blurb on your paramspec and have
      that override the nick/blurb from the redirect target.

   However, since redirected properties are invisible to 
   g_object_class_list_properties() and 
   g_object_class_find_property(), I'm not sure that's a good
   thing to allow.

 - I didn't change the fact that 
   g_object_iterface_find_property/list_properties() take a
   GType rather than an iface class; to my mind that needs
   to wait until g_type_class_ref (iface_type) works.

 - g_object_class_check_iface_properties() needs to be reworked
   once we have a g_type_set_interface_postinit_function().

 - Doc comments will be moved into the template files once we 
   are done discussing the interfaces. 

Regards,
						Owen

Index: gobject.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gobject.c,v
retrieving revision 1.59
diff -u -p -r1.59 gobject.c
--- gobject.c	8 Aug 2003 19:47:35 -0000	1.59
+++ gobject.c	27 Aug 2003 19:01:21 -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 ("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
 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,65 @@ g_object_class_install_property (GObject
     class->construct_properties = g_slist_remove (class->construct_properties, pspec);
 }
 
+/**
+ * g_object_interface_install_property:
+ * @g_iface: an interface structure passed to the base_init
+ *   function for an interface.
+ * @pspec: the #GParamSpec for the new property
+ * 
+ * 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 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_REDIRECT 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_get_redirect_target ()) #GParamSpecOverride
+ * provides an easy way to create an implementation property.
+ *
+ * This function is meant to be called from the interface's
+ * base_init function the first time that base_init function
+ * is called.
+ *
+ * <informalexample><programlisting>
+ * static void
+ * my_iface_base_init (gpointer g_iface)
+ * {
+ *   static gboolean initialized = FALSE;
+ *   if (!initialized)
+ *     {
+ *       g_object_interface_install_property (g_iface,
+ *                                             g_param_spec_boolean ("my_prop",
+ *                                                                   "My Property",
+ *                                                                   "Some boolean property",
+ *                                                                   FALSE,
+ *                                                                   G_PARAM_READWRITE));
+ *        initialized = TRUE;
+ *     }
+ *  }
+ * </informalexample></programlisting>
+ *
+ * Note that in the rare case that you are using dynamically loaded
+ * interfaces, then you have to keep a count of the difference between
+ * the number of times the base_init and base_finalize are called
+ * and install properties when that count goes from 0 to 1.
+ **/
+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_TYPE_IS_OBJECT (iface_class->g_instance_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_class->g_type, 0, pspec);
+}
+
 GParamSpec*
 g_object_class_find_property (GObjectClass *class,
 			      const gchar  *property_name)
@@ -312,6 +380,92 @@ 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)
+{
+  GParamSpec *pspec;
+  GParamSpec *redirect;
+	
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_type), NULL);
+  g_return_val_if_fail (property_name != NULL, NULL);
+  
+  pspec = g_param_spec_pool_lookup (pspec_pool,
+				    property_name,
+				    iface_type,
+				    FALSE);
+  
+  redirect = g_param_spec_get_redirect_target (pspec);
+  if (redirect)
+    return redirect;
+  else
+    return pspec;
+
+}
+
+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 */
 g_object_class_list_properties (GObjectClass *class,
 				guint        *n_properties_p)
@@ -330,6 +484,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 +777,98 @@ 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 REDIRECT 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 +887,11 @@ 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 check fails, bail out, leaking class */
+  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);
@@ -1532,13 +1815,6 @@ g_value_set_object (GValue   *value,
 void
 g_value_set_object_take_ownership (GValue  *value,
 				   gpointer v_object)
-{
-  g_value_take_object (value, v_object);
-}
-
-void
-g_value_take_object (GValue  *value,
-		     gpointer v_object)
 {
   g_return_if_fail (G_VALUE_HOLDS_OBJECT (value));
 
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	27 Aug 2003 19:01:21 -0000
@@ -116,6 +116,19 @@ 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    (GType        iface_type,
+						 const gchar *property_name);
+GParamSpec**g_object_interface_list_properties  (GType        iface_type,
+						 guint       *n_properties_p);
+
+GParamSpec* g_object_property_get_redirect_target (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	27 Aug 2003 19:01:21 -0000
@@ -63,6 +63,7 @@ static gchar*	value_param_lcopy_value		(
 
 /* --- variables --- */
 static GQuark quark_floating = 0;
+static GQuark quark_redirect_target = 0;
 G_LOCK_DEFINE_STATIC (pspec_ref_count);
 
 
@@ -123,6 +124,7 @@ g_param_spec_class_init (GParamSpecClass
 			 gpointer         class_data)
 {
   quark_floating = g_quark_from_static_string ("GParamSpec-floating");
+  quark_redirect_target = g_quark_from_static_string ("GParamSpec-redirect-target");
 
   class->value_type = G_TYPE_NONE;
   class->finalize = g_param_spec_finalize;
@@ -246,7 +248,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 +267,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 +363,65 @@ g_param_spec_steal_qdata (GParamSpec *ps
   return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
 }
 
+/**
+ * g_param_spec_get_redirect_target:
+ * @pspec: a #GParamSpec
+ * 
+ * Gets the paramspec previously set by g_param_spec_set_redirect_target(),
+ * if any.
+ * 
+ * Return value: paramspec to which requests on this paramspec should
+ *  be redirected, or %NULL if none.
+ **/
+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)
+    return NULL;
+  
+  return g_param_spec_get_qdata (pspec, quark_redirect_target);
+}
+
+/**
+ * g_param_spec_set_redirect_target:
+ * @pspec: a #GParamSpec
+ * @redirect: the paramspec to which requests on this paramspec should be redirected
+ * 
+ * Establishes that operations on @pspec other than setting or getting the value
+ * should be redirected to @redirect. This affects accessing the nick and blurb
+ * for the paramspec, validating a value, and so forth. This is normally used behind the
+ * scenes, and will seldom or never be useful to call directly. See
+ * g_object_class_override_property() for the interface an application would generally
+ * use, g_param_spec_override() for the interface you would use if you were implementing
+ * a new type of base object.
+ **/
+void
+g_param_spec_set_redirect_target (GParamSpec *pspec,
+				  GParamSpec *redirect)
+{
+  g_return_if_fail (G_IS_PARAM_SPEC (pspec));
+  g_return_if_fail (G_IS_PARAM_SPEC (redirect));
+
+  pspec->flags |= G_PARAM_REDIRECT;
+
+  /* Dereference redirections for redirect
+   */
+  while (TRUE)
+    {
+      GParamSpec *indirect = g_param_spec_get_redirect_target (redirect);
+      if (indirect)
+	redirect = indirect;
+      else
+	break;
+    }
+
+  g_param_spec_set_qdata_full (pspec, quark_redirect_target,
+			       g_param_spec_ref (redirect),
+			       (GDestroyNotify) g_param_spec_unref);
+}
+
 void
 g_param_value_set_default (GParamSpec *pspec,
 			   GValue     *value)
@@ -793,10 +876,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 +887,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 (pspec);
+	      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;
@@ -836,6 +941,29 @@ pool_depth_list (gpointer key,
     }
 }
 
+/* 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,9 +984,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);
+
+  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 - 1; i++)
-    slists[i] = pspec_list_remove_overridden (slists[i], pool->hash_table, owner_type, n_pspecs_p);
+    slists[i] = pspec_list_remove_overridden_and_redirected (slists[i], pool->hash_table, owner_type, n_pspecs_p);
   *n_pspecs_p += g_slist_length (slists[i]);
   pspecs = g_new (GParamSpec*, *n_pspecs_p + 1);
   p = pspecs;
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	27 Aug 2003 19:01:21 -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,10 @@ 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_spec_set_redirect_target (GParamSpec   *pspec,
+						  GParamSpec   *redirect);
+
 void		g_param_value_set_default	(GParamSpec    *pspec,
 						 GValue	       *value);
 gboolean	g_param_value_defaults		(GParamSpec    *pspec,
Index: gparamspecs.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gparamspecs.c,v
retrieving revision 1.25
diff -u -p -r1.25 gparamspecs.c
--- gparamspecs.c	7 Feb 2003 22:04:24 -0000	1.25
+++ gparamspecs.c	27 Aug 2003 19:01:21 -0000
@@ -966,6 +966,42 @@ 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_set_default (GParamSpec *pspec,
+			    GValue     *value)
+{
+  GParamSpec *redirect_target = g_param_spec_get_redirect_target (pspec);
+  g_assert (redirect_target);
+
+  g_param_value_set_default (redirect_target, value);
+}
+
+static gboolean
+param_override_validate (GParamSpec *pspec,
+			 GValue     *value)
+{
+  GParamSpec *redirect_target = g_param_spec_get_redirect_target (pspec);
+  g_assert (redirect_target);
+  
+  return g_param_value_validate (redirect_target, value);
+}
+
+static gint
+param_override_values_cmp (GParamSpec   *pspec,
+			   const GValue *value1,
+			   const GValue *value2)
+{
+  GParamSpec *redirect_target = g_param_spec_get_redirect_target (pspec);
+  g_assert (redirect_target);
+  
+  return g_param_values_cmp (redirect_target, value1, value2);
+}
 
 /* --- type initialization --- */
 GType *g_param_spec_types = NULL;
@@ -973,7 +1009,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 +1377,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 */
+      NULL,			 /* 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 +1884,36 @@ g_param_spec_object (const gchar *name,
   G_PARAM_SPEC (ospec)->value_type = object_type;
   
   return G_PARAM_SPEC (ospec);
+}
+
+/**
+ * g_param_spec_override:
+ * @name: the name of the property.
+ * @overridden: The property that is being overridden
+ * 
+ * Creates a new property of type #GParamSpecOverride. This is a
+ * magic paramspec type that simply redirects all operations to
+ * another paramspec.  (See g_param_spec_set_redirect_target). It is
+ * used in implementing g_object_class_override_property(), and
+ * generally will not be directly useful unless you are implementing a
+ * new base type similar to GObject.
+ * 
+ * Return value: the newly created #GParamSpec
+ **/
+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);
+  
+  pspec = g_param_spec_internal (G_TYPE_PARAM_OVERRIDE,
+				 name, NULL, NULL,
+				 overridden->flags);
+  pspec->value_type = G_PARAM_SPEC_VALUE_TYPE (overridden);
+  g_param_spec_set_redirect_target (pspec, overridden);
+
+  return pspec;
 }
Index: gparamspecs.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gparamspecs.h,v
retrieving revision 1.21
diff -u -p -r1.21 gparamspecs.h
--- gparamspecs.h	22 Nov 2001 18:55:05 -0000	1.21
+++ gparamspecs.h	27 Aug 2003 19:01:21 -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,10 @@ struct _GParamSpecObject
 {
   GParamSpec    parent_instance;
 };
+struct _GParamSpecOverride
+{
+  GParamSpec    parent_instance;
+};
 
 /* --- GParamSpec prototypes --- */
 GParamSpec*	g_param_spec_char	 (const gchar	 *name,
@@ -382,6 +390,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: testgobject.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/testgobject.c,v
retrieving revision 1.9
diff -u -p -r1.9 testgobject.c
--- testgobject.c	19 Aug 2003 03:25:46 -0000	1.9
+++ testgobject.c	27 Aug 2003 19:01:21 -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,31 @@ 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;
+
+      g_object_interface_install_property (iface,
+					   g_param_spec_int ("testint1",
+							     PROP_TESTINT1_NICK,
+							     PROP_TESTINT1_BLURB,
+							     G_MININT, /* min */
+							     G_MAXINT, /* max */
+							     0,	       /* default */
+							     (G_PARAM_READABLE |
+							      G_PARAM_WRITABLE)));
+    }
 }
 static void
 iface_base_finalize (TestIfaceClass *iface)
@@ -149,6 +179,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 +224,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 +239,8 @@ test_object_class_init (TestObjectClass 
 		g_cclosure_marshal_STRING__OBJECT_POINTER,
 		G_TYPE_STRING, 2, TEST_TYPE_IFACE, G_TYPE_POINTER);
 
+  g_object_class_override_property (gobject_class, PROP_TESTINT1, "testint1");
+
   g_type_class_add_private (class, sizeof (TestObjectPrivate));
 }
 static void
@@ -212,6 +255,46 @@ test_object_init (TestObject *tobject)
 
   priv->dummy1 = 54321;
 }
+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->dummy2 = 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->dummy2);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
 /* Check to see if private data initialization in the
  * instance init function works.
  */
@@ -382,6 +465,9 @@ main (int   argc,
   DerivedObject *dobject;
   TestObjectPrivate *priv;
   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 |
@@ -398,12 +484,25 @@ 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);
   test_object_check_private_init (TEST_OBJECT (dobject));
 
   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]