[glib/wip/gsettings-list: 2/2] list stuff



commit b12b0a26c2172e79fbe6ce16a19b97e11c1b1f06
Author: Ryan Lortie <desrt desrt ca>
Date:   Mon Sep 13 13:19:09 2010 -0400

    list stuff

 gio/gio.symbols                |    5 +
 gio/gmemorysettingsbackend.c   |  117 +++++++++++++++-
 gio/gsettings.c                |  309 ++++++++++++++++++++++++++++------------
 gio/gsettings.h                |   27 +++-
 gio/gsettingsbackend.c         |   45 ++++++
 gio/gsettingsbackend.h         |   19 ++-
 gio/gsettingsbackendinternal.h |   34 +++++
 7 files changed, 453 insertions(+), 103 deletions(-)
---
diff --git a/gio/gio.symbols b/gio/gio.symbols
index fa5d623..ad700e9 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1558,6 +1558,11 @@ g_settings_sync
 g_settings_list_keys
 g_settings_list_children
 g_settings_get_mapped
+g_settings_add_child
+g_settings_can_add_child
+g_settings_can_remove_child
+g_settings_remove_child
+g_settings_get_destroyed
 #endif
 #endif
 
diff --git a/gio/gmemorysettingsbackend.c b/gio/gmemorysettingsbackend.c
index 6b60058..50c2f41 100644
--- a/gio/gmemorysettingsbackend.c
+++ b/gio/gmemorysettingsbackend.c
@@ -32,11 +32,42 @@
                                          G_TYPE_MEMORY_SETTINGS_BACKEND,     \
                                          GMemorySettingsBackend))
 
+typedef struct
+{
+  GHashTable/*<string, GVariant>*/                     *values;
+  GHashTable/*<string, GHashTable<string, TablePair>*/ *lists;
+} TablePair;
+
+static void
+table_pair_free (gpointer data)
+{
+  TablePair *pair = data;
+
+  g_hash_table_unref (pair->values);
+  g_hash_table_unref (pair->list);
+}
+
+static TablePair *
+table_pair_new (void)
+{
+  TablePair *pair;
+
+  pair = g_slice_new (TablePair);
+  pair->values = g_hash_table_new (g_str_hash, g_str_equal, g_free,
+                                   (GDestroyNotify) g_variant_unref);
+  pair->lists = g_hash_table_new (g_str_hash, g_str_equal, g_free,
+                                  (GDestroyNotify) g_hash_table_unref);
+
+  return pair;
+}
+
+
+
 typedef GSettingsBackendClass GMemorySettingsBackendClass;
 typedef struct
 {
   GSettingsBackend parent_instance;
-  GHashTable *table;
+  TablePair *root;
 } GMemorySettingsBackend;
 
 G_DEFINE_TYPE_WITH_CODE (GMemorySettingsBackend,
@@ -45,6 +76,90 @@ G_DEFINE_TYPE_WITH_CODE (GMemorySettingsBackend,
                          g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
                                                          g_define_type_id, "memory", 10))
 
+static GHashTable *
+g_memory_settings_backend_get_table (GHashTable   *table,
+                                     const gchar **key_ptr)
+{
+  const gchar *key = *key_ptr;
+  gchar *prefix;
+  gchar *name;
+  gint i, j;
+
+  for (i = 2; key[i - 2] != ':' || key[i - 1] != '/'; i++)
+    if (key[i - 2] == '\0')
+      /* no :/ in the string -- pass through */
+      return table;
+
+  for (j = i + 1; key[j - 1] != '/'; j++)
+    if (key[j - 1] == '\0')
+      /* string of form /some/path:/child -- definitely invalid */
+      return NULL;
+
+  /* We now have this situation:
+   *
+   *   /some/path:/child/item
+   *   ^0          ^i    ^j
+   *
+   * So we can split the string into 3 parts:
+   *
+   *   prefix   = /some/path:/
+   *   child    = child/
+   *   *key_ptr = item
+   */
+  prefix = g_strndup (key, i);
+  name = g_strndup (key + i, j - i);
+
+  /* name is either like 'foo' or ':foo'
+   *
+   * If it doesn't start with a colon, it's in the schema.
+   */
+  if (name[0] == ':')
+    {
+      table = g_hash_table_lookup (table, prefix);
+      if (table)
+        table = g_hash_table_lookup (table, name);
+    }
+  else
+    {
+      gpointer value;
+
+      if (!g_hash_table_lookup_extended (table, prefix, NULL, &value))
+        g_hash_table_insert (table, prefix,
+                             value = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                            g_free, g_hash_table_unref));
+
+      table = value;
+
+      if (!g_hash_table_lookup_extended (table, prefix, NULL, &value))
+        g_hash_table_insert (table, prefix,
+                             value = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                            g_free, g_hash_table_unref));
+    }
+
+    if (key[i] == ':' && key[i + 1] == '/')
+      {
+        gchar *prefix;
+        gchar *name;
+        gint j;
+
+        i += 2;
+
+        for (j = i; (*key)[j] != '/'; j++)
+          /* found a path of the form /a:/b
+           * which is never a valid spot for a key.
+           */
+          if ((*key)[j] == '\0')
+            return NULL;
+
+        name = g_strndup (*key + i, j - i);
+        prefix = g_strndup (*key, i);
+        table = g_hash_table_lookup (table, prefix);
+        g_free (prefix);
+      }
+
+  return table;
+}
+
 static GVariant *
 g_memory_settings_backend_read (GSettingsBackend   *backend,
                                 const gchar        *key,
diff --git a/gio/gsettings.c b/gio/gsettings.c
index eb4370c..365ddd1 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -195,6 +195,7 @@ struct _GSettingsPrivate
   gchar *path;
 
   GDelayedSettingsBackend *delayed;
+  gboolean destroyed;
 };
 
 enum
@@ -465,6 +466,10 @@ g_settings_constructed (GObject *object)
                             settings->priv->main_context);
   g_settings_backend_subscribe (settings->priv->backend,
                                 settings->priv->path);
+
+  settings->priv->destroyed =
+    !g_settings_backend_check (settings->priv->backend,
+                               settings->priv->path);
 }
 
 static void
@@ -1975,7 +1980,7 @@ g_settings_get_has_unapplied (GSettings *settings)
            G_DELAYED_SETTINGS_BACKEND (settings->priv->backend));
 }
 
-/* Extra API (reset, sync, get_child, is_writable, list_*) {{{1 */
+/* Extra API (reset, sync, get_child, is_writable) {{{1 */
 /**
  * g_settings_reset:
  * @settings: a #GSettings object
@@ -2089,95 +2094,6 @@ g_settings_get_child (GSettings   *settings,
   return child;
 }
 
-/**
- * g_settings_list_keys:
- * @settings: a #GSettings object
- * @returns: a list of the keys on @settings
- *
- * Introspects the list of keys on @settings.
- *
- * You should probably not be calling this function from "normal" code
- * (since you should already know what keys are in your schema).  This
- * function is intended for introspection reasons.
- *
- * You should free the return value with g_strfreev() when you are done
- * with it.
- */
-gchar **
-g_settings_list_keys (GSettings *settings)
-{
-  const GQuark *keys;
-  gchar **strv;
-  gint n_keys;
-  gint i, j;
-
-  keys = g_settings_schema_list (settings->priv->schema, &n_keys);
-  strv = g_new (gchar *, n_keys + 1);
-  for (i = j = 0; i < n_keys; i++)
-    {
-      const gchar *key = g_quark_to_string (keys[i]);
-
-      if (!g_str_has_suffix (key, "/"))
-        strv[j++] = g_strdup (key);
-    }
-  strv[j] = NULL;
-
-  return strv;
-}
-
-/**
- * g_settings_list_children:
- * @settings: a #GSettings object
- * @returns: a list of the children on @settings
- *
- * Gets the list of children on @settings.
- *
- * The list is exactly the list of strings for which it is not an error
- * to call g_settings_get_child().
- *
- * For GSettings objects that are lists, this value can change at any
- * time and you should connect to the "children-changed" signal to watch
- * for those changes.  Note that there is a race condition here: you may
- * request a child after listing it only for it to have been destroyed
- * in the meantime.  For this reason, g_settings_get_chuld() may return
- * %NULL even for a child that was listed by this function.
- *
- * For GSettings objects that are not lists, you should probably not be
- * calling this function from "normal" code (since you should already
- * know what children are in your schema).  This function may still be
- * useful there for introspection reasons, however.
- *
- * You should free the return value with g_strfreev() when you are done
- * with it.
- */
-gchar **
-g_settings_list_children (GSettings *settings)
-{
-  const GQuark *keys;
-  gchar **strv;
-  gint n_keys;
-  gint i, j;
-
-  keys = g_settings_schema_list (settings->priv->schema, &n_keys);
-  strv = g_new (gchar *, n_keys + 1);
-  for (i = j = 0; i < n_keys; i++)
-    {
-      const gchar *key = g_quark_to_string (keys[i]);
-
-      if (g_str_has_suffix (key, "/"))
-        {
-          gint length = strlen (key);
-
-          strv[j] = g_memdup (key, length);
-          strv[j][length - 1] = '\0';
-          j++;
-        }
-    }
-  strv[j] = NULL;
-
-  return strv;
-}
-
 /* Binding {{{1 */
 typedef struct
 {
@@ -2736,6 +2652,219 @@ g_settings_unbind (gpointer     object,
   g_object_set_qdata (object, binding_quark, NULL);
 }
 
+/* Children (add, remove, can_{add,remove}, list, get_destroyed) {{{1 */
+
+/**
+ * g_settings_add_child:
+ * @settings: a list #GSettings
+ * @prefix: the prefix of the new child name
+ * @name: return location for the new child name
+ * @returns: %TRUE if the child was added
+ *
+ * Adds a child to a list.
+ *
+ * The child will be created with a new unique name that starts with
+ * @prefix.  For example, if @prefix is "item" then the child's name may
+ * end up looking like "item0", "item1", or so on.  No particular format
+ * is specified -- only that the resulting name will have the given
+ * prefix.
+ *
+ * If the creation was successful then @name (if non-%NULL) is set to
+ * the name of the new child and %TRUE is returned.
+ *
+ * If the creation fails then %FALSE is returned and @name is untouched.
+ **/
+gboolean
+g_settings_add_child (GSettings    *settings,
+                      const gchar  *prefix,
+                      gchar       **name)
+{
+  g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+  g_return_val_if_fail (prefix != NULL, FALSE);
+
+  return g_settings_backend_insert (settings->priv->backend,
+                                    settings->priv->path,
+                                    -1, prefix, name);
+}
+
+/**
+ * g_settings_remove_child:
+ * @settings: a list #GSettings
+ * @id: the id of the child to remove
+ * @returns: %TRUE if the child was successfully removed
+ *
+ * Removes a child from the list.
+ *
+ * In the case that the removal was successful then %TRUE is returned.
+ * In the case that it failed, %FALSE is returned.
+ *
+ * The return value of this function is not particularly useful, since
+ * it is not specified if the non-operation resulting from the request
+ * to remove a non-existent child is considered to be a success (and
+ * this situation can easily arise as a race condition).  Most usually
+ * you will actually probably want to ignore the return value here and
+ * just monitor the "children-changed" signal and make changes to your
+ * user interface accordingly.
+ **/
+gboolean
+g_settings_remove_child (GSettings   *settings,
+                         const gchar *id)
+{
+  g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+  g_return_val_if_fail (id != NULL, FALSE);
+
+  return g_settings_backend_remove (settings->priv->backend,
+                                    settings->priv->path,
+                                    id);
+}
+
+/**
+ * g_settings_can_add_child:
+ * @settings: a #GSettings object
+ * @returns: %TRUE if a call to g_settings_add_child() would succeed
+ *
+ * Checks if it is valid to add children to @settings.
+ **/
+gboolean
+g_settings_can_add_child (GSettings *settings)
+{
+  g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+
+  return g_settings_backend_can_insert (settings->priv->backend,
+                                        settings->priv->path);
+}
+
+/**
+ * g_settings_can_remove_child:
+ * @settings: a #GSettings object
+ * @id: the identifier of an existing child
+ * @returns: %TRUE if a call to g_settings_remove_child() would succeed
+ *
+ * Checks if it is valid to remove @id from @settings.
+ *
+ * The return value of this function is unspecified for the case that
+ * @id does not exist.
+ **/
+gboolean
+g_settings_can_remove_child (GSettings   *settings,
+                             const gchar *id)
+{
+  g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+
+  return g_settings_backend_can_remove (settings->priv->backend,
+                                        settings->priv->path, id);
+}
+
+/**
+ * g_settings_get_destroyed:
+ * @settings: a #GSettings
+ * @returns: %TRUE if @settings has been destroyed
+ *
+ * Checks if @settings has been destroyed.
+ *
+ * If @settings is a member of a list (or a child thereof) and has since
+ * been removed from the list then it will be considered as having been
+ * destroyed.  The "notify" signal will be emitted on the "destroyed"
+ * property and this function will return %TRUE thereafter.
+ *
+ * A destroyed #GSettings object is never revived and nearly all
+ * operations performed on it will be meaningless.
+ **/
+gboolean
+g_settings_get_destroyed (GSettings *settings)
+{
+  return settings->priv->destroyed;
+}
+
+/**
+ * g_settings_list_keys:
+ * @settings: a #GSettings object
+ * @returns: a list of the keys on @settings
+ *
+ * Introspects the list of keys on @settings.
+ *
+ * You should probably not be calling this function from "normal" code
+ * (since you should already know what keys are in your schema).  This
+ * function is intended for introspection reasons.
+ *
+ * You should free the return value with g_strfreev() when you are done
+ * with it.
+ */
+gchar **
+g_settings_list_keys (GSettings *settings)
+{
+  const GQuark *keys;
+  gchar **strv;
+  gint n_keys;
+  gint i, j;
+
+  keys = g_settings_schema_list (settings->priv->schema, &n_keys);
+  strv = g_new (gchar *, n_keys + 1);
+  for (i = j = 0; i < n_keys; i++)
+    {
+      const gchar *key = g_quark_to_string (keys[i]);
+
+      if (!g_str_has_suffix (key, "/"))
+        strv[j++] = g_strdup (key);
+    }
+  strv[j] = NULL;
+
+  return strv;
+}
+
+/**
+ * g_settings_list_children:
+ * @settings: a #GSettings object
+ * @returns: a list of the children on @settings
+ *
+ * Gets the list of children on @settings.
+ *
+ * The list is exactly the list of strings for which it is not an error
+ * to call g_settings_get_child().
+ *
+ * For GSettings objects that are lists, this value can change at any
+ * time and you should connect to the "children-changed" signal to watch
+ * for those changes.  Note that there is a race condition here: you may
+ * request a child after listing it only for it to have been destroyed
+ * in the meantime.  For this reason, g_settings_get_chuld() may return
+ * %NULL even for a child that was listed by this function.
+ *
+ * For GSettings objects that are not lists, you should probably not be
+ * calling this function from "normal" code (since you should already
+ * know what children are in your schema).  This function may still be
+ * useful there for introspection reasons, however.
+ *
+ * You should free the return value with g_strfreev() when you are done
+ * with it.
+ */
+gchar **
+g_settings_list_children (GSettings *settings)
+{
+  const GQuark *keys;
+  gchar **strv;
+  gint n_keys;
+  gint i, j;
+
+  keys = g_settings_schema_list (settings->priv->schema, &n_keys);
+  strv = g_new (gchar *, n_keys + 1);
+  for (i = j = 0; i < n_keys; i++)
+    {
+      const gchar *key = g_quark_to_string (keys[i]);
+
+      if (g_str_has_suffix (key, "/"))
+        {
+          gint length = strlen (key);
+
+          strv[j] = g_memdup (key, length);
+          strv[j][length - 1] = '\0';
+          j++;
+        }
+    }
+  strv[j] = NULL;
+
+  return strv;
+}
+
 /* Epilogue {{{1 */
 
 /* vim:set foldmethod=marker: */
diff --git a/gio/gsettings.h b/gio/gsettings.h
index 294d885..c051ac2 100644
--- a/gio/gsettings.h
+++ b/gio/gsettings.h
@@ -58,7 +58,10 @@ struct _GSettingsClass
                                         const GQuark *keys,
                                         gint          n_keys);
 
-  gpointer padding[20];
+  void        (*can_remove_changed)    (GSettings    *settings);
+  void        (*children_changed)      (GSettings    *settings);
+
+  gpointer padding[18];
 };
 
 struct _GSettings
@@ -79,11 +82,6 @@ GSettings *             g_settings_new_with_backend                     (const g
 GSettings *             g_settings_new_with_backend_and_path            (const gchar        *schema,
                                                                          GSettingsBackend   *backend,
                                                                          const gchar        *path);
-gchar **                g_settings_list_children                        (GSettings          *settings);
-gchar **                g_settings_list_keys                            (GSettings          *settings);
-
-gboolean                g_settings_get_destroyed                        (GSettings          *settings);
-GPermission *           g_settings_get_permission                       (GSettings          *settings);
 
 gboolean                g_settings_set_value                            (GSettings          *settings,
                                                                          const gchar        *key,
@@ -256,6 +254,23 @@ gpointer                g_settings_get_mapped                           (GSettin
                                                                          GSettingsGetMapping      mapping,
                                                                          gpointer                 user_data);
 
+gchar **                g_settings_list_children                        (GSettings               *settings);
+gchar **                g_settings_list_keys                            (GSettings               *settings);
+
+gboolean                g_settings_can_add_child                        (GSettings               *settings);
+
+gboolean                g_settings_can_remove_child                     (GSettings               *settings,
+                                                                         const gchar             *id);
+
+gboolean                g_settings_add_child                            (GSettings               *settings,
+                                                                         const gchar             *prefix,
+                                                                         gchar                  **name);
+
+gboolean                g_settings_remove_child                         (GSettings               *settings,
+                                                                         const gchar             *id);
+
+gboolean                g_settings_get_destroyed                        (GSettings               *settings);
+
 G_END_DECLS
 
 #endif  /* __G_SETTINGS_H__ */
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
index 8485cd3..3414d73 100644
--- a/gio/gsettingsbackend.c
+++ b/gio/gsettingsbackend.c
@@ -1050,3 +1050,48 @@ g_settings_backend_sync_default (void)
   if (class->sync)
     class->sync (backend);
 }
+
+gboolean
+g_settings_backend_can_insert (GSettingsBackend *backend,
+                               const gchar      *path)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->can_insert (backend, path);
+}
+
+gboolean
+g_settings_backend_can_remove (GSettingsBackend *backend,
+                               const gchar      *path,
+                               const gchar      *id)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->can_remove (backend, path, id);
+}
+
+gboolean
+g_settings_backend_insert (GSettingsBackend  *backend,
+                           const gchar       *path,
+                           gint               index_,
+                           const gchar       *prefix,
+                           gchar            **name)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->insert (backend, path, index_, prefix, name);
+}
+
+gboolean
+g_settings_backend_remove (GSettingsBackend *backend,
+                           const gchar      *path,
+                           const gchar      *id)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->remove (backend, path, id);
+}
+
+gboolean
+g_settings_backend_check (GSettingsBackend *backend,
+                          const gchar      *path)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->check (backend, path);
+}
diff --git a/gio/gsettingsbackend.h b/gio/gsettingsbackend.h
index 781874e..2f4aeaa 100644
--- a/gio/gsettingsbackend.h
+++ b/gio/gsettingsbackend.h
@@ -96,16 +96,23 @@ struct _GSettingsBackendClass
   gchar **      (*list)             (GSettingsBackend    *backend,
                                      const gchar         *path,
                                      const gchar * const *schema_items);
-  gchar *       (*add)              (GSettingsBackend    *backend,
+  gboolean      (*check)            (GSettingsBackend    *backend,
+                                     const gchar         *path);
+  gboolean      (*can_insert)       (GSettingsBackend    *backend,
+                                     const gchar         *path);
+  gboolean      (*can_remove)       (GSettingsBackend    *backend,
+                                     const gchar         *path,
+                                     const gchar         *id);
+  gboolean      (*insert)           (GSettingsBackend    *backend,
                                      const gchar         *path,
-                                     const gchar         *hint);
+                                     gint                 index_,
+                                     const gchar         *prefix,
+                                     gchar              **name);
   gboolean      (*remove)           (GSettingsBackend    *backend,
                                      const gchar         *path,
-                                     const gchar         *name);
-  gboolean      (*check)            (GSettingsBackend    *backend,
-                                     const gchar         *path);
+                                     const gchar         *id);
 
-  gpointer padding[20];
+  gpointer padding[18];
 };
 
 struct _GSettingsBackend
diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h
index 68a61a8..ea284e9 100644
--- a/gio/gsettingsbackendinternal.h
+++ b/gio/gsettingsbackendinternal.h
@@ -41,12 +41,24 @@ typedef struct
                                   const gchar         *prefix,
                                   const gchar * const *names,
                                   gpointer             origin_tag);
+
   void (* writable_changed)      (GObject             *target,
                                   GSettingsBackend    *backend,
                                   const gchar         *key);
   void (* path_writable_changed) (GObject             *target,
                                   GSettingsBackend    *backend,
                                   const gchar         *path);
+
+  void (* children_changed)      (GObject             *target,
+                                  GSettingsBackend    *backend,
+                                  const gchar         *path,
+                                  gpointer             origin_tag);
+  void (* can_add_changed)       (GObject             *target,
+                                  GSettingsBackend    *backend,
+                                  const gchar         *path);
+  void (* can_remove_changed)    (GObject             *target,
+                                  GSettingsBackend    *backend,
+                                  const gchar         *path);
 } GSettingsListenerVTable;
 
 G_GNUC_INTERNAL
@@ -91,6 +103,28 @@ void                    g_settings_backend_subscribe                    (GSettin
 G_GNUC_INTERNAL
 GPermission *           g_settings_backend_get_permission               (GSettingsBackend               *backend,
                                                                          const gchar                    *path);
+
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_check                        (GSettingsBackend               *backend,
+                                                                         const gchar                    *path);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_insert                       (GSettingsBackend               *backend,
+                                                                         const gchar                    *path,
+                                                                         gint                            index,
+                                                                         const gchar                    *prefix,
+                                                                         gchar                         **name);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_remove                       (GSettingsBackend               *backend,
+                                                                         const gchar                    *path,
+                                                                         const gchar                    *id);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_can_insert                   (GSettingsBackend               *backend,
+                                                                         const gchar                    *path);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_can_remove                   (GSettingsBackend               *backend,
+                                                                         const gchar                    *path,
+                                                                         const gchar                    *id);
+
 G_GNUC_INTERNAL
 GMainContext *          g_settings_backend_get_active_context           (void);
 G_GNUC_INTERNAL



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