Another iteration of the properties-on-interfaces patch
- From: Owen Taylor <otaylor redhat com>
- To: timj gtk org, gtk-devel-list gtk org
- Subject: Another iteration of the properties-on-interfaces patch
- Date: 10 Jul 2003 17:46:11 -0400
Hi Tim,
Here's another iteration of the properties on interfaces patch
incorporating 2 changes that we discussed on IRC some while
ago (I've attached the discussion to
http://bugzilla.gnome.org/show_bug.cgi?id=105894)
- Rename G_PARAM_OVERRIDE to G_PARAM_REDIRECT and
g_object_property_find_overridden() to
g_object_property_get_redirect_target()
GParamSpecOverride keeps its name, as discussed
- g_object_interface_install_property() now takes a
pointer to an interface (from base_init) rather than
a GType
g_object_interface_install_property() an
g_object_interface_list_properties keep the GType as
their parameter because they aren't called from base_init()
and it would be really annoying to have to find a type
implementing an interface in order to list the properties
of the interface.
Other notes:
- I didn't try to change the way GParamSpecPool works
to optimize g_object_property_find_overridden();
that can be done later.
- The docs are inline, I'll move them out of line before
comitting
- I'd really like to get this in CVS soon, even if the
implementation isn't perfect, so that we can start
using it, especially for the new file chooser.
Regards,
Owen
Index: gobject.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gobject.c,v
retrieving revision 1.58
diff -u -p -r1.58 gobject.c
--- gobject.c 30 May 2003 18:44:57 -0000 1.58
+++ gobject.c 10 Jul 2003 21:39:27 -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,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,108 @@ 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_get_redirect_target:
+ * @pspec: a #GParamSpec
+ *
+ * Given a property, finds the property that provides the
+ * defaults for this property. (The common example of this
+ * is using a #GParamSpecOverride in a class implementation
+ * to provide the implementation for a property in one
+ * of the class's interfaces.) @pspec must have the
+ * %G_PARAM_REDIRECT flag set for this function to do
+ * anything interesting. If %G_PARAM_REDIRECT is set,
+ * then first the parent types of the property's owner
+ * type are checked for a property with the same name, then the
+ * interfaces that the owner type implements are checked.
+ * If the flag is not set, %NULL is returned immediately.
+ *
+ * Return value: a #GParamSpec, or %NULL if this paramspec
+ * does not inherit defaults from another paramspec.
+ **/
+GParamSpec*
+g_object_property_get_redirect_target (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_REDIRECT))
+ 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_get_redirect_target() "
+ "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 +500,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 +793,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 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 +904,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 10 Jul 2003 21:39:27 -0000
@@ -116,6 +116,16 @@ 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 (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 10 Jul 2003 21:39:27 -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_REDIRECT &&
+ pspec->owner_type != G_TYPE_NONE &&
+ G_TYPE_IS_OBJECT (pspec->owner_type))
+ {
+ GParamSpec *redirect_target;
+
+ redirect_target = g_object_property_get_redirect_target (pspec);
+ if (redirect_target && redirect_target->_nick)
+ return redirect_target->_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_REDIRECT &&
+ pspec->owner_type != G_TYPE_NONE &&
+ G_TYPE_IS_OBJECT (pspec->owner_type))
+ {
+ GParamSpec *redirect_target;
+
+ redirect_target = g_object_property_get_redirect_target (pspec);
+ if (redirect_target && redirect_target->_blurb)
+ return redirect_target->_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 10 Jul 2003 21:39:27 -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;
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 10 Jul 2003 21:39:27 -0000
@@ -25,6 +25,7 @@
#include "gparamspecs.h"
+#include "gobject.h" /* For g_object_property_find_overridden */
#include "gvaluecollector.h"
#include "gvaluearray.h"
#include <string.h>
@@ -966,6 +967,67 @@ 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)
+{
+ GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec);
+
+ if (G_VALUE_TYPE (&ospec->default_value) != G_TYPE_NONE)
+ {
+ g_value_copy (&ospec->default_value, value);
+ }
+ else
+ {
+ GParamSpec *redirect_target = g_object_property_get_redirect_target (pspec);
+
+ if (redirect_target)
+ g_param_value_set_default (redirect_target, value);
+ else
+ g_warning ("GParamSpecOverride: cannot find overridden property for set_default()");
+ }
+}
+
+static gboolean
+param_override_validate (GParamSpec *pspec,
+ GValue *value)
+{
+ GParamSpec *redirect_target = g_object_property_get_redirect_target (pspec);
+
+ if (redirect_target)
+ {
+ return g_param_value_validate (redirect_target, value);
+ }
+ else
+ {
+ g_warning ("GParamSpecOverride: cannot find overridden property for validate()");
+ return FALSE;
+ }
+}
+
+static gint
+param_override_values_cmp (GParamSpec *pspec,
+ const GValue *value1,
+ const GValue *value2)
+{
+ GParamSpec *redirect_target = g_object_property_get_redirect_target (pspec);
+
+ if (redirect_target)
+ {
+ return g_param_values_cmp (redirect_target, value1, value2);
+ }
+ else
+ {
+ g_warning ("GParamSpecOverride: cannot find overridden property for values_cmp()");
+ return -1;
+ }
+}
/* --- type initialization --- */
GType *g_param_spec_types = NULL;
@@ -973,7 +1035,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 +1403,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 +1910,112 @@ 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.
+ * @valuetype: value type for this property. This must
+ * match the value type for the property
+ * being overridden or an error will occur.
+ * @flags: flags for this property. These must be
+ * less restrictive than those of the property
+ * being overridden. It is not necessary to
+ * specify %G_PARAM_REDIRECT here: it will
+ * automatically be added.
+ *
+ * Creates a new property of type #GParamSpecOverride.
+ * This is a magic paramspec type, used when overriding
+ * a property of a parent object type. The name, nick, blurb,
+ * default value, and, if applicable, range will come
+ * from the parent object type. The default value can
+ * be overridden with g_param_spec_override_set_default().
+ *
+ * Return value: the newly created #GParamSpec
+ **/
+GParamSpec*
+g_param_spec_override (const gchar *name,
+ GType value_type,
+ GParamFlags flags)
+{
+ GParamSpecOverride *ospec;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ ospec = g_param_spec_internal (G_TYPE_PARAM_OVERRIDE,
+ name, NULL, NULL,
+ flags | G_PARAM_REDIRECT);
+ G_PARAM_SPEC (ospec)->value_type = value_type;
+
+ return G_PARAM_SPEC (ospec);
+}
+
+/**
+ * g_param_spec_override_set_default_value:
+ * @ospec: a #GParamSpecOverride
+ * @default_value: GValue holding the new default value,
+ * or %NULL if the default value should
+ * come from the overridden property.
+ *
+ * Sets the default value of a GParamSpecOverride,
+ * overriding the default value of the overridden property.
+ **/
+void
+g_param_spec_override_set_default_value (GParamSpecOverride *ospec,
+ GValue *default_value)
+{
+ g_return_if_fail (G_IS_PARAM_SPEC_OVERRIDE (ospec));
+
+ if (default_value)
+ {
+ g_value_init (&ospec->default_value, G_PARAM_SPEC (ospec)->value_type);
+ g_value_copy (&ospec->default_value, default_value);
+ }
+ else
+ {
+ g_value_unset (&ospec->default_value);
+ }
+}
+
+/**
+ * g_param_spec_override_set_default:
+ * @ospec: a #GParamSpecOverride
+ * @Varargs: the new default value
+ *
+ * Like g_param_spec_override_set_default_value(), but uses
+ * a variable argument list so that the value can be
+ * passed in C form rather than as a #GValue. The form
+ * of the passed parameter is the same as it would be
+ * for g_object_set(). (Unsetting the value is not possible
+ * with this function, use g_param_spec_override_set_default_value()
+ * for that)
+ **/
+void
+g_param_spec_override_set_default (GParamSpecOverride *ospec,
+ ...)
+{
+ va_list var_args;
+ GType value_type;
+ gchar *error = NULL;
+
+ g_return_if_fail (G_IS_PARAM_SPEC_OVERRIDE (ospec));
+
+ value_type = G_PARAM_SPEC (ospec)->value_type;
+
+ va_start (var_args, ospec);
+
+ g_value_init (&ospec->default_value, value_type);
+ G_VALUE_COLLECT (&ospec->default_value, var_args, 0, &error);
+
+ if (error)
+ {
+ g_warning ("g_param_spec_override_set_default: %s", error);
+ g_free (error);
+
+ /* we purposely leak the value here, it might not be
+ * in a sane state if an error condition occured
+ */
+ }
+
+ va_end (var_args);
}
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 10 Jul 2003 21:39:27 -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,11 @@ struct _GParamSpecObject
{
GParamSpec parent_instance;
};
+struct _GParamSpecOverride
+{
+ GParamSpec parent_instance;
+ GValue default_value;
+};
/* --- GParamSpec prototypes --- */
GParamSpec* g_param_spec_char (const gchar *name,
@@ -382,6 +391,13 @@ GParamSpec* g_param_spec_object (const
GType object_type,
GParamFlags flags);
+GParamSpec* g_param_spec_override (const gchar *name,
+ GType value_type,
+ GParamFlags flags);
+void g_param_spec_override_set_default_value (GParamSpecOverride *ospec,
+ GValue *default_value);
+void g_param_spec_override_set_default (GParamSpecOverride *ospec,
+ ...);
/* --- internal --- */
/* We prefix variable declarations so they can
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 10 Jul 2003 21:39:27 -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 (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,12 @@ 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);
+ GParamSpec *pspec;
+ 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 +244,14 @@ test_object_class_init (TestObjectClass
g_cclosure_marshal_STRING__OBJECT_POINTER,
G_TYPE_STRING, 2, TEST_TYPE_IFACE, G_TYPE_POINTER);
+ pspec = g_param_spec_override ("testint1",
+ G_TYPE_INT,
+ G_PARAM_READWRITE);
+ g_param_spec_override_set_default (G_PARAM_SPEC_OVERRIDE (pspec), 24);
+ g_object_class_install_property (gobject_class,
+ PROP_TESTINT1,
+ pspec);
+
g_type_class_add_private (class, sizeof (TestObjectPrivate));
}
static void
@@ -209,6 +263,48 @@ test_object_init (TestObject *tobject)
g_assert (priv);
g_assert ((gchar *)priv >= (gchar *)tobject + sizeof (TestObject));
+
+ priv->dummy1 = 24;
+}
+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,
@@ -360,6 +456,10 @@ main (int argc,
TestObject *sigarg;
DerivedObject *dobject;
gchar *string = NULL;
+ TestObjectClass *test_class;
+ GParamSpec *pspec;
+ GValue value = { 0, };
+ gint int_result;
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
G_LOG_LEVEL_WARNING |
@@ -376,10 +476,28 @@ 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);
+
+ /* Test g_param_spec_override_set_default() */
+ g_value_init (&value, G_TYPE_INT);
+ g_value_set_int (&value, 24);
+ g_assert (g_param_value_defaults (pspec, &value));
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]