NAME

Glib::Ex::ConnectProperties -- link properties between objects

SYNOPSIS

use Glib::Ex::ConnectProperties;
my $conn = Glib::Ex::ConnectProperties->new ([$check,'active'],
                                             [$widget,'visible']);

$conn->disconnect;   # explicit disconnect

DESCRIPTION

Glib::Ex::ConnectProperties links together specified properties on two or more Glib::Objects (including Gtk2 widgets) so a change made to any one of them is propagated to the others.

This is an easy way to tie a user control widget to a setting elsewhere. For example a CheckButton active could be linked to the visible of another widget, letting the user click to hide or show it.

+--------------------+             +-------------+
| CheckButton/active |  <------->  | Foo/visible |
+--------------------+             +-------------+

The advantage of ConnectProperties is that it's bi-directional, so if other code changes "Foo/visible" then that change is sent to "CheckButton/active" too, ensuring the button display is up-to-date with what it's controlling, no matter how the target changes.

Property Types

String, number, enum, flags, and object properties are supported. Some boxed types like Gtk2::Gdk::Color work too, but others have problems (see "IMPLEMENTATION NOTES" below).

Read-only properties can be used. They're read and propagated, but changes in other linked properties are not stored back. This can leave different values, which rather defeats the purpose of the linkage. Linking a read-only probably only makes sense if the read-only one is the only one changing.

Write-only properties can be used. Nothing is read out of them, they're just set from changes in the other linked properties. (Write-only properties are often pseudo "add" methods etc, so it's probably unlikely linking a write-only will do much good.)

It works to connect two properties on the same object; doing so can ensure they update together. It also works to have two different ConnectProperties with an object/property in common; a change coming from one group propagates through to the other just fine. In fact such a setup arises quite naturally if you've got two controls for the same target; neither needs to know the other exists.

Values

Before storing a value it's put through value_validate on the target ParamSpec (in Glib-Perl 1.220 where that method is available). This for instance clamps numbers which might be out of range, etc. This may not be quite right, but at least lets the target get close.

In the future the intention is to have some "map" options or transformation functions on a per object+property basis, allowing for example a boolean to become a string, or enum values to be changed around, etc.

FUNCTIONS

$conn = Glib::Ex::ConnectProperties->new ([$obj1,$prop1], [$obj,$prop2], ...)

Connect two or more given object+property combinations. Each argument is an arrayref with an object and a property name. For example

$conn = Glib::Ex::ConnectProperties->new
            ([$aa_object, 'one-prop'],
             [$bb_object, 'another-prop']);

The return value is a Perl object of type Glib::Ex::ConnectProperties. You can keep that to later break the connection explicitly with disconnect below, or otherwise you can ignore it.

An initial value is propagated from the first object+property (or the first with readable flag) to set all the others, in case they're not already the same. So put the object with the right initial value first.

A ConnectProperties linkage lasts as long as the linked objects exist, but it only keeps weak references to those objects, so the linkage doesn't prevent some or all of them being garbage collected.

$conn->disconnect()

Disconnect the given ConnectProperties linkage.

IMPLEMENTATION NOTES

ConnectProperties uses a notify signal handler on each object to update the others. Updating those others makes them in turn emit further notify signals (even if the value is unchanged), so some care must be taken not to have an infinite loop. The present strategy is twofold

  • An "in progress" flag in the ConnectProperties object, so during an update it recognises any further notify emissions as its own doing and can be ignored.

  • On each target the value from a get is compared before doing a set. If it's already what's wanted then the set call is not made at all.

The in-progress flag acts against immediate further notifys. They could also be avoided by disconnecting or blocking the respective handlers temporarily, but that seems more work than ignoring.

The compare-before-set copes with freeze_notify, because in that case the notify calls don't come while the "in progress" flag is on, only later, perhaps a long time later.

It might be wondered if something simpler is possible, and the short answer is that for the general case, not really. The specific set_foo methods on most widgets and objects will often skip an unchanged setting, but alas when using the generic set_property the protection above is needed.

Equality

An existing value and prospective new value are compared using values_cmp in Glib-Perl 1.220 or a fallback otherwise. ParamSpec subclasses can thus control what they consider equal. For example on float point properties anything within "epsilon" (1e-30 by default) is close enough.

The core Glib::Param::Boxed only compares by pointer value, which is fairly useless because boxed objects are copied so you hardly ever see an identical pointer. ConnectProperties tries to improve this by: Using an equal or compare method from the value type when available (eg. Gtk2::Gdk::Color). Using eq on Glib::Scalar though that may be of limited help (subclassing the scalar ParamSpec and giving a new values_cmp is probably much better, if/when it's possible). And using special code on Glib::Strv and Gtk2::Border comparing by content, and Gtk2::Gdk::Cursor comparing by type (but bitmap cursors are still only by pointer).

Potentially a Glib::Param::Object pspec could benefit from using an equal or compare method from the value type, so object classes could say how their contents can be compared. For now that's not done, since none of the core types have such methods, and since like say Glib::Scalar it may be better as a values_cmp in a ParamSpec subclass, so both C and Perl code then know how to compare.

Notifies

Incidentally, if you're writing an object or widget don't forget you must explicitly notify if changing a property from anywhere outside a SET_PROPERTY call. (Duplicate notifies from within SET_PROPERTY are ok and are collapsed to just one emission at the end.) Of course this is required for any object or widget, but failing to do will mean in particular that ConnectProperties won't work.

SEE ALSO

Glib::Object

HOME PAGE

http://user42.tuxfamily.org/glib-ex-connectproperties/index.html

LICENSE

Copyright 2007, 2008, 2009, 2010 Kevin Ryde

Glib-Ex-ConnectProperties is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.

Glib-Ex-ConnectProperties 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 General Public License for more details.

You should have received a copy of the GNU General Public License along with Glib-Ex-ConnectProperties. If not, see http://www.gnu.org/licenses/.