diff --git a/gio/Makefile.am b/gio/Makefile.am index f736d8792..9a6b12805 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -87,6 +87,7 @@ settings_sources = \ gdelayedsettingsbackend.c \ gmemorysettingsbackend.h \ gmemorysettingsbackend.c \ + gsettingsbackendinternal.h \ gsettingsbackend.h \ gsettingsbackend.c \ gsettingsschema.h \ diff --git a/gio/gdelayedsettingsbackend.c b/gio/gdelayedsettingsbackend.c index 0b4d7b564..61371c43a 100644 --- a/gio/gdelayedsettingsbackend.c +++ b/gio/gdelayedsettingsbackend.c @@ -9,6 +9,7 @@ */ #include "gdelayedsettingsbackend.h" +#include "gsettingsbackendinternal.h" #include diff --git a/gio/gsettings.c b/gio/gsettings.c index cde803194..9d7e3225a 100644 --- a/gio/gsettings.c +++ b/gio/gsettings.c @@ -15,6 +15,7 @@ #include "gsettings.h" #include "gdelayedsettingsbackend.h" +#include "gsettingsbackendinternal.h" #include "gio-marshal.h" #include "gsettingsschema.h" @@ -32,12 +33,11 @@ struct _GSettingsPrivate { GSettingsBackend *backend; - gchar *base_path; - GSettingsSchema *schema; gchar *schema_name; + gchar *context; + gchar *path; - guint handler_id; guint unapplied_handler; gboolean delayed; }; @@ -45,17 +45,20 @@ struct _GSettingsPrivate { enum { PROP_0, - PROP_STORAGE, - PROP_SCHEMA_NAME, + PROP_BACKEND, PROP_SCHEMA, - PROP_BASE_PATH, + PROP_CONTEXT, + PROP_PATH, PROP_DELAY_APPLY, PROP_HAS_UNAPPLIED, }; enum { - SIGNAL_CHANGES, + SIGNAL_ALL_WRITABLE_CHANGED, + SIGNAL_ALL_CHANGED, + SIGNAL_KEYS_CHANGED, + SIGNAL_WRITABLE_CHANGED, SIGNAL_CHANGED, SIGNAL_DESTROYED, N_SIGNALS @@ -66,88 +69,156 @@ static guint g_settings_signals[N_SIGNALS]; G_DEFINE_TYPE (GSettings, g_settings, G_TYPE_OBJECT) static void -g_settings_storage_changed (GSettingsBackend *backend, - const gchar *prefix, - gchar const * const *names, - gint n_names, - gpointer origin_tag, - gpointer user_data) +settings_backend_changed (GSettingsBackend *backend, + const gchar *key, + gpointer origin_tag, + gpointer user_data) { GSettings *settings = G_SETTINGS (user_data); - gchar **changes; - GQuark *quarks; - gint i, c; + gint i; g_assert (settings->priv->backend == backend); - /* slow and stupid. */ - changes = g_new (gchar *, n_names); - quarks = g_new (GQuark, n_names); - for (i = 0; i < n_names; i++) - changes[i] = g_strconcat (prefix, names[i], NULL); + for (i = 0; key[i] == settings->priv->path[i]; i++); - /* check which are for us */ - c = 0; - for (i = 0; i < n_names; i++) - if (g_str_has_prefix (changes[i], settings->priv->base_path)) - { - const gchar *rel = changes[i] + strlen (settings->priv->base_path); + if (settings->priv->path[i] == '\0' && + g_settings_schema_has_key (settings->priv->schema, key + i)) + { + GQuark quark; - if (strchr (rel, '/') == NULL) - /* XXX also check schema to ensure it's really a key */ - quarks[c++] = g_quark_from_string (rel); - } - - g_settings_changes (settings, quarks, c); - - for (i = 0; i < n_names; i++) - g_free (changes[i]); - g_free (changes); - g_free (quarks); -} - -/** - * g_settings_changes: - * @settings: a #GSettings object - * @keys: an array of #GQuark key names - * @n_keys: the length of @keys - * - * Emits the #GSettings::changes signal on a #GSettings object. - * - * It is an error to call this function with a quark in @keys that is - * not a valid key for @settings (according to its schema). - * - * Since: 2.26 - */ -void -g_settings_changes (GSettings *settings, - const GQuark *keys, - gint n_keys) -{ - g_return_if_fail (settings != NULL); - - if (n_keys == 0) - return; - - g_return_if_fail (keys != NULL); - g_return_if_fail (n_keys > 0); - - g_signal_emit (settings, - g_settings_signals[SIGNAL_CHANGES], 0, - keys, n_keys); + quark = g_quark_from_string (key + i); + g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGED], + quark, g_quark_to_string (quark)); + } } static void -g_settings_real_changes (GSettings *settings, - const GQuark *keys, - gint n_keys) +settings_backend_path_changed (GSettingsBackend *backend, + const gchar *path, + gpointer origin_tag, + gpointer user_data) { + GSettings *settings = G_SETTINGS (user_data); + + g_assert (settings->priv->backend == backend); + + if (g_str_has_prefix (settings->priv->path, path)) + g_signal_emit (settings, g_settings_signals[SIGNAL_ALL_CHANGED], 0); +} + +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); gint i; - for (i = 0; i < n_keys; 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_KEYS_CHANGED], + 0, quarks, l); + } +} + +static void +settings_backend_path_writable_changed (GSettingsBackend *backend, + const gchar *path, + gpointer user_data) +{ + GSettings *settings = G_SETTINGS (user_data); + + g_assert (settings->priv->backend == backend); + + if (g_str_has_prefix (settings->priv->path, path)) g_signal_emit (settings, - g_settings_signals[SIGNAL_CHANGED], - keys[i], g_quark_to_string (keys[i])); + g_settings_signals[SIGNAL_ALL_WRITABLE_CHANGED], 0); +} + +static void +settings_backend_writable_changed (GSettingsBackend *backend, + const gchar *key, + gpointer user_data) +{ + GSettings *settings = G_SETTINGS (user_data); + 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_CHANGED], + quark, g_quark_to_string (quark)); + } +} + +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\n", + 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\n", + 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 @@ -188,8 +259,14 @@ g_settings_set_delay_apply (GSettings *settings, g_assert (delayed); backend = g_delayed_settings_backend_new (settings->priv->backend); - g_signal_handler_disconnect (settings->priv->backend, - settings->priv->handler_id); + g_settings_backend_unwatch (settings->priv->backend, settings); + g_settings_backend_watch (backend, + settings_backend_changed, + settings_backend_path_changed, + settings_backend_keys_changed, + settings_backend_writable_changed, + settings_backend_path_writable_changed, + settings); g_object_unref (settings->priv->backend); settings->priv->backend = backend; @@ -197,13 +274,6 @@ g_settings_set_delay_apply (GSettings *settings, g_signal_connect_swapped (backend, "notify::has-unapplied", G_CALLBACK (g_settings_notify_unapplied), settings); - settings->priv->handler_id = - g_signal_connect (settings->priv->backend, "changed", - G_CALLBACK (g_settings_storage_changed), - settings); - - g_free (settings->priv->base_path); - settings->priv->base_path = g_strdup (""); settings->priv->delayed = TRUE; } @@ -278,26 +348,13 @@ g_settings_set_property (GObject *object, switch (prop_id) { - case PROP_SCHEMA_NAME: + case PROP_SCHEMA: g_assert (settings->priv->schema_name == NULL); settings->priv->schema_name = g_value_dup_string (value); break; - case PROP_SCHEMA: - g_assert (settings->priv->schema == NULL); - settings->priv->schema = g_value_dup_object (value); - break; - - case PROP_DELAY_APPLY: - g_settings_set_delay_apply (settings, g_value_get_boolean (value)); - break; - - case PROP_BASE_PATH: - settings->priv->base_path = g_value_dup_string (value); - break; - - case PROP_STORAGE: - settings->priv->backend = g_value_dup_object (value); + case PROP_PATH: + settings->priv->path = g_value_dup_string (value); break; default: @@ -351,158 +408,27 @@ g_settings_finalize (GObject *object) { GSettings *settings = G_SETTINGS (object); - g_signal_handler_disconnect (settings->priv->backend, settings->priv->handler_id); + 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->base_path); + g_free (settings->priv->path); } -static void -g_settings_constructed (GObject *object) -{ - GSettings *settings = G_SETTINGS (object); - - if (settings->priv->backend == NULL) - settings->priv->backend = g_settings_backend_get_with_context (NULL); - - settings->priv->handler_id = - g_signal_connect (settings->priv->backend, "changed", - G_CALLBACK (g_settings_storage_changed), - settings); - - if (settings->priv->schema != NULL && settings->priv->schema_name != NULL) - g_error ("Schema and schema name specified"); - - if (settings->priv->schema == NULL && settings->priv->schema_name != NULL) - settings->priv->schema = g_settings_schema_new (settings->priv->schema_name); - - if (settings->priv->schema == NULL) - settings->priv->schema = g_settings_schema_new ("empty"); - - if (settings->priv->base_path == NULL && settings->priv->schema == NULL) - g_error ("Attempting to make settings with no schema or path"); - - if (settings->priv->base_path != NULL && settings->priv->schema != NULL) - { - const gchar *schema_path, *given_path; - - schema_path = g_settings_schema_get_path (settings->priv->schema); - given_path = settings->priv->base_path; - - if (schema_path && strcmp (schema_path, given_path) != 0) - g_error ("Specified path of '%s' but schema says '%s'", - given_path, schema_path); - } - - if (settings->priv->base_path == NULL) - settings->priv->base_path = g_strdup ( - g_settings_schema_get_path (settings->priv->schema)); - - if (settings->priv->base_path == NULL) - g_error ("No base path given and none from schema"); - - g_settings_backend_subscribe (settings->priv->backend, - settings->priv->base_path); -} - -/** - * 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-name", 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. - * - * 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-name", schema, "base-path", path, NULL); -} - -#if 0 -static GType -g_settings_get_gtype_for_schema (GSettingsSchema *schema) -{ - GVariantIter iter; - const gchar *item; - GVariant *list; - - list = g_settings_schema_get_inheritance (schema); - g_variant_iter_init (&iter, list); - g_variant_unref (list); - - while (g_variant_iter_next (&iter, "s", &item)) - if (strcmp (item, "list") == 0) - return G_TYPE_SETTINGS_LIST; - - return G_TYPE_SETTINGS; -} - -GSettings * -g_settings_get_settings (GSettings *settings, - const gchar *name) -{ - const gchar *schema_name; - GSettingsSchema *schema; - GSettings *child; - gchar *path; - - schema_name = g_settings_schema_get_schema (settings->priv->schema, name); - - if (schema_name == NULL) - { - schema_name = g_settings_schema_get_schema (settings->priv->schema, - "/default"); - - if G_UNLIKELY (schema_name == NULL) - g_error ("Schema has no child schema named '%s' and no " - "default child schema", name); - } - - schema = g_settings_schema_new (schema_name); - path = g_strdup_printf ("%s%s/", settings->priv->base_path, name); - - child = g_object_new (g_settings_get_gtype_for_schema (schema), - "schema", schema, - "backend", settings->priv->backend, - "base-path", path, - NULL); - - g_free (path); - - return child; -} -#endif - static void g_settings_class_init (GSettingsClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); - - class->changes = g_settings_real_changes; - +/* + class->all_writable_changed = g_settings_real_all_writable_changed; + class->all_changed = g_settings_real_all_changed; + class->keys_changed = g_settings_real_keys_changed; + class->writable_changed = g_settings_real_writable_changed; + class->changed = g_settings_real_changed; + class->destroyed = g_settings_real_destroyed; +*/ object_class->set_property = g_settings_set_property; object_class->get_property = g_settings_get_property; object_class->constructed = g_settings_constructed; @@ -511,27 +437,51 @@ g_settings_class_init (GSettingsClass *class) g_type_class_add_private (object_class, sizeof (GSettingsPrivate)); /** - * GSettings::changes: + * GSettings::all-changed: + * @settings: the object on which the signal was emitted + * + * The "all-changed" signal is emitted when potentially every key in + * the settings object has changed. This occurs, for example, if + * g_settings_reset() is called. + * + * The default implementation for this signal is to emit the + * GSettings::changed signal for each key in the schema. + */ + g_settings_signals[SIGNAL_ALL_CHANGED] = + g_signal_new ("all-changed", G_TYPE_SETTINGS, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GSettingsClass, all_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + /** + * GSettings::keys-changed: * @settings: the object on which the signal was emitted * @keys: an array of #GQuarks for the changed keys * @n_keys: the length of the @keys array * - * The "changes" signal is emitted when a set of keys changes. + * The "changes" signal is emitting when a number of keys have + * potentially been changed. This occurs, for example, if + * g_settings_apply() is called on a delayed-apply #GSettings. + * + * The default implemenetation for this signal is to emit the + * GSettings::changed signal for each item in the list of keys. */ - g_settings_signals[SIGNAL_CHANGES] = - g_signal_new ("changes", G_TYPE_SETTINGS, + g_settings_signals[SIGNAL_KEYS_CHANGED] = + g_signal_new ("keys-changed", G_TYPE_SETTINGS, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSettingsClass, changes), + G_STRUCT_OFFSET (GSettingsClass, keys_changed), NULL, NULL, _gio_marshal_VOID__POINTER_INT, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_INT); /** - * GSettings::chnaged: + * GSettings::changed: * @settings: the object on which the signal was emitted - * @key: the changed key + * @key: the changed key * - * The "changed" signal is emitted for each changed key. + * The "changed" signal is emitted when a key has potentially been + * changed. */ g_settings_signals[SIGNAL_CHANGED] = g_signal_new ("changed", G_TYPE_SETTINGS, @@ -542,31 +492,37 @@ g_settings_class_init (GSettingsClass *class) G_TYPE_NONE, 1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE); /** - * GSettings::destroyed: - * @settings: the object on which this signal was emitted + * GSettings::all-writable-changed: * - * The "destroyed" signal is emitted when the root of the settings - * is removed from the backend storage. + * The "all-writable-changes" signal is emitted when the writability + * of potentially every key has changed. This occurs, for example, if + * an entire subpath is locked down in the settings backend. + * + * The default implementation for this signal is to emit the + * GSettings:writable-changed signal for each key in the schema. */ - g_settings_signals[SIGNAL_DESTROYED] = - g_signal_new ("destroyed", G_TYPE_SETTINGS, + g_settings_signals[SIGNAL_ALL_WRITABLE_CHANGED] = + g_signal_new ("all-writable-changed", G_TYPE_SETTINGS, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSettingsClass, destroyed), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - + G_STRUCT_OFFSET (GSettingsClass, all_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** - * GSettings:backend: + * GSettings::writable-changed: + * @settings: the object on which the signal was emitted + * @key: the key * - * The #GSettingsBackend object that provides the backing storage - * for this #GSettings object. + * 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. */ - g_object_class_install_property (object_class, PROP_STORAGE, - g_param_spec_object ("backend", - P_("Backend storage"), - P_("The GSettingsBackend object for this settings object"), - G_TYPE_SETTINGS_BACKEND, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + 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:schema-name: @@ -574,35 +530,21 @@ g_settings_class_init (GSettingsClass *class) * The name of the schema that describes the types of keys * for this #GSettings object. */ - g_object_class_install_property (object_class, PROP_SCHEMA_NAME, - g_param_spec_string ("schema-name", + 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:schema: - * - * The #GSettingsSchema object that describes the types of - * keys for this #GSettings object. - */ - g_object_class_install_property (object_class, PROP_SCHEMA, - g_param_spec_object ("schema", - P_("Schema"), - P_("The GSettingsSchema object for this settings object"), - G_TYPE_OBJECT, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** * GSettings:base-path: * * The path within the backend where the settings are stored. */ - g_object_class_install_property (object_class, PROP_BASE_PATH, - g_param_spec_string ("base-path", + 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, @@ -660,7 +602,7 @@ g_settings_get_value (GSettings *settings, GVariant *sval; gchar *path; - path = g_strconcat (settings->priv->base_path, key, NULL); + path = g_strconcat (settings->priv->path, key, NULL); sval = g_settings_schema_get_value (settings->priv->schema, key, NULL); type = g_variant_get_type (sval); value = g_settings_backend_read (settings->priv->backend, path, type); @@ -800,236 +742,109 @@ g_settings_is_writable (GSettings *settings, gboolean writable; gchar *path; - path = g_strconcat (settings->priv->base_path, name, NULL); + path = g_strconcat (settings->priv->path, name, NULL); writable = g_settings_backend_get_writable (settings->priv->backend, path); g_free (path); return writable; } -void -g_settings_destroy (GSettings *settings) +/** + * 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) { - g_signal_emit (settings, g_settings_signals[SIGNAL_DESTROYED], 0); + return g_object_new (G_TYPE_SETTINGS, + "schema", schema, + NULL); } -#if 0 -typedef struct +/** + * 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) { - GSettings *settings; - GObject *object; - - guint property_handler_id; - const GParamSpec *property; - guint key_handler_id; - const 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->key_handler_id) - g_signal_handler_disconnect (binding->settings, - binding->key_handler_id); - - if (binding->property_handler_id) - g_signal_handler_disconnect (binding->object, - binding->property_handler_id); - - g_object_unref (binding->settings); - - g_slice_free (GSettingsBinding, binding); + return g_object_new (G_TYPE_SETTINGS, + "schema", schema, + "path", path, + NULL); } -static GQuark -g_settings_binding_quark (const char *property) +/** + * 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) { - GQuark quark; - gchar *tmp; - - tmp = g_strdup_printf ("gsettingsbinding-%s", property); - quark = g_quark_from_string (tmp); - g_free (tmp); - - return quark; + return g_object_new (G_TYPE_SETTINGS, + "schema", schema, + "context", context, + NULL); } -static void -g_settings_binding_key_changed (GSettings *settings, - const gchar *key, - gpointer user_data) +/** + * 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) { - 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 (g_value_deserialise (&value, variant)) - g_object_set_property (binding->object, - binding->property->name, - &value); - g_value_unset (&value); - - binding->running = FALSE; + return g_object_new (G_TYPE_SETTINGS, + "schema", schema, + "context", context, + "path", path, + 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 = g_value_serialise (&value, binding->type))) - { - g_settings_set_value (binding->settings, - binding->key, - variant); - g_variant_unref (variant); - } - g_value_unset (&value); - - binding->running = FALSE; -} - -void -g_settings_bind (GSettings *settings, - const gchar *key, - gpointer object, - const gchar *property, - GSettingsBindFlags flags) -{ - GSettingsBinding *binding; - GObjectClass *objectclass; - gchar *detailed_signal; - GQuark binding_quark; - gboolean insensitive; - - objectclass = G_OBJECT_GET_CLASS (object); - - binding = g_slice_new (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->running = FALSE; - - 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; - } - - binding->type = g_settings_schema_get_key_type (settings->priv->schema, - key); - - if (binding->type == NULL) - { - g_critical ("g_settings_bind: no key '%s' on schema '%s'", - key, settings->priv->schema_name); - return; - } - - if (!g_type_serialiser_check (G_PARAM_SPEC_VALUE_TYPE (binding->property), - 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_NO_SENSITIVITY) - { - GParamSpec *sensitive; - - sensitive = g_object_class_find_property (objectclass, "sensitive"); - if (sensitive && sensitive->value_type == G_TYPE_BOOLEAN) - { - insensitive = !g_settings_is_writable (settings, key); - g_object_set (object, "sensitive", !insensitive, NULL); - } - else - insensitive = FALSE; - } - else - insensitive = FALSE; - - if (!insensitive && (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) - { - 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); - } - - binding_quark = g_settings_binding_quark (property); - g_object_set_qdata_full (object, binding_quark, - binding, g_settings_binding_free); -} - -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); -} -#endif diff --git a/gio/gsettings.h b/gio/gsettings.h index 69ed86a71..4cac48225 100644 --- a/gio/gsettings.h +++ b/gio/gsettings.h @@ -1,17 +1,30 @@ /* - * Copyright © 2009 Codethink Limited + * Copyright © 2009, 2010 Codethink Limited * - * This program is free software: you can redistribute it and/or modify - * it under the terms of version 3 of the GNU General Public License as - * published by the Free Software Foundation. + * 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. * - * See the included COPYING file for more information. + * 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 "gsettingsbackend.h" +#include + +G_BEGIN_DECLS #define G_TYPE_SETTINGS (g_settings_get_type ()) #define G_SETTINGS(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ @@ -31,17 +44,16 @@ struct _GSettingsClass { GObjectClass parent_class; + void (*all_writable_changed) (GSettings *settings); + void (*all_changed) (GSettings *settings); + void (*keys_changed) (GSettings *settings, + const GQuark *keys, + gint n_keys); - GSettings * (*get_settings) (GSettings *settings, - const gchar *name); - - - void (*changes) (GSettings *settings, - const GQuark *keys, - gint n_keys); - void (*changed) (GSettings *settings, - const gchar *key); - void (*destroyed) (GSettings *settings); + void (*writable_changed) (GSettings *settings, + const gchar *key); + void (*changed) (GSettings *settings, + const gchar *key); }; struct _GSettings @@ -50,62 +62,46 @@ struct _GSettings GSettingsPrivate *priv; }; -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) -} GSettingsBindFlags; - -G_BEGIN_DECLS GType g_settings_get_type (void); -void g_settings_revert (GSettings *settings); -void g_settings_apply (GSettings *settings); -gboolean g_settings_get_delay_apply (GSettings *settings); -gboolean g_settings_get_has_unapplied (GSettings *settings); -void g_settings_set_delay_apply (GSettings *settings, - gboolean delay); 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); void g_settings_set_value (GSettings *settings, const gchar *key, GVariant *value); - GVariant * g_settings_get_value (GSettings *settings, const gchar *key); void g_settings_set (GSettings *settings, const gchar *key, - const gchar *format, + const gchar *format_string, ...); - void g_settings_get (GSettings *settings, const gchar *key, const gchar *format_string, ...); -GSettings * g_settings_get_settings (GSettings *settings, +GSettings * g_settings_get_child (GSettings *settings, const gchar *name); gboolean g_settings_is_writable (GSettings *settings, const gchar *name); -void g_settings_changes (GSettings *settings, - const GQuark *keys, - gint n_keys); -void g_settings_destroy (GSettings *settings); -void g_settings_bind (GSettings *settings, - const gchar *key, - gpointer object, - const gchar *property, - GSettingsBindFlags flags); -void g_settings_unbind (gpointer object, - const gchar *key); +void g_settings_apply (GSettings *settings); +void g_settings_revert (GSettings *settings); +gboolean g_settings_get_delay_apply (GSettings *settings); +gboolean g_settings_get_has_unapplied (GSettings *settings); +void g_settings_set_delay_apply (GSettings *settings, + gboolean delay); G_END_DECLS diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c index 882da834e..42a150c18 100644 --- a/gio/gsettingsbackend.c +++ b/gio/gsettingsbackend.c @@ -23,7 +23,7 @@ #include "config.h" -#include "gsettingsbackend.h" +#include "gsettingsbackendinternal.h" #include "gmemorysettingsbackend.h" #include "giomodule-priv.h" #include "gio-marshal.h" @@ -35,19 +35,18 @@ #include "gioalias.h" +G_DEFINE_ABSTRACT_TYPE (GSettingsBackend, g_settings_backend, G_TYPE_OBJECT) + +typedef struct _GSettingsBackendWatch GSettingsBackendWatch; + struct _GSettingsBackendPrivate { + GSettingsBackendWatch *watches; gchar *context; }; -G_DEFINE_ABSTRACT_TYPE (GSettingsBackend, g_settings_backend, G_TYPE_OBJECT) - -static guint writable_changed_signal; -static guint keys_changed_signal; -static guint changed_signal; - enum { - PROP_ZERO, + PROP_0, PROP_CONTEXT }; @@ -72,6 +71,61 @@ enum { * suitable trees. **/ +struct _GSettingsBackendWatch +{ + GSettingsBackendChangedFunc changed; + GSettingsBackendPathChangedFunc path_changed; + GSettingsBackendKeysChangedFunc keys_changed; + GSettingsBackendWritableChangedFunc writable_changed; + GSettingsBackendPathWritableChangedFunc path_writable_changed; + gpointer user_data; + + GSettingsBackendWatch *next; +}; + +void +g_settings_backend_watch (GSettingsBackend *backend, + GSettingsBackendChangedFunc changed, + GSettingsBackendPathChangedFunc path, + GSettingsBackendKeysChangedFunc keys, + GSettingsBackendWritableChangedFunc writable, + GSettingsBackendPathWritableChangedFunc path_writable, + gpointer user_data) +{ + GSettingsBackendWatch *watch; + + watch = g_slice_new (GSettingsBackendWatch); + watch->changed = changed; + watch->path_changed = path; + watch->keys_changed = keys; + watch->writable_changed = writable; + watch->path_writable_changed = path_writable; + watch->user_data = user_data; + + watch->next = backend->priv->watches; + backend->priv->watches = watch; +} + +void +g_settings_backend_unwatch (GSettingsBackend *backend, + gpointer user_data) +{ + GSettingsBackendWatch **ptr; + + for (ptr = &backend->priv->watches; *ptr; ptr = &(*ptr)->next) + if ((*ptr)->user_data == user_data) + { + GSettingsBackendWatch *tmp = *ptr; + + *ptr = tmp->next; + g_slice_free (GSettingsBackendWatch, tmp); + + return; + } + + g_assert_not_reached (); +} + /** * g_settings_backend_changed: * @backend: a #GSettingsBackend implementation @@ -108,10 +162,13 @@ g_settings_backend_changed (GSettingsBackend *backend, const gchar *name, gpointer origin_tag) { + GSettingsBackendWatch *watch; + g_return_if_fail (backend != NULL); g_return_if_fail (name != NULL); - - g_signal_emit (backend, changed_signal, 0, name, origin_tag); + + for (watch = backend->priv->watches; watch; watch = watch->next) + watch->changed (backend, name, origin_tag, watch->user_data); } /** @@ -154,18 +211,27 @@ g_settings_backend_keys_changed (GSettingsBackend *backend, gchar const * const *items, gpointer origin_tag) { + GSettingsBackendWatch *watch; + g_return_if_fail (backend != NULL); g_return_if_fail (prefix != NULL); g_return_if_fail (items != NULL); - g_signal_emit (backend, keys_changed_signal, 0, prefix, items, origin_tag); + for (watch = backend->priv->watches; watch; watch = watch->next) + watch->keys_changed (backend, prefix, items, origin_tag, watch->user_data); } void g_settings_backend_writable_changed (GSettingsBackend *backend, const gchar *name) { - g_signal_emit (backend, writable_changed_signal, 0, name); + GSettingsBackendWatch *watch; + + g_return_if_fail (backend != NULL); + g_return_if_fail (name != NULL); + + for (watch = backend->priv->watches; watch; watch = watch->next) + watch->writable_changed (backend, name, watch->user_data); } static gboolean @@ -194,6 +260,7 @@ g_settings_backend_changed_tree (GSettingsBackend *backend, GTree *tree, gpointer origin_tag) { + GSettingsBackendWatch *watch; gchar **list; list = g_new (gchar *, g_tree_nnodes (tree) + 1); @@ -206,8 +273,10 @@ g_settings_backend_changed_tree (GSettingsBackend *backend, g_assert (list + g_tree_nnodes (tree) == ptr); } - g_signal_emit (backend, changed_signal, 0, - "", list, g_tree_nnodes (tree), origin_tag); + for (watch = backend->priv->watches; watch; watch = watch->next) + watch->keys_changed (backend, "/", + (const gchar * const *) list, + origin_tag, watch->user_data); g_free (list); } @@ -431,52 +500,6 @@ g_settings_backend_class_init (GSettingsBackendClass *class) g_type_class_add_private (class, sizeof (GSettingsBackendPrivate)); - /** - * GSettingsBackend::changed: - * @backend: the object on which the signal was emitted - * @prefix: a common prefix of the changed keys - * @items: the list of changed keys - * @origin_tag: the origin tag - * - * The "changed" signal gets emitted when one or more keys change - * in the #GSettingsBackend store. The new values of all updated - * keys will be visible to any signal callbacks. - * - * The list of changed keys, relative to the root of the settings store, - * is @prefix prepended to each of the @items. It must either be the - * case that @prefix is equal to "" or ends in "/" or that @items - * contains exactly one item: "". @prefix need not be the largest - * possible prefix. - * - * Since: 2.26 - */ - changed_signal = - g_signal_new ("changed", G_TYPE_SETTINGS_BACKEND, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSettingsBackendClass, changed), - NULL, NULL, - _gio_marshal_VOID__STRING_POINTER, G_TYPE_NONE, - 2, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE | - G_TYPE_POINTER); - - keys_changed_signal = - g_signal_new ("keys-changed", G_TYPE_SETTINGS_BACKEND, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSettingsBackendClass, keys_changed), - NULL, NULL, - _gio_marshal_VOID__STRING_BOXED_POINTER, G_TYPE_NONE, - 3, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE, - G_TYPE_STRV | G_SIGNAL_TYPE_STATIC_SCOPE, G_TYPE_POINTER); - - writable_changed_signal = - g_signal_new ("writable-changed", G_TYPE_SETTINGS_BACKEND, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GSettingsBackendClass, writable_changed), - NULL, NULL, - _gio_marshal_VOID__STRING_POINTER, G_TYPE_NONE, - 2, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE | - G_TYPE_POINTER); - /** * GSettingsBackend:context: * diff --git a/gio/gsettingsbackend.h b/gio/gsettingsbackend.h index 375d5a06c..570909406 100644 --- a/gio/gsettingsbackend.h +++ b/gio/gsettingsbackend.h @@ -51,20 +51,12 @@ typedef struct _GSettingsBackendPrivate GSettingsBackendPriv typedef struct _GSettingsBackendClass GSettingsBackendClass; typedef struct _GSettingsBackend GSettingsBackend; + struct _GSettingsBackendClass { GObjectClass parent_class; - void (*changed) (GSettingsBackend *backend, - const gchar *name, - gpointer origin_tag); - void (*keys_changed) (GSettingsBackend *backend, - const gchar *prefix, - gchar const * const *names, - gpointer origin_tag); - void (*writable_changed) (GSettingsBackend *backend, - const gchar *name); - + gboolean (*supports_context) (const gchar *context); GVariant * (*read) (GSettingsBackend *backend, const gchar *key, @@ -82,8 +74,6 @@ struct _GSettingsBackendClass const gchar *name); void (*unsubscribe) (GSettingsBackend *backend, const gchar *name); - - gboolean (*supports_context) (const gchar *context); }; struct _GSettingsBackend @@ -95,28 +85,6 @@ struct _GSettingsBackend }; GType g_settings_backend_get_type (void); -gboolean g_settings_backend_supports_context (const gchar *context); -GSettingsBackend * g_settings_backend_get_with_context (const gchar *context); -GTree * g_settings_backend_create_tree (void); - -GVariant * g_settings_backend_read (GSettingsBackend *backend, - const gchar *key, - const GVariantType *expected_type); -void g_settings_backend_write (GSettingsBackend *backend, - const gchar *key, - GVariant *value, - gpointer origin_tag); -void g_settings_backend_write_keys (GSettingsBackend *backend, - GTree *tree, - gpointer origin_tag); - -gboolean g_settings_backend_get_writable (GSettingsBackend *backend, - const char *name); - -void g_settings_backend_unsubscribe (GSettingsBackend *backend, - const char *name); -void g_settings_backend_subscribe (GSettingsBackend *backend, - const char *name); void g_settings_backend_changed (GSettingsBackend *backend, const gchar *path, @@ -133,4 +101,4 @@ void g_settings_backend_changed_tree (GSettin G_END_DECLS -#endif /* __G_SETTINGS_BACKEND_H__ */ +#endif /* __G_SETTINGS_BACKEND_H__ */ diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c index 8b1476632..671696dbe 100644 --- a/gio/gsettingsschema.c +++ b/gio/gsettingsschema.c @@ -244,3 +244,11 @@ g_settings_schema_get_path (GSettingsSchema *schema) return result; } + +gboolean +g_settings_schema_has_key (GSettingsSchema *schema, + const gchar *key) +{ + /* XXX vuntz wants a commit now :) */ + return TRUE; +} diff --git a/gio/gsettingsschema.h b/gio/gsettingsschema.h index faf2211a5..8a99254ca 100644 --- a/gio/gsettingsschema.h +++ b/gio/gsettingsschema.h @@ -59,6 +59,8 @@ const gchar * g_settings_schema_get_path (GSettin GVariant * g_settings_schema_get_value (GSettingsSchema *schema, const gchar *key, GVariant **options); +gboolean g_settings_schema_has_key (GSettingsSchema *schema, + const gchar *key); G_END_DECLS