[glib/new-gsettings] GSettings seems to work now



commit 14173184a7ced3d31f4a3bb3d11efb28baf67ce5
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Apr 13 16:46:40 2010 -0400

    GSettings seems to work now

 gio/Makefile.am        |   13 +-
 gio/gio.h              |    1 +
 gio/gsettings.c        |  848 ++++++++++++++++++++++++++++++++++++++++++++++++
 gio/gsettings.h        |  114 +++++++
 gio/gsettingsbackend.c |   19 +-
 gio/gsettingsschema.c  |   33 ++-
 gio/gsettingsschema.h  |    6 +-
 gio/gvdb/gvdb-reader.c |   14 +-
 gio/tests/.gitignore   |    3 +-
 gio/tests/Makefile.am  |    5 +-
 gio/tests/gsettings.c  |   25 ++
 11 files changed, 1060 insertions(+), 21 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index b34d9d1..f736d87 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -83,8 +83,16 @@ settings_sources = \
 	gvdb/gvdb-format.h		\
 	gvdb/gvdb-reader.h		\
 	gvdb/gvdb-reader.c		\
+	gdelayedsettingsbackend.h	\
+	gdelayedsettingsbackend.c	\
+	gmemorysettingsbackend.h	\
+	gmemorysettingsbackend.c	\
+	gsettingsbackend.h		\
+	gsettingsbackend.c		\
 	gsettingsschema.h		\
-	gsettingsschema.c
+	gsettingsschema.c		\
+	gsettings.h			\
+	gsettings.c
 
 local_sources = \
 	glocaldirectorymonitor.c 	\
@@ -257,9 +265,6 @@ libgio_2_0_la_SOURCES =		\
 	gpollfilemonitor.h 	\
 	gresolver.c		\
 	gseekable.c 		\
-	gdelayedsettingsbackend.c\
-	gsettingsbackend.c	\
-	gmemorysettingsbackend.c\
 	gsimpleasyncresult.c 	\
 	gsocket.c		\
 	gsocketaddress.c	\
diff --git a/gio/gio.h b/gio/gio.h
index 861f172..e05a164 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -95,6 +95,7 @@
 #include <gio/gvolumemonitor.h>
 #include <gio/gzlibcompressor.h>
 #include <gio/gzlibdecompressor.h>
+#include <gio/gsettings.h>
 
 #undef __GIO_GIO_H_INSIDE__
 
diff --git a/gio/gsettings.c b/gio/gsettings.c
new file mode 100644
index 0000000..e63bf47
--- /dev/null
+++ b/gio/gsettings.c
@@ -0,0 +1,848 @@
+/*
+ * Copyright © 2009 Codethink Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#include "gsettings.h"
+
+#include "gdelayedsettingsbackend.h"
+#include "gio-marshal.h"
+#include "gsettingsschema.h"
+
+#include <string.h>
+
+#include "gioalias.h"
+
+struct _GSettingsPrivate {
+  GSettingsBackend *backend;
+  gchar *base_path;
+
+  GSettingsSchema *schema;
+  gchar *schema_name;
+
+  guint handler_id;
+  guint unapplied_handler;
+  gboolean locked;
+  gboolean delayed;
+};
+
+enum
+{
+  PROP_0,
+  PROP_SCHEMA_NAME,
+  PROP_SCHEMA,
+  PROP_DELAY_APPLY,
+  PROP_STORAGE,
+  PROP_BASE_PATH,
+  PROP_HAS_UNAPPLIED,
+};
+
+enum
+{
+  SIGNAL_CHANGES,
+  SIGNAL_CHANGED,
+  SIGNAL_DESTROYED,
+  N_SIGNALS
+};
+
+static guint g_settings_signals[N_SIGNALS];
+
+G_DEFINE_TYPE (GSettings, g_settings, G_TYPE_OBJECT)
+
+static void
+g_settings_storage_changed (GSettingsBackend    *backend,
+                            const gchar         *prefix,
+                            gchar const * const *names,
+                            gint                 n_names,
+                            gpointer             origin_tag,
+                            gpointer             user_data)
+{
+  GSettings *settings = G_SETTINGS (user_data);
+  gchar **changes;
+  GQuark *quarks;
+  gint i, c;
+
+  g_assert (settings->priv->backend == backend);
+
+  /* slow and stupid. */
+  changes = g_new (gchar *, n_names);
+  quarks = g_new (GQuark, n_names);
+  for (i = 0; i < n_names; i++)
+    changes[i] = g_strconcat (prefix, names[i], NULL);
+
+  /* check which are for us */
+  c = 0;
+  for (i = 0; i < n_names; i++)
+    if (g_str_has_prefix (changes[i], settings->priv->base_path))
+      {
+        const gchar *rel = changes[i] + strlen (settings->priv->base_path);
+
+        if (strchr (rel, '/') == NULL)
+          /* XXX also check schema to ensure it's really a key */
+          quarks[c++] = g_quark_from_string (rel);
+      }
+
+  g_settings_changes (settings, quarks, c);
+
+  for (i = 0; i < n_names; i++)
+    g_free (changes[i]);
+  g_free (changes);
+  g_free (quarks);
+}
+
+/**
+ * g_settings_changes:
+ * @settings: a #GSettings object
+ * @keys: an array of #GQuark key names
+ * @n_keys: the length of @keys
+ *
+ * Emits the "changes" signal on a #GSettings object.
+ *
+ * It is an error to call this function with a quark in @keys that is
+ * not a valid key for @settings (according to its schema).
+ **/
+void
+g_settings_changes (GSettings    *settings,
+                    const GQuark *keys,
+                    gint          n_keys)
+{
+  g_return_if_fail (settings != NULL);
+
+  if (n_keys == 0)
+    return;
+
+  g_return_if_fail (keys != NULL);
+  g_return_if_fail (n_keys > 0);
+
+  g_signal_emit (settings,
+                 g_settings_signals[SIGNAL_CHANGES], 0,
+                 keys, n_keys);
+}
+
+static void
+g_settings_real_changes (GSettings    *settings,
+                         const GQuark *keys,
+                         gint          n_keys)
+{
+  gint i;
+
+  for (i = 0; i < n_keys; i++)
+    g_signal_emit (settings,
+                   g_settings_signals[SIGNAL_CHANGED],
+                   keys[i], g_quark_to_string (keys[i]));
+}
+
+static void
+g_settings_init (GSettings *settings)
+{
+  settings->priv = G_TYPE_INSTANCE_GET_PRIVATE (settings,
+                                                G_TYPE_SETTINGS,
+                                                GSettingsPrivate);
+}
+
+static void
+g_settings_notify_unapplied (GSettings *settings)
+{
+  g_object_notify (G_OBJECT (settings), "has-unapplied");
+}
+
+void
+g_settings_set_delay_apply (GSettings *settings,
+                            gboolean   delayed)
+{
+  delayed = !!delayed;
+
+  if (delayed != settings->priv->delayed)
+    {
+      GSettingsBackend *backend;
+
+      if G_UNLIKELY (settings->priv->locked)
+        g_error ("Can only set delayed-apply attribute on "
+                 "freshly-created GSettings instances");
+
+      g_assert (delayed);
+
+      backend = g_delayed_settings_backend_new (settings->priv->backend,
+                                                settings->priv->base_path);
+      g_settings_backend_subscribe (backend, "");
+      g_settings_backend_unsubscribe (settings->priv->backend,
+                                      settings->priv->base_path);
+      g_signal_handler_disconnect (settings->priv->backend,
+                                   settings->priv->handler_id);
+      g_object_unref (settings->priv->backend);
+
+      settings->priv->backend = backend;
+      settings->priv->unapplied_handler =
+        g_signal_connect_swapped (backend, "notify::has-unapplied",
+                                  G_CALLBACK (g_settings_notify_unapplied),
+                                  settings);
+      settings->priv->handler_id =
+        g_signal_connect (settings->priv->backend, "changed",
+                          G_CALLBACK (g_settings_storage_changed),
+                          settings);
+
+      g_free (settings->priv->base_path);
+      settings->priv->base_path = g_strdup ("");
+
+      settings->priv->delayed = TRUE;
+      settings->priv->locked = TRUE;
+    }
+}
+
+gboolean
+g_settings_get_delay_apply (GSettings *settings)
+{
+  return settings->priv->delayed;
+}
+
+/**
+ * g_settings_apply:
+ * @settings: a #GSettings instance
+ *
+ * Applies any changes that have been made to the settings.  This
+ * function does nothing unless @settings is in 'delay-apply' mode;
+ * see g_settings_set_delay_apply().  In the normal case settings are
+ * always applied immediately.
+ **/
+void
+g_settings_apply (GSettings *settings)
+{
+  if (settings->priv->delayed)
+    {
+      GDelayedSettingsBackend *delayed;
+
+      delayed = G_DELAYED_SETTINGS_BACKEND (settings->priv->backend);
+      g_delayed_settings_backend_apply (delayed);
+    }
+}
+
+/**
+ * g_settings_revert:
+ * @settings: a #GSettings instance
+ *
+ * Reverts all non-applied changes to the settings.  This function
+ * does nothing unless @settings is in 'delay-apply' mode; see
+ * g_settings_set_delay_apply().  In the normal case settings are
+ * always applied immediately.
+ *
+ * Change notifications will be emitted for affected keys.
+ **/
+void
+g_settings_revert (GSettings *settings)
+{
+  if (settings->priv->delayed)
+    {
+      GDelayedSettingsBackend *delayed;
+
+      delayed = G_DELAYED_SETTINGS_BACKEND (settings->priv->backend);
+      g_delayed_settings_backend_revert (delayed);
+    }
+}
+
+static void
+g_settings_set_property (GObject *object, guint prop_id,
+                         const GValue *value, GParamSpec *pspec)
+{
+  GSettings *settings = G_SETTINGS (object);
+
+  switch (prop_id)
+    {
+     case PROP_SCHEMA_NAME:
+      g_assert (settings->priv->schema_name == NULL);
+      settings->priv->schema_name = g_value_dup_string (value);
+      break;
+
+     case PROP_SCHEMA:
+      g_assert (settings->priv->schema == NULL);
+      settings->priv->schema = g_value_dup_object (value);
+      break;
+
+     case PROP_DELAY_APPLY:
+      g_settings_set_delay_apply (settings, g_value_get_boolean (value));
+      break;
+
+     case PROP_BASE_PATH:
+      settings->priv->base_path = g_value_dup_string (value);
+      break;
+
+     case PROP_STORAGE:
+      settings->priv->backend = g_value_dup_object (value);
+      break;
+
+     default:
+      g_assert_not_reached ();
+    }
+}
+
+gboolean
+g_settings_get_has_unapplied (GSettings *settings)
+{
+  return settings->priv->delayed &&
+         g_delayed_settings_backend_get_has_unapplied (
+           G_DELAYED_SETTINGS_BACKEND (settings->priv->backend));
+}
+
+static void
+g_settings_get_property (GObject *object, guint prop_id,
+                         GValue *value, GParamSpec *pspec)
+{
+  GSettings *settings = G_SETTINGS (object);
+
+  switch (prop_id)
+    {
+     case PROP_SCHEMA:
+      g_value_set_object (value, settings->priv->schema);
+      break;
+
+     case PROP_HAS_UNAPPLIED:
+      g_value_set_boolean (value, g_settings_get_has_unapplied (settings));
+      break;
+
+     default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+g_settings_finalize (GObject *object)
+{
+  GSettings *settings = G_SETTINGS (object);
+
+  g_signal_handler_disconnect (settings->priv->backend, settings->priv->handler_id);
+  g_object_unref (settings->priv->backend);
+  g_object_unref (settings->priv->schema);
+  g_free (settings->priv->schema_name);
+  g_free (settings->priv->base_path);
+}
+
+static void
+g_settings_constructed (GObject *object)
+{
+  GSettings *settings = G_SETTINGS (object);
+
+  if (settings->priv->backend == NULL)
+    settings->priv->backend = g_settings_backend_get_with_context (NULL);
+
+  settings->priv->handler_id =
+    g_signal_connect (settings->priv->backend, "changed",
+                      G_CALLBACK (g_settings_storage_changed),
+                      settings);
+
+  if (settings->priv->schema != NULL && settings->priv->schema_name != NULL)
+    g_error ("Schema and schema name specified");
+
+  if (settings->priv->schema == NULL && settings->priv->schema_name != NULL)
+    settings->priv->schema = g_settings_schema_new (settings->priv->schema_name);
+
+  if (settings->priv->schema == NULL)
+    settings->priv->schema = g_settings_schema_new ("empty");
+
+  if (settings->priv->base_path == NULL && settings->priv->schema == NULL)
+    g_error ("Attempting to make settings with no schema or path");
+
+  if (settings->priv->base_path != NULL && settings->priv->schema != NULL)
+    {
+      const gchar *schema_path, *given_path;
+
+      schema_path = g_settings_schema_get_path (settings->priv->schema);
+      given_path = settings->priv->base_path;
+
+      if (schema_path && strcmp (schema_path, given_path) != 0)
+        g_error ("Specified path of '%s' but schema says '%s'",
+                 given_path, schema_path);
+     }
+
+  if (settings->priv->base_path == NULL)
+    settings->priv->base_path = g_strdup (
+      g_settings_schema_get_path (settings->priv->schema));
+
+  if (settings->priv->base_path == NULL)
+    g_error ("No base path given and none from schema");
+
+  g_settings_backend_subscribe (settings->priv->backend,
+                                settings->priv->base_path);
+}
+
+GSettings *
+g_settings_new (const gchar *schema)
+{
+  return g_object_new (G_TYPE_SETTINGS, "schema-name", schema, NULL);
+}
+
+GSettings *
+g_settings_new_from_path (const gchar *path)
+{
+  return g_object_new (G_TYPE_SETTINGS, "base-path", path, NULL);
+}
+
+#if 0
+static GType
+g_settings_get_gtype_for_schema (GSettingsSchema *schema)
+{
+  GVariantIter iter;
+  const gchar *item;
+  GVariant *list;
+
+  list = g_settings_schema_get_inheritance (schema);
+  g_variant_iter_init (&iter, list);
+  g_variant_unref (list);
+
+  while (g_variant_iter_next (&iter, "s", &item))
+    if (strcmp (item, "list") == 0)
+      return G_TYPE_SETTINGS_LIST;
+
+  return G_TYPE_SETTINGS;
+}
+
+GSettings *
+g_settings_get_settings (GSettings   *settings,
+                         const gchar *name)
+{
+  const gchar *schema_name;
+  GSettingsSchema *schema;
+  GSettings *child;
+  gchar *path;
+
+  schema_name = g_settings_schema_get_schema (settings->priv->schema, name);
+
+  if (schema_name == NULL)
+    {
+      schema_name = g_settings_schema_get_schema (settings->priv->schema,
+                                                  "/default");
+
+      if G_UNLIKELY (schema_name == NULL)
+        g_error ("Schema has no child schema named '%s' and no "
+                 "default child schema", name);
+    }
+
+  schema = g_settings_schema_new (schema_name);
+  path = g_strdup_printf ("%s%s/", settings->priv->base_path, name);
+
+  child = g_object_new (g_settings_get_gtype_for_schema (schema),
+                        "schema", schema,
+                        "backend", settings->priv->backend,
+                        "base-path", path,
+                        NULL);
+
+  g_free (path);
+
+  return child;
+}
+#endif
+
+static void
+g_settings_class_init (GSettingsClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  class->changes = g_settings_real_changes;
+
+  object_class->set_property = g_settings_set_property;
+  object_class->get_property = g_settings_get_property;
+  object_class->constructed = g_settings_constructed;
+  object_class->finalize = g_settings_finalize;
+
+  g_type_class_add_private (object_class, sizeof (GSettingsPrivate));
+
+  g_settings_signals[SIGNAL_CHANGES] =
+    g_signal_new ("changes", G_TYPE_SETTINGS,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GSettingsClass, changes),
+                  NULL, NULL,
+                  _gio_marshal_VOID__POINTER_INT,
+                  G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_INT);
+
+  g_settings_signals[SIGNAL_CHANGED] =
+    g_signal_new ("changed", G_TYPE_SETTINGS,
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                  G_STRUCT_OFFSET (GSettingsClass, changed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__STRING,
+                  G_TYPE_NONE, 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+  g_settings_signals[SIGNAL_DESTROYED] =
+    g_signal_new ("destroyed", G_TYPE_SETTINGS,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GSettingsClass, destroyed),
+                  NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+  g_object_class_install_property (object_class, PROP_STORAGE,
+    g_param_spec_object ("backend", "backend storage",
+                         "The GSettingsBackend object for this GSettings",
+                         G_TYPE_SETTINGS_BACKEND, G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_READWRITE | G_PARAM_STATIC_NICK |
+                         G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (object_class, PROP_SCHEMA_NAME,
+    g_param_spec_string ("schema-name", "schema name",
+                         "The name of the schema for this settings object",
+                         NULL, G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_READWRITE | G_PARAM_STATIC_NICK |
+                         G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
+
+   g_object_class_install_property (object_class, PROP_DELAY_APPLY,
+    g_param_spec_boolean ("delay-apply", "delayed apply",
+                          "If TRUE, you must call apply() to write changes",
+                          FALSE, G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_READWRITE | G_PARAM_STATIC_NICK |
+                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
+
+   g_object_class_install_property (object_class, PROP_HAS_UNAPPLIED,
+    g_param_spec_boolean ("has-unapplied", "has unapplied changes",
+                          "TRUE if there are outstanding changes to apply()",
+                          FALSE, G_PARAM_READABLE | G_PARAM_STATIC_NICK |
+                          G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (object_class, PROP_SCHEMA,
+    g_param_spec_object ("schema", "schema",
+                         "The GSettingsSchema object for this GSettings",
+                         G_TYPE_OBJECT, G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_READWRITE | G_PARAM_STATIC_NICK |
+                         G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (object_class, PROP_BASE_PATH,
+    g_param_spec_string ("base-path", "base path",
+                         "the path within the backend where the settings are",
+                         NULL, G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_READWRITE | G_PARAM_STATIC_NICK |
+                         G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB));
+}
+
+GVariant *
+g_settings_get_value (GSettings   *settings,
+                      const gchar *key)
+{
+  const GVariantType *type;
+  const gchar *format;
+  GVariant *value;
+  GVariant *sval;
+  gchar *path;
+
+  path = g_strconcat (settings->priv->base_path, key, NULL);
+  sval = g_settings_schema_get_value (settings->priv->schema, key, NULL);
+  type = g_variant_get_type (sval);
+  value = g_settings_backend_read (settings->priv->backend, path, type);
+  g_free (path);
+
+  if (value && !g_variant_is_of_type (value, type))
+    {
+      g_variant_unref (value);
+      value = NULL;
+    }
+
+  if (value == NULL)
+    value = g_variant_ref (sval);
+
+  g_variant_unref (sval);
+
+  return value;
+}
+
+/**
+ * g_settings_set_value:
+ * @settings: a #GSettings object
+ * @key: the name of the key to set
+ * @value: a #GVariant of the correct type
+ *
+ * Sets @key in @settings to @value.
+ *
+ * It is a programmer error to give a @key that isn't valid for
+ * @settings.  It is a programmer error to give a @value of the
+ * incorrect type.
+ *
+ * If @value is floating then this function consumes the reference.
+ **/
+void
+g_settings_set_value (GSettings   *settings,
+                      const gchar *key,
+                      GVariant    *value)
+{
+  GTree *tree;
+  gchar *path;
+
+  tree = g_settings_backend_create_tree ();
+  path = g_strconcat (settings->priv->base_path, key, NULL);
+  g_tree_insert (tree, g_strdup (""), g_variant_ref_sink (value));
+  g_settings_backend_write (settings->priv->backend, path, tree, NULL);
+  g_free (path);
+}
+
+void
+g_settings_get (GSettings   *settings,
+                const gchar *key,
+                const gchar *format,
+                ...)
+{
+  GVariant *value;
+  va_list ap;
+
+  value = g_settings_get_value (settings, key);
+
+  va_start (ap, format);
+  g_variant_get_va (value, format, NULL, &ap);
+  va_end (ap);
+
+  g_variant_unref (value);
+}
+
+void
+g_settings_set (GSettings   *settings,
+                const gchar *key,
+                const gchar *format,
+                ...)
+{
+  GVariant *value;
+  va_list ap;
+
+  va_start (ap, format);
+  value = g_variant_new_va (format, NULL, &ap);
+  va_end (ap);
+
+  g_settings_set_value (settings, key, value);
+}
+
+gboolean
+g_settings_is_writable (GSettings   *settings,
+                        const gchar *name)
+{
+  gboolean writable;
+  gchar *path;
+
+  path = g_strconcat (settings->priv->base_path, name, NULL);
+  writable = g_settings_backend_get_writable (settings->priv->backend, path);
+  g_free (path);
+
+  return writable;
+}
+
+void
+g_settings_destroy (GSettings *settings)
+{
+  g_signal_emit (settings, g_settings_signals[SIGNAL_DESTROYED], 0);
+}
+
+#if 0
+typedef struct
+{
+  GSettings *settings;
+  GObject *object;
+
+  guint property_handler_id;
+  const GParamSpec *property;
+  guint key_handler_id;
+  const GVariantType *type;
+  const gchar *key;
+
+  /* prevent recursion */
+  gboolean running;
+} GSettingsBinding;
+
+static void
+g_settings_binding_free (gpointer data)
+{
+  GSettingsBinding *binding = data;
+
+  g_assert (!binding->running);
+
+  if (binding->key_handler_id)
+    g_signal_handler_disconnect (binding->settings,
+                                 binding->key_handler_id);
+
+  if (binding->property_handler_id)
+  g_signal_handler_disconnect (binding->object,
+                               binding->property_handler_id);
+
+  g_object_unref (binding->settings);
+
+  g_slice_free (GSettingsBinding, binding);
+}
+
+static GQuark
+g_settings_binding_quark (const char *property)
+{
+  GQuark quark;
+  gchar *tmp;
+
+  tmp = g_strdup_printf ("gsettingsbinding-%s", property);
+  quark = g_quark_from_string (tmp);
+  g_free (tmp);
+
+  return quark;
+}
+
+static void
+g_settings_binding_key_changed (GSettings   *settings,
+                                const gchar *key,
+                                gpointer     user_data)
+{
+  GSettingsBinding *binding = user_data;
+  GValue value = {  };
+  GVariant *variant;
+
+  g_assert (settings == binding->settings);
+  g_assert (key == binding->key);
+
+  if (binding->running)
+    return;
+
+  binding->running = TRUE;
+
+  g_value_init (&value, binding->property->value_type);
+  variant = g_settings_get_value (settings, key);
+  if (g_value_deserialise (&value, variant))
+    g_object_set_property (binding->object,
+                           binding->property->name,
+                           &value);
+  g_value_unset (&value);
+
+  binding->running = FALSE;
+}
+
+static void
+g_settings_binding_property_changed (GObject          *object,
+                                     const GParamSpec *pspec,
+                                     gpointer          user_data)
+{
+  GSettingsBinding *binding = user_data;
+  GValue value = {  };
+  GVariant *variant;
+
+  g_assert (object == binding->object);
+  g_assert (pspec == binding->property);
+
+  if (binding->running)
+    return;
+
+  binding->running = TRUE;
+
+  g_value_init (&value, pspec->value_type);
+  g_object_get_property (object, pspec->name, &value);
+  if ((variant = g_value_serialise (&value, binding->type)))
+    {
+      g_settings_set_value (binding->settings,
+                            binding->key,
+                            variant);
+      g_variant_unref (variant);
+    }
+  g_value_unset (&value);
+
+  binding->running = FALSE;
+}
+
+void
+g_settings_bind (GSettings          *settings,
+                 const gchar        *key,
+                 gpointer            object,
+                 const gchar        *property,
+                 GSettingsBindFlags  flags)
+{
+  GSettingsBinding *binding;
+  GObjectClass *objectclass;
+  gchar *detailed_signal;
+  GQuark binding_quark;
+  gboolean insensitive;
+
+  objectclass = G_OBJECT_GET_CLASS (object);
+
+  binding = g_slice_new (GSettingsBinding);
+  binding->settings = g_object_ref (settings);
+  binding->object = object;
+  binding->key = g_intern_string (key);
+  binding->property = g_object_class_find_property (objectclass, property);
+  binding->running = FALSE;
+
+  if (!(flags & (G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET)))
+    flags |= G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET;
+
+  if (binding->property == NULL)
+    {
+      g_critical ("g_settings_bind: no property '%s' on class '%s'",
+                  property, G_OBJECT_TYPE_NAME (object));
+      return;
+    }
+
+  binding->type = g_settings_schema_get_key_type (settings->priv->schema,
+                                                  key);
+
+  if (binding->type == NULL)
+    {
+      g_critical ("g_settings_bind: no key '%s' on schema '%s'",
+                  key, settings->priv->schema_name);
+      return;
+    }
+
+  if (!g_type_serialiser_check (G_PARAM_SPEC_VALUE_TYPE (binding->property),
+                                binding->type))
+    {
+      g_critical ("g_settings_bind: property '%s' on class '%s' has type"
+                  "'%s' which is not compatible with type '%s' of key '%s'"
+                  "on schema '%s'", property, G_OBJECT_TYPE_NAME (object),
+                  g_type_name (binding->property->value_type),
+                  g_variant_type_dup_string (binding->type), key,
+                  settings->priv->schema_name);
+      return;
+    }
+
+  if (~flags & G_SETTINGS_BIND_NO_SENSITIVITY)
+    {
+      GParamSpec *sensitive;
+
+      sensitive = g_object_class_find_property (objectclass, "sensitive");
+      if (sensitive && sensitive->value_type == G_TYPE_BOOLEAN)
+        {
+          insensitive = !g_settings_is_writable (settings, key);
+          g_object_set (object, "sensitive", !insensitive, NULL);
+        }
+      else
+        insensitive = FALSE;
+    }
+  else
+    insensitive = FALSE;
+
+  if (!insensitive && (flags & G_SETTINGS_BIND_SET))
+    {
+      detailed_signal = g_strdup_printf ("notify::%s", property);
+      binding->property_handler_id =
+        g_signal_connect (object, detailed_signal,
+                          G_CALLBACK (g_settings_binding_property_changed),
+                          binding);
+      g_free (detailed_signal);
+
+      if (~flags & G_SETTINGS_BIND_GET)
+        g_settings_binding_property_changed (object,
+                                             binding->property,
+                                             binding);
+    }
+
+  if (flags & G_SETTINGS_BIND_GET)
+    {
+      detailed_signal = g_strdup_printf ("changed::%s", key);
+      binding->key_handler_id =
+        g_signal_connect (settings, detailed_signal,
+                          G_CALLBACK (g_settings_binding_key_changed),
+                          binding);
+      g_free (detailed_signal);
+
+      g_settings_binding_key_changed (settings, binding->key, binding);
+    }
+
+  binding_quark = g_settings_binding_quark (property);
+  g_object_set_qdata_full (object, binding_quark,
+                           binding, g_settings_binding_free);
+}
+
+void
+g_settings_unbind (gpointer     object,
+                   const gchar *property)
+{
+  GQuark binding_quark;
+
+  binding_quark = g_settings_binding_quark (property);
+  g_object_set_qdata (object, binding_quark, NULL);
+}
+#endif
diff --git a/gio/gsettings.h b/gio/gsettings.h
new file mode 100644
index 0000000..9ace2e4
--- /dev/null
+++ b/gio/gsettings.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2009 Codethink Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 3 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * See the included COPYING file for more information.
+ */
+
+#ifndef _gsettings_h_
+#define _gsettings_h_
+
+#include "gsettingsbackend.h"
+
+#define G_TYPE_SETTINGS                                     (g_settings_get_type ())
+#define G_SETTINGS(inst)                                    (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_SETTINGS, GSettings))
+#define G_SETTINGS_CLASS(class)                             (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_SETTINGS, GSettingsClass))
+#define G_IS_SETTINGS(inst)                                 (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_SETTINGS))
+#define G_IS_SETTINGS_CLASS(class)                          (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_SETTINGS))
+#define G_SETTINGS_GET_CLASS(inst)                          (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_SETTINGS, GSettingsClass))
+
+typedef struct _GSettingsPrivate                            GSettingsPrivate;
+typedef struct _GSettingsClass                              GSettingsClass;
+typedef struct _GSettings                                   GSettings;
+
+struct _GSettingsClass
+{
+  GObjectClass parent_class;
+
+
+  GSettings * (*get_settings) (GSettings    *settings,
+                               const gchar  *name);
+
+
+  void        (*changes)      (GSettings    *settings,
+                               const GQuark *keys,
+                               gint          n_keys);
+  void        (*changed)      (GSettings    *settings,
+                               const gchar  *key);
+  void        (*destroyed)    (GSettings    *settings);
+};
+
+struct _GSettings
+{
+  GObject parent_instance;
+  GSettingsPrivate *priv;
+};
+
+typedef enum
+{
+  G_SETTINGS_BIND_DEFAULT,
+  G_SETTINGS_BIND_GET            = (1<<0),
+  G_SETTINGS_BIND_SET            = (1<<1),
+  G_SETTINGS_BIND_NO_SENSITIVITY = (1<<2)
+} GSettingsBindFlags;
+
+G_BEGIN_DECLS
+
+GType                   g_settings_get_type                             (void);
+void                    g_settings_revert                               (GSettings          *settings);
+void                    g_settings_apply                                (GSettings          *settings);
+
+gboolean                g_settings_get_delay_apply                      (GSettings          *settings);
+gboolean                g_settings_get_has_unapplied                    (GSettings          *settings);
+void                    g_settings_set_delay_apply                      (GSettings          *settings,
+                                                                         gboolean            delay_apply);
+gboolean                g_settings_get_locked                           (GSettings          *settings);
+void                    g_settings_lock                                 (GSettings          *settings);
+
+GSettings *             g_settings_new                                  (const gchar        *schema);
+GSettings *             g_settings_new_from_path                        (const gchar        *path);
+
+void                    g_settings_set_value                            (GSettings          *settings,
+                                                                         const gchar        *key,
+                                                                         GVariant           *value);
+
+GVariant *              g_settings_get_value                            (GSettings          *settings,
+                                                                         const gchar        *key);
+
+void                    g_settings_set                                  (GSettings          *settings,
+                                                                         const gchar        *key,
+                                                                         const gchar        *format,
+                                                                         ...);
+
+void                    g_settings_get                                  (GSettings          *settings,
+                                                                         const gchar        *key,
+                                                                         const gchar        *format_string,
+                                                                         ...);
+
+GSettings *             g_settings_get_settings                         (GSettings          *settings,
+                                                                         const gchar        *name);
+
+gboolean                g_settings_is_writable                          (GSettings          *settings,
+                                                                         const gchar        *name);
+void                    g_settings_changes                              (GSettings          *settings,
+                                                                         const GQuark       *keys,
+                                                                         gint                n_keys);
+void                    g_settings_destroy                              (GSettings          *settings);
+
+void                    g_settings_bind                                 (GSettings          *settings,
+                                                                         const gchar        *key,
+                                                                         gpointer            object,
+                                                                         const gchar        *property,
+                                                                         GSettingsBindFlags  flags);
+void                    g_settings_unbind                               (gpointer            object,
+                                                                         const gchar        *key);
+
+G_END_DECLS
+
+#endif /* _gsettings_h_ */
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
index f10d3d4..9b07a3e 100644
--- a/gio/gsettingsbackend.c
+++ b/gio/gsettingsbackend.c
@@ -9,7 +9,7 @@
  */
 
 #include "gsettingsbackend.h"
-
+#include "gmemorysettingsbackend.h"
 #include "giomodule-priv.h"
 #include "gio-marshal.h"
 
@@ -29,6 +29,7 @@ G_DEFINE_ABSTRACT_TYPE (GSettingsBackend, g_settings_backend, G_TYPE_OBJECT)
 static guint changed_signal;
 
 enum {
+  PROP_ZERO,
   PROP_CONTEXT
 };
 
@@ -322,10 +323,19 @@ g_settings_backend_finalize (GObject *object)
 }
 
 static void
+ignore_subscription (GSettingsBackend *backend,
+                     const gchar      *key)
+{
+}
+
+static void
 g_settings_backend_class_init (GSettingsBackendClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
 
+  class->subscribe = ignore_subscription;
+  class->unsubscribe = ignore_subscription;
+
   gobject_class->get_property = g_settings_backend_get_property;
   gobject_class->set_property = g_settings_backend_set_property;
   gobject_class->finalize = g_settings_backend_finalize;
@@ -485,6 +495,13 @@ g_settings_backend_get_with_context (const gchar *context)
   static GHashTable *backends;
   GSettingsBackend *backend;
 
+  _g_io_modules_ensure_extension_points_registered ();
+  G_TYPE_MEMORY_SETTINGS_BACKEND;
+
+  /* FIXME: hash null properly? */
+  if (!context)
+    context = "";
+
   if (!backends)
     backends = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
index f7cb9ec..f14edbe 100644
--- a/gio/gsettingsschema.c
+++ b/gio/gsettingsschema.c
@@ -94,7 +94,7 @@ g_settings_schema_class_init (GSettingsSchemaClass *class)
 GSettingsSchema *
 g_settings_schema_new (const gchar *name)
 {
-  GSettingsSchema *schema = NULL;
+  GSettingsSchema *schema;
   GvdbTable *table = NULL;
   GSList *source;
 
@@ -108,12 +108,12 @@ g_settings_schema_new (const gchar *name)
         break;
     }
 
-  if (table != NULL)
-    {
-      schema = g_object_new (G_TYPE_SETTINGS_SCHEMA, NULL);
-      schema->priv->name = g_strdup (name);
-      schema->priv->table = table;
-    }
+  if (table == NULL)
+    g_error ("Settings schema '%s' is not installed\n", name);
+
+  schema = g_object_new (G_TYPE_SETTINGS_SCHEMA, NULL);
+  schema->priv->name = g_strdup (name);
+  schema->priv->table = table;
   
   return schema;
 }
@@ -125,3 +125,22 @@ g_settings_schema_get_value (GSettingsSchema  *schema,
 {
   return gvdb_table_get_value (schema->priv->table, key, options);
 }
+
+const gchar *
+g_settings_schema_get_path (GSettingsSchema *schema)
+{
+  const gchar *result;
+  GVariant *value;
+
+  value = gvdb_table_get_value (schema->priv->table, ".path", NULL);
+
+  if (value && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+    {
+      result = g_variant_get_string (value, NULL);
+      g_variant_unref (value);
+    }
+  else
+    result = NULL;
+
+  return result;
+}
diff --git a/gio/gsettingsschema.h b/gio/gsettingsschema.h
index 3acaca1..8cc0881 100644
--- a/gio/gsettingsschema.h
+++ b/gio/gsettingsschema.h
@@ -53,7 +53,11 @@ struct _GSettingsSchema
   GSettingsSchemaPrivate *priv;
 };
 
-GSettingsSchema *       g_settings_schema_new                           (const gchar *name);
+GSettingsSchema *       g_settings_schema_new                           (const gchar      *name);
+const gchar *           g_settings_schema_get_path                      (GSettingsSchema  *schema);
+GVariant *              g_settings_schema_get_value                     (GSettingsSchema  *schema,
+                                                                         const gchar      *key,
+                                                                         GVariant        **options);
 
 G_END_DECLS
 
diff --git a/gio/gvdb/gvdb-reader.c b/gio/gvdb/gvdb-reader.c
index 0cdbc4d..5f80d1f 100644
--- a/gio/gvdb/gvdb-reader.c
+++ b/gio/gvdb/gvdb-reader.c
@@ -224,6 +224,7 @@ gvdb_table_lookup (GvdbTable   *file,
 {
   guint32 hash_value = 5381;
   guint key_length;
+  guint32 lastno;
   guint32 itemno;
 
   if G_UNLIKELY (file->n_buckets == 0 || file->n_hash_items == 0)
@@ -236,20 +237,21 @@ gvdb_table_lookup (GvdbTable   *file,
     return NULL;
 
   itemno = file->hash_buckets[hash_value % file->n_buckets];
-  hash_value &= ~(1u << 31);
 
-  while G_LIKELY (itemno < file->n_hash_items)
+  if (hash_value % file->n_buckets != file->n_buckets - 1)
+    lastno = file->hash_buckets[hash_value % file->n_buckets + 1];
+  else
+    lastno = file->n_hash_items;
+
+  while G_LIKELY (itemno < lastno)
     {
       struct gvdb_hash_item *item = &file->hash_items[itemno];
 
-      if (hash_value == (guint32_from_le (item->hash_value) & ~(1u << 31)))
+      if (hash_value == guint32_from_le (item->hash_value))
         if G_LIKELY (gvdb_table_check_name (file, item, key, key_length))
           if G_LIKELY (item->type == type)
             return item;
 
-      if (guint32_from_le (item->hash_value) & (1u << 31))
-        return NULL;
-
       itemno++;
     }
 
diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore
index c75c8cd..d7fc756 100644
--- a/gio/tests/.gitignore
+++ b/gio/tests/.gitignore
@@ -11,6 +11,7 @@ filter-streams
 g-file
 g-file-info
 g-icon
+gsettings
 httpd
 live-g-file
 memory-input-stream
@@ -25,4 +26,4 @@ socket-server
 srvtarget
 unix-fd
 unix-streams
-utf8-input-stream
\ No newline at end of file
+utf8-input-stream
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 8f9f483..61773ae 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -32,7 +32,8 @@ TEST_PROGS +=	 		\
 	filter-streams		\
 	simple-async-result	\
 	srvtarget		\
-	contexts
+	contexts		\
+	gsettings
 
 SAMPLE_PROGS = 			\
 	resolver		\
@@ -133,4 +134,6 @@ contexts_SOURCES	  = contexts.c
 contexts_LDADD		  = $(progs_ldadd) \
 	$(top_builddir)/gthread/libgthread-2.0.la
 
+gsettings_LDADD = $(progs_ldadd)
+
 DISTCLEAN_FILES = applications/mimeinfo.cache
diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
new file mode 100644
index 0000000..f3bcb27
--- /dev/null
+++ b/gio/tests/gsettings.c
@@ -0,0 +1,25 @@
+#include <gio.h>
+
+int
+main (void)
+{
+  GSettings *settings;
+  gchar *str = NULL;
+
+  g_type_init ();
+
+  settings = g_settings_new ("org.gtk.test");
+
+  g_settings_get (settings, "greeting", "s", &str);
+  g_print ("it was '%s'\n", str);
+
+  g_settings_set (settings, "greeting", "s", "goodbye world");
+
+  g_settings_get (settings, "greeting", "s", &str);
+  g_print ("it is now '%s'\n", str);
+
+  g_settings_set (settings, "greeting", "i", 555);
+
+  g_settings_get (settings, "greeting", "s", &str);
+  g_print ("finally, it is '%s'\n", str);
+}



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