diff --git a/gio/Makefile.am b/gio/Makefile.am index 1fe7d07aa..efefd3baf 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -79,21 +79,25 @@ gio-marshal.c: gio-marshal.h gio-marshal.list $(glib_genmarshal) --prefix=_gio_marshal $(srcdir)/gio-marshal.list --body --internal) > $@.tmp && \ mv $@.tmp $@ +settings_headers = \ + gsettingsbackend.h \ + gsettings.h + settings_sources = \ - gsettingsbackendinternal.h \ - gdelayedsettingsbackend.h \ - gdelayedsettingsbackend. \ - gnullsettingsbackend.h \ - gnullsettingsbackend.c \ - gsettingsbackend.c \ gvdb/gvdb-format.h \ gvdb/gvdb-reader.h \ gvdb/gvdb-reader.c \ + gdelayedsettingsbackend.h \ + gdelayedsettingsbackend.c \ + gnullsettingsbackend.h \ + gnullsettingsbackend.c \ + gsettingsbackendinternal.h \ + gsettingsbackend.c \ gsettingsschema.h \ - gsettingsschema.c - -settings_headers = \ - gsettingsbackend.h + gsettingsschema.c \ + gsettings-mapping.h \ + gsettings-mapping.c \ + gsettings.c local_sources = \ glocaldirectorymonitor.c \ @@ -428,7 +432,7 @@ gio_headers = \ gvolumemonitor.h \ gzlibcompressor.h \ gzlibdecompressor.h \ - $(settings_headers) \ + $(settings_headers) \ $(NULL) gioincludedir=$(includedir)/glib-2.0/gio/ @@ -489,9 +493,9 @@ gio_querymodules_LDADD = \ gschema_compile_LDADD = $(top_builddir)/glib/libglib-2.0.la gschema_compile_SOURCES = \ - gvdb/gvdb-format.h \ - gvdb/gvdb-builder.h \ - gvdb/gvdb-builder.c \ + gvdb/gvdb-format.h \ + gvdb/gvdb-builder.h \ + gvdb/gvdb-builder.c \ gschema-compile.c dist-hook: $(BUILT_EXTRA_DIST) ../build/win32/vs9/gio.vcproj diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list index 269ec3560..8439304bf 100644 --- a/gio/gio-marshal.list +++ b/gio/gio-marshal.list @@ -4,3 +4,5 @@ VOID:BOOLEAN,POINTER VOID:OBJECT,OBJECT,ENUM BOOLEAN:OBJECT,OBJECT VOID:STRING,BOXED,BOXED +BOOL:POINTER,INT +BOOL:UINT diff --git a/gio/gio.h b/gio/gio.h index 861f17231..ac0dc7fbf 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -76,6 +76,7 @@ #include #include #include +#include #include #include #include diff --git a/gio/gio.symbols b/gio/gio.symbols index 37e62b1a8..0842ade07 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -957,6 +957,7 @@ g_socket_protocol_get_type G_GNUC_CONST g_socket_msg_flags_get_type G_GNUC_CONST g_resolver_error_get_type G_GNUC_CONST g_zlib_compressor_format_get_type +g_settings_bind_flags_get_type #endif #endif @@ -1352,3 +1353,36 @@ g_settings_backend_writable_changed g_settings_backend_changed_tree #endif #endif + +#if IN_HEADER(__G_SETTINGS_H__) +#if IN_FILE(__G_SETTINGS_C__) +g_settings_apply +g_settings_bind +g_settings_bind_with_mapping +g_settings_delay +g_settings_get +g_settings_get_child +g_settings_get_has_unapplied +g_settings_get_type +g_settings_get_value +g_settings_is_writable +g_settings_new +g_settings_new_with_context +g_settings_new_with_context_and_path +g_settings_new_with_path +g_settings_revert +g_settings_set +g_settings_set_value +g_settings_unbind +g_settings_get_int +g_settings_set_int +g_settings_get_string +g_settings_set_string +g_settings_get_strv +g_settings_set_strv +g_settings_get_double +g_settings_set_double +g_settings_get_boolean +g_settings_set_boolean +#endif +#endif diff --git a/gio/gsettings-mapping.c b/gio/gsettings-mapping.c new file mode 100644 index 000000000..e7f1ba5f4 --- /dev/null +++ b/gio/gsettings-mapping.c @@ -0,0 +1,367 @@ +/* + * Copyright © 2010 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Vincent Untz + */ + +#include "config.h" + +#include "gsettings-mapping.h" + +static GVariant * +g_settings_set_mapping_numeric (const GValue *value, + const GVariantType *expected_type) +{ + GVariant *variant = NULL; + glong l; + + if (G_VALUE_HOLDS_INT (value)) + l = g_value_get_int (value); + else if (G_VALUE_HOLDS_INT64 (value)) + l = g_value_get_int64 (value); + else if (G_VALUE_HOLDS_DOUBLE (value)) + l = g_value_get_double (value); + else + return NULL; + + if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT16)) + { + if (G_MININT16 <= l && l <= G_MAXINT16) + variant = g_variant_new_int16 ((gint16) l); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT16)) + { + if (0 <= l && l <= G_MAXUINT16) + variant = g_variant_new_uint16 ((guint16) l); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT32)) + { + if (G_MININT32 <= l && l <= G_MAXINT32) + variant = g_variant_new_int32 ((gint) l); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT32)) + { + if (0 <= l && l <= G_MAXUINT32) + variant = g_variant_new_uint32 ((guint) l); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT64)) + { + if (G_MININT64 <= l && l <= G_MAXINT64) + variant = g_variant_new_int64 ((gint64) l); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT64)) + { + if (0 <= l && l <= G_MAXUINT64) + variant = g_variant_new_uint64 ((guint64) l); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_HANDLE)) + { + if (0 <= l && l <= G_MAXUINT32) + variant = g_variant_new_handle ((guint) l); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_DOUBLE)) + variant = g_variant_new_double ((double) l); + + return variant; +} + +static GVariant * +g_settings_set_mapping_unsigned_numeric (const GValue *value, + const GVariantType *expected_type) +{ + GVariant *variant = NULL; + gulong u; + + if (G_VALUE_HOLDS_UINT (value)) + u = g_value_get_uint (value); + else if (G_VALUE_HOLDS_UINT64 (value)) + u = g_value_get_uint64 (value); + else + return NULL; + + if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT16)) + { + if (u <= G_MAXINT16) + variant = g_variant_new_int16 ((gint16) u); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT16)) + { + if (u <= G_MAXUINT16) + variant = g_variant_new_uint16 ((guint16) u); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT32)) + { + if (u <= G_MAXINT32) + variant = g_variant_new_int32 ((gint) u); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT32)) + { + if (u <= G_MAXUINT32) + variant = g_variant_new_uint32 ((guint) u); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_INT64)) + { + if (u <= G_MAXINT64) + variant = g_variant_new_int64 ((gint64) u); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_UINT64)) + { + if (u <= G_MAXUINT64) + variant = g_variant_new_uint64 ((guint64) u); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_HANDLE)) + { + if (u <= G_MAXUINT32) + variant = g_variant_new_handle ((guint) u); + } + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_DOUBLE)) + variant = g_variant_new_double ((double) u); + + return variant; +} + +static gboolean +g_settings_get_mapping_numeric (GValue *value, + GVariant *variant) +{ + const GVariantType *type; + glong l; + + type = g_variant_get_type (variant); + + if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16)) + l = g_variant_get_int16 (variant); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) + l = g_variant_get_int32 (variant); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64)) + l = g_variant_get_int64 (variant); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE)) + l = g_variant_get_double (variant); + else + return FALSE; + + if (G_VALUE_HOLDS_INT (value)) + { + g_value_set_int (value, l); + return (G_MININT32 <= l && l <= G_MAXINT32); + } + else if (G_VALUE_HOLDS_UINT (value)) + { + g_value_set_uint (value, l); + return (0 <= l && l <= G_MAXUINT32); + } + else if (G_VALUE_HOLDS_INT64 (value)) + { + g_value_set_int64 (value, l); + return (G_MININT64 <= l && l <= G_MAXINT64); + } + else if (G_VALUE_HOLDS_UINT64 (value)) + { + g_value_set_uint64 (value, l); + return (0 <= l && l <= G_MAXUINT64); + } + else if (G_VALUE_HOLDS_DOUBLE (value)) + { + g_value_set_double (value, l); + return TRUE; + } + + return FALSE; +} + +static gboolean +g_settings_get_mapping_unsigned_numeric (GValue *value, + GVariant *variant) +{ + const GVariantType *type; + gulong u; + + type = g_variant_get_type (variant); + + if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16)) + u = g_variant_get_uint16 (variant); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) + u = g_variant_get_uint32 (variant); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64)) + u = g_variant_get_uint64 (variant); + else if (g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE)) + u = g_variant_get_handle (variant); + else + return FALSE; + + if (G_VALUE_HOLDS_INT (value)) + { + g_value_set_int (value, u); + return (u <= G_MAXINT32); + } + else if (G_VALUE_HOLDS_UINT (value)) + { + g_value_set_uint (value, u); + return (u <= G_MAXUINT32); + } + else if (G_VALUE_HOLDS_INT64 (value)) + { + g_value_set_int64 (value, u); + return (u <= G_MAXINT64); + } + else if (G_VALUE_HOLDS_UINT64 (value)) + { + g_value_set_uint64 (value, u); + return (u <= G_MAXUINT64); + } + else if (G_VALUE_HOLDS_DOUBLE (value)) + { + g_value_set_double (value, u); + return TRUE; + } + + return FALSE; +} + +GVariant * +g_settings_set_mapping (const GValue *value, + const GVariantType *expected_type, + gpointer user_data) +{ + gchar *type_string; + + if (G_VALUE_HOLDS_BOOLEAN (value)) + { + if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_BOOLEAN)) + return g_variant_new_boolean (g_value_get_boolean (value)); + } + + else if (G_VALUE_HOLDS_CHAR (value) || + G_VALUE_HOLDS_UCHAR (value)) + { + if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_BYTE)) + { + if (G_VALUE_HOLDS_CHAR (value)) + return g_variant_new_byte (g_value_get_char (value)); + else + return g_variant_new_byte (g_value_get_uchar (value)); + } + } + + else if (G_VALUE_HOLDS_INT (value) || + G_VALUE_HOLDS_INT64 (value) || + G_VALUE_HOLDS_DOUBLE (value)) + return g_settings_set_mapping_numeric (value, expected_type); + + else if (G_VALUE_HOLDS_UINT (value) || + G_VALUE_HOLDS_UINT64 (value)) + return g_settings_set_mapping_unsigned_numeric (value, expected_type); + + else if (G_VALUE_HOLDS_STRING (value)) + { + if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_STRING)) + return g_variant_new_string (g_value_get_string (value)); + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_OBJECT_PATH)) + return g_variant_new_object_path (g_value_get_string (value)); + else if (g_variant_type_equal (expected_type, G_VARIANT_TYPE_SIGNATURE)) + return g_variant_new_signature (g_value_get_string (value)); + } + + type_string = g_variant_type_dup_string (expected_type); + g_critical ("No GSettings bind handler for type \"%s\".", type_string); + g_free (type_string); + + return NULL; +} + +gboolean +g_settings_get_mapping (GValue *value, + GVariant *variant, + gpointer user_data) +{ + if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN)) + { + if (!G_VALUE_HOLDS_BOOLEAN (value)) + return FALSE; + g_value_set_boolean (value, g_variant_get_boolean (variant)); + return TRUE; + } + + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_BYTE)) + { + if (G_VALUE_HOLDS_UCHAR (value)) + g_value_set_uchar (value, g_variant_get_byte (variant)); + else if (G_VALUE_HOLDS_CHAR (value)) + g_value_set_char (value, (gchar) g_variant_get_byte (variant)); + else + return FALSE; + return TRUE; + } + + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT16) || + g_variant_is_of_type (variant, G_VARIANT_TYPE_INT32) || + g_variant_is_of_type (variant, G_VARIANT_TYPE_INT64) || + g_variant_is_of_type (variant, G_VARIANT_TYPE_DOUBLE)) + return g_settings_get_mapping_numeric (value, variant); + + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT16) || + g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32) || + g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT64) || + g_variant_is_of_type (variant, G_VARIANT_TYPE_HANDLE)) + return g_settings_get_mapping_unsigned_numeric (value, variant); + + else if (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING) || + 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; + } + + g_critical ("No GSettings bind handler for type \"%s\".", + g_variant_get_type_string (variant)); + + return FALSE; +} + +gboolean +g_settings_mapping_is_compatible (GType gvalue_type, + const GVariantType *variant_type) +{ + gboolean ok = FALSE; + + if (gvalue_type == G_TYPE_BOOLEAN) + ok = g_variant_type_equal (variant_type, G_VARIANT_TYPE_BOOLEAN); + else if (gvalue_type == G_TYPE_CHAR || + gvalue_type == G_TYPE_UCHAR) + ok = g_variant_type_equal (variant_type, G_VARIANT_TYPE_BYTE); + else if (gvalue_type == G_TYPE_INT || + gvalue_type == G_TYPE_UINT || + gvalue_type == G_TYPE_INT64 || + gvalue_type == G_TYPE_UINT64 || + gvalue_type == G_TYPE_DOUBLE) + ok = (g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT16) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT16) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT32) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT32) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_INT64) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_UINT64) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_HANDLE) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_DOUBLE)); + else if (gvalue_type == G_TYPE_STRING) + ok = (g_variant_type_equal (variant_type, G_VARIANT_TYPE_STRING) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_OBJECT_PATH) || + g_variant_type_equal (variant_type, G_VARIANT_TYPE_SIGNATURE)); + + return ok; +} diff --git a/gio/gsettings-mapping.h b/gio/gsettings-mapping.h new file mode 100644 index 000000000..18ce3183e --- /dev/null +++ b/gio/gsettings-mapping.h @@ -0,0 +1,39 @@ +/* + * Copyright © 2010 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Vincent Untz + */ + +#ifndef __G_SETTINGS_MAPPING_H__ +#define __G_SETTINGS_MAPPING_H__ + +#include + +G_GNUC_INTERNAL +GVariant * g_settings_set_mapping (const GValue *value, + const GVariantType *expected_type, + gpointer user_data); +G_GNUC_INTERNAL +gboolean g_settings_get_mapping (GValue *value, + GVariant *variant, + gpointer user_data); +G_GNUC_INTERNAL +gboolean g_settings_mapping_is_compatible (GType gvalue_type, + const GVariantType *variant_type); + +#endif /* __G_SETTINGS_MAPPING_H__ */ diff --git a/gio/gsettings.c b/gio/gsettings.c new file mode 100644 index 000000000..4d42838b0 --- /dev/null +++ b/gio/gsettings.c @@ -0,0 +1,1715 @@ +/* + * Copyright © 2009, 2010 Codethink Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Ryan Lortie + */ + +#include "config.h" +#include +#include +#include + +#include "gsettings.h" + +#include "gdelayedsettingsbackend.h" +#include "gsettingsbackendinternal.h" +#include "gsettings-mapping.h" +#include "gio-marshal.h" +#include "gsettingsschema.h" + +#include + +#include "gioalias.h" + +/** + * SECTION:gsettings + * @short_description: a high-level API for application settings + * + * The #GSettings class provides a convenient API for storing and retrieving + * application settings. + * + * When creating a GSettings instance, you have to specify a schema + * that describes the keys in your settings and their types and default + * values, as well as some other information. + * + * Normally, a schema has as fixed path that determines where the settings + * are stored in the conceptual global tree of settings. However, schemas + * can also be 'relocatable', i.e. not equipped with a fixed path. This is + * useful e.g. when the schema describes an 'account', and you want to be + * able to store a arbitrary number of accounts. + * + * Unlike other configuration systems (like GConf), GSettings does not + * restrict keys to basic types like strings and numbers. GSettings stores + * values as #GVariant, and allows any #GVariantType for keys. Key names + * are restricted to lowercase characters, numbers and '-'. Furthermore, + * the names must begin with a lowercase character, must not end + * with a '-', and must not contain consecutive dashes. Key names can + * be up to 32 characters long. + * + * Similar to GConf, the default values in GSettings schemas can be + * localized, but the localized values are stored in gettext catalogs + * and looked up with the domain that is specified in the gettext-domain + * attribute of the schemalist or schema elements + * and the category that is specified in the l10n attribute of the + * key element. + * + * GSettings uses schemas in a compact binary form that is created + * by the gschema-compile utility. The input is a schema description in + * an XML format that can be described by the following DTD: + * |[ + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * ]]> + * ]| + * + * + * Binding + * + * A very convenient feature of GSettings lets you bind #GObject properties + * directly to settings, using g_settings_bind(). Once a GObject property + * has been bound to a setting, changes on either side are automatically + * propagated to the other side. GSettings handles details like + * mapping between GObject and GVariant types, and preventing infinite + * cycles. + * + * + * This makes it very easy to hook up a preferences dialog to the + * underlying settings. To make this even more convenient, GSettings + * looks for a boolean property with the name "sensitivity" and + * automatically binds it to the writability of the bound setting. + * If this 'magic' gets in the way, it can be suppressed with the + * #G_SETTINGS_BIND_NO_SENSITIVITY flag. + * + * + */ + +struct _GSettingsPrivate +{ + GSettingsBackend *backend; + GSettingsSchema *schema; + gchar *schema_name; + gchar *context; + gchar *path; + + GDelayedSettingsBackend *delayed; +}; + +enum +{ + PROP_0, + PROP_BACKEND, + PROP_SCHEMA, + PROP_CONTEXT, + PROP_PATH, + PROP_HAS_UNAPPLIED, +}; + +enum +{ + SIGNAL_WRITABLE_CHANGE_EVENT, + SIGNAL_WRITABLE_CHANGED, + SIGNAL_CHANGE_EVENT, + SIGNAL_CHANGED, + N_SIGNALS +}; + +static guint g_settings_signals[N_SIGNALS]; + +G_DEFINE_TYPE (GSettings, g_settings, G_TYPE_OBJECT) + +static gboolean +g_settings_real_change_event (GSettings *settings, + const GQuark *keys, + gint n_keys) +{ + gint i; + + if (keys == NULL) + keys = g_settings_schema_list (settings->priv->schema, &n_keys); + + for (i = 0; i < n_keys; i++) + g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGED], + keys[i], g_quark_to_string (keys[i])); + + return FALSE; +} + +static gboolean +g_settings_real_writable_change_event (GSettings *settings, + GQuark key) +{ + const GQuark *keys = &key; + gint n_keys = 1; + gint i; + + if (key == 0) + keys = g_settings_schema_list (settings->priv->schema, &n_keys); + + for (i = 0; i < n_keys; i++) + { + const gchar *string = g_quark_to_string (keys[i]); + + g_signal_emit (settings, g_settings_signals[SIGNAL_WRITABLE_CHANGED], + keys[i], string); + g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGED], + keys[i], string); + } + + return FALSE; +} + +static void +settings_backend_changed (GSettingsBackend *backend, + const gchar *key, + gpointer origin_tag, + gpointer user_data) +{ + GSettings *settings = G_SETTINGS (user_data); + gboolean ignore_this; + gint i; + + g_assert (settings->priv->backend == backend); + + for (i = 0; key[i] == settings->priv->path[i]; i++); + + if (settings->priv->path[i] == '\0' && + g_settings_schema_has_key (settings->priv->schema, key + i)) + { + GQuark quark; + + quark = g_quark_from_string (key + i); + g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGE_EVENT], + 0, &quark, 1, &ignore_this); + } +} + +static void +settings_backend_path_changed (GSettingsBackend *backend, + const gchar *path, + gpointer origin_tag, + gpointer user_data) +{ + GSettings *settings = G_SETTINGS (user_data); + gboolean ignore_this; + + g_assert (settings->priv->backend == backend); + + if (g_str_has_prefix (settings->priv->path, path)) + g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGE_EVENT], + 0, NULL, 0, &ignore_this); +} + +static void +settings_backend_keys_changed (GSettingsBackend *backend, + const gchar *path, + const gchar * const *items, + gpointer origin_tag, + gpointer user_data) +{ + GSettings *settings = G_SETTINGS (user_data); + gboolean ignore_this; + gint i; + + g_assert (settings->priv->backend == backend); + + for (i = 0; settings->priv->path[i] && + settings->priv->path[i] == path[i]; i++); + + if (path[i] == '\0') + { + GQuark quarks[256]; + gint j, l = 0; + + for (j = 0; items[j]; j++) + { + const gchar *item = items[j]; + gint k; + + for (k = 0; item[k] == settings->priv->path[i + k]; k++); + + if (settings->priv->path[i + k] == '\0' && + g_settings_schema_has_key (settings->priv->schema, item + k)) + quarks[l++] = g_quark_from_string (item + k); + + /* "256 quarks ought to be enough for anybody!" + * If this bites you, I'm sorry. Please file a bug. + */ + g_assert (l < 256); + } + + if (l > 0) + g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGE_EVENT], + 0, quarks, l, &ignore_this); + } +} + +static void +settings_backend_writable_changed (GSettingsBackend *backend, + const gchar *key, + gpointer user_data) +{ + GSettings *settings = G_SETTINGS (user_data); + gboolean ignore_this; + gint i; + + g_assert (settings->priv->backend == backend); + + for (i = 0; key[i] == settings->priv->path[i]; i++); + + if (settings->priv->path[i] == '\0' && + g_settings_schema_has_key (settings->priv->schema, key + i)) + g_signal_emit (settings, g_settings_signals[SIGNAL_WRITABLE_CHANGE_EVENT], + 0, g_quark_from_string (key + i), &ignore_this); +} + +static void +settings_backend_path_writable_changed (GSettingsBackend *backend, + const gchar *path, + gpointer user_data) +{ + GSettings *settings = G_SETTINGS (user_data); + gboolean ignore_this; + + g_assert (settings->priv->backend == backend); + + if (g_str_has_prefix (settings->priv->path, path)) + g_signal_emit (settings, g_settings_signals[SIGNAL_WRITABLE_CHANGE_EVENT], + 0, (GQuark) 0, &ignore_this); +} + +static void +g_settings_constructed (GObject *object) +{ + GSettings *settings = G_SETTINGS (object); + const gchar *schema_path; + + settings->priv->schema = g_settings_schema_new (settings->priv->schema_name); + schema_path = g_settings_schema_get_path (settings->priv->schema); + + if (settings->priv->path && schema_path && strcmp (settings->priv->path, schema_path) != 0) + g_error ("settings object created with schema '%s' and path '%s', but " + "path '%s' is specified by schema", + settings->priv->schema_name, settings->priv->path, schema_path); + + if (settings->priv->path == NULL) + { + if (schema_path == NULL) + g_error ("attempting to create schema '%s' without a path", + settings->priv->schema_name); + + settings->priv->path = g_strdup (schema_path); + } + + settings->priv->backend = g_settings_backend_get_with_context (settings->priv->context); + g_settings_backend_watch (settings->priv->backend, + settings_backend_changed, + settings_backend_path_changed, + settings_backend_keys_changed, + settings_backend_writable_changed, + settings_backend_path_writable_changed, + settings); + g_settings_backend_subscribe (settings->priv->backend, + settings->priv->path); +} + +static void +g_settings_init (GSettings *settings) +{ + settings->priv = G_TYPE_INSTANCE_GET_PRIVATE (settings, + G_TYPE_SETTINGS, + GSettingsPrivate); +} + +/** + * g_settings_delay: + * @settings: a #GSettings object + * + * Changes the #GSettings object into 'delay-apply' mode. In this + * mode, changes to @settings are not immediately propagated to the + * backend, but kept locally until g_settings_apply() is called. + * + * Since: 2.26 + */ +void +g_settings_delay (GSettings *settings) +{ + if (settings->priv->delayed) + return; + + settings->priv->delayed = + g_delayed_settings_backend_new (settings->priv->backend, settings); + g_settings_backend_unwatch (settings->priv->backend, settings); + g_object_unref (settings->priv->backend); + + settings->priv->backend = G_SETTINGS_BACKEND (settings->priv->delayed); + g_settings_backend_watch (settings->priv->backend, + settings_backend_changed, + settings_backend_path_changed, + settings_backend_keys_changed, + settings_backend_writable_changed, + settings_backend_path_writable_changed, + settings); +} + +/** + * g_settings_apply: + * @settings: a #GSettings instance + * + * Applies any changes that have been made to the settings. This + * function does nothing unless @settings is in 'delay-apply' mode; + * see g_settings_set_delay_apply(). In the normal case settings are + * always applied immediately. + **/ +void +g_settings_apply (GSettings *settings) +{ + if (settings->priv->delayed) + { + GDelayedSettingsBackend *delayed; + + delayed = G_DELAYED_SETTINGS_BACKEND (settings->priv->backend); + g_delayed_settings_backend_apply (delayed); + } +} + +/** + * g_settings_revert: + * @settings: a #GSettings instance + * + * Reverts all non-applied changes to the settings. This function + * does nothing unless @settings is in 'delay-apply' mode; see + * g_settings_set_delay_apply(). In the normal case settings are + * always applied immediately. + * + * Change notifications will be emitted for affected keys. + **/ +void +g_settings_revert (GSettings *settings) +{ + if (settings->priv->delayed) + { + GDelayedSettingsBackend *delayed; + + delayed = G_DELAYED_SETTINGS_BACKEND (settings->priv->backend); + g_delayed_settings_backend_revert (delayed); + } +} + +static void +g_settings_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GSettings *settings = G_SETTINGS (object); + + switch (prop_id) + { + case PROP_SCHEMA: + g_assert (settings->priv->schema_name == NULL); + settings->priv->schema_name = g_value_dup_string (value); + break; + + case PROP_PATH: + settings->priv->path = g_value_dup_string (value); + break; + + case PROP_CONTEXT: + settings->priv->context = g_value_dup_string (value); + break; + + default: + g_assert_not_reached (); + } +} + +/** + * g_settings_get_has_unapplied: + * @settings: a #GSettings object + * @returns: %TRUE if @settings has unapplied changes + * + * Returns whether the #GSettings object has any unapplied + * changes. This can only be the case if it is in 'delayed-apply' mode. + * + * Since: 2.26 + */ +gboolean +g_settings_get_has_unapplied (GSettings *settings) +{ + return settings->priv->delayed && + g_delayed_settings_backend_get_has_unapplied ( + G_DELAYED_SETTINGS_BACKEND (settings->priv->backend)); +} + +static void +g_settings_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GSettings *settings = G_SETTINGS (object); + + switch (prop_id) + { + case PROP_SCHEMA: + g_value_set_object (value, settings->priv->schema); + break; + + case PROP_HAS_UNAPPLIED: + g_value_set_boolean (value, g_settings_get_has_unapplied (settings)); + break; + + default: + g_assert_not_reached (); + } +} + +static void +g_settings_finalize (GObject *object) +{ + GSettings *settings = G_SETTINGS (object); + + g_settings_backend_unwatch (settings->priv->backend, settings); + g_settings_backend_unsubscribe (settings->priv->backend, + settings->priv->path); + g_object_unref (settings->priv->backend); + g_object_unref (settings->priv->schema); + g_free (settings->priv->schema_name); + g_free (settings->priv->path); +} + +static void +g_settings_class_init (GSettingsClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + class->writable_change_event = g_settings_real_writable_change_event; + class->change_event = g_settings_real_change_event; + + object_class->set_property = g_settings_set_property; + object_class->get_property = g_settings_get_property; + object_class->constructed = g_settings_constructed; + object_class->finalize = g_settings_finalize; + + g_type_class_add_private (object_class, sizeof (GSettingsPrivate)); + + /** + * GSettings::changed: + * @settings: the object on which the signal was emitted + * + * The "changed" signal is emitted when a key has potentially changed. + * You should call one of the g_settings_get() calls to check the new + * value. + * + * This signal supports detailed connections. You can connect to the + * detailed signal "changed::x" in order to only receive callbacks + * when key "x" changes. + */ + g_settings_signals[SIGNAL_CHANGED] = + g_signal_new ("changed", G_TYPE_SETTINGS, + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (GSettingsClass, changed), + NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, + 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE); + + /** + * GSettings::change-event: + * @settings: the object on which the signal was emitted + * @keys: an array of #GQuarks for the changed keys, or %NULL + * @n_keys: the length of the @keys array, or 0 + * @returns: %TRUE to stop other handlers from being invoked for the + * event. FALSE to propagate the event further. + * + * The "change-event" signal is emitted once per change event that + * affects this settings object. You should connect to this signal + * only if you are interested in viewing groups of changes before they + * are split out into multiple emissions of the "changed" signal. + * For most use cases it is more appropriate to use the "changed" signal. + * + * In the event that the change event applies to one or more specified + * keys, @keys will be an array of #GQuark of length @n_keys. In the + * event that the change event applies to the #GSettings object as a + * whole (ie: potentially every key has been changed) then @keys will + * be %NULL and @n_keys will be 0. + * + * The default handler for this signal invokes the "changed" signal + * for each affected key. If any other connected handler returns + * %TRUE then this default functionality will be supressed. + */ + g_settings_signals[SIGNAL_CHANGE_EVENT] = + g_signal_new ("change-event", G_TYPE_SETTINGS, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSettingsClass, change_event), + g_signal_accumulator_true_handled, NULL, + _gio_marshal_BOOL__POINTER_INT, + G_TYPE_BOOLEAN, 2, G_TYPE_POINTER, G_TYPE_INT); + + /** + * GSettings::writable-changed: + * @settings: the object on which the signal was emitted + * @key: the key + * + * The "writable-changed" signal is emitted when the writability of a + * key has potentially changed. You should call + * g_settings_is_writable() in order to determine the new status. + * + * This signal supports detailed connections. You can connect to the + * detailed signal "writable-changed::x" in order to only receive + * callbacks when the writability of "x" changes. + */ + g_settings_signals[SIGNAL_WRITABLE_CHANGED] = + g_signal_new ("writable-changed", G_TYPE_SETTINGS, + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (GSettingsClass, changed), + NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, + 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE); + + /** + * GSettings::writable-change-event: + * @settings: the object on which the signal was emitted + * @key: the quark of the key, or 0 + * @returns: %TRUE to stop other handlers from being invoked for the + * event. FALSE to propagate the event further. + * + * The "writable-change-event" signal is emitted once per writability + * change event that affects this settings object. You should connect + * to this signal if you are interested in viewing groups of changes + * before they are split out into multiple emissions of the + * "writable-changed" signal. For most use cases it is more + * appropriate to use the "writable-changed" signal. + * + * In the event that the writability change applies only to a single + * key, @key will be set to the #GQuark for that key. In the event + * that the writability change affects the entire settings object, + * @key will be 0. + * + * The default handler for this signal invokes the "writable-changed" + * and "changed" signals for each affected key. This is done because + * changes in writability might also imply changes in value (if for + * example, a new mandatory setting is introduced). If any other + * connected handler returns %TRUE then this default functionality + * will be supressed. + */ + g_settings_signals[SIGNAL_WRITABLE_CHANGE_EVENT] = + g_signal_new ("writable-change-event", G_TYPE_SETTINGS, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSettingsClass, writable_change_event), + g_signal_accumulator_true_handled, NULL, + _gio_marshal_BOOLEAN__UINT, G_TYPE_BOOLEAN, 1, G_TYPE_UINT); + + /** + * GSettings:context: + * + * The name of the context that the settings are stored in. + */ + g_object_class_install_property (object_class, PROP_CONTEXT, + g_param_spec_string ("context", + P_("Context name"), + P_("The name of the context for this settings object"), + "", G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GSettings:schema: + * + * The name of the schema that describes the types of keys + * for this #GSettings object. + */ + g_object_class_install_property (object_class, PROP_SCHEMA, + g_param_spec_string ("schema", + P_("Schema name"), + P_("The name of the schema for this settings object"), + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GSettings:path: + * + * The path within the backend where the settings are stored. + */ + g_object_class_install_property (object_class, PROP_PATH, + g_param_spec_string ("path", + P_("Base path"), + P_("The path within the backend where the settings are"), + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GSettings:has-unapplied: + * + * If this property is %TRUE, the #GSettings object has outstanding + * changes that will be applied when g_settings_apply() is called. + */ + g_object_class_install_property (object_class, PROP_HAS_UNAPPLIED, + g_param_spec_boolean ("has-unapplied", + P_("Has unapplied changes"), + P_("TRUE if there are outstanding changes to apply()"), + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + +} + +/** + * g_settings_get_value: + * @settings: a #GSettings object + * @key: the key to get the value for + * @returns: a new #GVariant + * + * Gets the value that is stored in @settings for @key. + * + * It is a programmer error to give a @key that isn't valid for + * @settings. + * + * Since: 2.26 + */ +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; + + sval = g_settings_schema_get_value (settings->priv->schema, key, &options); + + if G_UNLIKELY (sval == NULL) + g_error ("schema '%s' does not contain a key named '%s'", + settings->priv->schema_name, key); + + path = g_strconcat (settings->priv->path, key, NULL); + type = g_variant_get_type (sval); + value = g_settings_backend_read (settings->priv->backend, path, type); + g_free (path); + + if (options != NULL) + { + GVariantIter iter; + const gchar *key; + GVariant *value; + + g_variant_iter_init (&iter, options); + while (g_variant_iter_loop (&iter, "{&sv}", &key, &value)) + { + if (strcmp (key, "l10n") == 0) + g_variant_get (value, "(y&s)", &lc_char, &unparsed); + else + g_warning ("unknown schema extension '%s'", key); + } + } + + if (value && !g_variant_is_of_type (value, type)) + { + g_variant_unref (value); + value = NULL; + } + + if (value == NULL && lc_char != '\0') + /* we will need to translate the schema default value */ + { + const gchar *translated; + GError *error = NULL; + const gchar *domain; + gint lc_category; + + domain = g_settings_schema_get_gettext_domain (settings->priv->schema); + + if (lc_char == 't') + lc_category = LC_TIME; + else + lc_category = LC_MESSAGES; + + translated = dcgettext (domain, unparsed, lc_category); + + if (translated != unparsed) + /* it was translated, so we need to re-parse it */ + { + 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); + } + } + } + + if (value == NULL) + /* either translation failed or there was none to do. + * use the pre-compiled default. + */ + value = g_variant_ref (sval); + + g_variant_unref (sval); + + return value; +} + +/** + * g_settings_set_value: + * @settings: a #GSettings object + * @key: the name of the key to set + * @value: a #GVariant of the correct type + * @returns: %TRUE if setting the key succeeded, + * %FALSE if the key was not writable + * + * Sets @key in @settings to @value. + * + * It is a programmer error to give a @key that isn't valid for + * @settings. It is a programmer error to give a @value of the + * incorrect type. + * + * If @value is floating then this function consumes the reference. + * + * Since: 2.26 + **/ +gboolean +g_settings_set_value (GSettings *settings, + const gchar *key, + GVariant *value) +{ + gboolean correct_type; + gboolean result; + GVariant *sval; + gchar *path; + + sval = g_settings_schema_get_value (settings->priv->schema, key, NULL); + correct_type = g_variant_is_of_type (value, g_variant_get_type (sval)); + g_variant_unref (sval); + + g_return_val_if_fail (correct_type, FALSE); + + path = g_strconcat (settings->priv->path, key, NULL); + result = g_settings_backend_write (settings->priv->backend, + path, value, NULL); + g_free (path); + + return result; +} + +/** + * g_settings_get: + * @settings: a #GSettings object + * @key: the key to get the value for + * @format: a #GVariant format string + * @...: arguments as per @format + * + * Gets the value that is stored at @key in @settings. + * + * A convenience function that combines g_settings_get_value() with + * g_variant_get(). + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or a @format_string that doesn't match the type of @key according + * to the schema of @settings. + * + * Since: 2.26 + */ +void +g_settings_get (GSettings *settings, + const gchar *key, + const gchar *format, + ...) +{ + GVariant *value; + va_list ap; + + value = g_settings_get_value (settings, key); + + va_start (ap, format); + g_variant_get_va (value, format, NULL, &ap); + va_end (ap); + + g_variant_unref (value); +} + +/** + * g_settings_set: + * @settings: a #GSettings object + * @key: the name of the key to set + * @format: a #GVariant format string + * @...: arguments as per @format + * @returns: %TRUE if setting the key succeeded, + * %FALSE if the key was not writable + * + * Sets @key in @settings to @value. + * + * A convenience function that combines g_settings_set_value() with + * g_variant_new(). + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or a @format that doesn't match the type of @key according + * to the schema of @settings. + * + * Since: 2.26 + */ +gboolean +g_settings_set (GSettings *settings, + const gchar *key, + const gchar *format, + ...) +{ + GVariant *value; + va_list ap; + + va_start (ap, format); + value = g_variant_new_va (format, NULL, &ap); + va_end (ap); + + return g_settings_set_value (settings, key, value); +} + +/** + * g_settings_is_writable: + * @settings: a #GSettings object + * @name: the name of a key + * @returns: %TRUE if the key @name is writable + * + * Finds out if a key can be written or not + * + * Since: 2.26 + */ +gboolean +g_settings_is_writable (GSettings *settings, + const gchar *name) +{ + gboolean writable; + gchar *path; + + path = g_strconcat (settings->priv->path, name, NULL); + writable = g_settings_backend_get_writable (settings->priv->backend, path); + g_free (path); + + return writable; +} + +/** + * g_settings_get_child: + * @settings: a #GSettings object + * @name: the name of the 'child' schema + * @returns: a 'child' settings object + * + * Creates a 'child' settings object which has a base path of + * base-path/@name", where + * base-path is the base path of @settings. + * + * The schema for the child settings object must have been declared + * in the schema of @settings using a child element. + * + * Since: 2.26 + */ +GSettings * +g_settings_get_child (GSettings *settings, + const gchar *name) +{ + GVariant *child_schema; + gchar *child_path; + gchar *child_name; + GSettings *child; + + child_name = g_strconcat (name, "/", NULL); + child_schema = g_settings_schema_get_value (settings->priv->schema, + child_name, NULL); + if (child_schema == NULL || + !g_variant_is_of_type (child_schema, G_VARIANT_TYPE_STRING)) + g_error ("Schema '%s' has no child '%s'", + settings->priv->schema_name, name); + + child_path = g_strconcat (settings->priv->path, child_name, NULL); + child = g_object_new (G_TYPE_SETTINGS, + "schema", g_variant_get_string (child_schema, NULL), + "path", child_path, + NULL); + g_variant_unref (child_schema); + g_free (child_path); + g_free (child_name); + + return child; +} + +/** + * g_settings_new: + * @schema: the name of the schema + * @returns: a new #GSettings object + * + * Creates a new #GSettings object with a given schema. + * + * Since: 2.26 + */ +GSettings * +g_settings_new (const gchar *schema) +{ + return g_object_new (G_TYPE_SETTINGS, + "schema", schema, + NULL); +} + +/** + * g_settings_new_with_path: + * @schema: the name of the schema + * @path: the path to use + * @returns: a new #GSettings object + * + * Creates a new #GSettings object with a given schema and path. + * + * You only need to do this if you want to directly create a settings + * object with a schema that doesn't have a specified path of its own. + * That's quite rare. + * + * It is a programmer error to call this function for a schema that + * has an explicitly specified path. + * + * Since: 2.26 + */ +GSettings * +g_settings_new_with_path (const gchar *schema, + const gchar *path) +{ + return g_object_new (G_TYPE_SETTINGS, + "schema", schema, + "path", path, + NULL); +} + +/** + * g_settings_new_with_context: + * @schema: the name of the schema + * @context: the context to use + * @returns: a new #GSettings object + * + * Creates a new #GSettings object with a given schema and context. + * + * Creating settings objects with a context allow accessing settings + * from a database other than the usual one. For example, it may make + * sense to specify "defaults" in order to get a settings object that + * modifies the system default settings instead of the settings for this + * user. + * + * It is a programmer error to call this function for an unsupported + * context. Use g_settings_supports_context() to determine if a context + * is supported if you are unsure. + * + * Since: 2.26 + */ +GSettings * +g_settings_new_with_context (const gchar *schema, + const gchar *context) +{ + return g_object_new (G_TYPE_SETTINGS, + "schema", schema, + "context", context, + NULL); +} + +/** + * g_settings_new_with_context_and_path: + * @schema: the name of the schema + * @path: the path to use + * @returns: a new #GSettings object + * + * Creates a new #GSettings object with a given schema, context and + * path. + * + * This is a mix of g_settings_new_with_context() and + * g_settings_new_with_path(). + * + * Since: 2.26 + */ +GSettings * +g_settings_new_with_context_and_path (const gchar *schema, + const gchar *context, + const gchar *path) +{ + return g_object_new (G_TYPE_SETTINGS, + "schema", schema, + "context", context, + "path", path, + NULL); +} + +typedef struct +{ + GSettings *settings; + GObject *object; + + GSettingsBindGetMapping get_mapping; + GSettingsBindSetMapping set_mapping; + gpointer user_data; + GDestroyNotify destroy; + + guint writable_handler_id; + guint property_handler_id; + const GParamSpec *property; + guint key_handler_id; + GVariantType *type; + const gchar *key; + + /* prevent recursion */ + gboolean running; +} GSettingsBinding; + +static void +g_settings_binding_free (gpointer data) +{ + GSettingsBinding *binding = data; + + g_assert (!binding->running); + + if (binding->writable_handler_id) + g_signal_handler_disconnect (binding->settings, + binding->writable_handler_id); + + if (binding->key_handler_id) + g_signal_handler_disconnect (binding->settings, + binding->key_handler_id); + + if (g_signal_handler_is_connected (binding->object, + binding->property_handler_id)) + g_signal_handler_disconnect (binding->object, + binding->property_handler_id); + + g_variant_type_free (binding->type); + g_object_unref (binding->settings); + + if (binding->destroy) + binding->destroy (binding->user_data); + + g_slice_free (GSettingsBinding, binding); +} + +static GQuark +g_settings_binding_quark (const char *property) +{ + GQuark quark; + gchar *tmp; + + tmp = g_strdup_printf ("gsettingsbinding-%s", property); + quark = g_quark_from_string (tmp); + g_free (tmp); + + return quark; +} + +static void +g_settings_binding_key_changed (GSettings *settings, + const gchar *key, + gpointer user_data) +{ + GSettingsBinding *binding = user_data; + GValue value = { }; + GVariant *variant; + + g_assert (settings == binding->settings); + g_assert (key == binding->key); + + if (binding->running) + return; + + binding->running = TRUE; + + g_value_init (&value, binding->property->value_type); + variant = g_settings_get_value (settings, key); + if (binding->get_mapping (&value, variant, binding->user_data)) + g_object_set_property (binding->object, + binding->property->name, + &value); + g_value_unset (&value); + + binding->running = FALSE; +} + +static void +g_settings_binding_writable_changed (GSettings *settings, + const gchar *key, + gpointer user_data) +{ + GSettingsBinding *binding = user_data; + gboolean writable; + + g_assert (settings == binding->settings); + g_assert (key == binding->key); + + writable = g_settings_is_writable (settings, key); + g_object_set (binding->object, "sensitive", writable, NULL); +} + +static void +g_settings_binding_property_changed (GObject *object, + const GParamSpec *pspec, + gpointer user_data) +{ + GSettingsBinding *binding = user_data; + GValue value = { }; + GVariant *variant; + + g_assert (object == binding->object); + g_assert (pspec == binding->property); + + if (binding->running) + return; + + binding->running = TRUE; + + g_value_init (&value, pspec->value_type); + g_object_get_property (object, pspec->name, &value); + if ((variant = binding->set_mapping (&value, binding->type, + binding->user_data))) + { + g_settings_set_value (binding->settings, + binding->key, + variant); + g_variant_unref (variant); + } + g_value_unset (&value); + + binding->running = FALSE; +} + +/** + * g_settings_bind: + * @settings: a #GSettings object + * @key: the key to bind + * @object: a #GObject + * @property: the name of the property to bind + * @flags: flags for the binding + * + * Create a binding between the @key in the @settings object + * and the property @property of @object. + * + * The binding uses the default GIO mapping functions to map + * between the settings and property values. These functions + * handle booleans, numeric types and string types in a + * straightforward way. Use g_settings_bind_with_mapping() + * if you need a custom mapping, or map between types that + * are not supported by the default mapping functions. + * + * Note that the lifecycle of the binding is tied to the object, + * and that you can have only one binding per object property. + * If you bind the same property twice on the same object, the second + * binding overrides the first one. + * + * Since: 2.26 + */ +void +g_settings_bind (GSettings *settings, + const gchar *key, + gpointer object, + const gchar *property, + GSettingsBindFlags flags) +{ + g_settings_bind_with_mapping (settings, key, object, property, + flags, NULL, NULL, NULL, NULL); +} + +/** + * g_settings_bind_with_mapping: + * @settings: a #GSettings object + * @key: the key to bind + * @object: a #GObject + * @property: the name of the property to bind + * @flags: flags for the binding + * @get_mapping: a function that gets called to convert values + * from @settings to @object, or %NULL to use the default GIO mapping + * @set_mapping: a function that gets called to convert values + * from @object to @settings, or %NULL to use the default GIO mapping + * @user_data: data that gets passed to @get_mapping and @set_mapping + * @destroy: #GDestroyNotify function for @user_data + * + * Create a binding between the @key in the @settings object + * and the property @property of @object. + * + * The binding uses the provided mapping functions to map between + * settings and property values. + * + * Note that the lifecycle of the binding is tied to the object, + * and that you can have only one binding per object property. + * If you bind the same property twice on the same object, the second + * binding overrides the first one. + * + * Since: 2.26 + */ +void +g_settings_bind_with_mapping (GSettings *settings, + const gchar *key, + gpointer object, + const gchar *property, + GSettingsBindFlags flags, + GSettingsBindGetMapping get_mapping, + GSettingsBindSetMapping set_mapping, + gpointer user_data, + GDestroyNotify destroy) +{ + GSettingsBinding *binding; + GObjectClass *objectclass; + gboolean bind_sensitive; + gchar *detailed_signal; + GQuark binding_quark; + + objectclass = G_OBJECT_GET_CLASS (object); + + binding = g_slice_new0 (GSettingsBinding); + binding->settings = g_object_ref (settings); + binding->object = object; + binding->key = g_intern_string (key); + binding->property = g_object_class_find_property (objectclass, property); + binding->user_data = user_data; + binding->destroy = destroy; + binding->get_mapping = get_mapping ? get_mapping : g_settings_get_mapping; + binding->set_mapping = set_mapping ? set_mapping : g_settings_set_mapping; + + if (!(flags & (G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET))) + flags |= G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET; + + if (binding->property == NULL) + { + g_critical ("g_settings_bind: no property '%s' on class '%s'", + property, G_OBJECT_TYPE_NAME (object)); + return; + } + + { + GVariant *value; + + value = g_settings_schema_get_value (settings->priv->schema, key, NULL); + binding->type = g_variant_type_copy (g_variant_get_type (value)); + if (value) + g_variant_unref (value); + } + + if (binding->type == NULL) + { + g_critical ("g_settings_bind: no key '%s' on schema '%s'", + key, settings->priv->schema_name); + return; + } + + if (((get_mapping == NULL && (flags & G_SETTINGS_BIND_GET)) || + (set_mapping == NULL && (flags & G_SETTINGS_BIND_SET))) && + !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'" + "on schema '%s'", property, G_OBJECT_TYPE_NAME (object), + g_type_name (binding->property->value_type), + g_variant_type_dup_string (binding->type), key, + settings->priv->schema_name); + return; + } + + if ((flags & G_SETTINGS_BIND_SET) && + (~flags & G_SETTINGS_BIND_NO_SENSITIVITY)) + { + GParamSpec *sensitive; + + sensitive = g_object_class_find_property (objectclass, "sensitive"); + bind_sensitive = sensitive && sensitive->value_type == G_TYPE_BOOLEAN; + } + else + bind_sensitive = FALSE; + + if (flags & G_SETTINGS_BIND_SET) + { + detailed_signal = g_strdup_printf ("notify::%s", property); + binding->property_handler_id = + g_signal_connect (object, detailed_signal, + G_CALLBACK (g_settings_binding_property_changed), + binding); + g_free (detailed_signal); + + if (~flags & G_SETTINGS_BIND_GET) + g_settings_binding_property_changed (object, + binding->property, + binding); + } + + if (flags & G_SETTINGS_BIND_GET) + { + if (~flags & G_SETTINGS_BIND_GET_NO_CHANGES) + { + detailed_signal = g_strdup_printf ("changed::%s", key); + binding->key_handler_id = + g_signal_connect (settings, detailed_signal, + G_CALLBACK (g_settings_binding_key_changed), + binding); + g_free (detailed_signal); + } + + g_settings_binding_key_changed (settings, binding->key, binding); + } + + if (bind_sensitive) + { + detailed_signal = g_strdup_printf ("writable-changed::%s", key); + binding->writable_handler_id = + g_signal_connect (settings, detailed_signal, + G_CALLBACK (g_settings_binding_writable_changed), + binding); + g_free (detailed_signal); + + g_settings_binding_writable_changed (settings, binding->key, binding); + } + + binding_quark = g_settings_binding_quark (property); + g_object_set_qdata_full (object, binding_quark, + binding, g_settings_binding_free); +} + +/** + * g_settings_unbind: + * @object: the object + * @property: the property whose binding is removed + * + * Removes an existing binding for @property on @object. + * + * Note that bindings are automatically removed when the + * object is finalized, so it is rarely necessary to call this + * function. + * + * Since: 2.26 + */ +void +g_settings_unbind (gpointer object, + const gchar *property) +{ + GQuark binding_quark; + + binding_quark = g_settings_binding_quark (property); + g_object_set_qdata (object, binding_quark, NULL); +} + +/** + * g_settings_get_string: + * @settings: a #GSettings object + * @key: the key to get the value for + * @returns: a newly-allocated string + * + * Gets the value that is stored at @key in @settings. + * + * A convenience variant of g_settings_get() for strings. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type string. + * + * Since: 2.26 + */ +gchar * +g_settings_get_string (GSettings *settings, + const gchar *key) +{ + GVariant *value; + gchar *result; + + value = g_settings_get_value (settings, key); + result = g_variant_dup_string (value, NULL); + g_variant_unref (value); + + return result; +} + +/** + * g_settings_set_string: + * @settings: a #GSettings object + * @key: the name of the key to set + * @value: the value to set it to + * @returns: %TRUE if setting the key succeeded, + * %FALSE if the key was not writable + * + * Sets @key in @settings to @value. + * + * A convenience variant of g_settings_set() for strings. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type string. + * + * Since: 2.26 + */ +gboolean +g_settings_set_string (GSettings *settings, + const gchar *key, + const gchar *value) +{ + return g_settings_set_value (settings, key, g_variant_new_string (value)); +} + +/** + * g_settings_get_int: + * @settings: a #GSettings object + * @key: the key to get the value for + * @returns: an integer + * + * Gets the value that is stored at @key in @settings. + * + * A convenience variant of g_settings_get() for 32-bit integers. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type int32. + * + * Since: 2.26 + */ +gint +g_settings_get_int (GSettings *settings, + const gchar *key) +{ + GVariant *value; + gint result; + + value = g_settings_get_value (settings, key); + result = g_variant_get_int32 (value); + g_variant_unref (value); + + return result; +} + +/** + * g_settings_set_int: + * @settings: a #GSettings object + * @key: the name of the key to set + * @value: the value to set it to + * @returns: %TRUE if setting the key succeeded, + * %FALSE if the key was not writable + * + * Sets @key in @settings to @value. + * + * A convenience variant of g_settings_set() for 32-bit integers. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type int32. + * + * Since: 2.26 + */ +gboolean +g_settings_set_int (GSettings *settings, + const gchar *key, + gint value) +{ + return g_settings_set_value (settings, key, g_variant_new_int32 (value)); +} + +/** + * g_settings_get_double: + * @settings: a #GSettings object + * @key: the key to get the value for + * @returns: a double + * + * Gets the value that is stored at @key in @settings. + * + * A convenience variant of g_settings_get() for doubles. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type double. + * + * Since: 2.26 + */ +gdouble +g_settings_get_double (GSettings *settings, + const gchar *key) +{ + GVariant *value; + gdouble result; + + value = g_settings_get_value (settings, key); + result = g_variant_get_double (value); + g_variant_unref (value); + + return result; +} + +/** + * g_settings_set_double: + * @settings: a #GSettings object + * @key: the name of the key to set + * @value: the value to set it to + * @returns: %TRUE if setting the key succeeded, + * %FALSE if the key was not writable + * + * Sets @key in @settings to @value. + * + * A convenience variant of g_settings_set() for doubles. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type double. + * + * Since: 2.26 + */ +gboolean +g_settings_set_double (GSettings *settings, + const gchar *key, + gdouble value) +{ + return g_settings_set_value (settings, key, g_variant_new_double (value)); +} + +/** + * g_settings_get_boolean: + * @settings: a #GSettings object + * @key: the key to get the value for + * @returns: a boolean + * + * Gets the value that is stored at @key in @settings. + * + * A convenience variant of g_settings_get() for booleans. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type boolean. + * + * Since: 2.26 + */ +gboolean +g_settings_get_boolean (GSettings *settings, + const gchar *key) +{ + GVariant *value; + gboolean result; + + value = g_settings_get_value (settings, key); + result = g_variant_get_boolean (value); + g_variant_unref (value); + + return result; +} + +/** + * g_settings_set_boolean: + * @settings: a #GSettings object + * @key: the name of the key to set + * @value: the value to set it to + * @returns: %TRUE if setting the key succeeded, + * %FALSE if the key was not writable + * + * Sets @key in @settings to @value. + * + * A convenience variant of g_settings_set() for booleans. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type boolean. + * + * Since: 2.26 + */ +gboolean +g_settings_set_boolean (GSettings *settings, + const gchar *key, + gboolean value) +{ + return g_settings_set_value (settings, key, g_variant_new_boolean (value)); +} + +/** + * g_settings_get_strv: + * @settings: a #GSettings object + * @key: the key to get the value for + * @returns: a newly-allocated, %NULL-terminated array of strings + * + * Gets the value that is stored at @key in @settings. + * + * A convenience variant of g_settings_get() for string arrays. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type 'string array'. + * + * Since: 2.26 + */ +gchar ** +g_settings_get_strv (GSettings *settings, + const gchar *key, + gsize *length) +{ + GVariant *value; + gchar **result; + + value = g_settings_get_value (settings, key); + result = g_variant_dup_strv (value, length); + g_variant_unref (value); + + return result; +} + +/** + * g_settings_set_strv: + * @settings: a #GSettings object + * @key: the name of the key to set + * @value: the value to set it to + * @returns: %TRUE if setting the key succeeded, + * %FALSE if the key was not writable + * + * Sets @key in @settings to @value. + * + * A convenience variant of g_settings_set() for string arrays. + * + * It is a programmer error to pass a @key that isn't valid for + * @settings or is not of type 'string array'. + * + * Since: 2.26 + */ +gboolean +g_settings_set_strv (GSettings *settings, + const gchar *key, + const gchar * const *value, + gssize length) +{ + return g_settings_set_value (settings, key, g_variant_new_strv (value, length)); +} + +#define __G_SETTINGS_C__ +#include "gioaliasdef.c" diff --git a/gio/gsettings.h b/gio/gsettings.h new file mode 100644 index 000000000..5a70b5d4b --- /dev/null +++ b/gio/gsettings.h @@ -0,0 +1,203 @@ +/* + * Copyright © 2009, 2010 Codethink Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Ryan Lortie + */ + +#ifndef __G_SETTINGS_H__ +#define __G_SETTINGS_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_SETTINGS (g_settings_get_type ()) +#define G_SETTINGS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_SETTINGS, GSettings)) +#define G_SETTINGS_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_SETTINGS, GSettingsClass)) +#define G_IS_SETTINGS(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_SETTINGS)) +#define G_IS_SETTINGS_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_SETTINGS)) +#define G_SETTINGS_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_SETTINGS, GSettingsClass)) + +typedef struct _GSettingsPrivate GSettingsPrivate; +typedef struct _GSettingsClass GSettingsClass; +typedef struct _GSettings GSettings; + +struct _GSettingsClass +{ + GObjectClass parent_class; + + /* Signals */ + void (*writable_changed) (GSettings *settings, + const gchar *key); + void (*changed) (GSettings *settings, + const gchar *key); + gboolean (*writable_change_event) (GSettings *settings, + GQuark key); + gboolean (*change_event) (GSettings *settings, + const GQuark *keys, + gint n_keys); + + gpointer padding[20]; +}; + +struct _GSettings +{ + GObject parent_instance; + GSettingsPrivate *priv; +}; + + +GType g_settings_get_type (void); + +GSettings * g_settings_new (const gchar *schema); +GSettings * g_settings_new_with_path (const gchar *schema, + const gchar *path); +gboolean g_settings_supports_context (const gchar *context); +GSettings * g_settings_new_with_context (const gchar *schema, + const gchar *context); +GSettings * g_settings_new_with_context_and_path (const gchar *schema, + const gchar *context, + const gchar *path); + +gboolean g_settings_set_value (GSettings *settings, + const gchar *key, + GVariant *value); +GVariant * g_settings_get_value (GSettings *settings, + const gchar *key); + +gboolean g_settings_set (GSettings *settings, + const gchar *key, + const gchar *format, + ...); +void g_settings_get (GSettings *settings, + const gchar *key, + const gchar *format, + ...); + +gint g_settings_get_int (GSettings *settings, + const gchar *key); +gboolean g_settings_set_int (GSettings *settings, + const gchar *key, + gint value); +gchar * g_settings_get_string (GSettings *settings, + const gchar *key); +gboolean g_settings_set_string (GSettings *settings, + const gchar *key, + const gchar *value); +gboolean g_settings_get_boolean (GSettings *settings, + const gchar *key); +gboolean g_settings_set_boolean (GSettings *settings, + const gchar *key, + gboolean value); +gdouble g_settings_get_double (GSettings *settings, + const gchar *key); +gboolean g_settings_set_double (GSettings *settings, + const gchar *key, + gdouble value); +gchar ** g_settings_get_strv (GSettings *settings, + const gchar *key, + gsize *length); +gboolean g_settings_set_strv (GSettings *settings, + const gchar *key, + const gchar *const *value, + gssize length); + +GSettings * g_settings_get_child (GSettings *settings, + const gchar *name); + +gboolean g_settings_is_writable (GSettings *settings, + const gchar *name); + +void g_settings_delay (GSettings *settings); +void g_settings_apply (GSettings *settings); +void g_settings_revert (GSettings *settings); +gboolean g_settings_get_has_unapplied (GSettings *settings); + +/** + * GSettingsBindSetMapping: + * @value: a #GValue containing the property value to map + * @expected_type: the #GVariantType to create + * @user_data: user data that was specified when the binding was created + * @returns: a new #GVariant holding the data from @value, + * or %NULL in case of an error + * + * The type for the function that is used to convert an object property + * value to a #GVariant for storing it in #GSettings. + */ +typedef GVariant * (*GSettingsBindSetMapping) (const GValue *value, + const GVariantType *expected_type, + gpointer user_data); + +/** + * GSettingsBindGetMapping: + * @value: return location for the property value + * @variant: the #GVariant + * @user_data: user data that was specified when the binding was created + * @returns: %TRUE if the conversion succeeded, %FALSE in case of an error + * + * The type for the function that is used to convert from #GSettings to + * an object property. The @value is already initialized to hold values + * of the appropriate type. + */ +typedef gboolean (*GSettingsBindGetMapping) (GValue *value, + GVariant *variant, + gpointer user_data); + +/** + * GSettingsBindFlags: + * @G_SETTINGS_BIND_DEFAULT: Equivalent to G_SETTINGS_BIND_GET|G_SETTINGS_BIND_SET + * @G_SETTINGS_BIND_GET: Update the #GObject property when the #GSettings key changes + * @G_SETTINGS_BIND_SET: Update the #GSettings key when the #GObject property changes + * @G_SETTINGS_BIND_NO_SENSITIVITY: Do not try to bind a "sensitivity" property to #GSettings writability + * + * Flags used when creating a binding. These flags determine in which + * direction the binding works. The default is to synchronize in both + * directions. + */ +typedef enum +{ + G_SETTINGS_BIND_DEFAULT, + G_SETTINGS_BIND_GET = (1<<0), + G_SETTINGS_BIND_SET = (1<<1), + G_SETTINGS_BIND_NO_SENSITIVITY = (1<<2), + G_SETTINGS_BIND_GET_NO_CHANGES = (1<<3) +} GSettingsBindFlags; + +void g_settings_bind (GSettings *settings, + const gchar *key, + gpointer object, + const gchar *property, + GSettingsBindFlags flags); +void g_settings_bind_with_mapping (GSettings *settings, + const gchar *key, + gpointer object, + const gchar *property, + GSettingsBindFlags flags, + GSettingsBindGetMapping get_mapping, + GSettingsBindSetMapping set_mapping, + gpointer user_data, + GDestroyNotify destroy); +void g_settings_unbind (gpointer object, + const gchar *property); + +G_END_DECLS + +#endif /* __G_SETTINGS_H__ */ diff --git a/gio/pltcheck.sh b/gio/pltcheck.sh index bfd73fa6b..08f6e47c3 100755 --- a/gio/pltcheck.sh +++ b/gio/pltcheck.sh @@ -9,7 +9,7 @@ if ! which readelf 2>/dev/null >/dev/null; then exit 0 fi -SKIP='\