mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-14 05:16:18 +01:00
4b94c0831e
Back in the far-off twentieth century, it was normal on unix workstations for U+0060 GRAVE ACCENT to be drawn as "‛" and for U+0027 APOSTROPHE to be drawn as "’". This led to the convention of using them as poor-man's ‛smart quotes’ in ASCII-only text. However, "'" is now universally drawn as a vertical line, and "`" at a 45-degree angle, making them an `odd couple' when used together. Unfortunately, there are lots of very old strings in glib, and also lots of new strings in which people have kept up the old tradition, perhaps entirely unaware that it used to not look stupid. Fix this by just using 'dumb quotes' everywhere. https://bugzilla.gnome.org/show_bug.cgi?id=700746
1037 lines
27 KiB
C
1037 lines
27 KiB
C
/*
|
|
* Copyright © 2010 Codethink Limited
|
|
* Copyright © 2011 Canonical Limited
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the licence, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gsettingsschema-internal.h"
|
|
#include "gsettings.h"
|
|
|
|
#include "gvdb/gvdb-reader.h"
|
|
#include "strinfo.c"
|
|
|
|
#include <glibintl.h>
|
|
#include <locale.h>
|
|
#include <string.h>
|
|
|
|
/**
|
|
* SECTION:gsettingsschema
|
|
* @short_description: introspecting and controlling the loading of
|
|
* GSettings schemas
|
|
*
|
|
* The #GSettingsSchemaSource and #GSettingsSchema APIs provide a
|
|
* mechanism for advanced control over the loading of schemas and a
|
|
* mechanism for introspecting their content.
|
|
*
|
|
* Plugin loading systems that wish to provide plugins a way to access
|
|
* settings face the problem of how to make the schemas for these
|
|
* settings visible to GSettings. Typically, a plugin will want to ship
|
|
* the schema along with itself and it won't be installed into the
|
|
* standard system directories for schemas.
|
|
*
|
|
* #GSettingsSchemaSource provides a mechanism for dealing with this by
|
|
* allowing the creation of a new 'schema source' from which schemas can
|
|
* be acquired. This schema source can then become part of the metadata
|
|
* associated with the plugin and queried whenever the plugin requires
|
|
* access to some settings.
|
|
*
|
|
* Consider the following example:
|
|
*
|
|
* |[
|
|
* typedef struct
|
|
* {
|
|
* ...
|
|
* GSettingsSchemaSource *schema_source;
|
|
* ...
|
|
* } Plugin;
|
|
*
|
|
* Plugin *
|
|
* initialise_plugin (const gchar *dir)
|
|
* {
|
|
* Plugin *plugin;
|
|
*
|
|
* ...
|
|
*
|
|
* plugin->schema_source =
|
|
* g_settings_new_schema_source_from_directory (dir,
|
|
* g_settings_schema_source_get_default (), FALSE, NULL);
|
|
*
|
|
* ...
|
|
*
|
|
* return plugin;
|
|
* }
|
|
*
|
|
* ...
|
|
*
|
|
* GSettings *
|
|
* plugin_get_settings (Plugin *plugin,
|
|
* const gchar *schema_id)
|
|
* {
|
|
* GSettingsSchema *schema;
|
|
*
|
|
* if (schema_id == NULL)
|
|
* schema_id = plugin->identifier;
|
|
*
|
|
* schema = g_settings_schema_source_lookup (plugin->schema_source,
|
|
* schema_id, FALSE);
|
|
*
|
|
* if (schema == NULL)
|
|
* {
|
|
* ... disable the plugin or abort, etc ...
|
|
* }
|
|
*
|
|
* return g_settings_new_full (schema, NULL, NULL);
|
|
* }
|
|
* ]|
|
|
*
|
|
* The code above shows how hooks should be added to the code that
|
|
* initialises (or enables) the plugin to create the schema source and
|
|
* how an API can be added to the plugin system to provide a convenient
|
|
* way for the plugin to access its settings, using the schemas that it
|
|
* ships.
|
|
*
|
|
* From the standpoint of the plugin, it would need to ensure that it
|
|
* ships a gschemas.compiled file as part of itself, and then simply do
|
|
* the following:
|
|
*
|
|
* |[
|
|
* {
|
|
* GSettings *settings;
|
|
* gint some_value;
|
|
*
|
|
* settings = plugin_get_settings (self, NULL);
|
|
* some_value = g_settings_get_int (settings, "some-value");
|
|
* ...
|
|
* }
|
|
* ]|
|
|
*
|
|
* It's also possible that the plugin system expects the schema source
|
|
* files (ie: .gschema.xml files) instead of a gschemas.compiled file.
|
|
* In that case, the plugin loading system must compile the schemas for
|
|
* itself before attempting to create the settings source.
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
|
|
/**
|
|
* GSettingsSchema:
|
|
*
|
|
* This is an opaque structure type. You may not access it directly.
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
struct _GSettingsSchema
|
|
{
|
|
const gchar *gettext_domain;
|
|
const gchar *path;
|
|
GQuark *items;
|
|
gint n_items;
|
|
GvdbTable *table;
|
|
gchar *id;
|
|
|
|
gint ref_count;
|
|
};
|
|
|
|
/**
|
|
* G_TYPE_SETTINGS_SCHEMA_SOURCE:
|
|
*
|
|
* A boxed #GType corresponding to #GSettingsSchemaSource.
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
G_DEFINE_BOXED_TYPE (GSettingsSchemaSource, g_settings_schema_source, g_settings_schema_source_ref, g_settings_schema_source_unref)
|
|
|
|
/**
|
|
* G_TYPE_SETTINGS_SCHEMA:
|
|
*
|
|
* A boxed #GType corresponding to #GSettingsSchema.
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
G_DEFINE_BOXED_TYPE (GSettingsSchema, g_settings_schema, g_settings_schema_ref, g_settings_schema_unref)
|
|
|
|
/**
|
|
* GSettingsSchemaSource:
|
|
*
|
|
* This is an opaque structure type. You may not access it directly.
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
struct _GSettingsSchemaSource
|
|
{
|
|
GSettingsSchemaSource *parent;
|
|
GvdbTable *table;
|
|
|
|
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
|
|
*
|
|
* Increase the reference count of @source, returning a new reference.
|
|
*
|
|
* Returns: a new reference to @source
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
GSettingsSchemaSource *
|
|
g_settings_schema_source_ref (GSettingsSchemaSource *source)
|
|
{
|
|
g_atomic_int_inc (&source->ref_count);
|
|
|
|
return source;
|
|
}
|
|
|
|
/**
|
|
* g_settings_schema_source_unref:
|
|
* @source: a #GSettingsSchemaSource
|
|
*
|
|
* Decrease the reference count of @source, possibly freeing it.
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
void
|
|
g_settings_schema_source_unref (GSettingsSchemaSource *source)
|
|
{
|
|
if (g_atomic_int_dec_and_test (&source->ref_count))
|
|
{
|
|
if (source == schema_sources)
|
|
g_error ("g_settings_schema_source_unref() called too many times on the default schema source");
|
|
|
|
if (source->parent)
|
|
g_settings_schema_source_unref (source->parent);
|
|
gvdb_table_unref (source->table);
|
|
|
|
g_slice_free (GSettingsSchemaSource, source);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_settings_schema_source_new_from_directory:
|
|
* @directory: the filename of a directory
|
|
* @parent: (allow-none): a #GSettingsSchemaSource, or %NULL
|
|
* @trusted: %TRUE, if the directory is trusted
|
|
* @error: a pointer to a #GError pointer set to %NULL, or %NULL
|
|
*
|
|
* Attempts to create a new schema source corresponding to the contents
|
|
* of the given directory.
|
|
*
|
|
* This function is not required for normal uses of #GSettings but it
|
|
* may be useful to authors of plugin management systems.
|
|
*
|
|
* The directory should contain a file called
|
|
* <filename>gschemas.compiled</filename> as produced by
|
|
* <command>glib-compile-schemas</command>.
|
|
*
|
|
* If @trusted is %TRUE then <filename>gschemas.compiled</filename> is
|
|
* trusted not to be corrupted. This assumption has a performance
|
|
* advantage, but can result in crashes or inconsistent behaviour in the
|
|
* case of a corrupted file. Generally, you should set @trusted to
|
|
* %TRUE for files installed by the system and to %FALSE for files in
|
|
* the home directory.
|
|
*
|
|
* If @parent is non-%NULL then there are two effects.
|
|
*
|
|
* First, if g_settings_schema_source_lookup() is called with the
|
|
* @recursive flag set to %TRUE and the schema can not be found in the
|
|
* source, the lookup will recurse to the parent.
|
|
*
|
|
* Second, any references to other schemas specified within this
|
|
* source (ie: <literal>child</literal> or <literal>extends</literal>)
|
|
* references may be resolved from the @parent.
|
|
*
|
|
* For this second reason, except in very unusual situations, the
|
|
* @parent should probably be given as the default schema source, as
|
|
* returned by g_settings_schema_source_get_default().
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
GSettingsSchemaSource *
|
|
g_settings_schema_source_new_from_directory (const gchar *directory,
|
|
GSettingsSchemaSource *parent,
|
|
gboolean trusted,
|
|
GError **error)
|
|
{
|
|
GSettingsSchemaSource *source;
|
|
GvdbTable *table;
|
|
gchar *filename;
|
|
|
|
filename = g_build_filename (directory, "gschemas.compiled", NULL);
|
|
table = gvdb_table_new (filename, trusted, error);
|
|
g_free (filename);
|
|
|
|
if (table == NULL)
|
|
return NULL;
|
|
|
|
source = g_slice_new (GSettingsSchemaSource);
|
|
source->parent = parent ? g_settings_schema_source_ref (parent) : NULL;
|
|
source->table = table;
|
|
source->ref_count = 1;
|
|
|
|
return source;
|
|
}
|
|
|
|
static void
|
|
initialise_schema_sources (void)
|
|
{
|
|
static gsize initialised;
|
|
|
|
/* need a separate variable because 'schema_sources' may legitimately
|
|
* be null if we have zero valid schema sources
|
|
*/
|
|
if G_UNLIKELY (g_once_init_enter (&initialised))
|
|
{
|
|
const gchar * const *dirs;
|
|
const gchar *path;
|
|
gint i;
|
|
|
|
/* iterate in reverse: count up, then count down */
|
|
dirs = g_get_system_data_dirs ();
|
|
for (i = 0; dirs[i]; i++);
|
|
|
|
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);
|
|
|
|
if (table != NULL)
|
|
prepend_schema_table (table);
|
|
|
|
g_free (filename);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
g_once_init_leave (&initialised, TRUE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_settings_schema_source_get_default:
|
|
*
|
|
* Gets the default system schema source.
|
|
*
|
|
* This function is not required for normal uses of #GSettings but it
|
|
* may be useful to authors of plugin management systems or to those who
|
|
* want to introspect the content of schemas.
|
|
*
|
|
* If no schemas are installed, %NULL will be returned.
|
|
*
|
|
* The returned source may actually consist of multiple schema sources
|
|
* from different directories, depending on which directories were given
|
|
* in <envar>XDG_DATA_DIRS</envar> and
|
|
* <envar>GSETTINGS_SCHEMA_DIR</envar>. For this reason, all lookups
|
|
* performed against the default source should probably be done
|
|
* recursively.
|
|
*
|
|
* Returns: (transfer none): the default schema source
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
GSettingsSchemaSource *
|
|
g_settings_schema_source_get_default (void)
|
|
{
|
|
initialise_schema_sources ();
|
|
|
|
return schema_sources;
|
|
}
|
|
|
|
/**
|
|
* g_settings_schema_source_lookup:
|
|
* @source: a #GSettingsSchemaSource
|
|
* @schema_id: a schema ID
|
|
* @recursive: %TRUE if the lookup should be recursive
|
|
*
|
|
* Looks up a schema with the identifier @schema_id in @source.
|
|
*
|
|
* This function is not required for normal uses of #GSettings but it
|
|
* may be useful to authors of plugin management systems or to those who
|
|
* want to introspect the content of schemas.
|
|
*
|
|
* If the schema isn't found directly in @source and @recursive is %TRUE
|
|
* then the parent sources will also be checked.
|
|
*
|
|
* If the schema isn't found, %NULL is returned.
|
|
*
|
|
* Returns: (transfer full): a new #GSettingsSchema
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
GSettingsSchema *
|
|
g_settings_schema_source_lookup (GSettingsSchemaSource *source,
|
|
const gchar *schema_id,
|
|
gboolean recursive)
|
|
{
|
|
GSettingsSchema *schema;
|
|
GvdbTable *table;
|
|
|
|
g_return_val_if_fail (source != NULL, NULL);
|
|
g_return_val_if_fail (schema_id != NULL, NULL);
|
|
|
|
table = gvdb_table_get_table (source->table, schema_id);
|
|
|
|
if (table == NULL && recursive)
|
|
for (source = source->parent; source; source = source->parent)
|
|
if ((table = gvdb_table_get_table (source->table, schema_id)))
|
|
break;
|
|
|
|
if (table == NULL)
|
|
return NULL;
|
|
|
|
schema = g_slice_new0 (GSettingsSchema);
|
|
schema->ref_count = 1;
|
|
schema->id = g_strdup (schema_id);
|
|
schema->table = table;
|
|
schema->path = g_settings_schema_get_string (schema, ".path");
|
|
schema->gettext_domain = g_settings_schema_get_string (schema, ".gettext-domain");
|
|
|
|
if (schema->gettext_domain)
|
|
bind_textdomain_codeset (schema->gettext_domain, "UTF-8");
|
|
|
|
return schema;
|
|
}
|
|
|
|
static gboolean
|
|
steal_item (gpointer key,
|
|
gpointer value,
|
|
gpointer user_data)
|
|
{
|
|
gchar ***ptr = user_data;
|
|
|
|
*(*ptr)++ = (gchar *) key;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static const gchar * const *non_relocatable_schema_list;
|
|
static const gchar * const *relocatable_schema_list;
|
|
static gsize schema_lists_initialised;
|
|
|
|
static void
|
|
ensure_schema_lists (void)
|
|
{
|
|
if (g_once_init_enter (&schema_lists_initialised))
|
|
{
|
|
GSettingsSchemaSource *source;
|
|
GHashTable *single, *reloc;
|
|
const gchar **ptr;
|
|
gchar **list;
|
|
gint i;
|
|
|
|
initialise_schema_sources ();
|
|
|
|
/* We use hash tables to avoid duplicate listings for schemas that
|
|
* appear in more than one file.
|
|
*/
|
|
single = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
reloc = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
|
|
for (source = schema_sources; source; source = source->parent)
|
|
{
|
|
list = gvdb_table_list (source->table, "");
|
|
|
|
/* empty schema cache file? */
|
|
if (list == NULL)
|
|
continue;
|
|
|
|
for (i = 0; list[i]; i++)
|
|
{
|
|
if (!g_hash_table_lookup (single, list[i]) &&
|
|
!g_hash_table_lookup (reloc, list[i]))
|
|
{
|
|
GvdbTable *table;
|
|
|
|
table = gvdb_table_get_table (source->table, list[i]);
|
|
g_assert (table != NULL);
|
|
|
|
if (gvdb_table_has_value (table, ".path"))
|
|
g_hash_table_insert (single, g_strdup (list[i]), NULL);
|
|
else
|
|
g_hash_table_insert (reloc, g_strdup (list[i]), NULL);
|
|
|
|
gvdb_table_unref (table);
|
|
}
|
|
}
|
|
|
|
g_strfreev (list);
|
|
}
|
|
|
|
ptr = g_new (const gchar *, g_hash_table_size (single) + 1);
|
|
non_relocatable_schema_list = ptr;
|
|
g_hash_table_foreach_steal (single, steal_item, &ptr);
|
|
g_hash_table_unref (single);
|
|
*ptr = NULL;
|
|
|
|
ptr = g_new (const gchar *, g_hash_table_size (reloc) + 1);
|
|
relocatable_schema_list = ptr;
|
|
g_hash_table_foreach_steal (reloc, steal_item, &ptr);
|
|
g_hash_table_unref (reloc);
|
|
*ptr = NULL;
|
|
|
|
g_once_init_leave (&schema_lists_initialised, TRUE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_settings_list_schemas:
|
|
*
|
|
* Gets a list of the #GSettings schemas installed on the system. The
|
|
* returned list is exactly the list of schemas for which you may call
|
|
* g_settings_new() without adverse effects.
|
|
*
|
|
* This function does not list the schemas that do not provide their own
|
|
* paths (ie: schemas for which you must use
|
|
* g_settings_new_with_path()). See
|
|
* g_settings_list_relocatable_schemas() for that.
|
|
*
|
|
* Returns: (element-type utf8) (transfer none): a list of #GSettings
|
|
* schemas that are available. The list must not be modified or
|
|
* freed.
|
|
*
|
|
* Since: 2.26
|
|
**/
|
|
const gchar * const *
|
|
g_settings_list_schemas (void)
|
|
{
|
|
ensure_schema_lists ();
|
|
|
|
return non_relocatable_schema_list;
|
|
}
|
|
|
|
/**
|
|
* g_settings_list_relocatable_schemas:
|
|
*
|
|
* Gets a list of the relocatable #GSettings schemas installed on the
|
|
* system. These are schemas that do not provide their own path. It is
|
|
* usual to instantiate these schemas directly, but if you want to you
|
|
* can use g_settings_new_with_path() to specify the path.
|
|
*
|
|
* The output of this function, taken together with the output of
|
|
* g_settings_list_schemas() represents the complete list of all
|
|
* installed schemas.
|
|
*
|
|
* Returns: (element-type utf8) (transfer none): a list of relocatable
|
|
* #GSettings schemas that are available. The list must not be
|
|
* modified or freed.
|
|
*
|
|
* Since: 2.28
|
|
**/
|
|
const gchar * const *
|
|
g_settings_list_relocatable_schemas (void)
|
|
{
|
|
ensure_schema_lists ();
|
|
|
|
return relocatable_schema_list;
|
|
}
|
|
|
|
/**
|
|
* g_settings_schema_ref:
|
|
* @schema: a #GSettingsSchema
|
|
*
|
|
* Increase the reference count of @schema, returning a new reference.
|
|
*
|
|
* Returns: a new reference to @schema
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
GSettingsSchema *
|
|
g_settings_schema_ref (GSettingsSchema *schema)
|
|
{
|
|
g_atomic_int_inc (&schema->ref_count);
|
|
|
|
return schema;
|
|
}
|
|
|
|
/**
|
|
* g_settings_schema_unref:
|
|
* @schema: a #GSettingsSchema
|
|
*
|
|
* Decrease the reference count of @schema, possibly freeing it.
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
void
|
|
g_settings_schema_unref (GSettingsSchema *schema)
|
|
{
|
|
if (g_atomic_int_dec_and_test (&schema->ref_count))
|
|
{
|
|
gvdb_table_unref (schema->table);
|
|
g_free (schema->items);
|
|
g_free (schema->id);
|
|
|
|
g_slice_free (GSettingsSchema, schema);
|
|
}
|
|
}
|
|
|
|
const gchar *
|
|
g_settings_schema_get_string (GSettingsSchema *schema,
|
|
const gchar *key)
|
|
{
|
|
const gchar *result = NULL;
|
|
GVariant *value;
|
|
|
|
if ((value = gvdb_table_get_raw_value (schema->table, key)))
|
|
{
|
|
result = g_variant_get_string (value, NULL);
|
|
g_variant_unref (value);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
GVariantIter *
|
|
g_settings_schema_get_value (GSettingsSchema *schema,
|
|
const gchar *key)
|
|
{
|
|
GVariantIter *iter;
|
|
GVariant *value;
|
|
|
|
value = gvdb_table_get_raw_value (schema->table, key);
|
|
|
|
if G_UNLIKELY (value == NULL || !g_variant_is_of_type (value, G_VARIANT_TYPE_TUPLE))
|
|
g_error ("Settings schema '%s' does not contain a key named '%s'", schema->id, key);
|
|
|
|
iter = g_variant_iter_new (value);
|
|
g_variant_unref (value);
|
|
|
|
return iter;
|
|
}
|
|
|
|
/**
|
|
* g_settings_schema_get_path:
|
|
* @schema: a #GSettingsSchema
|
|
*
|
|
* Gets the path associated with @schema, or %NULL.
|
|
*
|
|
* Schemas may be single-instance or relocatable. Single-instance
|
|
* schemas correspond to exactly one set of keys in the backend
|
|
* database: those located at the path returned by this function.
|
|
*
|
|
* Relocatable schemas can be referenced by other schemas and can
|
|
* threfore describe multiple sets of keys at different locations. For
|
|
* relocatable schemas, this function will return %NULL.
|
|
*
|
|
* Returns: (transfer none): the path of the schema, or %NULL
|
|
*
|
|
* Since: 2.32
|
|
**/
|
|
const gchar *
|
|
g_settings_schema_get_path (GSettingsSchema *schema)
|
|
{
|
|
return schema->path;
|
|
}
|
|
|
|
const gchar *
|
|
g_settings_schema_get_gettext_domain (GSettingsSchema *schema)
|
|
{
|
|
return schema->gettext_domain;
|
|
}
|
|
|
|
gboolean
|
|
g_settings_schema_has_key (GSettingsSchema *schema,
|
|
const gchar *key)
|
|
{
|
|
return gvdb_table_has_value (schema->table, key);
|
|
}
|
|
|
|
const GQuark *
|
|
g_settings_schema_list (GSettingsSchema *schema,
|
|
gint *n_items)
|
|
{
|
|
gint i, j;
|
|
|
|
if (schema->items == NULL)
|
|
{
|
|
gchar **list;
|
|
gint len;
|
|
|
|
list = gvdb_table_list (schema->table, "");
|
|
len = list ? g_strv_length (list) : 0;
|
|
|
|
schema->items = g_new (GQuark, len);
|
|
j = 0;
|
|
|
|
for (i = 0; i < len; i++)
|
|
if (list[i][0] != '.')
|
|
schema->items[j++] = g_quark_from_string (list[i]);
|
|
schema->n_items = j;
|
|
|
|
g_strfreev (list);
|
|
}
|
|
|
|
*n_items = schema->n_items;
|
|
return schema->items;
|
|
}
|
|
|
|
/**
|
|
* g_settings_schema_get_id:
|
|
* @schema: a #GSettingsSchema
|
|
*
|
|
* Get the ID of @schema.
|
|
*
|
|
* Returns: (transfer none): the ID
|
|
**/
|
|
const gchar *
|
|
g_settings_schema_get_id (GSettingsSchema *schema)
|
|
{
|
|
return schema->id;
|
|
}
|
|
|
|
static inline void
|
|
endian_fixup (GVariant **value)
|
|
{
|
|
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
|
GVariant *tmp;
|
|
|
|
tmp = g_variant_byteswap (*value);
|
|
g_variant_unref (*value);
|
|
*value = tmp;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
g_settings_schema_key_init (GSettingsSchemaKey *key,
|
|
GSettingsSchema *schema,
|
|
const gchar *name)
|
|
{
|
|
GVariantIter *iter;
|
|
GVariant *data;
|
|
guchar code;
|
|
|
|
memset (key, 0, sizeof *key);
|
|
|
|
iter = g_settings_schema_get_value (schema, name);
|
|
|
|
key->schema = g_settings_schema_ref (schema);
|
|
key->default_value = g_variant_iter_next_value (iter);
|
|
endian_fixup (&key->default_value);
|
|
key->type = g_variant_get_type (key->default_value);
|
|
key->name = g_intern_string (name);
|
|
|
|
while (g_variant_iter_next (iter, "(y*)", &code, &data))
|
|
{
|
|
switch (code)
|
|
{
|
|
case 'l':
|
|
/* translation requested */
|
|
g_variant_get (data, "(y&s)", &key->lc_char, &key->unparsed);
|
|
break;
|
|
|
|
case 'e':
|
|
/* enumerated types... */
|
|
key->is_enum = TRUE;
|
|
goto choice;
|
|
|
|
case 'f':
|
|
/* flags... */
|
|
key->is_flags = TRUE;
|
|
goto choice;
|
|
|
|
choice: case 'c':
|
|
/* ..., choices, aliases */
|
|
key->strinfo = g_variant_get_fixed_array (data, &key->strinfo_length, sizeof (guint32));
|
|
break;
|
|
|
|
case 'r':
|
|
g_variant_get (data, "(**)", &key->minimum, &key->maximum);
|
|
endian_fixup (&key->minimum);
|
|
endian_fixup (&key->maximum);
|
|
break;
|
|
|
|
default:
|
|
g_warning ("unknown schema extension '%c'", code);
|
|
break;
|
|
}
|
|
|
|
g_variant_unref (data);
|
|
}
|
|
|
|
g_variant_iter_free (iter);
|
|
}
|
|
|
|
void
|
|
g_settings_schema_key_clear (GSettingsSchemaKey *key)
|
|
{
|
|
if (key->minimum)
|
|
g_variant_unref (key->minimum);
|
|
|
|
if (key->maximum)
|
|
g_variant_unref (key->maximum);
|
|
|
|
g_variant_unref (key->default_value);
|
|
|
|
g_settings_schema_unref (key->schema);
|
|
}
|
|
|
|
gboolean
|
|
g_settings_schema_key_type_check (GSettingsSchemaKey *key,
|
|
GVariant *value)
|
|
{
|
|
g_return_val_if_fail (value != NULL, FALSE);
|
|
|
|
return g_variant_is_of_type (value, key->type);
|
|
}
|
|
|
|
gboolean
|
|
g_settings_schema_key_range_check (GSettingsSchemaKey *key,
|
|
GVariant *value)
|
|
{
|
|
if (key->minimum == NULL && key->strinfo == NULL)
|
|
return TRUE;
|
|
|
|
if (g_variant_is_container (value))
|
|
{
|
|
gboolean ok = TRUE;
|
|
GVariantIter iter;
|
|
GVariant *child;
|
|
|
|
g_variant_iter_init (&iter, value);
|
|
while (ok && (child = g_variant_iter_next_value (&iter)))
|
|
{
|
|
ok = g_settings_schema_key_range_check (key, child);
|
|
g_variant_unref (child);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
if (key->minimum)
|
|
{
|
|
return g_variant_compare (key->minimum, value) <= 0 &&
|
|
g_variant_compare (value, key->maximum) <= 0;
|
|
}
|
|
|
|
return strinfo_is_string_valid (key->strinfo, key->strinfo_length,
|
|
g_variant_get_string (value, NULL));
|
|
}
|
|
|
|
GVariant *
|
|
g_settings_schema_key_range_fixup (GSettingsSchemaKey *key,
|
|
GVariant *value)
|
|
{
|
|
const gchar *target;
|
|
|
|
if (g_settings_schema_key_range_check (key, value))
|
|
return g_variant_ref (value);
|
|
|
|
if (key->strinfo == NULL)
|
|
return NULL;
|
|
|
|
if (g_variant_is_container (value))
|
|
{
|
|
GVariantBuilder builder;
|
|
GVariantIter iter;
|
|
GVariant *child;
|
|
|
|
g_variant_iter_init (&iter, value);
|
|
g_variant_builder_init (&builder, g_variant_get_type (value));
|
|
|
|
while ((child = g_variant_iter_next_value (&iter)))
|
|
{
|
|
GVariant *fixed;
|
|
|
|
fixed = g_settings_schema_key_range_fixup (key, child);
|
|
g_variant_unref (child);
|
|
|
|
if (fixed == NULL)
|
|
{
|
|
g_variant_builder_clear (&builder);
|
|
return NULL;
|
|
}
|
|
|
|
g_variant_builder_add_value (&builder, fixed);
|
|
g_variant_unref (fixed);
|
|
}
|
|
|
|
return g_variant_ref_sink (g_variant_builder_end (&builder));
|
|
}
|
|
|
|
target = strinfo_string_from_alias (key->strinfo, key->strinfo_length,
|
|
g_variant_get_string (value, NULL));
|
|
return target ? g_variant_ref_sink (g_variant_new_string (target)) : NULL;
|
|
}
|
|
|
|
|
|
GVariant *
|
|
g_settings_schema_key_get_translated_default (GSettingsSchemaKey *key)
|
|
{
|
|
const gchar *translated;
|
|
GError *error = NULL;
|
|
const gchar *domain;
|
|
GVariant *value;
|
|
|
|
domain = g_settings_schema_get_gettext_domain (key->schema);
|
|
|
|
if (key->lc_char == '\0')
|
|
/* translation not requested for this key */
|
|
return NULL;
|
|
|
|
if (key->lc_char == 't')
|
|
translated = g_dcgettext (domain, key->unparsed, LC_TIME);
|
|
else
|
|
translated = g_dgettext (domain, key->unparsed);
|
|
|
|
if (translated == key->unparsed)
|
|
/* the default value was not translated */
|
|
return NULL;
|
|
|
|
/* try to parse the translation of the unparsed default */
|
|
value = g_variant_parse (key->type, translated, NULL, NULL, &error);
|
|
|
|
if (value == NULL)
|
|
{
|
|
g_warning ("Failed to parse translated string '%s' for "
|
|
"key '%s' in schema '%s': %s", key->unparsed, key->name,
|
|
g_settings_schema_get_id (key->schema), error->message);
|
|
g_warning ("Using untranslated default instead.");
|
|
g_error_free (error);
|
|
}
|
|
|
|
else if (!g_settings_schema_key_range_check (key, value))
|
|
{
|
|
g_warning ("Translated default '%s' for key '%s' in schema '%s' "
|
|
"is outside of valid range", key->unparsed, key->name,
|
|
g_settings_schema_get_id (key->schema));
|
|
g_variant_unref (value);
|
|
value = NULL;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
gint
|
|
g_settings_schema_key_to_enum (GSettingsSchemaKey *key,
|
|
GVariant *value)
|
|
{
|
|
gboolean it_worked;
|
|
guint result;
|
|
|
|
it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length,
|
|
g_variant_get_string (value, NULL),
|
|
&result);
|
|
|
|
/* 'value' can only come from the backend after being filtered for validity,
|
|
* from the translation after being filtered for validity, or from the schema
|
|
* itself (which the schema compiler checks for validity). If this assertion
|
|
* fails then it's really a bug in GSettings or the schema compiler...
|
|
*/
|
|
g_assert (it_worked);
|
|
|
|
return result;
|
|
}
|
|
|
|
GVariant *
|
|
g_settings_schema_key_from_enum (GSettingsSchemaKey *key,
|
|
gint value)
|
|
{
|
|
const gchar *string;
|
|
|
|
string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, value);
|
|
|
|
if (string == NULL)
|
|
return NULL;
|
|
|
|
return g_variant_new_string (string);
|
|
}
|
|
|
|
guint
|
|
g_settings_schema_key_to_flags (GSettingsSchemaKey *key,
|
|
GVariant *value)
|
|
{
|
|
GVariantIter iter;
|
|
const gchar *flag;
|
|
guint result;
|
|
|
|
result = 0;
|
|
g_variant_iter_init (&iter, value);
|
|
while (g_variant_iter_next (&iter, "&s", &flag))
|
|
{
|
|
gboolean it_worked;
|
|
guint flag_value;
|
|
|
|
it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length, flag, &flag_value);
|
|
/* as in g_settings_to_enum() */
|
|
g_assert (it_worked);
|
|
|
|
result |= flag_value;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
GVariant *
|
|
g_settings_schema_key_from_flags (GSettingsSchemaKey *key,
|
|
guint value)
|
|
{
|
|
GVariantBuilder builder;
|
|
gint i;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
|
|
|
|
for (i = 0; i < 32; i++)
|
|
if (value & (1u << i))
|
|
{
|
|
const gchar *string;
|
|
|
|
string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, 1u << i);
|
|
|
|
if (string == NULL)
|
|
{
|
|
g_variant_builder_clear (&builder);
|
|
return NULL;
|
|
}
|
|
|
|
g_variant_builder_add (&builder, "s", string);
|
|
}
|
|
|
|
return g_variant_builder_end (&builder);
|
|
}
|