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.
This commit is contained in:
Ryan Lortie 2010-07-01 18:58:56 -04:00
parent 69fe50c116
commit 5383c7110f
19 changed files with 594 additions and 52 deletions

View File

@ -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

View File

@ -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

View File

@ -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_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,
"<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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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 \

View File

@ -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

View File

@ -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 ();

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;