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