[glib] Bug 622124 - implement flags for GSettings
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] Bug 622124 - implement flags for GSettings
- Date: Thu, 1 Jul 2010 23:06:04 +0000 (UTC)
commit 5383c7110f793bfa749370cec9d708a6a5018751
Author: Ryan Lortie <desrt desrt ca>
Date: Thu Jul 1 18:58:56 2010 -0400
Bug 622124 - implement flags for GSettings
Add a <flags> tag to the schema file format and a flags='' attribute to
go along with. Add some extra test cases for those.
Add new g_settings_{get,set}_flags() calls and support binding to
GParamSpecFlags properties. Add test cases.
docs/reference/gio/gio-sections.txt | 2 +
gio/gio.symbols | 2 +
gio/gschema-compile.c | 161 +++++++++++++----
gio/gsettings-mapping.c | 65 +++++++-
gio/gsettings.c | 194 +++++++++++++++++++-
gio/gsettings.h | 5 +
gio/strinfo.c | 8 +
gio/tests/Makefile.am | 7 +
gio/tests/gschema-compile.c | 11 +-
gio/tests/gsettings.c | 80 ++++++++-
gio/tests/org.gtk.test.gschema.xml | 9 +
.../enum-with-repeated-nick.gschema.xml | 10 +
.../enum-with-repeated-value.gschema.xml | 10 +
.../schema-tests/flags-aliased-default.gschema.xml | 19 ++
.../schema-tests/flags-bad-default.gschema.xml | 16 ++
.../flags-more-than-one-bit.gschema.xml | 10 +
.../schema-tests/flags-with-enum-attr.gschema.xml | 14 ++
.../schema-tests/flags-with-enum-tag.gschema.xml | 14 ++
gio/tests/testenum.h | 9 +
19 files changed, 594 insertions(+), 52 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 420111b..4e10c7f 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -2168,6 +2168,8 @@ g_settings_get_strv
g_settings_set_strv
g_settings_get_enum
g_settings_set_enum
+g_settings_get_flags
+g_settings_set_flags
<SUBSECTION MappedGet>
GSettingsGetMapping
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 0dd8b79..8f43a54 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1477,6 +1477,8 @@ g_settings_get_boolean
g_settings_set_boolean
g_settings_get_enum
g_settings_set_enum
+g_settings_get_flags
+g_settings_set_flags
g_settings_sync
g_settings_list_items
g_settings_get_mapped
diff --git a/gio/gschema-compile.c b/gio/gschema-compile.c
index cc1e3e3..d7ae5f8 100644
--- a/gio/gschema-compile.c
+++ b/gio/gschema-compile.c
@@ -35,14 +35,32 @@
#include "strinfo.c"
/* Handling of <enum> {{{1 */
-typedef GString EnumState;
+typedef struct
+{
+ GString *strinfo;
+
+ gboolean is_flags;
+} EnumState;
static void
enum_state_free (gpointer data)
{
EnumState *state = data;
- g_string_free (state, TRUE);
+ g_string_free (state->strinfo, TRUE);
+ g_slice_free (EnumState, state);
+}
+
+EnumState *
+enum_state_new (gboolean is_flags)
+{
+ EnumState *state;
+
+ state = g_slice_new (EnumState);
+ state->strinfo = g_string_new (NULL);
+ state->is_flags = is_flags;
+
+ return state;
}
static void
@@ -58,12 +76,14 @@ enum_state_add_value (EnumState *state,
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
- "enum nick must be a minimum of 2 characters");
+ "nick must be a minimum of 2 characters");
return;
}
value = g_ascii_strtoll (valuestr, &end, 0);
- if (*end || value > G_MAXINT32 || value < G_MININT32)
+ if (*end || state->is_flags ?
+ (value > G_MAXUINT32 || value < 0) :
+ (value > G_MAXINT32 || value < G_MININT32))
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
@@ -71,15 +91,38 @@ enum_state_add_value (EnumState *state,
return;
}
- if (strinfo_builder_contains (state, nick))
+ if (strinfo_builder_contains (state->strinfo, nick))
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
- "<value nick='%s'> already specified", nick);
+ "<value nick='%s'/> already specified", nick);
return;
}
- strinfo_builder_append_item (state, nick, value);
+ if (strinfo_builder_contains_value (state->strinfo, value))
+ {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "value='%s' already specified", valuestr);
+ return;
+ }
+
+ if (state->is_flags && (value & (value - 1)))
+ {
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "flags values must have at most 1 bit set");
+ return;
+ }
+
+ /* Since we reject exact duplicates of value='' and we only allow one
+ * bit to be set, it's not possible to have overlaps.
+ *
+ * If we loosen the one-bit-set restriction we need an overlap check.
+ */
+
+
+ strinfo_builder_append_item (state->strinfo, nick, value);
}
static void
@@ -91,7 +134,7 @@ enum_state_end (EnumState **state_ptr,
state = *state_ptr;
*state_ptr = NULL;
- if (state->len == 0)
+ if (state->strinfo->len == 0)
g_set_error_literal (error,
G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
"<enum> must contain at least one <value>");
@@ -116,6 +159,7 @@ typedef struct
GString *strinfo;
gboolean is_enum;
+ gboolean is_flags;
GVariant *minimum;
GVariant *maximum;
@@ -131,16 +175,20 @@ typedef struct
static KeyState *
key_state_new (const gchar *type_string,
const gchar *gettext_domain,
- EnumState *enum_data)
+ gboolean is_enum,
+ gboolean is_flags,
+ GString *strinfo)
{
KeyState *state;
state = g_slice_new0 (KeyState);
state->type = g_variant_type_new (type_string);
state->have_gettext_domain = gettext_domain != NULL;
+ state->is_enum = is_enum;
+ state->is_flags = is_flags;
- if ((state->is_enum = (enum_data != NULL)))
- state->strinfo = g_string_new_len (enum_data->str, enum_data->len);
+ if (strinfo)
+ state->strinfo = g_string_new_len (strinfo->str, strinfo->len);
else
state->strinfo = g_string_new (NULL);
@@ -159,6 +207,7 @@ key_state_override (KeyState *state,
copy->strinfo = g_string_new_len (state->strinfo->str,
state->strinfo->len);
copy->is_enum = state->is_enum;
+ copy->is_flags = state->is_flags;
copy->is_override = TRUE;
if (state->minimum)
@@ -251,6 +300,12 @@ key_state_check_range (KeyState *state,
"<%s> is not a valid member of "
"the specified enumerated type", tag);
+ else if (state->is_flags)
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "<%s> contains string not in the "
+ "specified flags type", tag);
+
else
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
@@ -442,11 +497,11 @@ key_state_start_aliases (KeyState *state,
G_MARKUP_ERROR_INVALID_CONTENT,
"<aliases> already specified for this key");
- if (!state->is_enum && !state->has_choices)
+ if (!state->is_flags && !state->is_enum && !state->has_choices)
g_set_error_literal (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"<aliases> can only be specified for keys with "
- "enumerated types or after <choices>");
+ "enumerated or flags types or after <choices>");
}
static void
@@ -582,6 +637,7 @@ key_state_serialise (KeyState *state)
state->strinfo = NULL;
g_variant_builder_add (&builder, "(y au)",
+ state->is_flags ? 'f' :
state->is_enum ? 'e' : 'c',
array);
}
@@ -771,13 +827,15 @@ schema_state_add_child (SchemaState *state,
static KeyState *
schema_state_add_key (SchemaState *state,
GHashTable *enum_table,
+ GHashTable *flags_table,
const gchar *name,
const gchar *type_string,
const gchar *enum_type,
+ const gchar *flags_type,
GError **error)
{
- GString *enum_data;
SchemaState *node;
+ GString *strinfo;
KeyState *key;
if (state->list_of)
@@ -820,29 +878,37 @@ schema_state_add_key (SchemaState *state,
}
}
- if ((type_string == NULL) == (enum_type == NULL))
+ if ((type_string != NULL) + (enum_type != NULL) + (flags_type != NULL) != 1)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_MISSING_ATTRIBUTE,
- "exactly one of 'type' or 'enum' must "
+ "exactly one of 'type', 'enum' or 'flags' must "
"be specified as an attribute to <key>");
return NULL;
}
- if (enum_type != NULL)
+ if (type_string == NULL) /* flags or enums was specified */
{
- enum_data = g_hash_table_lookup (enum_table, enum_type);
+ EnumState *enum_state;
+
+ if (enum_type)
+ enum_state = g_hash_table_lookup (enum_table, enum_type);
+ else
+ enum_state = g_hash_table_lookup (flags_table, flags_type);
- if (enum_data == NULL)
+
+ if (enum_state == NULL)
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
- "<enum id='%s'> not (yet) defined.", enum_type);
+ "<%s id='%s'> not (yet) defined.",
+ flags_type ? "flags" : "enum",
+ flags_type ? flags_type : enum_type);
return NULL;
}
- g_assert (type_string == NULL);
- type_string = "s";
+ type_string = flags_type ? "as" : "s";
+ strinfo = enum_state->strinfo;
}
else
{
@@ -854,10 +920,11 @@ schema_state_add_key (SchemaState *state,
return NULL;
}
- enum_data = NULL;
+ strinfo = NULL;
}
- key = key_state_new (type_string, state->gettext_domain, enum_data);
+ key = key_state_new (type_string, state->gettext_domain,
+ enum_type != NULL, flags_type != NULL, strinfo);
g_hash_table_insert (state->keys, g_strdup (name), key);
return key;
@@ -922,13 +989,14 @@ override_state_end (KeyState **key_state,
typedef struct
{
GHashTable *schema_table; /* string -> SchemaState */
- GHashTable *enum_table; /* string -> GString */
+ GHashTable *flags_table; /* string -> EnumState */
+ GHashTable *enum_table; /* string -> EnumState */
gchar *schemalist_domain; /* the <schemalist> gettext domain */
SchemaState *schema_state; /* non-NULL when inside <schema> */
KeyState *key_state; /* non-NULL when inside <key> */
- GString *enum_state; /* non-NULL when inside <enum> */
+ EnumState *enum_state; /* non-NULL when inside <enum> */
GString *string; /* non-NULL when accepting text */
} ParseState;
@@ -1047,18 +1115,22 @@ parse_state_start_schema (ParseState *state,
static void
parse_state_start_enum (ParseState *state,
const gchar *id,
+ gboolean is_flags,
GError **error)
{
- if (g_hash_table_lookup (state->enum_table, id))
+ GHashTable *table = is_flags ? state->flags_table : state->enum_table;
+
+ if (g_hash_table_lookup (table, id))
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
- "<enum id='%s'> already specified", id);
+ "<%s id='%s'> already specified",
+ is_flags ? "flags" : "enum", id);
return;
}
- state->enum_state = g_string_new (NULL);
- g_hash_table_insert (state->enum_table, g_strdup (id), state->enum_state);
+ state->enum_state = enum_state_new (is_flags);
+ g_hash_table_insert (table, g_strdup (id), state->enum_state);
}
/* GMarkup Parser Functions {{{1 */
@@ -1117,12 +1189,19 @@ start_element (GMarkupParseContext *context,
return;
}
- else if (strcmp (element_name, "enum") == 0 ||
- strcmp (element_name, "flags") == 0)
+ else if (strcmp (element_name, "enum") == 0)
{
const gchar *id;
if (COLLECT (STRING, "id", &id))
- parse_state_start_enum (state, id, error);
+ parse_state_start_enum (state, id, FALSE, error);
+ return;
+ }
+
+ else if (strcmp (element_name, "flags") == 0)
+ {
+ const gchar *id;
+ if (COLLECT (STRING, "id", &id))
+ parse_state_start_enum (state, id, TRUE, error);
return;
}
}
@@ -1133,16 +1212,19 @@ start_element (GMarkupParseContext *context,
{
if (strcmp (element_name, "key") == 0)
{
- const gchar *name, *type_string, *enum_type;
+ const gchar *name, *type_string, *enum_type, *flags_type;
- if (COLLECT (STRING, "name", &name,
- OPTIONAL | STRING, "type", &type_string,
- OPTIONAL | STRING, "enum", &enum_type))
+ if (COLLECT (STRING, "name", &name,
+ OPTIONAL | STRING, "type", &type_string,
+ OPTIONAL | STRING, "enum", &enum_type,
+ OPTIONAL | STRING, "flags", &flags_type))
state->key_state = schema_state_add_key (state->schema_state,
state->enum_table,
+ state->flags_table,
name, type_string,
- enum_type, error);
+ enum_type, flags_type,
+ error);
return;
}
else if (strcmp (element_name, "child") == 0)
@@ -1481,6 +1563,9 @@ parse_gschema_files (gchar **files,
state.enum_table = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, enum_state_free);
+ state.flags_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, enum_state_free);
+
state.schema_table = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, schema_state_free);
diff --git a/gio/gsettings-mapping.c b/gio/gsettings-mapping.c
index 52a9b21..d85fae1 100644
--- a/gio/gsettings-mapping.c
+++ b/gio/gsettings-mapping.c
@@ -392,6 +392,34 @@ g_settings_set_mapping (const GValue *value,
return NULL;
}
+ else if (G_VALUE_HOLDS_FLAGS (value))
+ {
+ GVariantBuilder builder;
+ GFlagsValue *flagsval;
+ GFlagsClass *fclass;
+ guint flags;
+
+ fclass = g_type_class_peek (G_VALUE_TYPE (value));
+ flags = g_value_get_flags (value);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
+ while (flags)
+ {
+ flagsval = g_flags_get_first_value (fclass, flags);
+
+ if (flagsval == NULL)
+ {
+ g_variant_builder_clear (&builder);
+ return NULL;
+ }
+
+ g_variant_builder_add (&builder, "s", flagsval->value_nick);
+ flags &= ~flagsval->value;
+ }
+
+ return g_variant_builder_end (&builder);
+ }
+
type_string = g_variant_type_dup_string (expected_type);
g_critical ("No GSettings bind handler for type \"%s\".", type_string);
g_free (type_string);
@@ -447,8 +475,7 @@ g_settings_get_mapping (GValue *value,
return TRUE;
}
- else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING) &&
- G_VALUE_HOLDS_ENUM (value))
+ else if (G_VALUE_HOLDS_ENUM (value))
{
GEnumClass *eclass;
GEnumValue *evalue;
@@ -469,6 +496,38 @@ g_settings_get_mapping (GValue *value,
return FALSE;
}
}
+ else if (g_variant_is_of_type (variant, G_VARIANT_TYPE ("as")))
+ {
+ if (G_VALUE_HOLDS_FLAGS (value))
+ {
+ GFlagsClass *fclass;
+ GFlagsValue *fvalue;
+ const gchar *nick;
+ GVariantIter iter;
+ guint flags = 0;
+
+ fclass = g_type_class_peek (G_VALUE_TYPE (value));
+
+ g_variant_iter_init (&iter, variant);
+ while (g_variant_iter_next (&iter, "&s", &nick))
+ {
+ fvalue = g_flags_get_value_by_nick (fclass, nick);
+
+ if (fvalue)
+ flags |= fvalue->value;
+
+ else
+ {
+ g_warning ("Unable to lookup flags nick '%s' via GType\n",
+ nick);
+ return FALSE;
+ }
+ }
+
+ g_value_set_flags (value, flags);
+ return TRUE;
+ }
+ }
else if (g_variant_is_of_type (variant, G_VARIANT_TYPE ("ay")))
{
g_value_set_string (value, g_variant_get_byte_array (variant, NULL));
@@ -512,6 +571,8 @@ g_settings_mapping_is_compatible (GType gvalue_type,
g_variant_type_equal (variant_type, G_VARIANT_TYPE_SIGNATURE));
else if (G_TYPE_IS_ENUM (gvalue_type))
ok = g_variant_type_equal (variant_type, G_VARIANT_TYPE_STRING);
+ else if (G_TYPE_IS_FLAGS (gvalue_type))
+ ok = g_variant_type_equal (variant_type, G_VARIANT_TYPE ("as"));
return ok;
}
diff --git a/gio/gsettings.c b/gio/gsettings.c
index 5a5ceed..e5ac148 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -831,7 +831,7 @@ g_settings_new_with_backend_and_path (const gchar *schema,
NULL);
}
-/* Internal read/write utilities, enum conversion, validation {{{1 */
+/* Internal read/write utilities, enum/flags conversion, validation {{{1 */
typedef struct
{
GSettings *settings;
@@ -839,7 +839,9 @@ typedef struct
GSettingsSchema *schema;
- gboolean is_enum;
+ guint is_flags : 1;
+ guint is_enum : 1;
+
const guint32 *strinfo;
gsize strinfo_length;
@@ -879,9 +881,16 @@ g_settings_get_key_info (GSettingsKeyInfo *info,
break;
case 'e':
- /* enumerated types, ... */
+ /* enumerated types... */
info->is_enum = TRUE;
- case 'c':
+ goto choice;
+
+ case 'f':
+ /* flags... */
+ info->is_flags = TRUE;
+ goto choice;
+
+ choice: case 'c':
/* ..., choices, aliases */
info->strinfo = g_variant_get_fixed_array (data,
&info->strinfo_length,
@@ -956,7 +965,7 @@ g_settings_range_check (GSettingsKeyInfo *info,
g_variant_iter_init (&iter, value);
while (ok && (child = g_variant_iter_next_value (&iter)))
{
- ok = g_settings_range_check (info, value);
+ ok = g_settings_range_check (info, child);
g_variant_unref (child);
}
@@ -992,6 +1001,7 @@ g_settings_range_fixup (GSettingsKeyInfo *info,
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)))
@@ -1123,7 +1133,64 @@ g_settings_from_enum (GSettingsKeyInfo *info,
if (string == NULL)
return NULL;
- return g_variant_ref_sink (g_variant_new_string (string));
+ return g_variant_new_string (string);
+}
+
+static guint
+g_settings_to_flags (GSettingsKeyInfo *info,
+ 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 (info->strinfo,
+ info->strinfo_length,
+ flag, &flag_value);
+ /* as in g_settings_to_enum() */
+ g_assert (it_worked);
+
+ result |= flag_value;
+ }
+
+ return result;
+}
+
+static GVariant *
+g_settings_from_flags (GSettingsKeyInfo *info,
+ 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 (info->strinfo,
+ info->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);
}
/* Public Get/Set API {{{1 (get, get_value, set, set_value, get_mapped) */
@@ -1235,7 +1302,7 @@ g_settings_get_enum (GSettings *settings,
* schema for @settings or is not marked as an enumerated type, or for
* @value not to be a valid value for the named type.
*
- * After performing the write, accessing @key directly
+ * After performing the write, accessing @key directly with
* g_settings_get_string() will return the 'nick' associated with
* @value.
**/
@@ -1271,7 +1338,118 @@ g_settings_set_enum (GSettings *settings,
success = g_settings_write_to_backend (&info, variant);
g_settings_free_key_info (&info);
- g_variant_unref (variant);
+
+ return success;
+}
+
+/**
+ * g_settings_get_flags:
+ * @settings: a #GSettings object
+ * @key: the key to get the value for
+ * @returns: the flags value
+ *
+ * Gets the value that is stored in @settings for @key and converts it
+ * to the flags value that it represents.
+ *
+ * In order to use this function the type of the value must be an array
+ * of strings and it must be marked in the schema file as an flags type.
+ *
+ * It is a programmer error to give a @key that isn't contained in the
+ * schema for @settings or is not marked as a flags type.
+ *
+ * If the value stored in the configuration database is not a valid
+ * value for the flags type then this function will return the default
+ * value.
+ *
+ * Since: 2.26
+ **/
+guint
+g_settings_get_flags (GSettings *settings,
+ const gchar *key)
+{
+ GSettingsKeyInfo info;
+ GVariant *value;
+ guint result;
+
+ g_return_val_if_fail (G_IS_SETTINGS (settings), -1);
+ g_return_val_if_fail (key != NULL, -1);
+
+ g_settings_get_key_info (&info, settings, key);
+
+ if (!info.is_flags)
+ {
+ g_critical ("g_settings_get_flags() called on key `%s' which is not "
+ "associated with a flags type", info.key);
+ g_settings_free_key_info (&info);
+ return -1;
+ }
+
+ value = g_settings_read_from_backend (&info);
+
+ if (value == NULL)
+ value = g_settings_get_translated_default (&info);
+
+ if (value == NULL)
+ value = g_variant_ref (info.default_value);
+
+ result = g_settings_to_flags (&info, value);
+ g_settings_free_key_info (&info);
+ g_variant_unref (value);
+
+ return result;
+}
+
+/**
+ * g_settings_set_flags:
+ * @settings: a #GSettings object
+ * @key: a key, within @settings
+ * @value: a flags value
+ * @returns: %TRUE, if the set succeeds
+ *
+ * Looks up the flags type nicks for the bits specified by @value, puts
+ * them in an array of strings and writes the array to @key, withing
+ * @settings.
+ *
+ * It is a programmer error to give a @key that isn't contained in the
+ * schema for @settings or is not marked as a flags type, or for @value
+ * to contain any bits that are not value for the named type.
+ *
+ * After performing the write, accessing @key directly with
+ * g_settings_get_strv() will return an array of 'nicks'; one for each
+ * bit in @value.
+ **/
+gboolean
+g_settings_set_flags (GSettings *settings,
+ const gchar *key,
+ guint value)
+{
+ GSettingsKeyInfo info;
+ GVariant *variant;
+ gboolean success;
+
+ g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+
+ g_settings_get_key_info (&info, settings, key);
+
+ if (!info.is_flags)
+ {
+ g_critical ("g_settings_set_flags() called on key `%s' which is not "
+ "associated with a flags type", info.key);
+ return FALSE;
+ }
+
+ if (!(variant = g_settings_from_flags (&info, value)))
+ {
+ g_critical ("g_settings_set_flags(): invalid flags value 0x%08x "
+ "for key `%s' in schema `%s'. Doing nothing.",
+ value, info.key, info.settings->priv->schema_name);
+ g_settings_free_key_info (&info);
+ return FALSE;
+ }
+
+ success = g_settings_write_to_backend (&info, variant);
+ g_settings_free_key_info (&info);
return success;
}
diff --git a/gio/gsettings.h b/gio/gsettings.h
index f0da640..70dbbd8 100644
--- a/gio/gsettings.h
+++ b/gio/gsettings.h
@@ -126,6 +126,11 @@ gint g_settings_get_enum (GSettin
gboolean g_settings_set_enum (GSettings *settings,
const gchar *key,
gint value);
+guint g_settings_get_flags (GSettings *settings,
+ const gchar *key);
+gboolean g_settings_set_flags (GSettings *settings,
+ const gchar *key,
+ guint value);
GSettings * g_settings_get_child (GSettings *settings,
const gchar *name);
diff --git a/gio/strinfo.c b/gio/strinfo.c
index f762fc5..84e4acf 100644
--- a/gio/strinfo.c
+++ b/gio/strinfo.c
@@ -309,3 +309,11 @@ strinfo_builder_contains (GString *builder,
strinfo_find_string ((const guint32 *) builder->str,
builder->len / 4, string, TRUE) != -1;
}
+
+G_GNUC_UNUSED static gboolean
+strinfo_builder_contains_value (GString *builder,
+ guint value)
+{
+ return strinfo_string_from_enum ((const guint32 *) builder->str,
+ builder->len / 4, value) != NULL;
+}
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index d95c618..9b3286a 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -300,8 +300,15 @@ schema_tests = \
schema-tests/enum-with-choice.gschema.xml \
schema-tests/enum-with-invalid-alias.gschema.xml \
schema-tests/enum-with-repeated-alias.gschema.xml \
+ schema-tests/enum-with-repeated-nick.gschema.xml \
+ schema-tests/enum-with-repeated-value.gschema.xml \
schema-tests/enum-with-shadow-alias.gschema.xml \
schema-tests/enum.gschema.xml \
+ schema-tests/flags-aliased-default.gschema.xml \
+ schema-tests/flags-bad-default.gschema.xml \
+ schema-tests/flags-more-than-one-bit.gschema.xml \
+ schema-tests/flags-with-enum-attr.gschema.xml \
+ schema-tests/flags-with-enum-tag.gschema.xml \
schema-tests/extend-and-shadow-indirect.gschema.xml \
schema-tests/extend-and-shadow.gschema.xml \
schema-tests/extend-missing.gschema.xml \
diff --git a/gio/tests/gschema-compile.c b/gio/tests/gschema-compile.c
index 983a1c8..930d352 100644
--- a/gio/tests/gschema-compile.c
+++ b/gio/tests/gschema-compile.c
@@ -71,6 +71,8 @@ static const SchemaTest tests[] = {
{ "enum-with-aliases", NULL, NULL },
{ "enum-with-invalid-alias", NULL, "*'banger' is not in enumerated type*" },
{ "enum-with-repeated-alias", NULL, "*<alias value='sausages'/> already specified*" },
+ { "enum-with-repeated-nick", NULL, "*<value nick='spam'/> already specified*" },
+ { "enum-with-repeated-value", NULL, "*value='1' already specified*" },
{ "enum-with-chained-alias", NULL, "*'sausages' is not in enumerated type*" },
{ "enum-with-shadow-alias", NULL, "*'mash' is already a member of the enum*" },
{ "enum-with-choice", NULL, "*<choices> can not be specified*" },
@@ -80,7 +82,7 @@ static const SchemaTest tests[] = {
{ "bad-choice", NULL, "*<default> contains string not in <choices>*" },
{ "choice-bad", NULL, "*<default> contains string not in <choices>*" },
{ "choice-badtype", NULL, "*<choices> not allowed for keys of type 'i'*" },
- { "bare-alias", NULL, "*enumerated types or after <choices>*" },
+ { "bare-alias", NULL, "*enumerated or flags types or after <choices>*" },
{ "choice-alias", NULL, NULL },
{ "default-in-aliases", NULL, "*<default> contains string not in <choices>*" },
{ "choice-invalid-alias", NULL, "*'befor' is not in <choices>*" },
@@ -109,7 +111,12 @@ static const SchemaTest tests[] = {
{ "override-range-error", NULL, "*<override> is not contained in the specified range*"},
{ "override-then-key", NULL, "*shadows <key name='foo'> in <schema id='base'>*" },
{ "override-twice", NULL, "*<override name='foo'> already specified*" },
- { "override-type-error", NULL, "*invalid character in number*" }
+ { "override-type-error", NULL, "*invalid character in number*" },
+ { "flags-aliased-default", NULL, "*<default> * not in the specified flags type*" },
+ { "flags-bad-default", NULL, "*<default> * not in the specified flags type*" },
+ { "flags-more-than-one-bit", NULL, "*flags values must have at most 1 bit set*" },
+ { "flags-with-enum-attr", NULL, "*<enum id='flags'> not (yet) defined*" },
+ { "flags-with-enum-tag", NULL, "*<flags id='flags'> not (yet) defined*" }
};
int
diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
index cdfa9b8..0e43506 100644
--- a/gio/tests/gsettings.c
+++ b/gio/tests/gsettings.c
@@ -1249,8 +1249,6 @@ test_strinfo (void)
g_assert (!strinfo_is_string_valid (strinfo, length, "quux"));
}
-
-
static void
test_enums (void)
{
@@ -1276,6 +1274,11 @@ test_enums (void)
g_settings_set_string (settings, "test", "qux");
g_test_trap_assert_failed ();
g_test_trap_assert_stderr ("*g_settings_range_check*");
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ g_settings_get_flags (settings, "test");
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*not associated with a flags*");
}
str = g_settings_get_string (settings, "test");
@@ -1304,6 +1307,78 @@ test_enums (void)
}
static void
+test_flags (void)
+{
+ GSettings *settings, *direct;
+ gchar **strv;
+ gchar *str;
+
+ settings = g_settings_new ("org.gtk.test.enums");
+ direct = g_settings_new ("org.gtk.test.enums.direct");
+
+ if (!backend_set)
+ {
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ g_settings_get_flags (direct, "test");
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*not associated with a flags*");
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ g_settings_set_flags (settings, "f-test", 0x42);
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*invalid flags value 0x00000042*");
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ g_settings_set_strv (settings, "f-test",
+ (const gchar **) g_strsplit ("rock", ",", 0));
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*g_settings_range_check*");
+
+ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR))
+ g_settings_get_enum (settings, "f-test");
+ g_test_trap_assert_failed ();
+ g_test_trap_assert_stderr ("*not associated with an enum*");
+ }
+
+ strv = g_settings_get_strv (settings, "f-test");
+ str = g_strjoinv (",", strv);
+ g_assert_cmpstr (str, ==, "");
+ g_strfreev (strv);
+ g_free (str);
+
+ g_settings_set_flags (settings, "f-test",
+ TEST_FLAGS_WALKING | TEST_FLAGS_TALKING);
+
+ strv = g_settings_get_strv (settings, "f-test");
+ str = g_strjoinv (",", strv);
+ g_assert_cmpstr (str, ==, "talking,walking");
+ g_strfreev (strv);
+ g_free (str);
+
+ g_assert_cmpint (g_settings_get_flags (settings, "f-test"), ==,
+ TEST_FLAGS_WALKING | TEST_FLAGS_TALKING);
+
+ strv = g_strsplit ("speaking,laughing", ",", 0);
+ g_settings_set_strv (direct, "f-test", (const gchar **) strv);
+ g_strfreev (strv);
+
+ strv = g_settings_get_strv (direct, "f-test");
+ str = g_strjoinv (",", strv);
+ g_assert_cmpstr (str, ==, "speaking,laughing");
+ g_strfreev (strv);
+ g_free (str);
+
+ strv = g_settings_get_strv (settings, "f-test");
+ str = g_strjoinv (",", strv);
+ g_assert_cmpstr (str, ==, "talking,laughing");
+ g_strfreev (strv);
+ g_free (str);
+
+ g_assert_cmpint (g_settings_get_flags (settings, "f-test"), ==,
+ TEST_FLAGS_TALKING | TEST_FLAGS_LAUGHING);
+}
+
+static void
test_range (void)
{
GSettings *settings, *direct;
@@ -1408,6 +1483,7 @@ main (int argc, char *argv[])
g_test_add_func ("/gsettings/child-schema", test_child_schema);
g_test_add_func ("/gsettings/strinfo", test_strinfo);
g_test_add_func ("/gsettings/enums", test_enums);
+ g_test_add_func ("/gsettings/flags", test_flags);
g_test_add_func ("/gsettings/range", test_range);
result = g_test_run ();
diff --git a/gio/tests/org.gtk.test.gschema.xml b/gio/tests/org.gtk.test.gschema.xml
index a7c6c13..c7d3d97 100644
--- a/gio/tests/org.gtk.test.gschema.xml
+++ b/gio/tests/org.gtk.test.gschema.xml
@@ -99,9 +99,18 @@
<alias value='qux' target='quux'/>
</aliases>
</key>
+ <key name='f-test' flags='org.gtk.test.TestFlags'>
+ <default>[]</default>
+ <aliases>
+ <alias value='speaking' target='talking'/>
+ </aliases>
+ </key>
</schema>
<schema id='org.gtk.test.enums.direct' path='/tests/enums/'>
+ <key name='f-test' type='as'>
+ <default>[]</default>
+ </key>
<key name='test' type='s'>
<default>'bar'</default>
</key>
diff --git a/gio/tests/schema-tests/enum-with-repeated-nick.gschema.xml b/gio/tests/schema-tests/enum-with-repeated-nick.gschema.xml
new file mode 100644
index 0000000..8910711
--- /dev/null
+++ b/gio/tests/schema-tests/enum-with-repeated-nick.gschema.xml
@@ -0,0 +1,10 @@
+<schemalist>
+ <enum id='org.gtk.test.MyEnum'>
+ <value nick='nospam' value='0'/>
+ <value nick='spam' value='1'/>
+ <value nick='ham' value='2'/>
+ <value nick='eggs' value='3'/>
+ <value nick='bangers' value='4'/>
+ <value nick='spam' value='5'/>
+ </enum>
+</schemalist>
diff --git a/gio/tests/schema-tests/enum-with-repeated-value.gschema.xml b/gio/tests/schema-tests/enum-with-repeated-value.gschema.xml
new file mode 100644
index 0000000..a357d11
--- /dev/null
+++ b/gio/tests/schema-tests/enum-with-repeated-value.gschema.xml
@@ -0,0 +1,10 @@
+<schemalist>
+ <enum id='org.gtk.test.MyEnum'>
+ <value nick='nospam' value='0'/>
+ <value nick='spam' value='1'/>
+ <value nick='ham' value='2'/>
+ <value nick='eggs' value='3'/>
+ <value nick='bangers' value='4'/>
+ <value nick='mash' value='1'/>
+ </enum>
+</schemalist>
diff --git a/gio/tests/schema-tests/flags-aliased-default.gschema.xml b/gio/tests/schema-tests/flags-aliased-default.gschema.xml
new file mode 100644
index 0000000..c36bd62
--- /dev/null
+++ b/gio/tests/schema-tests/flags-aliased-default.gschema.xml
@@ -0,0 +1,19 @@
+<schemalist>
+ <flags id='flags'>
+ <value nick='none' value='0'/>
+ <value nick='mourning' value='1'/>
+ <value nick='laughing' value='2'/>
+ <value nick='talking' value='4'/>
+ <value nick='walking' value='8'/>
+ </flags>
+
+ <schema id='xyz'>
+ <key name='abc' flags='flags'>
+ <aliases>
+ <alias value='speaking' target='talking'/>
+ </aliases>
+ <default>['speaking']</default>
+ </key>
+ </schema>
+</schemalist>
+
diff --git a/gio/tests/schema-tests/flags-bad-default.gschema.xml b/gio/tests/schema-tests/flags-bad-default.gschema.xml
new file mode 100644
index 0000000..d412057
--- /dev/null
+++ b/gio/tests/schema-tests/flags-bad-default.gschema.xml
@@ -0,0 +1,16 @@
+<schemalist>
+ <flags id='flags'>
+ <value nick='none' value='0'/>
+ <value nick='mourning' value='1'/>
+ <value nick='laughing' value='2'/>
+ <value nick='talking' value='4'/>
+ <value nick='walking' value='8'/>
+ </flags>
+
+ <schema id='xyz'>
+ <key name='abc' flags='flags'>
+ <default>['speaking']</default>
+ </key>
+ </schema>
+</schemalist>
+
diff --git a/gio/tests/schema-tests/flags-more-than-one-bit.gschema.xml b/gio/tests/schema-tests/flags-more-than-one-bit.gschema.xml
new file mode 100644
index 0000000..ce2faaf
--- /dev/null
+++ b/gio/tests/schema-tests/flags-more-than-one-bit.gschema.xml
@@ -0,0 +1,10 @@
+<schemalist>
+ <flags id='flags'>
+ <value nick='none' value='0'/>
+ <value nick='mourning' value='1'/>
+ <value nick='laughing' value='2'/>
+ <value nick='talking' value='4'/>
+ <value nick='walking' value='24'/>
+ </flags>
+</schemalist>
+
diff --git a/gio/tests/schema-tests/flags-with-enum-attr.gschema.xml b/gio/tests/schema-tests/flags-with-enum-attr.gschema.xml
new file mode 100644
index 0000000..a48547f
--- /dev/null
+++ b/gio/tests/schema-tests/flags-with-enum-attr.gschema.xml
@@ -0,0 +1,14 @@
+<schemalist>
+ <flags id='flags'>
+ <value nick='none' value='0'/>
+ <value nick='mourning' value='1'/>
+ <value nick='laughing' value='2'/>
+ <value nick='talking' value='4'/>
+ <value nick='walking' value='8'/>
+ </flags>
+
+ <schema id='foo'>
+ <key name='xyz' enum='flags'/>
+ </schema>
+</schemalist>
+
diff --git a/gio/tests/schema-tests/flags-with-enum-tag.gschema.xml b/gio/tests/schema-tests/flags-with-enum-tag.gschema.xml
new file mode 100644
index 0000000..4b2fb90
--- /dev/null
+++ b/gio/tests/schema-tests/flags-with-enum-tag.gschema.xml
@@ -0,0 +1,14 @@
+<schemalist>
+ <enum id='flags'>
+ <value nick='none' value='0'/>
+ <value nick='mourning' value='1'/>
+ <value nick='laughing' value='2'/>
+ <value nick='talking' value='4'/>
+ <value nick='walking' value='8'/>
+ </enum>
+
+ <schema id='foo'>
+ <key name='xyz' flags='flags'/>
+ </schema>
+</schemalist>
+
diff --git a/gio/tests/testenum.h b/gio/tests/testenum.h
index 4329860..def8713 100644
--- a/gio/tests/testenum.h
+++ b/gio/tests/testenum.h
@@ -5,3 +5,12 @@ typedef enum
TEST_ENUM_BAZ,
TEST_ENUM_QUUX
} TestEnum;
+
+typedef enum
+{
+ TEST_FLAGS_NONE = 0,
+ TEST_FLAGS_MOURNING = (1 << 0),
+ TEST_FLAGS_LAUGHING = (1 << 1),
+ TEST_FLAGS_TALKING = (1 << 2),
+ TEST_FLAGS_WALKING = (1 << 3)
+} TestFlags;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]