From 2630b719fcdd87e9defbc94da1bf62297249fb6f Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Sun, 3 Jun 2012 16:44:41 -0400 Subject: [PATCH] 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 588f9aff4..02806f637 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 f9d79eb68..5e4cae65e 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 f8be19fa9..afd7c31b8 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,13 +297,26 @@ 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; return source; } +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) { @@ -324,31 +337,15 @@ initialise_schema_sources (void) while (i--) { - gchar *filename; - GvdbTable *table; + gchar *dirname; - filename = g_build_filename (dirs[i], "glib-2.0", "schemas", "gschemas.compiled", NULL); - table = gvdb_table_new (filename, TRUE, NULL); - - 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 277907da6..26b2eef9e 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__ */