From 5383c7110f793bfa749370cec9d708a6a5018751 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Thu, 1 Jul 2010 18:58:56 -0400 Subject: [PATCH] Bug 622124 - implement flags for GSettings Add a 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 + .../flags-aliased-default.gschema.xml | 19 ++ .../flags-bad-default.gschema.xml | 16 ++ .../flags-more-than-one-bit.gschema.xml | 10 + .../flags-with-enum-attr.gschema.xml | 14 ++ .../flags-with-enum-tag.gschema.xml | 14 ++ gio/tests/testenum.h | 9 + 19 files changed, 594 insertions(+), 52 deletions(-) create mode 100644 gio/tests/schema-tests/enum-with-repeated-nick.gschema.xml create mode 100644 gio/tests/schema-tests/enum-with-repeated-value.gschema.xml create mode 100644 gio/tests/schema-tests/flags-aliased-default.gschema.xml create mode 100644 gio/tests/schema-tests/flags-bad-default.gschema.xml create mode 100644 gio/tests/schema-tests/flags-more-than-one-bit.gschema.xml create mode 100644 gio/tests/schema-tests/flags-with-enum-attr.gschema.xml create mode 100644 gio/tests/schema-tests/flags-with-enum-tag.gschema.xml diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 420111b29..4e10c7fae 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 GSettingsGetMapping diff --git a/gio/gio.symbols b/gio/gio.symbols index 0dd8b79a9..8f43a5407 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 cc1e3e3cb..d7ae5f88c 100644 --- a/gio/gschema-compile.c +++ b/gio/gschema-compile.c @@ -35,14 +35,32 @@ #include "strinfo.c" /* Handling of {{{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, - " already specified", nick); + " 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, " must contain at least one "); @@ -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, " 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, " can only be specified for keys with " - "enumerated types or after "); + "enumerated or flags types or after "); } 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 "); 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_data == NULL) + 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_state == NULL) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, - " 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 gettext domain */ SchemaState *schema_state; /* non-NULL when inside */ KeyState *key_state; /* non-NULL when inside */ - GString *enum_state; /* non-NULL when inside */ + EnumState *enum_state; /* non-NULL when inside */ 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, - " 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 52a9b218f..d85fae187 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 5a5ceed81..e5ac14879 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 f0da640ab..70dbbd8e1 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 f762fc5c3..84e4acff3 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 d95c6184a..9b3286a05 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 983a1c88b..930d3527b 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, "* already specified*" }, + { "enum-with-repeated-nick", NULL, "* 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, "* can not be specified*" }, @@ -80,7 +82,7 @@ static const SchemaTest tests[] = { { "bad-choice", NULL, "* contains string not in *" }, { "choice-bad", NULL, "* contains string not in *" }, { "choice-badtype", NULL, "* not allowed for keys of type 'i'*" }, - { "bare-alias", NULL, "*enumerated types or after *" }, + { "bare-alias", NULL, "*enumerated or flags types or after *" }, { "choice-alias", NULL, NULL }, { "default-in-aliases", NULL, "* contains string not in *" }, { "choice-invalid-alias", NULL, "*'befor' is not in *" }, @@ -109,7 +111,12 @@ static const SchemaTest tests[] = { { "override-range-error", NULL, "* is not contained in the specified range*"}, { "override-then-key", NULL, "*shadows in *" }, { "override-twice", NULL, "* already specified*" }, - { "override-type-error", NULL, "*invalid character in number*" } + { "override-type-error", NULL, "*invalid character in number*" }, + { "flags-aliased-default", NULL, "* * not in the specified flags type*" }, + { "flags-bad-default", NULL, "* * 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, "* not (yet) defined*" }, + { "flags-with-enum-tag", NULL, "* not (yet) defined*" } }; int diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c index cdfa9b828..0e435069e 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"); @@ -1303,6 +1306,78 @@ test_enums (void) g_assert_cmpint (g_settings_get_enum (settings, "test"), ==, TEST_ENUM_QUUX); } +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) { @@ -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 a7c6c13d8..c7d3d974a 100644 --- a/gio/tests/org.gtk.test.gschema.xml +++ b/gio/tests/org.gtk.test.gschema.xml @@ -99,9 +99,18 @@ + + [] + + + + + + [] + 'bar' 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 000000000..8910711e4 --- /dev/null +++ b/gio/tests/schema-tests/enum-with-repeated-nick.gschema.xml @@ -0,0 +1,10 @@ + + + + + + + + + + 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 000000000..a357d11e0 --- /dev/null +++ b/gio/tests/schema-tests/enum-with-repeated-value.gschema.xml @@ -0,0 +1,10 @@ + + + + + + + + + + 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 000000000..c36bd6291 --- /dev/null +++ b/gio/tests/schema-tests/flags-aliased-default.gschema.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + ['speaking'] + + + + 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 000000000..d412057e1 --- /dev/null +++ b/gio/tests/schema-tests/flags-bad-default.gschema.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + ['speaking'] + + + + 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 000000000..ce2faaf21 --- /dev/null +++ b/gio/tests/schema-tests/flags-more-than-one-bit.gschema.xml @@ -0,0 +1,10 @@ + + + + + + + + + + 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 000000000..a48547f1c --- /dev/null +++ b/gio/tests/schema-tests/flags-with-enum-attr.gschema.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + 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 000000000..4b2fb9024 --- /dev/null +++ b/gio/tests/schema-tests/flags-with-enum-tag.gschema.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/gio/tests/testenum.h b/gio/tests/testenum.h index 432986000..def871387 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;