[glib/wip/settings-backend: 13/14] GSettingsSchema: add API for introspecting keys



commit 2630b719fcdd87e9defbc94da1bf62297249fb6f
Author: Ryan Lortie <desrt desrt ca>
Date:   Sun Jun 3 16:44:41 2012 -0400

    GSettingsSchema: add API for introspecting keys
    
    So far only supporting getting key summary/description.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=668232

 gio/gio.symbols                |    6 +
 gio/gsettingsschema-internal.h |    9 +-
 gio/gsettingsschema.c          |  394 ++++++++++++++++++++++++++++++++++++----
 gio/gsettingsschema.h          |   12 ++
 4 files changed, 380 insertions(+), 41 deletions(-)
---
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 588f9af..02806f6 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1156,6 +1156,12 @@ g_settings_schema_ref
 g_settings_schema_unref
 g_settings_schema_get_id
 g_settings_schema_get_path
+g_settings_schema_get_key
+g_settings_schema_key_get_type
+g_settings_schema_key_ref
+g_settings_schema_key_unref
+g_settings_schema_key_get_summary
+g_settings_schema_key_get_description
 g_settings_list_schemas
 g_settings_list_relocatable_schemas
 g_settings_apply
diff --git a/gio/gsettingsschema-internal.h b/gio/gsettingsschema-internal.h
index f9d79eb..5e4cae6 100644
--- a/gio/gsettingsschema-internal.h
+++ b/gio/gsettingsschema-internal.h
@@ -22,7 +22,7 @@
 
 #include "gsettingsschema.h"
 
-typedef struct
+struct _GSettingsSchemaKey
 {
   GSettingsSchema *schema;
   const gchar *name;
@@ -39,14 +39,13 @@ typedef struct
   const GVariantType *type;
   GVariant *minimum, *maximum;
   GVariant *default_value;
-} GSettingsSchemaKey;
+
+  gint ref_count;
+};
 
 G_GNUC_INTERNAL
 const gchar *           g_settings_schema_get_gettext_domain            (GSettingsSchema  *schema);
 G_GNUC_INTERNAL
-GVariantIter *          g_settings_schema_get_value                     (GSettingsSchema  *schema,
-                                                                         const gchar      *key);
-G_GNUC_INTERNAL
 gboolean                g_settings_schema_has_key                       (GSettingsSchema  *schema,
                                                                          const gchar      *key);
 G_GNUC_INTERNAL
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
index f8be19f..afd7c31 100644
--- a/gio/gsettingsschema.c
+++ b/gio/gsettingsschema.c
@@ -138,6 +138,7 @@
  **/
 struct _GSettingsSchema
 {
+  GSettingsSchemaSource *source;
   const gchar *gettext_domain;
   const gchar *path;
   GQuark *items;
@@ -145,6 +146,9 @@ struct _GSettingsSchema
   GvdbTable *table;
   gchar *id;
 
+  GHashTable *summaries;
+  GHashTable *descriptions;
+
   gint ref_count;
 };
 
@@ -176,27 +180,15 @@ G_DEFINE_BOXED_TYPE (GSettingsSchema, g_settings_schema, g_settings_schema_ref,
 struct _GSettingsSchemaSource
 {
   GSettingsSchemaSource *parent;
+  gchar *directory;
   GvdbTable *table;
+  GHashTable **text_tables;
 
   gint ref_count;
 };
 
 static GSettingsSchemaSource *schema_sources;
 
-static void
-prepend_schema_table (GvdbTable *table)
-{
-  GSettingsSchemaSource *source;
-
-  /* we steal the reference from 'schema_sources' for our ->parent */
-  source = g_slice_new (GSettingsSchemaSource);
-  source->parent = schema_sources;
-  source->table = table;
-  source->ref_count = 1;
-
-  schema_sources = source;
-}
-
 /**
  * g_settings_schema_source_ref:
  * @source: a #GSettingsSchemaSource
@@ -234,6 +226,14 @@ g_settings_schema_source_unref (GSettingsSchemaSource *source)
       if (source->parent)
         g_settings_schema_source_unref (source->parent);
       gvdb_table_unref (source->table);
+      g_free (source->directory);
+
+      if (source->text_tables)
+        {
+          g_hash_table_unref (source->text_tables[0]);
+          g_hash_table_unref (source->text_tables[1]);
+          g_free (source->text_tables);
+        }
 
       g_slice_free (GSettingsSchemaSource, source);
     }
@@ -297,7 +297,9 @@ g_settings_schema_source_new_from_directory (const gchar            *directory,
     return NULL;
 
   source = g_slice_new (GSettingsSchemaSource);
+  source->directory = g_strdup (directory);
   source->parent = parent ? g_settings_schema_source_ref (parent) : NULL;
+  source->text_tables = NULL;
   source->table = table;
   source->ref_count = 1;
 
@@ -305,6 +307,17 @@ g_settings_schema_source_new_from_directory (const gchar            *directory,
 }
 
 static void
+try_prepend_dir (const gchar *directory)
+{
+  GSettingsSchemaSource *source;
+
+  source = g_settings_schema_source_new_from_directory (directory, schema_sources, TRUE, NULL);
+
+  if (source != NULL)
+    schema_sources = source;
+}
+
+static void
 initialise_schema_sources (void)
 {
   static gsize initialised;
@@ -324,31 +337,15 @@ initialise_schema_sources (void)
 
       while (i--)
         {
-          gchar *filename;
-          GvdbTable *table;
-
-          filename = g_build_filename (dirs[i], "glib-2.0", "schemas", "gschemas.compiled", NULL);
-          table = gvdb_table_new (filename, TRUE, NULL);
+          gchar *dirname;
 
-          if (table != NULL)
-            prepend_schema_table (table);
-
-          g_free (filename);
+          dirname = g_build_filename (dirs[i], "glib-2.0", "schemas", NULL);
+          try_prepend_dir (dirname);
+          g_free (dirname);
         }
 
       if ((path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL)
-        {
-          gchar *filename;
-          GvdbTable *table;
-
-          filename = g_build_filename (path, "gschemas.compiled", NULL);
-          table = gvdb_table_new (filename, TRUE, NULL);
-
-          if (table != NULL)
-            prepend_schema_table (table);
-
-          g_free (filename);
-        }
+        try_prepend_dir (path);
 
       g_once_init_leave (&initialised, TRUE);
     }
@@ -427,6 +424,7 @@ g_settings_schema_source_lookup (GSettingsSchemaSource *source,
     return NULL;
 
   schema = g_slice_new0 (GSettingsSchema);
+  schema->source = g_settings_schema_source_ref (source);
   schema->ref_count = 1;
   schema->id = g_strdup (schema_id);
   schema->table = table;
@@ -439,6 +437,226 @@ g_settings_schema_source_lookup (GSettingsSchemaSource *source,
   return schema;
 }
 
+typedef struct
+{
+  GHashTable *summaries;
+  GHashTable *descriptions;
+  GSList     *gettext_domain;
+  GSList     *schema_id;
+  GSList     *key_name;
+  GString    *string;
+} TextTableParseInfo;
+
+static const gchar *
+get_attribute_value (GSList *list)
+{
+  GSList *node;
+
+  for (node = list; node; node = node->next)
+    if (node->data)
+      return node->data;
+
+  return NULL;
+}
+
+static void
+pop_attribute_value (GSList **list)
+{
+  gchar *top;
+
+  top = (*list)->data;
+  *list = g_slist_remove (*list, top);
+
+  g_free (top);
+}
+
+static void
+push_attribute_value (GSList      **list,
+                      const gchar  *value)
+{
+  *list = g_slist_prepend (*list, g_strdup (value));
+}
+
+static void
+start_element (GMarkupParseContext  *context,
+               const gchar          *element_name,
+               const gchar         **attribute_names,
+               const gchar         **attribute_values,
+               gpointer              user_data,
+               GError              **error)
+{
+  TextTableParseInfo *info = user_data;
+  const gchar *gettext_domain = NULL;
+  const gchar *schema_id = NULL;
+  const gchar *key_name = NULL;
+  gint i;
+
+  for (i = 0; attribute_names[i]; i++)
+    {
+      if (g_str_equal (attribute_names[i], "gettext-domain"))
+        gettext_domain = attribute_values[i];
+      else if (g_str_equal (attribute_names[i], "id"))
+        schema_id = attribute_values[i];
+      else if (g_str_equal (attribute_names[i], "name"))
+        key_name = attribute_values[i];
+    }
+
+  push_attribute_value (&info->gettext_domain, gettext_domain);
+  push_attribute_value (&info->schema_id, schema_id);
+  push_attribute_value (&info->key_name, key_name);
+
+  if (info->string)
+    {
+      g_string_free (info->string, TRUE);
+      info->string = NULL;
+    }
+
+  if (g_str_equal (element_name, "summary") || g_str_equal (element_name, "description"))
+    info->string = g_string_new (NULL);
+}
+
+static void
+end_element (GMarkupParseContext *context,
+             const gchar *element_name,
+             gpointer user_data,
+             GError **error)
+{
+  TextTableParseInfo *info = user_data;
+
+  pop_attribute_value (&info->gettext_domain);
+  pop_attribute_value (&info->schema_id);
+  pop_attribute_value (&info->key_name);
+
+  if (info->string)
+    {
+      GHashTable *source_table = NULL;
+      const gchar *gettext_domain;
+      const gchar *schema_id;
+      const gchar *key_name;
+      const gchar *string;
+
+      gettext_domain = get_attribute_value (info->gettext_domain);
+      schema_id = get_attribute_value (info->schema_id);
+      key_name = get_attribute_value (info->key_name);
+
+      if (gettext_domain)
+        string = g_dgettext (gettext_domain, info->string->str);
+      else
+        string = info->string->str;
+
+      if (g_str_equal (element_name, "summary"))
+        source_table = info->summaries;
+      else if (g_str_equal (element_name, "description"))
+        source_table = info->descriptions;
+
+      if (source_table && schema_id && key_name)
+        {
+          GHashTable *schema_table;
+
+          schema_table = g_hash_table_lookup (source_table, schema_id);
+
+          if (schema_table == NULL)
+            {
+              schema_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+              g_hash_table_insert (source_table, g_strdup (schema_id), schema_table);
+            }
+
+          g_hash_table_insert (schema_table, g_strdup (key_name), g_strdup (string));
+        }
+
+      g_string_free (info->string, TRUE);
+      info->string = NULL;
+    }
+}
+
+static void
+text (GMarkupParseContext  *context,
+      const gchar          *text,
+      gsize                 text_len,
+      gpointer              user_data,
+      GError              **error)
+{
+  TextTableParseInfo *info = user_data;
+
+  if (info->string)
+    g_string_append_len (info->string, text, text_len);
+}
+
+static void
+parse_into_text_tables (const gchar *directory,
+                        GHashTable  *summaries,
+                        GHashTable  *descriptions)
+{
+  GMarkupParser parser = { start_element, end_element, text };
+  TextTableParseInfo info = { summaries, descriptions };
+  const gchar *basename;
+  GDir *dir;
+
+  dir = g_dir_open (directory, 0, NULL);
+
+  if (dir == NULL)
+    return;
+
+  while ((basename = g_dir_read_name (dir)))
+    {
+      gchar *filename;
+      gchar *contents;
+      gsize size;
+
+      filename = g_build_filename (directory, basename, NULL);
+      if (g_file_get_contents (filename, &contents, &size, NULL))
+        {
+          GMarkupParseContext *context;
+
+          context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, &info, NULL);
+          if (g_markup_parse_context_parse (context, contents, size, NULL))
+            g_markup_parse_context_end_parse (context, NULL);
+          g_markup_parse_context_free (context);
+
+          /* Clean up dangling stuff in case there was an error. */
+          g_slist_free_full (info.gettext_domain, g_free);
+          g_slist_free_full (info.schema_id, g_free);
+          g_slist_free_full (info.key_name, g_free);
+
+          info.gettext_domain = NULL;
+          info.schema_id = NULL;
+          info.key_name = NULL;
+
+          if (info.string)
+            {
+              g_string_free (info.string, TRUE);
+              info.string = NULL;
+            }
+
+          g_free (contents);
+        }
+
+      g_free (filename);
+    }
+
+  g_dir_close (dir);
+}
+
+static GHashTable **
+g_settings_schema_source_get_text_tables (GSettingsSchemaSource *source)
+{
+  if (g_once_init_enter (&source->text_tables))
+    {
+      GHashTable **text_tables;
+
+      text_tables = g_new (GHashTable *, 2);
+      text_tables[0] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref);
+      text_tables[1] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref);
+
+      if (source->directory)
+        parse_into_text_tables (source->directory, text_tables[0], text_tables[1]);
+
+      g_once_init_leave (&source->text_tables, text_tables);
+    }
+
+  return source->text_tables;
+}
+
 static gboolean
 steal_item (gpointer key,
             gpointer value,
@@ -603,6 +821,7 @@ g_settings_schema_unref (GSettingsSchema *schema)
 {
   if (g_atomic_int_dec_and_test (&schema->ref_count))
     {
+      g_settings_schema_source_unref (schema->source);
       gvdb_table_unref (schema->table);
       g_free (schema->items);
       g_free (schema->id);
@@ -627,7 +846,7 @@ g_settings_schema_get_string (GSettingsSchema *schema,
   return result;
 }
 
-GVariantIter *
+static GVariantIter *
 g_settings_schema_get_value (GSettingsSchema *schema,
                              const gchar     *key)
 {
@@ -1034,3 +1253,106 @@ g_settings_schema_key_from_flags (GSettingsSchemaKey *key,
 
   return g_variant_builder_end (&builder);
 }
+
+G_DEFINE_BOXED_TYPE (GSettingsSchemaKey, g_settings_schema_key, g_settings_schema_key_ref, g_settings_schema_key_unref)
+
+GSettingsSchemaKey *
+g_settings_schema_key_ref (GSettingsSchemaKey *key)
+{
+  g_atomic_int_inc (&key->ref_count);
+
+  return key;
+}
+
+void
+g_settings_schema_key_unref (GSettingsSchemaKey *key)
+{
+  if (g_atomic_int_dec_and_test (&key->ref_count))
+    {
+      g_settings_schema_key_clear (key);
+
+      g_slice_free (GSettingsSchemaKey, key);
+    }
+}
+
+/**
+ * g_settings_schema_key_get_summary:
+ * @key: a #GSettingsSchemaKey
+ *
+ * Gets the summary for @key.
+ *
+ * If no summary has been provided in the schema for @key, returns
+ * %NULL.
+ *
+ * The summary is a short description of the purpose of the key; usually
+ * one short sentence.  Summaries can be translated and the value
+ * returned from this function is is the current locale.
+ *
+ * This function is slow.  The summary and description information for
+ * the schemas is not stored in the compiled schema database so this
+ * function has to parse all of the source XML files in the schema
+ * directory.
+ *
+ * Returns: the summary for @key, or %NULL
+ *
+ * Since: 2.34
+ **/
+const gchar *
+g_settings_schema_key_get_summary (GSettingsSchemaKey *key)
+{
+  GHashTable **text_tables;
+  GHashTable *summaries;
+
+  text_tables = g_settings_schema_source_get_text_tables (key->schema->source);
+  summaries = g_hash_table_lookup (text_tables[0], key->schema->id);
+
+  return summaries ? g_hash_table_lookup (summaries, key->name) : NULL;
+}
+
+/**
+ * g_settings_schema_key_get_description:
+ * @key: a #GSettingsSchemaKey
+ *
+ * Gets the description for @key.
+ *
+ * If no description has been provided in the schema for @key, returns
+ * %NULL.
+ *
+ * The description can be one sentence to several paragraphs in length.
+ * Paragraphs are delimited with a double newline.  Descriptions can be
+ * translated and the value returned from this function is is the
+ * current locale.
+ *
+ * This function is slow.  The summary and description information for
+ * the schemas is not stored in the compiled schema database so this
+ * function has to parse all of the source XML files in the schema
+ * directory.
+ *
+ * Returns: the description for @key, or %NULL
+ *
+ * Since: 2.34
+ **/
+const gchar *
+g_settings_schema_key_get_description (GSettingsSchemaKey *key)
+{
+  GHashTable **text_tables;
+  GHashTable *descriptions;
+
+  text_tables = g_settings_schema_source_get_text_tables (key->schema->source);
+  descriptions = g_hash_table_lookup (text_tables[1], key->schema->id);
+
+  return descriptions ? g_hash_table_lookup (descriptions, key->name) : NULL;
+}
+
+GSettingsSchemaKey *
+g_settings_schema_get_key (GSettingsSchema *schema,
+                           const gchar     *name)
+{
+  GSettingsSchemaKey *key;
+
+  key = g_slice_new (GSettingsSchemaKey);
+  g_settings_schema_key_init (key, schema, name);
+  key->ref_count = 1;
+
+  return key;
+}
diff --git a/gio/gsettingsschema.h b/gio/gsettingsschema.h
index 277907d..26b2eef 100644
--- a/gio/gsettingsschema.h
+++ b/gio/gsettingsschema.h
@@ -25,6 +25,7 @@
 
 typedef struct _GSettingsSchemaSource                       GSettingsSchemaSource;
 typedef struct _GSettingsSchema                             GSettingsSchema;
+typedef struct _GSettingsSchemaKey                          GSettingsSchemaKey;
 
 #define                 G_TYPE_SETTINGS_SCHEMA_SOURCE                   (g_settings_schema_source_get_type ())
 GLIB_AVAILABLE_IN_2_32
@@ -57,5 +58,16 @@ void                    g_settings_schema_unref                         (GSettin
 
 const gchar *           g_settings_schema_get_id                        (GSettingsSchema        *schema);
 const gchar *           g_settings_schema_get_path                      (GSettingsSchema        *schema);
+GSettingsSchemaKey *    g_settings_schema_get_key                       (GSettingsSchema        *schema,
+                                                                         const gchar            *key);
+
+#define                 G_TYPE_SETTINGS_SCHEMA_KEY                      (g_settings_schema_key_get_type ())
+GType                   g_settings_schema_key_get_type                  (void) G_GNUC_CONST;
+
+GSettingsSchemaKey *    g_settings_schema_key_ref                       (GSettingsSchemaKey     *key);
+void                    g_settings_schema_key_unref                     (GSettingsSchemaKey     *key);
+
+const gchar *           g_settings_schema_key_get_summary               (GSettingsSchemaKey     *key);
+const gchar *           g_settings_schema_key_get_description           (GSettingsSchemaKey     *key);
 
 #endif /* __G_SETTINGS_SCHEMA_H__ */



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