[glib/gsettings] add GSettingsList



commit 58fe5bb7e7e2093e93d5238032ea9265242dbb1d
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Sep 1 19:18:02 2009 -0400

    add GSettingsList

 gio/Makefile.am     |    2 +
 gio/gio.symbols     |   11 ++
 gio/gsettings.c     |    6 +-
 gio/gsettingslist.c |  432 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gio/gsettingslist.h |   71 +++++++++
 5 files changed, 518 insertions(+), 4 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 91d3007..57a9665 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -239,6 +239,7 @@ libgio_2_0_la_SOURCES =		\
 	gseekable.c 		\
 	gsettingsbackend.c	\
 	gsettingsdelayedbackend.c \
+	gsettingslist.c		\
 	gsettingsschema.c	\
 	gsettings.c		\
 	gsimpleasyncresult.c 	\
@@ -371,6 +372,7 @@ gio_headers =			\
 	gseekable.h 		\
 	gsettingsbackend.h	\
 	gsettingsdelayedbackend.h \
+	gsettingslist.h		\
 	gsettingsschema.h	\
 	gsettings.h		\
 	gsimpleasyncresult.h 	\
diff --git a/gio/gio.symbols b/gio/gio.symbols
index d7b663a..3f3265f 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1317,3 +1317,14 @@ g_settings_set_delay_apply
 g_settings_set_value
 #endif
 #endif
+
+#if IN_HEADER(_gsettingslist_h_)
+#if IN_FILE(_gsettingslist_c_)
+g_settings_get_list
+g_settings_list_add
+g_settings_list_get
+g_settings_list_get_type
+g_settings_list_list
+g_settings_list_remove
+#endif
+#endif
diff --git a/gio/gsettings.c b/gio/gsettings.c
index d621506..f20ff91 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -11,6 +11,7 @@
 #include "gsettings.h"
 
 #include "gsettingsdelayedbackend.h"
+#include "gsettingslist.h"
 #include "gio-marshal.h"
 #include "gsettingsschema.h"
 
@@ -383,7 +384,6 @@ g_settings_new_from_path (const gchar *path)
 static GType
 g_settings_get_gtype_for_schema (GSettingsSchema *schema)
 {
-  //extern GType g_settings_list_get_type (void);
   GVariantIter iter;
   const gchar *item;
   GVariant *list;
@@ -392,11 +392,9 @@ g_settings_get_gtype_for_schema (GSettingsSchema *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_settings_list_get_type ();
-  */
+      return G_TYPE_SETTINGS_LIST;
 
   return G_TYPE_SETTINGS;
 }
diff --git a/gio/gsettingslist.c b/gio/gsettingslist.c
new file mode 100644
index 0000000..8c540f6
--- /dev/null
+++ b/gio/gsettingslist.c
@@ -0,0 +1,432 @@
+/*
+ * 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 "gsettingslist.h"
+
+#include "gsettingsschema.h"
+#include <string.h>
+
+#include "gioalias.h"
+
+typedef struct _ListItem ListItem;
+struct _ListItem
+{
+  gchar         *string;
+  GSettingsList *owner;
+  GSList        *children;
+  gint           index;
+
+  ListItem      *next;
+};
+
+struct _GSettingsListPrivate {
+  ListItem  *list;
+  ListItem **array;
+  gint       n_items;
+
+  gint       tables;
+};
+
+G_DEFINE_TYPE (GSettingsList, g_settings_list, G_TYPE_SETTINGS)
+
+enum
+{
+  SIGNAL_ADDED,
+  SIGNAL_REMOVED,
+  SIGNAL_MOVED,
+  NR_SIGNALS
+};
+
+static guint g_settings_list_signals[NR_SIGNALS];
+
+static void
+g_settings_list_init (GSettingsList *list)
+{
+  list->priv = G_TYPE_INSTANCE_GET_PRIVATE (list,
+                                            G_TYPE_SETTINGS_LIST,
+                                            GSettingsListPrivate);
+}
+
+static void
+g_settings_list_rebuild_array (GSettingsList *list)
+{
+  ListItem *item;
+  gint index;
+
+  g_assert (list->priv->array == NULL);
+
+  list->priv->array = g_new (ListItem *, list->priv->n_items);
+  index = 0;
+
+  for (item = list->priv->list; item; item = item->next)
+    list->priv->array[index++] = item;
+}
+
+static void
+g_settings_list_free_array (GSettingsList *list)
+{
+  g_free (list->priv->array);
+  list->priv->array = NULL;
+}
+
+static void
+g_settings_list_constructed (GObject *object)
+{
+  GSettingsList *list = G_SETTINGS_LIST (object);
+  const gchar *string;
+  GVariantIter iter;
+  ListItem **item;
+
+  G_OBJECT_CLASS (g_settings_list_parent_class)
+    ->constructed (object);
+
+  g_settings_get (G_SETTINGS (list), "list", &iter, NULL);
+  item = &list->priv->list;
+
+  while (g_variant_iter_next (&iter, "s", &string))
+    {
+      (*item) = g_slice_new (ListItem);
+      (*item)->children = NULL;
+      (*item)->index = list->priv->n_items++;
+      (*item)->owner = list;
+      (*item)->string = g_strdup (string);
+      item = &(*item)->next;
+    }
+  *item = NULL;
+
+  g_settings_list_rebuild_array (list);
+}
+
+static void
+g_settings_list_child_finalized (gpointer  user_data,
+                                 GObject  *where_object_was)
+{
+  ListItem *item = user_data;
+
+  item->children = g_slist_remove (item->children, where_object_was);
+  g_object_unref (item->owner);
+}
+
+static void
+g_settings_list_remove_items (GSettingsList *list,
+                              GVariant      *new_list)
+{
+  ListItem **item;
+  gint index;
+
+  index = 0;
+  item = &list->priv->list;
+  while (*item)
+    {
+      GVariantIter iter;
+      const gchar *str;
+
+      g_variant_iter_init (&iter, new_list);
+      while (g_variant_iter_next (&iter, "s", &str))
+        if (strcmp (str, (*item)->string) == 0)
+          g_variant_iter_cancel (&iter);
+
+      if (g_variant_iter_was_cancelled (&iter))
+        {
+          (*item)->index = index++;
+          item = &(*item)->next;
+        }
+      else
+        {
+          ListItem *tmp;
+          GSList *node;
+
+          tmp = (*item);
+          *item = (*item)->next;
+          list->priv->n_items--;
+
+          g_signal_emit (list, g_settings_list_signals[SIGNAL_REMOVED],
+                         0, tmp->string, tmp->index);
+
+          for (node = tmp->children; node; node = node->next)
+            {
+              g_object_ref (node->data);
+              g_settings_destroy (node->data);
+              g_object_weak_unref (node->data,
+                                   g_settings_list_child_finalized,
+                                   tmp);
+              g_object_unref (node->data);
+              g_object_unref (list);
+            }
+
+          g_slist_free (tmp->children);
+          g_free (tmp->string);
+
+          g_slice_free (ListItem, tmp);
+        }
+    }
+}
+
+static void
+g_settings_list_insert_and_move_items (GSettingsList *list,
+                                       GVariant      *new_items)
+{
+  const gchar *new_item;
+  GVariantIter iter;
+  gint index;
+  ListItem **item;
+
+  g_variant_iter_init (&iter, new_items);
+
+  index = 0;
+  item = &list->priv->list;
+  while (g_variant_iter_next (&iter, "s", &new_item))
+    {
+      ListItem **node;
+
+      /* find the nearest instance of the item with this name */
+      for (node = item; (*node); node = &(*node)->next)
+        if (strcmp ((*node)->string, new_item) == 0)
+          break;
+
+      if (*node)
+        {
+          /* if it's not already here... */
+          if (node != item)
+            {
+              /* move it. */
+              ListItem *tmp = *node;
+              *node = (*node)->next;
+
+              tmp->next = (*item);
+              *item = tmp;
+
+              g_signal_emit (list, g_settings_list_signals[SIGNAL_MOVED],
+                             0, tmp->string, tmp->index, index);
+            }
+
+          (*item)->index = index++;
+          item = &(*item)->next;
+        }
+      else
+        {
+          /* double check to ensure that the item isn't already in the
+           * list (ie: we got a bogus input that contains an item twice)
+           */
+          for (node = &list->priv->list; node != item; node = &(*node)->next)
+            if (strcmp ((*node)->string, new_item) == 0)
+              break;
+
+          if (node != item)
+            break;
+
+          /* not found. insert here */
+          ListItem *tmp = g_slice_new (ListItem);
+          tmp->next = *item;
+          tmp->string = g_strdup (new_item);
+          tmp->index = index++;
+          tmp->owner = list;
+          tmp->children = NULL;
+          *item = tmp;
+          item = &tmp->next;
+
+          list->priv->n_items++;
+
+          g_signal_emit (list, g_settings_list_signals[SIGNAL_ADDED],
+                         0, tmp->string, tmp->index);
+        }
+    }
+}
+
+static void
+g_settings_list_changed (GSettings   *settings,
+                         const gchar *key)
+{
+  GSettingsList *list = G_SETTINGS_LIST (settings);
+  GVariant *new_list;
+
+  new_list = g_settings_get_value (settings, "list");
+
+  g_settings_list_free_array (list);
+  g_settings_list_remove_items (list, new_list);
+  g_settings_list_insert_and_move_items (list, new_list);
+  g_settings_list_rebuild_array (list);
+}
+
+static void
+g_settings_list_finalize (GObject *object)
+{
+  GSettingsList *list = G_SETTINGS_LIST (object);
+  ListItem *item;
+
+  while ((item = list->priv->list))
+    {
+      list->priv->list = item->next;
+
+      g_free (item->string);
+      g_slice_free (ListItem, item);
+    }
+
+  g_free (list->priv->array);
+}
+
+static GSettings *
+g_settings_list_get_settings (GSettings   *settings,
+                              const gchar *name)
+{
+  GSettingsClass *parent_class = G_SETTINGS_CLASS (g_settings_list_parent_class);
+  GSettingsList *list = G_SETTINGS_LIST (settings);
+  GSettings *child;
+  ListItem *item;
+
+  for (item = list->priv->list; item; item = item->next)
+    if (strcmp (item->string, name) == 0)
+      break;
+
+  if (item == NULL)
+    g_error ("No such list item %s\n", name);
+
+  child = parent_class->get_settings (settings, name);
+
+  item->children = g_slist_prepend (item->children, child);
+  g_object_weak_ref (G_OBJECT (child), g_settings_list_child_finalized, item);
+  g_object_ref (list);
+
+  return child;
+}
+
+static void
+g_settings_list_class_init (GSettingsListClass *class)
+{
+  GSettingsClass *settings_class = G_SETTINGS_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  settings_class->get_settings = g_settings_list_get_settings;
+  settings_class->changed = g_settings_list_changed;
+
+  object_class->constructed = g_settings_list_constructed;
+  object_class->finalize = g_settings_list_finalize;
+
+  g_type_class_add_private (class, sizeof (GSettingsListPrivate));
+}
+
+GSettingsList *
+g_settings_get_list (GSettings   *settings,
+                     const gchar *name)
+{
+  GSettings *list;
+
+  list = g_settings_get_settings (settings, name);
+  g_assert (G_IS_SETTINGS_LIST (list));
+
+  return G_SETTINGS_LIST (list);
+}
+
+gchar **
+g_settings_list_list (GSettingsList *list,
+                      gint          *n_items)
+{
+  const gchar *item;
+  GVariantIter iter;
+  GVariant *value;
+  gchar **result;
+  gchar **ptr;
+  gint n;
+
+  if (!n_items)
+    n_items = &n;
+
+  value = g_settings_get_value (G_SETTINGS (list), "list");
+  *n_items = g_variant_iter_init (&iter, value);
+  result = g_new (gchar *, *n_items + 1);
+  g_variant_unref (value);
+
+  ptr = result;
+  while (g_variant_iter_next (&iter, "s", &item))
+    *ptr++ = g_strdup (item);
+  *ptr = NULL;
+
+  return result;
+}
+
+GSettings *
+g_settings_list_get (GSettingsList *list,
+                     const gchar   *id)
+{
+  return g_settings_get_settings (G_SETTINGS (list), id);
+}
+
+gchar *
+g_settings_list_add (GSettingsList *list,
+                     const gchar   *prefix,
+                     gint           before)
+{
+  GVariantBuilder *builder;
+  gboolean already_in;
+  GVariant *old_list;
+  gint i, length;
+  gboolean in;
+
+  builder = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+                                   G_VARIANT_TYPE ("as"));
+  old_list = g_settings_get_value (G_SETTINGS (list), "list");
+  length = g_variant_n_children (old_list);
+  already_in = FALSE;
+  in = FALSE;
+
+  for (i = 0; i < length; i++)
+    {
+      GVariant *item;
+
+      if (before == i)
+        {
+          g_variant_builder_add (builder, "s", prefix);
+          in = TRUE;
+        }
+
+      item = g_variant_get_child_value (old_list, i);
+      g_variant_builder_add_value (builder, item);
+
+      if (strcmp (g_variant_get_string (item, NULL), prefix) == 0)
+        already_in = TRUE;
+
+      g_variant_unref (item);
+    }
+
+  g_variant_unref (old_list);
+
+  if (!in)
+    g_variant_builder_add (builder, "s", prefix);
+
+  if (!already_in)
+    g_settings_set_value (G_SETTINGS (list), "list",
+                          g_variant_builder_end (builder));
+  else
+    g_variant_builder_cancel (builder);
+
+  return g_strdup (prefix);
+}
+
+void
+g_settings_list_remove (GSettingsList *list,
+                        const gchar   *id)
+{
+  GVariantBuilder *new_list;
+  GVariantIter old_list;
+  GVariant *value;
+
+  new_list = g_variant_builder_new (G_VARIANT_TYPE_CLASS_ARRAY,
+                                    G_VARIANT_TYPE ("as"));
+  g_settings_get (G_SETTINGS (list), "list", &old_list);
+
+  while ((value = g_variant_iter_next_value (&old_list)))
+    if (strcmp (g_variant_get_string (value, NULL), id))
+      g_variant_builder_add_value (new_list, value);
+
+  g_settings_set (G_SETTINGS (list), "list", new_list);
+}
+
+#define _gsettingslist_c_
+#include "gioaliasdef.c"
diff --git a/gio/gsettingslist.h b/gio/gsettingslist.h
new file mode 100644
index 0000000..7e926a2
--- /dev/null
+++ b/gio/gsettingslist.h
@@ -0,0 +1,71 @@
+/*
+ * 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 _gsettingslist_h_
+#define _gsettingslist_h_
+
+#include "gsettings.h"
+
+#define G_TYPE_SETTINGS_LIST                                (g_settings_list_get_type ())
+#define G_SETTINGS_LIST(inst)                               (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_SETTINGS_LIST, GSettingsList))
+#define G_SETTINGS_LIST_CLASS(class)                        (G_TYPE_CHECK_CLASS_CAST ((class),                       \
+                                                             G_TYPE_SETTINGS_LIST, GSettingsListClass))
+#define G_IS_SETTINGS_LIST(inst)                            (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_SETTINGS_LIST))
+#define G_IS_SETTINGS_LIST_CLASS(class)                     (G_TYPE_CHECK_CLASS_TYPE ((class),                       \
+                                                             G_TYPE_SETTINGS_LIST))
+#define G_SETTINGS_LIST_GET_CLASS(inst)                     (G_TYPE_INSTANCE_GET_CLASS ((inst),                      \
+                                                             G_TYPE_SETTINGS_LIST, GSettingsListClass))
+
+typedef struct _GSettingsListPrivate                        GSettingsListPrivate;
+typedef struct _GSettingsListClass                          GSettingsListClass;
+typedef struct _GSettingsList                               GSettingsList;
+
+struct _GSettingsListClass
+{
+  GSettingsClass parent_class;
+};
+
+struct _GSettingsList
+{
+  GSettings parent_instance;
+  GSettingsListPrivate *priv;
+};
+
+G_BEGIN_DECLS
+
+
+GType                   g_settings_list_get_type                        (void);
+
+gchar **                g_settings_list_list                            (GSettingsList  *list,
+                                                                         gint           *n_items);
+
+GSettings *             g_settings_list_get                             (GSettingsList  *list,
+                                                                         const gchar    *id);
+
+gchar *                 g_settings_list_add                             (GSettingsList  *list,
+                                                                         const gchar    *prefix,
+                                                                         gint            before);
+
+void                    g_settings_list_remove                          (GSettingsList  *list,
+                                                                         const gchar    *id);
+
+gboolean                g_settings_list_move_item                       (GSettingsList  *list,
+                                                                         const gchar    *id,
+                                                                         gint            new_index);
+
+/* avoid header pain -- put this here */
+GSettingsList *         g_settings_get_list                             (GSettings      *settings,
+                                                                         const gchar    *name);
+
+G_END_DECLS
+
+#endif /* _gsettingslist_h_ */



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