binding: Add G_BINDING_INVERT_BOOLEAN
authorEmmanuele Bassi <ebassi@linux.intel.com>
Tue, 3 Aug 2010 09:29:50 +0000 (10:29 +0100)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Tue, 3 Aug 2010 09:29:50 +0000 (10:29 +0100)
Since GSettings got the same functionality and flag in commit ca3b7b75b
GBinding should also have the ability to automatically invert a boolean
value without requiring a custom transformation function.

gobject/gbinding.c
gobject/gbinding.h
gobject/tests/binding.c

index b8ccdb9..0388d07 100644 (file)
@@ -124,6 +124,7 @@ g_binding_flags_get_type (void)
         { G_BINDING_DEFAULT, "G_BINDING_DEFAULT", "default" },
         { G_BINDING_BIDIRECTIONAL, "G_BINDING_BIDIRECTIONAL", "bidirectional" },
         { G_BINDING_SYNC_CREATE, "G_BINDING_SYNC_CREATE", "sync-create" },
+        { G_BINDING_INVERT_BOOLEAN, "G_BINDING_INVERT_BOOLEAN", "invert-boolean" },
         { 0, NULL, NULL }
       };
       GType g_define_type_id =
@@ -301,21 +302,44 @@ done:
   return TRUE;
 }
 
+static inline gboolean
+default_invert_boolean_transform (const GValue *value_a,
+                                  GValue       *value_b)
+{
+  gboolean value;
+
+  g_assert (G_VALUE_HOLDS_BOOLEAN (value_a));
+  g_assert (G_VALUE_HOLDS_BOOLEAN (value_b));
+
+  value = g_value_get_boolean (value_a);
+  value = !value;
+
+  g_value_set_boolean (value_b, value);
+
+  return TRUE;
+}
+
 static gboolean
-default_transform_to (GBinding     *binding G_GNUC_UNUSED,
+default_transform_to (GBinding     *binding,
                       const GValue *value_a,
                       GValue       *value_b,
                       gpointer      user_data G_GNUC_UNUSED)
 {
+  if (binding->flags & G_BINDING_INVERT_BOOLEAN)
+    return default_invert_boolean_transform (value_a, value_b);
+
   return default_transform (value_a, value_b);
 }
 
 static gboolean
-default_transform_from (GBinding     *binding G_GNUC_UNUSED,
+default_transform_from (GBinding     *binding,
                         const GValue *value_a,
                         GValue       *value_b,
                         gpointer      user_data G_GNUC_UNUSED)
 {
+  if (binding->flags & G_BINDING_INVERT_BOOLEAN)
+    return default_invert_boolean_transform (value_a, value_b);
+
   return default_transform (value_a, value_b);
 }
 
@@ -809,6 +833,15 @@ g_object_bind_property_full (gpointer               source,
       return NULL;
     }
 
+  /* remove the G_BINDING_INVERT_BOOLEAN flag in case we have
+   * custom transformation functions
+   */
+  if ((flags & G_BINDING_INVERT_BOOLEAN) &&
+      (transform_to != NULL || transform_from != NULL))
+    {
+      flags &= ~G_BINDING_INVERT_BOOLEAN;
+    }
+
   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source), source_property);
   if (pspec == NULL)
     {
@@ -838,6 +871,18 @@ g_object_bind_property_full (gpointer               source,
       return NULL;
     }
 
+  if ((flags & G_BINDING_INVERT_BOOLEAN) &&
+      !(G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN))
+    {
+      g_warning ("%s: The G_BINDING_INVERT_BOOLEAN flag can only be used "
+                 "when binding boolean properties; the source property '%s' "
+                 "is of type '%s'",
+                 G_STRLOC,
+                 source_property,
+                 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
+      return NULL;
+    }
+
   pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (target), target_property);
   if (pspec == NULL)
     {
@@ -867,6 +912,18 @@ g_object_bind_property_full (gpointer               source,
       return NULL;
     }
 
+  if ((flags & G_BINDING_INVERT_BOOLEAN) &&
+      !(G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN))
+    {
+      g_warning ("%s: The G_BINDING_INVERT_BOOLEAN flag can only be used "
+                 "when binding boolean properties; the target property '%s' "
+                 "is of type '%s'",
+                 G_STRLOC,
+                 target_property,
+                 g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
+      return NULL;
+    }
+
   binding = g_object_new (G_TYPE_BINDING,
                           "source", source,
                           "source-property", source_property,
index 361eef9..fe6799d 100644 (file)
@@ -72,13 +72,18 @@ typedef gboolean (* GBindingTransformFunc) (GBinding     *binding,
 /**
  * GBindingFlags:
  * @G_BINDING_DEFAULT: The default binding; if the source property
- *   changes, the target property is updated with its value
+ *   changes, the target property is updated with its value.
  * @G_BINDING_BIDIRECTIONAL: Bidirectional binding; if either the
  *   property of the source or the property of the target changes,
- *   the other is updated
+ *   the other is updated.
  * @G_BINDING_SYNC_CREATE: Synchronize the values of the source and
  *   target properties when creating the binding; the direction of
- *   the synchronization is always from the source to the target
+ *   the synchronization is always from the source to the target.
+ * @G_BINDING_INVERT_BOOLEAN: If the two properties being bound are
+ *   booleans, setting one to %TRUE will result in the other being
+ *   set to %FALSE and vice versa. This flag will only work for
+ *   boolean properties, and cannot be used when passing custom
+ *   transformation functions to g_object_bind_property_full().
  *
  * Flags to be passed to g_object_bind_property() or
  * g_object_bind_property_full().
@@ -88,10 +93,11 @@ typedef gboolean (* GBindingTransformFunc) (GBinding     *binding,
  * Since: 2.26
  */
 typedef enum { /*< prefix=G_BINDING >*/
-  G_BINDING_DEFAULT       = 0,
+  G_BINDING_DEFAULT        = 0,
 
-  G_BINDING_BIDIRECTIONAL = 1 << 0,
-  G_BINDING_SYNC_CREATE   = 1 << 1
+  G_BINDING_BIDIRECTIONAL  = 1 << 0,
+  G_BINDING_SYNC_CREATE    = 1 << 1,
+  G_BINDING_INVERT_BOOLEAN = 1 << 2
 } GBindingFlags;
 
 GType                 g_binding_flags_get_type      (void) G_GNUC_CONST;
index 710776b..979a681 100644 (file)
@@ -8,6 +8,7 @@ typedef struct _BindingSource
 
   gint foo;
   gdouble value;
+  gboolean toggle;
 } BindingSource;
 
 typedef struct _BindingSourceClass
@@ -20,8 +21,8 @@ enum
   PROP_SOURCE_0,
 
   PROP_SOURCE_FOO,
-
-  PROP_SOURCE_VALUE
+  PROP_SOURCE_VALUE,
+  PROP_SOURCE_TOGGLE
 };
 
 G_DEFINE_TYPE (BindingSource, binding_source, G_TYPE_OBJECT);
@@ -44,6 +45,10 @@ binding_source_set_property (GObject      *gobject,
       source->value = g_value_get_double (value);
       break;
 
+    case PROP_SOURCE_TOGGLE:
+      source->toggle = g_value_get_boolean (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     }
@@ -67,6 +72,10 @@ binding_source_get_property (GObject    *gobject,
       g_value_set_double (value, source->value);
       break;
 
+    case PROP_SOURCE_TOGGLE:
+      g_value_set_boolean (value, source->toggle);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     }
@@ -90,6 +99,10 @@ binding_source_class_init (BindingSourceClass *klass)
                                                         -100.0, 200.0,
                                                         0.0,
                                                         G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, PROP_SOURCE_TOGGLE,
+                                   g_param_spec_boolean ("toggle", "Toggle", "Toggle",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
 }
 
 static void
@@ -103,6 +116,7 @@ typedef struct _BindingTarget
 
   gint bar;
   gdouble value;
+  gboolean toggle;
 } BindingTarget;
 
 typedef struct _BindingTargetClass
@@ -115,8 +129,8 @@ enum
   PROP_TARGET_0,
 
   PROP_TARGET_BAR,
-
-  PROP_TARGET_VALUE
+  PROP_TARGET_VALUE,
+  PROP_TARGET_TOGGLE
 };
 
 G_DEFINE_TYPE (BindingTarget, binding_target, G_TYPE_OBJECT);
@@ -139,6 +153,10 @@ binding_target_set_property (GObject      *gobject,
       target->value = g_value_get_double (value);
       break;
 
+    case PROP_TARGET_TOGGLE:
+      target->toggle = g_value_get_boolean (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     }
@@ -162,6 +180,10 @@ binding_target_get_property (GObject    *gobject,
       g_value_set_double (value, target->value);
       break;
 
+    case PROP_TARGET_TOGGLE:
+      g_value_set_boolean (value, target->toggle);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
     }
@@ -180,11 +202,15 @@ binding_target_class_init (BindingTargetClass *klass)
                                                      -1, 100,
                                                      0,
                                                      G_PARAM_READWRITE));
-  g_object_class_install_property (gobject_class, PROP_SOURCE_VALUE,
+  g_object_class_install_property (gobject_class, PROP_TARGET_VALUE,
                                    g_param_spec_double ("value", "Value", "Value",
                                                         -100.0, 200.0,
                                                         0.0,
                                                         G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, PROP_TARGET_TOGGLE,
+                                   g_param_spec_boolean ("toggle", "Toggle", "Toggle",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
 }
 
 static void
@@ -469,6 +495,33 @@ binding_sync_create (void)
   g_object_unref (target);
 }
 
+static void
+binding_invert_boolean (void)
+{
+  BindingSource *source = g_object_new (binding_source_get_type (),
+                                        "toggle", TRUE,
+                                        NULL);
+  BindingTarget *target = g_object_new (binding_target_get_type (),
+                                        "toggle", FALSE,
+                                        NULL);
+  GBinding *binding;
+
+  binding = g_object_bind_property (source, "toggle",
+                                    target, "toggle",
+                                    G_BINDING_DEFAULT | G_BINDING_INVERT_BOOLEAN);
+
+  g_assert (source->toggle);
+  g_assert (!target->toggle);
+
+  g_object_set (source, "toggle", FALSE, NULL);
+  g_assert (!source->toggle);
+  g_assert (target->toggle);
+
+  g_object_unref (binding);
+  g_object_unref (source);
+  g_object_unref (target);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -481,6 +534,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/binding/transform-closure", binding_transform_closure);
   g_test_add_func ("/binding/chain", binding_chain);
   g_test_add_func ("/binding/sync-create", binding_sync_create);
+  g_test_add_func ("/binding/invert-boolean", binding_invert_boolean);
 
   return g_test_run ();
 }