diff --git a/gio/gsettings.c b/gio/gsettings.c index 8636d66df..4433971c0 100644 --- a/gio/gsettings.c +++ b/gio/gsettings.c @@ -2285,6 +2285,23 @@ g_settings_binding_property_changed (GObject *object, binding->running = FALSE; } +static gboolean +g_settings_bind_invert_boolean_get_mapping (GValue *value, + GVariant *variant, + gpointer user_data) +{ + g_value_set_boolean (value, !g_variant_get_boolean (variant)); + return TRUE; +} + +static GVariant * +g_settings_bind_invert_boolean_set_mapping (const GValue *value, + const GVariantType *expected_type, + gpointer user_data) +{ + return g_variant_new_boolean (!g_value_get_boolean (value)); +} + /** * g_settings_bind: * @settings: a #GSettings object @@ -2323,8 +2340,20 @@ g_settings_bind (GSettings *settings, const gchar *property, GSettingsBindFlags flags) { - g_settings_bind_with_mapping (settings, key, object, property, - flags, NULL, NULL, NULL, NULL); + GSettingsBindGetMapping get_mapping = NULL; + GSettingsBindSetMapping set_mapping = NULL; + + if (flags & G_SETTINGS_BIND_INVERT_BOOLEAN) + { + get_mapping = g_settings_bind_invert_boolean_get_mapping; + set_mapping = g_settings_bind_invert_boolean_set_mapping; + + /* can't pass this flag to g_settings_bind_with_mapping() */ + flags &= ~G_SETTINGS_BIND_INVERT_BOOLEAN; + } + + g_settings_bind_with_mapping (settings, key, object, property, flags, + get_mapping, set_mapping, NULL, NULL); } /** @@ -2371,6 +2400,7 @@ g_settings_bind_with_mapping (GSettings *settings, GQuark binding_quark; g_return_if_fail (G_IS_SETTINGS (settings)); + g_return_if_fail (~flags & G_SETTINGS_BIND_INVERT_BOOLEAN); objectclass = G_OBJECT_GET_CLASS (object); @@ -2408,10 +2438,39 @@ g_settings_bind_with_mapping (GSettings *settings, 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->info.type)) + if (get_mapping == g_settings_bind_invert_boolean_get_mapping) + { + /* g_settings_bind_invert_boolean_get_mapping() is a private + * function, so if we are here it means that g_settings_bind() was + * called with G_SETTINGS_BIND_INVERT_BOOLEAN. + * + * Ensure that both sides are boolean. + */ + + if (binding->property->value_type != G_TYPE_BOOLEAN) + { + g_critical ("g_settings_bind: G_SETTINGS_BIND_INVERT_BOOLEAN " + "was specified, but property `%s' on type `%s' has " + "type `%s'", property, G_OBJECT_TYPE_NAME (object), + g_type_name ((binding->property->value_type))); + return; + } + + if (!g_variant_type_equal (binding->info.type, G_VARIANT_TYPE_BOOLEAN)) + { + g_critical ("g_settings_bind: G_SETTINGS_BIND_INVERT_BOOLEAN " + "was specified, but key `%s' on schema `%s' has " + "type `%s'", key, settings->priv->schema_name, + g_variant_type_dup_string (binding->info.type)); + return; + } + + } + + else 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->info.type)) { g_critical ("g_settings_bind: property '%s' on class '%s' has type " "'%s' which is not compatible with type '%s' of key '%s' " diff --git a/gio/gsettings.h b/gio/gsettings.h index 799370eed..938ec1ead 100644 --- a/gio/gsettings.h +++ b/gio/gsettings.h @@ -207,6 +207,9 @@ typedef gboolean (*GSettingsGetMapping) (GVarian * @G_SETTINGS_BIND_NO_SENSITIVITY: Do not try to bind a "sensitivity" property to the writability of the setting * @G_SETTINGS_BIND_GET_NO_CHANGES: When set in addition to #G_SETTINGS_BIND_GET, set the #GObject property * value initially from the setting, but do not listen for changes of the setting + * @G_SETTINGS_BIND_INVERT_BOOLEAN: When passed to g_settings_bind(), uses a pair of mapping functions that invert + * the boolean value when mapping between the setting and the property. The setting and property must both + * be booleans. You can not pass this flag to g_settings_bind_with_mapping(). * * Flags used when creating a binding. These flags determine in which * direction the binding works. The default is to synchronize in both @@ -218,7 +221,8 @@ typedef enum 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) + G_SETTINGS_BIND_GET_NO_CHANGES = (1<<3), + G_SETTINGS_BIND_INVERT_BOOLEAN = (1<<4) } GSettingsBindFlags; void g_settings_bind (GSettings *settings, diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c index 864871c58..1f6942f1e 100644 --- a/gio/tests/gsettings.c +++ b/gio/tests/gsettings.c @@ -681,6 +681,7 @@ enum { PROP_0, PROP_BOOL, + PROP_ANTI_BOOL, PROP_BYTE, PROP_INT16, PROP_UINT16, @@ -701,6 +702,7 @@ typedef struct GObject parent_instance; gboolean bool_prop; + gboolean anti_bool_prop; gchar byte_prop; gint int16_prop; guint16 uint16_prop; @@ -750,6 +752,9 @@ test_object_get_property (GObject *object, case PROP_BOOL: g_value_set_boolean (value, test_object->bool_prop); break; + case PROP_ANTI_BOOL: + g_value_set_boolean (value, test_object->anti_bool_prop); + break; case PROP_BYTE: g_value_set_char (value, test_object->byte_prop); break; @@ -805,6 +810,9 @@ test_object_set_property (GObject *object, case PROP_BOOL: test_object->bool_prop = g_value_get_boolean (value); break; + case PROP_ANTI_BOOL: + test_object->anti_bool_prop = g_value_get_boolean (value); + break; case PROP_BYTE: test_object->byte_prop = g_value_get_char (value); break; @@ -883,6 +891,8 @@ test_object_class_init (TestObjectClass *class) g_object_class_install_property (gobject_class, PROP_BOOL, g_param_spec_boolean ("bool", "", "", FALSE, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_ANTI_BOOL, + g_param_spec_boolean ("anti-bool", "", "", FALSE, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_BYTE, g_param_spec_char ("byte", "", "", G_MININT8, G_MAXINT8, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_INT16, @@ -942,7 +952,6 @@ test_simple_binding (void) obj = test_object_new (); g_settings_bind (settings, "bool", obj, "bool", G_SETTINGS_BIND_DEFAULT); - g_object_set (obj, "bool", TRUE, NULL); g_assert_cmpint (g_settings_get_boolean (settings, "bool"), ==, TRUE); @@ -951,6 +960,16 @@ test_simple_binding (void) g_object_get (obj, "bool", &b, NULL); g_assert_cmpint (b, ==, FALSE); + g_settings_bind (settings, "anti-bool", obj, "anti-bool", + G_SETTINGS_BIND_INVERT_BOOLEAN); + g_object_set (obj, "anti-bool", FALSE, NULL); + g_assert_cmpint (g_settings_get_boolean (settings, "anti-bool"), ==, TRUE); + + g_settings_set_boolean (settings, "anti-bool", FALSE); + b = FALSE; + g_object_get (obj, "anti-bool", &b, NULL); + g_assert_cmpint (b, ==, TRUE); + g_settings_bind (settings, "byte", obj, "byte", G_SETTINGS_BIND_DEFAULT); g_object_set (obj, "byte", 123, NULL); diff --git a/gio/tests/org.gtk.test.gschema.xml b/gio/tests/org.gtk.test.gschema.xml index 4bc5bcd04..6d166d839 100644 --- a/gio/tests/org.gtk.test.gschema.xml +++ b/gio/tests/org.gtk.test.gschema.xml @@ -81,6 +81,9 @@ false + + false + 0