[glib/enums] enums



commit a586d0ea64f2a357cc617d9f550b3ae1e0111201
Author: Ryan Lortie <desrt desrt ca>
Date:   Fri Jun 11 15:10:49 2010 -0400

    enums

 gio/gio.symbols                    |    2 +
 gio/gschema-compile.c              |  205 +++++++++++++++++++++----
 gio/gsettings-mapping.c            |   24 +++-
 gio/gsettings.c                    |  304 ++++++++++++++++++++++++++++--------
 gio/gsettings.h                    |    6 +-
 gio/gsettingsbackend.c             |   27 +++-
 gio/tests/org.gtk.test.gschema.xml |   14 ++
 7 files changed, 483 insertions(+), 99 deletions(-)
---
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 3f61cff..d200694 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1467,6 +1467,8 @@ g_settings_get_double
 g_settings_set_double
 g_settings_get_boolean
 g_settings_set_boolean
+g_settings_get_enum
+g_settings_set_enum
 g_settings_sync
 #endif
 #endif
diff --git a/gio/gschema-compile.c b/gio/gschema-compile.c
index f462a7b..14339e2 100644
--- a/gio/gschema-compile.c
+++ b/gio/gschema-compile.c
@@ -31,12 +31,35 @@
 
 #include "gvdb/gvdb-builder.h"
 
+static gpointer
+g_memmem (gconstpointer haystack,
+          gsize         haystack_len,
+          gconstpointer needle,
+          gsize         needle_len)
+{
+#ifndef HAVE_MEMMEM
+  while (needle_len <= haystack_len)
+    {
+      if (memcmp (haystack, needle, needle_len) == 0)
+        return (gpointer) haystack;
+
+      haystack = ((gchar *) haystack) + 1;
+      haystack_len--;
+    }
+
+  return NULL;
+#else
+  return memmem (haystack, haystack_len, needle, needle_len);
+#endif
+}
+
 typedef struct
 {
   gboolean byteswap;
 
   GVariantBuilder key_options;
   GHashTable *schemas;
+  GHashTable *enums;
   gchar *schemalist_domain;
 
   GHashTable *schema;
@@ -52,6 +75,8 @@ typedef struct
   gchar l10n;
   gchar *context;
   GVariantType *type;
+
+  GString *enumdesc;
 } ParseState;
 
 static gboolean allow_any_name = FALSE;
@@ -140,40 +165,53 @@ type_allows_range (const GVariantType *type)
 
 static gboolean
 is_valid_choices (GVariant    *variant,
-                  const gchar *choices)
+                  const gchar *choices,
+                  gsize        choices_size)
 {
   switch (g_variant_classify (variant))
     {
       case G_VARIANT_CLASS_MAYBE:
       case G_VARIANT_CLASS_ARRAY:
         {
-          gsize i, n;
-          GVariant *child;
-          gboolean is_valid;
+          gboolean valid = TRUE;
+          GVariantIter iter;
 
-          n = g_variant_n_children (variant);
-          for (i = 0; i < n; ++i)
-            {
-              child = g_variant_get_child_value (variant, i);
-              is_valid = is_valid_choices (child, choices);
-              g_variant_unref (child);
+          g_variant_iter_init (&iter, variant);
 
-              if (!is_valid)
-                return FALSE;
+          while (valid && (variant = g_variant_iter_next_value (&iter)))
+            {
+              valid = is_valid_choices (variant, choices, choices_size);
+              g_variant_unref (variant);
             }
 
-          return TRUE;
+          return valid;
         }
 
       case G_VARIANT_CLASS_STRING:
         {
+          gchar stack_buffer[64];
           const gchar *string;
+          gboolean matched;
+          gchar *buffer;
+          gsize length;
+
+          string = g_variant_get_string (variant, &length);
+
+          if (length + 2 <= sizeof stack_buffer)
+            buffer = stack_buffer;
+          else
+            buffer = g_malloc (length + 2);
+
+          buffer[0] = '\0';
+          memcpy (buffer + 1, string, length + 1);
 
-          g_variant_get (variant, "&s", &string);
+          matched = g_memmem (choices, choices_size,
+                              buffer, length + 2) != NULL;
 
-          while ((choices = strstr (choices, string)) && choices[-1] != 0xff);
+          if (buffer != stack_buffer)
+            g_free (buffer);
 
-          return choices != NULL;
+          return matched;
         }
 
       default:
@@ -254,18 +292,64 @@ start_element (GMarkupParseContext  *context,
             }
           return;
         }
+
+      if (strcmp (element_name, "enum") == 0)
+        {
+          const gchar *id;
+
+          if (COLLECT (STRING, "id", &id))
+            {
+              if (g_hash_table_lookup (state->enums, id))
+                g_set_error (error, G_MARKUP_ERROR,
+                             G_MARKUP_ERROR_INVALID_CONTENT,
+                             "<enum id='%s'> already specified", id);
+
+                g_hash_table_insert (state->enums, g_strdup (id),
+                                     state->enumdesc = g_string_new (NULL));
+            }
+
+          return;
+        }
     }
   else if (strcmp (container, "schema") == 0)
     {
       if (strcmp (element_name, "key") == 0)
         {
-          const gchar *name, *type;
+          const gchar *name, *type, *enumtype;
 
-          if (COLLECT (STRING, "name", &name, STRING, "type", &type))
+          if (COLLECT (STRING, "name", &name,
+                       OPTIONAL | STRING, "type", &type,
+                       OPTIONAL | STRING, "enum", &enumtype))
             {
+              GString *enumdata = NULL;
+
               if (!is_valid_keyname (name, error))
                 return;
 
+              if ((type == NULL) == (enumtype == NULL))
+                {
+                  g_set_error (error, G_MARKUP_ERROR,
+                               G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+                               "exactly one of 'type' or 'enum' must "
+                               "be specified as an attribute to <key>");
+                  return;
+                }
+
+              if (enumtype)
+                {
+                  enumdata = g_hash_table_lookup (state->enums, enumtype);
+
+                  if (enumdata == NULL)
+                    {
+                      g_set_error (error, G_MARKUP_ERROR,
+                                   G_MARKUP_ERROR_INVALID_CONTENT,
+                                   "<enum id='%s'> not (yet) defined.", enumtype);
+                      return;
+                    }
+
+                  type = "s";
+                }
+
               if (!g_hash_table_lookup (state->schema, name))
                 {
                   state->key = gvdb_hash_table_insert (state->schema, name);
@@ -291,6 +375,16 @@ start_element (GMarkupParseContext  *context,
 
               g_variant_builder_init (&state->key_options,
                                       G_VARIANT_TYPE ("a{sv}"));
+
+              if (enumdata != NULL)
+                {
+                  GVariant *array;
+
+                  array = g_variant_new_byte_array (enumdata->str,
+                                                    enumdata->len);
+                  g_variant_builder_add (&state->key_options,
+                                         "{sv}", "enum", array);
+                }
             }
 
           return;
@@ -436,13 +530,14 @@ start_element (GMarkupParseContext  *context,
               gchar *type = g_variant_type_dup_string (state->type);
               g_set_error (error, G_MARKUP_ERROR,
                            G_MARKUP_ERROR_INVALID_CONTENT,
-                           "Element <%s> not allowed for keys of type \"%s\"\n",
+                           "Element <%s> not allowed for keys of type '%s'",
                            element_name, type);
               g_free (type);
               return;
             }
 
-          state->choices = g_string_new ("\xff");
+          state->choices = g_string_new (NULL);
+          g_string_append_c (state->choices, 0);
 
           NO_ATTRS ();
           return;
@@ -455,7 +550,62 @@ start_element (GMarkupParseContext  *context,
           const gchar *value;
 
           if (COLLECT (STRING, "value", &value))
-            g_string_append_printf (state->choices, "%s\xff", value);
+            g_string_append_len (state->choices, value, strlen (value) + 1);
+
+          return;
+        }
+    }
+  else if (strcmp (container, "enum") == 0)
+    {
+      if (strcmp (element_name, "value") == 0)
+        {
+          const gchar *nick, *valuestr;
+
+          if (COLLECT (STRING, "nick", &nick,
+                       STRING, "value", &valuestr))
+            {
+              gint64 big_value;
+              guint32 value;
+              gsize before;
+              gchar *end;
+
+              if (nick[0] == '\0' || nick[1] == '\0')
+                {
+                  g_set_error (error, G_MARKUP_ERROR,
+                               G_MARKUP_ERROR_INVALID_CONTENT,
+                               "enum nick must be a minimum of 2 characters");
+                  return;
+                }
+
+              big_value = g_ascii_strtoll (valuestr, &end, 0);
+              if (*end || big_value > G_MAXINT32 || big_value < G_MININT32)
+                {
+                  g_set_error (error, G_MARKUP_ERROR,
+                               G_MARKUP_ERROR_INVALID_CONTENT,
+                               "invalid numeric value");
+                  return;
+                }
+
+              value = big_value;
+              value = GUINT32_TO_LE (value);
+
+              g_string_append_len (state->enumdesc, (gpointer) &value, 4);
+
+              before = state->enumdesc->len;
+              g_string_append_c (state->enumdesc, 0xff);
+              g_string_append (state->enumdesc, nick);
+              g_string_append_c (state->enumdesc, 0);
+              while (~state->enumdesc->len & 3)
+                g_string_append_c (state->enumdesc, 0);
+              g_string_append_c (state->enumdesc, 0xff);
+
+              if (g_memmem (state->enumdesc->str, before,
+                            state->enumdesc->str + before,
+                            state->enumdesc->len - before))
+                g_set_error (error, G_MARKUP_ERROR,
+                             G_MARKUP_ERROR_INVALID_CONTENT,
+                             "<value nick='%s'> already specified", nick);
+            }
 
           return;
         }
@@ -540,7 +690,9 @@ end_element (GMarkupParseContext  *context,
         }
       else if (state->choices != NULL)
         {
-          if (!is_valid_choices (state->value, state->choices->str))
+          if (!is_valid_choices (state->value,
+                                 state->choices->str,
+                                 state->choices->len))
             {
               g_set_error_literal (error, G_MARKUP_ERROR,
                                    G_MARKUP_ERROR_INVALID_CONTENT,
@@ -566,12 +718,10 @@ end_element (GMarkupParseContext  *context,
 
   else if (strcmp (element_name, "choices") == 0)
     {
-      gchar *choices;
-
-      choices = g_string_free (state->choices, FALSE);
       g_variant_builder_add (&state->key_options, "{sv}", "choices",
-                             g_variant_new_byte_array (choices, -1));
-      g_free (choices);
+                             g_variant_new_byte_array (state->choices->str,
+                                                       state->choices->len));
+      g_string_free (state->choices, TRUE);
     }
 }
 
@@ -613,6 +763,7 @@ parse_gschema_files (gchar    **files,
   context = g_markup_parse_context_new (&parser,
                                         G_MARKUP_PREFIX_ERROR_POSITION,
                                         &state, NULL);
+  state.enums = gvdb_hash_table_new (NULL, NULL);
   state.schemas = gvdb_hash_table_new (NULL, NULL);
 
   while ((filename = *files++) != NULL)
diff --git a/gio/gsettings-mapping.c b/gio/gsettings-mapping.c
index 6b17bfb..cf1ac11 100644
--- a/gio/gsettings-mapping.c
+++ b/gio/gsettings-mapping.c
@@ -377,6 +377,21 @@ g_settings_set_mapping (const GValue       *value,
         return g_variant_new_signature (g_value_get_string (value));
     }
 
+  else if (G_VALUE_HOLDS_ENUM (value))
+    {
+      GEnumValue *enumval;
+      GEnumClass *eclass;
+
+      eclass = g_type_class_ref (G_VALUE_TYPE (value));
+      enumval = g_enum_get_value (eclass, g_value_get_enum (value));
+      g_type_class_unref (eclass);
+
+      if (enumval)
+        return g_variant_new_string (enumval->value_nick);
+      else
+        return NULL;
+    }
+
   type_string = g_variant_type_dup_string (expected_type);
   g_critical ("No GSettings bind handler for type \"%s\".", type_string);
   g_free (type_string);
@@ -426,8 +441,11 @@ g_settings_get_mapping (GValue   *value,
            g_variant_is_of_type (variant, G_VARIANT_TYPE_OBJECT_PATH) ||
            g_variant_is_of_type (variant, G_VARIANT_TYPE_SIGNATURE))
     {
-      g_value_set_string (value, g_variant_get_string (variant, NULL));
-      return TRUE;
+      if (G_VALUE_HOLDS_STRING (value))
+        {
+          g_value_set_string (value, g_variant_get_string (variant, NULL));
+          return TRUE;
+        }
     }
   else if (g_variant_is_of_type (variant, G_VARIANT_TYPE ("ay")))
     {
@@ -470,6 +488,8 @@ g_settings_mapping_is_compatible (GType               gvalue_type,
           g_variant_type_equal (variant_type, G_VARIANT_TYPE ("ay")) ||
           g_variant_type_equal (variant_type, G_VARIANT_TYPE_OBJECT_PATH) ||
           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);
 
   return ok;
 }
diff --git a/gio/gsettings.c b/gio/gsettings.c
index 15de030..219d298 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -19,7 +19,9 @@
  * Author: Ryan Lortie <desrt desrt ca>
  */
 
+#define _GNU_SOURCE
 #include "config.h"
+
 #include <glib.h>
 #include <glibintl.h>
 #include <locale.h>
@@ -144,6 +146,28 @@
  *  </refsect2>
  */
 
+static gpointer
+g_memmem (gconstpointer haystack,
+          gsize         haystack_len,
+          gconstpointer needle,
+          gsize         needle_len)
+{
+#ifndef HAVE_MEMMEM
+  while (needle_len <= haystack_len)
+    {
+      if (memcmp (haystack, needle, needle_len) == 0)
+        return (gpointer) haystack;
+
+      haystack = ((gchar *) haystack) + 1;
+      haystack_len--;
+    }
+
+  return NULL;
+#else
+  return memmem (haystack, haystack_len, needle, needle_len);
+#endif
+}
+
 struct _GSettingsPrivate
 {
   /* where the signals go... */
@@ -730,6 +754,177 @@ g_settings_class_init (GSettingsClass *class)
 
 }
 
+typedef struct
+{
+  GSettings *settings;
+  const gchar *key;
+
+  GSettingsSchema *schema;
+
+  const gchar *enumdata;
+  gsize enumsize;
+
+  const gchar *unparsed;
+  gchar lc_char;
+
+  const GVariantType *type;
+  GVariant *value;
+} GSettingsKeyInfo;
+
+static GVariant *
+g_settings_get_translated_default (GSettingsKeyInfo *info)
+{
+  const gchar *translated;
+  GError *error = NULL;
+  const gchar *domain;
+  GVariant *value;
+
+  if (info->lc_char == '\0')
+    return NULL;
+
+  domain = g_settings_schema_get_gettext_domain (info->schema);
+
+  if (info->lc_char == 't')
+    translated = g_dcgettext (domain, info->unparsed, LC_TIME);
+  else
+    translated = g_dgettext (domain, info->unparsed);
+
+  if (translated == info->unparsed)
+    return NULL;
+
+  value = g_variant_parse (info->type, translated, NULL, NULL, &error);
+
+  if (value == NULL)
+    {
+      g_warning ("Failed to parse translated string `%s' for "
+                 "key `%s' in schema `%s': %s", info->unparsed, info->key,
+                 info->settings->priv->schema_name, error->message);
+      g_warning ("Using untranslated default instead.");
+      g_error_free (error);
+    }
+
+  return value;
+}
+
+static gboolean
+g_settings_to_enum (GSettingsKeyInfo *info,
+                    GVariant         *value,
+                    gint             *result)
+{
+  const gchar *str;
+  gsize length;
+
+  if (info->enumdata == NULL)
+    {
+      g_critical ("g_settings_get_enum() called on key '%s' which is not "
+                  "associated with an enumerated type", info->key);
+      *(gint *) result = 0;
+
+      return TRUE;
+    }
+
+  str = g_variant_get_string (value, &length);
+
+  if (2 < length && length <= 64)
+    {
+      guint32 *found_value;
+      gchar buffer[80];
+      gint pad;
+
+      pad = (2u - length) & 3;
+      buffer[0] = '\xff';
+      memcpy (buffer + 1, str, length + 1);
+      memset (buffer + length + 2, 0, pad);
+      buffer[length + pad + 2] = '\xff';
+      length += pad + 2;
+
+      found_value = g_memmem (info->enumdata, info->enumsize, buffer, length);
+
+      if (found_value)
+        {
+          *result = GUINT32_FROM_LE (found_value[-1]);
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static GVariant *
+g_settings_from_enum (GSettingsKeyInfo *info,
+                      gint              value)
+{
+  guint32 pattern[] = { -1u, value, -1u };
+  const gchar *found_nick;
+
+  pattern[1] = GUINT32_TO_LE (pattern[1]);
+  found_nick = g_memmem (info->enumdata, info->enumsize,
+                         ((gchar *) pattern) + 3, 6);
+
+  if (found_nick)
+    return g_variant_new_string (found_nick + 6);
+
+  return NULL;
+}
+
+static GSettingsKeyInfo
+g_settings_find_key (GSettings   *settings,
+                     const gchar *key)
+{
+  GSettingsKeyInfo info = { settings, key, settings->priv->schema };
+  GSettingsSchema *schema;
+  GVariant *array;
+
+  schema = settings->priv->schema;
+  info.value = g_settings_schema_get_value (schema, key, &array);
+
+  if G_UNLIKELY (info.value == NULL)
+    g_error ("schema '%s' does not contain a key named '%s'",
+             settings->priv->schema_name, key);
+
+  info.type = g_variant_get_type (info.value);
+
+  if (array != NULL)
+    {
+      GVariant *option_value;
+      const gchar *option;
+      GVariantIter iter;
+
+      g_variant_iter_init (&iter, array);
+      while (g_variant_iter_loop (&iter, "{&sv}", &option, &option_value))
+        {
+          if (strcmp (option, "l10n") == 0)
+            g_variant_get (option_value, "(y&s)",
+                           &info.lc_char, &info.unparsed);
+
+          else if (strcmp (option, "enum") == 0)
+            info.enumdata = g_variant_get_byte_array (option_value,
+                                                      &info.enumsize);
+
+          else
+            g_warning ("unknown schema extension '%s'", option);
+        }
+
+      g_variant_unref (array);
+    }
+
+  return info;
+}
+
+static GVariant *
+g_settings_get_from_backend (GSettingsKeyInfo *info)
+{
+  GVariant *value;
+  gchar *path;
+
+  path = g_strconcat (info->settings->priv->path, info->key, NULL);
+  value = g_settings_backend_read (info->settings->priv->backend,
+                                   path, info->type, FALSE);
+  g_free (path);
+
+  return value;
+}
+
 /**
  * g_settings_get_value:
  * @settings: a #GSettings object
@@ -747,88 +942,73 @@ GVariant *
 g_settings_get_value (GSettings   *settings,
                       const gchar *key)
 {
-  const gchar *unparsed = NULL;
-  GVariant *value, *options;
-  const GVariantType *type;
-  gchar lc_char = '\0';
-  GVariant *sval;
-  gchar *path;
+  GSettingsKeyInfo info;
+  GVariant *value;
 
   g_return_val_if_fail (G_IS_SETTINGS (settings), NULL);
+  g_return_val_if_fail (key != NULL, NULL);
 
-  sval = g_settings_schema_get_value (settings->priv->schema, key, &options);
+  info = g_settings_find_key (settings, key);
+  value = g_settings_get_from_backend (&info);
 
-  if G_UNLIKELY (sval == NULL)
-    g_error ("schema '%s' does not contain a key named '%s'",
-             settings->priv->schema_name, key);
+  if (value == NULL)
+    value = g_settings_get_translated_default (&info);
 
-  path = g_strconcat (settings->priv->path, key, NULL);
-  type = g_variant_get_type (sval);
-  value = g_settings_backend_read (settings->priv->backend, path, type, FALSE);
-  g_free (path);
+  if (value == NULL)
+    value = g_variant_ref (info.value);
 
-  if (options != NULL)
-    {
-      GVariantIter iter;
-      const gchar *option;
-      GVariant *option_value;
+  g_variant_unref (info.value);
 
-      g_variant_iter_init (&iter, options);
-      while (g_variant_iter_loop (&iter, "{&sv}", &option, &option_value))
-        {
-          if (strcmp (option, "l10n") == 0)
-            g_variant_get (option_value, "(y&s)", &lc_char, &unparsed);
-          else
-            g_warning ("unknown schema extension '%s'", option);
-        }
-    }
+  return value;
+}
 
-  if (value && !g_variant_is_of_type (value, type))
+gint
+g_settings_get_enum (GSettings   *settings,
+                     const gchar *key)
+{
+  GSettingsKeyInfo info;
+  GVariant *value;
+  gint result;
+
+  g_return_val_if_fail (G_IS_SETTINGS (settings), -1);
+  g_return_val_if_fail (key != NULL, -1);
+
+  info = g_settings_find_key (settings, key);
+  value = g_settings_get_from_backend (&info);
+
+  if (value != NULL && !g_settings_to_enum (&info, value, &result))
     {
       g_variant_unref (value);
       value = NULL;
     }
 
-  if (value == NULL && lc_char != '\0')
-  /* we will need to translate the schema default value */
+  if (value == NULL)
     {
-      const gchar *translated;
-      GError *error = NULL;
-      const gchar *domain;
-
-      domain = g_settings_schema_get_gettext_domain (settings->priv->schema);
+      value = g_settings_get_translated_default (&info);
 
-      if (lc_char == 't')
-        translated = g_dcgettext (domain, unparsed, LC_TIME);
-      else
-        translated = g_dgettext (domain, unparsed);
-
-      if (translated != unparsed)
-        /* it was translated, so we need to re-parse it */
+      if (value != NULL && !g_settings_to_enum (&info, value, &result))
         {
-          value = g_variant_parse (g_variant_get_type (sval),
-                                   translated, NULL, NULL, &error);
-
-          if (value == NULL)
-            {
-              g_warning ("Failed to parse translated string `%s' for "
-                         "key `%s' in schema `%s': %s", unparsed, key,
-                         settings->priv->schema_name, error->message);
-              g_warning ("Using untranslated default instead.");
-              g_error_free (error);
-            }
+          g_variant_unref (value);
+          value = NULL;
         }
     }
 
   if (value == NULL)
-    /* either translation failed or there was none to do.
-     * use the pre-compiled default.
-     */
-    value = g_variant_ref (sval);
+    g_settings_to_enum (&info, info.value, &result);
+  else
+    g_variant_unref (value);
 
-  g_variant_unref (sval);
+  g_variant_unref (info.value);
 
-  return value;
+  return result;
+}
+
+gboolean
+g_settings_set_enum (GSettings   *settings,
+                     const gchar *key,
+                     gint         value)
+{
+  return FALSE;
 }
 
 /**
@@ -1388,8 +1568,8 @@ g_settings_bind_with_mapping (GSettings               *settings,
       !g_settings_mapping_is_compatible (binding->property->value_type,
                                          binding->type))
     {
-      g_critical ("g_settings_bind: property '%s' on class '%s' has type"
-                  "'%s' which is not compatible with type '%s' of key '%s'"
+      g_critical ("g_settings_bind: property '%s' on class '%s' has type "
+                  "'%s' which is not compatible with type '%s' of key '%s' "
                   "on schema '%s'", property, G_OBJECT_TYPE_NAME (object),
                   g_type_name (binding->property->value_type),
                   g_variant_type_dup_string (binding->type), key,
diff --git a/gio/gsettings.h b/gio/gsettings.h
index d766bd0..aad224e 100644
--- a/gio/gsettings.h
+++ b/gio/gsettings.h
@@ -121,7 +121,11 @@ gchar **                g_settings_get_strv                             (GSettin
 gboolean                g_settings_set_strv                             (GSettings          *settings,
                                                                          const gchar        *key,
                                                                          const gchar *const *value);
-
+gint                    g_settings_get_enum                             (GSettings          *settings,
+                                                                         const gchar        *key);
+gboolean                g_settings_set_enum                             (GSettings          *settings,
+                                                                         const gchar        *key,
+                                                                         gint                value);
 GSettings *             g_settings_get_child                            (GSettings          *settings,
                                                                          const gchar        *name);
 
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
index dd22593..044ef48 100644
--- a/gio/gsettingsbackend.c
+++ b/gio/gsettingsbackend.c
@@ -732,7 +732,8 @@ g_settings_backend_changed_tree (GSettingsBackend *backend,
  * g_settings_backend_read:
  * @backend: a #GSettingsBackend implementation
  * @key: the key to read
- * @expected_type: a #GVariantType hint
+ * @expected_type: a #GVariantType
+ * @default_value: if the default value should be returned
  * @returns: the value that was read, or %NULL
  *
  * Reads a key. This call will never block.
@@ -740,11 +741,13 @@ g_settings_backend_changed_tree (GSettingsBackend *backend,
  * If the key exists, the value associated with it will be returned.
  * If the key does not exist, %NULL will be returned.
  *
- * If @expected_type is given, it serves as a type hint to the backend.
- * If you expect a key of a certain type then you should give
- * @expected_type to increase your chances of getting it.  Some backends
- * may ignore this argument and return values of a different type; it is
- * mostly used by backends that don't store strong type information.
+ * The returned value will be of the type given in @expected_type.  If
+ * the backend stored a value of a different type then %NULL will be
+ * returned.
+ *
+ * If @default_value is %TRUE then this gets the default value from the
+ * backend (ie: the one that the backend would contain if
+ * g_settings_reset() were called).
  */
 GVariant *
 g_settings_backend_read (GSettingsBackend   *backend,
@@ -752,8 +755,18 @@ g_settings_backend_read (GSettingsBackend   *backend,
                          const GVariantType *expected_type,
                          gboolean            default_value)
 {
-  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+  GVariant *value;
+
+  value = G_SETTINGS_BACKEND_GET_CLASS (backend)
     ->read (backend, key, expected_type, default_value);
+
+  if G_UNLIKELY (value && !g_variant_is_of_type (value, expected_type))
+    {
+      g_variant_unref (value);
+      value = NULL;
+    }
+
+  return value;
 }
 
 /*< private >
diff --git a/gio/tests/org.gtk.test.gschema.xml b/gio/tests/org.gtk.test.gschema.xml
index 9d1a821..48259f9 100644
--- a/gio/tests/org.gtk.test.gschema.xml
+++ b/gio/tests/org.gtk.test.gschema.xml
@@ -92,4 +92,18 @@
     </key>
   </schema>
 
+  <enum id='org.gtk.test.TestEnum'>
+    <value nick='invalid' value='0'/>
+    <value nick='foo' value='1'/>
+    <value nick='bar' value='2'/>
+    <value nick='baz' value='3'/>
+    <value nick='quux' value='4'/>
+  </enum>
+    
+  <schema id='org.gtk.test.enums' path='/tests/enums/'>
+    <key name='test' enum='org.gtk.test.TestEnum'>
+      <default>'bar'</default>
+    </key>
+  </schema>
+
 </schemalist>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]