Re: Interface properties (Re: 2.4 schedule)
- From: Owen Taylor <otaylor redhat com>
- To: Tim Janik <timj gtk org>
- Cc: Gtk+ Developers <gtk-devel-list gnome org>
- Subject: Re: Interface properties (Re: 2.4 schedule)
- Date: Mon, 13 Oct 2003 23:51:39 -0400
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.)
Regards,
Owen
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 03:32:55 -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 03:32:55 -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 03:32:56 -0000
@@ -181,6 +181,94 @@ 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 a few cases,
+and in particular, for of @construct_properties argument of
+the @constructor virtual function, and the @pspec argument of
+of the "notify" signal, 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 03:32:56 -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/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 03:32:56 -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 +442,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)
{
@@ -574,6 +705,84 @@ object_set_property (GObject
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_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 (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
g_object_new (GType object_type,
const gchar *first_property_name,
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 03:32:56 -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 03:32:57 -0000
@@ -22,7 +22,7 @@
*/
#include "gparam.h"
-
+#include "gparamspecs.h"
#include "gvaluecollector.h"
#include <string.h>
@@ -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,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 +836,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 +847,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 +895,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 +951,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 03:32:57 -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 03:32:57 -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 03:32:57 -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 03:32:58 -0000
@@ -50,6 +50,7 @@ test_programs = \
ifacecheck \
ifaceinit \
ifaceinherit \
+ ifaceproperties \
override
check_PROGRAMS = $(test_programs)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]