1package Gtk2::Ex::FormFactory::Context;
2
3use strict;
4use Carp;
5
6use Gtk2::Ex::FormFactory::ProxyBuffered;
7
8sub get_proxies_by_name		{ shift->{proxies_by_name}		}
9sub get_widgets_by_attr		{ shift->{widgets_by_attr}		}
10sub get_widgets_by_object	{ shift->{widgets_by_object}		}
11sub get_depend_trigger_href	{ shift->{depend_trigger_href}		}
12sub get_aggregated_by_href	{ shift->{aggregated_by_href}		}
13sub get_update_hooks_by_object	{ shift->{update_hooks_by_object}	}
14sub get_widget_activity_href	{ shift->{widget_activity_href}		}
15
16sub get_default_set_prefix	{ shift->{default_set_prefix}		}
17sub get_default_get_prefix	{ shift->{default_get_prefix}		}
18
19sub set_default_set_prefix	{ shift->{default_set_prefix}	= $_[1]	}
20sub set_default_get_prefix	{ shift->{default_get_prefix}	= $_[1]	}
21
22sub new {
23	my $class = shift;
24	my %par = @_;
25	my  ($default_set_prefix, $default_get_prefix) =
26	@par{'default_set_prefix','default_get_prefix'};
27
28	$default_set_prefix = "set_" if not defined $default_set_prefix;
29	$default_get_prefix = "get_" if not defined $default_get_prefix;
30
31	my $self = bless {
32		default_set_prefix	=> $default_set_prefix,
33		default_get_prefix	=> $default_get_prefix,
34
35		proxies_by_name		=> {},  # ->{"$object"} = $proxy
36		widgets_by_attr		=> {},  # ->{"$object.$attr"}->{"$widget_ff_name.$widget_name"} = $widget
37		widgets_by_object	=> {},  # ->{"$object"} = $widget
38		update_hooks_by_object  => {},  # ->{"$object"} = CODEREF
39		depend_trigger_href	=> {},  # ->{"$master_object.$master_attr"}->{"$slave_object.$slave_attr"} = 1
40		aggregated_by_href	=> {},  # ->{"$object.$attr"}->{"$object"} = 1
41		widget_activity_href	=> {},  # ->{"$object.$attr"}->{"$widget_name"} = $widget
42
43	}, $class;
44
45	$self->add_object(
46		name   => "__dummy",
47		object => bless {}, "Gtk2::Ex::FormFactory::Dummy",
48	);
49
50	return $self;
51}
52
53sub norm_object {
54        my $self = shift;
55        my ($object) = @_;
56        my ($o, $a) = split(/\./, $object);
57        return $o;
58}
59
60sub norm_object_attr {
61        my $self = shift;
62        my ($object, $attr) = @_;
63
64        $object = '' if ! defined $object;
65        $attr   = '' if ! defined $attr;
66
67        return ""      if $object eq '' && $attr eq '';
68        return $attr   if defined $attr && $attr =~ /\./;
69        return $object if !defined $attr || $attr eq '';
70
71        if ( $object =~ /\./ ) {
72            my ($o, $a) = split(/\./, $object);
73            return "$o.$attr";
74        }
75
76        die "Illegal object.attr definition object='$object' attr='$attr'"
77            if $object eq '';
78
79        return ($object, $attr) if wantarray;
80        return "$object.$attr";
81}
82
83sub add_object {
84	my $self = shift;
85	my %par = @_;
86	my  ($name, $object, $set_prefix, $get_prefix, $attr_activity_href) =
87	@par{'name','object','set_prefix','get_prefix','attr_activity_href'};
88	my  ($attr_depends_href, $attr_accessors_href, $update_hook) =
89	@par{'attr_depends_href','attr_accessors_href','update_hook'};
90	my  ($aggregated_by, $accessor, $buffered, $changes_attr_filter) =
91	@par{'aggregated_by','accessor','buffered','changes_attr_filter'};
92
93        $set_prefix = $self->get_default_set_prefix if ! defined $set_prefix;
94        $get_prefix = $self->get_default_get_prefix if ! defined $get_prefix;
95
96	if ( $attr_depends_href ) {
97		my $depend_trigger_href = $self->get_depend_trigger_href;
98		foreach my $attr ( keys %{$attr_depends_href} ) {
99			if ( not ref $attr_depends_href->{$attr} ) {
100				my $depends_attr = $attr_depends_href->{$attr};
101                                $depends_attr = $self->norm_object_attr($name, $depends_attr);
102				$depend_trigger_href->{$depends_attr}->{"$name.$attr"} = 1;
103			} elsif ( ref $attr_depends_href->{$attr} eq 'ARRAY' ) {
104				foreach my $depends_attr ( @{$attr_depends_href->{$attr}} ) {
105                                        $depends_attr = $self->norm_object_attr($name, $depends_attr);
106					$depend_trigger_href->{$depends_attr}->{"$name.$attr"} = 1;
107				}
108			} else {
109				croak "Illegal attr_depends_href value for attribute '$attr'";
110			}
111		}
112	}
113
114	my $proxies_by_name = $self->get_proxies_by_name;
115
116	die "Object with name '$name' already registered to this context"
117		if $proxies_by_name->{$name};
118
119	$self->get_update_hooks_by_object->{$name} = $update_hook
120		if $update_hook;
121
122	if ( $aggregated_by ) {
123		my ($parent_object, $parent_attr) = split(/\./, $aggregated_by);
124                die "aggregated_by definition of object '$name' needs an attr"
125                    unless $parent_attr;
126		my $parent_proxy = $self->get_proxy($parent_object);
127		$parent_proxy->get_attr_aggregate_href->{$parent_attr} = $name;
128		$self->get_aggregated_by_href->{$aggregated_by}->{$name} = 1;
129	}
130
131	my $proxy_class = $buffered ? "Gtk2::Ex::FormFactory::ProxyBuffered" :
132				      "Gtk2::Ex::FormFactory::Proxy";
133
134	return $proxies_by_name->{$name} = $proxy_class->new (
135		    context       	=> $self,
136		    name          	=> $name,
137		    object        	=> $object,
138		    set_prefix    	=> $set_prefix,
139		    get_prefix    	=> $get_prefix,
140		    attr_activity_href	=> $attr_activity_href,
141		    attr_accessors_href	=> $attr_accessors_href,
142		    aggregated_by	=> $aggregated_by,
143		    accessor		=> $accessor,
144                    changes_attr_filter => $changes_attr_filter,
145	);
146}
147
148sub remove_object {
149	my $self = shift;
150	my ($name) = @_;
151
152	my $proxies_by_name = $self->get_proxies_by_name;
153
154	die "Object with name '$name' not registered to this context"
155		unless $proxies_by_name->{$name};
156
157	$proxies_by_name->{$name}->set_object(undef);
158	delete $proxies_by_name->{$name};
159
160	1;
161}
162
163sub register_widget {
164	my $self = shift;
165	my ($widget) = @_;
166
167        my $object = $widget->get_object;
168
169	if ( $widget->get_active_depends ) {
170	    my $dep = $widget->get_active_depends;
171	    $dep = [ $dep ] unless ref $dep eq 'ARRAY';
172	    for my $oa ( @{$dep} ) {
173                my $norm_oa = $self->norm_object_attr($oa);
174                my $norm_o  = $self->norm_object($oa);
175		$self->get_widget_activity_href
176		     ->{$self->norm_object_attr($norm_oa)}
177		     ->{$widget->get_name} = $widget;
178                if ( $norm_oa ne $norm_o ) {
179		    $self->get_widget_activity_href
180			 ->{$self->norm_object_attr($norm_o)}
181			 ->{$widget->get_name} = $widget;
182                }
183	    }
184	}
185
186	my $object_attr = $self->norm_object_attr($widget->get_object, $widget->get_attr);
187
188	return if $object_attr eq '';
189
190	my $widget_full_name =
191		$widget->get_form_factory->get_name.".".
192		$widget->get_name;
193
194	$Gtk2::Ex::FormFactory::DEBUG &&
195	    print "REGISTER: $object_attr => $widget_full_name\n";
196
197	$self->get_widgets_by_attr
198	     ->{$object_attr}
199	     ->{$widget_full_name} = $widget;
200
201	if ( $widget->has_additional_attrs ) {
202		my $add_attrs = $widget->has_additional_attrs;
203		my $object = $widget->get_object;
204		foreach my $add_attr ( @{$add_attrs} ) {
205			my $get_attr_name_method = "get_attr_$add_attr";
206			my $attr = $widget->$get_attr_name_method();
207			$self->get_widgets_by_attr
208			     ->{$self->norm_object_attr($object, $attr)}
209			     ->{$widget_full_name} = $widget;
210		}
211	}
212
213	$self->get_widgets_by_object
214	     ->{$widget->get_object}
215	     ->{$widget_full_name} = $widget;
216
217	1;
218}
219
220sub deregister_widget {
221	my $self = shift;
222	my ($widget) = @_;
223
224	$Gtk2::Ex::FormFactory::DEBUG &&
225	    print "DEREGISTER ".$widget->get_name."\n";
226
227        my $object      = $widget->get_object;
228	my $object_attr = $self->norm_object_attr($widget->get_object, $widget->get_attr);
229        return if $object_attr eq '';
230
231	my $widget_full_name =
232		$widget->get_form_factory->get_name.".".
233		$widget->get_name;
234
235	$Gtk2::Ex::FormFactory::DEBUG &&
236	    print "DEREGISTER: $object_attr => $widget_full_name\n";
237
238	warn "Widget not registered ($object_attr => $widget_full_name)"
239		unless $self->get_widgets_by_attr
240			    ->{$object_attr}
241			    ->{$widget_full_name};
242
243	delete $self->get_widgets_by_attr
244		    ->{$object_attr}
245		    ->{$widget_full_name};
246
247        delete $self->get_widgets_by_attr->{$object_attr}
248            if keys %{$self->get_widgets_by_attr->{$object_attr}} == 0;
249
250	if ( $widget->get_active_depends ) {
251	    my $dep = $widget->get_active_depends;
252	    $dep = [ $dep ] unless ref $dep eq 'ARRAY';
253	    for my $oa ( @{$dep} ) {
254                my $norm_oa = $self->norm_object_attr($oa);
255                my $norm_o  = $self->norm_object($oa);
256		delete $self->get_widget_activity_href
257		            ->{$self->norm_object_attr($norm_oa)}
258		            ->{$widget->get_name};
259                if ( $norm_oa ne $norm_o ) {
260		    delete $self->get_widget_activity_href
261			        ->{$self->norm_object_attr($norm_o)}
262			        ->{$widget->get_name};
263                }
264	    }
265	}
266
267	if ( $widget->has_additional_attrs ) {
268		my $add_attrs = $widget->has_additional_attrs;
269		my $object = $widget->get_object;
270		foreach my $add_attr ( @{$add_attrs} ) {
271			my $get_attr_name_method = "get_attr_$add_attr";
272			my $attr = $widget->$get_attr_name_method();
273                        my $norm_attr = $self->norm_object_attr($object, $attr);
274			delete $self->get_widgets_by_attr
275				    ->{$norm_attr}
276				    ->{$widget_full_name};
277                        delete $self->get_widgets_by_attr->{$norm_attr}
278                            if keys %{$self->get_widgets_by_attr->{$norm_attr}} == 0;
279		}
280	}
281
282	delete $self->get_widgets_by_object
283		    ->{$widget->get_object}
284		    ->{$widget_full_name};
285
286	1;
287}
288
289sub get_proxy {
290	my $self = shift;
291	my ($name) = @_;
292
293        $name = $self->norm_object($name);
294
295	my $proxy = $self->get_proxies_by_name->{$name};
296
297	croak "Object '$name' not added to this context"
298		unless $proxy;
299
300	return $proxy;
301}
302
303sub get_object {
304	my $self = shift;
305	my ($name) = @_;
306
307        $name = $self->norm_object($name);
308
309	my $proxy = $self->get_proxies_by_name->{$name};
310
311	croak "Object '$name' not added to this context"
312		unless $proxy;
313
314	return $proxy->get_object;
315}
316
317sub set_object {
318	my $self = shift;
319	my ($name, $object) = @_;
320
321        $name = $self->norm_object($name);
322
323	my $proxy = $self->get_proxies_by_name->{$name};
324
325	croak "Object $name not added to this context"
326		unless $proxy;
327
328	$proxy->set_object($object);
329
330	return $object;
331}
332
333sub set_object_attr {
334	my $self = shift;
335	my ($object_name, $attr_name, $value);
336	if ( @_ == 2 ) {
337		($object_name, $attr_name) = split(/\./, $_[0]);
338		$value = $_[1];
339	} elsif ( @_ == 3 ) {
340		($object_name, $attr_name, $value) = @_;
341	} else {
342		croak qq[Usage: set_object_attr("object.attr","value")].
343		      qq[       set_object_attr("object","attr","value")];
344
345	}
346
347	my $proxy = $self->get_proxies_by_name->{$object_name}
348		or die "Object '$object_name' not registered";
349
350	$proxy->set_attr($attr_name, $value);
351
352	return $value;
353}
354
355sub get_object_attr {
356	my $self = shift;
357	my ($object_name, $attr_name);
358	if ( @_ == 1 ) {
359		($object_name, $attr_name) = split(/\./, $_[0]);
360	} elsif ( @_ == 2 ) {
361		($object_name, $attr_name) = @_;
362	} else {
363		croak qq[Usage: get_object_attr("object.attr")].
364		      qq[       get_object_attr("object","attr")];
365
366	}
367
368	my $proxy = $self->get_proxies_by_name->{$object_name}
369		or die "Object '$object_name' not registered";
370
371	return $proxy->get_attr($attr_name);
372}
373
374sub update_object_attr_widgets {
375	my $self = shift;
376	my ($object, $attr) = @_;
377
378        my $object_attr = $self->norm_object_attr($object, $attr);
379
380	$Gtk2::Ex::FormFactory::DEBUG &&
381	    print "update_object_attr_widgets($object, $attr)\n";
382
383	my $widgets_by_attr      = $self->get_widgets_by_attr;
384	my $depend_trigger_href  = $self->get_depend_trigger_href;
385	my $widget_activity_href = $self->get_widget_activity_href;
386
387        foreach my $w ( values %{$widgets_by_attr->{$object_attr}} ) {
388            $w->update;
389        }
390        foreach my $w ( values %{$widget_activity_href->{$object_attr}} ) {
391            $w->update;
392        }
393
394	foreach my $update_object_attr ( keys %{$depend_trigger_href->{$object_attr}} ) {
395                foreach my $w ( values %{$widgets_by_attr->{$update_object_attr}} ) {
396                    $w->update;
397                }
398                foreach my $name ( keys %{$self->get_aggregated_by_href->{$update_object_attr}} ) {
399                    $self->get_proxy($name)->update_by_aggregation;
400                }
401	}
402
403	1;
404}
405
406sub update_object_widgets {
407	my $self = shift;
408	my ($name) = @_;
409
410	$Gtk2::Ex::FormFactory::DEBUG &&
411	    print "update_object_widgets($name)\n";
412
413        $name = $self->norm_object_attr($name);
414
415	my $object       = $self->get_object($name);
416	my $change_state = defined $object ? '' : 'empty,inactive';
417
418	my $widgets_by_object = $self->get_widgets_by_object;
419
420        foreach my $w ( values %{$widgets_by_object->{$name}} ) {
421            $w->update($change_state);
422        }
423
424	my $widget_activity_href = $self->get_widget_activity_href;
425        foreach my $w ( values %{$widget_activity_href->{$name}} ) {
426            $w->update($change_state);
427        }
428
429	my $update_hook = $self->get_update_hooks_by_object->{$name};
430	&$update_hook($object) if $update_hook;
431
432	1;
433}
434
435sub update_object_widgets_activity {
436	my $self = shift;
437	my ($name, $activity) = @_;
438
439	warn "activity !(empty|inactive|active)"
440		unless $activity =~ /^empty|inactive|active$/;
441
442	$Gtk2::Ex::FormFactory::DEBUG &&
443	    print "update_object_activity($name)\n";
444
445	my $widgets_by_object = $self->get_widgets_by_object;
446
447        foreach my $w ( values %{$widgets_by_object->{$name}} ) {
448            $w->update;
449        }
450
451	1;
452}
453
4541;
455
456__END__
457
458=head1 NAME
459
460Gtk2::Ex::FormFactory::Context - Context in a FormFactory framework
461
462=head1 SYNOPSIS
463
464  my $context = Gtk2::Ex::FormFactory::Context->new (
465    default_get_prefix => Default prefix for read accessors,
466    default_set_prefix => Default prefix for write accessors,
467  );
468
469  $context->add_object (
470    name                => Name of the application object in
471    			   this Context,
472    aggregated_by       => Object.Attribute of the parent object
473                           aggregating this object
474    object              => The application object itself or a
475    			   callback which returns the object,
476			   or undef if aggregated or set later
477    get_prefix          => Prefix for read accessors,
478    set_prefix          => Prefix for write accessors,
479    accessor            => CODEREF which handles access to all object
480    			   attributes
481    attr_activity_href  => Hash of CODEREFS for attributes which return
482			   activity of the corresponding attributes,
483    attr_depends_href   => Hash defining attribute dependencies,
484    attr_accessors_href => Hash of CODEREFS which override correspondent
485    			   accessors in this Context,
486    buffered            => Indicates whether changes should be buffered,
487    changes_attr_filter => Regex for attributes which should not trigger
488                           the object's 'changed' status
489  );
490
491=head1 DESCRIPTION
492
493This module implements a very importent concept of the
494Gtk2::Ex::FormFactory framework.
495
496The Context knows of all
497your application objects, how attributes of the objects
498can be accessed (for reading and writing), which attributes
499probably depend on other attributes and knows of how to control
500the activity state of attributes resp. of the Widgets which
501represent these attributes.
502
503So the Context is a layer between your application objects and
504the GUI which represents particular attributes of your objects.
505
506=head1 OBJECT HIERARCHY
507
508  Gtk2::Ex::FormFactory::Context
509
510=head1 ATTRIBUTES
511
512Attributes are handled through the common get_ATTR(), set_ATTR()
513style accessors, but they are mostly passed once to the object
514constructor and must not be altered after associated FormFactory's
515were built.
516
517=over 4
518
519=item B<default_get_prefix> = SCALAR [optional]
520
521Usually your application's objects use a common prefix for all
522attribute accessors. This defines the default prefix for read
523accessors and defaults to "B<get_>".
524
525=item B<default_set_prefix> = SCALAR [optional]
526
527Usually your application's objects use a common prefix for all
528attribute accessors. This defines the default prefix for write
529accessors and defaults to "B<set_>".
530
531=back
532
533=head1 METHODS
534
535=over 4
536
537=item $context->B<add_object> (...)
538
539All your application objects must be added to the Context using
540this method. Parameters are passed to the method as a hash:
541
542=over 4
543
544=item B<name> = SCALAR [mandatory]
545
546Each object in a Context need a unique name, so this parameter
547is mandatory. You refer to this name when you create Widgets and
548want to associate these with your application object's attributes.
549
550=item B<object> = BLESSED REF|CODEREF [optional]
551
552This is the application object itself, or a code reference which
553returns the object. Using the code reference option gives you
554very flexible control of what this object actually is. But also
555note that this may have some impact on performance, because this
556code reference will be called quite often.
557
558Often objects are aggregated by other objects, in that case don't
559set the object reference here but use the B<aggregate_by> option
560described below.
561
562An application object in terms of the Context may become undef,
563that's why the B<object> parameter is optional here. Also the
564code reference may return undef.
565
566Once an object gets undef, all
567associated widgets will be set inactive automatically. You can
568control per widget if it should render invisible or insensitive
569in that case. Refer to L<Gtk2::Ex::FormFactory::Widget> for
570details.
571
572=item B<aggregated_by> = "object.attr" [optional]
573
574If this object has a parent object set this option to the
575fully qualified attribute holding the object reference, using
576the object dot attribute notation:
577
578  object.attr
579
580where B<object> is the name of the parent object used to register
581it to this Context, and B<attr> the attribute holding
582the reference to the object currently added to the Context.
583
584Once this attribute resp. the parent object change, the Context
585will be updated automatically, including all widgets depending
586on this widget.
587
588This way you can define your full object aggregation hierarchy
589and Gtk2::Ex::FormFactory takes care of all resulting dependencies
590on the GUI.
591
592=item B<get_prefix> = SCALAR [optional]
593
594With this parameter you can override the B<default_get_prefix>
595setting of this Context for this object.
596
597=item B<set_prefix> = SCALAR [optional]
598
599With this parameter you can override the B<default_set_prefix>
600setting of this Context for this object.
601
602=item B<accessor> = CODEREF(object,attr[,value]) [optional]
603
604If B<accessor> is set this code reference is used as a generic
605accessor callback for all attributes. It handles getting and
606setting as well.
607
608Called with two arguments the passed attribute is to be read,
609with three arguments, the third argument is the value which
610is to be assigned to the attribute.
611
612This overrides B<attr_accessors_href> described beyond.
613
614=item B<attr_accessors_href> = HASHREF [optional]
615
616Often your application object attribute values doesn't fit the
617data type a particular Widget expects, e.g. in case of the
618Gtk2::Ex::FormFactory::List widget, which expects a two dimensional
619array for its content.
620
621Since you need this conversion only for a particular GUI task it
622makes sense to implement the conversion routine in the Context
623instead of adding such GUI specific methods to your underlying
624classes, which should be as much GUI independent as possible.
625
626That's why you can override arbitrary accessors (read and write)
627using the B<attr_accessors_href> parameter. Key is the name of method to
628be overriden and constant scalar value or a code reference, which
629is called instead of the real method.
630
631The code reference gets your application object as the first parameter,
632as usual for object methods, and additionally the new value in case of
633write access.
634
635A short example. Here we override the accessors B<get_tracks> and
636B<set_tracks> of an imagnary B<disc> object, which represents an
637audio CD. The track title is stored as a simple array and needs
638to be converted to a two dimensional array as expected by
639Gtk2::Ex::FormFactory::List. Additionally an constant accessor
640is defined for a Gtk2::Ex::FormFactory::Popup showing a bunch
641of music genres:
642
643  $context->add_object (
644    name => "disc",
645    attr_accessors_href => {
646      get_tracks => sub {
647        my $disc = shift;
648	#-- Convert the one dimensional array of disc
649	#-- tracks to the two dimensional array expected
650	#-- by Gtk2::Ex::FormFactory::List. Also the number
651	#-- of the track is added to the first column here
652	my @list;
653	my $nr = 1;
654	foreach my $track ( @{$disc->get_tracks} ) {
655	  push @list, [ $nr++, $track ];
656	}
657	return\@list;
658      },
659      set_tracks => sub {
660        my $disc = shift;
661	my ($list_ref) = @_;
662	#-- Convert the array back (the List is editable in
663	#-- our example, so values can be changed).
664	my @list;
665	foreach my $row ( @{$list_ref} ) {
666		push @list, $row->[1];
667	}
668	$disc->set_tracks(\@list);
669	return \@list;
670      },
671      genre_list => {
672        "rock" => "Rock",
673	"pop"  => "Pop",
674	"elec" => "Electronic",
675	"jazz" => "Jazz",
676      },
677    },
678  );
679
680
681=item B<attr_activity_href> = HASHREF [OPTIONAL]
682
683As mentioned earlier activity of Widgets is controlled by
684the Gtk2::Ex::FormFactory framework. E.g. if the an object
685becomes undef, all associated widgets render inactive.
686
687With the B<attr_activity_href> setting you can handle
688activity on attribute level, not only on object level.
689
690The key of the hash is the attribute name and value is
691a code reference, which returns TRUE or FALSE and control
692the activity this way.
693
694Again an example: imagine a text entry which usually is
695set with a default value controlled by your application.
696But if the user wants to override the entry he first has
697to press a correpondent checkbox to activate this.
698
699  $context->add_object (
700    name => "person",
701    attr_activity_href => {
702      ident_number => sub {
703        my $person = shift;
704	return $person->get_may_override_ident_number;
705      },
706    },
707    attr_depends_href => {
708      ident_number => "person.may_override_ident_number",
709    },
710  );
711
712For details about the B<attr_depends_href> option read on.
713
714=item B<attr_depends_href> = HASHREF [OPTIONAL]
715
716This hash defines dependencies between attributes. If you
717look at the example above you see why this is necessary.
718The B<ident_number> of a person may be overriden only if
719the B<may_override_ident_number> attribute of this person
720is set. Since this dependency is coded inside the code
721reference, Gtk2::Ex::FormFactory isn't aware of it until
722you add a corresponding B<attr_depends_href> entry.
723
724Now the GUI will automatically activate the Widget for
725the B<ident_number> attribute once B<may_override_ident_number>
726is set, e.g. by a CheckBox the user clicked.
727
728If an attribute depends on more than one other attributes
729you can use a list reference:
730
731  attr_depends_href => sub {
732      ident_number => [
733        "person.may_override_ident_number",
734	"foo.bar",
735      ],
736  },
737
738=item B<buffered> = BOOL [OPTIONAL]
739
740If set to TRUE this activates buffering for this object. Please
741refer to the BUFFERED CONTEXT OBJECTS chapter for details.
742
743=item B<changes_attr_filter> = REGEX [OPTIONAL]
744
745Gtk2::Ex::FormFactory maintains a flag indicating whether an
746object was changed. Under special circumstances you want
747specific attributes not affecting this "changed" state of
748an object. You can specify an regular expression here. Changes
749of attributes matching this expression won't touch the
750changes state of the object.
751
752To receive or change the object's changed state refer to
753the B<object_changed> attribute of Gtk2::Ex::FormFactory::Proxy.
754
755=back
756
757=item $context->B<remove_object> ( $name )
758
759Remove the object $name from this context.
760
761=item $app_object = $context->B<get_object> ( $name )
762
763This returns the application object registered as B<$name>
764to this context.
765
766=item $context->B<set_object> ( $name => $object )
767
768This sets a new object, which was registered as B<$name>
769to this context.
770
771=item $context->B<get_object_attr> ( "$object.$attr" )
772
773Retrieves the attribute named B<$attr> of the object B<$object>.
774
775=item $context->B<set_object_attr> ( "$object.$attr", $value )
776
777Set the attribute named B<$attr> of the object B<$object>
778to B<$value>. Dependent widgets update automatically.
779
780=item $context->B<update_object_attr_widgets> ( $object_name, $attr_name )
781
782Triggers updates on all GUI widgets which are associated with
783the attribute B<$attr_name> of the object registered as B<$object_name>
784to this context.
785
786You may omit B<$attr_name> and pass a fully qualified "object.attr"
787noted string as the first argument instead.
788
789=item $context->B<update_object_widgets> ( $object_name )
790
791Triggers updates on all GUI widgets which are associated with
792the object registered as B<$object_name> to this context.
793
794=item $context->B<update_object_widgets_activity> ( $object_name, $activity )
795
796This updates the activity state of all GUI widgets which are
797associated with the object registered as B<$object_name> to
798this context.
799
800B<$activity> is 'inactive' or 'active'.
801
802=item $proxy = $context->B<get_proxy> ( $object_name )
803
804This returns the Gtk2::Ex::FormFactory::Proxy instance which was
805created for the object registered as B<$name> to this context.
806With the proxy you can do updates on object attributes which
807trigger the correspondent GUI updates as well.
808
809=back
810
811=head1 BUFFERED CONTEXT OBJECTS
812
813This feature was introduced in version 0.58 and is marked experimental
814so please use with care.
815
816If you set B<buffered> => 1 when adding an object to the Context
817a buffering Proxy will be used for this object. That means that
818all GUI changes in a synchronized FormFactory dialog are buffered
819in the proxy object. Normally all changes are commited immediately
820to the object, which is Ok in many situations, but makes implementing
821a Cancel button difficult resp. you need to care about this yourself by
822using a copy of the object or something like that.
823
824A FormFactory gets "buffered" if B<all> its widgets are connected to
825a buffered object. In that case Gtk2::Ex::Form::DialogButtons show
826a Cancel button automatically, even in a synchronized dialog.
827
828=head2 What's this good for?
829
830If your FormFactory doesn't has the B<sync> flag set you get Cancel
831button as well, since no changes are applied to the objects until
832the user hit the Ok button. All changes are kept in the "GUI". But
833such a dialog lacks of all dynamic auto-widget-updating features,
834e.g. setting widgets inactive under specific conditions. For very
835simple dialogs this is Ok, but if you need these features you need
836the buffering facility as well.
837
838But take care when implementing your closures for widget activity
839checking: they must not use the original objects! They need to access
840the attributes through the Context, because your original object doesn't
841see any GUI changes until the FormFactory is applied! All changes
842are buffered in the Context. If you access your objects through
843the Context anything works as expected.
844
845Buffering is useful as well in other situations e.g. if you're
846accessing remote objects over a network (for example with Event::RPC)
847where a method call is somewhat expensive.
848
849=head1 AUTHORS
850
851 J�rn Reder <joern at zyn dot de>
852
853=head1 COPYRIGHT AND LICENSE
854
855Copyright 2004-2006 by J�rn Reder.
856
857This library is free software; you can redistribute it and/or modify
858it under the terms of the GNU Library General Public License as
859published by the Free Software Foundation; either version 2.1 of the
860License, or (at your option) any later version.
861
862This library is distributed in the hope that it will be useful, but
863WITHOUT ANY WARRANTY; without even the implied warranty of
864MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
865Library General Public License for more details.
866
867You should have received a copy of the GNU Library General Public
868License along with this library; if not, write to the Free Software
869Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307
870USA.
871
872=cut
873