add plugin example for GSettingsSchemaSource
[platform/upstream/glib.git] / gio / gsettingsschema.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  * Copyright © 2011 Canonical Limited
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the licence, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "gsettingsschema-internal.h"
24 #include "gsettings.h"
25
26 #include "gvdb/gvdb-reader.h"
27 #include "strinfo.c"
28
29 #include <glibintl.h>
30 #include <string.h>
31
32 /**
33  * SECTION:gsettingsschema
34  * @short_description: introspecting and controlling the loading of
35  *                     #GSettings schemas
36  *
37  * The #GSettingsSchemaSource and #GSettingsSchema APIs provide a
38  * mechanism for advanced control over the loading of schemas and a
39  * mechanism for introspecting their content.
40  *
41  * Plugin loading systems that wish to provide plugins a way to access
42  * settings face the problem of how to make the schemas for these
43  * settings visible to GSettings.  Typically, a plugin will want to ship
44  * the schema along with itself and it won't be installed into the
45  * standard system directories for schemas.
46  *
47  * #GSettingsSchemaSource provides a mechanism for dealing with this by
48  * allowing the creation of a new 'schema source' from which schemas can
49  * be acquired.  This schema source can then become part of the metadata
50  * associated with the plugin and queried whenever the plugin requires
51  * access to some settings.
52  *
53  * Consider the following example:
54  *
55  * |[
56  * typedef struct
57  * {
58  *    ...
59  *    GSettingsSchemaSource *schema_source;
60  *    ...
61  * } Plugin;
62  *
63  * Plugin *
64  * initialise_plugin (const gchar *dir)
65  * {
66  *   Plugin *plugin;
67  *
68  *   ...
69  *
70  *   plugin->schema_source =
71  *     g_settings_new_schema_source_from_directory (dir,
72  *       g_settings_schema_source_get_default (), FALSE, NULL);
73  *
74  *   ...
75  *
76  *   return plugin;
77  * }
78  *
79  * ...
80  *
81  * GSettings *
82  * plugin_get_settings (Plugin      *plugin,
83  *                      const gchar *schema_id)
84  * {
85  *   GSettingsSchema *schema;
86  *
87  *   if (schema_id == NULL)
88  *     schema_id = plugin->identifier;
89  *
90  *   schema = g_settings_schema_source_lookup (plugin->schema_source,
91  *                                             schema_id, FALSE);
92  *
93  *   if (schema == NULL)
94  *     {
95  *       ... disable the plugin or abort, etc ...
96  *     }
97  *
98  *   return g_settings_new_full (schema, NULL, NULL);
99  * }
100  * ]|
101  *
102  * The code above shows how hooks should be added to the code that
103  * initialises (or enables) the plugin to create the schema source and
104  * how an API can be added to the plugin system to provide a convenient
105  * way for the plugin to access its settings, using the schemas that it
106  * ships.
107  *
108  * From the standpoint of the plugin, it would need to ensure that it
109  * ships a gschemas.compiled file as part of itself, and then simply do
110  * the following:
111  *
112  * |[
113  * {
114  *   GSettings *settings;
115  *   gint some_value;
116  *
117  *   settings = plugin_get_settings (self, NULL);
118  *   some_value = g_settings_get_int (settings, "some-value");
119  *   ...
120  * }
121  * ]|
122  *
123  * It's also possible that the plugin system expects the schema source
124  * files (ie: .gschema.xml files) instead of a gschemas.compiled file.
125  * In that case, the plugin loading system must compile the schemas for
126  * itself before attempting to create the settings source.
127  *
128  * Since: 2.32
129  **/
130
131 /**
132  * GSettingsSchema:
133  *
134  * This is an opaque structure type.  You may not access it directly.
135  *
136  * Since: 2.32
137  **/
138 struct _GSettingsSchema
139 {
140   const gchar *gettext_domain;
141   const gchar *path;
142   GQuark *items;
143   gint n_items;
144   GvdbTable *table;
145   gchar *id;
146
147   gint ref_count;
148 };
149
150 typedef struct _GSettingsSchemaSource GSettingsSchemaSource;
151
152 /**
153  * G_TYPE_SETTINGS_SCHEMA_SOURCE:
154  *
155  * A boxed #GType corresponding to #GSettingsSchemaSource.
156  *
157  * Since: 2.32
158  **/
159 G_DEFINE_BOXED_TYPE (GSettingsSchemaSource, g_settings_schema_source, g_settings_schema_source_ref, g_settings_schema_source_unref)
160
161 /**
162  * G_TYPE_SETTINGS_SCHEMA:
163  *
164  * A boxed #GType corresponding to #GSettingsSchema.
165  *
166  * Since: 2.32
167  **/
168 G_DEFINE_BOXED_TYPE (GSettingsSchema, g_settings_schema, g_settings_schema_ref, g_settings_schema_unref)
169
170 /**
171  * GSettingsSchemaSource:
172  *
173  * This is an opaque structure type.  You may not access it directly.
174  *
175  * Since: 2.32
176  **/
177 struct _GSettingsSchemaSource
178 {
179   GSettingsSchemaSource *parent;
180   GvdbTable *table;
181
182   gint ref_count;
183 };
184
185 static GSettingsSchemaSource *schema_sources;
186
187 static void
188 prepend_schema_table (GvdbTable *table)
189 {
190   GSettingsSchemaSource *source;
191
192   /* we steal the reference from 'schema_sources' for our ->parent */
193   source = g_slice_new (GSettingsSchemaSource);
194   source->parent = schema_sources;
195   source->table = table;
196   source->ref_count = 1;
197
198   schema_sources = source;
199 }
200
201 /**
202  * g_settings_schema_source_ref:
203  * @source: a #GSettingsSchemaSource
204  *
205  * Increase the reference count of @source, returning a new reference.
206  *
207  * Returns: a new reference to @source
208  *
209  * Since: 2.32
210  **/
211 GSettingsSchemaSource *
212 g_settings_schema_source_ref (GSettingsSchemaSource *source)
213 {
214   g_atomic_int_inc (&source->ref_count);
215
216   return source;
217 }
218
219 /**
220  * g_settings_schema_source_unref:
221  * @source: a #GSettingsSchemaSource
222  *
223  * Decrease the reference count of @source, possibly freeing it.
224  *
225  * Since: 2.32
226  **/
227 void
228 g_settings_schema_source_unref (GSettingsSchemaSource *source)
229 {
230   if (g_atomic_int_dec_and_test (&source->ref_count))
231     {
232       if (source == schema_sources)
233         g_error ("g_settings_schema_source_unref() called too many times on the default schema source");
234
235       if (source->parent)
236         g_settings_schema_source_unref (source->parent);
237       gvdb_table_unref (source->table);
238
239       g_slice_free (GSettingsSchemaSource, source);
240     }
241 }
242
243 /**
244  * g_settings_schema_source_new_from_directory:
245  * @directory: the filename of a directory
246  * @parent: (allow-none): a #GSettingsSchemaSource, or %NULL
247  * @trusted: %TRUE, if the directory is trusted
248  * @error: a pointer to a #GError pointer set to %NULL, or %NULL
249  *
250  * Attempts to create a new schema source corresponding to the contents
251  * of the given directory.
252  *
253  * This function is not required for normal uses of #GSettings but it
254  * may be useful to authors of plugin management systems.
255  *
256  * The directory should contain a file called
257  * <filename>gschemas.compiled</filename> as produced by
258  * <command>glib-compile-schemas</command>.
259  *
260  * If @trusted is %TRUE then <filename>gschemas.compiled</filename> is
261  * trusted not to be corrupted.  This assumption has a performance
262  * advantage, but can result in crashes or inconsistent behaviour in the
263  * case of a corrupted file.  Generally, you should set @trusted to
264  * %TRUE for files installed by the system and to %FALSE for files in
265  * the home directory.
266  *
267  * If @parent is non-%NULL then there are two effects.
268  *
269  * First, if g_settings_schema_source_lookup() is called with the
270  * @recursive flag set to %TRUE and the schema can not be found in the
271  * source, the lookup will recurse to the parent.
272  *
273  * Second, any references to other schemas specified within this
274  * source (ie: <literal>child</literal> or <literal>extents</literal>)
275  * references may be resolved from the @parent.
276  *
277  * For this second reason, except in very unusual situations, the
278  * @parent should probably be given as the default schema source, as
279  * returned by g_settings_schema_source_get_default().
280  *
281  * Since: 2.32
282  **/
283 GSettingsSchemaSource *
284 g_settings_schema_source_new_from_directory (const gchar            *directory,
285                                              GSettingsSchemaSource  *parent,
286                                              gboolean                trusted,
287                                              GError                **error)
288 {
289   GSettingsSchemaSource *source;
290   GvdbTable *table;
291   gchar *filename;
292
293   filename = g_build_filename (directory, "gschemas.compiled", NULL);
294   table = gvdb_table_new (filename, trusted, error);
295   g_free (filename);
296
297   if (table == NULL)
298     return NULL;
299
300   source = g_slice_new (GSettingsSchemaSource);
301   source->parent = parent ? g_settings_schema_source_ref (parent) : NULL;
302   source->table = table;
303   source->ref_count = 1;
304
305   return source;
306 }
307
308 static void
309 initialise_schema_sources (void)
310 {
311   static gsize initialised;
312
313   /* need a separate variable because 'schema_sources' may legitimately
314    * be null if we have zero valid schema sources
315    */
316   if G_UNLIKELY (g_once_init_enter (&initialised))
317     {
318       const gchar * const *dirs;
319       const gchar *path;
320       gint i;
321
322       /* iterate in reverse: count up, then count down */
323       dirs = g_get_system_data_dirs ();
324       for (i = 0; dirs[i]; i++);
325
326       while (i--)
327         {
328           gchar *filename;
329           GvdbTable *table;
330
331           filename = g_build_filename (dirs[i], "glib-2.0", "schemas", "gschemas.compiled", NULL);
332           table = gvdb_table_new (filename, TRUE, NULL);
333
334           if (table != NULL)
335             prepend_schema_table (table);
336
337           g_free (filename);
338         }
339
340       if ((path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL)
341         {
342           gchar *filename;
343           GvdbTable *table;
344
345           filename = g_build_filename (path, "gschemas.compiled", NULL);
346           table = gvdb_table_new (filename, TRUE, NULL);
347
348           if (table != NULL)
349             prepend_schema_table (table);
350
351           g_free (filename);
352         }
353
354       g_once_init_leave (&initialised, TRUE);
355     }
356 }
357
358 /**
359  * g_settings_schema_source_get_default:
360  *
361  * Gets the default system schema source.
362  *
363  * This function is not required for normal uses of #GSettings but it
364  * may be useful to authors of plugin management systems or to those who
365  * want to introspect the content of schemas.
366  *
367  * If no schemas are installed, %NULL will be returned.
368  *
369  * The returned source may actually consist of multiple schema sources
370  * from different directories, depending on which directories were given
371  * in <envar>XDG_DATA_DIRS</envar> and
372  * <envar>GSETTINGS_SCHEMA_DIR</envar>.  For this reason, all lookups
373  * performed against the default source should probably be done
374  * recursively.
375  *
376  * Returns: (transfer none): the default schema source
377  *
378  * Since: 2.32
379  **/
380  GSettingsSchemaSource *
381 g_settings_schema_source_get_default (void)
382 {
383   initialise_schema_sources ();
384
385   return schema_sources;
386 }
387
388 /**
389  * g_settings_schema_source_lookup:
390  * @source: a #GSettingsSchemaSource
391  * @schema_id: a schema ID
392  * @recursive: %TRUE if the lookup should be recursive
393  *
394  * Looks up a schema with the identifier @schema_id in @source.
395  *
396  * This function is not required for normal uses of #GSettings but it
397  * may be useful to authors of plugin management systems or to those who
398  * want to introspect the content of schemas.
399  *
400  * If the schema isn't found directly in @source and @recursive is %TRUE
401  * then the parent sources will also be checked.
402  *
403  * If the schema isn't found, %NULL is returned.
404  *
405  * Returns: (transfer full): a new #GSettingsSchema
406  *
407  * Since: 2.32
408  **/
409 GSettingsSchema *
410 g_settings_schema_source_lookup (GSettingsSchemaSource *source,
411                                  const gchar           *schema_id,
412                                  gboolean               recursive)
413 {
414   GSettingsSchema *schema;
415   GvdbTable *table;
416
417   g_return_val_if_fail (source != NULL, NULL);
418   g_return_val_if_fail (schema_id != NULL, NULL);
419
420   table = gvdb_table_get_table (source->table, schema_id);
421
422   if (table == NULL && recursive)
423     for (source = source->parent; source; source = source->parent)
424       if ((table = gvdb_table_get_table (source->table, schema_id)))
425         break;
426
427   if (table == NULL)
428     return NULL;
429
430   schema = g_slice_new0 (GSettingsSchema);
431   schema->ref_count = 1;
432   schema->id = g_strdup (schema_id);
433   schema->table = table;
434   schema->path = g_settings_schema_get_string (schema, ".path");
435   schema->gettext_domain = g_settings_schema_get_string (schema, ".gettext-domain");
436
437   if (schema->gettext_domain)
438     bind_textdomain_codeset (schema->gettext_domain, "UTF-8");
439
440   return schema;
441 }
442
443 static gboolean
444 steal_item (gpointer key,
445             gpointer value,
446             gpointer user_data)
447 {
448   gchar ***ptr = user_data;
449
450   *(*ptr)++ = (gchar *) key;
451
452   return TRUE;
453 }
454
455 static const gchar * const *non_relocatable_schema_list;
456 static const gchar * const *relocatable_schema_list;
457 static gsize schema_lists_initialised;
458
459 static void
460 ensure_schema_lists (void)
461 {
462   if (g_once_init_enter (&schema_lists_initialised))
463     {
464       GSettingsSchemaSource *source;
465       GHashTable *single, *reloc;
466       const gchar **ptr;
467       gchar **list;
468       gint i;
469
470       initialise_schema_sources ();
471
472       /* We use hash tables to avoid duplicate listings for schemas that
473        * appear in more than one file.
474        */
475       single = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
476       reloc = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
477
478       for (source = schema_sources; source; source = source->parent)
479         {
480           list = gvdb_table_list (source->table, "");
481
482           g_assert (list != NULL);
483
484           for (i = 0; list[i]; i++)
485             {
486               if (!g_hash_table_lookup (single, list[i]) &&
487                   !g_hash_table_lookup (reloc, list[i]))
488                 {
489                   GvdbTable *table;
490
491                   table = gvdb_table_get_table (source->table, list[i]);
492                   g_assert (table != NULL);
493
494                   if (gvdb_table_has_value (table, ".path"))
495                     g_hash_table_insert (single, g_strdup (list[i]), NULL);
496                   else
497                     g_hash_table_insert (reloc, g_strdup (list[i]), NULL);
498
499                   gvdb_table_unref (table);
500                 }
501             }
502
503           g_strfreev (list);
504         }
505
506       ptr = g_new (const gchar *, g_hash_table_size (single) + 1);
507       non_relocatable_schema_list = ptr;
508       g_hash_table_foreach_steal (single, steal_item, &ptr);
509       g_hash_table_unref (single);
510       *ptr = NULL;
511
512       ptr = g_new (const gchar *, g_hash_table_size (reloc) + 1);
513       relocatable_schema_list = ptr;
514       g_hash_table_foreach_steal (reloc, steal_item, &ptr);
515       g_hash_table_unref (reloc);
516       *ptr = NULL;
517
518       g_once_init_leave (&schema_lists_initialised, TRUE);
519     }
520 }
521
522 /**
523  * g_settings_list_schemas:
524  *
525  * Gets a list of the #GSettings schemas installed on the system.  The
526  * returned list is exactly the list of schemas for which you may call
527  * g_settings_new() without adverse effects.
528  *
529  * This function does not list the schemas that do not provide their own
530  * paths (ie: schemas for which you must use
531  * g_settings_new_with_path()).  See
532  * g_settings_list_relocatable_schemas() for that.
533  *
534  * Returns: (element-type utf8) (transfer none):  a list of #GSettings
535  *   schemas that are available.  The list must not be modified or
536  *   freed.
537  *
538  * Since: 2.26
539  **/
540 const gchar * const *
541 g_settings_list_schemas (void)
542 {
543   ensure_schema_lists ();
544
545   return non_relocatable_schema_list;
546 }
547
548 /**
549  * g_settings_list_relocatable_schemas:
550  *
551  * Gets a list of the relocatable #GSettings schemas installed on the
552  * system.  These are schemas that do not provide their own path.  It is
553  * usual to instantiate these schemas directly, but if you want to you
554  * can use g_settings_new_with_path() to specify the path.
555  *
556  * The output of this function, taken together with the output of
557  * g_settings_list_schemas() represents the complete list of all
558  * installed schemas.
559  *
560  * Returns: (element-type utf8) (transfer none): a list of relocatable
561  *   #GSettings schemas that are available.  The list must not be
562  *   modified or freed.
563  *
564  * Since: 2.28
565  **/
566 const gchar * const *
567 g_settings_list_relocatable_schemas (void)
568 {
569   ensure_schema_lists ();
570
571   return relocatable_schema_list;
572 }
573
574 /**
575  * g_settings_schema_ref:
576  * @schema: a #GSettingsSchema
577  *
578  * Increase the reference count of @schema, returning a new reference.
579  *
580  * Returns: a new reference to @schema
581  *
582  * Since: 2.32
583  **/
584 GSettingsSchema *
585 g_settings_schema_ref (GSettingsSchema *schema)
586 {
587   g_atomic_int_inc (&schema->ref_count);
588
589   return schema;
590 }
591
592 /**
593  * g_settings_schema_unref:
594  * @schema: a #GSettingsSchema
595  *
596  * Decrease the reference count of @schema, possibly freeing it.
597  *
598  * Since: 2.32
599  **/
600 void
601 g_settings_schema_unref (GSettingsSchema *schema)
602 {
603   if (g_atomic_int_dec_and_test (&schema->ref_count))
604     {
605       gvdb_table_unref (schema->table);
606       g_free (schema->items);
607       g_free (schema->id);
608
609       g_slice_free (GSettingsSchema, schema);
610     }
611 }
612
613 const gchar *
614 g_settings_schema_get_string (GSettingsSchema *schema,
615                               const gchar     *key)
616 {
617   const gchar *result = NULL;
618   GVariant *value;
619
620   if ((value = gvdb_table_get_raw_value (schema->table, key)))
621     {
622       result = g_variant_get_string (value, NULL);
623       g_variant_unref (value);
624     }
625
626   return result;
627 }
628
629 GVariantIter *
630 g_settings_schema_get_value (GSettingsSchema *schema,
631                              const gchar     *key)
632 {
633   GVariantIter *iter;
634   GVariant *value;
635
636   value = gvdb_table_get_raw_value (schema->table, key);
637
638   if G_UNLIKELY (value == NULL)
639     g_error ("Settings schema '%s' does not contain a key named '%s'", schema->id, key);
640
641   iter = g_variant_iter_new (value);
642   g_variant_unref (value);
643
644   return iter;
645 }
646
647 /**
648  * g_settings_schema_get_path:
649  * @schema: a #GSettingsSchema
650  *
651  * Gets the path associated with @schema, or %NULL.
652  *
653  * Schemas may be single-instance or relocatable.  Single-instance
654  * schemas correspond to exactly one set of keys in the backend
655  * database: those located at the path returned by this function.
656  *
657  * Relocatable schemas can be referenced by other schemas and can
658  * threfore describe multiple sets of keys at different locations.  For
659  * relocatable schemas, this function will return %NULL.
660  *
661  * Returns: (transfer none): the path of the schema, or %NULL
662  *
663  * Since: 2.32
664  **/
665 const gchar *
666 g_settings_schema_get_path (GSettingsSchema *schema)
667 {
668   return schema->path;
669 }
670
671 const gchar *
672 g_settings_schema_get_gettext_domain (GSettingsSchema *schema)
673 {
674   return schema->gettext_domain;
675 }
676
677 gboolean
678 g_settings_schema_has_key (GSettingsSchema *schema,
679                            const gchar     *key)
680 {
681   return gvdb_table_has_value (schema->table, key);
682 }
683
684 const GQuark *
685 g_settings_schema_list (GSettingsSchema *schema,
686                         gint            *n_items)
687 {
688   gint i, j;
689
690   if (schema->items == NULL)
691     {
692       gchar **list;
693       gint len;
694
695       list = gvdb_table_list (schema->table, "");
696       len = list ? g_strv_length (list) : 0;
697
698       schema->items = g_new (GQuark, len);
699       j = 0;
700
701       for (i = 0; i < len; i++)
702         if (list[i][0] != '.')
703           schema->items[j++] = g_quark_from_string (list[i]);
704       schema->n_items = j;
705
706       g_strfreev (list);
707     }
708
709   *n_items = schema->n_items;
710   return schema->items;
711 }
712
713 /**
714  * g_settings_schema_get_id:
715  * @schema: a #GSettingsSchema
716  *
717  * Get the ID of @schema.
718  *
719  * Returns: (transfer none): the ID
720  **/
721 const gchar *
722 g_settings_schema_get_id (GSettingsSchema *schema)
723 {
724   return schema->id;
725 }
726
727 static inline void
728 endian_fixup (GVariant **value)
729 {
730 #if G_BYTE_ORDER == G_BIG_ENDIAN
731   GVariant *tmp;
732
733   tmp = g_variant_byteswap (*value);
734   g_variant_unref (*value);
735   *value = tmp;
736 #endif
737 }
738
739 void
740 g_settings_schema_key_init (GSettingsSchemaKey *key,
741                             GSettingsSchema    *schema,
742                             const gchar        *name)
743 {
744   GVariantIter *iter;
745   GVariant *data;
746   guchar code;
747
748   memset (key, 0, sizeof *key);
749
750   iter = g_settings_schema_get_value (schema, name);
751
752   key->schema = g_settings_schema_ref (schema);
753   key->default_value = g_variant_iter_next_value (iter);
754   endian_fixup (&key->default_value);
755   key->type = g_variant_get_type (key->default_value);
756   key->name = g_intern_string (name);
757
758   while (g_variant_iter_next (iter, "(y*)", &code, &data))
759     {
760       switch (code)
761         {
762         case 'l':
763           /* translation requested */
764           g_variant_get (data, "(y&s)", &key->lc_char, &key->unparsed);
765           break;
766
767         case 'e':
768           /* enumerated types... */
769           key->is_enum = TRUE;
770           goto choice;
771
772         case 'f':
773           /* flags... */
774           key->is_flags = TRUE;
775           goto choice;
776
777         choice: case 'c':
778           /* ..., choices, aliases */
779           key->strinfo = g_variant_get_fixed_array (data, &key->strinfo_length, sizeof (guint32));
780           break;
781
782         case 'r':
783           g_variant_get (data, "(**)", &key->minimum, &key->maximum);
784           endian_fixup (&key->minimum);
785           endian_fixup (&key->maximum);
786           break;
787
788         default:
789           g_warning ("unknown schema extension '%c'", code);
790           break;
791         }
792
793       g_variant_unref (data);
794     }
795
796   g_variant_iter_free (iter);
797 }
798
799 void
800 g_settings_schema_key_clear (GSettingsSchemaKey *key)
801 {
802   if (key->minimum)
803     g_variant_unref (key->minimum);
804
805   if (key->maximum)
806     g_variant_unref (key->maximum);
807
808   g_variant_unref (key->default_value);
809
810   g_settings_schema_unref (key->schema);
811 }
812
813 gboolean
814 g_settings_schema_key_type_check (GSettingsSchemaKey *key,
815                                   GVariant           *value)
816 {
817   g_return_val_if_fail (value != NULL, FALSE);
818
819   return g_variant_is_of_type (value, key->type);
820 }
821
822 gboolean
823 g_settings_schema_key_range_check (GSettingsSchemaKey *key,
824                                    GVariant           *value)
825 {
826   if (key->minimum == NULL && key->strinfo == NULL)
827     return TRUE;
828
829   if (g_variant_is_container (value))
830     {
831       gboolean ok = TRUE;
832       GVariantIter iter;
833       GVariant *child;
834
835       g_variant_iter_init (&iter, value);
836       while (ok && (child = g_variant_iter_next_value (&iter)))
837         {
838           ok = g_settings_schema_key_range_check (key, child);
839           g_variant_unref (child);
840         }
841
842       return ok;
843     }
844
845   if (key->minimum)
846     {
847       return g_variant_compare (key->minimum, value) <= 0 &&
848              g_variant_compare (value, key->maximum) <= 0;
849     }
850
851   return strinfo_is_string_valid (key->strinfo, key->strinfo_length,
852                                   g_variant_get_string (value, NULL));
853 }
854
855 GVariant *
856 g_settings_schema_key_range_fixup (GSettingsSchemaKey *key,
857                                    GVariant           *value)
858 {
859   const gchar *target;
860
861   if (g_settings_schema_key_range_check (key, value))
862     return g_variant_ref (value);
863
864   if (key->strinfo == NULL)
865     return NULL;
866
867   if (g_variant_is_container (value))
868     {
869       GVariantBuilder builder;
870       GVariantIter iter;
871       GVariant *child;
872
873       g_variant_iter_init (&iter, value);
874       g_variant_builder_init (&builder, g_variant_get_type (value));
875
876       while ((child = g_variant_iter_next_value (&iter)))
877         {
878           GVariant *fixed;
879
880           fixed = g_settings_schema_key_range_fixup (key, child);
881           g_variant_unref (child);
882
883           if (fixed == NULL)
884             {
885               g_variant_builder_clear (&builder);
886               return NULL;
887             }
888
889           g_variant_builder_add_value (&builder, fixed);
890           g_variant_unref (fixed);
891         }
892
893       return g_variant_ref_sink (g_variant_builder_end (&builder));
894     }
895
896   target = strinfo_string_from_alias (key->strinfo, key->strinfo_length,
897                                       g_variant_get_string (value, NULL));
898   return target ? g_variant_ref_sink (g_variant_new_string (target)) : NULL;
899 }
900
901
902 GVariant *
903 g_settings_schema_key_get_translated_default (GSettingsSchemaKey *key)
904 {
905   const gchar *translated;
906   GError *error = NULL;
907   const gchar *domain;
908   GVariant *value;
909
910   domain = g_settings_schema_get_gettext_domain (key->schema);
911
912   if (key->lc_char == '\0')
913     /* translation not requested for this key */
914     return NULL;
915
916   if (key->lc_char == 't')
917     translated = g_dcgettext (domain, key->unparsed, LC_TIME);
918   else
919     translated = g_dgettext (domain, key->unparsed);
920
921   if (translated == key->unparsed)
922     /* the default value was not translated */
923     return NULL;
924
925   /* try to parse the translation of the unparsed default */
926   value = g_variant_parse (key->type, translated, NULL, NULL, &error);
927
928   if (value == NULL)
929     {
930       g_warning ("Failed to parse translated string `%s' for "
931                  "key `%s' in schema `%s': %s", key->unparsed, key->name,
932                  g_settings_schema_get_id (key->schema), error->message);
933       g_warning ("Using untranslated default instead.");
934       g_error_free (error);
935     }
936
937   else if (!g_settings_schema_key_range_check (key, value))
938     {
939       g_warning ("Translated default `%s' for key `%s' in schema `%s' "
940                  "is outside of valid range", key->unparsed, key->name,
941                  g_settings_schema_get_id (key->schema));
942       g_variant_unref (value);
943       value = NULL;
944     }
945
946   return value;
947 }
948
949 gint
950 g_settings_schema_key_to_enum (GSettingsSchemaKey *key,
951                                GVariant           *value)
952 {
953   gboolean it_worked;
954   guint result;
955
956   it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length,
957                                         g_variant_get_string (value, NULL),
958                                         &result);
959
960   /* 'value' can only come from the backend after being filtered for validity,
961    * from the translation after being filtered for validity, or from the schema
962    * itself (which the schema compiler checks for validity).  If this assertion
963    * fails then it's really a bug in GSettings or the schema compiler...
964    */
965   g_assert (it_worked);
966
967   return result;
968 }
969
970 GVariant *
971 g_settings_schema_key_from_enum (GSettingsSchemaKey *key,
972                                  gint                value)
973 {
974   const gchar *string;
975
976   string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, value);
977
978   if (string == NULL)
979     return NULL;
980
981   return g_variant_new_string (string);
982 }
983
984 guint
985 g_settings_schema_key_to_flags (GSettingsSchemaKey *key,
986                                 GVariant           *value)
987 {
988   GVariantIter iter;
989   const gchar *flag;
990   guint result;
991
992   result = 0;
993   g_variant_iter_init (&iter, value);
994   while (g_variant_iter_next (&iter, "&s", &flag))
995     {
996       gboolean it_worked;
997       guint flag_value;
998
999       it_worked = strinfo_enum_from_string (key->strinfo, key->strinfo_length, flag, &flag_value);
1000       /* as in g_settings_to_enum() */
1001       g_assert (it_worked);
1002
1003       result |= flag_value;
1004     }
1005
1006   return result;
1007 }
1008
1009 GVariant *
1010 g_settings_schema_key_from_flags (GSettingsSchemaKey *key,
1011                                   guint               value)
1012 {
1013   GVariantBuilder builder;
1014   gint i;
1015
1016   g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
1017
1018   for (i = 0; i < 32; i++)
1019     if (value & (1u << i))
1020       {
1021         const gchar *string;
1022
1023         string = strinfo_string_from_enum (key->strinfo, key->strinfo_length, 1u << i);
1024
1025         if (string == NULL)
1026           {
1027             g_variant_builder_clear (&builder);
1028             return NULL;
1029           }
1030
1031         g_variant_builder_add (&builder, "s", string);
1032       }
1033
1034   return g_variant_builder_end (&builder);
1035 }