mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-11 23:16:14 +01:00
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:
parent
69fe50c116
commit
5383c7110f
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
194
gio/gsettings.c
194
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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 ();
|
||||
|
@ -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>
|
||||
|
10
gio/tests/schema-tests/enum-with-repeated-nick.gschema.xml
Normal file
10
gio/tests/schema-tests/enum-with-repeated-nick.gschema.xml
Normal 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>
|
10
gio/tests/schema-tests/enum-with-repeated-value.gschema.xml
Normal file
10
gio/tests/schema-tests/enum-with-repeated-value.gschema.xml
Normal 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>
|
19
gio/tests/schema-tests/flags-aliased-default.gschema.xml
Normal file
19
gio/tests/schema-tests/flags-aliased-default.gschema.xml
Normal 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>
|
||||
|
16
gio/tests/schema-tests/flags-bad-default.gschema.xml
Normal file
16
gio/tests/schema-tests/flags-bad-default.gschema.xml
Normal 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>
|
||||
|
10
gio/tests/schema-tests/flags-more-than-one-bit.gschema.xml
Normal file
10
gio/tests/schema-tests/flags-more-than-one-bit.gschema.xml
Normal 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>
|
||||
|
14
gio/tests/schema-tests/flags-with-enum-attr.gschema.xml
Normal file
14
gio/tests/schema-tests/flags-with-enum-attr.gschema.xml
Normal 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>
|
||||
|
14
gio/tests/schema-tests/flags-with-enum-tag.gschema.xml
Normal file
14
gio/tests/schema-tests/flags-with-enum-tag.gschema.xml
Normal 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>
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user