[glib/new-gsettings] some GSettings stuff



commit e06ca1fae6b2e77ac9d7641842e30d77cfb4dcc0
Author: Ryan Lortie <desrt desrt ca>
Date:   Sat Apr 10 21:57:55 2010 -0400

    some GSettings stuff

 gio/Makefile.am               |    5 +
 gio/gdelayedsettingsbackend.c |  329 ++++++++++++++++++++++++++++++++++++++++
 gio/gdelayedsettingsbackend.h |   59 ++++++++
 gio/gio-marshal.list          |    2 +
 gio/gmemorysettingsbackend.c  |   98 ++++++++++++
 gio/gmemorysettingsbackend.h  |   51 +++++++
 gio/gsettingsbackend.c        |  332 +++++++++++++++++++++++++++++++++++++++++
 gio/gsettingsbackend.h        |  108 +++++++++++++
 8 files changed, 984 insertions(+), 0 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index fd42438..80368cf 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -250,6 +250,11 @@ libgio_2_0_la_SOURCES =		\
 	gpollfilemonitor.h 	\
 	gresolver.c		\
 	gseekable.c 		\
+	gsettings.c		\
+	gsettingsschema.c	\
+	gdelayedsettingsbackend.c\
+	gsettingsbackend.c	\
+	gmemorysettingsbackend.c\
 	gsimpleasyncresult.c 	\
 	gsocket.c		\
 	gsocketaddress.c	\
diff --git a/gio/gdelayedsettingsbackend.c b/gio/gdelayedsettingsbackend.c
new file mode 100644
index 0000000..6128268
--- /dev/null
+++ b/gio/gdelayedsettingsbackend.c
@@ -0,0 +1,329 @@
+/*
+ * 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 "gdelayedsettingsbackend.h"
+
+#include <string.h>
+
+#include "gioalias.h"
+
+enum
+{
+  PROP_NONE,
+  PROP_BACKEND,
+  PROP_BASE_PATH,
+  PROP_HAS_UNAPPLIED
+};
+
+struct _GDelayedSettingsBackendPrivate {
+  GSettingsBackend *backend;
+  guint handler_id;
+  gchar *base_path;
+  GTree *delayed;
+};
+
+G_DEFINE_TYPE (GDelayedSettingsBackend,
+               g_delayed_settings_backend,
+               G_TYPE_SETTINGS_BACKEND)
+
+static gboolean
+g_delayed_settings_backend_add_to_tree (gpointer key,
+                                        gpointer value,
+                                        gpointer user_data)
+{
+  gpointer *args = user_data;
+
+  g_tree_insert (args[0],
+                 g_strjoin (NULL, args[1], key, NULL),
+                 g_variant_ref (value));
+
+  return FALSE;
+}
+
+static void
+g_delayed_settings_backend_write (GSettingsBackend *backend,
+                                  const gchar      *prefix,
+                                  GTree            *tree,
+                                  gpointer          origin_tag)
+{
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
+  gconstpointer args[2] = { delayed->priv->delayed, prefix };
+  gboolean was_empty;
+
+  was_empty = g_tree_nnodes (delayed->priv->delayed) == 0;
+
+  g_tree_foreach (tree, g_delayed_settings_backend_add_to_tree, args);
+
+  g_settings_backend_changed_tree (backend, prefix, tree, origin_tag);
+
+  if (was_empty)
+    g_object_notify (G_OBJECT (delayed), "has-unapplied");
+}
+
+static GVariant *
+g_delayed_settings_backend_read (GSettingsBackend   *backend,
+                                 const gchar        *key,
+                                 const GVariantType *expected_type)
+{
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
+  GVariant *result = NULL;
+  gchar *path;
+
+  if ((result = g_tree_lookup (delayed->priv->delayed, key))) 
+    return g_variant_ref (result);
+
+  path = g_strconcat (delayed->priv->base_path, key, NULL);
+  result = g_settings_backend_read (delayed->priv->backend,
+                                    path, expected_type);
+  g_free (path);
+
+  return result;
+}
+
+gboolean
+g_delayed_settings_backend_get_has_unapplied (GDelayedSettingsBackend *delayed)
+{
+  return g_tree_nnodes (delayed->priv->delayed) > 0;
+}
+
+void
+g_delayed_settings_backend_apply (GDelayedSettingsBackend *delayed)
+{
+  if (g_tree_nnodes (delayed->priv->delayed))
+    {
+      GTree *tmp;
+
+      tmp = delayed->priv->delayed;
+      delayed->priv->delayed = g_settings_backend_create_tree ();
+
+      g_settings_backend_write (delayed->priv->backend,
+                                delayed->priv->base_path,
+                                tmp, delayed->priv);
+      g_tree_unref (tmp);
+
+      g_object_notify (G_OBJECT (delayed), "has-unapplied");
+    }
+}
+
+void
+g_delayed_settings_backend_revert (GDelayedSettingsBackend *delayed)
+{
+  if (g_tree_nnodes (delayed->priv->delayed))
+    {
+      GTree *tmp;
+
+      tmp = delayed->priv->delayed;
+      delayed->priv->delayed = g_settings_backend_create_tree ();
+      g_settings_backend_changed_tree (G_SETTINGS_BACKEND (delayed),
+                                       "", tmp, NULL);
+      g_tree_destroy (tmp);
+
+      g_object_notify (G_OBJECT (delayed), "has-unapplied");
+    }
+}
+
+static gboolean
+g_delayed_settings_backend_get_writable (GSettingsBackend *backend,
+                                         const gchar      *name)
+{
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (backend);
+  gboolean sensitive;
+  gchar *path;
+
+  path = g_strconcat (delayed->priv->base_path, name, NULL);
+  sensitive = g_settings_backend_get_writable (delayed->priv->backend, path);
+  g_free (path);
+
+  return sensitive;
+}
+
+static void
+g_delayed_settings_backend_subscribe (GSettingsBackend *base,
+                                      const char       *name)
+{
+}
+
+static void
+g_delayed_settings_backend_get_property (GObject *object, guint prop_id,
+                                         GValue *value, GParamSpec *pspec)
+{
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object);
+
+  switch (prop_id)
+    {
+      gboolean has;
+
+    case PROP_HAS_UNAPPLIED:
+      has = g_delayed_settings_backend_get_has_unapplied (delayed);
+      g_value_set_boolean (value, has);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+g_delayed_settings_backend_set_property (GObject *object, guint prop_id,
+                                         const GValue *value, GParamSpec *pspec)
+{
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object);
+
+  switch (prop_id)
+    {
+    case PROP_BACKEND:
+      g_assert (delayed->priv->backend == NULL);
+      delayed->priv->backend = g_value_dup_object (value);
+      break;
+
+    case PROP_BASE_PATH:
+      g_assert (delayed->priv->base_path == NULL);
+      delayed->priv->base_path = g_value_dup_string (value);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+g_delayed_settings_backend_backend_changed (GSettingsBackend    *backend,
+                                            const gchar         *prefix,
+                                            const gchar * const *items,
+                                            gint                 n_items,
+                                            gpointer             origin_tag,
+                                            gpointer             user_data)
+{
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data);
+
+  if (origin_tag == delayed->priv)
+    return;
+
+  if (g_str_has_prefix (prefix, delayed->priv->base_path))
+    {
+      g_settings_backend_changed (G_SETTINGS_BACKEND (delayed),
+                                  prefix + strlen (delayed->priv->base_path),
+                                  items, n_items, origin_tag);
+    }
+
+  else if (g_str_has_prefix (delayed->priv->base_path, prefix))
+    {
+      const gchar **my_items;
+      const gchar *relative;
+      gint relative_length;
+      gint i, j;
+
+      relative = delayed->priv->base_path + strlen (prefix);
+      relative_length = strlen (relative);
+
+      my_items = g_new (const gchar *, n_items + 1);
+
+      for (i = j = 0; i < n_items; i++)
+        if (g_str_has_prefix (items[i], relative))
+          my_items[j++] = items[i] + relative_length;
+      my_items[j] = NULL;
+
+      if (j > 0)
+        g_settings_backend_changed (G_SETTINGS_BACKEND (delayed),
+                                    "", my_items, j, origin_tag);
+      g_free (my_items);
+    }
+
+  else
+    /* do nothing */;
+}
+
+static void
+g_delayed_settings_backend_constructed (GObject *object)
+{
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object);
+
+  g_assert (delayed->priv->backend != NULL);
+  g_assert (delayed->priv->base_path != NULL);
+
+  delayed->priv->handler_id = 
+    g_signal_connect (delayed->priv->backend, "changed",
+                      G_CALLBACK (g_delayed_settings_backend_backend_changed),
+                      delayed);
+
+  g_settings_backend_subscribe (delayed->priv->backend,
+                                delayed->priv->base_path);
+}
+
+static void
+g_delayed_settings_backend_finalize (GObject *object)
+{
+  GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object);
+
+  g_signal_handler_disconnect (delayed->priv->backend,
+                               delayed->priv->handler_id);
+  g_settings_backend_unsubscribe (delayed->priv->backend,
+                                  delayed->priv->base_path);
+  g_object_unref (delayed->priv->backend);
+  g_free (delayed->priv->base_path);
+}
+
+static void
+g_delayed_settings_backend_class_init (GDelayedSettingsBackendClass *class)
+{
+  GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  g_type_class_add_private (class, sizeof (GDelayedSettingsBackendPrivate));
+
+  backend_class->write = g_delayed_settings_backend_write;
+  backend_class->read = g_delayed_settings_backend_read;
+  backend_class->get_writable = g_delayed_settings_backend_get_writable;
+  backend_class->subscribe = g_delayed_settings_backend_subscribe;
+  backend_class->unsubscribe = g_delayed_settings_backend_subscribe;
+
+  object_class->get_property = g_delayed_settings_backend_get_property;
+  object_class->set_property = g_delayed_settings_backend_set_property;
+  object_class->constructed = g_delayed_settings_backend_constructed;
+  object_class->finalize = g_delayed_settings_backend_finalize;
+
+  g_object_class_install_property (object_class, PROP_BACKEND,
+    g_param_spec_object ("backend", "backend backend", "backend",
+                         G_TYPE_SETTINGS_BACKEND, G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class, PROP_BASE_PATH,
+    g_param_spec_string ("base-path", "base path", "base",
+                         "", G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class, PROP_HAS_UNAPPLIED,
+    g_param_spec_boolean ("has-unapplied", "has unapplied", "unapplied",
+                          FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+}
+
+static void
+g_delayed_settings_backend_init (GDelayedSettingsBackend *delayed)
+{
+  delayed->priv =
+    G_TYPE_INSTANCE_GET_PRIVATE (delayed, G_TYPE_DELAYED_SETTINGS_BACKEND,
+                                 GDelayedSettingsBackendPrivate);
+
+  delayed->priv->delayed = g_settings_backend_create_tree ();
+}
+
+GSettingsBackend *
+g_delayed_settings_backend_new (GSettingsBackend *backend,
+                       const gchar      *base_path)
+{
+  return g_object_new (G_TYPE_DELAYED_SETTINGS_BACKEND,
+                       "backend", backend,
+                       "base-path", base_path,
+                       NULL);
+}
+
+#define _gsettingsdelayedbackend_c_
+#include "gioaliasdef.c"
diff --git a/gio/gdelayedsettingsbackend.h b/gio/gdelayedsettingsbackend.h
new file mode 100644
index 0000000..e7dedf1
--- /dev/null
+++ b/gio/gdelayedsettingsbackend.h
@@ -0,0 +1,59 @@
+/*
+ * 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 _gsettingsdelayedbackend_h_
+#define _gsettingsdelayedbackend_h_
+
+#include <glib-object.h>
+
+#include <gio/gsettingsbackend.h>
+
+#define G_TYPE_DELAYED_SETTINGS_BACKEND                     (g_delayed_settings_backend_get_type ())
+#define G_DELAYED_SETTINGS_BACKEND(inst)                    (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_DELAYED_SETTINGS_BACKEND,                        \
+                                                             GDelayedSettingsBackend))
+#define G_DELAYED_SETTINGS_BACKEND_CLASS(class)             (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_DELAYED_SETTINGS_BACKEND,                        \
+                                                             GDelayedSettingsBackendClass))
+#define G_IS_DELAYED_SETTINGS_BACKEND(inst)                 (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_DELAYED_SETTINGS_BACKEND))
+#define G_IS_DELAYED_SETTINGS_BACKEND_CLASS(class)          (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_DELAYED_SETTINGS_BACKEND))
+#define G_DELAYED_SETTINGS_BACKEND_GET_CLASS(inst)          (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_DELAYED_SETTINGS_BACKEND,                        \
+                                                             GDelayedSettingsBackendClass))
+
+typedef struct _GDelayedSettingsBackendPrivate              GDelayedSettingsBackendPrivate;
+typedef struct _GDelayedSettingsBackendClass                GDelayedSettingsBackendClass;
+typedef struct _GDelayedSettingsBackend                     GDelayedSettingsBackend;
+
+struct _GDelayedSettingsBackendClass
+{
+  GSettingsBackendClass parent_class;
+};
+
+struct _GDelayedSettingsBackend
+{
+  GSettingsBackend parent_instance;
+  GDelayedSettingsBackendPrivate *priv;
+};
+
+G_BEGIN_DECLS
+
+GType                           g_delayed_settings_backend_get_type     (void);
+GSettingsBackend *              g_delayed_settings_backend_new          (GSettingsBackend        *backend,
+                                                                         const gchar             *base_path);
+void                            g_delayed_settings_backend_revert       (GDelayedSettingsBackend *delayed);
+void                            g_delayed_settings_backend_apply        (GDelayedSettingsBackend *delayed);
+gboolean                        g_delayed_settings_backend_get_has_unapplied (GDelayedSettingsBackend *delayed);
+
+G_END_DECLS
+
+#endif /* _gsettingsdelayedbackend_h_ */
diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list
index 269ec35..49c2916 100644
--- a/gio/gio-marshal.list
+++ b/gio/gio-marshal.list
@@ -4,3 +4,5 @@ VOID:BOOLEAN,POINTER
 VOID:OBJECT,OBJECT,ENUM
 BOOLEAN:OBJECT,OBJECT
 VOID:STRING,BOXED,BOXED
+VOID:STRING,BOXED,INT,POINTER
+VOID:POINTER,INT
diff --git a/gio/gmemorysettingsbackend.c b/gio/gmemorysettingsbackend.c
new file mode 100644
index 0000000..4548414
--- /dev/null
+++ b/gio/gmemorysettingsbackend.c
@@ -0,0 +1,98 @@
+#include "gmemorysettingsbackend.h"
+
+G_DEFINE_TYPE_WITH_CODE (GMemorySettingsBackend,
+                         g_memory_settings_backend,
+                         G_TYPE_SETTINGS_BACKEND,
+                         g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
+                                                         g_define_type_id, "memory", 0))
+
+struct _GMemorySettingsBackendPrivate
+{
+  GHashTable *table;
+};
+
+static GVariant *
+g_memory_settings_backend_read (GSettingsBackend   *backend,
+                                const gchar        *key,
+                                const GVariantType *expected_type)
+{
+  GVariant *value;
+
+  GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+
+  value = g_hash_table_lookup (memory->priv->table, key);
+
+  if (value != NULL)
+    g_variant_ref (value);
+
+  return value;
+}
+
+static gboolean
+g_memory_settings_backend_write_one (gpointer key,
+                                     gpointer value,
+                                     gpointer data)
+{
+  GMemorySettingsBackend *memory = ((gpointer *) data)[0];
+  const gchar *prefix = ((gpointer *) data)[1];
+
+  g_hash_table_insert (memory->priv->table,
+                       g_strjoin ("", prefix, key, NULL),
+                       g_variant_ref (value));
+
+  return FALSE;
+}
+
+static void
+g_memory_settings_backend_write (GSettingsBackend *backend,
+                                 const gchar      *prefix,
+                                 GTree            *tree,
+                                 gpointer          origin_tag)
+{
+  gpointer write_info[] = { backend, (gpointer) prefix };
+  g_tree_foreach (tree, g_memory_settings_backend_write_one, write_info);
+  g_settings_backend_changed_tree (backend, prefix, tree, origin_tag);
+}
+
+static gboolean
+g_memory_settings_backend_get_writable (GSettingsBackend *backend,
+                                        const gchar      *name)
+{
+  return TRUE;
+}
+
+static void
+g_memory_settings_backend_finalize (GObject *object)
+{
+  GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (object);
+
+  g_hash_table_unref (memory->priv->table);
+
+  G_OBJECT_CLASS (g_memory_settings_backend_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_memory_settings_backend_init (GMemorySettingsBackend *memory)
+{
+  memory->priv = G_TYPE_INSTANCE_GET_PRIVATE (memory,
+                                              G_TYPE_MEMORY_SETTINGS_BACKEND,
+                                              GMemorySettingsBackendPrivate);
+  memory->priv->table =
+    g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+                           (GDestroyNotify) g_variant_unref);
+}
+
+static void
+g_memory_settings_backend_class_init (GMemorySettingsBackendClass *class)
+{
+  GSettingsBackendClass *backend_class = G_SETTINGS_BACKEND_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  backend_class->read = g_memory_settings_backend_read;
+  backend_class->write = g_memory_settings_backend_write;
+  backend_class->get_writable = g_memory_settings_backend_get_writable;
+  object_class->finalize = g_memory_settings_backend_finalize;
+
+  g_type_class_add_private (class, sizeof (GMemorySettingsBackendPrivate));
+}
diff --git a/gio/gmemorysettingsbackend.h b/gio/gmemorysettingsbackend.h
new file mode 100644
index 0000000..a765175
--- /dev/null
+++ b/gio/gmemorysettingsbackend.h
@@ -0,0 +1,51 @@
+#ifndef _gmemorysettingsbackend_h_
+#define _gmemorysettingsbackend_h_
+
+#include <gio/gsettingsbackend.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_MEMORY_SETTINGS_BACKEND                      (g_memory_settings_backend_get_type ())
+#define G_MEMORY_SETTINGS_BACKEND(inst)                     (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_MEMORY_SETTINGS_BACKEND,                         \
+                                                             GMemorySettingsBackend))
+#define G_MEMORY_SETTINGS_BACKEND_CLASS(class)              (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_MEMORY_SETTINGS_BACKEND,                         \
+                                                             GMemorySettingsBackendClass))
+#define G_IS_MEMORY_SETTINGS_BACKEND(inst)                  (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_MEMORY_SETTINGS_BACKEND))
+#define G_IS_MEMORY_SETTINGS_BACKEND_CLASS(class)           (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_MEMORY_SETTINGS_BACKEND))
+#define G_MEMORY_SETTINGS_BACKEND_GET_CLASS(inst)           (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_MEMORY_SETTINGS_BACKEND,                         \
+                                                             GMemorySettingsBackendClass))
+
+#define G_MEMORY_SETTINGS_BACKEND_EXTENSION_POINT_NAME "gsettings-backend"
+
+/**
+ * GMemorySettingsBackend:
+ *
+ * A backend to GSettings that stores the settings in memory.
+ **/
+typedef struct _GMemorySettingsBackendPrivate               GMemorySettingsBackendPrivate;
+typedef struct _GMemorySettingsBackendClass                 GMemorySettingsBackendClass;
+typedef struct _GMemorySettingsBackend                      GMemorySettingsBackend;
+
+struct _GMemorySettingsBackendClass
+{
+  GSettingsBackendClass parent_class;
+};
+
+struct _GMemorySettingsBackend
+{
+  GSettingsBackend parent_instance;
+
+  /*< private >*/
+  GMemorySettingsBackendPrivate *priv;
+};
+
+GType                           g_memory_settings_backend_get_type      (void);
+
+G_END_DECLS
+
+#endif /* _gmemorysettingsbackend_h_ */
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
new file mode 100644
index 0000000..b0da255
--- /dev/null
+++ b/gio/gsettingsbackend.c
@@ -0,0 +1,332 @@
+/*
+ * 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 "gsettingsbackend.h"
+
+#include "giomodule-priv.h"
+#include "gio-marshal.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include "gioalias.h"
+
+G_DEFINE_ABSTRACT_TYPE (GSettingsBackend, g_settings_backend, G_TYPE_OBJECT)
+
+static guint changed_signal;
+
+/**
+ * SECTION:gsettingsbackend
+ * @short_description: an interface for settings backend implementations
+ *
+ * The #GSettingsBackend inteface defines a generic interface for
+ * non-strictly-typed data backend in a hierarchy.
+ *
+ * The interface defines methods for reading and writing values, a
+ * method for determining if writing of certain values will fail
+ * (lockdown) and a change notification signal.
+ *
+ * The semantics of the interface are very precisely defined and
+ * implementations must carefully adhere to the expectations of
+ * callers that are documented on each of the interface methods.
+ **/
+
+/**
+ * g_settings_backend_changed:
+ * @backend: a #GSettingsBackend implementation
+ * @prefix: the longest common prefix
+ * @items: the NULL-terminated list of changed keys
+ * @n_items: the number of items in @items
+ * @origin_tag: the origin tag
+ *
+ * Emits the changed signal on @backend.  This function should only be
+ * called by the implementation itself, to indicate that a change has
+ * occurred.
+ *
+ * The list of changed keys, relative to the root of the settings store,
+ * is @prefix prepended to each of the @items.  It must either be the
+ * case that @prefix is equal to "" or ends in "/" or that @items
+ * contains exactly one item: "".  @prefix need not be the largest
+ * possible prefix.
+ *
+ * The implementation must call this function during any call to
+ * g_settings_backend_write(), before the call returns (except in the
+ * case that no keys are actually changed).  It may not rely on the
+ * existence of a mainloop for dispatching the signal later.
+ *
+ * The implementation may call this function at any other time it likes
+ * in response to other events (such as changes occuring outside of the
+ * program).  These calls may originate from a mainloop or may originate
+ * in response to any other action (including from calls to
+ * g_settings_backend_write()).
+ *
+ * In the case that this call is in response to a call to
+ * g_settings_backend_write() then @origin_tag must be set to the same
+ * value that was passed to that call.
+ **/
+void
+g_settings_backend_changed (GSettingsBackend    *backend,
+                            const gchar         *prefix,
+                            gchar const * const *items,
+                            gint                 n_items,
+                            gpointer             origin_tag)
+{
+  if (n_items == -1)
+    for (n_items = 0; items[n_items]; n_items++);
+
+  g_assert (items[n_items] == NULL);
+
+  g_signal_emit (backend, changed_signal, 0,
+                 prefix, items, n_items, origin_tag);
+}
+
+static gboolean
+g_settings_backend_append_to_list (gpointer key,
+                                   gpointer value,
+                                   gpointer user_data)
+{
+  return (*((*((gchar ***) user_data))++) = key, FALSE);
+}
+
+/**
+ * g_settings_backend_changed_tree:
+ * @backend: a #GSettingsBackend implementation
+ * @prefix: the longest common prefix
+ * @tree: a #GTree
+ * @origin_tag: the origin tag
+ *
+ * This call is a convenience wrapper around
+ * g_settings_backend_changed().  It gets the list of changes from
+ * @tree.
+ **/
+void
+g_settings_backend_changed_tree (GSettingsBackend *backend,
+                                 const gchar      *prefix,
+                                 GTree            *tree,
+                                 gpointer          origin_tag)
+{
+  gchar **list;
+
+  list = g_new (gchar *, g_tree_nnodes (tree) + 1);
+
+  {
+    gchar **ptr = list;
+    g_tree_foreach (tree, g_settings_backend_append_to_list, &ptr);
+    *ptr = NULL;
+
+    g_assert (list + g_tree_nnodes (tree) == ptr);
+  }
+
+  g_signal_emit (backend, changed_signal, 0,
+                 prefix, list, g_tree_nnodes (tree), origin_tag);
+  g_free (list);
+}
+
+/**
+ * g_settings_backend_read:
+ * @backend: a #GSettingsBackend implementation
+ * @key: the key to read
+ * @expected_type: a #GVariantType hint
+ * @returns: the values that was read, or %NULL
+ *
+ * Reads a keys.  This call will never block.
+ *
+ * If the key exists, it will be returned.  If the key does not exist,
+ * %NULL will be returned.
+ *
+ * If @expected_type is given, it serves as a type hint to the backend.
+ * If you expect a key of a certain type then you should give
+ * @expected_type to increase your chances of getting it.  Some backends
+ * may ignore this argument and return values of a different type; it is
+ * mostly used by backends that don't store strong type information.
+ **/
+GVariant *
+g_settings_backend_read (GSettingsBackend   *backend,
+                         const gchar        *key,
+                         const GVariantType *expected_type)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->read (backend, key, expected_type);
+}
+
+/**
+ * g_settings_backend_write:
+ * @backend: a #GSettingsBackend implementation
+ * @prefix: the longest common prefix
+ * @values: a #GTree containing values to write
+ * @origin_tag: the origin tag
+ *
+ * Writes one or more keys.  This call will never block.
+ *
+ * For each item in @values, a key is written.  The key to be written is
+ * @prefix prepended to the key used in the tree.  The value stored in
+ * the tree is expected to be a #GVariant instance.  It must either be
+ * the case that @prefix is equal to "" or ends in "/" or that @values
+ * contains exactly one item, with a key of "".  @prefix need not be the
+ * largest possible prefix.
+ *
+ * This call does not fail.  During this call a "changed" signal will be
+ * emitted if any keys have been changed.  The new values of all updated
+ * keys will be visible to any signal callbacks.
+ *
+ * One possible method that an implementation might deal with failures
+ * is to emit a second "backend-changed" signal (either during this
+ * call, or later) to indicate that the affected keys have suddenly
+ * "changed back" to their old values.
+ **/
+void
+g_settings_backend_write (GSettingsBackend *backend,
+                          const gchar      *prefix,
+                          GTree            *values,
+                          gpointer          origin_tag)
+{
+  G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->write (backend, prefix, values, origin_tag);
+}
+
+/**
+ * g_settings_backend_get_writable:
+ * @backend: a #GSettingsBackend implementation
+ * @name: the name of a key, relative to the root of the backend
+ * @returns: %TRUE if the key is writable
+ *
+ * Ensures that a key is available for writing to.  This is the
+ * interface through which 'lockdown' is implemented.  Locked down
+ * keys will have %FALSE returned by this call.
+ *
+ * You should not write to locked-down keys, but if you do, the
+ * implementation will deal with it.
+ **/
+gboolean
+g_settings_backend_get_writable (GSettingsBackend *backend,
+                                 const gchar      *name)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->get_writable (backend, name);
+}
+
+/**
+ * g_settings_backend_subscribe:
+ * @backend: a #GSettingsBackend
+ * @name: a key or path to subscribe to
+ *
+ * Reverses the effect of a previous call to
+ * g_settings_backend_subscribe().
+ **/
+void
+g_settings_backend_unsubscribe (GSettingsBackend *backend,
+                                const char       *name)
+{
+  G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->unsubscribe (backend, name);
+}
+
+/**
+ * g_settings_backend_subscribe:
+ * @backend: a #GSettingsBackend
+ * @name: a key or path to subscribe to
+ *
+ * Requests that change signals be emitted for events on @name.
+ **/
+void
+g_settings_backend_subscribe (GSettingsBackend *backend,
+                              const gchar      *name)
+{
+  G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->subscribe (backend, name);
+}
+
+static void
+g_settings_backend_class_init (GSettingsBackendClass *class)
+{
+  changed_signal =
+    g_signal_new ("changed", G_TYPE_SETTINGS_BACKEND,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GSettingsBackendClass, changed),
+                  NULL, NULL,
+                  _gio_marshal_VOID__STRING_BOXED_INT_POINTER, G_TYPE_NONE,
+                  4, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
+                  G_TYPE_STRV | G_SIGNAL_TYPE_STATIC_SCOPE,
+                  G_TYPE_INT, G_TYPE_POINTER);
+}
+
+/**
+ * g_settings_backend_create_tree:
+ * @returns: a new #GTree
+ *
+ * This is a convenience function for creating a tree that is compatible
+ * with g_settings_backend_write().  It merely calls g_tree_new_full()
+ * with strcmp() g_free() and g_variant_unref().
+ **/
+GTree *
+g_settings_backend_create_tree (void)
+{
+  return g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
+                          g_free, (GDestroyNotify) g_variant_unref);
+}
+
+static gpointer
+get_default_backend (gpointer user_data)
+{
+  GIOExtension *extension = NULL;
+  GIOExtensionPoint *point;
+  GList *extensions;
+  const gchar *env;
+  GType type;
+
+  _g_io_modules_ensure_loaded ();
+
+  point = g_io_extension_point_lookup (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME);
+
+  if ((env = getenv ("GSETTINGS_BACKEND")))
+    {
+      extension = g_io_extension_point_get_extension_by_name (point, env);
+
+      if (extension == NULL)
+        g_warning ("Can't find GSettings backend '%s' given in "
+                   "GSETTINGS_BACKEND environment variable", env);
+    }
+
+  if (extension == NULL)
+    {
+      extensions = g_io_extension_point_get_extensions (point);
+
+      if (extensions == NULL)
+        g_error ("No GSettingsBackend implementations exist.");
+
+      extension = extensions->data;
+    }
+
+  type = g_io_extension_get_type (extension);
+
+  return g_object_new (type, NULL);
+}
+
+/**
+ * g_settings_backend_get_default:
+ * @returns: the default #GSettingsBackend
+ *
+ * Returns the default #GSettingsBackend.
+ *
+ * The user does not own the return value and it must not be freed.
+ **/
+GSettingsBackend *
+g_settings_backend_get_default (void)
+{
+  static GOnce once = G_ONCE_INIT;
+
+  return g_once (&once, get_default_backend, NULL);
+}
+
+static void
+g_settings_backend_init (GSettingsBackend *backend)
+{
+}
diff --git a/gio/gsettingsbackend.h b/gio/gsettingsbackend.h
new file mode 100644
index 0000000..6c2bd10
--- /dev/null
+++ b/gio/gsettingsbackend.h
@@ -0,0 +1,108 @@
+/*
+ * 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 _gsettingsbackend_h_
+#define _gsettingsbackend_h_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SETTINGS_BACKEND                             (g_settings_backend_get_type ())
+#define G_SETTINGS_BACKEND(inst)                            (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_SETTINGS_BACKEND, GSettingsBackend))
+#define G_SETTINGS_BACKEND_CLASS(class)                     (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_SETTINGS_BACKEND, GSettingsBackendClass))
+#define G_IS_SETTINGS_BACKEND(inst)                         (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_SETTINGS_BACKEND))
+#define G_IS_SETTINGS_BACKEND_CLASS(class)                  (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_SETTINGS_BACKEND))
+#define G_SETTINGS_BACKEND_GET_CLASS(inst)                  (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_SETTINGS_BACKEND, GSettingsBackendClass))
+
+#define G_SETTINGS_BACKEND_EXTENSION_POINT_NAME "gsettings-backend"
+
+/**
+ * GSettingsBackend:
+ *
+ * An implementation of a settings storage repository.
+ **/
+typedef struct _GSettingsBackendPrivate                     GSettingsBackendPrivate;
+typedef struct _GSettingsBackendClass                       GSettingsBackendClass;
+typedef struct _GSettingsBackend                            GSettingsBackend;
+
+struct _GSettingsBackendClass
+{
+  GObjectClass parent_class;
+
+  void        (*changed)      (GSettingsBackend    *backend,
+                               const gchar         *prefix,
+                               gchar const * const *names,
+                               gint                 names_len,
+                               gpointer             origin_tag);
+
+  GVariant *  (*read)         (GSettingsBackend    *backend,
+                               const gchar         *key,
+                               const GVariantType  *expected_type);
+  void        (*write)        (GSettingsBackend    *backend,
+                               const gchar         *prefix,
+                               GTree               *tree,
+                               gpointer             origin_tag);
+  gboolean    (*get_writable) (GSettingsBackend    *backend,
+                               const gchar         *name);
+  void        (*subscribe)    (GSettingsBackend    *backend,
+                               const gchar         *name);
+  void        (*unsubscribe)  (GSettingsBackend    *backend,
+                               const gchar         *name);
+};
+
+struct _GSettingsBackend
+{
+  GObject parent_instance;
+
+  /*< private >*/
+  GSettingsBackendPrivate *priv;
+};
+
+GType                           g_settings_backend_get_type             (void);
+GSettingsBackend *              g_settings_backend_get_default          (void);
+void                            g_settings_backend_set_default          (GSettingsBackend    *backend);
+GTree *                         g_settings_backend_create_tree          (void);
+
+GVariant *                      g_settings_backend_read                 (GSettingsBackend    *backend,
+                                                                         const gchar         *key,
+                                                                         const GVariantType  *expected_type);
+
+void                            g_settings_backend_write                (GSettingsBackend    *backend,
+                                                                         const gchar         *prefix,
+                                                                         GTree               *values,
+                                                                         gpointer             origin_tag);
+
+gboolean                        g_settings_backend_get_writable         (GSettingsBackend    *backend,
+                                                                         const char          *name);
+
+void                            g_settings_backend_unsubscribe          (GSettingsBackend    *backend,
+                                                                         const char          *name);
+void                            g_settings_backend_subscribe            (GSettingsBackend    *backend,
+                                                                         const char          *name);
+
+void                            g_settings_backend_changed              (GSettingsBackend    *backend,
+                                                                         const gchar         *prefix,
+                                                                         gchar const * const *items,
+                                                                         gint                 n_items,
+                                                                         gpointer             origin_tag);
+void                            g_settings_backend_changed_tree         (GSettingsBackend    *backend,
+                                                                         const gchar         *prefix,
+                                                                         GTree               *tree,
+                                                                         gpointer             origin_tag);
+
+G_END_DECLS
+
+#endif /* _gsettingsbackend_h_ */



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