/* Copyright (C) 2014 The glibmm Development Team * * 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.1 of the License, 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, see . */ #include #include #include #include #include _DEFS(glibmm,glib) _PINCLUDE(glibmm/private/object_p.h) namespace Glib { /** Bind two object properties. * * %Glib::Binding is the representation of a binding between a property on a * Glib::ObjectBase instance (or source) and another property on another Glib::ObjectBase * instance (or target). Whenever the source property changes, the same * value is applied to the target property; for instance, the following binding: * * @code * Glib::Binding::bind_property(object1->property_a(), object2->property_b()); * @endcode * * will cause property_b() of @a object2 to be updated * every time the value of property_a() of @a object1 changes. * * It is possible to create a bidirectional binding between two properties * of two Glib::ObjectBase instances, so that if either property changes, the * other is updated as well, for instance: * * @code * Glib::Binding::bind_property(object1->property_a(), object2->property_b(), * Glib::Binding::Flags::BIDIRECTIONAL); * @endcode * * will keep the two properties in sync. * * It is also possible to set a custom transformation function (in both * directions, in case of a bidirectional binding) to apply a custom * transformation from the source value to the target value before * applying it; for instance, the following binding: * * @code * bool celsius_to_fahrenheit(const double& celsius, double& fahrenheit); * bool fahrenheit_to_celsius(const double& fahrenheit, double& celsius); * Glib::Binding::bind_property(adjustment1->property_value(), * adjustment2->property_value(), Glib::Binding::Flags::BIDIRECTIONAL, * sigc::ptr_fun(celsius_to_fahrenheit), sigc::ptr_fun(fahrenheit_to_celsius)); * @endcode * * will keep property_value() of the two adjustments in sync; the * celsius_to_fahrenheit() function will be called whenever * property_value() of @a adjustment1 changes and will transform the current value * of the property before applying it to property_value() of @a adjustment2. * * Vice versa, the fahrenheit_to_celsius() function will be called whenever * property_value() of @a adjustment2 changes, and will transform the * current value of the property before applying it to property_value() * of @a adjustment1. * * Note that Glib::Binding does not resolve cycles by itself; a cycle like * * @code * object1->property_A() -> object2->property_B() * object2->property_B() -> object3->property_C() * object3->property_C() -> object1->property_A() * @endcode * * might lead to an infinite loop. The loop, in this particular case, * can be avoided if the objects emit the GObject::notify signal only * if the value has effectively been changed. A binding is implemented * using the GObject::notify signal, so it is susceptible to all the * various ways of blocking a signal emission, like Glib::SignalProxyNormal::emission_stop() * or g_signal_handler_block(). * * The binding between the two properties is broken whenever either one of the * Glib::ObjectBase instances it refers to is deleted or when unbind() is called. * If a RefPtr remains after the binding has been broken, get_source() * and get_target() return a nullptr, and dup_source() and dup_target() * return an empty RefPtr. It's not necessary to keep a RefPtr * in order to keep the binding alive. * * @newin{2,44} */ class GLIBMM_API Binding : public Glib::Object { _CLASS_GOBJECT(Binding, GBinding, G_BINDING, Glib::Object, GObject, , , GLIBMM_API) public: _WRAP_ENUM(Flags, GBindingFlags, newin "2,44", decl_prefix GLIBMM_API) /** A slot to be called to transform values in a binding created by * bind_property_value(). * * For instance: * @code * bool on_transform_to(const GValue* from_value, GValue* to_value); * @endcode * * @return true if the transformation was successful, and false otherwise. */ using SlotTransform = sigc::slot; /** A slot to be called to transform values in a binding created by * bind_property(). * * For instance: * @code * std::optional on_transform_to(const Glib::ustring& from_string); * @endcode * * @return a value of type T_to if the transformation was successful, * and an empty optional with no value (i.e. std::nullopt) otherwise. */ template using SlotTypedTransform = sigc::slot (const T_from&)>; // GValue* or Glib::ValueBase& in SlotTransform? // Binding_transform_callback_common() is simpler and faster with GValue*. // No need to copy between GValue and Glib::ValueBase. ValueBase would only // be marginally better for users of bind_property_value(). Users would want // Value and Value, meaning that bind_property_value() // would have to be a template function. Most users would probably still // prefer bind_property(). bind_property_value() is public partly because // it's a good place to present documentation common to all the // bind_property() overloads. // See also https://gitlab.gnome.org/GNOME/glibmm/issues/61 /** Creates a binding between @a source_property and @a target_property, * allowing you to set the transformation functions to be used by the binding. * * If @a flags contains Glib::Binding::Flags::BIDIRECTIONAL then the binding will be mutual: * if @a target_property changes then the @a source_property * will be updated as well. The @a transform_from function is only used in case * of bidirectional bindings, otherwise it will be ignored. * * The binding will automatically be removed when either the source or the * target instance is deleted. To remove the binding without affecting the * source and the target you can call unbind() on the returned Binding instance. * * A Glib::ObjectBase instance can have multiple bindings. * * If you supply transformation functions, it is usually easier to use one of the * bind_property() overloads, to avoid the use of GValue in the transformation functions. * * @param source_property The source property to bind. * @param target_property The target property to bind. * @param flags Flags to pass to Binding. * @param transform_to The transformation function from the source to the target, * or an empty slot to use the default. * @param transform_from The transformation function from the target to the source, * or an empty slot to use the default. * @return The Binding instance representing the binding between the two * Glib::ObjectBase instances, or nullptr in case of error. * * @newin{2,44} */ static Glib::RefPtr bind_property_value( const PropertyProxy_Base& source_property, const PropertyProxy_Base& target_property, Flags flags = Flags::DEFAULT, const SlotTransform& transform_to = {}, const SlotTransform& transform_from = {}); _IGNORE(g_object_bind_property, g_object_bind_property_full, g_object_bind_property_with_closures) /** Creates a binding between @a source_property and @a target_property. * * @param source_property The source property to bind. * @param target_property The target property to bind. * @param flags Flags to pass to Binding. * @return The Binding instance representing the binding between the two * Glib::ObjectBase instances, or nullptr in case of error. * * @see bind_property_value() * * @newin{2,44} */ static Glib::RefPtr bind_property( const PropertyProxy_Base& source_property, const PropertyProxy_Base& target_property, Flags flags = Flags::DEFAULT) { return bind_property_value(source_property, target_property, flags); } /** Creates a binding between @a source_property and @a target_property, * allowing you to set a transformation function to be used by the binding. * * @param source_property The source property to bind. * @param target_property The target property to bind. * @param flags Flags to pass to Binding. * @param transform_to The transformation function from the source to the target, * or an empty slot to use the default. * @return The Binding instance representing the binding between the two * Glib::ObjectBase instances, or nullptr in case of error. * * @tparam T_source Type of the source property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_target Type of the target property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_functor_to Type of functor that translates from the source to the target. * Must be convertible to SlotTypedTransform. * * @see bind_property_value() * * @newin{2,44} */ template static Glib::RefPtr bind_property( const PropertyProxy& source_property, const PropertyProxy& target_property, Flags flags, const T_functor_to& transform_to) { SlotTypedTransform slot_transform_to = transform_to; return bind_property_value(source_property, target_property, flags, slot_transform_to.empty() ? SlotTransform() : TransformProp(slot_transform_to)); } /** Creates a binding between @a source_property and @a target_property, * allowing you to set a transformation function to be used by the binding. * * @param source_property The source property to bind. * @param target_property The target property to bind. * @param flags Flags to pass to Binding. * @param transform_to The transformation function from the source to the target, * or an empty slot to use the default. * @return The Binding instance representing the binding between the two * Glib::ObjectBase instances, or nullptr in case of error. * * @tparam T_source Type of the source property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_target Type of the target property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_functor_to Type of functor that translates from the source to the target. * Must be convertible to SlotTypedTransform. * * @see bind_property_value() * * @newin{2,44} */ template static Glib::RefPtr bind_property( const PropertyProxy& source_property, const PropertyProxy_WriteOnly& target_property, Flags flags, const T_functor_to& transform_to) { SlotTypedTransform slot_transform_to = transform_to; return bind_property_value(source_property, target_property, flags, slot_transform_to.empty() ? SlotTransform() : TransformProp(slot_transform_to)); } /** Creates a binding between @a source_property and @a target_property, * allowing you to set a transformation function to be used by the binding. * * @param source_property The source property to bind. * @param target_property The target property to bind. * @param flags Flags to pass to Binding. * @param transform_to The transformation function from the source to the target, * or an empty slot to use the default. * @return The Binding instance representing the binding between the two * Glib::ObjectBase instances, or nullptr in case of error. * * @tparam T_source Type of the source property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_target Type of the target property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_functor_to Type of functor that translates from the source to the target. * Must be convertible to SlotTypedTransform. * * @see bind_property_value() * * @newin{2,44} */ template static Glib::RefPtr bind_property( const PropertyProxy_ReadOnly& source_property, const PropertyProxy& target_property, Flags flags, const T_functor_to& transform_to) { SlotTypedTransform slot_transform_to = transform_to; return bind_property_value(source_property, target_property, flags, slot_transform_to.empty() ? SlotTransform() : TransformProp(slot_transform_to)); } /** Creates a binding between @a source_property and @a target_property, * allowing you to set a transformation function to be used by the binding. * * @param source_property The source property to bind. * @param target_property The target property to bind. * @param flags Flags to pass to Binding. * @param transform_to The transformation function from the source to the target, * or an empty slot to use the default. * @return The Binding instance representing the binding between the two * Glib::ObjectBase instances, or nullptr in case of error. * * @tparam T_source Type of the source property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_target Type of the target property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_functor_to Type of functor that translates from the source to the target. * Must be convertible to SlotTypedTransform. * * @see bind_property_value() * * @newin{2,44} */ template static Glib::RefPtr bind_property( const PropertyProxy_ReadOnly& source_property, const PropertyProxy_WriteOnly& target_property, Flags flags, const T_functor_to& transform_to) { SlotTypedTransform slot_transform_to = transform_to; return bind_property_value(source_property, target_property, flags, slot_transform_to.empty() ? SlotTransform() : TransformProp(slot_transform_to)); } /** Creates a binding between @a source_property and @a target_property, * allowing you to set the transformation functions to be used by the binding. * * @param source_property The source property to bind. * @param target_property The target property to bind. * @param flags Flags to pass to Binding. * @param transform_to The transformation function from the source to the target, * or an empty slot to use the default. * @param transform_from The transformation function from the target to the source, * or an empty slot to use the default. * @return The Binding instance representing the binding between the two * Glib::ObjectBase instances, or nullptr in case of error. * * @tparam T_source Type of the source property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_target Type of the target property. Must be a type that can be * stored in a Glib::Value object. * @tparam T_functor_to Type of functor that translates from the source to the target. * Must be convertible to SlotTypedTransform. * @tparam T_functor_from Type of functor that translates from the target to the source. * Must be convertible to SlotTypedTransform. * * @see bind_property_value() * * @newin{2,44} */ template static Glib::RefPtr bind_property( const PropertyProxy& source_property, const PropertyProxy& target_property, Flags flags, const T_functor_to& transform_to, const T_functor_from& transform_from) { SlotTypedTransform slot_transform_to = transform_to; SlotTypedTransform slot_transform_from = transform_from; return bind_property_value(source_property, target_property, flags, slot_transform_to.empty() ? SlotTransform() : TransformProp(slot_transform_to), slot_transform_from.empty() ? SlotTransform() : TransformProp(slot_transform_from)); } #m4 _CONVERSION(`GObject*',`Glib::ObjectBase*',`Glib::wrap_auto($3)') _WRAP_METHOD(Glib::ObjectBase* get_source(), g_binding_get_source, newin "2,44", deprecated "Use dup_source() instead.") _WRAP_METHOD(const Glib::ObjectBase* get_source() const, g_binding_get_source, constversion, newin "2,44", deprecated "Use dup_source() instead.") // Don't use Glib::RefPtr Glib::wrap(GObject* object, bool take_copy). // The object may be an interface object, e.g. Gio::Action. Such an object can't be // cast to Glib::Object. https://gitlab.gnome.org/GNOME/glibmm/-/issues/93 #m4 _CONVERSION(`GObject*',`Glib::RefPtr',`Glib::make_refptr_for_instance(Glib::wrap_auto($3))') _WRAP_METHOD(Glib::RefPtr dup_source(), g_binding_dup_source, newin "2,70") _WRAP_METHOD(Glib::RefPtr dup_source() const, g_binding_dup_source, constversion, newin "2,70") _WRAP_METHOD(Glib::ustring get_source_property() const, g_binding_get_source_property, newin "2,44") _WRAP_METHOD(Glib::ObjectBase* get_target(), g_binding_get_target, newin "2,44", deprecated "Use dup_target() instead.") _WRAP_METHOD(const Glib::ObjectBase* get_target() const, g_binding_get_target, constversion, newin "2,44", deprecated "Use dup_target() instead.") _WRAP_METHOD(Glib::RefPtr dup_target(), g_binding_dup_target, newin "2,70") _WRAP_METHOD(Glib::RefPtr dup_target() const, g_binding_dup_target, constversion, newin "2,70") _WRAP_METHOD(Glib::ustring get_target_property() const, g_binding_get_target_property, newin "2,44") _WRAP_METHOD(Flags get_flags() const, g_binding_get_flags, newin "2,44") /** Explicitly releases the binding between the source and the target * property expressed by this %Binding instance. * * The binding is also released if either the source object or the target * object is deleted. * * @newin{2,44} */ void unbind(); _IGNORE(g_binding_unbind) _WRAP_PROPERTY("flags", Flags, newin "2,44") _WRAP_PROPERTY("source", Glib::RefPtr, newin "2,44") _WRAP_PROPERTY("source-property", Glib::ustring, newin "2,44") _WRAP_PROPERTY("target", Glib::RefPtr, newin "2,44") _WRAP_PROPERTY("target-property", Glib::ustring, newin "2,44") private: // The functor TransformProp can be implicitly converted to a SlotTransform // and used in a call to bind_property_value(). template class TransformProp { public: explicit TransformProp(const SlotTypedTransform& slot) : typed_transform(slot) {} // g++-10 with optimization level -O1 or higher warns about possible use of // an uninitialized variable when tests/glibmm_binding/main.cc is compiled. // I don't understand why. Don't optimize this function. /Kjell 2020-12-08 // sigc++-3.0/sigc++/functors/slot.h:226:21: warning: ‘’ may be // used uninitialized in this function [-Wmaybe-uninitialized] // 226 | return T_return(); #if __GNUC__ >= 10 __attribute__ ((optimize(0))) #endif bool operator()(const GValue* from_value, GValue* to_value) { Glib::Value from_glib_value; from_glib_value.init(from_value); const auto to = typed_transform(from_glib_value.get()); if (!to.has_value()) return false; Glib::Value to_glib_value; to_glib_value.init(to_value); to_glib_value.set(*to); g_value_copy(to_glib_value.gobj(), to_value); return true; } private: SlotTypedTransform typed_transform; }; }; } // namespace Glib