[glib/wip/gproperty-2: 12/23] gproperty: Add default values



commit 13a62ca2255f175631d65b57e594b908c432ac5d
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Wed Apr 24 11:53:40 2013 -0400

    gproperty: Add default values
    
    GProperty should store the default value (if any) of a property, as well
    as the eventual overrides coming from the sub-classes of the class that
    defined the property.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=648526

 gobject/gproperty.c      |  442 +++++++++++++++++++++++++++++++++++++++++++++-
 gobject/gproperty.h      |   26 +++-
 gobject/tests/property.c |   32 ++++
 3 files changed, 491 insertions(+), 9 deletions(-)
---
diff --git a/gobject/gproperty.c b/gobject/gproperty.c
index 5a22b03..e06c7fa 100644
--- a/gobject/gproperty.c
+++ b/gobject/gproperty.c
@@ -399,6 +399,23 @@ g_property_create (GType           pspec_type,
   return prop;
 }
 
+static inline void
+g_property_ensure_prop_id (GProperty *property)
+{
+  char *prop_id;
+
+  if (G_LIKELY (property->prop_id != 0))
+    return;
+
+  prop_id = g_strconcat ("-g-property-",
+                         G_PARAM_SPEC (property)->name,
+                         NULL);
+
+  property->prop_id = g_quark_from_string (prop_id);
+
+  g_free (prop_id);
+}
+
 #define DEFINE_PROPERTY_INTEGER(G_t, g_t, c_t, G_T, minVal, maxVal) \
 typedef struct { \
   GProperty parent; \
@@ -2748,14 +2765,7 @@ _g_property_set_installed (GProperty *property,
         property->priv_offset = g_type_class_get_instance_private_offset (g_class);
     }
 
-  if (property->prop_id == 0)
-    {
-      gchar *prop_id = g_strconcat ("-g-property-id-",
-                                    G_PARAM_SPEC (property)->name,
-                                    NULL);
-      property->prop_id = g_quark_from_string (prop_id);
-      g_free (prop_id);
-    }
+  g_property_ensure_prop_id (property);
 
   property->is_installed = TRUE;
 }
@@ -3450,6 +3460,395 @@ g_property_get_range (GProperty *property,
   return retval;
 }
 
+static void
+value_unset_and_free (gpointer data)
+{
+  if (G_LIKELY (data != NULL))
+    {
+      GValue *value = data;
+
+      g_value_unset (value);
+      g_free (value);
+    }
+}
+
+static inline void
+g_property_set_default_value_internal (GProperty *property,
+                                       GValue    *value)
+{
+  GHashTable *default_values;
+
+  g_property_ensure_prop_id (property);
+
+  default_values = g_param_spec_get_qdata (G_PARAM_SPEC (property),
+                                           property->prop_id);
+  if (default_values == NULL)
+    {
+      default_values = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                              g_free,
+                                              value_unset_and_free);
+      g_param_spec_set_qdata_full (G_PARAM_SPEC (property),
+                                   property->prop_id,
+                                   default_values,
+                                   (GDestroyNotify) g_hash_table_unref);
+    }
+
+  g_hash_table_replace (default_values,
+                        g_strdup ("-g-property-default-value"),
+                        value);
+}
+
+static inline const GValue *
+g_property_get_default_value_internal (GProperty *property,
+                                       GType      gtype)
+{
+  GHashTable *default_values;
+  const GValue *default_value = NULL;
+  GType iter;
+
+  if (property->prop_id == 0)
+    return NULL;
+
+  default_values = g_param_spec_get_qdata (G_PARAM_SPEC (property),
+                                           property->prop_id);
+  if (default_values == NULL)
+    return NULL;
+
+  /* we look for overridden default values on ourselves and our parent
+   * classes first...*/
+  iter = gtype;
+  while (iter != G_TYPE_INVALID && default_value == NULL)
+    {
+      default_value = g_hash_table_lookup (default_values, g_type_name (iter));
+
+      iter = g_type_parent (iter);
+    }
+
+  /* ...as well as on our interfaces... */
+  if (default_value == NULL)
+    {
+      GType *ifaces = NULL;
+      guint n_ifaces = 0;
+
+      ifaces = g_type_interfaces (gtype, &n_ifaces);
+      while (n_ifaces-- && default_value == NULL)
+        {
+          iter = ifaces[n_ifaces];
+          default_value = g_hash_table_lookup (default_values, g_type_name (iter));
+        }
+
+      g_free (ifaces);
+    }
+
+  /* ...and if we fail, we pick the default value set by the class that
+   * installed the property first
+   */
+  if (default_value == NULL)
+    default_value = g_hash_table_lookup (default_values, "-g-property-default-value");
+
+  return default_value;
+}
+
+static inline void
+g_property_override_default_value_internal (GProperty *property,
+                                            GType      class_type,
+                                            GValue    *value)
+{
+  GHashTable *default_values;
+
+  g_property_ensure_prop_id (property);
+
+  default_values = g_param_spec_get_qdata (G_PARAM_SPEC (property),
+                                           property->prop_id);
+  if (default_values == NULL)
+    {
+      default_values = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                              g_free,
+                                              value_unset_and_free);
+      g_param_spec_set_qdata_full (G_PARAM_SPEC (property),
+                                   property->prop_id,
+                                   default_values,
+                                   (GDestroyNotify) g_hash_table_unref);
+    }
+
+  g_hash_table_replace (default_values,
+                        g_strdup (g_type_name (class_type)),
+                        value);
+}
+
+/**
+ * g_property_set_default_value:
+ * @property: a #GProperty
+ * @value: the default value of the property
+ *
+ * Sets the default value of @property using @value.
+ *
+ * This function will copy the passed #GValue, transforming it into the
+ * type required by the @property using the #GValue transformation rules
+ * if necessary.
+ *
+ * Since: 2.38
+ */
+void
+g_property_set_default_value (GProperty    *property,
+                              const GValue *value)
+{
+  GValue *default_value;
+
+  g_return_if_fail (G_IS_PROPERTY (property));
+  g_return_if_fail (value != NULL);
+
+  default_value = g_new0 (GValue, 1);
+  g_value_init (default_value, G_PARAM_SPEC (property)->value_type);
+
+  if (!g_value_transform (value, default_value))
+    {
+      g_critical (G_STRLOC ": Unable to transform a value of type '%s' "
+                  "into a value of type '%s'",
+                  g_type_name (G_VALUE_TYPE (value)),
+                  g_type_name (G_VALUE_TYPE (default_value)));
+      return;
+    }
+
+  /* will take ownership of the GValue */
+  g_property_set_default_value_internal (property, default_value);
+}
+
+/**
+ * g_property_get_default_value:
+ * @property: a #GProperty
+ * @gobject: a #GObject
+ * @value: a #GValue
+ *
+ * Retrieves the default value of @property using the type of
+ * the @object instance, and places it into @value.
+ *
+ * The default value takes into account eventual overrides installed by the
+ * class of @gobject.
+ *
+ * This function will initialize @value to the type of the property,
+ * if @value is not initialized; otherwise, it will obey #GValue
+ * transformation rules between the type of the property and the type
+ * of the passed #GValue.
+ *
+ * Since: 2.38
+ */
+void
+g_property_get_default_value (GProperty *property,
+                              gpointer   gobject,
+                              GValue    *value)
+{
+  const GValue *default_value;
+  GType gtype;
+
+  g_return_if_fail (G_IS_PROPERTY (property));
+  g_return_if_fail (G_IS_OBJECT (gobject));
+
+  gtype = G_OBJECT_TYPE (gobject);
+  default_value = g_property_get_default_value_internal (property, gtype);
+
+  /* initialize the GValue */
+  if (!G_IS_VALUE (value))
+    {
+      g_value_init (value, G_PARAM_SPEC (property)->value_type);
+
+      if (default_value != NULL)
+        g_value_copy (default_value, value);
+    }
+  else
+    {
+      if (default_value == NULL)
+        return;
+
+      if (!g_value_transform (default_value, value))
+        {
+          g_critical (G_STRLOC ": Unable to transform a value of type '%s' "
+                      "into a value of type '%s'",
+                      g_type_name (G_VALUE_TYPE (default_value)),
+                      g_type_name (G_VALUE_TYPE (value)));
+        }
+    }
+}
+
+/**
+ * g_property_override_default_value:
+ * @property: a #GProperty
+ * @class_type: the type that is overriding the default value of @property
+ * @value: a #GValue containing the new default value
+ *
+ * Overrides the default value of @property for the class of @class_type.
+ *
+ * This function should be called by sub-classes that desire overriding
+ * the default value of @property.
+ *
+ * Since: 2.38
+ */
+void
+g_property_override_default_value (GProperty    *property,
+                                   GType         class_type,
+                                   const GValue *value)
+{
+  GValue *default_value;
+
+  g_return_if_fail (G_IS_PROPERTY (property));
+  g_return_if_fail (class_type != G_TYPE_INVALID);
+  g_return_if_fail (value != NULL);
+
+  default_value = g_new0 (GValue, 1);
+  g_value_init (default_value, G_PARAM_SPEC (property)->value_type);
+
+  if (!g_value_transform (value, default_value))
+    {
+      g_critical (G_STRLOC ": Unable to transform a value of type '%s' "
+                  "into a value of type '%s'",
+                  g_type_name (G_VALUE_TYPE (value)),
+                  g_type_name (G_VALUE_TYPE (default_value)));
+      return;
+    }
+
+  /* will take ownership of the GValue */
+  g_property_override_default_value_internal (property, class_type, default_value);
+}
+
+/**
+ * g_property_set_default:
+ * @property: a #GProperty
+ * @...: the default value for the property
+ *
+ * Sets the default value of @property.
+ *
+ * This function is for the convenience of the C API users; language
+ * bindings should use the equivalent g_property_set_default_value()
+ * instead.
+ *
+ * Since: 2.38
+ */
+void
+g_property_set_default (GProperty *property,
+                        ...)
+{
+  va_list var_args;
+  GValue *value;
+  char *error;
+
+  g_return_if_fail (G_IS_PROPERTY (property));
+
+  va_start (var_args, property);
+
+  value = g_new0 (GValue, 1);
+  G_VALUE_COLLECT_INIT (value, G_PARAM_SPEC (property)->value_type, var_args, 0, &error);
+  if (error != NULL)
+    {
+      g_critical (G_STRLOC ": %s", error);
+      g_free (error);
+      g_value_unset (value);
+      g_free (value);
+    }
+  else
+    {
+      /* takes ownership of the GValue */
+      g_property_set_default_value_internal (property, value);
+    }
+
+  va_end (var_args);
+}
+
+/**
+ * g_property_get_default:
+ * @property: a #GProperty
+ * @gobject: a #GObject
+ * @...: return location for the default value
+ *
+ * Retrieves the default value of @property for the given @gobject instance.
+ *
+ * The default value takes into account eventual overrides installed by the
+ * class of @gobject.
+ *
+ * Since: 2.38
+ */
+void
+g_property_get_default (GProperty *property,
+                        gpointer   gobject,
+                        ...)
+{
+  const GValue *default_value;
+  GValue value = G_VALUE_INIT;
+  char *error = NULL;
+  va_list var_args;
+  GType gtype;
+
+  g_return_if_fail (G_IS_PROPERTY (property));
+  g_return_if_fail (G_IS_OBJECT (gobject));
+
+  g_value_init (&value, G_PARAM_SPEC (property)->value_type);
+
+  gtype = G_OBJECT_TYPE (gobject);
+  default_value = g_property_get_default_value_internal (property, gtype);
+  if (default_value != NULL)
+    g_value_copy (default_value, &value);
+
+  va_start (var_args, gobject);
+
+  G_VALUE_LCOPY (&value, var_args, 0, &error);
+  if (error != NULL)
+    {
+      g_warning (G_STRLOC ": %s", error);
+      g_free (error);
+    }
+
+  va_end (var_args);
+  g_value_unset (&value);
+}
+
+/**
+ * g_property_override_default:
+ * @property: a #GProperty
+ * @class_type: the type that is overriding the default value of @property
+ * @...: the default value for the property
+ *
+ * Overrides the default value of @property for the class of @class_type.
+ *
+ * This function should be called by sub-classes that desire overriding
+ * the default value of @property.
+ *
+ * This function is for the convenience of the C API users; language
+ * bindings should use the equivalent g_property_override_default_value()
+ * instead.
+ *
+ * Since: 2.38
+ */
+void
+g_property_override_default (GProperty *property,
+                             GType      class_type,
+                             ...)
+{
+  va_list var_args;
+  GValue *value;
+  char *error;
+
+  g_return_if_fail (G_IS_PROPERTY (property));
+  g_return_if_fail (class_type != G_TYPE_INVALID);
+
+  va_start (var_args, class_type);
+
+  value = g_new0 (GValue, 1);
+  G_VALUE_COLLECT_INIT (value, G_PARAM_SPEC (property)->value_type, var_args, 0, &error);
+  if (error != NULL)
+    {
+      g_critical (G_STRLOC ": %s", error);
+      g_free (error);
+      g_value_unset (value);
+      g_free (value);
+    }
+  else
+    {
+      /* takes ownership of the GValue */
+      g_property_override_default_value_internal (property, class_type, value);
+    }
+
+  va_end (var_args);
+}
+
 /**
  * g_property_set_va:
  * @property: a #GProperty
@@ -4171,6 +4570,20 @@ g_property_get_value (GProperty *property,
   g_value_unset (&copy);
 }
 
+void
+g_property_init_default (GProperty *property,
+                         gpointer   gobject)
+{
+  const GValue *default_value;
+
+  g_return_if_fail (G_IS_PROPERTY (property));
+  g_return_if_fail (G_IS_OBJECT (gobject));
+
+  default_value = g_property_get_default_value_internal (property, G_OBJECT_TYPE (gobject));
+  if (default_value != NULL)
+    g_property_set_value_internal (property, gobject, default_value);
+}
+
 /**
  * g_property_get_value_type:
  * @property: a #GProperty
@@ -4609,12 +5022,25 @@ property_values_cmp (GParamSpec   *pspec,
 }
 
 static void
+property_value_set_default (GParamSpec *pspec,
+                            GValue     *value)
+{
+  const GValue *v;
+
+  v = g_property_get_default_value_internal ((GProperty *) pspec, G_TYPE_INVALID);
+
+  if (v != NULL)
+    g_value_copy (v, value);
+}
+
+static void
 property_class_init (GParamSpecClass *klass)
 {
   klass->value_type = G_TYPE_INVALID;
 
   klass->value_validate = property_validate;
   klass->values_cmp = property_values_cmp;
+  klass->value_set_default = property_value_set_default;
 
   klass->finalize = property_finalize;
 }
diff --git a/gobject/gproperty.h b/gobject/gproperty.h
index a103279..deed674 100644
--- a/gobject/gproperty.h
+++ b/gobject/gproperty.h
@@ -118,7 +118,31 @@ void            g_property_set_range                    (GProperty    *property,
 GLIB_AVAILABLE_IN_2_38
 gboolean        g_property_get_range                    (GProperty    *property,
                                                          ...);
-
+GLIB_AVAILABLE_IN_2_38
+void            g_property_set_default_value            (GProperty    *property,
+                                                         const GValue *value);
+GLIB_AVAILABLE_IN_2_38
+void            g_property_get_default_value            (GProperty    *property,
+                                                         gpointer      gobject,
+                                                         GValue       *value);
+GLIB_AVAILABLE_IN_2_38
+void            g_property_override_default_value       (GProperty    *property,
+                                                         GType         gtype,
+                                                         const GValue *value);
+GLIB_AVAILABLE_IN_2_38
+void            g_property_set_default                  (GProperty    *property,
+                                                         ...);
+GLIB_AVAILABLE_IN_2_38
+void            g_property_get_default                  (GProperty    *property,
+                                                         gpointer      gobject,
+                                                         ...);
+GLIB_AVAILABLE_IN_2_38
+void            g_property_override_default             (GProperty    *property,
+                                                         GType         gtype,
+                                                         ...);
+GLIB_AVAILABLE_IN_2_38
+void            g_property_init_default                 (GProperty    *property,
+                                                         gpointer      gobject);
 GLIB_AVAILABLE_IN_2_38
 void            g_property_set_prerequisite             (GProperty    *property,
                                                          GType         gtype);
diff --git a/gobject/tests/property.c b/gobject/tests/property.c
index c987018..7fc2b47 100644
--- a/gobject/tests/property.c
+++ b/gobject/tests/property.c
@@ -33,6 +33,8 @@ struct _TestObjectPrivate
 
   TestEnum enum_val;
   guint enum_val_set : 1;
+
+  guint8 with_default;
 };
 
 GType test_enum_get_type (void);
@@ -71,6 +73,7 @@ enum
   PROP_STRING_VAL,
   PROP_BOOL_VAL,
   PROP_ENUM_VAL,
+  PROP_WITH_DEFAULT,
 
   LAST_PROP
 };
@@ -147,6 +150,14 @@ test_object_class_init (TestObjectClass *klass)
   g_property_set_prerequisite ((GProperty *) test_object_properties[PROP_ENUM_VAL],
                                test_enum_get_type ());
 
+  test_object_properties[PROP_WITH_DEFAULT] =
+    g_uint8_property_new ("with-default",
+                          G_PROPERTY_READWRITE,
+                          G_STRUCT_OFFSET (TestObjectPrivate, with_default),
+                          NULL,
+                          NULL);
+  g_property_set_default ((GProperty *) test_object_properties[PROP_WITH_DEFAULT], 255);
+
   g_object_class_install_properties (gobject_class, LAST_PROP, test_object_properties);
 }
 
@@ -156,6 +167,8 @@ test_object_init (TestObject *self)
   TestObjectPrivate *priv = test_object_get_private (self);
 
   priv->enum_val = TEST_ENUM_UNSET;
+
+  g_property_init_default ((GProperty *) test_object_properties[PROP_WITH_DEFAULT], self);
 }
 
 static void
@@ -263,6 +276,24 @@ gproperty_explicit_set (void)
   g_object_unref (obj);
 }
 
+static void
+gproperty_default_init (void)
+{
+  TestObject *obj = g_object_new (test_object_get_type (), NULL);
+  guint8 with_default = 0;
+
+  g_object_get (obj, "with-default", &with_default, NULL);
+  g_assert_cmpint (with_default, ==, 255);
+
+  g_object_unref (obj);
+
+  obj = g_object_new (test_object_get_type (), "with-default", 128, NULL);
+  g_object_get (obj, "with-default", &with_default, NULL);
+  g_assert_cmpint (with_default, ==, 128);
+
+  g_object_unref (obj);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -274,6 +305,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/gproperty/object-set", gproperty_object_set);
   g_test_add_func ("/gproperty/object-get", gproperty_object_get);
   g_test_add_func ("/gproperty/explicit-set", gproperty_explicit_set);
+  g_test_add_func ("/gproperty/default/init", gproperty_default_init);
 
   return g_test_run ();
 }


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