Gtk2-Ex-FormFactory-0.67/0000755000175000017500000000000011620730434014074 5ustar joernjoernGtk2-Ex-FormFactory-0.67/LICENSE0000644000175000017500000004313107324302233015101 0ustar joernjoern GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. Gtk2-Ex-FormFactory-0.67/tutorial/0000755000175000017500000000000011620730434015737 5ustar joernjoernGtk2-Ex-FormFactory-0.67/tutorial/music.sql0000644000175000017500000000122110262234344017574 0ustar joernjoerncreate table artist ( id int not null auto_increment, name varchar(255) not null, notes mediumtext, primary key (id) ); create table genre ( id int not null auto_increment, name varchar(255) not null, primary key (id) ); create table album ( id int not null auto_increment, artist int not null, genre int, title varchar(255) not null, year int, notes mediumtext, primary key (id) ); create table song ( id int not null auto_increment, album int not null, title varchar(255) not null, nr int not null, primary key (id) ); Gtk2-Ex-FormFactory-0.67/tutorial/config.pm0000644000175000017500000000351710262236674017560 0ustar joernjoern# $Id: config.pm,v 1.1 2005/07/04 13:46:36 joern Exp $ package Music::Config; use strict; sub get_dbi_source { shift->{dbi_source} } sub get_dbi_username { shift->{dbi_username} } sub get_dbi_password { shift->{dbi_password} } sub get_dbi_test_message { shift->{dbi_test_message} } sub set_dbi_source { shift->{dbi_source} = $_[1] } sub set_dbi_username { shift->{dbi_username} = $_[1] } sub set_dbi_password { shift->{dbi_password} = $_[1] } sub set_dbi_test_message { shift->{dbi_test_message} = $_[1] } sub get_filename { "music.conf" } sub get_connection_data { my $self = shift; return ($self->get_dbi_source, $self->get_dbi_username, $self->get_dbi_password); } sub get_db_connection_ok { shift->{db_connection_ok} } sub set_db_connection_ok { shift->{db_connection_ok} = $_[1] } sub new { my $class = shift; my $filename = $class->get_filename; my $self; if ( -f $filename ) { $self = do $filename; } else { $self = bless { db_connection_ok => 0, dbi_source => "dbi:mysql:gtk2ff", dbi_username => "", dbi_password => "", }, $class; } $Music::Config::instance = $self; return $self; } sub test_db_connection { my $self = shift; require DBI; my $dbh = eval { DBI->connect($self->get_connection_data) }; my $ok = $dbh?1:0; $self->set_db_connection_ok($ok); $dbh->disconnect if $dbh; if ( $ok ) { require "model.pm" if $ok; $self->set_dbi_test_message("Connection Ok"); } else { $self->set_dbi_test_message("$DBI::errstr"); } return $ok; } sub save { my $self = shift; require Data::Dumper; require FileHandle; my $dd = Data::Dumper->new ( [$self], ['self'] ); $dd->Indent(1); my $data = $dd->Dump; my $file = $self->get_filename; my $fh = FileHandle->new; open ($fh, ">$file") or die "can't write $file"; print $fh $data; close $fh; 1; } 1; Gtk2-Ex-FormFactory-0.67/tutorial/import.pl0000755000175000017500000000411610264513004017606 0ustar joernjoern#!/usr/bin/perl $Id: import.pl,v 1.2 2005/07/11 15:53:40 joern Exp $ use strict; use MP3::Info; use Ogg::Vorbis::Header; use File::Find; use Encode; require "config.pm"; my $USAGE = <<__EOU; Usage: import_from_files.pl directory Description: Scans the given directory recursively for .mp3 and .ogg files, read their tags and adds corresponding entries to the music database of this Gtk2::Ex::FormFactory tutorial. __EOU main: { my $dir = shift @ARGV; if ( !$dir or @ARGV ) { print $USAGE; exit 1; } my $config = Music::Config->new; $config->test_db_connection; die "Start music.pl first, for database configuration" unless $config->get_db_connection_ok; scan_directory($dir); } sub scan_directory { my ($dir) = @_; my $genre = Music::Genre->find_or_create({ name => "Unknown" }); my (%artists, %albums); find ( sub { return unless /\.(ogg|mp3)$/i; my $filename = $File::Find::name; my ($artist, $album, $song, $nr); if ( $filename =~ /ogg$/i ) { my $header = Ogg::Vorbis::Header->new($filename) or return; $artist = ($header->comment('artist'))[0]; $album = ($header->comment('album'))[0]; $song = ($header->comment('title'))[0]; $nr = ($header->comment('tracknumber'))[0]; Encode::from_to($_,"utf8","latin1") for ($artist, $album, $song); } else { my $tag = get_mp3tag($filename) or return; $artist = $tag->{ARTIST}; $album = $tag->{ALBUM}; $song = $tag->{TITLE}; $nr = $tag->{TRACKNUM}; } print "Found: $artist / $album / $nr - $song\n"; my $artist_obj = $artists{$artist} || Music::Artist->find_or_create({ name => $artist }); $artists{$artist} ||= $artist_obj; my $album_obj = $albums{"$artist:$album"} || (Music::Album->search ( { artist => $artist_obj, title => $album } ))[0] || $artist_obj->add_to_albums({ title => $album, genre => $genre, }); $albums{"$artist:$album"} ||= $album_obj; my $song_obj = Music::Song->search ( { album => $album_obj, title => $song } ) || $album_obj->add_to_songs ({ nr => $nr, title => $song, }); }, $dir ); } Gtk2-Ex-FormFactory-0.67/tutorial/music.pl0000755000175000017500000005725410273132153017431 0ustar joernjoern#!/usr/bin/perl # $Id: music.pl,v 1.8 2005/07/31 11:22:19 joern Exp $ use strict; use lib '../lib'; use Gtk2::Ex::FormFactory; require "config.pm"; main: { Gtk2->init; Music::GUI->new->build_main_window; Gtk2->main; } package Music::GUI; sub get_config { shift->{config} } sub get_context { shift->{context} } sub get_form_factory { shift->{form_factory} } sub get_config_form_factory { shift->{config_form_factory} } sub get_selected_genre_id { shift->{selected_genre_id} } sub get_selected_artist_id { shift->{selected_artist_id} } sub get_selected_album_id { shift->{selected_album_id} } sub get_selected_song_id { shift->{selected_song_id} } sub set_config { shift->{config} = $_[1] } sub set_context { shift->{context} = $_[1] } sub set_form_factory { shift->{form_factory} = $_[1] } sub set_config_form_factory { shift->{config_form_factory} = $_[1] } sub set_selected_genre_id { shift->{selected_genre_id} = $_[1] } sub set_selected_artist_id { my $self = shift; my ($id) = @_; $self->{selected_artist_id} = $id; $self->set_selected_album_id(undef); $id; } sub set_selected_album_id { my $self = shift; my ($id) = @_; $self->{selected_album_id} = $id; $self->set_selected_song_id(undef); $id; } sub set_selected_song_id { shift->{selected_song_id} = $_[1] } sub new { my $class = shift; my $config = Music::Config->new; return bless { config => $config }, $class; } sub create_context { my $self = shift; my $config = $self->get_config; $config->test_db_connection; my $context = Gtk2::Ex::FormFactory::Context->new; $context->add_object ( name => "config", object => $config, attr_accessors_href => { get_dbi_source_presets => [ "dbi:mysql:gtk2ff" ], }, ); $context->add_object ( name => "db", object => ($config->get_db_connection_ok ? 1 : undef), ); $context->add_object ( name => "gui", object => $self, attr_accessors_href => { get_artists => sub { my $self = shift; return if !$self->get_config->get_db_connection_ok; my @artist_data; my $iter = Music::Artist->search_like ( name => '%', { order_by => 'name' }); while ( my $artist = $iter->next ) { push @artist_data, [ $artist->get_id, $artist->get_name ], } return \@artist_data; }, get_genre_list => sub { return if !$self->get_config->get_db_connection_ok; my @genre_data; my $iter = Music::Genre->search_like ( name => '%', { order_by => 'name' }); while ( my $genre = $iter->next ) { push @genre_data, [ $genre->get_id, $genre->get_name ], } return \@genre_data; }, set_genre_list => sub { my ($db, $data) = @_; my $list = $self->get_config_form_factory->lookup_widget("genre_list"); my $genre = $context->get_object("genre"); return if not $genre; my $genre_id = $genre->get_id; my $row = 0; ++$row while $data->[$row][0] != $genre_id; $genre->set_name($data->[$row][1]); 1; }, get_selected_artist => sub { my $artist_id = $self->get_selected_artist_id ? $self->get_selected_artist_id->[0] : return; Music::Artist->retrieve($artist_id); }, get_selected_genre => sub { my $genre_id = $self->get_selected_genre_id ? $self->get_selected_genre_id->[0] : return; Music::Genre->retrieve($genre_id); }, }, attr_depends_href => { selected_genre => "gui.selected_genre_id", selected_artist => "gui.selected_artist_id", }, ); $context->add_object ( name => "genre", aggregated_by => "gui.selected_genre", ); $context->add_object ( name => "artist", aggregated_by => "gui.selected_artist", attr_accessors_href => { get_albums => sub { my $self = shift; my @album_data; my $iter = $self->albums; while ( my $album = $iter->next ) { push @album_data, [ $album->get_id, $album->get_title ], } return \@album_data; }, get_selected_album => sub { my $album_id = $self->get_selected_album_id ? $self->get_selected_album_id->[0] : return; Music::Album->retrieve($album_id); }, }, attr_depends_href => { selected_album => "gui.selected_album_id", }, ); $context->add_object ( name => "album", aggregated_by => "artist.selected_album", attr_accessors_href => { get_songs => sub { my $self = shift; my @song_data; my $iter = $self->songs; while ( my $song = $iter->next ) { push @song_data, [ $song->get_id, $song->get_nr, $song->get_title ], } return \@song_data; }, get_selected_song => sub { my $song_id = $self->get_selected_song_id ? $self->get_selected_song_id->[0] : return; Music::Song->retrieve($song_id); }, get_genre_list => sub { $context->get_proxy("gui")->get_attr("genre_list") }, }, attr_depends_href => { selected_song => "gui.selected_song_id", }, ); $context->add_object ( name => "song", aggregated_by => "album.selected_song", ); return $self->set_context($context); } sub build_main_window { my $self = shift; my $context = $self->create_context; my $ff = Gtk2::Ex::FormFactory->new ( context => $context, sync => 1, content => [ Gtk2::Ex::FormFactory::Window->new ( title => "Music database - Gtk2::Ex::FormFactory example", properties => { default_width => 640, default_height => 640, }, quit_on_close => 1, content => [ $self->build_menu, Gtk2::Ex::FormFactory::Table->new ( expand => 1, layout => " +----------------------------------------------------+ | Buttons | +----------------------------------------------------+ | Sep | +----------------+>----------------------------------+ ^ Artist list | Artist Form | | +-----------------------------------+ | | Album List | | +-----------------------------------+ | | Album Form | | +-----------------------------------+ | ^ Song List | | +-----------------------------------+ | | Song Form | +----------------+-----------------------------------+ ", content => [ $self->build_buttons, Gtk2::Ex::FormFactory::HSeparator->new(), $self->build_artist_list, $self->build_artist_form, $self->build_album_list, $self->build_album_form, $self->build_song_list, $self->build_song_form, ], ), ], ), ], ); $ff->open; $ff->update; $self->set_form_factory($ff); if ( !$self->get_config->get_db_connection_ok ) { $self->open_preferences; } 1; } sub build_menu { my $self = shift; return Gtk2::Ex::FormFactory::Menu->new ( menu_tree => [ "_File" => { item_type => '', children => [ "_Exit" => { item_type => '', extra_data => 'gtk-quit', callback => sub { $self->get_form_factory->close; Gtk2->main_quit; }, accelerator => 'q', }, ], }, "_Edit" => { item_type => '', children => [ "_Preferences" => { item_type => '', extra_data => 'gtk-preferences', callback => sub { $self->open_preferences }, accelerator => 'p', }, "sep1" => { item_type => '', }, "Add _artist" => { object => "db", callback => sub { $self->add_artist }, accelerator => 'a', }, "Ad_d album" => { object => "artist", callback => sub { $self->add_album }, accelerator => 'd', }, "Add _song" => { object => "album", callback => sub { $self->add_song }, accelerator => 's', }, "sep2" => { item_type => '', }, "_Delete selected artist" => { object => "artist", callback => sub { $self->delete_artist }, accelerator => 'a', }, "D_elete selected album" => { object => "album", callback => sub { $self->delete_album }, accelerator => 'd', }, "De_lete selected song" => { object => "song", callback => sub { $self->delete_song }, accelerator => 's', }, ], }, ], ); } sub build_artist_list { my $self = shift; return Gtk2::Ex::FormFactory::VBox->new ( object => "db", title => "Select an artist", content => [ Gtk2::Ex::FormFactory::List->new ( name => "artist_list", expand => 1, attr => "gui.artists", attr_select => "gui.selected_artist_id", attr_select_column => 0, scrollbars => [ "never", "automatic" ], columns => [ "id", "Artists" ], visible => [ 0, 1 ], selection_mode => "single", no_header => 1, changed_hook_after => sub { $self->get_form_factory ->lookup_widget("album_list") ->get_gtk_widget->select(0); }, ), ], ); } sub build_artist_form { my $self = shift; return Gtk2::Ex::FormFactory::Form->new ( object => "artist", title => "Edit selected artist", content => [ Gtk2::Ex::FormFactory::Entry->new ( label => "Name", attr => "artist.name", changed_hook_after => sub { my ($artist) = @_; my $list = $self->get_form_factory->lookup_widget("artist_list"); $list->get_data->[$list->get_selected_rows->[0]]->[1] = $artist->get_name; }, ), Gtk2::Ex::FormFactory::TextView->new ( label => "Notes", attr => "artist.notes", ), ], ); } sub build_album_list { my $self = shift; return Gtk2::Ex::FormFactory::VBox->new ( object => "artist", title => "Select an album", content => [ Gtk2::Ex::FormFactory::List->new ( name => "album_list", expand => 1, attr => "artist.albums", attr_select => "gui.selected_album_id", attr_select_column => 0, scrollbars => [ "never", "automatic" ], columns => [ "id", "Albums" ], visible => [ 0, 1 ], selection_mode => "single", no_header => 1, height => 80, changed_hook_after => sub { $self->get_form_factory ->lookup_widget("song_list") ->get_gtk_widget->select(0); }, ), ], ); } sub build_album_form { my $self = shift; return Gtk2::Ex::FormFactory::Form->new ( object => "album", title => "Edit selected album", content => [ Gtk2::Ex::FormFactory::Entry->new ( label => "Title", attr => "album.title", changed_hook_after => sub { my ($album) = @_; my $list = $self->get_form_factory->lookup_widget("album_list"); $list->get_data->[$list->get_selected_rows->[0]]->[1] = $album->get_title; }, ), Gtk2::Ex::FormFactory::Entry->new ( label => "Year", attr => "album.year", width => 60, expand_h => 0, ), Gtk2::Ex::FormFactory::Popup->new ( attr => "album.genre", label => "Genre", width => 150, expand_h => 0, ), Gtk2::Ex::FormFactory::TextView->new ( label => "Notes", attr => "album.notes", ), ], ); return Gtk2::Ex::FormFactory::Label->new ( label => "build_album_form" ); } sub build_song_list { my $self = shift; return Gtk2::Ex::FormFactory::VBox->new ( object => "album", title => "Select a song", content => [ Gtk2::Ex::FormFactory::List->new ( name => "song_list", expand => 1, attr => "album.songs", attr_select => "gui.selected_song_id", attr_select_column => 0, scrollbars => [ "never", "automatic" ], columns => [ "id", "Nr", "Title" ], visible => [ 0, 1 ], selection_mode => "single", no_header => 1, ) ], ); } sub build_song_form { my $self = shift; return Gtk2::Ex::FormFactory::Form->new ( object => "song", title => "Edit selected song", content => [ Gtk2::Ex::FormFactory::Entry->new ( label => "Title", attr => "song.title", changed_hook_after => sub { my ($song) = @_; my $list = $self->get_form_factory->lookup_widget("song_list"); $list->get_data->[$list->get_selected_rows->[0]]->[2] = $song->get_title; }, ), ], ); } sub build_buttons { my $self = shift; return Gtk2::Ex::FormFactory::HBox->new ( content => [ Gtk2::Ex::FormFactory::Button->new ( label => "Add artist", object => "db", clicked_hook => sub { $self->add_artist }, ), Gtk2::Ex::FormFactory::Button->new ( label => "Add album", object => "artist", clicked_hook => sub { $self->add_album }, ), Gtk2::Ex::FormFactory::Button->new ( label => "Add song", object => "album", clicked_hook => sub { $self->add_song }, ), Gtk2::Ex::FormFactory::Button->new ( label => "Delete artist", object => "artist", clicked_hook => sub { $self->delete_artist }, ), Gtk2::Ex::FormFactory::Button->new ( label => "Delete album", object => "album", clicked_hook => sub { $self->delete_album }, ), Gtk2::Ex::FormFactory::Button->new ( label => "Delete song", object => "song", clicked_hook => sub { $self->delete_song }, ), ], ); } sub add_artist { my $self = shift; my $artist = Music::Artist->create ({ name => "Unnamed" }); $self->get_context->update_object_widgets("gui"); my $list = $self->get_form_factory->lookup_widget("artist_list"); $list->select_row_by_attr($artist->get_id); 1; } sub add_album { my $self = shift; my $artist = $self->get_context->get_object("artist"); my $album = $artist->add_to_albums ({ title => "Unnamed" }); $self->get_context->update_object_widgets("artist"); my $list = $self->get_form_factory->lookup_widget("album_list"); $list->select_row_by_attr($album->get_id); 1; } sub add_song { my $self = shift; my $album = $self->get_context->get_object("album"); my @songs = $album->songs; my $nr = @songs ? $songs[-1]->get_nr + 1 : 1; my $song = $album->add_to_songs ({ title => "Unnamed", nr => $nr }); $self->get_context->update_object_widgets("album"); my $list = $self->get_form_factory->lookup_widget("song_list"); $list->select_row_by_attr($song->get_id); 1; } sub add_genre { my $self = shift; my $genre = Music::Genre->create({ name => "Unnamed" }); $self->get_context->update_object_attr_widgets("gui.genre_list"); my $list = $self->get_config_form_factory->lookup_widget("genre_list"); $list->select_row_by_attr($genre->get_id); 1; } sub delete_artist { my $self = shift; my $context = $self->get_context; my $artist = $context->get_object("artist"); $artist->delete; $context->set_object_attr("gui.selected_artist_id", undef); $context->update_object_widgets("gui"); 1; } sub delete_album { my $self = shift; my $context = $self->get_context; my $album = $context->get_object("album"); $album->delete; $context->set_object_attr("gui.selected_album_id", undef); $context->update_object_widgets("artist"); 1; } sub delete_song { my $self = shift; my $context = $self->get_context; my $song = $context->get_object("song"); $song->delete; $context->set_object_attr("gui.selected_song_id", undef); $context->update_object_widgets("album"); 1; } sub delete_genre { my $self = shift; my $context = $self->get_context; my $genre = $context->get_object("genre"); $genre->delete; $context->set_object_attr("gui.selected_genre_id", undef); $self->get_context->update_object_attr_widgets("gui.genre_list"); 1; } sub open_preferences { my $self = shift; my $config = $self->get_config; my $context = $self->get_context; my $pref_ff = Gtk2::Ex::FormFactory->new ( parent_ff => $self->get_form_factory, sync => 1, context => $context, content => [ Gtk2::Ex::FormFactory::Window->new ( title => "Music database: Preferences", properties => { modal => 1, default_width => 350, default_height => 350, }, content => [ Gtk2::Ex::FormFactory::Form->new ( title => "Database settings", content => [ Gtk2::Ex::FormFactory::Combo->new ( attr => "config.dbi_source", label => "DBI source", ), Gtk2::Ex::FormFactory::Entry->new ( attr => "config.dbi_username", label => "Username", ), Gtk2::Ex::FormFactory::Entry->new ( attr => "config.dbi_password", label => "Password", properties => { visibility => 0 }, ), Gtk2::Ex::FormFactory::HBox->new ( content => [ Gtk2::Ex::FormFactory::Button->new ( label => "Test settings", expand_h => 0, clicked_hook => sub { $self->test_db_connection; }, ), Gtk2::Ex::FormFactory::Button->new ( label => "Create database", expand_h => 0, clicked_hook => sub { $self->create_database; }, ), Gtk2::Ex::FormFactory::Button->new ( object => "db", label => "Fill database", tip => "Inserts some example entries", expand_h => 0, clicked_hook => sub { $self->fill_database; }, ), ], ), Gtk2::Ex::FormFactory::HSeparator->new, Gtk2::Ex::FormFactory::Label->new ( label => "Message", attr => "config.dbi_test_message", with_markup => 1, ), ], ), Gtk2::Ex::FormFactory::VBox->new ( object => "db", title => "Manage genres", expand => 1, content => [ Gtk2::Ex::FormFactory::List->new ( name => "genre_list", attr => "gui.genre_list", attr_select => "gui.selected_genre_id", attr_select_column => 0, expand => 1, scrollbars => [ "never", "automatic" ], columns => [ "id", "Name" ], visible => [ 0, 1, ], editable => [ 0, 1, ], selection_mode => 'single', no_header => 1, tip => "List is editable, change names here", ), Gtk2::Ex::FormFactory::HBox->new ( content => [ Gtk2::Ex::FormFactory::Button->new ( object => "db", label => "Add", clicked_hook => sub { $self->add_genre }, ), Gtk2::Ex::FormFactory::Button->new ( object => "genre", label => "Delete", clicked_hook => sub { $self->delete_genre }, ), ], ), ], ), Gtk2::Ex::FormFactory::DialogButtons->new ( clicked_hook_after => sub { $self->test_db_connection; $config->save; $context->update_object_attr_widgets("album.genre"); }, ), ], ), ], ); $self->set_config_form_factory($pref_ff); $pref_ff->open; $pref_ff->update; 1; } sub test_db_connection { my $self = shift; my $config = $self->get_config, my $context = $self->get_context; $config->test_db_connection; if ( $config->get_db_connection_ok ) { $context->set_object( db => 1 ); $self->get_form_factory->update_all; $self->get_config_form_factory->update_all; } else { $context->set_object( db => undef ); $context->set_object( artist => undef ); $context->set_object( album => undef ); $context->set_object( song => undef ); $context->update_object_widgets("gui"); } $self->get_context->update_object_attr_widgets( "config.dbi_test_message", ); 1; } sub create_database { my $self = shift; my @connection_data = $self->get_config->get_connection_data; my ($db_name) = $connection_data[0] =~ /^dbi:mysql:([^;]*)/; $db_name ||= "gtk2ff"; $connection_data[0] = "dbi:mysql:"; eval { my $dbh = DBI->connect( @connection_data, { RaiseError => 1, PrintError => 0 } ); open (SQL, "music.sql") or die "can't read music.sql"; $dbh->do("create database $db_name"); $dbh->do("use $db_name"); my $command; while () { $command .= $_; if ( $command =~ /;\s*$/ ) { $dbh->do($command); $command = ""; } } close SQL; }; if ( $@ ) { my $msg = $@; $msg =~ s/&/&/; $msg =~ s/get_config_form_factory->open_message_window ( message => $msg, type => "warning", ); } else { $self->get_config_form_factory->open_message_window ( message => "Database successfully created", ); $self->test_db_connection; } 1; } sub fill_database { my $self = shift; Music::Genre->find_or_create({ name => "Unknown" }); Music::Genre->find_or_create({ name => "Electronic" }); Music::Genre->find_or_create({ name => "Rock" }); Music::Genre->find_or_create({ name => "Pop" }); Music::Genre->find_or_create({ name => "Jazz" }); Music::Genre->find_or_create({ name => "Instrumental" }); my $genre = Music::Genre->find_or_create({ name => "NuJazz" }); Music::Artist->find_or_create({ name => 'Mike Oldfield' }); Music::Artist->find_or_create({ name => 'Beanfield' }); my $bugge = Music::Artist->find_or_create({ name => 'Bugge Wesseltoft' }); if ( ! Music::Album->search ( { artist => $bugge->get_id, title => 'Moving' } ) ) { my $moving = $bugge->add_to_albums({ title => 'Moving', year => '2001', genre => $genre, }); $moving->add_to_songs({ nr => 1, title => 'Change' }); $moving->add_to_songs({ nr => 2, title => 'Gare Du Nord' }); $moving->add_to_songs({ nr => 3, title => 'Yellow Is The Colour' }); $moving->add_to_songs({ nr => 4, title => 'Lone' }); $moving->add_to_songs({ nr => 5, title => 'Moving' }); $moving->add_to_songs({ nr => 6, title => 'South' }); } my $nils = Music::Artist->find_or_create({ name => 'Nils Petter Molvaer' }); if ( ! Music::Album->search ( { artist => $nils->get_id, title => 'NP3' } ) ) { my $np3 = $nils->add_to_albums ({ title => 'NP3', year => '2002', genre => $genre, }); $np3->add_to_songs({ nr => 1, title => 'Tabula Rasa' }); $np3->add_to_songs({ nr => 2, title => 'Axis Of Ignorance' }); $np3->add_to_songs({ nr => 3, title => 'Hurry Slowly' }); $np3->add_to_songs({ nr => 4, title => 'Marrow' }); $np3->add_to_songs({ nr => 5, title => 'Frozen' }); $np3->add_to_songs({ nr => 6, title => 'Presence' }); $np3->add_to_songs({ nr => 7, title => 'Simply So' }); $np3->add_to_songs({ nr => 8, title => 'Little Indian' }); $np3->add_to_songs({ nr => 9, title => 'Nebulizer' }); } $self->get_form_factory->update_all; $self->get_config_form_factory->update_all; 1; } 1; Gtk2-Ex-FormFactory-0.67/tutorial/model.pm0000644000175000017500000000205510262253502017374 0ustar joernjoern# $Id: model.pm,v 1.2 2005/07/04 15:35:30 joern Exp $ package Music::DBI; use base 'Class::DBI'; Music::DBI->connection($Music::Config::instance->get_connection_data); sub accessor_name { "get_$_[1]" } sub mutator_name { "set_$_[1]" } sub autoupdate { 1 } package Music::Artist; use base 'Music::DBI'; Music::Artist->table('artist'); Music::Artist->columns(All => qw/id name notes/); Music::Artist->has_many(albums => 'Music::Album', { order_by => 'title' } ); package Music::Genre; use base 'Music::DBI'; Music::Genre->table('genre'); Music::Genre->columns(All => qw/id name/); package Music::Album; use base 'Music::DBI'; Music::Album->table('album'); Music::Album->columns(All => qw/id artist genre title year notes/); Music::Album->has_a(artist => 'Music::Artist'); Music::Album->has_a(genre => 'Music::Genre'); Music::Album->has_many(songs => 'Music::Song', { order_by => 'nr' } ); package Music::Song; use base 'Music::DBI'; Music::Song->table('song'); Music::Song->columns(All => qw/id album title nr/); Music::Song->has_a(album => 'Music::Album'); 1; Gtk2-Ex-FormFactory-0.67/tutorial/README0000644000175000017500000000526410264513420016623 0ustar joernjoern$Id: README,v 1.7 2005/07/11 15:58:08 joern Exp $ Gtk2::Ex::FormFactory tutorial program ====================================== The tutorial isn't written yet, but the corrsponding example program is ;) You need the following bits in order to get the program to work: * A running MySQL database server and a user with "create database" privileges * A bunch of Perl modules: - Gtk2 and Gtk2::Ex::FormFactory (of course ;) - DBI, DBD::mysql and Class::DBI - Optionally: MP3::Info, Ogg::Vorbis::Header for the import.pl program Just change into the "tutorial" subdirectory and start the music.pl program: % cd tutorial % perl music.pl On first startup the program checks for a valid database connection - without success. This triggers a preferences dialog asking for the MySQL connection settings. Enter username and password here and hit the "Test settings button". It should report a missing "gtk2ff" database, but no "Access denied message" or something like this. Check username and password you entered if that happens. Then hit the "Create database" button. This will create the "gtk2ff" database and tables by executing the SQL commands listed in the file "music.sql". You should get a success dialog, or an error dialog with a detailed error message. If the database was created successfully you may hit the "Fill database" button. This will add some example entries, so you have something to play with and don't need to bootstrap with your own data. Alternatively you may use the import.pl command line program to import the tags of an existent mp3/ogg collection into the database. Now close the preferences dialog and play around with the application. Create, modify and delete artists, albums and songs. The GUI has always a consistent state (e.g. the "Add album" button is greyed out until you selected an artist). If you find bugs please report, I tried hard avoiding them ;) Overview of the files ===================== README The document you're reading music.pl GUI program using Gtk2::Ex::FormFactory music.sql MySQL DDL script to create the neccessary tables model.pm Data model implementation using Class::DBI music.conf Created by music.pl; contains configuration data config.pm Tiny module managing the database configuration data import.pl Script to populate the database from ogg/mp3 tags The program has no documentation yet, but is a good example of how less code is needed to build complex GUI's with Gtk2::Ex::FormFactory (about 1000 lines, including code for test data generation) and of course Class::DBI, which made implementing the whole data model in about 30 lines possible ;). Have fun. Jrn Reder Gtk2-Ex-FormFactory-0.67/META.yml0000644000175000017500000000104711620730434015347 0ustar joernjoern--- #YAML:1.0 name: Gtk2-Ex-FormFactory version: 0.67 abstract: ~ author: [] license: unknown distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 requires: Gtk2: 0 Gtk2::SimpleList: 0 Gtk2::SimpleMenu: 0 no_index: directory: - t - inc generated_by: ExtUtils::MakeMaker version 6.55_02 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 Gtk2-Ex-FormFactory-0.67/Changes0000644000175000017500000003544711620730431015401 0ustar joernjoern$Id: Changes,v 1.30 2011-08-11 10:38:17 joern Exp $ Revision history and release notes for Gtk2::Ex::FormFactory: 0.67 Thu Aug 11, 2011, joern Features: - Added parse_tags option to TextView. This way you can easily create tagged text by using a simple HTML based markup syntax: Tagged text. - Added HPaned widget 0.66 Sun Nov 11, 2009, joern Features: - image attribute for Button and ToggleButton; set a filename here to put an arbitrary image on a button Bugfixes: - Applied patches from Jeff Hallock fixing a bunch of "Use of uninitialized value" warnings. As well Jeff fixed a bug which renders empty "getter" prefixes unusable. - build() Method now prevents building a Factory twice by itself. - Container->add_child_widget() missed registering children to the Form Factory. - Widget activity tracking called active check callback without an object reference if no object was currently set instead of rendering the widget inactive in that case. 0.65 Sun Jul 2, 2006, joern Bugfixes: - Don't use for() loops with global $_ iterator, which interacts badly with application method calls which modify $_ as well 0.64 Sat Jun 17, 2006, joern Features: - Gtk2::Ex::FormFactory - Added options 'yes_label' and 'no_label' to open_confirm_window(). - Gtk2::Ex::FormFactory::Button - Added 'with_repeat' option, which triggers the callback continuously as long button is pressed. Bugfixes: - Gtk2::Ex::FormFactory::Label: - Set initial text only if the widget object has no associated object attribute. - Gtk2::Ex::FormFactory::ToggleButton: - Set button label only if no stock icon is given. 0.63 Sun Apr 23, 2006, joern Bugfixes: - Gtk2::Ex::FormFactory::Notebook: - fixed an exception which occured if no object is associated with the Notebook - Gtk2::Ex::FormFactory::Layout - fixed wrong set_gtk_widget/set_gtk_parent_widget for Gtk2::Ex::FormFactory::GtkWidget objects 0.62 Sun Apr 9, 2006, joern Bugfixes: - Associate tab label widgets with the page content widget, so if the page gets inactive the label greys out as well. - Notebook: show page before switching to it, otherwise page-switching is ignored. This was workarounded until now by a delayed page-switching, which didn't work with recent gtk2 versions anymore, so the true fix for this issue was overdue ;) Thanks to Alex Petkov for the detailed bug report and debugging info. 0.61 Sun Apr 2, 2006, joern Features: - You can check whether an application object was changed with the GUI using the new Proxy's 'object_changed' attribute. Additionally you can control which attributes shouldn't affect the object's changed state with a regex passed as 'changes_attr_filter' when adding the object to the Context. - You can add icons to Notebook tab titles by prefixing the title with the stock item named in square brackets. Bugfix: - Prevent double object update triggering when switching Notebook tabs and Radiobuttons. 0.60 Mon Mar 27, 2006, joern Features: - Gtk2::Ex::FormFactory::Menu - Add 'active_cond' and 'active_depends' keys to the menu item definition to control widget activity not just on a per object basis. - Gtk2::Ex::FormFactory::Widget: - active_cond may return not just a boolean but one of the strings 'insensitive', 'invisible', 'sensitive', 'visible' to get the corresponding state. - Gtk2::Ex::FormFactory::ExecFlow: - New widget for displaying Event::ExecFlow job plans - Gtk2::Ex::FormFactory::VPaned: - New container widget for Gtk2::VPaned - Gtk2::Ex::FormFactory::ToggleButton: - Stock image support added - Gtk2::Ex::FormFactory::Context - Some internal cleanup to reduce memory footprint Bugfixes: - Gtk2::Ex::FormFactory::Menu - On cleanup deregistering of objects the menu depends on was not complete. - Gtk2::Ex::FormFactory::Context - Widgets with active_depends => "object.attr" (not just an object) weren't updated if the object changed. 0.59 Wed Dec 28, 2005, joern Features: - Default layout implementaion for width/height now uses set_size_request() only for non-window widgets and set_default_size() for windows, which is better in general. Bugfixes: - Small layout issue of buttons with stock image and a defined but empty label fixed. 0.58 Sun Oct 9, 2005, joern Features: - Gtk2::Ex::FormFactory::Container: - new methods add_child_widget() and remove_child_widget() to add/remove Widgets at runtime. E.g. you can extend a notebook at runtime. Thanks to Kaare Rasmussen for the suggestions. - Buffered context objects: EXPERIMENTAL! - if you specify 'buffered => 1' on Context->add_object a buffering Proxy will be used for this object. That means all attribute updates are buffered in the proxy object instead of passing them through to the underlying application object immediately. This way you can use a synchronized FormFactory (with all the advantages of automatically widget updating/activating stuff) but the possibility to cancel all changes easily. DialogButtons take care of this, so a "Cancel" button is displayed in a FormFactory automatically if all widgets in this factory are connected to a buffered context object. Tested with simpler forms and works for me, but this may not work in very complex forms with a lot of internal dependencies, in particular if your callbacks for activation checking and stuff don't use the Context but access the original object directly, which doesn't see any changes until the FormFactory is applied! - Gtk2::Ex::FormFactory::List: prevent unnecessary selection updates not only for lists with the attr_select_column property set - Gtk2::Ex::FormFactory::Widget: active_cond callback now gets the current application object passed. Bugfixes: - Gtk2::Ex::FormFactory::Context: active_depends property didn't work with an array ref of attributes as stated in the docs. - Gtk2::Ex::FormFactory::Label: don't try to update from an object atribute without any set. 0.57 Mon Aug 1, 2005, joern Features: - Gtk2::Ex::FormFactory::Window: - on window close FormFactory->close is called automatically if the Window is the 1st child of the FormFactory, unless a closed_hook is set. Thanks to Kaare Rasmussen for the hint. - added 'quit_on_close' option, which quits the Gtk2 main loop as well in that case. - Gtk2::Ex::FormFactory::List: - scroll to selected row only on first show, to prevent unneccessary scrolling. - trigger selection dependency updates only when the selection really changed (but only for Lists with attr_select and attr_select_column set, because otherwise a selection change can't be detected safely) - Documentation updates & fixes Bugfixes: - Gtk2::Ex::FormFactory::Layout: setting 'scrollbars' for non-scrollable widgets didn't work - Gtk2::Ex::FormFactory::Notebook: restore selected page only if an object attribute is associated with the Widget. - Gtk2::Ex::FormFactory: POD typo fixed, thanks for the report to Christian Marillat. 0.56 Wed Jul 20, 2005, joern Features: - Gtk2::Ex::FormFactory::Rules: - added 'zero', 'non-zero', 'positive-zero-integer', 'negative-zero-integer', 'positive-zero-float', 'negative-zero-float' rules. - added special 'or-empty' rule, refer to the man page for details. - added 'executable-command' rule. - Gtk2::Ex::FormFactory::Context: - added get_object_attr() helper method to retrieve a specific application object attribute - added 'accessor' option to the ->add_object() method - Gtk2::Ex::FormFactory::Widget: - added 'label_for' attribute, so auto-generated labels can be bound to specific widgets as well. - added 'avtive_cond' and 'active_depends' attributes for direct activity control of the widget. - added 'changed_hook_after' - Gtk2::Ex::FormFactory::Popup: - added 'items' attribute for static popup content - Gtk2::Ex::FormFactory::Popup: - default 'scale' to 1 of no other scaling options are given, images would be invisible without this. - Gtk2::Ex::FormFactory: - added 'with_cancel' option to the ->open_confirm_window() helper method - Gtk2::Ex::FormFactory::CheckButtonGroup: - dimensions can be configured through application object attributes. Row and/or column labels may be added. - Gtk2::Ex::FormFactory::Label: - added bold option. - Gtk2::Ex::FormFactory::Layout: - center button icons/labels. Bugfixes: - Gtk2::Ex::FormFactory::Combo: rule checking didn't work. - Gtk2::Ex::FormFactory::Widget: rule checking in synchronized FormFactories didn't work if no object attribute was associated with the widget - Gtk2::Ex::FormFactory::Table: due to missing outer whitespace truncation parsing of some table layouts was messed up. - Gtk2::Ex::FormFactory::List: dropped "selects_object" feature introduced in 0.55 because this can be easily done by defining attribute dependencies. - Gtk2::Ex::FormFactory::Layout: setting a label group didn't work for Label widgets, only for implicite labels set through the Widget's label attribute. - Gtk2::Ex::FormFactory::Loader: VSeparator setup was missing. 0.55 Mon Jul 11, 2005, joern Features: - Added a fully functional tutorial program (a simple music database) which makes use of a lot of simple and advanced Gtk2::Ex::FormFactory features. Not documented yet, but nevertheless a good example to learn how Gtk2::Ex::FormFactory works. More details in tutorial/README. - On demand loading of widget modules saves some memory - Gtk2::Ex::FormFactory::Context - pass object aggregation information to define dependencies between objects. This way all dependent objects update automatically if their parent object changes. - defining constant attribute accessors is now possible, e.g. for simple Combo presets not changing at runtime. - new method set_object_attr() sets an specific application object attribute and triggers corresponding GUI updates - Gtk2::Ex::FormFactory::List - new attribute "selects_object" for lists representing an index of objects to select one of them. The corresponding widgets are updated automatically once the list selection changes. - new attribute "visible" to control visibility of columns - new attribute "no_header" to suppress list headers - new attribute "attr_select_column" to have specific column values instead of selected indices in the "attr_select" attribute - Gtk2::Ex::FormFactory::Label: declare an arbitrary Label widget as the label of another widget, so this label is deactivated/activated together with the assigned widget automatically, even in complex layouts. - Gtk2::Ex::FormFactory::Popup: additional data model, two dimensional array to define ordered key/value pairs. - Gtk2::Ex::FormFactory::Window: title may be controlled by an application object. - Gtk2::Ex::FormFactory::Widget: moved get_widget() from Gtk2::Ex::FormFactory to here, so it's available for all widgets, not only in a FormFactory. Bugfixes: - Removed ok_hook() from Gtk2::Ex::FormFactory::Intro documentation, which was never implemented that way. - Tooltips on combo entries didn't work. 0.54 Sun May 29, 2005, joern Features: - Gtk2::Ex::FormFactory::Table now handles arbitrary alignments of widgets in the cells. - Gtk2::Ex::FormFactory: new convenience method change_mouse_cursor(). - Gtk2::Ex::FormFactory::Layout: no frames around forms if placed on a notebook page 0.53 Sun Apr 10, 2005, joern Features: - $ff->get_widget($name) added. - $context->remove_object($name) added. - DialogButtons: control the set of buttons displayed. Bugfixes: - Timestamp: placeholders were mixed up, so the calculated timestamp was nonsense for some values. - Widget's changed_hook wasn't called at the appropriate time for some widgets. 0.52 Sun Feb 06, 2005, joern Bugfixes: - Popup: didn't consider custom attribute activity conditions in all cases. - Container: apply_changes() didn't recurse correctly into all children Features: - New widgets: - CheckButtonGroup: a table of check buttons representing a multiple selection of predefined values. - GtkWidget: support arbitrary Gtk widgets which aren't worth an own Gtk2::Ex::FormFactory module. - TextView: a Gtk2::TextView - Timestamp: Entries to enter correct timestamps down to minute level, while calculating internally with unix timestamps. - FormFactory: added parent_ff attribute - FormFactory: added hide parameter to open() and show() method - FormFactory: added open_confirm_window() method - FormFactory: added get_image_path() method - Button: added stock attribute - Combo: added presets attribute - DialogButtons: buttons may now be controlled with the object attribute's value - HBox/VBox: added no_frame attribute - List: added update_selection_only attribute - Proxy: get_attr/set_attr consider object.attr notation accordingly and switch to the proxy of the correspondent object for convenience. - Table: warn if table layout string contains tab characters - Widget: added gtk_properties_widget - Widget: added label_markup attribute - Widget: added 'type' option to show_error_message() and error windows are now modal. => WARNING: incompatible API change! - Window: added closed_hook attribute 0.51 Mon Dec 27, 2004, joern Bugfixes: - Container: on cleanup the internal content list was set to undef instead to []. Features: - Context: attr_depends_href extended, so it accepts a list reference of attributes an attribute depends on, not only a single attribute. - Image: with a constant scale of 1 all magic regarding scale calculation is switched off - Window: if a window has a FormFactory parent, FormFactory->close() is called automatically when the window is destroyed. - Container: Print a better error message if a child of a container isn't defined. Notes: - Added GPL LICENSE file to the distribution 0.50 Sat Nov 20, 2004, joern First public release. Refer to http://www.exit1.org/ for comprehensive online documentation. WARNING: API is not stable yet and may change incompatibly. Gtk2-Ex-FormFactory-0.67/lib/0000755000175000017500000000000011620730434014642 5ustar joernjoernGtk2-Ex-FormFactory-0.67/lib/Gtk2/0000755000175000017500000000000011620730434015451 5ustar joernjoernGtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/0000755000175000017500000000000011620730434016025 5ustar joernjoernGtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory.pm0000644000175000017500000003624511620727362020636 0ustar joernjoernpackage Gtk2::Ex::FormFactory; $VERSION = "0.67"; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); use Gtk2; use Gtk2::Ex::FormFactory::Loader; use Scalar::Util; sub get_type { "form_factory" } use Gtk2::Ex::FormFactory::Context; use Gtk2::Ex::FormFactory::Layout; use Gtk2::Ex::FormFactory::Rules; sub get_context { shift->{context} } sub get_sync { shift->{sync} } sub get_layouter { shift->{layouter} } sub get_rule_checker { shift->{rule_checker} } sub get_gtk_size_groups { shift->{gtk_size_groups} } sub get_parent_ff { shift->{parent_ff} } sub get_widgets_by_name { shift->{widgets_by_name} } sub get_buffered { shift->{buffered} } sub set_context { shift->{context} = $_[1] } sub set_sync { shift->{sync} = $_[1] } sub set_layouter { shift->{layouter} = $_[1] } sub set_rule_checker { shift->{rule_checker} = $_[1] } sub set_gtk_size_groups { shift->{gtk_size_groups} = $_[1] } sub set_parent_ff { shift->{parent_ff} = $_[1] } sub set_widgets_by_name { shift->{widgets_by_name} = $_[1] } sub set_buffered { shift->{buffered} = $_[1] } sub get_form_factory { shift } sub new { my $class = shift; my %par = @_; my ($context, $parent_ff, $sync, $layouter, $rule_checker) = @par{'context','parent_ff','sync','layouter','rule_checker'}; my $self = $class->SUPER::new(@_); $sync = 1 unless defined $sync; $context ||= Gtk2::Ex::FormFactory::Context->new; $layouter ||= Gtk2::Ex::FormFactory::Layout->new; $rule_checker ||= Gtk2::Ex::FormFactory::Rules->new; $self->set_parent_ff ($parent_ff); $self->set_context ($context); $self->set_sync ($sync); $self->set_layouter ($layouter); $self->set_rule_checker ($rule_checker); $self->set_gtk_size_groups ({}); $self->set_widgets_by_name ({}); $self->set_buffered (1); return $self; } sub cleanup { my $self = shift; $self->SUPER::cleanup(@_); $self->set_gtk_size_groups({}); $self->set_widgets_by_name({}); 1; } sub open { my $self = shift; my %par = @_; my ($hide) = $par{'hide'}; #-- First build all widgets as implemented in the Widget class $self->build(); #-- Now show all widgets, if we shouldn't keep the hided $self->show if not $hide; 1; } sub build { my $self = shift; return if $self->get_built; #-- first register all widgets of this FormFactory #-- to resolve cross references between Widgets #-- during building. $self->register_all_widgets($self); #-- Now call Container's build $self->SUPER::build(); 1; } sub show { my $self = shift; #-- Show all widgets foreach my $child ( @{$self->get_content} ) { $child->get_gtk_parent_widget->show_all; } #-- And finally connect the changed signals - this need to be #-- done *after* showing the widgets. For containers ->show() #-- could trigger updates which cause object updates. Since #-- ->update() isn't called yet, this would invalidate the #-- object's state. $self->connect_signals; 1; } sub update { my $self = shift; $self->update_all; } sub ok { my $self = shift; $self->apply; $self->close; 1; } sub apply { my $self = shift; if ( $self->get_sync ) { $self->commit_proxy_buffers_all; } else { $self->apply_changes_all; } 1; } sub cancel { my $self = shift; $self->discard_proxy_buffers_all if $self->get_sync; $self->close; 1; } sub close { my $self = shift; $_->get_gtk_parent_widget->destroy for @{$self->get_content}; $self->cleanup; 1; } sub register_all_widgets { my $self = shift; my ($widget) = @_; if ( $widget->isa("Gtk2::Ex::FormFactory::Container") ) { $self->register_widget($widget); foreach my $child ( @{$widget->get_content} ) { $self->register_all_widgets($child); } } else { $self->register_widget($widget); } 1; } sub register_widget { my $self = shift; my ($widget) = @_; $self->get_widgets_by_name->{$widget->get_name} = $widget; $self->set_buffered(0) if $widget->get_object && !$self->get_context ->get_proxy($widget->get_object) ->get_buffered; 1; } sub get_form_factory_gtk_window { my $self = shift; my $gtk_window; foreach my $child ( @{$self->get_content} ) { if ( $child->isa("Gtk2::Ex::FormFactory::Window") ) { $gtk_window = $child->get_gtk_parent_widget; last; } } return $gtk_window; } sub open_confirm_window { my $self = shift; my %par = @_; my ($message, $yes_callback, $no_callback, $position, $with_cancel) = @par{'message','yes_callback','no_callback','position','with_cancel'}; my ($yes_label, $no_label) = @par{'yes_label','no_label'}; $position ||= "center-on-parent"; $yes_label ||= "gtk-yes"; $no_label ||= "gtk-no"; my $confirm = Gtk2::MessageDialog->new_with_markup ( $self->get_form_factory_gtk_window, ["modal","destroy-with-parent"], "question", "none", $message ); $confirm->add_buttons ("gtk-cancel", "cancel") if $with_cancel; $confirm->add_buttons ($no_label, "no"); $confirm->add_buttons ($yes_label, "yes"); $confirm->signal_connect("response", sub { my ($widget, $answer) = @_; if ( $answer eq 'yes' ) { &$yes_callback() if $yes_callback; } if ( $answer eq 'no' ) { &$no_callback() if $no_callback; } $widget->destroy; 1; }); $confirm->set_position ($position); $confirm->show; 1; } sub open_message_window { my $self = shift; my %par = @_; my ($type, $message, $position) = @par{'type','message','position'}; $position ||= "center-on-parent"; $type ||= "info"; my $confirm = Gtk2::MessageDialog->new_with_markup ( $self->get_form_factory_gtk_window, ["modal","destroy-with-parent"], $type, "ok", $message ); $confirm->signal_connect("response", sub { my ($widget) = @_; $widget->destroy; 1; }); $confirm->set_position ($position); $confirm->show; 1; } sub get_image_path { my $class = shift; my ($filename) = @_; foreach my $dir ( @INC ) { return "$dir/$filename" if -f "$dir/$filename"; } return; } sub change_mouse_cursor { my $self = shift; my ($type, $gtk_window) = @_; $gtk_window ||= $self->get_form_factory_gtk_window; return unless $gtk_window; my $cursor = $type ? Gtk2::Gdk::Cursor->new($type) : undef; $gtk_window->window->set_cursor($cursor); Gtk2->main_iteration while Gtk2->events_pending; 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory - Makes building complex GUI's easy =head1 SYNOPSIS #-- Refer to http://www.exit1.org/ for #-- a comprehensive online documentation. #-- Read Gtk2::Ex::FormFactory::Intro use Gtk2::Ex::FormFactory; my $context = Gtk2::Ex::FormFactory::Context->new; $context->add_object ( name => "worksheet", object => My::Worksheet->new, ); # derived from Gtk2::Ex::FormFactory::Layout my $layouter = My::Layout->new; # derived from Gtk2::Ex::FormFactory::Rules my $rule_checker = My::Rules->new; my $ff = Gtk2::Ex::FormFactory->new ( context => $context, layouter => $layouter, rule_checker => $rule_checker, content => [ Gtk2::Ex::FormFactory::Window->new ( title => "Worksheet Editor", content => [ Gtk2::Ex::FormFactory::Form->new ( title => "Main data", content => [ Gtk2::Ex::FormFactory::Entry->new ( label => "Worksheet title", attr => "worksheet.title", tip => "Title of this worksheet", ), #-- More widgets... ], ), Gtk2::Ex::FormFactory->DialogButtons->new, ], ), ], ); $ff->open; $ff->update; Gtk2->main; =head1 ABSTRACT With Gtk2::Ex::FormFactory you can build a GUI which consistently represents the data of your application. =head1 DESCRIPTION This is a framework which tries to make building complex GUI's easy, by offering these two main features: * Consistent looking GUI without the need to code resp. tune each widget by hand. Instead you declare the structure of your GUI, connect it to the data of your program (which should be a well defined set of objects) and control how this structure is transformed into a specific layout in a very generic way. * Automatically keep widget and object states in sync (in both directions), even with complex data structures with a lot of internal dependencies, object nesting etc. This manpage describes the facilities of Gtk2::Ex::FormFactory objects which are only a small part of the whole framework. To get a full introduction and overview of how this framework works refer to Gtk2::Ex::FormFactory::Intro. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container +--- Gtk2::Ex::FormFactory =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B = Gtk2::Ex::FormFactory::Context [optional] This is the Context of this FormFactory. The Context connects your application objects and their attributes with the GUI build through the FormFactory. Refer to Gtk2::Ex::FormFactory::Context for details. If you omit this option in the new() object constructor an empty Context is created which can be accessed with B. =item B = Gtk2::Ex::FormFactory::Layout [optional] This is the Layout module of this FormFactory. The Layout module actually builds the GUI and thus controls all details of appearance. Refer to Gtk2::Ex::FormFactory::Layout for details, if you're interested in writing your own Layout module. If you omit this option in the new() object constructor a default Gtk2::Ex::FormFactory::Layout object is created which can be accessed with B. =item B = Gtk2::Ex::FormFactory::Rules [optional] This is the rule checker module of this FormFactory. It's responsible to check user input against a set of rules which may be associated with a widget. Refer to Gtk2::Ex::FormFactory::Rules for details, if you're interested in writing your own rule checker module. If you omit this option in the new() object constructor a default Gtk2::Ex::FormFactory::Rules object is created which can be accessed with B. =item B = BOOL [optional] By default all changes on the GUI trigger corresopndent updates on your application objects immediately. If you want to build dialogs with local changes on the GUI only, e.g. to be able to implement a Cancel button in a simple fashion (refer to Gtk2::Ex::FormFactory::DialogButtons), you may switch this synchronisation off by setting B to FALSE. But note that asynchronous dialogs are static. Dependencies between objects and attributes, which are defined in the associated Gtk2::Ex::FormFactory::Context, don't work on widget/GUI level. That's why automatic dependency resolution / widget updating only works for FormFactory's with B set to TRUE. =item B = Gtk2::Ex::FormFactory object [optional] You may specify a parent Gtk2::Ex::FormFactory object. The Gtk Window of this FormFactory will be set transient to the Gtk Window of the parent FormFactory. =back =head1 METHODS =over 4 =item $form_factory->B ( [ hide => BOOL ]) This actually builds and displays the GUI, if you set the B parameter to a true value. Until this method is called you can add new or modify existent Widgets of this FormFactory. If you set B you need to call $form_factory->show later, otherwise all widgets will keep invisible. No object data will be transfered to the GUI, so it will be more or less empty. Call B to put data into the GUI. =item $form_factory->B () After building the GUI you should call B to transfer your application data to the GUI. =item $form_factory->B () This method applies all changes of a asynchronous FormFactory and closes it afterwards. =item $form_factory->B () All changes to Widgets inside this FormFactory are applied to the associated application object attributes. Useful only in a FormFactory with B=FALSE. =item $form_factory->B () When you exit the program you B call B on all FormFactories which are actually open. Otherwise you will get error messages like this from Perl's garbage collector: Attempt to free unreferenced scalar: SV 0x85d7374 during global destruction. That's because circular references are necessary between Gtk2 and Gtk2::Ex::FormFactory widgets. These references need first to be deleted until Perl can exit the program cleanly. =item $form_factory->B Currently this simply calls $form_factory->B. =item $form_factory->B ( parameters ) This is a convenience method to open a confirmation window which is set modal and transient to the window of this FormFactory. The following parameters are known: message The message resp. question, HTML markup allowed position Position of the dialog. Defaults to 'center-on-parent'. Other known values are: 'none', 'center', 'mouse' and 'center-always' yes_callback Code reference to be called if the user answered your question with "Yes" no_callback Code reference to be called if the user answered your question with "No" yes_label (Stock-)Label for the yes button. Default 'gtk-yes' no_label (Stock-)Label for the no button. Default 'gtk-no' =item $form_factory->B ( parameters ) This is a convenience method to open a message window which is set modal and transient to the window of this FormFactory. The following parameters are known: type Type of the dialog. Defaults to 'info'. Other known values are: 'warning', 'question' and 'error' message The message, HTML markup allowed position Position of the dialog. Defaults to 'center-on-parent'. Other known values are: 'none', 'center', 'mouse' and 'center-always' =item $filename = $form_factory->B This is a convenience method to find a filename inside Perl's @INC path. You will need this if you ship images or icons inside your module namespace and want to retreive the actual filenames of them. =item $form_factory->B ( $type [, $gtk_widget] ) This convenience method changes the mouse cursor of the window of this FormFactory, or of an arbitrary widget passed as $gtk_widget. $type is the cursor type, e.g. "watch" for a typical busy / sandglass cursor. Refer to the Gtk documentation for a complete list of possible mouse cursors. =back =head1 AUTHORS Jrn Reder =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/0000755000175000017500000000000011620730434020260 5ustar joernjoernGtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Container.pm0000644000175000017500000001740610473655400022554 0ustar joernjoernpackage Gtk2::Ex::FormFactory::Container; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_content { shift->{content} } sub get_title { shift->{title} } sub set_content { shift->{content} = $_[1] } sub set_title { shift->{title} = $_[1] } sub isa_container { 1 } sub new { my $class = shift; my %par = @_; my ($content, $title) = @par{'content','title'}; my $self = $class->SUPER::new(@_); #-- Handle some defaults for 'content' parameter if ( not defined $content ) { $content = []; } elsif ( ref $content ne 'ARRAY' ) { $content = [ $content ]; } #-- For convenience the developer may write pairs of #-- the Widget's short name and a hash ref with its #-- attributes instead of adding real objects. This #-- loop search for such non-objects and creates #-- objects accordingly. my @content_with_objects; for ( my $i=0; $i < @{$content}; ++$i ) { if ( not defined $content->[$i] ) { die "Child #$i of ".$self->get_type."/".$self->get_name. " is not defined"; } if ( not ref $content->[$i] ) { #-- No object, so derive the class name #-- from the short name my $class = $content->[$i]; $class =~ s/^(.)/uc($1)/e; $class =~ s/_(.)/uc($1)/eg; $class =~ s/_//g; $class = "Gtk2::Ex::FormFactory::$class"; #-- And create the object my $object = $class->new(%{$content->[$i+1]}); push @content_with_objects, $object; #-- Skip next entry in @content ++$i; } else { #-- Regular objects are taken as is push @content_with_objects, $content->[$i]; } } $self->set_content(\@content_with_objects); $self->set_title($title); return $self; } sub debug_dump { my $self = shift; my ($level) = @_; $self->SUPER::debug_dump($level); foreach my $c ( @{$self->get_content} ) { if ( $c ) { $c->debug_dump($level+1); } else { print " "x($level+1),"UNDEF\n"; } } 1; } sub build { my $self = shift; #-- First build the widget itself $self->SUPER::build(@_); #-- Now build the children $self->build_children; 1; } sub build_children { my $self = shift; $Gtk2::Ex::FormFactory::DEBUG && print "$self->build_children\n"; my $layouter = $self->get_form_factory->get_layouter; foreach my $child ( @{$self->get_content} ) { $child->set_parent($self); $child->set_form_factory($self->get_form_factory); $child->build; $layouter->add_widget_to_container ($child, $self); } 1; } sub add_child_widget { my $self = shift; my ($child) = @_; push @{$self->get_content}, $child; return unless $self->get_built; my $form_factory = $self->get_form_factory; my $layouter = $form_factory->get_layouter; $form_factory->register_all_widgets($child); $child->set_parent($self); $child->set_form_factory($form_factory); $child->build; $layouter->add_widget_to_container($child, $self); $child->connect_signals; $child->update_all; $child->get_gtk_parent_widget->show_all; 1; } sub remove_child_widget { my $self = shift; my ($child) = @_; my $found; my $i = 0; foreach my $c ( @{$self->get_content} ) { $found = 1, last if $c eq $child; ++$i; } die "Widget '".$child->get_name. "' no child of container '". $self->get_name."'" unless $found; splice @{$self->get_content}, $i, 1; return unless $self->get_built; my $child_gtk_widget = $child->get_gtk_parent_widget; my $gtk_widget = $self->get_gtk_widget; $gtk_widget->remove($child_gtk_widget); $child->cleanup; 1; } sub update_all { my $self = shift; $self->SUPER::update(@_); foreach my $c ( @{$self->get_content} ) { $c->update_all; } 1; } sub apply_changes_all { my $self = shift; $self->SUPER::apply_changes(@_); foreach my $c ( @{$self->get_content} ) { $c->apply_changes_all; } 1; } sub commit_proxy_buffers_all { my $self = shift; $self->SUPER::commit_proxy_buffers(@_); foreach my $c ( @{$self->get_content} ) { $c->commit_proxy_buffers_all; } 1; } sub discard_proxy_buffers_all { my $self = shift; $self->SUPER::discard_proxy_buffers(@_); foreach my $c ( @{$self->get_content} ) { $c->discard_proxy_buffers_all; } 1; } sub connect_signals { my $self = shift; $self->SUPER::connect_signals(@_); foreach my $c ( @{$self->get_content} ) { $c->connect_signals; } 1; } sub cleanup { my $self = shift; foreach my $c ( @{$self->get_content} ) { $c->cleanup; } $self->SUPER::cleanup(@_); $self->set_content([]); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Container - A container in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Container->new ( title => Visible title of this container, content => [ List of children ], ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This is an abstract base class for all containers in the Gtk2::Ex::FormFactory framework. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container +--- Gtk2::Ex::FormFactory::Buttons +--- Gtk2::Ex::FormFactory::Expander +--- Gtk2::Ex::FormFactory::Form +--- Gtk2::Ex::FormFactory::HBox +--- Gtk2::Ex::FormFactory::Notebook +--- Gtk2::Ex::FormFactory::Table +--- Gtk2::Ex::FormFactory::VBox +--- Gtk2::Ex::FormFactory::Window Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B = SCALAR [optional] Each container may have a title. How this title actually is rendered depends on the implementation of a particular container resp. the implementation of this container in Gtk2::Ex::FormFactory::Layout. Default is to draw a frame with this title around the container widget. =item B<content> = ARRAYREF of Gtk2::Ex::FormFactory::Widget's [optional] This is a reference to an array containing the children of this container. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 METHODS =over 4 =item $container->B<add_child_widget> ( $widget ) With this method you add a child widget to a container widget. If the container actually wasn't built yet the widget is just appended to the content list of the container and will be built later together with the container. Otherwise the widget will be built, shown and updated, so adding widgets at runtime is no problem. =item $container->B<remove_child_widget> ( $widget ) Removes a child widget from this container. If the container is built the widget will be destroyed completely and the $widget reference may not be used furthermore. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/VSeparator.pm��������������������������������������0000644�0001750�0001750�00000003270�10400606040�022674� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::VSeparator; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "vseparator" } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::VSeparator - A VSeparator in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::VSeparator->new ( ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a VSeparator in a Gtk2::Ex::FormFactory framework. No application object attributes are associated with a VSeparator. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::VSeparator Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Widget. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/CheckButtonGroup.pm��������������������������������0000644�0001750�0001750�00000027321�10400606040�024037� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::CheckButtonGroup; use Carp; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); use POSIX qw(ceil); sub get_type { "check_button_group" } sub get_max_columns { shift->{max_columns} } sub get_max_rows { shift->{max_rows} } sub get_attr_max_columns { shift->{attr_max_columns} } sub get_attr_max_rows { shift->{attr_max_rows} } sub get_column_labels { shift->{column_labels} } sub get_attr_column_labels { shift->{attr_column_labels} } sub get_row_labels { shift->{row_labels} } sub get_attr_row_labels { shift->{attr_row_labels} } sub get_homogeneous { shift->{homogeneous} } sub set_max_columns { shift->{max_columns} = $_[1] } sub set_max_rows { shift->{max_rows} = $_[1] } sub set_attr_max_columns { shift->{attr_max_columns} = $_[1] } sub set_attr_max_rows { shift->{attr_max_rows} = $_[1] } sub set_column_labels { shift->{column_labels} = $_[1] } sub set_attr_column_labels { shift->{attr_column_labels} = $_[1] } sub set_row_labels { shift->{row_labels} = $_[1] } sub set_attr_row_labels { shift->{attr_row_labels} = $_[1] } sub set_homogeneous { shift->{homogeneous} = $_[1] } sub get_gtk_check_buttons { shift->{gtk_check_buttons} } sub get_gtk_table { shift->{gtk_table} } sub set_gtk_check_buttons { shift->{gtk_check_buttons} = $_[1] } sub set_gtk_table { shift->{gtk_table} = $_[1] } sub get_last_toggled_value { shift->{last_toggled_value} } sub set_last_toggled_value { shift->{last_toggled_value} = $_[1] } sub get_in_selection_update { shift->{in_selection_update} } sub set_in_selection_update { shift->{in_selection_update} = $_[1] } sub new { my $class = shift; my %par = @_; my ($max_columns, $max_rows, $attr_max_columns, $attr_max_rows) = @par{'max_columns','max_rows','attr_max_columns','attr_max_rows'}; my ($column_labels, $attr_column_labels, $row_labels) = @par{'column_labels','attr_column_labels','row_labels'}; my ($attr_row_labels, $homogeneous) = @par{'attr_row_labels','homogeneous'}; my $self = $class->SUPER::new(@_); $max_rows = 1 if ($max_rows == 0 && $max_columns == 0) && !($attr_max_rows || $attr_max_columns); $homogeneous = 1 if not defined $homogeneous; $self->set_max_columns ($max_columns); $self->set_max_rows ($max_rows); $self->set_attr_max_columns ($attr_max_columns); $self->set_attr_max_rows ($attr_max_rows); $self->set_column_labels ($column_labels); $self->set_attr_column_labels ($attr_column_labels); $self->set_row_labels ($row_labels); $self->set_attr_row_labels ($attr_row_labels); $self->set_homogeneous ($homogeneous); return $self; } sub cleanup { my $self = shift; $self->SUPER::cleanup(@_); $self->set_gtk_check_buttons(undef); $self->set_gtk_table(undef); 1; } sub object_to_widget { my $self = shift; #-- $checkboxes = [ [0, "Sun"], [1 ,"Mon"], [2,"Tue"], ... ] my $checkboxes = $self->get_proxy->get_attr_list( $self->get_attr, $self->get_name ); #-- $selected_href = { 0 => 1, 2 => 1 } - Sun and Tue are selected my $selected_href = $self->get_object_value; my $hbox = $self->get_gtk_widget; my @children = $hbox->get_children; $hbox->remove($_) for @children; my ($rows, $columns); my $max_rows = $self->get_max_rows; my $max_columns = $self->get_max_columns; my $row_labels = $self->get_row_labels; my $column_labels = $self->get_column_labels; my $cnt = @{$checkboxes}; if ( $self->get_attr_max_rows ) { $max_rows = $self->get_proxy->get_attr($self->get_attr_max_rows); } elsif ( $self->get_attr_max_columns ) { $max_columns = $self->get_proxy->get_attr($self->get_attr_max_columns); } if ( $self->get_attr_row_labels ) { $row_labels = $self->get_proxy->get_attr($self->get_attr_row_labels); } if ( $self->get_attr_column_labels ) { $column_labels = $self->get_proxy->get_attr($self->get_attr_column_labels); } if ( $max_rows ) { $rows = $max_rows; $rows = $cnt if $rows > $cnt; $columns = ceil($cnt / $rows); } else { $columns = $max_columns; $columns = $cnt if $columns > $cnt; $rows = ceil($cnt / $columns); } my %gtk_check_buttons; my $gtk_table = Gtk2::Table->new ($rows, $columns); $gtk_table->set ( homogeneous => $self->get_homogeneous ); ++$columns if $row_labels; ++$rows if $column_labels; my $i = 0; for ( my $c=0; $c < $columns && $i < $cnt; ++$c ) { for ( my $r=0; $r < $rows && $i < $cnt; ++$r ) { next if $column_labels && $c == 0 && $r == 0; if ( $row_labels && $c==0 && $r > 0 ) { my $gtk_label = Gtk2::Label->new($row_labels->[$r-1]); $gtk_table->attach_defaults($gtk_label, $c, $c+1, $r, $r+1); next; } if ( $column_labels && $r==0 && $c > 0 ) { my $gtk_label = Gtk2::Label->new($column_labels->[$c-1]); $gtk_table->attach_defaults($gtk_label, $c, $c+1, $r, $r+1); next; } my $checkbox = $checkboxes->[$i]; my $gtk_check_button = Gtk2::CheckButton->new($checkbox->[1]); $gtk_check_buttons{$checkbox->[0]} = $gtk_check_button; $gtk_check_button->set_active(1) if $selected_href->{$checkbox->[0]}; $gtk_table->attach_defaults($gtk_check_button, $c, $c+1, $r, $r+1); ++$i; } } $hbox->pack_start($gtk_table, 0, 1, 0); $hbox->show_all; $self->set_gtk_check_buttons(\%gtk_check_buttons); $self->set_gtk_table($gtk_table); $self->connect_changed_signal_for_all_buttons; 1; } sub update_selection { my $self = shift; $self->set_in_selection_update(1); my $selected_href = $self->get_object_value; my $gtk_check_buttons = $self->get_gtk_check_buttons; while ( my ($value, $gtk_check_button) = each %{$gtk_check_buttons} ) { $gtk_check_button->set_active( $selected_href->{$value} ); } $self->set_in_selection_update(0); 1; } sub widget_to_object { my $self = shift; return if $self->get_in_selection_update; my $gtk_check_buttons = $self->get_gtk_check_buttons; my %selected; while ( my ($value, $gtk_check_button) = each %{$gtk_check_buttons} ) { $selected{$value} = 1 if $gtk_check_button->get_active; } $self->set_object_value(\%selected); 1; } sub connect_changed_signal_for_all_buttons { my $self = shift; my $gtk_check_buttons = $self->get_gtk_check_buttons; while ( my ($value, $gtk_check_button) = each %{$gtk_check_buttons} ) { $gtk_check_button->signal_connect ( toggled => sub { return 1 if $self->get_in_selection_update; $self->set_last_toggled_value($value); $self->widget_value_changed; 1; } ); } 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::CheckButtonGroup - A group of checkbuttons =head1 SYNOPSIS Gtk2::Ex::FormFactory::CheckButtonGroup->new ( max_columns => Maximum number of columns, max_rows => Maximum number of rows, column_labels => Array of column label strings, row_labels => Array of row label strings, attr_max_columns => Object attribute for number of columns, attr_max_rows => Object attribute for number of rows, attr_column_labels => Object attribute for column labels, attr_row_labels => Object attribute for row labels, homogeneous => Force homogeneous layout of underlying table? ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a group of check buttons which allow a multiple selection out of a set from predefined values. It's arranged in a two dimensional table. You can specify either the maximum number of rows or columns, the actual dimensions are calculated automatically. Optionally you can add column and/or row labels, extending the corresponding table accordingly. You can pass the configuration data statically or specify application object attributes controlling them, so the checkbutton group builds dynamically at runtime. The value of a CheckBoxGroup is a hash. The value of each selected checkbox will result in a correspondent hash key with a true value assigned. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::CheckButtonGroup Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<max_columns> = SCALAR [optional] Maximum number of columns the table should have. You must not set B<max_rows> when you specify B<max_columns>. =item B<max_rows> = SCALAR [optional] Maximum number of rows the table should have. You must not set B<max_columns> when you specify B<max_rows>. If you omit both attributes B<max_rows> defaults to 1, so all buttons will appear in one row. =item B<column_labels> = ARRAY [optional] You can add column labels by setting an array of strings to the B<column_labels> attribute. The number of entries should correspond to the B<max_columns> setting. =item B<row_labels> = ARRAY [optional] You can add row labels by setting an array of strings to the B<row_labels> attribute. The number of entries should correspond to the B<max_rows> setting. =item B<attr_max_columns> = "object.attr" [optional] As an alternative to B<max_columns> the maximum number of columns may be controlled by an application object attribute which needs to be passed here in "object.attr" notation. =item B<attr_max_rows> = "object.attr" [optional] As an alternative to B<max_rows> the maximum number of rows may be controlled by an application object attribute which needs to be passed here in "object.attr" notation. =item B<attr_column_labels> = "object.attr" [optional] As an alternative to B<column_labels> the column labels may be controlled by an application object attribute which needs to be passed here in "object.attr" notation. =item B<attr_row_labels> = "object.attr" [optional] As an alternative to B<row_labels> the row labels may be controlled by an application object attribute which needs to be passed here in "object.attr" notation. =item B<homogeneous> = BOOL [optional] Defaults to 1 forcing the underlying table to homogeneous layout. =back =head1 REQUIREMENTS FOR ASSOCIATED APPLICATION OBJECTS Application objects represented by a Gtk2::Ex::FormFactory::CheckButtonGroup must define additional methods. The naming of the methods listed beyond uses the standard B<get_> prefix for the attribute read accessor. B<ATTR> needs to be replaced by the actual name of the attribute associated with the widget. =over 4 =item B<get_ATTR_list> This method must return a two dimensional array resp. a list of lists which represent the values the user can select from. Example: [ [ 0, "Sun" ], [ 1, "Mon" ], [ 2, "Tue" ], ... ] Each entry in the list consists of a list ref with two elements. The first is the value associated with the checkbox (which will become a hash key in the associated object attribute), the second the label of the checkbox on the GUI. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Timestamp.pm���������������������������������������0000644�0001750�0001750�00000013715�10400606040�022556� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Timestamp; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); use Time::Local; sub get_type { "timestamp" } sub get_format { shift->{format} } sub set_format { shift->{format} = $_[1] } sub get_gtk_mday_widget { shift->{gtk_mday_widget} } sub get_gtk_mon_widget { shift->{gtk_mon_widget} } sub get_gtk_year_widget { shift->{gtk_year_widget} } sub get_gtk_hour_widget { shift->{gtk_hour_widget} } sub get_gtk_min_widget { shift->{gtk_min_widget} } sub set_gtk_mday_widget { shift->{gtk_mday_widget} = $_[1] } sub set_gtk_mon_widget { shift->{gtk_mon_widget} = $_[1] } sub set_gtk_year_widget { shift->{gtk_year_widget} = $_[1] } sub set_gtk_hour_widget { shift->{gtk_hour_widget} = $_[1] } sub set_gtk_min_widget { shift->{gtk_min_widget} = $_[1] } sub get_gtk_tip_widgets {[ $_[0]->get_gtk_mday_widget, $_[0]->get_gtk_mon_widget, $_[0]->get_gtk_year_widget, $_[0]->get_gtk_hour_widget, $_[0]->get_gtk_min_widget, ]} sub new { my $class = shift; my %par = @_; my ($format) = $par{'format'}; my $self = $class->SUPER::new(@_); $format ||= '%d.%m.%Y %k:%M'; $self->set_format($format); return $self; } sub cleanup { my $self = shift; $self->SUPER::cleanup(@_); $self->set_gtk_mday_widget(undef); $self->set_gtk_mon_widget(undef); $self->set_gtk_year_widget(undef); $self->set_gtk_hour_widget(undef); $self->set_gtk_min_widget(undef); 1; } sub object_to_widget { my $self = shift; my $format = $self->get_format; my $unix_time = $self->get_object_value; my @d = localtime($unix_time); $self->get_gtk_year_widget->set_text(sprintf("%04d",$d[5]+1900)); $self->get_gtk_mon_widget->set_text(sprintf("%02d",$d[4]+1)); $self->get_gtk_mday_widget->set_text(sprintf("%02d",$d[3])); $self->get_gtk_hour_widget->set_text(sprintf("%02d",$d[2])); $self->get_gtk_min_widget->set_text(sprintf("%02d",$d[1])); 1; } sub get_widget_unix_time { my $self = shift; my $format = $self->get_format; my @d = (0, 0, 0, 1, 0, 0); $d[5] = $self->get_gtk_year_widget->get_text-1900 if $format =~ /%Y/; $d[4] = $self->get_gtk_mon_widget->get_text-1 if $format =~ /%m/; $d[3] = $self->get_gtk_mday_widget->get_text if $format =~ /%d/; $d[2] = $self->get_gtk_hour_widget->get_text if $format =~ /%k/; $d[1] = $self->get_gtk_min_widget->get_text if $format =~ /%M/; my $unix_time = eval { timelocal(@d) }; return "$unix_time"; } sub widget_to_object { my $self = shift; my $unix_time = $self->get_widget_unix_time; if ( not defined $unix_time ) { $self->show_error_message ( message => $self->get_label." is no valid timestamp", ); } else { $self->set_object_value ( $unix_time ); } 1; } sub backup_widget_value { my $self = shift; my @backup; foreach my $type (qw( year mon mday hour min )) { my $widget = $self->{"gtk_${type}_widget"}; if ( $widget ) { push @backup, $widget->get_text; } else { push @backup, undef; } } $self->set_backup_widget_value (\@backup); 1; } sub restore_widget_value { my $self = shift; my $backup = $self->get_backup_widget_value; my $i; foreach my $type (qw( year mon mday hour min )) { my $widget = $self->{"gtk_${type}_widget"}; my $value = $backup->[$i]; if ( $widget ) { $widget->set_text($value); } ++$i; } 1; } sub get_widget_check_value { $_[0]->get_widget_unix_time; } sub connect_changed_signal { my $self = shift; $_->signal_connect ( changed => sub { $self->widget_value_changed }, ) for ( $self->get_gtk_year_widget, $self->get_gtk_mon_widget, $self->get_gtk_mday_widget, $self->get_gtk_hour_widget, $self->get_gtk_min_widget ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Timestamp - Enter a valid timestamp =head1 SYNOPSIS Gtk2::Ex::FormFactory::Timestamp->new ( format => A format string describing the the time fields ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a bunch of text entries to manage a valid timestamp (date and time down to minute level). The object value is a unix timestamp (in localtime). =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Timestamp Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<format> = SCALAR [optional] This is a format string to define which timestamp fields should be edited and how they should be displayed. Format wildcards are defined as follows: %Y Year, four digits %m Month, two digits %d Day of month, two digits %k Hour of the day, two digits %M Minutes, two digits Everything else in the format string will be rendered as labels between the time field text entries. The format string defaults to %d.%m.%Y %k:%M All time fields which are not part of the format string will get a default value of 0. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ���������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Image.pm�������������������������������������������0000644�0001750�0001750�00000020300�10400606040�021621� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Image; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "image" } sub get_with_frame { shift->{with_frame} } sub get_bgcolor { shift->{bgcolor} } sub get_scale_to_fit { shift->{scale_to_fit} } sub get_max_width { shift->{max_width} } sub get_max_height { shift->{max_height} } sub get_widget_width { shift->{widget_width} } sub get_widget_height { shift->{widget_height} } sub get_scale { shift->{scale} } sub get_scale_hook { shift->{scale_hook} } sub get_gtk_event_box { shift->{gtk_event_box} } sub set_with_frame { shift->{with_frame} = $_[1] } sub set_bgcolor { shift->{bgcolor} = $_[1] } sub set_scale_to_fit { shift->{scale_to_fit} = $_[1] } sub set_max_width { shift->{max_width} = $_[1] } sub set_max_height { shift->{max_height} = $_[1] } sub set_widget_width { shift->{widget_width} = $_[1] } sub set_widget_height { shift->{widget_height} = $_[1] } sub set_scale { shift->{scale} = $_[1] } sub set_scale_hook { shift->{scale_hook} = $_[1] } sub set_gtk_event_box { shift->{gtk_event_box} = $_[1] } sub get_gtk_signal_widget { shift->get_gtk_event_box; } sub new { my $class = shift; my %par = @_; my ($with_frame, $bgcolor, $scale_to_fit, $max_width) = @par{'with_frame','bgcolor','scale_to_fit','max_width'}; my ($max_height, $scale, $scale_hook) = @par{'max_height','scale','scale_hook'}; my $self = $class->SUPER::new(@_); $scale ||= 1 unless $scale_to_fit || $scale_hook; $self->set_with_frame($with_frame); $self->set_bgcolor($bgcolor); $self->set_scale_to_fit($scale_to_fit); $self->set_max_width($max_width); $self->set_max_height($max_height); $self->set_scale($scale); $self->set_scale_hook($scale_hook); return $self; } sub object_to_widget { my $self = shift; my $filename = $self->get_object_value; return $self->empty_widget unless $filename and -r $filename; my $gtk_image = $self->get_gtk_widget; my $gtk_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($filename); my $scale_to_fit = $self->get_scale_to_fit; my $max_width = $self->get_max_width; my $max_height = $self->get_max_height; my $scale = $self->get_scale; my $scale_hook = $self->get_scale_hook; if ( defined $scale ) { my $image_width = $gtk_pixbuf->get_width; my $image_height = $gtk_pixbuf->get_height; if ( $scale == 1 ) { $gtk_image->set_from_pixbuf ( $gtk_pixbuf ); $gtk_image->set_size_request ($image_width, $image_height); return 1; } my $new_width = int($image_width * $scale); my $new_height = int($image_height * $scale); return if $new_width <= 0 or $new_height <= 0; my $scaled_pixbuf = $gtk_pixbuf->scale_simple ( $new_width, $new_height, "GDK_INTERP_BILINEAR" ); $gtk_image->set_from_pixbuf ( $scaled_pixbuf ); $gtk_image->set_size_request ($image_width, $image_height); } elsif ( $scale_hook ) { my $scale = &$scale_hook($self, $gtk_pixbuf); my $image_width = $gtk_pixbuf->get_width; my $image_height = $gtk_pixbuf->get_height; my $new_width = int($image_width * $scale); my $new_height = int($image_height * $scale); return if $new_width <= 0 or $new_height <= 0; my $scaled_pixbuf = $gtk_pixbuf->scale_simple ( $new_width, $new_height, "GDK_INTERP_BILINEAR" ); $gtk_image->set_from_pixbuf ( $scaled_pixbuf ); } elsif ( $scale_to_fit or $max_width or $max_height ) { my $image_width = $gtk_pixbuf->get_width; my $image_height = $gtk_pixbuf->get_height; my $widget_width = $self->get_widget_width; my $widget_height = $self->get_widget_height; $widget_width = $max_width if defined $max_width and $max_width < $widget_width; $widget_height = $max_height if defined $max_height and $max_height < $widget_height; my $width_scale = $widget_width / $image_width; my $height_scale = $widget_height / $image_height; my $scale = $width_scale < $height_scale ? $width_scale : $height_scale; my $new_width = int($image_width * $scale); my $new_height = int($image_height * $scale); return if $new_width <= 0 or $new_height <= 0; my $scaled_pixbuf = $gtk_pixbuf->scale_simple ( $new_width, $new_height, "GDK_INTERP_BILINEAR" ); $gtk_image->set_from_pixbuf ( $scaled_pixbuf ); } else { $gtk_image->set_from_pixbuf ( $gtk_pixbuf ); } 1; } sub empty_widget { my $self = shift; $self->get_gtk_widget->set_from_pixbuf(undef); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Image - An Image in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Image->new ( bgcolor => Background color of the widget, with_frame => Draw a frame around the image?, scale_to_fit => Automatially scale the image in its container?, max_width => Maximum width the image may scale to, max_height => Maximum height the image may scale to, scale => Display the image with this constant scaling, scale_hook => Callback which returns the actual scale, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements an Image in a Gtk2::Ex::FormFactory framework. The image is always displayed with its natural aspect ratio, but there are various possibilities to control the scaling of the image. The value of the associated application object attribute is the filename of the displayed image. The file format must be supported by Gtk2::Gdk::PixBuf. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Image Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<bgcolor> = "RGB Hex Triple" [optional] This is the background color of the widget. If set all areas of the widget not filled with the image are painted with this color. =item B<with_frame> = BOOL [optional] If set to TRUE a frame is rendered around the image. =item B<scale_to_fit> = BOOL [optional] If set to TRUE the image will automatically scale proportionally into to the space the container of this widget allocated for the image widget. =item B<max_width> = INTEGER [optional] With this attribute you can define a maximm width the image may scale to. =item B<max_height> = INTEGER [optional] With this attribute you can define a maximm height the image may scale to. =item B<scale> = FLOAT [optional] If you set this attribute, no dynamic scaling applies, but the image will be constantly scaled with this value. This overrides all other attributes regarding dynamic scaling. =item B<scale_hook> = CODEREF(FormFactory::Image, PixBuf) [optional] This code reference is called if the image needs an update e.g. if the associated application object attribute changed or the parent was resized and B<scale_to_fit> was set. The Gtk2::Ex::FormFactory::Image instance and the Gtk2::Gdk::PixBuf of the image are passed to the function. If B<scale_to_fit> is set the widget's dimension are tracked automatically. The actual width and height are stored in the attributes B<widget_width> and B<widget_height> and thus can be accessed from the callback by calling B<get_widget_width> and B<get_widget_width>. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Rules.pm�������������������������������������������0000644�0001750�0001750�00000026272�10400606040�021707� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Rules; use strict; use Carp; use File::Basename; my %RULES = ( "empty" => sub { $_[0] eq '' }, "not-empty" => sub { $_[0] ne '' }, "alphanumeric" => sub { $_[0] =~ /^\w+$/ }, "identifier" => sub { $_[0] =~ /^[a-z_]\w*$/i }, "no-whitespace" => sub { $_[0] !~ /\s/ }, "zero" => sub { $_[0] =~ /^0(\.0*)?$/ }, "not-zero" => sub { $_[0] !~ /^0(\.0*)?$/ }, "integer" => sub { $_[0] =~ /^[+-]?\d+$/ }, "positive-integer" => sub { $_[0] =~ /^\+?\d+$/ && $_[0] > 0; }, "positive-zero-integer" => sub { $_[0] =~ /^\+?\d+$/ }, "negative-integer" => sub { $_[0] =~ /^-\d+$/ }, "negative-zero-integer" => sub { $_[0] =~ /^(-\d+|0+)$/ }, "float" => sub { $_[0] =~ /^[+-]?\d+(\.\d+)?$/ }, "positive-float" => sub { $_[0] =~ /^\+?\d+(\.\d+)?$/ && $_[0] > 0 }, "positive-zero-float" => sub { $_[0] =~ /^\+?\d+(\.\d+)?$/ }, "negative-float" => sub { $_[0] =~ /^-\d+(\.\d+)?$/ }, "negative-zero-float" => sub { $_[0] =~ /^(-\d+(\.\d+)?|0+)$/ }, "odd" => sub { $_[0] % 2 }, "even" => sub { !($_[0] % 2) }, "file-executable" => sub { (!-d $_[0] && -x $_[0]) }, "file-writable" => sub { (!-d $_[0] && -w $_[0]) }, "file-readable" => sub { (!-d $_[0] && -r $_[0]) }, "dir-writable" => sub { (-d $_[0] && -w $_[0]) }, "dir-readable" => sub { (-d $_[0] && -r $_[0]) }, "parent-dir-writable" => sub { -w dirname($_[0]) }, "parent-dir-readable" => sub { -r dirname($_[0]) }, "executable-command" => \&rule_executable_command, ); my %RULES_MESSAGES = ( "empty" => "{field} is not empty.", "not-empty" => "{field} is empty.", "alphanumeric" => "{field} is not alphanumeric.", "identifier" => "{field} is no identifier.", "no-whitespace" => "{field} contains whitespace.", "zero" => "{field} is not zero", "not-zero" => "{field} is zero", "integer" => "{field} is no integer.", "positive-integer" => "{field} is no positive integer.", "positive-zero-integer" => "{field} is no positive/zero integer.", "negative-integer" => "{field} is no negative integer.", "negative-zero-integer" => "{field} is no negative/zero integer.", "float" => "{field} is no float.", "positive-float" => "{field} is no positive float.", "positive-zero-float" => "{field} is no positive/zero float.", "negative-float" => "{field} is no negative float.", "negative-zero-float" => "{field} is no negative/zero float.", "odd" => "{field} is not odd.", "even" => "{field} is not even.", "file-executable" => "{field} is no file and/or not executable.", "file-writable" => "{field} is no file and/or not writable.", "file-readable" => "{field} is no file and/or not readable.", "dir-writable" => "{field} is no directory and/or not writable.", "dir-readable" => "{field} is no directory and/or not readable.", "parent-dir-writable" => "{field} has no writable parent directory.", "parent-dir-readable" => "{field} has no readable parent directory.", "executable-command" => "_rule_result", ); my $MESSAGE_FORMAT = "Data entered is invalid.\n\n[MESSAGES]\nOld value restored."; sub get_rules { shift->{rules} } sub get_rules_messages { shift->{rules_messages} } sub get_message_format { shift->{message_format} } sub set_rules { shift->{rules} = $_[1] } sub set_rules_messages { shift->{rules_messages} = $_[1] } sub set_message_format { shift->{message_format} = $_[1] } sub new { my $class = shift; my %par = @_; my ($rules, $rules_messages, $message_format) = @par{'rules','rules_messages','message_format'}; $rules ||= {}; $rules_messages ||= {}; $message_format ||= $MESSAGE_FORMAT; my $self = bless { rules => $rules, rules_messages => $rules_messages, message_format => $message_format, }, $class; return $self; } sub check { my $self = shift; my ($rules, $field, $value) = @_; $field ||= "Value"; $rules = [ $rules ] unless ref $rules eq 'ARRAY'; my $messages; foreach my $rule ( @{$rules} ) { if ( $rule eq 'or-empty' ) { return "" if $value eq ''; next; } if ( ref $rule eq 'CODE' ) { my $msg = &$rule($value); $messages .= "$msg\n" if $msg; next; } my $coderef = $self->get_rules->{$rule} || $RULES{$rule}; if ( $coderef ) { my $rc = &$coderef($value); my $message = $self->get_rules_messages->{$rule} || $RULES_MESSAGES{$rule}; warn "Message of rule '$rule' not defined" if $message eq ''; $message ||= "{field} has an unknown error"; if ( $message eq '_rule_result' ) { if ( $rc ne '' ) { $messages .= "$rc\n"; } } elsif ( !$rc ) { $messages .= "$message\n"; } } else { warn "Unknown rule '$rule'. Verification skipped."; } } if ( $messages ) { my $format = $self->get_message_format; $messages =~ s/\{field\}/$field/g; $format =~ s/\[MESSAGES\]/$messages/; $messages = $format; } return $messages; } sub rule_executable_command { my ($command) = @_; my ($file) = split (/ /, $command); if ( not -f $file ) { foreach my $p ( split (/:/, $ENV{PATH}) ) { $file = "$p/$file",last if -x "$p/$file"; } } if ( -x $file ) { return ""; } else { return "{field} not found" if not -e $file; return "{field} not executable"; } } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Rules - Rule checking in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Rules->new ( rules => Hashref of rules and their implemenation CODEREF's, rules_messages => Hashref of the rules' error messages, message_format => Format of the "Invalid rules" message thrown on the GUI, ); =head1 DESCRIPTION This class implements rule checking in a Gtk2::Ex::FormFactory framework. Each widget can have on or more rules (combined with the locical B<and> operator, except for the special "or-empty" rule described beyond) which are checked against the widget's value when the user changes it. This way you can prevent the user from entering illegal data at a high level. Once the user entered illegal data, the old (legal) value is restored and a corresponding error dialog pops up. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Rules =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors. =over 4 =item B<rules> = HASHREF [optional] This is a hash of user specified rules. A rule has a name (the hash key) and a CODREF (the hash value) which implements the rule. The CODEREF has the following prototype: $error = &$CODEREF ($value) If B<$value> doesn't match the rule, B<$error> is the corresponding error message. B<$error> is undef, if B<$value> is Ok. =item B<rules_messages> = HASHREF [optional] This is a hash of the error messages of the user specified rules. A message should read read as follows: {field} is an odd value. When presented to the user, the {field} place holder is replaced with the label of the widget in question. =item B<message_format> = SCALAR [optional] This is the format string for the error message which is displayed to the user. The default is: Data entered is invalid.\n\n[MESSAGES]\nOld value restored. where B<[MESSAGES]> is replaced with the actual list of error messages. =back =head1 BUILTIN RULES This is a verbatim snapshot of the builtin rules and rules_messages hashes. Please take a look at Gtk2::Ex::FormFactory::Rules' source code for a recent list of builtin rules: my %RULES = ( "empty" => sub { $_[0] eq '' }, "not-empty" => sub { $_[0] ne '' }, "alphanumeric" => sub { $_[0] =~ /^\w+$/ }, "identifier" => sub { $_[0] =~ /^[a-z_]\w*$/i }, "no-whitespace" => sub { $_[0] !~ /\s/ }, "zero" => sub { $_[0] =~ /^0(\.0*)?$/ }, "not-zero" => sub { $_[0] !~ /^0(\.0*)?$/ }, "integer" => sub { $_[0] =~ /^[+-]?\d+$/ }, "positive-integer" => sub { $_[0] =~ /^[+]?\d+$/ }, "negative-integer" => sub { $_[0] =~ /^-\d+$/ }, "float" => sub { $_[0] =~ /^[+-]?\d+(\.\d+)?$/ }, "positive-float" => sub { $_[0] =~ /^\+?\d+(\.\d+)?$/ }, "negative-float" => sub { $_[0] =~ /^-\d+(\.\d+)?$/ }, "odd" => sub { $_[0] % 2 }, "even" => sub { !($_[0] % 2) }, "file-executable" => sub { (!-d $_[0] && -x $_[0]) }, "file-writable" => sub { (!-d $_[0] && -w $_[0]) }, "file-readable" => sub { (!-d $_[0] && -r $_[0]) }, "dir-writable" => sub { (-d $_[0] && -w $_[0]) }, "dir-readable" => sub { (-d $_[0] && -r $_[0]) }, "parent-dir-writable" => sub { -w dirname($_[0]) }, "parent-dir-readable" => sub { -r dirname($_[0]) }, "executable-command" => "_rule_result", ); my %RULES_MESSAGES = ( "empty" => "{field} is not empty.", "not-empty" => "{field} is empty.", "alphanumeric" => "{field} is not alphanumeric.", "identifier" => "{field} is no identifier.", "no-whitespace" => "{field} contains whitespace.", "zero" => "{field} is not zero", "not-zero" => "{field} is zero", "integer" => "{field} is no integer.", "positive-integer" => "{field} is no positive integer.", "negative-integer" => "{field} is no negative integer.", "float" => "{field} is no float.", "positive-float" => "{field} is no positive float.", "negative-float" => "{field} is no negativ float.", "odd" => "{field} is not odd.", "even" => "{field} is not even.", "file-executable" => "{field} is no file and/or not executable.", "file-writable" => "{field} is no file and/or not writable.", "file-readable" => "{field} is no file and/or not readable.", "dir-writable" => "{field} is no directory and/or not writable.", "dir-readable" => "{field} is no directory and/or not readable.", "parent-dir-writable" => "{field} has no writable parent directory.", "parent-dir-readable" => "{field} has no readable parent directory.", ); =head2 Special "or-empty" rule There is a special rule called "or-empty". If this rule occurs everywhere in the list of rules and the actual value is empty, rule checking quits immediately with a positive result, discarding error states from earlier rules. Example: [ "positive-integer", "or-empty" ] All rules are combined with "and", which is usually sufficient, but without this special "or-empty" case the common case optionally empty fields can't be done. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Loader.pm������������������������������������������0000644�0001750�0001750�00000007126�11620461523�022032� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Loader; use strict; @Gtk2::Ex::FormFactory::Button::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::CheckButtonGroup::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::CheckButton::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Combo::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::DialogButtons::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Entry::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Expander::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::ExecFlow::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Form::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::GtkWidget::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::HBox::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::HSeparator::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Image::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Label::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::List::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Menu::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Notebook::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Popup::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::ProgressBar::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::RadioButton::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Table::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::TextView::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Timestamp::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::ToggleButton::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::VBox::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::VSeparator::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::VPaned::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::HPaned::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::Window::ISA = qw( Gtk2::Ex::FormFactory::Loader ); @Gtk2::Ex::FormFactory::YesNo::ISA = qw( Gtk2::Ex::FormFactory::Loader ); sub new { my $class = shift; eval "use $class; shift \@$class:\:ISA"; if ( $@ ) { print $@; exit; } return $class->new(@_); } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Loader - Dynamic loading of FormFactory modules =head1 SYNOPSIS No synposis, internally used by Gtk2::Ex::FormFactory. =head1 DESCRIPTION This class implements dynamic loading of Gtk2::Ex::FormFactory widget classes and has no external interface. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Popup.pm�������������������������������������������0000644�0001750�0001750�00000014045�10451730234�021724� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Popup; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "popup" } sub get_items { shift->{items} } sub set_items { shift->{items} = $_[1] } sub new { my $class = shift; my %par = @_; my ($items) = $par{'items'}; my $self = $class->SUPER::new(@_); $self->set_items($items); return $self; } sub object_to_widget { my $self = shift; my $content = $self->get_items || $self->get_proxy->get_attr_list($self->get_attr, $self->get_name); my $value = $self->get_object_value; my $gtk_popup = $self->get_gtk_widget; my $gtk_popup_menu = Gtk2::Menu->new; $gtk_popup->remove_menu; $gtk_popup->set_menu($gtk_popup_menu); my ($history, $i); if ( ref $content eq 'ARRAY' ) { my ($item); $history = $i = 0; foreach my $text ( @{$content} ) { if ( ref $text eq 'ARRAY' ) { $item = Gtk2::MenuItem->new ($text->[1]); $item->show; $item->{value} = $text->[0]; $gtk_popup_menu->append($item); $history = $i if $text->[0] eq $value; } else { $item = Gtk2::MenuItem->new ($text); $item->show; $item->{value} = $i; $gtk_popup_menu->append($item); $history = $i if $i == $value; } ++$i; } } else { my (@content, $k, $v); push @content, [ $k, $v ] while ($k,$v) = each %{$content}; my ($item); $history = $i = 0; foreach my $c ( sort { $a->[1] cmp $b->[1] } @content ) { $item = Gtk2::MenuItem->new ($c->[1]); $item->show; $item->{value} = $c->[0]; $gtk_popup_menu->append($item); $history = $i if $value eq $c->[0]; ++$i; } } $gtk_popup->set_history ($history); 1; } sub widget_to_object { my $self = shift; $self->set_object_value ( $self->get_gtk_widget->get_menu->get_active->{value} ); 1; } sub get_widget_value { my $self = shift; return $self->get_gtk_widget->get_menu->get_active->{value}; } sub empty_widget { my $self = shift; my $gtk_popup = $self->get_gtk_widget; my $gtk_popup_menu = Gtk2::Menu->new; $gtk_popup->remove_menu; $gtk_popup->set_menu($gtk_popup_menu); my $item = Gtk2::MenuItem->new (" "); $item->show; $gtk_popup_menu->append($item); $gtk_popup->set_history ( 0 ); 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value ( $self->get_gtk_widget->get_history ); 1; } sub restore_widget_value { my $self = shift; $self->get_gtk_widget->set_history($self->get_backup_widget_value); 1; } sub get_widget_check_value { $_[0]->get_gtk_widget->get_menu->get_active->{value}; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->signal_connect ( changed => sub { $self->widget_value_changed }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Popup - A Popup in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Popup->new ( ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a Popup in a Gtk2::Ex::FormFactory framework. The selected entry of the Popup is controlled by the value of the associated application object attribute, which is either an index in an array of possible Popup entries or a key of a hash of possible Popup entries. Refer to the chapter REQUIREMENTS FOR ASSOCIATED APPLICATION OBJECTS for details. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Popup Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<items> = ARRAYREF|HASHREF [optional] This attribute takes a static list of popup items, if the popup shouldn't be controlled dynamically by an associated application object. Refer to the next chapter for details of the data structure applied here. =back =head1 REQUIREMENTS FOR ASSOCIATED APPLICATION OBJECTS Application objects represented by a Gtk2::Ex::FormFactory::Popup must define additional methods, unless their content is static by setting B<items>. The naming of the methods listed beyond uses the standard B<get_> prefix for the attribute read accessor. B<ATTR> needs to be replaced by the actual name of the attribute associated with the widget. =over 4 =item B<get_ATTR_list> This returns the entries of the Popup. Three data models are supported here: =over 7 =item Simple B<ARRAY> If the method returns a reference to a simple array, the popup will be filled with the array values in the original array order. The index of the actually selected popup entry is stored in the attribute of the associated application object. =item Two dimensional B<ARRAY> The method may return a reference to a two dimensional array. Each row needs to have the attribute value in the first column and the label for the corresponding item in the second. =item B<HASH> If the method returns a reference to a hash, the popup will be filled with the alphanumerically sorted hash B<values>. In turn the hash B<key> of the actually selected popup entry is stored in the attribute of the associated application object. =back =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/RadioButton.pm�������������������������������������0000644�0001750�0001750�00000006521�10412571764�023063� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::RadioButton; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "radio_button" } sub has_label { 1 } sub get_value { shift->{value} } sub set_value { shift->{value} = $_[1] } sub new { my $class = shift; my %par = @_; my ($value) = $par{'value'}; my $self = $class->SUPER::new(@_); $self->set_value($value); return $self; } sub object_to_widget { my $self = shift; $self->get_gtk_widget->set_active (1) if $self->get_object_value eq $self->get_value; 1; } sub widget_to_object { my $self = shift; if ( $self->get_gtk_widget->get_active ) { $self->set_object_value ($self->get_value); } 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value ($self->get_gtk_widget->get_active ? 1 : 0); 1; } sub restore_widget_value { my $self = shift; $self->get_gtk_widget->set_active($self->get_backup_widget_value); 1; } sub get_widget_check_value { $_[0]->get_gtk_widget->get_active ? $_[0]->get_value : undef; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->signal_connect ( "released" => sub { $self->widget_value_changed } ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::RadioButton - A RadioButton in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::RadioButton->new ( value => Value this RadioButton sets in the associated application object attribute, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a RadioButton in a Gtk2::Ex::FormFactory framework. The activation state of the RadioButton is controlled by the value of the associated application object attribute. All RadioButton's which should be grouped together need to be added to the same container resp. they must have the same parent. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::RadioButton Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<value> = SCALAR [optional] This value is transferred to the attribute of the associated application object when the RadioButton is activated. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/VPaned.pm������������������������������������������0000644�0001750�0001750�00000004454�10400606040�021770� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::VPaned; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "vpaned" } sub object_to_widget { my $self = shift; $self->get_gtk_widget->set_position ( $self->get_object_value ); 1; } sub widget_to_object { my $self = shift; $self->set_object_value ($self->get_gtk_widget->get("position")); 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value (self->get_gtk_widget->get("position")); 1; } sub restore_widget_value { my $self = shift; self->get_gtk_widget->set_position ($self->get_backup_widget_value); 1; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->signal_connect ( move_handle => sub { $self->widget_value_changed; 1; }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::VPaned - A VPaned container in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::VPaned->new ( ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a VPaned container in a Gtk2::Ex::FormFactory framework. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::VPaned Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Widget. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Layout.pm������������������������������������������0000644�0001750�0001750�00000076006�11620461466�022112� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Layout; use strict; use Gtk2::SimpleList; use Gtk2::SimpleMenu; my $DEFAULT_SPACING = 5; sub new { my $class = shift; return bless {}, $class; } sub build_widget { my $self = shift; my ($widget) = @_; return if $widget->get_type eq 'form_factory'; if ( $widget->can("build_widget") ) { $widget->build_widget; } else { my $widget_type = $widget->get_type; my $method = "build_$widget_type"; $Gtk2::Ex::FormFactory::DEBUG && print "build_widget: ".$widget->get_type. "(".$widget->get_name.")\n"; $self->$method($widget); } if ( $widget->get_properties ) { $widget->get_gtk_properties_widget->set ( %{$widget->get_properties} ); } if ( $widget->get_width or $widget->get_height ) { my $size_method = $widget->get_gtk_widget->isa("Gtk2::Window") ? "set_default_size" : "set_size_request"; $widget->get_gtk_widget->$size_method ( ($widget->get_width||-1), ($widget->get_height||-1), ); } my $tip = !$widget->isa_container ? $widget->get_tip : ""; if ( $tip ) { $tip .= "." if $tip !~ /\.\s*$/; my $gtk_tip = Gtk2::Tooltips->new; for ( @{$widget->get_gtk_tip_widgets} ) { $gtk_tip->set_tip ($_, $tip, undef); } } my $scrollbars = $widget->get_scrollbars; if ( $scrollbars ) { my $sw = Gtk2::ScrolledWindow->new; my $can_scroll = eval { $widget->get_gtk_parent_widget->get("hadjustment") }; if ( $can_scroll ) { $sw->add($widget->get_gtk_parent_widget); } else { $sw->add_with_viewport($widget->get_gtk_parent_widget); } $sw->set_policy(@{$scrollbars}); $widget->set_gtk_parent_widget($sw); } if ( $widget->get_gtk_widget and $widget->get_widget_group ) { my $group = $widget->get_widget_group; my $gtk_size_group = $widget->get_form_factory ->get_gtk_size_groups->{$group} ||= Gtk2::SizeGroup->new("horizontal"); $gtk_size_group->add_widget ($widget->get_gtk_widget); } if ( $widget->get_customize_hook ) { my $cb = $widget->get_customize_hook; &$cb($widget->get_gtk_widget, $widget); } 1; } sub add_widget_to_container { my $self = shift; my ($widget, $container) = @_; return if $container->get_type eq 'form_factory'; my $container_type = $container->get_type; my $widget_type = $widget->get_type; my $method = "add_".$widget_type."_to_".$container_type; if ( not $self->can($method) ) { $widget_type = "widget"; $method = "add_".$widget_type."_to_".$container_type; } $Gtk2::Ex::FormFactory::DEBUG && print "add widget: ". $container->get_type. "(".$container->get_name.") + ". $widget->get_type. "(".$widget->get_name.")\n"; $self->$method($widget, $container); if ( $widget->get_gtk_label_widget and $widget->get_label_group ) { my $group = $widget->get_label_group; my $gtk_size_group = $widget->get_form_factory ->get_gtk_size_groups->{$group} ||= Gtk2::SizeGroup->new("horizontal"); $gtk_size_group->add_widget ($widget->get_gtk_label_widget); } 1; } sub create_label_widget { my $self = shift; my ($widget) = @_; my $gtk_label; if ( $widget->get_label_markup ) { $gtk_label = Gtk2::Label->new; $gtk_label->set_markup($widget->get_label); } else { $gtk_label = Gtk2::Label->new($widget->get_label); } $gtk_label->set ( yalign => 0.5, xalign => 0 ); if ( $widget->get_label_for ) { my $for_widget = $widget->lookup_widget($widget->get_label_for); $for_widget->set_gtk_label_widget($gtk_label); } return $gtk_label; } sub create_bold_label_widget { my $self = shift; my ($label_text) = @_; my $gtk_label = Gtk2::Label->new; $gtk_label->set ( yalign => 0.5, xalign => 0 ); $label_text =~ s/&/&/g; $label_text =~ s/</</g; $gtk_label->set_markup("<b>$label_text </b>"); return $gtk_label; } sub build_window { my $self = shift; my ($window) = @_; my $gtk_window = Gtk2::Window->new; $gtk_window->set_title($window->get_title); $gtk_window->set_position('center'); my $vbox = Gtk2::VBox->new(0, 5); $vbox->set ( border_width => $DEFAULT_SPACING ); $gtk_window->add($vbox); $window->set_gtk_widget($vbox); $window->set_gtk_parent_widget($gtk_window); $window->set_gtk_properties_widget($gtk_window); my $closed_hook = $window->get_closed_hook; if ( $closed_hook ) { $gtk_window->signal_connect ( delete_event => $closed_hook ); } else { if ( $window->get_form_factory->get_content->[0] eq $window ) { $gtk_window->signal_connect ( delete_event => sub { $window->get_form_factory->close; Gtk2->main_quit if $window->get_quit_on_close; 1; }, ); } } if ( $window->get_parent->isa("Gtk2::Ex::FormFactory") ) { $gtk_window->signal_connect ( destroy => sub { #-- Close FormFactory. If no FormFactory #-- is set, cleanup() was already called. $window->get_form_factory->close if $window->get_form_factory; 1; }, ); } if ( $window->get_form_factory->get_parent_ff ) { my $gtk_parent_window = $window->get_form_factory ->get_parent_ff ->get_form_factory_gtk_window; $gtk_window->set_transient_for($gtk_parent_window); } 1; } sub build_menu { my $self = shift; my ($menu) = @_; my $gtk_menu = Gtk2::SimpleMenu->new ( menu_tree => $menu->get_menu_tree, default_callback => $menu->get_default_callback, user_data => $menu->get_user_data, ); $menu->set_gtk_widget($gtk_menu->{widget}); $menu->set_gtk_simple_menu($gtk_menu); 1; } sub build_block_widget { my $self = shift; my ($gtk_widget, $title) = @_; my $gtk_label = $self->create_bold_label_widget($title); my $gtk_frame = Gtk2::Frame->new; $gtk_frame->set_label_widget($gtk_label); $gtk_frame->add($gtk_widget); $gtk_widget->set ( border_width => $DEFAULT_SPACING ); return $gtk_frame } sub build_notebook { my $self = shift; my ($notebook) = @_; my $gtk_notebook = Gtk2::Notebook->new; my $title = $notebook->get_title; if ( $title ) { my $block_widget = $self->build_block_widget($gtk_notebook, $title); $notebook->set_gtk_parent_widget($block_widget); } $notebook->set_gtk_widget($gtk_notebook); 1; } sub build_expander { my $self = shift; my ($expander) = @_; my $gtk_expander = Gtk2::Expander->new ($expander->get_label); $expander->set_gtk_widget($gtk_expander); 1; } sub build_form { my $self = shift; my ($form) = @_; my $child_cnt = @{$form->get_content}; my $title = $form->get_title; my $block_widget; my $table = Gtk2::Table->new($child_cnt, 2); $table->set ( row_spacing => 1, column_spacing => 5 ); if ( $title && $form->get_parent->get_type ne 'notebook' ) { $block_widget = $self->build_block_widget($table, $title); } $form->set_gtk_widget($table); $form->set_gtk_parent_widget($block_widget); 1; } sub build_form_label_right { shift->build_form(@_); } sub build_table { my $self = shift; my ($table) = @_; my $title = $table->get_title; my $gtk_table = Gtk2::Table->new($table->get_rows, $table->get_columns); $gtk_table->set ( row_spacing => 2, column_spacing => 2 ); if ( $title ) { my $block_widget = $self->build_block_widget($gtk_table, $title); $table->set_gtk_parent_widget($block_widget); } $table->set_gtk_widget($gtk_table); my $layout_widget_cnt = @{$table->get_widget_table_attach}; my $content_widget_cnt = @{$table->get_content}; if ( $layout_widget_cnt != $content_widget_cnt ) { die "Table ".$table->get_name.": layout defines ". "$layout_widget_cnt widgets, but the table contains ". "$content_widget_cnt widgets"; } 1; } sub build_vpaned { my $self = shift; my ($vpaned) = @_; my $gtk_vpaned = Gtk2::VPaned->new; $gtk_vpaned->set ( position_set => 1 ); $vpaned->set_gtk_widget($gtk_vpaned); 1; } sub build_hpaned { my $self = shift; my ($hpaned) = @_; my $gtk_hpaned = Gtk2::HPaned->new; $gtk_hpaned->set ( position_set => 1 ); $hpaned->set_gtk_widget($gtk_hpaned); 1; } sub build_vbox { my $self = shift; my ($vbox) = @_; my $title = $vbox->get_title; my $block_widget; my $gtk_vbox = Gtk2::VBox->new($vbox->get_homogenous,($vbox->get_spacing||$DEFAULT_SPACING)); if ( $title and not $vbox->get_no_frame ) { $block_widget = $self->build_block_widget($gtk_vbox, $title); } $vbox->set_gtk_widget($gtk_vbox); $vbox->set_gtk_parent_widget($block_widget); 1; } sub build_hbox { my $self = shift; my ($hbox) = @_; my $title = $hbox->get_title; my $block_widget; my $gtk_hbox = Gtk2::HBox->new($hbox->get_homogenous,($hbox->get_spacing||$DEFAULT_SPACING)); if ( $title and not $hbox->get_no_frame ) { $block_widget = $self->build_block_widget($gtk_hbox, $title); } $hbox->set_gtk_widget($gtk_hbox); $hbox->set_gtk_parent_widget($block_widget); 1; } sub build_label { my $self = shift; my ($label) = @_; my $gtk_label = Gtk2::Label->new; $gtk_label->set_text ($label->get_label) if $label->get_label && !$label->get_attr; $gtk_label->set_markup ($label->get_label) if $label->get_with_markup; $gtk_label->set_markup ("<b>".$label->get_label."</b>") if $label->get_label && $label->get_bold; $gtk_label->set ( xalign => 0, yalign => 0.5 ); $label->set_gtk_widget($gtk_label); if ( $label->get_for ) { my $for_widget = $label->lookup_widget($label->get_for); $for_widget->set_gtk_label_widget($gtk_label); } if ( $label->get_label_group ) { my $group = $label->get_label_group; my $gtk_size_group = $label->get_form_factory ->get_gtk_size_groups->{$group} ||= Gtk2::SizeGroup->new("horizontal"); $gtk_size_group->add_widget ($gtk_label); } 1; } sub build_hseparator { my $self = shift; my ($hseparator) = @_; my $gtk_sep = Gtk2::HSeparator->new; $gtk_sep->set ( height_request => 10 ); $hseparator->set_gtk_widget($gtk_sep); 1; } sub build_vseparator { my $self = shift; my ($vseparator) = @_; my $gtk_sep = Gtk2::VSeparator->new; $gtk_sep->set ( width_request => 10 ); $vseparator->set_gtk_widget($gtk_sep); 1; } sub build_entry { my $self = shift; my ($entry) = @_; my $gtk_entry = Gtk2::Entry->new; $entry->set_gtk_widget($gtk_entry); 1; } sub build_combo { my $self = shift; my ($combo) = @_; my $gtk_combo = Gtk2::Combo->new; $combo->set_gtk_widget($gtk_combo); 1; } sub build_check_button { my $self = shift; my ($check_button) = @_; my $gtk_check_button = Gtk2::CheckButton->new; $gtk_check_button->set_label($check_button->get_label) if $check_button->has_label; $check_button->set_gtk_widget($gtk_check_button); 1; } sub build_yesno { my $self = shift; my ($yesno) = @_; my $gtk_yes_radio = Gtk2::RadioButton->new_with_label( undef, $yesno->get_true_label ); my $gtk_no_radio = Gtk2::RadioButton->new_with_label( $gtk_yes_radio->get_group, $yesno->get_false_label ); my $hbox = Gtk2::HBox->new; $hbox->pack_start($gtk_yes_radio, 0, 1, 0); $hbox->pack_start($gtk_no_radio, 0, 1, 0); $yesno->set_gtk_parent_widget($hbox); $yesno->set_gtk_widget($gtk_yes_radio); $yesno->set_gtk_yes_widget($gtk_yes_radio); $yesno->set_gtk_no_widget($gtk_no_radio); 1; } sub build_radio_button { my $self = shift; my ($radio_button) = @_; my $group_name = $radio_button->get_object.".". $radio_button->get_attr; my $gtk_radio_group = $radio_button->get_parent->{_radio_group}->{$group_name}; my $gtk_radio_button = Gtk2::RadioButton->new_with_label( $gtk_radio_group, $radio_button->get_label ); $radio_button->get_parent->{_radio_group}->{$group_name} ||= $gtk_radio_button->get_group; $radio_button->set_gtk_widget($gtk_radio_button); 1; } sub build_toggle_button { my $self = shift; my ($toggle_button) = @_; return $self->build_button($toggle_button, "Gtk2::ToggleButton"); } sub build_button { my $self = shift; my ($button, $gtk_button_class) = @_; $gtk_button_class ||= "Gtk2::Button"; my $stock = $button->get_stock; my $label = $button->get_label; my $image = $button->get_image; my $gtk_button; #-- Button with image if ( $image ) { my $image = Gtk2::Image->new_from_file($image); $gtk_button = $gtk_button_class->new; $gtk_button->add($image); } #-- Button with stock and text elsif ( $stock and $label ne '' ) { my $hbox = Gtk2::HBox->new; my $image = Gtk2::Image->new_from_stock($stock,"small-toolbar"); my $gtk_label = Gtk2::Label->new($label); $hbox->pack_start($image, 0, 1, 0); $hbox->pack_start($gtk_label, 0, 1, 0); $gtk_button = $gtk_button_class->new; my $align = Gtk2::Alignment->new (0.5, 0.5, 0, 0); $align->add($hbox); $gtk_button->add($align); } #-- Button with stock and empty text elsif ( $stock and defined $label ) { my $image = Gtk2::Image->new_from_stock($stock,"small-toolbar"); $gtk_button = $gtk_button_class->new; $gtk_button->add($image); } #-- Default Stock button (with default text) elsif ( $stock and not $label ) { $gtk_button = $gtk_button_class->new_from_stock($stock); } #-- Simple text button else { $gtk_button = $gtk_button_class->new($label); } $button->set_gtk_widget($gtk_button); my $with_repeat = $button->can("get_with_repeat") && $button->get_with_repeat; my $clicked_hook = $button->get_clicked_hook; if ( $clicked_hook && !$with_repeat ) { $gtk_button->signal_connect ( clicked => sub { $clicked_hook->($button) } ); } if ( $clicked_hook && $with_repeat ) { my $button_pressed; my $timeout; $gtk_button->signal_connect ( button_press_event => sub { $clicked_hook->($button); $button_pressed = 1; $timeout = Glib::Timeout->add ( 80, sub { return 0 if $button_pressed == 0; ++$button_pressed; $clicked_hook->($button) if $button_pressed > 5; return 1; } ); 0; }); $gtk_button->signal_connect ( button_release_event => sub { Glib::Source->remove($timeout); $button_pressed = 0; 0; }); } 1; } sub build_list { my $self = shift; my ($list) = @_; my $columns = $list->get_columns; my $types = $list->get_types; my $editable = $list->get_editable; my $visible = $list->get_visible; my $no_header = $list->get_no_header; my (@slist, $i); foreach my $col ( @{$columns} ) { push @slist, $col, ($types->[$i]||"text"); ++$i; } my $slist = Gtk2::SimpleList->new ( @slist ); if ( $editable ) { $i = 0; foreach my $e ( @{$editable} ) { $slist->set_column_editable($i, $e); ++$i; } } if ( $visible ) { $i = 0; foreach my $v ( @{$visible} ) { $slist->get_column($i)->set_visible($v); ++$i; } } if ( $no_header ) { $slist->set_headers_visible(0); } $slist->get_selection->set_mode ($list->get_selection_mode) if $list->get_selection_mode; $list->set_gtk_widget($slist); 1; } sub build_popup { my $self = shift; my ($popup) = @_; my $gtk_popup_menu = Gtk2::Menu->new; my $gtk_popup = Gtk2::OptionMenu->new; $gtk_popup->set_menu($gtk_popup_menu); $popup->set_gtk_widget ( $gtk_popup ); 1; } sub build_progress_bar { my $self = shift; my ($progress_bar) = @_; my $gtk_progress_bar = Gtk2::ProgressBar->new; $progress_bar->set_gtk_widget($gtk_progress_bar); 1; } sub build_image { my $self = shift; my ($image) = @_; my $gtk_image = Gtk2::Image->new; $gtk_image->set_size_request(undef, undef); $image->set_gtk_widget($gtk_image); my $bgcolor = $image->get_bgcolor; my $gtk_event_box = Gtk2::EventBox->new; $gtk_event_box->modify_bg ("normal", Gtk2::Gdk::Color->parse ($bgcolor)) if defined $bgcolor; $gtk_event_box->add($gtk_image); $image->set_gtk_event_box($gtk_event_box); if ( $image->get_with_frame ) { my $gtk_frame = Gtk2::Frame->new; $gtk_frame->add($gtk_event_box); $image->set_gtk_parent_widget($gtk_frame); } else { $image->set_gtk_parent_widget($gtk_event_box); } my $update_timeout; if ( $image->get_scale_to_fit or $image->get_max_width or $image->get_max_height ) { $gtk_event_box->signal_connect ( "size-allocate" => sub { return if $image->get_widget_width == $_[1]->width and $image->get_widget_height == $_[1]->height; $image->set_widget_width($_[1]->width); $image->set_widget_height($_[1]->height); Glib::Source->remove($update_timeout) if $update_timeout; $update_timeout = Glib::Timeout->add ( 100, sub { $image->update; $update_timeout = undef; 0 } ); 0; } ); } 1; } sub build_dialog_buttons { my $self = shift; my ($dialog_buttons) = @_; my ($button_box, $button); $button_box = Gtk2::HButtonBox->new; $button_box->set ( layout_style => "end", spacing => 10, ); my $buttons = $dialog_buttons->get_buttons; my $form_factory = $dialog_buttons->get_form_factory; if ( !$form_factory->get_sync || $form_factory->get_buffered ) { if ( $buttons->{cancel} ) { $button = Gtk2::Button->new_from_stock("gtk-cancel"); $button->show; $button_box->pack_start($button, 0, 1, 0); $button->signal_connect ( clicked => sub { $dialog_buttons->cancel_button_clicked; }, ); $dialog_buttons->set_gtk_cancel_button($button); } if ( $buttons->{apply} ) { $button = Gtk2::Button->new_from_stock("gtk-apply"); $button->show; $button_box->pack_start($button, 0, 1, 0); $button->signal_connect ( clicked => sub { $dialog_buttons->apply_button_clicked; }, ); $dialog_buttons->set_gtk_apply_button($button); } } if ( $buttons->{ok} ) { $button = Gtk2::Button->new_from_stock("gtk-ok"); $button->show; $button_box->pack_start($button, 0, 1, 0); $button->signal_connect ( clicked => sub { $dialog_buttons->ok_button_clicked; }, ); $dialog_buttons->set_gtk_ok_button($button); } $dialog_buttons->set_gtk_widget($button_box); 1; } sub build_timestamp { my $self = shift; my ($timestamp) = @_; my $hbox = Gtk2::HBox->new; my $mday = Gtk2::Entry->new; my $mon = Gtk2::Entry->new; my $year = Gtk2::Entry->new; my $hour = Gtk2::Entry->new; my $min = Gtk2::Entry->new; $mday->set ( width_chars => 2, max_length => 2 ); $mon->set ( width_chars => 2, max_length => 2 ); $year->set ( width_chars => 4, max_length => 4 ); $hour->set ( width_chars => 2, max_length => 2 ); $min->set ( width_chars => 2, max_length => 2 ); $timestamp->set_gtk_widget($hbox); $timestamp->set_gtk_mday_widget($mday); $timestamp->set_gtk_mon_widget($mon); $timestamp->set_gtk_year_widget($year); $timestamp->set_gtk_hour_widget($hour); $timestamp->set_gtk_min_widget($min); my $format = $timestamp->get_format; while ( $format =~ /([^%]*)%(.)/g ) { my $text = $1; my $dfmt = $2; if ( $text ) { $hbox->pack_start(Gtk2::Label->new($text), 0, 1, 0); } if ( $dfmt eq 'd' ) { $hbox->pack_start ($mday, 0, 1, 0); } elsif ( $dfmt eq 'm' ) { $hbox->pack_start ($mon, 0, 1, 0); } elsif ( $dfmt eq 'Y' ) { $hbox->pack_start ($year, 0, 1, 0); } elsif ( $dfmt eq 'k' ) { $hbox->pack_start ($hour, 0, 1, 0); } elsif ( $dfmt eq 'M' ) { $hbox->pack_start ($min, 0, 1, 0); } else { warn "Unknown timestamp format \%$dfmt ignored"; } } 1; } sub build_gtk_widget { my $self = shift; my ($gtk_widget) = @_; $gtk_widget->set_gtk_widget($gtk_widget->get_custom_gtk_widget); 1; } sub build_check_button_group { my $self = shift; my ($check_button_group) = @_; my $hbox = Gtk2::HBox->new; $check_button_group->set_gtk_widget($hbox); 1; } sub build_text_view { my $self = shift; my ($text_view) = @_; my $gtk_text_view = Gtk2::TextView->new; $text_view->set_gtk_widget($gtk_text_view); 1; } sub add_widget_to_form { my $self = shift; my ($widget, $form) = @_; my $row = $form->get_layout_data->{row} || 0; my $gtk_table = $form->get_gtk_widget; my $gtk_widget = $widget->get_gtk_parent_widget; my $xopt = $widget->get_expand_h ? ['fill','expand'] : ['fill']; my $yopt = $widget->get_expand_v ? ['fill','expand'] : ['fill']; if ( (! defined $widget->get_label || $widget->get_label ne '') and not $widget->has_label ) { my $gtk_label = $self->create_label_widget ($widget); $widget->set_gtk_label_widget ($gtk_label); if ( $form->get_label_top_align ) { my $gtk_align = Gtk2::Alignment->new(0, 0, 0, 0); $gtk_align->add($gtk_label); $gtk_label = $gtk_align; } $gtk_table->attach($gtk_label, 0, 1, $row, $row+1, 'fill', 'fill', 0, 0); } if ( not $widget->get_expand_h ) { my $hbox = Gtk2::HBox->new; $hbox->pack_start($gtk_widget, 0, 1, 0); $gtk_widget = $hbox; } $gtk_table->attach($gtk_widget, 1, 2, $row, $row+1, $xopt, $yopt, 0, 0); $form->get_layout_data->{row}++; 1; } sub add_widget_to_form_label_right { my $self = shift; my ($widget, $form) = @_; my $row = $form->get_layout_data->{row} || 0; my $gtk_table = $form->get_gtk_widget; my $gtk_entry = $widget->get_gtk_parent_widget; $gtk_table->attach_defaults($gtk_entry, 0, 1, $row, $row+1); if ( (! defined $widget->get_label || $widget->get_label ne '') and not $widget->has_label ) { my $gtk_label = $self->create_label_widget ($widget); $gtk_table->attach($gtk_label, 1, 2, $row, $row+1, 'fill', [], 0, 0); $widget->set_gtk_label_widget ($gtk_label); } $form->get_layout_data->{row}++; 1; } sub add_widget_to_table { my $self = shift; my ($widget, $table) = @_; my $child_idx = $table->get_layout_data->{child_idx} || 0; my $table_attach = $table->get_widget_table_attach->[$child_idx]; my $table_align = $table->get_widget_table_align->[$child_idx]; my $gtk_widget = $table->get_content->[$child_idx]->get_gtk_parent_widget; if ( defined $table_align->{xalign} or defined $table_align->{yalign} ) { my $xalign = $table_align->{xalign}; my $yalign = $table_align->{yalign}; my $xscale = $xalign == -1 ? 1 : 0; my $yscale = $yalign == -1 ? 1 : 0; $xalign = 0 if $xalign == -1; $yalign = 0 if $yalign == -1; my $gtk_align = Gtk2::Alignment->new($xalign, $yalign, $xscale, $yscale); $gtk_align->add($gtk_widget); $gtk_widget = $gtk_align; } $table->get_gtk_widget->attach( $gtk_widget, @{$table_attach}, 0, 0 ); $table->get_layout_data->{child_idx}++; 1; } sub add_widget_to_vpaned { my $self = shift; my ($widget, $vpaned) = @_; my $gtk_vpaned = $vpaned->get_gtk_widget; if ( $gtk_vpaned->get_child1 ) { $gtk_vpaned->add2($widget->get_gtk_parent_widget); } else { $gtk_vpaned->add1($widget->get_gtk_parent_widget); } 1; } sub add_widget_to_hpaned { my $self = shift; my ($widget, $hpaned) = @_; return $self->add_widget_to_vpaned($widget, $hpaned); } sub add_widget_to_vbox { my $self = shift; my ($widget, $vbox) = @_; my $gtk_vbox = $vbox->get_gtk_widget; my $gtk_widget = $widget->get_gtk_parent_widget; if ( $widget->get_label ne '' and not $widget->has_label ) { my $gtk_label = $self->create_label_widget ($widget); $gtk_vbox->pack_start($gtk_label, 0, 1, 0); $widget->set_gtk_label_widget ($gtk_label); } $gtk_vbox->pack_start($gtk_widget, $widget->get_expand, 1, 0); 1; } sub add_widget_to_hbox { my $self = shift; my ($widget, $hbox) = @_; my $gtk_hbox = $hbox->get_gtk_widget; my $gtk_widget = $widget->get_gtk_parent_widget; if ( $widget->get_label ne '' and not $widget->has_label ) { my $gtk_label = $self->create_label_widget ($widget); $gtk_hbox->pack_start($gtk_label, 0, 1, 0); $widget->set_gtk_label_widget ($gtk_label); } $gtk_hbox->pack_start($gtk_widget, $widget->get_expand, 1, 0); 1; } sub add_widget_to_expander { my $self = shift; my ($widget, $expander) = @_; $expander->get_gtk_widget->add ($widget->get_gtk_parent_widget); 1; } sub add_widget_to_notebook { my $self = shift; my ($widget, $notebook) = @_; $widget->get_gtk_parent_widget->set ( border_width => $DEFAULT_SPACING ); my $label; if ( $widget->get_title =~ /^\[([^\]]+)\]\s*(.*)$/ ) { my ($stock, $title) = ($1, $2); my $hbox = Gtk2::HBox->new; my $image = Gtk2::Image->new_from_stock($stock,"small-toolbar"); my $gtk_label = Gtk2::Label->new($title); $hbox->pack_start($image, 0, 1, 0); $hbox->pack_start($gtk_label, 0, 1, 0); $label = $hbox; $label->show_all; } else { $label = Gtk2::Label->new($widget->get_title); } $widget->set_gtk_label_widget($label); $notebook->get_gtk_widget->append_page( $widget->get_gtk_parent_widget, $label ); 1; } sub add_widget_to_window { my $self = shift; my ($widget, $window) = @_; my $vbox = $window->get_gtk_widget; $vbox->pack_start( $widget->get_gtk_parent_widget, $widget->get_expand, 1, 0 ); 1; } sub add_menu_to_window { my $self = shift; my ($menu, $window) = @_; my $gtk_window_vbox = $window->get_gtk_widget; my $gtk_window = $window->get_gtk_parent_widget; my $gtk_menu_vbox = Gtk2::VBox->new(0,0); $gtk_menu_vbox->pack_start($menu->get_gtk_parent_widget, 0, 1, 0); $gtk_window->remove($gtk_window_vbox); $gtk_window->add($gtk_menu_vbox); $gtk_menu_vbox->pack_start($gtk_window_vbox, 1, 1, 0); $gtk_window->add_accel_group( $menu->get_gtk_simple_menu->{accel_group} ); 1; } sub add_buttons_to_window { my $self = shift; my ($buttons, $window) = @_; my $vbox = $window->get_gtk_widget; $vbox->pack_start($buttons->get_gtk_parent_widget, 0, 1, 0); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Layout - Do layout in a FormFactory framework =head1 SYNOPSIS package My::Layout; use base qw/Gtk2::Ex::FormFactory::Layout/; sub build_form { ... } sub add_widget_to_form { ... } ... package main; $ff = Gtk2::Ex::FormFactory->new ( layouter => My::Layout->new(), ... ); =head1 DESCRIPTION This class implements the layout of Containers and their Widgets in a Gtk2::Ex::FormFactory framework. "Layout" means, how are the widgets aligned to each other, how much space is between them, how are titles rendered, how labels, etc. The idea behind Gtk2::Ex::FormFactory::Layout is to have a unique point in a GUI application which actually implements these things. The advantage of this approach is obvious: the implementation is very generic and if you want to change layout things you subclass from Gtk2::Ex::FormFactory::Layout and implement your changes there, and not at hundreds of spots distributed over the source code of your application. The natural result: a consistent looking GUI. =head1 SUBCLASSING As described above implementing your own layout module starts with subclassing from Gtk2::Ex::FormFactory::Layout. To use your layout implementation set an object of your class as B<layouter> in your Gtk2::Ex::FormFactory objects. Gtk2::Ex::FormFactory::Layout mainly defines two sorts of methods. =head2 BUILD METHODS The names of the methods are derived from the Widget's short names (which can be retrieved with $widget->get_type), with a prepended B<build_>, e.g.: build_form ( ... ) build_label ( ... ) build_table ( ... ) The method prototype looks like this: =over 4 =item $layout->B<build_TYPE> ($widget) B<$widget> is the actual Gtk2::Ex::FormFactory::Widget, e.g. Gtk2::Ex::FormFactory::Form for B<build_form>($form). =back The B<build_TYPE> method actually creates the necessary Gtk2 widgets, e.g. a Gtk2::Table for a Gtk2::Ex::FormFactory::Form and adds these to the FormFactory's widget instance using the B<set_gtk_widget>() and B<set_gtk_parent_widget>() methods of Gtk2::Ex::FormFactory::Widget. Call $widget->B<set_gtk_widget>($gtk_widget) for the primary Gtk2 widget which directly displays the value in question, e.g. a Gtk2::Entry if you're dealing with a Gtk2::Ex::FormFactory::Entry. If you like to do more layout things which require to add the primary Gtk2 widget to a container, e.g. a Gtk2::Frame, you must call $widget->B<set_gtk_parent_widget>($gtk_parent_widget) with the most top level container widget. B<Note:> the implemenations of all the FormFactory's widgets expect a specific B<gtk_widget> to be set. If you like to change the primary Gtk widget you need to create your own Gtk2::Ex::FormFactory::Widget for this, because the default implemention most probably won't work with a another Gtk2::Widget. =head2 ADD...TO... METHODS The second type of methods are so called add-to methods, which place a widget inside a container. The prototye is as follows: =over 4 =item $layout->B<add_TYPE_to_TYPE> ($widget, $container) B<$widget> is the actual Gtk2::Ex::FormFactory::Widget, e.g. Gtk2::Ex::FormFactory::Form for B<build_form>($form). =back Examples: add_form_to_window ( ... ) add_table_to_form ( ... ) This way you can adjust layout at a very detailed level, but you need not. E.g. the implementation of these methods is most likely the same: add_entry_to_form ( ... ) add_popup_to_form ( ... ) because the implemenation mainly depends on the B<form> (the container widget) and not on the widget which is added to the form. That's why Gtk2::Ex::FormFactory::Layout knows a default mechanism: if no add-to method is found for a specific widget/container pair, a generic default implementation is used instead. These are named as follows: add_widget_to_window ( ... ) add_widget_to_form ( ... ) add_widget_to_table ( ... ) add_widget_to_vbox ( ... ) ... For a new Container you just need to implement the generic B<add_widget_to_TYPE> method, and everything will work. If you want to slightly modify the implementation for specific child widgets, you implement only the methods for these and you're done. For a example for such a specific add-to message refer to B<add_menu_to_window>() which attaches the menu without any space around it. The default of a Gtk2::Ex::FormFactory::Window is to have some spacing, which looks ugly around a menu. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Layout =head1 ATTRIBUTES This class has not attributes. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/List.pm��������������������������������������������0000644�0001750�0001750�00000026662�10400606040�021533� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::List; use strict; use Carp; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "list" } sub get_attr_select { shift->{attr_select} } sub get_attr_select_column { shift->{attr_select_column} } sub get_update_selection_only { shift->{update_selection_only} } sub get_columns { shift->{columns} } sub get_types { shift->{types} } sub get_visible { shift->{visible} } sub get_editable { shift->{editable} } sub get_selection_mode { shift->{selection_mode} } sub get_is_editable { shift->{is_editable} } sub get_selection_backup { shift->{selection_backup} } sub get_no_header { shift->{no_header} } sub get_last_applied_selection { shift->{last_applied_selection} } sub set_attr_select { shift->{attr_select} = $_[1] } sub set_attr_select_column { shift->{attr_select_column} = $_[1] } sub set_update_selection_only { shift->{update_selection_only}= $_[1] } sub set_columns { shift->{columns} = $_[1] } sub set_types { shift->{types} = $_[1] } sub set_visible { shift->{visible} = $_[1] } sub set_editable { shift->{editable} = $_[1] } sub set_selection_mode { shift->{selection_mode} = $_[1] } sub set_is_editable { shift->{is_editable} = $_[1] } sub set_selection_backup { shift->{selection_backup} = $_[1] } sub set_no_header { shift->{no_header} = $_[1] } sub set_last_applied_selection { shift->{last_applied_selection}= $_[1]} sub has_additional_attrs { [ "select" ] } sub get_selected_rows { my $self = shift; my @sel = $self->get_gtk_widget->get_selected_indices; return \@sel; } sub get_data { my $self = shift; return $self->get_gtk_widget->{data}; } sub select_row_by_attr { my $self = shift; my ($attr_value) = @_; my $column = $self->get_attr_select_column; my $data = $self->get_data; for ( my $i=0; $i < @{$data}; ++$i ) { if ( $data->[$i][$column] eq $attr_value ) { $self->get_gtk_widget->select($i); last; } } 1; } sub new { my $class = shift; my %par = @_; my ($attr_select, $columns, $types, $editable, $visible) = @par{'attr_select','columns','types','editable','visible'}; my ($update_selection_only, $selection_mode) = @par{'update_selection_only','selection_mode'}; my ($attr_select_column, $no_header) = @par{'attr_select_column','no_header'}; croak "'columns' attribute is mandatory" unless $columns; my $self = $class->SUPER::new(@_); $self->set_attr_select ($attr_select); $self->set_attr_select_column ($attr_select_column); $self->set_columns ($columns); $self->set_visible ($visible); $self->set_types ($types); $self->set_editable ($editable); $self->set_selection_mode ($selection_mode); $self->set_update_selection_only($update_selection_only); $self->set_no_header ($no_header); my $is_editable = 0; map { $is_editable = 1 if $_ } @{$editable}; $self->set_is_editable($is_editable); return $self; } sub object_to_widget { my $self = shift; my $object_value = $self->get_object_value || []; my $slist_was_empty = ! scalar(@{$self->get_gtk_widget->{data}}); if ( not $self->get_update_selection_only ) { $self->get_gtk_widget ->set_data_array($object_value); } else { $self->get_gtk_widget ->set_data_array($object_value) if @{$self->get_gtk_widget->{data}} == 0; } if ( $self->get_attr_select ) { my $proxy = $self->get_proxy; my $idx = $proxy->get_attr ( $self->get_attr_select ); my $sel; $sel = join("\t",@{$idx}) if $idx; $self->set_last_applied_selection($sel); my $gtk_simple_list = $self->get_gtk_widget; if ( defined $idx and @{$idx} and @{$self->get_gtk_widget->{data}} != 0 ) { if ( defined $self->get_attr_select_column ) { my $i = 0; my $col = $self->get_attr_select_column; my $data = $self->get_gtk_widget->{data}; my %col2idx = map { ($_->[$col], $i++) } @{$data}; my @idx = map { $col2idx{$_} } @{$idx}; $idx = \@idx; } if ( @{$idx} && defined $idx->[0] ) { $gtk_simple_list->select(@{$idx}); $gtk_simple_list->scroll_to_cell( Gtk2::TreePath->new_from_string($idx->[0]), ($gtk_simple_list->get_columns)[0], 0, 0 ) if $slist_was_empty; } else { $gtk_simple_list->get_selection->unselect_all; $proxy->set_attr($self->get_attr_select, undef); } } else { $gtk_simple_list->get_selection->unselect_all; } Glib::Idle->add (sub { $self->widget_selection_to_object; 0; }); } 1; } sub widget_to_object { my $self = shift; if ( $self->get_is_editable ) { my $data = $self->get_gtk_widget->{data}; my @value = @{$data}; $self->set_object_value (\@value); } $self->widget_selection_to_object if $self->get_attr_select; 1; } sub widget_selection_to_object { my $self = shift; return 1 if ! $self->get_proxy->get_object($self->get_object); my @sel = $self->get_gtk_widget->get_selected_indices; if ( defined $self->get_attr_select_column ) { my $column = $self->get_attr_select_column; my $data = $self->get_gtk_widget->{data}; $_ = $data->[$_][$column] for @sel; } my $sel = join("\t",@sel); return if $sel eq $self->get_last_applied_selection; $self->set_last_applied_selection($sel); $self->get_proxy->set_attr ( $self->get_attr_select, \@sel ); 1; } sub empty_widget { my $self = shift; $self->get_gtk_widget->set_data_array([]); $self->get_gtk_widget->get_selection->unselect_all; 1; } sub backup_widget_value { my $self = shift; if ( $self->get_is_editable ) { my $data = $self->get_gtk_widget->{data}; my @value = @{$data}; $self->set_backup_widget_value (\@value); } if ( $self->get_attr_select ) { my @sel = $self->get_gtk_widget->get_selected_indices; $self->set_selection_backup(\@sel); } 1; } sub restore_widget_value { my $self = shift; if ( $self->get_is_editable ) { $self->get_gtk_widget ->set_data_array($self->get_backup_widget_value||[]); } if ( $self->get_attr_select ) { my $idx = $self->get_selection_backup; $self->get_gtk_widget->select(@{$idx}); } 1; } sub get_widget_check_value { $_[0]->get_gtk_widget->{data}; } sub connect_changed_signal { my $self = shift; if ( $self->get_is_editable ) { $self->get_gtk_widget->get_model->signal_connect ( 'row-changed' => sub { $self->widget_value_changed }, ); } if ( $self->get_attr_select ) { $self->get_gtk_widget->get_selection->signal_connect ( 'changed' => sub { $self->widget_value_changed }, ); } 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::List - A List in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::List->new ( attr_select => Attribute name for selection tracking, attr_select_column => Use this column's value to store in attr_select columns => Titles of the list columns, types => Types of the list columns, editable => Which columns are editable?, visible => Which columns are visible? selection_mode => Selection mode of this list, no_header => Omit header? update_selection_only => Boolean, whether updates should only change the selection, not the list of values, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a List in a Gtk2::Ex::FormFactory framework (based on Gtk2::Ex::Simple::List). The value of the associated application object attribute needs to be a reference to a two dimensional array with the content of the list. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::List Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<attr_select> = SCALAR [optional] If you want to track the selection state of the List set the name of the attribute of the associated application object here. An array reference with the indicies of the selected rows (or specific column values if B<attr_select_column> is set) will be managed automatically and stored in this attribute. =item B<attr_select_column> Normally indicies of the selected rows are stored in the attribute passed with B<attr_select>. Specify a column number here and the corresponding values will be stored instead (e.g. an internal database ID of an invisible column). If you use this you may use the B<select_row_by_attr()> method as well, which is described below. =item B<columns> = ARRAYREF [mandatory] This is a reference to an array containing the column titles of this list. =item B<types> = ARRAYREF [optional] You may define types for the columns of the list. The type of a column defaults to 'text'. Other possible types are: text normal text strings markup pango markup strings int integer values double double-precision floating point values bool boolean values, displayed as toggle-able checkboxes scalar a perl scalar, displayed as a text string by default pixbuf a Gtk2::Gdk::Pixbuf =item B<editable> = ARRAYREF [optional] This an array reference of boolean values, one value for each column. Changes to columns marked editable are synchronized automatically with the associated application object attribute. =item B<visible> = ARRAYREF [optional] This an array reference of boolean values, one value for each column and controls the visibility of the corresponding columns. Default is to display all columns. =item B<selection_mode> = 'none'|'single'|'browse'|'multiple' [optional] You may specify a selection mode for the list. Please refer to the Gtk+ documentation of GtkSelectionMode for details about the possible selection modes. =item B<update_selection_only> = BOOL [optional] If you know the values of your list don't change at runtime, and only the actual selection is important, you should set this to a true value, because updating will be significantly faster, since only the actual selection is affected. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 METHODS =over 4 =item $rows = $widget->B<get_selected_rows> () Returns a list reference of selected row indicies. =item $data_lref = $widget->B<get_data> () Returns the data array of the underlying Gtk2::SimpleList. It's a two dimensional array of rows and columns. All manipulations affect the GUI immediately but bypasses all Gtk2::Ex::FormFactory automatic object value update magic, so be careful with this. =item $widget->B<select_row_by_attr> ($value) Selects a row by a given B<select_attr> attribute value. Works only if B<select_attr> is set for this list. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Expander.pm����������������������������������������0000644�0001750�0001750�00000005313�10400606040�022354� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Expander; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "expander" } sub has_label { 1 } sub object_to_widget { my $self = shift; $self->get_gtk_widget->set_expanded($self->get_object_value); 1; } sub widget_to_object { my $self = shift; $self->set_object_value ($self->get_gtk_widget->get_expanded); 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value ($self->get_gtk_widget->get_expanded); 1; } sub restore_widget_value { my $self = shift; $self->get_gtk_widget->set_expanded($self->get_backup_widget_value); 1; } sub get_widget_check_value { $_[0]->get_gtk_widget->get_expanded; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->signal_connect ( activate => sub { $self->widget_value_changed }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Expander - An Expander in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Expander->new ( ... Gtk2::Ex::FormFactory::Container attributes Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements an Expander in a Gtk2::Ex::FormFactory framework. The expansion state is controlled by the associated application object attribute, which should has a boolean value. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container +--- Gtk2::Ex::FormFactory::Expander Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Container, but some special notes apply: =over 4 =item B<content> = ARRAYREF of Gtk2::Ex::FormFactory::Widget's [optional] You may add only one child widget to this container. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/YesNo.pm�������������������������������������������0000644�0001750�0001750�00000010031�10400606040�021634� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::YesNo; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "yesno" } sub get_gtk_yes_widget { shift->{gtk_yes_widget} } sub get_gtk_no_widget { shift->{gtk_no_widget} } sub get_true_label { shift->{true_label} || "Yes" } sub get_false_label { shift->{false_label} || "No" } sub set_gtk_yes_widget { shift->{gtk_yes_widget} = $_[1] } sub set_gtk_no_widget { shift->{gtk_no_widget} = $_[1] } sub set_true_label { shift->{true_label} = $_[1] } sub set_false_label { shift->{false_label} = $_[1] } sub get_gtk_tip_widgets {[ $_[0]->get_gtk_yes_widget, $_[0]->get_gtk_no_widget, ]} sub new { my $class = shift; my %par = @_; my ($true_label, $false_label) = @par{'true_label','false_label'}; my $self = $class->SUPER::new(@_); $self->set_true_label($true_label); $self->set_false_label($false_label); return $self; } sub cleanup { my $self = shift; $self->SUPER::cleanup(@_); $self->set_gtk_yes_widget(undef); $self->set_gtk_no_widget(undef); 1; } sub object_to_widget { my $self = shift; if ( $self->get_object_value ) { $self->get_gtk_yes_widget->set_active(1); } else { $self->get_gtk_no_widget->set_active(1); } 1; } sub widget_to_object { my $self = shift; $self->set_object_value ($self->get_gtk_yes_widget->get_active ? 1 : 0); 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value ($self->get_gtk_yes_widget->get_active ? 1 : 0); 1; } sub restore_widget_value { my $self = shift; $self->get_gtk_yes_widget->set_active($self->get_backup_widget_value); 1; } sub get_widget_check_value { $_[0]->get_gtk_yes_widget->get_active; } sub connect_changed_signal { my $self = shift; $self->get_gtk_yes_widget->signal_connect ( toggled => sub { $self->widget_value_changed }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::YesNo - Yes/No radio buttons in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::YesNo->new ( true_label => Label of the "Yes" button, false_label => Label of the "No" button, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements two RadioButton's grouped together as a Yes and a No button. The state of both RadioButton's is controlled by one associated application object attribute, which should has a boolean value. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::YesNo Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<true_label> = SCALAR [optional] This is the label of the button which is activated when the associated application object attribute is TRUE. Defaults to "Yes". =item B<false_label> = SCALAR [optional] This is the label of the button which is activated when the associated application object attribute is FALSE. Defaults to "No". =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Widget.pm������������������������������������������0000644�0001750�0001750�00000134642�11620730034�022047� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Widget; use strict; use Carp; use Scalar::Util qw(weaken); my $NAME_CNT = 0; my %WIDGET_NAMES; #======================================================================== # Accessors for user specified attributes #======================================================================== sub get_name { shift->{name} } sub get_object { shift->{object} } sub get_attr { shift->{attr} } sub get_properties { shift->{properties} } sub get_label { shift->{label} } sub get_label_for { shift->{label_for} } sub get_label_markup { shift->{label_markup} } sub get_label_group { shift->{label_group} } sub get_widget_group { shift->{widget_group} } sub get_tip { shift->{tip} } sub get_inactive { shift->{inactive} } sub get_active { shift->{active} } sub get_rules { shift->{rules} } sub get_expand { shift->{expand} } sub get_expand_h { shift->{expand_h} } sub get_expand_v { shift->{expand_v} } sub get_scrollbars { shift->{scrollbars} } sub get_signal_connect { shift->{signal_connect} } sub get_signal_connect_after { shift->{signal_connect_after} } sub get_width { shift->{width} } sub get_height { shift->{height} } sub get_customize_hook { shift->{customize_hook} } sub get_changed_hook { shift->{changed_hook} } sub get_changed_hook_after { shift->{changed_hook_after} } sub get_active_cond { shift->{active_cond} } sub get_active_depends { shift->{active_depends} } #------------------------------------------------------------------------ sub set_name { shift->{name} = $_[1] } sub set_object { shift->{object} = $_[1] } sub set_attr { shift->{attr} = $_[1] } sub set_properties { shift->{properties} = $_[1] } sub set_label { shift->{label} = $_[1] } sub set_label_for { shift->{label_for} = $_[1] } sub set_label_markup { shift->{label_markup} = $_[1] } sub set_label_group { shift->{label_group} = $_[1] } sub set_widget_group { shift->{widget_group} = $_[1] } sub set_tip { shift->{tip} = $_[1] } sub set_inactive { shift->{inactive} = $_[1] } sub set_active { shift->{active} = $_[1] } sub set_rules { shift->{rules} = $_[1] } sub set_expand { shift->{expand} = $_[1] } sub set_expand_h { shift->{expand_h} = $_[1] } sub set_expand_v { shift->{expand_v} = $_[1] } sub set_scrollbars { shift->{scrollbars} = $_[1] } sub set_signal_connect { shift->{signal_connect} = $_[1] } sub set_signal_connect_after { shift->{signal_connect_after} = $_[1] } sub set_width { shift->{width} = $_[1] } sub set_height { shift->{height} = $_[1] } sub set_customize_hook { shift->{customize_hook} = $_[1] } sub set_changed_hook { shift->{changed_hook} = $_[1] } sub set_changed_hook_after { shift->{changed_hook_after} = $_[1] } sub set_active_cond { shift->{active_cond} = $_[1] } sub set_active_depends { shift->{active_depends} = $_[1] } #======================================================================== #======================================================================== # Accessors for internal attributes #======================================================================== sub get_context { shift->{form_factory}->get_context } sub get_form_factory { shift->{form_factory} } sub get_parent { shift->{parent} } sub get_gtk_widget { shift->{gtk_widget} } sub get_gtk_parent_widget { $_[0]->{gtk_parent_widget} || $_[0]->{gtk_widget} } sub get_gtk_properties_widget { $_[0]->{gtk_properties_widget} || $_[0]->{gtk_widget} } sub get_gtk_label_widget { shift->{gtk_label_widget} } sub get_layout_data { shift->{layout_data} } sub get_in_update { shift->{in_update} } sub get_no_widget_update { shift->{no_widget_update} } sub get_backup_widget_value { shift->{backup_widget_value} } sub get_widget_activity { shift->{widget_activity} } sub get_built { shift->{built} } #------------------------------------------------------------------------ sub set_form_factory { weaken( shift->{form_factory} = $_[1])} sub set_parent { weaken( shift->{parent} = $_[1])} sub set_gtk_widget { shift->{gtk_widget} = $_[1] } sub set_gtk_parent_widget { shift->{gtk_parent_widget} = $_[1] } sub set_gtk_properties_widget { shift->{gtk_properties_widget}= $_[1] } sub set_gtk_label_widget { shift->{gtk_label_widget} = $_[1] } sub set_layout_data { shift->{layout_data} = $_[1] } sub set_in_update { shift->{in_update} = $_[1] } sub set_no_widget_update { shift->{no_widget_update} = $_[1] } sub set_backup_widget_value { shift->{backup_widget_value} = $_[1] } sub set_widget_activity { shift->{widget_activity} = $_[1] } sub set_built { shift->{built} = $_[1] } #======================================================================== #======================================================================== # Methods, which may be implemented by Widget subclasses #======================================================================== sub get_type { die $_[0]." misses type() method" } sub get_gtk_signal_widget { $_[0]->get_gtk_widget } sub get_gtk_tip_widgets { [ $_[0]->get_gtk_widget ] } sub get_gtk_check_widget { $_[0]->get_gtk_widget } sub get_widget_check_value { undef } sub has_additional_attrs { "" } sub has_label { 0 } sub object_to_widget { 1 } sub widget_to_object { 1 } sub empty_widget { 1 } sub backup_widget_value { 1 } sub restore_widget_value { 1 } sub isa_container { 0 } sub widget_data_has_changed { $_[0]->get_backup_widget_value ne $_[0]->get_widget_check_value } #======================================================================== #======================================================================== # Widget constructor - must be called by subclasses #======================================================================== sub new { my $class = shift; my %par = @_; my ($name, $object, $attr, $properties, $label, $label_group) = @par{'name','object','attr','properties','label','label_group'}; my ($widget_group, $inactive, $rules, $expand, $scrollbars) = @par{'widget_group','inactive','rules','expand','scrollbars'}; my ($signal_connect, $width, $height, $customize_hook) = @par{'signal_connect','width','height','customize_hook'}; my ($changed_hook, $tip, $expand_h, $expand_v, $label_markup) = @par{'changed_hook','tip','expand_h','expand_v','label_markup'}; my ($active, $signal_connect_after, $label_for) = @par{'active','signal_connect_after','label_for'}; my ($active_cond, $active_depends, $changed_hook_after) = @par{'active_cond','active_depends','changed_hook_after'}; $active = 1 if not defined $active; #-- Short notation: 'object.attr', so you may omit 'object' if ( $attr and $attr =~ /^([^.]+)\.(.*)/ ) { $object = $1; $attr = $2; } #-- Set a default for the Widget's name if ( not $name and $object and $attr ) { #-- Default name is object.attr, if both #-- object and attr are set my $cnt = 1; my $add = ""; #-- Add a number, if the name is registered already while ( exists $WIDGET_NAMES{"$object.$attr$add"} ) { ++$cnt; $add="_$cnt"; } $name = "$object.$attr$add"; } elsif ( not $name ) { #-- Widgets non associated with an object and #-- an attribute get a name derived from the #-- Widget's type $name ||= $class->get_type."_".$NAME_CNT++; } #-- Check if widget name is not already registered croak "Widget name '$name' is already registered" if exists $WIDGET_NAMES{$name}; #-- Store widget name $WIDGET_NAMES{$name} = 1; #-- By default make widget insensitive when it's not active $inactive ||= "insensitive"; #-- Expanding defaults $expand_h = $expand_v = $expand if defined $expand; $expand = 0 unless defined $expand; $expand_h = 1 unless defined $expand_h; $expand_v = 0 unless defined $expand_v; croak "'inactive' must be 'insensitive' or 'invisible'" unless $inactive eq 'insensitive' or $inactive eq 'invisible'; my $self = bless { name => $name, object => $object, attr => $attr, properties => $properties, label => $label, label_for => $label_for, label_group => $label_group, label_markup => $label_markup, widget_group => $widget_group, tip => $tip, active => $active, inactive => $inactive, rules => $rules, expand => $expand, expand_h => $expand_h, expand_v => $expand_v, scrollbars => $scrollbars, signal_connect => $signal_connect, signal_connect_after => $signal_connect_after, width => $width, height => $height, customize_hook => $customize_hook, changed_hook => $changed_hook, changed_hook_after => $changed_hook_after, active_cond => $active_cond, active_depends => $active_depends, layout_data => {}, }, $class; return $self; } sub debug_dump { my $self = shift; my ($level) = @_; print " "x$level; print $self->{name}."|".$self->{attr}."\n"; 1; } #======================================================================== # Cleanup of widget data; break circular references #======================================================================== sub cleanup { my $self = shift; $Gtk2::Ex::FormFactory::DEBUG && print "CLEANUP: $self ".$self->get_name."(".$self->get_attr.")\n"; #-- Break circular references with the parent object $self->set_parent(undef); #-- Cut references to Gtk widgets - otherwise the Perl #-- garbage collector is confused. We have heavy circular #-- referencing from FormFactory widgets to Gtk widgets, #-- e.g. from callback closures. $self->set_gtk_widget(undef); $self->set_gtk_parent_widget(undef); $self->set_gtk_properties_widget(undef); $self->set_gtk_label_widget(undef); #-- Deregister the Widget name delete $WIDGET_NAMES{$self->get_name}; #-- Delete all references to this widget from the #-- associated Context $self->get_context->deregister_widget ($self); #-- Destroy reference to the FormFactory $self->set_form_factory(undef); 1; } #======================================================================== # Convenience method: get Object Proxy of this Widget #======================================================================== sub get_proxy { $_[0]->get_form_factory ->get_context ->get_proxy($_[0]->get_object); } #======================================================================== # Build this Widget, using the FormFactory's Layout instance #======================================================================== sub build { my $self = shift; $Gtk2::Ex::FormFactory::DEBUG && print "$self->build\n"; #-- The Layout object actually builds all widgets $self->get_form_factory ->get_layouter ->build_widget($self); $self->set_built(1); 1; } #======================================================================== # Connect all Gtk signals of this widget #======================================================================== sub connect_signals { my $self = shift; #-- Some widgets have not Gtk pendant, so there #-- may be no signal connecting at all my $gtk_widget = $self->get_gtk_widget; return unless $gtk_widget; #-- Need the context my $context = $self->get_context; #-- Register the widget here... #-- (deregistering is done in ->cleanup) $context->register_widget($self); #-- On focus-in we backup the current object value #-- (probably we need to restore this if the user #-- enters invalid data) $self->get_gtk_check_widget->signal_connect ("focus-in-event", sub { $self->backup_widget_value; 0; }); #-- On focus-out we check for valid data $self->get_gtk_check_widget->signal_connect ("focus-out-event", sub { $self->check_widget_value; 0; }); #-- Connect the changed signal, if the widgets provides #-- a method for this $self->connect_changed_signal if $self->can("connect_changed_signal"); #-- Connect additional user specified signals my $signal_connect = $self->get_signal_connect; if ( $signal_connect ) { my $signal_widget = $self->get_gtk_signal_widget; while ( my ($signal, $callback) = each %{$signal_connect} ) { $signal_widget->signal_connect ( $signal => $callback ); } } #-- Connect additional user specified signals (after) my $signal_connect_after = $self->get_signal_connect_after; if ( $signal_connect_after ) { my $signal_widget = $self->get_gtk_signal_widget; while ( my ($signal, $callback) = each %{$signal_connect_after} ) { $signal_widget->signal_connect ( $signal => $callback ); } } 1; } #======================================================================== # Lookup a widget #======================================================================== sub get_widget { my $self = shift; my ($name) = @_; my $widget; my $form_factory = $self->get_form_factory; croak "Widget '$name' not registered to this ". "form factory ('".$form_factory->get_name."')" unless $widget = $form_factory->get_widgets_by_name->{$name}; return $widget; } #======================================================================== # Lookup a widget reference #======================================================================== sub lookup_widget { my $self = shift; my ($name) = @_; if ( $name =~ /sibling\s*\((.*?)\)/ ) { my $sibling_idx = $1; my $siblings = $self->get_parent->get_content; my $self_idx; foreach my $sibling ( @{$siblings} ) { if ( $sibling eq $self ) { $self_idx ||= 0; last; } ++$self_idx; } die "Impossible" unless defined $self_idx; my $sibling = $siblings->[$sibling_idx+$self_idx]; die "Can't find sibling($sibling_idx)" unless $sibling; return $sibling; } else { return $self->get_form_factory->get_widget($name); } } #======================================================================== # Update this widgets resp. transfer the object's value to the Widget #======================================================================== sub update { my $self = shift; my ($change_state) = @_; $change_state = '' if not defined $change_state; $Gtk2::Ex::FormFactory::DEBUG && print "update_widget(".$self->get_name.", $change_state)\n"; #-- Check if widget updating is temoprarily disabled #-- (refer to widget_value_changed() for this) return if $self->get_no_widget_update; #-- Is no object associated with this widget? if ( not $self->get_object ) { #-- Only a activity update may be possible, if #-- an Gtk widget is present at all if ( $self->get_gtk_parent_widget ) { my $active = $self->get_active; $active = $active ? "active" : "inactive"; $self->update_widget_activity ( $active ); } return; } #-- We're going to change the widget's state. This will #-- trigger the widget's changed signal. To prevent, that #-- this triggers an object update again, we set this #-- widget into update state (refer to widget_value_changed() #-- for details) $self->set_in_update(1); #-- Do we have an activity update? (if $change state is given, #-- and contains the string 'inactive') - Default is to detect #-- activity by the correspondent Proxy method (see below) my $active; $active = $change_state =~ /inactive/ ? 0 : 1 if $change_state ne ''; #-- Now transform the object's activity state into a #-- correspondent widget sensivity/visibility. if ( $self->get_object and $self->get_gtk_parent_widget ) { #-- Get object's activity state $active = $self->get_proxy($self->get_object) ->get_attr_activity($self->get_attr) if not defined $active; #-- And set visibility or sensitivity accordingly, #-- dependend on what's defined in the widget $self->update_widget_activity ( $active ); } #-- Transfer object value to widget if ( $change_state eq '' ) { $self->object_to_widget if $self->get_proxy->get_object; } elsif ( $change_state =~ /empty/ ) { $self->empty_widget; } #-- Set widget into normal update state $self->set_in_update(0); 1; } #======================================================================== # Update this widget, and it's child; overwritten by Container class #======================================================================== sub update_all { my $self = shift; #-- For a non Container widget, this is the same as update() $self->update(@_); 1; } #======================================================================== # Update this widget's activity state: (in)sensitive / (in)visible #======================================================================== sub update_widget_activity { my $self = shift; my ($active) = @_; $active = 0 if $active eq 'inactive'; #-- Use the Widget's activity value over the given $active if ( defined $self->get_widget_activity ) { $active = $self->get_widget_activity; } #-- Get associated object (if there is one) my $object_name = $self->get_object; my $object = $object_name ? $self->get_proxy->get_object : undef; #-- If there is an object association but the object is #-- currently not defined, set widget inactive if ( $object_name && ! defined $object ) { $active = 0; } #-- Otherwise check if an additional condition needs to be applied else { my $cond = $self->get_active_cond; $active = &$cond($object) if $cond; } my $action = $self->get_inactive; if ( $active eq 'insensitive' ) { $action = "insensitive"; $active = 0; } elsif ( $active eq 'invisible' ) { $action = "invisible"; $active = 0; } elsif ( $active eq 'sensitive' ) { $action = "insensitive"; $active = 1; } elsif ( $active eq 'visible' ) { $action = "invisible"; $active = 1; } if ( $active ) { #-- Make the widget visible resp. sensitive if ( $action eq 'invisible' ) { $Gtk2::Ex::FormFactory::DEBUG && print " update_widget_activity(". $self->get_name. ", show)\n"; $self->get_gtk_parent_widget->show; $self->get_gtk_label_widget->show if $self->get_gtk_label_widget; } else { $Gtk2::Ex::FormFactory::DEBUG && print " update_widget_activity(". $self->get_name. ", sensitive)\n"; $self->get_gtk_parent_widget->show; $self->get_gtk_label_widget->show if $self->get_gtk_label_widget; $self->get_gtk_parent_widget->set_sensitive(1); $self->get_gtk_label_widget->set_sensitive(1) if $self->get_gtk_label_widget; } } else { #-- Make the widget invisible resp. insensitive if ( $action eq 'invisible' ) { $Gtk2::Ex::FormFactory::DEBUG && print " update_widget_activity(". $self->get_name. ", hide)\n"; $self->get_gtk_parent_widget->hide; $self->get_gtk_label_widget->hide if $self->get_gtk_label_widget; } else { $Gtk2::Ex::FormFactory::DEBUG && print " update_widget_activity(". $self->get_name. ", insensitive)\n"; $self->get_gtk_parent_widget->set_sensitive(0); $self->get_gtk_label_widget->set_sensitive(0) if $self->get_gtk_label_widget; } } #-- Remember state $self->set_active($active); 1; } #======================================================================== # Convenience method: get the Object's value #======================================================================== sub get_object_value { my $self = shift; my ($attr) = @_; #-- By default get the primary attribute $attr ||= $self->get_attr; #-- Return nothing if this widget has no associated Object return if not $self->get_object; #-- Otherweise use the Proxy to return the Object's value return $self->get_proxy($self->get_object) ->get_attr ($attr); } #======================================================================== # Convenience method: set the Object's value #======================================================================== sub set_object_value { my $self = shift; my ($attr, $value) = @_; #-- If only one argument is given this is the value of #-- the default attribute of this widget if ( @_ == 1 ) { $value = $attr; $attr = $self->get_attr; } #-- Do nothing if this widget has no associated Object return if not $self->get_object; #-- Otherwise use the Proxy to set the Object's value return $self->get_proxy($self->get_object) ->set_attr ($attr => $value ); } #======================================================================== # Check the widget value against the specified rules #======================================================================== sub check_widget_value { my $self = shift; #-- Return true, if this Widget has no associated rules my $rules = $self->get_rules; return 1 if not defined $rules; #-- Check only if data changed return 1 unless $self->widget_data_has_changed; #-- Rule checking is done by a Rules Object associated #-- with the FormFactory of this Widget my $rule_checker = $self->get_form_factory->get_rule_checker; my $message; if ( $self->get_form_factory->get_sync && $self->get_object ) { #-- If the FormFactory is in Sync mode, check #-- the Object's value (access is faster than getting #-- the Widget value) $message = $rule_checker->check ( $rules, $self->get_label, $self->get_object_value ); } else { #-- If the FormFactory is not in Sync mode, the #-- Widget value is checked $message = $rule_checker->check ( $rules, $self->get_label, $self->get_widget_check_value ); } #-- Restore the Widget value and print an error dialog, #-- if the Rule check failed. if ( $message ) { $self->restore_widget_value; $self->show_error_message ( message => $message, ); } return 0; } #======================================================================== # Callback method, called if the user changed the Widget #======================================================================== sub widget_value_changed { my $self = shift; #-- Do nothing if this Widget is already in update state #-- (otherwise recursive updates may be triggered) return if $self->get_in_update; $Gtk2::Ex::FormFactory::DEBUG && print $self->get_type."(".$self->get_name.") value changed\n"; my $object = $self->get_object ? $self->get_proxy->get_object : undef; if ( $self->get_form_factory->get_sync ) { #-- Call the Widget's change hook my $changed_hook = $self->get_changed_hook; &$changed_hook($object, $self) if $changed_hook; #-- Apply all changes and update dependent #-- widgets accordingly $self->apply_changes if $object; #-- Call Widget's change_after_hook my $changed_hook_after = $self->get_changed_hook_after; &$changed_hook_after($object, $self) if $changed_hook_after; } else { #-- Changing the object normally triggers this #-- change also in the widget (refer to #-- Context->update_object_attr_widgets). We need #-- to prevent this. $self->set_no_widget_update(1); #-- Call the Widget's change hook, if one was set my $changed_hook = $self->get_changed_hook; &$changed_hook($object, $self) if $changed_hook; #-- Now update all dependent widgets $self->get_form_factory ->get_context ->update_object_attr_widgets( $self->get_object, $self->get_attr ); #-- Set widget into normal update state again $self->set_no_widget_update(0); #-- Call Widget's change_after_hook my $changed_hook_after = $self->get_changed_hook_after; &$changed_hook_after($object, $self) if $changed_hook_after; } 1; } #======================================================================== # Transfer the Widget value to the Object; no activity update #======================================================================== sub apply_changes { my $self = shift; $Gtk2::Ex::FormFactory::DEBUG && print "apply_changes ".$self->get_type."(".$self->get_name.")\n"; #-- No widget update when setting the object value $self->set_no_widget_update(1); #-- Set object value from current widget value $self->widget_to_object; #-- Widget updates allowed again $self->set_no_widget_update(0); 1; } #======================================================================== # Apply all changes incl. children # (here the samy as apply, overriden by Container) #======================================================================== sub apply_changes_all { shift->apply_changes } #======================================================================== # Commit the Widget's Proxy Buffer (if Proxy is buffered at all) #======================================================================== sub commit_proxy_buffers { my $self = shift; return unless $self->get_object; #-- Nothing to do in synced FormFactories #-- where the Proxy doesn't buffer my $proxy = $self->get_proxy; return 1 unless $proxy->get_buffered; #-- Commit the Proxy's attribute buffer to the object $proxy->commit_attr($self->get_attr); #-- And probably additional attributes... if ( $self->has_additional_attrs ) { my $add_attrs = $self->has_additional_attrs; my $object = $self->get_object; foreach my $add_attr ( @{$add_attrs} ) { my $get_attr_name_method = "get_attr_$add_attr"; my $attr = $self->$get_attr_name_method(); $proxy->commit_attr($attr); } } return 1; } #======================================================================== # Commit proxy buffer changes incl. children # (here the samy as apply, overriden by Container) #======================================================================== sub commit_proxy_buffers_all { shift->commit_proxy_buffers } #======================================================================== # Commit the Widget's Proxy Buffer (if Proxy is buffered at all) #======================================================================== sub discard_proxy_buffers { my $self = shift; return unless $self->get_object; #-- Nothing to do in synced FormFactories #-- where the Proxy doesn't buffer my $proxy = $self->get_proxy; return 1 unless $proxy->get_buffered; #-- Discard the Proxy's attribute buffer $proxy->discard_attr($self->get_attr); #-- And probably additional attributes... if ( $self->has_additional_attrs ) { my $add_attrs = $self->has_additional_attrs; my $object = $self->get_object; foreach my $add_attr ( @{$add_attrs} ) { my $get_attr_name_method = "get_attr_$add_attr"; my $attr = $self->$get_attr_name_method(); $proxy->discard_attr($attr); } } return 1; } #======================================================================== # Commit proxy buffer changes incl. children # (here the samy as apply, overriden by Container) #======================================================================== sub discard_proxy_buffers_all { shift->discard_proxy_buffers } #======================================================================== # Show an error dialog #======================================================================== sub show_error_message { my $self = shift; my %par = @_; my ($message, $type) = @par{'message','type'}; $type ||= "error"; $type = "GTK_MESSAGE_".uc($type); my $dialog = Gtk2::MessageDialog->new ( $self->get_form_factory->get_form_factory_gtk_window, 'GTK_DIALOG_DESTROY_WITH_PARENT', $type, 'GTK_BUTTONS_CLOSE', $message, ); $dialog->signal_connect( "response", sub { $dialog->destroy } ); $dialog->set_position ('center'); $dialog->set ( modal => 1 ); $dialog->show; 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Widget - Base class for all FormFactory Widgets =head1 SYNOPSIS Gtk2::Ex::FormFactory::Widget->new ( name => Name of this Widget, object => Name of the associated application object, attr => Attribute represented by the Widget, label => Label text, label_markup => Boolean, indicating whether the label has markup, label_group => Name of a Gtk2::SizeGroup for the label, widget_group => Name of a Gtk2::SizeGroup for the widget, tip => Tooltip text, properties => { Gtk2 Properties ... } inactive => 'insensitive' | 'invisible', rules => [ Rules for this Widget ], expand => Boolean: should the Widget expand?, expand_h => Boolean: should the Widget expand horizontally?, expand_v => Boolean: should the Widget expand vertically?, scrollbars => [ hscrollbar_policy, vscrollbar_policy ], signal_connect => { signal => CODREF, ... }, signal_connect_after => { signal => CODREF, ... }, width => Desired width, height => Desired height, customize_hook => CODEREF: Customize the underlying Gtk2 Widget, changed_hook => CODEREF: Track changes made to the Widget, changed_hook_after => CODEREF: Track changes made to the Widget, active_cond => CODEREF: Condition for Widget being active active_depends => SCALAR|ARRAYREF: Attribute(s) activity depends on ); =head1 DESCRIPTION This is an abstract base class and usually not used directly from the application. For daily programming the attributes defined in this class are most important, since they are common to all Widgets of the Gtk2::Ex::FormFactory framework. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container | +--- Gtk2::Ex::FormFactory | +--- Gtk2::Ex::FormFactory::Expander | +--- Gtk2::Ex::FormFactory::Form | +--- Gtk2::Ex::FormFactory::HBox | +--- Gtk2::Ex::FormFactory::Notebook | +--- Gtk2::Ex::FormFactory::Table | +--- Gtk2::Ex::FormFactory::VBox | +--- Gtk2::Ex::FormFactory::Window +--- Gtk2::Ex::FormFactory::Button +--- Gtk2::Ex::FormFactory::CheckButton +--- Gtk2::Ex::FormFactory::CheckButtonGroup +--- Gtk2::Ex::FormFactory::Combo +--- Gtk2::Ex::FormFactory::DialogButtons +--- Gtk2::Ex::FormFactory::Entry +--- Gtk2::Ex::FormFactory::Expander +--- Gtk2::Ex::FormFactory::ExecFlow +--- Gtk2::Ex::FormFactory::GtkWidget +--- Gtk2::Ex::FormFactory::HPaned +--- Gtk2::Ex::FormFactory::HSeparator +--- Gtk2::Ex::FormFactory::Image +--- Gtk2::Ex::FormFactory::Label +--- Gtk2::Ex::FormFactory::List +--- Gtk2::Ex::FormFactory::Menu +--- Gtk2::Ex::FormFactory::Popup +--- Gtk2::Ex::FormFactory::ProgressBar +--- Gtk2::Ex::FormFactory::RadioButton +--- Gtk2::Ex::FormFactory::TextView +--- Gtk2::Ex::FormFactory::Timestamp +--- Gtk2::Ex::FormFactory::ToggleButton +--- Gtk2::Ex::FormFactory::VPaned +--- Gtk2::Ex::FormFactory::VSeparator +--- Gtk2::Ex::FormFactory::YesNo Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy +--- Gtk2::Ex::FormFactory::ProxyBuffered =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<name> = SCALAR [optional] Each widget has a unique name. If you don't specify it explicitly a name is generated automatically. You can select named widgets later by using the B<get_widget> and B<lookup_widget> methods described below. =item B<object> = SCALAR [optional] The name of the object, which controls this widget. This object name must be registered at the L<Gtk2::Ex::FormFactory::Context> of the L<Gtk2::Ex::FormFactory> associated with this Widget. You may omit the B<object> property and use a fully qualified "object.attr" notation in the B<attr> attribute described beyond. If you want to associate your Widget only with an object, but not to an attribute (e.g. to get the activity of a container widget without an associated object attribute managed automatically) just omit B<attr> and specify only B<object> here. =item B<attr> = SCALAR [optional] Usually a Widget represents a specific object attribute, e.g. a text entry shows the current value of the attribute you specify here. How this attribute is accessed is defined in the L<Gtk2::Ex::FormFactory::Context> instance. If you used the B<object> property just pass the name of your attribute here, but you may omit B<object> and pass "object.attr" to the B<attr> property for convenience as well. =item B<label> = SCALAR [optional] Each Widget may have an associated label. How this label is actually rendered depends on the L<Gtk2::Ex::FormFactory::Container> to which this Widget was added. E.g. L<Gtk2::Ex::FormFactory::Form> implements a simple two column table with the labels in the left and the widgets in the right column. =item B<label_markup> = BOOLEAN [optional] If this is set to a true value, the label will be rendered with a HTML like markup. Refer to the chapter "Pango Text Attribute Markup" of the official Gtk documentation for details about the known markup tags. =item B<label_group> = SCALAR [optional] If you have a complex layout and you want to align your labels although they are not part of the same container you can specify an arbitrary name of a label group here. A correspondent Gtk2::SizeGroup is managed automatically for you. Simply specify the same name for all Widgets for which you want to have the same label size. =item B<widget_group> = SCALAR [optional] This is very similar to the B<label_group> attribute. The difference is that the size allocated by the Widget is under control of a Gtk2::SizeGroup. =item B<tip> = SCALAR [optional] Optional text of the tooltip of this Widget. =item B<properties> = HASHREF [optional] This is a hash of Gtk+ properties for this Widget, e.g. you can specify { border_width => 5 } here to manipulate the border-width of this specific Widget. You should use this with care, because this breaks the strict isolation of GUI structure and appearance. Probably it's better to implement an own L<Gtk2::Ex::FormFactory::Layout> class, where you can control appearance of your widgets in a much more generic way. =item B<inactive> = 'insensitive' | 'invisible' [optional] Gtk2::Ex::FormFactory automatically manages the activity state of your Widgets. Specify if you want the Widget getting insensitive or invisible when the Widget is deactivated. This defaults to B<'insensitive'>. =item B<rules> = rule | [ rule, ... ] [optional] Data entered by the user must apply the rules specified here. Refer to L<Gtk2::Ex::FormFactory::Rules> for details about rules. =item B<expand> = BOOL [optional] By default a Widget doesn't expand into the space which is avaiable from its container. Specify a TRUE value to activate Widget expansion. Whether the Widget expands vertically or horizontally depends on its Container. E.g. in a VBox it will expand vertically, in a HBox horizontally. =item B<expand_h> = BOOL [optional] =item B<expand_v> = BOOL [optional] Some containers can expand the Widget in both directions, e.g. a Gtk2::Table. If your widget is added to such a container (e.g. to a L<Gtk2::Ex::FormFactory::Form>, which is implemented with a Gtk2::Table) you can specify both directions of expansion here. B<expand_h> defaults to TRUE and B<expand_v> to FALSE, or to B<expand> if specified. =item B<scrollbars> = [ h_policy, v_policy ] [optional] If you want your Widget inside a Gtk2::ScrolledWindow, simply specify the policy for horizontal and vertical scrollbars here. Possible values are: B<"always">, B<"automatic"> or B<"never">. =item B<changed_hook> = CODEREF(ApplicationObject, WidgetObject) [optional] This code reference is called after the user changed a value of the Widget, but before these changes are applied to the underlying application object. The application object is the first argument of the call, the Widget object the second. =item B<changed_hook_after> = CODEREF(ApplicationObject, WidgetObject) [optional] This code reference is called after the user changed a value of the Widget and after these changes are applied to the underlying application object. The application object is the first argument of the call, the Widget object the second. =item B<signal_connect> = HASHREF [optional] Specify all your signal connections in a single hash reference. Key is the name of the signal, and value the callback (a static subroutine reference or a closure). B<Note>: don't use this to track changes made on the GUI! Gtk2::Ex::FormFactory manages this for you. If you want to be notified about changes, use the Widget transparent B<changed_hook> described above. =item B<signal_connect_after> = HASHREF [optional] Same as B<signal_connect>, but signals are connected using Gtk2's B<signal_connect_after> method. =item B<width> = INTEGER [optional] =item B<height> = INTEGER [optional] You can specify a desired width and/or height. Internally B<Gtk2::Widget->set_default_size> is used on windows and B<Gtk2::Widget->set_size_request> on all other widgets. =item B<customize_hook> = CODEREF(Gtk2::Widget) [optional] This code reference is called after the Gtk2::Widget's are built. The Gtk2::Widget object of this Gtk2::Ex::FormFactory::Widget is the first argument of this call. You can use this hook to do very specific customization with this Widget. Again: use this with care, probably implement your own L<Gtk2::Ex::FormFactory::Layout> class to control the layout. =item B<active_cond> = CODEREF(ApplicationObject) [optional] Widget's activity state (visible/sensitive) is controlled by this condition resp. the return value of this code reference. Use this if you want to fine control the activity state of the widget with arbitrary conditions. Note that widgets get automatically inactive if the object they're bound to get's undef. The return value is as follows: 0 Widget gets inactive. According to the B<inactive> attribute it gets either invisible or insensitive. 1 Widget gets active. According to the B<inactive> attribute it gets either visible or sensitive. Or return one of these strings 'insensitive' 'invisible' 'sensitive' 'visible' to get the corresponding widget state. =item B<active_depends> = SCALAR | ARRAYREF [optional] This lists the attribute(s) the activity condition above depends on, resp. which attributes are variables in the condition. May point to objects or attributes (in "object.attr" notation). With this knowledge Gtk2::Ex::FormFactory is able to update the activity automatically if one of the corresponding objects or attributes changes. =back =head1 METHODS =over 4 =item $widget->B<update> () Updates this specific Widget resp. sets it's state to the value from the associated application object attribute. In case of a Container the child widgets are B<not> updated. =item $widget->B<update_all> () Same as B<update>, but containers will update their children as well. =item $widget->B<update_widget_activity> () Only update the Widget's activity state. =item $app_object_attr_value = $widget->B<get_object_value> ([$attr]) A convenience method to get the actual value of an associated application object attribute. If B<$attr> is omitted, the default attribute is used. =item $widget->B<set_object_value> ( [$attr, ] $value ) A convenience method to set the actual value of an associated application object attribute to B<$value>. If B<$attr> is omitted, the default attribute is used. =item $widget->B<check_widget_value> () Checks the current Widget value against the rules provided for this Widget. An error dialog is opened if the rule check failed and the previous value is restored automatically. Nothing happens if all rules apply. =item $widget->B<widget_value_changed> () This method is called if the Widget value was changed. All Widget implementations of Gtk2::Ex::FormFactory must connect their specific "changed" signal to this method call. =item $widget->B<apply_changes> () Copy the Widget value to the associated application object attribute. In a FormFactory with the B<sync> flag set to TRUE this happens on each change. If the FormFactory is asynchronous it's called only when the user hit the Ok button. =item $widget->B<show_error_message> ( message => $message, type => $type ) Small convenience method which opens a Gtk+ error dialog with B<$message>. B<$type> defaults to 'error', but you can specify 'info', 'warning' and 'question' as well to get corresponding dialogs. =item $proxy = $widget->B<get_proxy> () Convenience method which returns the Gtk2::Ex::FormFactory::Proxy instance for the object associated with this Widget. =item $another_widget = $widget->B<get_widget> ( $name ) Returns the Gtk2::Ex::FormFactory::Widget object named B<$name> of the FormFactory of this widget. =item $another_widget = $widget->B<lookup_widget> ($name) The same as B<get_widget> if a widget name is passed, but additionally you may dereference sibling widgets by passing sibling($n) This returns the $n-th sibling of this Widget, whereby $n may be a negative value. This method is used to lookup widgets assigned to a Gtk2::Ex::FormFactory::Label using the Label's B<for> attribute. =back The following methods are used by the Gtk2::Ex::FormFactory::Layout module, so you need them only if you implement your own layout. =over 4 =item $widget->B<set_gtk_widget> (Gtk2::Widget) The Gtk2::Widget which represents the associated application object attribute, e.g. this is a Gtk2::Entry for a Gtk2::Ex::FormFactory::Entry widget. =item $widget->B<set_gtk_parent_widget> (Gtk2::Widget) Often the real Gtk2 widget is inside a container, e.g. a Gtk2::Frame. The Gtk2 widget of the container needs to be set explicetly using this method. =back =head1 IMPLEMENT NEW WIDGETS You can implement new widgets by subclassing Gtk2::Ex::FormFactory::Widget or Gtk2::Ex::FormFactory::Container. You need to implement the following methods (not all are mandatory, e.g. if your Widget is a container actually doesn't representing any application object value, you can omit most of them): =over 4 =item $self->B<get_type>() [mandatory] This returns the short name of your Widget. It should be lower case and may contain underscores. If you wrap an existent Gtk2 widget try to derive the type name from the widget's name. =item $self->B<object_to_widget> [optional] This method transfers the value of the associated application object attribute to your widget. You may use the convenience method $self->B<get_object_value>() to get the value of the default attribute of this widget. =item $self->B<widget_to_object> [optional] This method transfers the value of your widget to the associated application object attribute. You may use the convenience method $self->B<set_object_value>($value) to set the value of the default attribute of this widget. =item $self->B<empty_widget> [optional] This method sets your widget to an empty value, e.g. an Image widget should display nothing, or the text of some sort of text entry should be deleted. =item $self->B<backup_widget_value> [optional] This method makes a backup of the current widget value. Gtk2::Ex::FormFactory::Widget has a convenience method for setting the backup value you may use: $self->B<set_backup_widget_value>($value). If your widget has a more complex value, which can't be covered by a single scalar, the implementation must care about this. =item $self->B<restore_widget_value> [optional] This restores a value from the backup created with $self->B<backup_widget_value>(). =item $self->B<get_gtk_check_widget> [optional] Returns the Gtk widget to which the focus-in and focus-out signals should be connected to for rule checking. Defaults to $self->B<get_gtk_widget>(). =item $self->B<get_widget_check_value> [optional] Currently Gtk2::Ex::FormFactory::Rules can only check a single scalar value. Your widget must implement this method to return the correspondent value. =item $self->B<connect_changed_signal> [optional] This method must connect the "changed" signal of the specific Gtk2 widget your implementation uses. The connected callback must call the $self->B<widget_value_changed>() method, so Gtk2::Ex::FormFactory can track all changes on the GUI. =item $gtk_widget = $self->B<get_gtk_signal_widget>() [optional] This defaults to $self->B<get_gtk_widget>() and returns the Gtk2 widget to which additional user specified signals should be connected. =item $gtk_widget = $self->B<get_gtk_properties_widget>() [optional] This defaults to $self->B<get_gtk_widget>() and returns the Gtk2 widget which should get the B<properties> defined for this Gtk2::Ex::FormFactory widget. This is useful if the actual GtkWidget is not the B<gtk_widget> (e.g. Gtk2::Ex::FormFactory::Window needs this, since it's finally a VBox, but you want to apply properties like default_width to the GtkWindow and not to the VBox). =item $widgets_lref = $self->B<get_gtk_tip_widgets>() [optional] This defaults to [ $self->B<get_gtk_widget>() ] and returns a list reference of Gtk2 widgets which should get a tooltip, if the user specified one. =item $self->B<has_additional_attrs>() [optional] If your widget supports additional application object attributes which should be managed automatically, this method returns a list reference of symbolic names for these attributes. Please refer to the implementation of Gtk2::Ex::FormFactory::List, which uses this feature to store the actually selected item(s) in the application object. =item BOOL = $self->B<has_label>() [optional] This defaults to 0. Set this to 1 if your widget manage it's label by itself (like a Gtk2::CheckBox does). =back =head2 Creating the Gtk2 widget(s) You probably recognized that a method which actually builds the Gtk2 widgets of your widget is missing here. This is covered by the Gtk2::Ex::FormFactory::Layout module. So create your own layouter and add the $layouter->B<build_TYPE>($widget) method for your widget to it. If your widget is a container you also need to implement at least the generic $layouter->B<add_widget_to_TYPE> method. For details about this please refer to the documentation of Gtk2::Ex::FormFactory::Layout. Nevertheless, if your widget is very specific to your application, e.g. because it displays a very specific data structure, creating your own Layout module just for that purpose is somewhat involved. In that case you can implement this method: =over 4 =item $self->B<build_widget>() If implemented this method is called to actually create the Gtk2 widgets for your Gtk2::Ex::FormFactory widget. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ����������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/DialogButtons.pm�����������������������������������0000644�0001750�0001750�00000015055�10400606040�023370� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::DialogButtons; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "dialog_buttons" } sub get_buttons { shift->{buttons} } sub get_clicked_hook_before { shift->{clicked_hook_before} } sub get_clicked_hook_after { shift->{clicked_hook_after} } sub set_buttons { shift->{buttons} = $_[1] } sub set_clicked_hook_before { shift->{clicked_hook_before} = $_[1] } sub set_clicked_hook_after { shift->{clicked_hook_after} = $_[1] } sub get_gtk_ok_button { shift->{gtk_ok_button} } sub get_gtk_cancel_button { shift->{gtk_cancel_button} } sub get_gtk_apply_button { shift->{gtk_apply_button} } sub set_gtk_ok_button { shift->{gtk_ok_button} = $_[1] } sub set_gtk_cancel_button { shift->{gtk_cancel_button} = $_[1] } sub set_gtk_apply_button { shift->{gtk_apply_button} = $_[1] } sub new { my $class = shift; my %par = @_; my ($buttons, $clicked_hook_before, $clicked_hook_after) = @par{'buttons','clicked_hook_before','clicked_hook_after'}; my $self = $class->SUPER::new(@_); $buttons ||= { ok => 1, cancel => 1 }; $self->set_buttons($buttons); $self->set_clicked_hook_before($clicked_hook_before); $self->set_clicked_hook_after($clicked_hook_after); return $self; } sub cleanup { my $self = shift; $self->SUPER::cleanup(@_); $self->set_gtk_ok_button(undef); $self->set_gtk_cancel_button(undef); $self->set_gtk_apply_button(undef); 1; } sub object_to_widget { my $self = shift; my $buttons; $buttons = $self->get_object_value if $self->get_attr; $buttons ||= "all"; foreach my $button ( keys %{$self->get_buttons} ) { my $active = $buttons eq 'all' || $buttons->{$button}; my $gtk_widget = $self->{"gtk_".$button."_button"}; next if not $gtk_widget; if ( $self->get_inactive eq 'invisible' ) { if ( $active ) { $gtk_widget->show; } else { $gtk_widget->hide; } } else { $gtk_widget->set_sensitive($active); } } 1; } sub cancel_button_clicked { my $self = shift; my $clicked_hook_before = $self->get_clicked_hook_before; my $clicked_hook_after = $self->get_clicked_hook_after; my $default_handler = 1; $default_handler = &$clicked_hook_before("cancel") if $clicked_hook_before; return if not $default_handler; $self->get_form_factory->cancel; &$clicked_hook_after("cancel") if $clicked_hook_after; 1; } sub apply_button_clicked { my $self = shift; my $clicked_hook_before = $self->get_clicked_hook_before; my $clicked_hook_after = $self->get_clicked_hook_after; my $default_handler = 1; $default_handler = &$clicked_hook_before("apply") if $clicked_hook_before; return if not $default_handler; $self->get_form_factory->apply; &$clicked_hook_after("apply") if $clicked_hook_after; 1; } sub ok_button_clicked { my $self = shift; my $clicked_hook_before = $self->get_clicked_hook_before; my $clicked_hook_after = $self->get_clicked_hook_after; my $default_handler = 1; $default_handler = &$clicked_hook_before("ok") if $clicked_hook_before; return if not $default_handler; $self->get_form_factory->ok if $self->get_form_factory; &$clicked_hook_after("ok") if $clicked_hook_after; 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::DialogButtons - Standard Ok, Apply, Cancel Buttons =head1 SYNOPSIS Gtk2::Ex::FormFactory::DialogButtons->new ( clicked_hook_before => CODEREF, clicked_hook_after => CODEREF, ... Gtk2::Ex::FormFactory::Container attributes Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a typical Ok, Apply, Cancel buttonbox in a Gtk2::Ex::FormFactory framework. If you associate an application object attribute the value needs to be a hash which may contain the keys 'ok', 'apply' and 'cancel' to control the activity of the correspondent buttons. Wheter inactive buttons should be render insensitive or invisible is controlled by the Gtk2::Ex::FormFactory::Widget attribute B<inactive>. By default the following methods of the associated Gtk2::Ex::FormFactory instance are triggered: Ok Gtk2::Ex::FormFactory->ok Cancel Gtk2::Ex::FormFactory->cancel Apply Gtk2::Ex::FormFactory->apply =head1 NOTES No I<Cancel> and I<Apply> buttons are generated if the associated Gtk2::Ex::FormFactory has the B<sync> attribute set. A synchronized FormFactory applies all changes immediately to the underlying objects, so there is no easy way of implementing the Apply and Cancel buttons. You can implement these by your own e.g. by registering a copy of your object to Gtk2::Ex::FormFactory::Context and hook into the button clicks using the B<clicked_hook_before> attribute described beyond. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::DialogButtons =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<clicked_hook_before> = CODEREF("ok"|"apply"|"cancel") Use this callback to hook into the clicked signal handler of the buttons. Argument is the name of the button actually clicked ("ok", "apply" or "cancel"). If the callback returns TRUE, Gtk2::Ex::FormFactory's default handler for the button is called afterwards. Return FALSE to prevent calling the default handler. =item B<clicked_hook_after> = CODEREF("ok"|"apply"|"cancel") This callback is called B<after> the default handler. This is useful if you want to exit your program cleanly if your main dialog was closed. First Gtk2::Ex::FormFactory closes the dialog window for you, doing all necessary cleanup stuff. Afterward you simply call Gtk2->main_quit to exit the program. The callback's return value doesn't matter. =back For more attributes refer to Gtk2::Ex::FormFactory::Widget. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Window.pm������������������������������������������0000644�0001750�0001750�00000006650�10400606040�022062� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Window; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "window" } sub get_closed_hook { shift->{closed_hook} } sub get_quit_on_close { shift->{quit_on_close} } sub set_closed_hook { shift->{closed_hook} = $_[1] } sub set_quit_on_close { shift->{quit_on_close} = $_[1] } sub new { my $class = shift; my %par = @_; my ($closed_hook, $quit_on_close) = @par{'closed_hook','quit_on_close'}; my $self = $class->SUPER::new(@_); $self->set_closed_hook($closed_hook); $self->set_quit_on_close($quit_on_close); return $self; } sub object_to_widget { my $self = shift; $self->get_gtk_parent_widget->set_title($self->get_object_value); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Window - A Window in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Window->new ( closed_hook => Code reference to be called on window close, quit_on_close => Quit Gtk2 mainloop if windows is closed ... Gtk2::Ex::FormFactory::Container attributes Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a Window in a Gtk2::Ex::FormFactory framework. The window title may be controlled by an associated application object. By default a Window automatically is implemented with an VBox. So you can add more than one widget to a Gtk2::Ex::FormFactory::Window in contrast to a Gtk2::Window. Note: if a window has a Gtk2::Ex::FormFactory parent, the FormFactory is closed automatically when the window gets destroyed. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container +--- Gtk2::Ex::FormFactory::Window Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<closed_hook> = CODEREF [optional] This code reference is called, when the window gets destroyed, e.g. because the user closes the window using the window manager's close button, or the program calls GtkWindow->destroy directly. If no B<closed_hook> is set an internal hook is connected which closes the Gtk2::Ex::FormFactory of this Window. =item B<quit_on_close> = BOOL [optional] If this is set to true the Gtk2 mainloop quits when the window is closed and no B<closed_hook> is set. If you use a B<closed_hook> quitting the mainloop is up to you. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ����������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/HSeparator.pm��������������������������������������0000644�0001750�0001750�00000003270�10400606040�022656� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::HSeparator; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "hseparator" } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::HSeparator - A HSeparator in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::HSeparator->new ( ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a HSeparator in a Gtk2::Ex::FormFactory framework. No application object attributes are associated with a HSeparator. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::HSeperator Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Widget. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/VBox.pm��������������������������������������������0000644�0001750�0001750�00000006051�10400606040�021464� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::VBox; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "vbox" } sub get_homogenous { shift->{homogenous} } sub get_spacing { shift->{spacing} } sub get_no_frame { shift->{no_frame} } sub set_homogenous { shift->{homogenous} = $_[1] } sub set_spacing { shift->{spacing} = $_[1] } sub set_no_frame { shift->{no_frame} = $_[1] } sub new { my $class = shift; my %par = @_; my ($homogenous, $spacing, $no_frame) = @par{'homogenous','spacing','no_frame'}; my $self = $class->SUPER::new(@_); $self->set_homogenous($homogenous); $self->set_spacing($spacing); $self->set_no_frame($no_frame); return $self; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::VBox - A VBox in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::VBox->new ( homogenous => Bool, spacing => Integer, no_frame => Bool, ... Gtk2::Ex::FormFactory::Container attributes Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a VBox in a Gtk2::Ex::FormFactory framework. No application object attributes are associated with a VBox. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container +--- Gtk2::Ex::FormFactory::VBox Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<homogenous> = BOOL [optional] If set to TRUE all children in this VBox will have the same height. This is a convenience attribute for the B<homogenous> property of Gtk2::Box. =item B<spacing> = INTEGER [optional] The number of pixels between the child widgets in this VBox. This is a convenience attribute for the B<spacing> property of Gtk2::Box. =item B<no_frame> = BOOL [optional] By default a frame is added to the VBox if it has a B<title>. Set B<no_frame> to a true value to supress this. =back For more attributes refer to Gtk2::Ex::FormFactory::Container. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/ToggleButton.pm������������������������������������0000644�0001750�0001750�00000013125�11275531725�023245� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::ToggleButton; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "toggle_button" } sub has_label { 1 } sub get_true_label { shift->{true_label} } sub get_false_label { shift->{false_label} } sub get_stock { shift->{stock} } sub get_image { shift->{image} } sub get_clicked_hook { shift->{clicked_hook} } sub set_true_label { shift->{true_label} = $_[1] } sub set_false_label { shift->{false_label} = $_[1] } sub set_stock { shift->{stock} = $_[1] } sub set_image { shift->{image} = $_[1] } sub set_clicked_hook { shift->{clicked_hook} = $_[1] } sub new { my $class = shift; my %par = @_; my ($true_label, $false_label, $label, $clicked_hook) = @par{'true_label','false_label','label','clicked_hook'}; my ($stock, $image) = @par{'stock','image'}; my $self = $class->SUPER::new(@_); $true_label = "Yes" if !defined($true_label) && !$stock; $false_label = "No" if !defined($false_label) && !$stock; $label = $false_label unless defined $label; $self->set_label($label); $self->set_true_label($true_label); $self->set_false_label($false_label); $self->set_stock($stock); $self->set_image($image); $self->set_clicked_hook($clicked_hook); return $self; } sub object_to_widget { my $self = shift; $self->get_gtk_widget->set_active($self->get_object_value); $self->update_button_label; 1; } sub widget_to_object { my $self = shift; $self->set_object_value ($self->get_gtk_widget->get_active ? 1 : 0); 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value ($self->get_gtk_widget->get_active ? 1 : 0); 1; } sub restore_widget_value { my $self = shift; $self->get_gtk_widget->set_active($self->get_backup_widget_value); 1; } sub get_widget_check_value { $_[0]->get_gtk_widget->get_active; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->signal_connect_after ( toggled => sub { $self->update_button_label; $self->widget_value_changed; }, ); 1; } sub update_button_label { my $self = shift; return if $self->get_true_label eq $self->get_false_label; my $gtk_widget; if ( $self->get_stock ) { my $gtk_align = ($self->get_gtk_parent_widget->get_children)[0]; my $gtk_hbox = ($gtk_align->get_children)[0]; $gtk_widget = ($gtk_hbox->get_children)[1]; } else { $gtk_widget = $self->get_gtk_widget; } my $value = $self->get_gtk_widget->get_active; if ( $value ) { $gtk_widget->set_label($self->get_true_label); } else { $gtk_widget->set_label($self->get_false_label); } 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::ToggleButton - A ToggleButton in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::ToggleButton->new ( true_label => Label of the activated button, false_label => Label of the deactivated button, stock => Name of stock image for this button, clicked_hook => Coderef to called on clicking, image => Filename of image to put on button, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a ToggleButton in a Gtk2::Ex::FormFactory framework. The state of the ToggleButton is controlled by the associated application object attribute, which should has a boolean value. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::ToggleButton Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<true_label> = SCALAR [optional] Once the button is activated this label is set. =item B<false_label> = SCALAR [optional] Once the button is deactivated this label is set. =item B<clicked_hook> = CODEREF [optional] This is for convenience and connects the CODEREF to the clicked signal of the button. =item B<stock> = SCALAR [optional] You may specify the name of a stock item here, which should be added to the button, e.g. 'gtk-edit' for the standard Gtk Edit stock item. You may combine B<stock> and B<label> arbitrarily. =item B<image> = FILENAME [optional] Use just this image for the button. No additional label is applied. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Label.pm�������������������������������������������0000644�0001750�0001750�00000010313�10400606040�021621� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Label; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "label" } sub has_label { shift->get_object eq '' } sub get_with_markup { shift->{with_markup} } sub get_for { shift->{for} } sub get_bold { shift->{bold} } sub set_with_markup { shift->{with_markup} = $_[1] } sub set_for { shift->{for} = $_[1] } sub set_bold { shift->{bold} = $_[1] } sub new { my $class = shift; my %par = @_; my ($with_markup, $for, $bold) = @par{'with_markup','for','bold'}; my $self = $class->SUPER::new(@_); $self->set_with_markup($with_markup); $self->set_for($for); $self->set_bold($bold); return $self; } sub object_to_widget { my $self = shift; return unless $self->get_attr; if ( $self->get_with_markup ) { $self->get_gtk_widget->set_markup($self->get_object_value); } elsif ( $self->get_bold ) { $self->get_gtk_widget->set_markup("<b>".$self->get_object_value."</b>"); } else { $self->get_gtk_widget->set_text($self->get_object_value); } 1; } sub empty_widget { my $self = shift; $self->get_gtk_widget->set_text(""); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Label - A Label in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Label->new ( with_markup => Should the label render with markup?, bold => Should the label render as bold text?, for => Name for Widget this label belongs to, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a Label in a Gtk2::Ex::FormFactory framework. The text of the Label is the value of the associated application object attribute. If no object is associated with the Label, the text is taken from the standard B<label> attribute. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Label Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<with_markup> = BOOL [optional] If this is set to TRUE the Label may contain basic HTML markup, which is rendered accordingly. Refer to the "Pango Text Attribute Markup" chapter in the Gtk+ documentation for details. =item B<bold> = BOOL [optional] If set to TRUE the label is implicitly renderd as markup set into E<lt>b>...E<lt>/b> tags. =item B<for> = SCALAR [optional] If this label belongs to another widget pass the widget's name here. This way the label greys out automatically, if the widget gets inactive. You may not just specify a simple name here but reference siblings of the label widget as well by specifying sibling($nr) whereby $nr has a negative value to address siblings left from the Label and positive to address right siblings. E.g. sibling(-1) is the first left sibling and sibling(2) the second on the right. If you add your Widgets to a Gtk2::Ex::ForFactory::Form container the B<label> attributes of the Widgets are rendered automatically correspondently. But you need this feature for complex layouts not covered by Gtk2::Ex::ForFactory::Form, e.g. in a Gtk2::Ex::ForFactory::Table. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Intro.pm�������������������������������������������0000644�0001750�0001750�00000031705�10400606040�021705� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1; __END__ RFC Gtk2::Ex::FormFactory ------------------------- =head1 NAME Gtk2::Ex::FormFactory::Intro - Introduction into the FormFactory framework =head1 DESCRIPTION The Gtk2::Ex::FormFactory framework is for Perl Gtk2 developers who (at least partially ;) agree with these statements: =over 3 =item * GUI programming is fun but often boring =item * A lot of tasks in GUI programming are similar and misleads the lazy programmer to do too much Copy'n Paste =item * RAD tools like Glade are fine for small applications but not if you want to have a consistent look and feel in bigger and modular applications =back Gtk2::Ex::FormFactory tries to help you with these issues by =over 3 =item * Strictly separating GUI design, application logic and data structures =item * Giving the developer a more declarative style of defining the structure of your GUI =item * Giving the developer the possibility of definiting the design of the GUI at a single spot in your program =item * Being lightweight and easy to learn. =back =head2 Enough buzzwords Imagine you want to build a configuration dialog for your application, which consists of a notebook, to distinguish several topics, each containing a bunch of simpler widgets (in the following example a single text entry). Also it should have the usual Ok and Cancel buttons. The straight approach often is to code all the stuff by hand or "draw" all widgets using Glade. At any rate you need to take care of: =over 3 =item * Consistent look and feel, e.g. labels should be bold and properly aligned to the widgets; the widgets iteslf should have some space around them, buttons should always be aligned to the form above etc. =item * Initializing the widgets with the actual content of your configuration data =item * Either connecting a lot of signals to track the changes the user made. This would apply all changes straight to your internal data structure, which may make implementing the Cancel button difficult or impossible =item * Or grabbing all (changed) data from the widgets, when the user hit the Ok button resp. simply close the window, when the user hit the Cancel button =back That's a lot of stuff, which needs to be repeated for every single dialog in your application. No fun anymore. Gtk2::Ex::FormFactory will do the boring stuff for you. That's how it works: =head2 Register your objects to the Context Create a Gtk2::Ex::FormFactory::Context object and register all your objects, which should be presented/changed by the GUI, here: my $context = Gtk2::Ex::FormFactory::Context->new; $context->add_object ( name => "config", object => $config_object ); $config_object has at least the following methods in our example below: - get_data_dir() - get_selected_page() - set_data_dir() - set_selected_page() The Context is a layer which encapsulates the methods of accessing your object's attributes. Also the Context knows about relationships between objects and/or their attributes, so it's able to handle correspondent updates on the GUI side automatically. We will discuss more details of Gtk2::Ex::FormFactory::Context later in this document. =head2 Define the structure of your GUI Create Gtk2::Ex::FormFactory object and define the B<structure> of your GUI. E.g, you want to have window which contains a notebook, which consists of a few pages with a bunch of text entries in them. This will look this way: [ very compressed and evil nesting for this document - for bigger dialogs you will break this into several pieces ] my $ff = Gtk2::Ex::FormFactory->new ( context => $context, content => [ Gtk2::Ex::FormFactory::Window->new( title => "Preferences", content => [ Gtk2::Ex::FormFactory::Notebook->new ( attr => "config.selected_page", content => [ Gtk2::Ex::FormFactory::VBox->new ( title => "Filesystem", content => [ Gtk2::Ex::FormFactory::Form->new ( content => [ Gtk2::Ex::FormFactory::Entry->new ( attr => "config.data_dir", label => "Data Directory", tip => "This directory takes all your files.", rules => "writable-directory", ), ], ), ], ), ], ); Gtk2::Ex::FormFactory::DialogButtons->new ], ), ], ); $ff->open; # actually build the GUI and open the window $ff->update; # fill in the values from $config_object So now you defined that you want to have a text entry, which contains a valid writable directory name, which should be inside a form on a notebook page. No details about the exact layout yet, this is just the B<strucure> of your dialog =head2 But how is this rendered? For this task Gtk2::Ex::FormFactory creates a Gtk2::Ex::FormFactory::Layout object which takes care of all the rendering details. Gtk2::Ex::FormFactory has a default implementation of this, but you can easily inherit from this module to define your own layout (that's mainly for what all this is good for!) and pass it to the FormFactory as the B<layouter>. The Layout module mainly consists of two types of methods =over 4 =item B<Methods for building a widget> build_TYPE() methods for each FormFactory widget type (Entry, SelectList, Popup, Foo etc.) you use in your dialog. These have the Gtk2 code actually necessary to create the corresponding Gtk2 widgets. =item B<Methods for adding a widget to a container> These are the so called add_WIDGET_to_CONTAINER() methods, which specify how a particular widget type is added to a particular container type. E.g. they're responsible for consistent looking labels beside widgets etc. Because the details of adding a widget mainly depend on the container the widget is added to, there are generic methods for adding arbitrary widgets to a container. If there is no specific method for a widget type this generic method is called instead. =back =head2 Layout methods for our example The Layout implementation needs the following methods, to be able to generate a layout for our FormFactory defined above: build_window => creates a Gtk2::VBox in a Gtk2::Window build_notebook => creates a Gtk2::Notebook build_form => creates a Gtk2::Table (2 columns) build_entry => creates a Gtk2::Entry build_dialog_buttons => creates a ButtonBox with Ok/Apply/Cancel add_widget_to_form => adds entry to table, label in 1st column add_widget_to_notebook => adds form to notebook with tab title add_widget_to_window => adds notebook and buttonbox to the window If you regularly code applications with Gtk2 you know, that none of this tasks is rocket science. B<But> you have a lot of parameters for each widget in question to take care of (simply think of the border_width property which may lead to an ugly misaligned mess, if you don't handle it really consistently) Because you define this tasks at a B<single> point in your program, it's really easy to create a consistently looking application. Or to change the look quickly. E.g. you decide to put a frame around all your forms? Just change B<one> method - build_form() - and you're done! =head2 Huh, a lot of new Widget classes to learn! Not really. The FormFactory Widget classes are very simple and mainly wrap correspondent Gtk2 widgets, so you don't need to learn much more. Using the builtin widgets is really easy. They all ship with a manual page describing their specific attributes, which usualy isn't much. Also Gtk2::Ex::FormFactory has some nifty wrappers for really inconvenient Gtk2 widgets, like Gtk2::Table. Take a look at Gtk2::Ex::FormFactory::Table to learn how easy programming complex table layouts can be. Or look at Gtk2::Ex::FormFactory::Image which is a nice image widget which resizes the image automatically in configurable ranges. =head2 Building your own FormFactory widgets If you need more widgets: implement them on your own. Gtk2::Ex::FormFactory widget classes don't have much Gtk2 code in them, they just define the properties, which represent this particular form item and implement mainly the following methods: =over 3 =item * Define a short name for the type (e.g. "entry" for a Gtk2::Entry - the Layout->add_X_to_Y() methods are derived from the short name) =item * Transfer the object's attribute value to the widget =item * Transfer the widget's value to the object's attribute =item * Connect the 'changed' signal for a synchronized dialog, e.g. if you want to react immedately on user input =back What the widget and object "value" actually is (a scalar, hash, array or complex structure) may be arbitrarly defined. How object attributes are accessed, is defined in the Context module. Our example uses the default set_foo(), get_foo() style accessors, but there are more methods up to defining callbacks, which can do very complex lookups. =head2 Data consistency Now we know that the FormFactory suite solve layout issues very well. Another important feature is automatic data consistency resp. keeping the GUI and your application data in sync. Change an object attribute: the correspondent GUI widgets will update automatically. The user entered data to a text entry: the object attribute associated with this entry will automatically get the new text. Gtk2::Ex::FormFactory must know your application's objects very well to do such a magic. That's what the Gtk2::Ex::FormFactory::Context module is good for, mentioned shortly at the top of our example. =head2 Abstraction from your application's objects All your application objects are registered with a unique name to the Context module. Each FormFactory has a reference to this Context, so it know the objects which are registered. When you register your object to the Context, you may specify how attributes are accessed by setting prefixes for read/write accessors. You may even override methods inside the Context by specifying correspondent closures, which are called instead of the original method. Also objects in terms of the Context module may be abstract things like "The currently selected disc from the currently selected artist", not only a simply hardwired object reference. This is done by calling a closure returning the actual object instead of using a hardwired object. This way dependend widgets update automaticly, as soon as the correspondent selection changes, e.g. updating a list of CD titles when switching to another disc in an imaginary CD database program. =head2 Widget consistency Another challenge in a good GUI program is to make your widgets consistent in terms of graying out widgets, which are not useful in a particular state of your program. Gtk2::Ex::FormFactory manages visibility and sensivity of your widgets automatically for you once you registered the correspondent dependencies at the Context. E.g. if there currently is no CD album selected, the corresponding fields are greyed out automatically, including the field labels. =head2 Data validity Gtk2::Ex::FormFactory specifies Gtk2::Ex::FormFactory::Rules, which are checked against the values the user entered. These conditions must apply, otherwise the old values are restored automatically. A bunch of rules are shipped, but you can define your own set by specifying a correspondent rule object or closures. =head2 Extensibility This framework was designed with extensibility in mind. You can =over 3 =item * Define your own FormFactory widgets, by simply using the base class Gtk2::Ex::FormFactory::Widget resp. Gtk2::Ex::FormFactory::Container. No matter how complex your widget is as long as you provide correspondent object attribute accessors, which transfer the widget's state to the object and vice versa. =item * Define your own FormFactory Layout module, by deriving from the default Layout implementation and passing a correspondent object to the FormFactory constructor. =item * All items in your FormFactory have a name, which will be set by default or to a value, you pass to the item's constructor. This way your Layout implementation can even do very special things for very special widgets, without the need of creating an extra Widget module for this. =item * You can request any Gtk widget from a FormFactory widget by name to do further manipulation, although you should consider doing this inside your Layout implementation, to keep the "single point of layout" rule. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �����������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Proxy.pm�������������������������������������������0000644�0001750�0001750�00000033006�11535450235�021744� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Proxy; use strict; use Carp; my $NAME_CNT = 0; sub get_context { shift->{context} } sub get_name { shift->{name} } sub get_aggregated_by { shift->{aggregated_by} } sub get_set_prefix { shift->{set_prefix} } sub get_get_prefix { shift->{get_prefix} } sub get_attr_activity_href { shift->{attr_activity_href} } sub get_attr_accessors_href { shift->{attr_accessors_href} } sub get_attr_aggregate_href { shift->{attr_aggregate_href} } sub get_accessor { shift->{accessor} } sub get_changes_attr_filter { shift->{changes_attr_filter} } sub get_buffered { 0 } sub get_object_changed { shift->{object_changed} } sub set_object_changed { shift->{object_changed} = $_[1] } sub new { my $class = shift; my %par = @_; my ($context, $object, $name, $set_prefix, $get_prefix) = @par{'context','object','name','set_prefix','get_prefix'}; my ($attr_accessors_href, $attr_activity_href, $aggregated_by) = @par{'attr_accessors_href','attr_activity_href','aggregated_by'}; my ($accessor, $changes_attr_filter) = @par{'accessor','changes_attr_filter'}; $attr_accessors_href ||= {}, $attr_activity_href ||= {}; $name ||= "object_".$NAME_CNT++; my $self = bless { context => $context, object => $object, name => $name, aggregated_by => $aggregated_by, set_prefix => $set_prefix, get_prefix => $get_prefix, attr_activity_href => $attr_activity_href, attr_accessors_href => $attr_accessors_href, accessor => $accessor, changes_attr_filter => $changes_attr_filter, attr_aggregate_href => {}, }, $class; return $self; } sub get_object { my $self = shift; my $object = $self->{object}; ref $object eq 'CODE' ? &$object() : $object; } sub update_by_aggregation { my $self = shift; my $aggregated_by = $self->get_aggregated_by; my $object = $self->get_attr($aggregated_by); $self->set_object($object); 1; } sub set_object { my $self = shift; my ($object) = @_; #-- nothing to do if it's the same object #-- (eval{} is for catching Class::DBI exceptions if #-- $self->{object} was deleted in the meantime) return if eval { $object eq $self->{object} }; #-- reset changed status $self->set_object_changed(0); #-- set object $self->{object} = $object; #-- Update all object widgets my $context = $self->get_context; $context->update_object_widgets ($self->get_name); #-- Update aggregated objects my $attr_aggregate_href = $self->get_attr_aggregate_href; my ($attr, $child_object_name, $child_object); while ( ($attr, $child_object_name) = each %{$attr_aggregate_href} ) { $child_object = $self->get_attr($attr); $context->set_object($child_object_name, $child_object); } return $object; } sub get_attr { my $self = shift; my ($attr_name) = @_; if ( $attr_name =~ /^([^.]+)\.(.*)$/ ) { $self = $self->get_context->get_proxy($1); $attr_name = $2; } my $accessor = $self->get_accessor; my $object = $self->get_object; return &$accessor($object, $attr_name) if $accessor; my $method = $self->get_get_prefix.$attr_name; $accessor = $self->get_attr_accessors_href->{$method}; return if not $object; return &$accessor($object) if $accessor; return $object->$method(); } sub set_attr { my $self = shift; my ($attr_name, $attr_value, $no_widget_update) = @_; if ( $attr_name =~ /^([^.]+)\.(.*)$/ ) { $self = $self->get_context->get_proxy($1); $attr_name = $2; } $self->object_changed($attr_name); my $accessor = $self->get_accessor; my $object = $self->get_object; my $name = $self->get_name; my $rc; if ( $accessor ) { $rc = &$accessor($object, $attr_name, $attr_value); } else { my $set_prefix = $self->get_set_prefix; my $method = $set_prefix.$attr_name; $accessor = $self->get_attr_accessors_href->{$method}; $rc = $accessor ? &$accessor($object, $attr_value) : $object->$method($attr_value); } return $rc if $no_widget_update; $self->get_context ->update_object_attr_widgets($name, $attr_name, $object); my $child_object_name = $self->get_attr_aggregate_href->{$attr_name}; $self->get_context->set_object($child_object_name, $attr_value) if $child_object_name; return $rc; } sub set_attrs { my $self = shift; my ($attrs_href, $no_widget_update) = @_; my $set_prefix = $self->get_set_prefix; my $object = $self->get_object; my $name = $self->get_name; my $context = $self->get_context; my $accessors = $self->get_attr_accessors_href; my ($method, $attr_name, $attr_value, $accessor, $child_object_name); $accessor = $self->get_accessor; while ( ($attr_name, $attr_value) = each %{$attrs_href} ) { $self->object_changed($attr_name); if ( $accessor ) { &$accessor($object, $attr_name, $attr_value); } else { $method = $set_prefix.$attr_name; $accessor = $accessors->{$method}; $accessor ? &$accessor($object, $attr_value) : $object->$method($attr_value); } $accessor = undef; next if $no_widget_update; $context->update_object_attr_widgets( $name, $attr_name, $object ); $child_object_name = $self->get_attr_aggregate_href->{$attr_name}; $context->set_object($child_object_name, $attr_value) if $child_object_name; } 1; } sub get_attr_presets { my $self = shift; my ($attr_name) = @_; my $method = $self->get_get_prefix.$attr_name."_presets"; my $object = $self->get_object; my $accessor = $self->get_attr_accessors_href->{$method}; return &$accessor($object) if ref $accessor eq 'CODE'; return $accessor if $accessor; return $object->$method(); } sub get_attr_rows { my $self = shift; my ($attr_name) = @_; my $method = $self->get_get_prefix.$attr_name."_rows"; my $object = $self->get_object; my $accessor = $self->get_attr_accessors_href->{$method}; return &$accessor($object) if ref $accessor eq 'CODE'; return $accessor if $accessor; return $object->$method(); } sub get_attr_list { my $self = shift; my ($attr_name, $widget_name) = @_; my $method = $self->get_get_prefix.$attr_name."_list"; my $object = $self->get_object; my $accessor = $self->get_attr_accessors_href->{$method}; return &$accessor($object, $widget_name) if ref $accessor eq 'CODE'; return $accessor if $accessor; return $object->$method($widget_name); } sub get_attr_presets_static { my $self = shift; my ($attr_name) = @_; my $method = $self->get_get_prefix.$attr_name."_presets_static"; my $object = $self->get_object; my $accessor = $self->get_attr_accessors_href->{$method}; return &$accessor($object) if ref $accessor eq 'CODE'; return $accessor if $accessor; return 1 if not $object->can($method); return $object->$method(); } sub get_attr_rows_static { my $self = shift; my ($attr_name) = @_; my $method = $self->get_get_prefix.$attr_name."_rows_static"; my $object = $self->get_object; my $accessor = $self->get_attr_accessors_href->{$method}; return &$accessor($object) if ref $accessor eq 'CODE'; return $accessor if $accessor; return 1 if not $object->can($method); return $object->$method(); } sub get_attr_list_static { my $self = shift; my ($attr_name) = @_; my $method = $self->get_get_prefix.$attr_name."_list_static"; my $object = $self->get_object; return 1 if not $object->can($method); return $object->$method(); } sub get_attr_activity { my $self = shift; my ($attr_name) = @_; $Gtk2::Ex::FormFactory::DEBUG && print " proxy->get_attr_activity($attr_name)\n"; my $object = $self->get_object; return 0 if not defined $object; my $attr_activity_href = $self->get_attr_activity_href; return 1 if not $attr_activity_href or not exists $attr_activity_href->{$attr_name}; my $attr_activity = $attr_activity_href->{$attr_name}; return $attr_activity if not ref $attr_activity eq 'CODE'; return &$attr_activity($object); } sub object_changed { my $self = shift; my ($attr_name) = @_; my $changes_attr_filter = $self->get_changes_attr_filter; if ( !$changes_attr_filter || $attr_name !~ $changes_attr_filter ) { $self->set_object_changed(1); my $aggregated_by = $self->get_aggregated_by; if ( $aggregated_by ) { my $context = $self->get_context; my ($object, $attr) = $context->norm_object_attr($aggregated_by); $context->get_proxy($object)->object_changed($attr); } } 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Proxy - Proxy class for application objects =head1 SYNOPSIS #-- Proxies are always created through #-- Gtk2::Ex::FormFactory::Context, never #-- directly by the application. Gtk2::Ex::FormFactory::Proxy->new ( context => Gtk2::Ex::FormFactory::Context, object => Object instance or CODEREF, name => Name of this proxy, set_prefix => Method prefix for write accessors, get_prefix => Method prefix for read accessors, attr_accessors_href => Hashref with accessor callbacks, attr_activity_href => Hashref with activity callbacks, aggregated_by => Fully qualified attribute of the parent, changes_attr_filter => Regex for attributes which should not trigger the object's 'changed' status ); =head1 DESCRIPTION This class implements a generic proxy mechanism for accessing application objects and their attributes. It defines attributes of the associated object are accessed. You never instantiate objects of this class by yourself; they're created internally by Gtk2::Ex::FormFactory::Context when adding objects with the Context->add_object() method. But you may use the proxy objects e.g. for updates which affect the application object and the GUI as well. You can receive Proxy objects using the Gtk2::Ex::FormFactory::Context->get_proxy() method. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors. =over 4 =item B<context> = Gtk2::Ex::FormFactory::Context [mandatory] The Context this proxy belongs to. =item B<object> = Object instance | CODEREF The application object itself or a code reference, which returns the object instance. =item B<name> = SCALAR [mandatory] The Context wide unique name of this Proxy. =item B<set_prefix> = SCALAR [optional] This is the method prefix for write accessors. Defaults to B<set_>. =item B<get_prefix> = SCALAR [optional] This is the method prefix for read accessors. Defaults to B<get_>. =item B<attr_accessors_href> = HASHREF [optional] With this hash you can override specific accessors with a code reference, which is called instead of the object's own accessor. Refer to Gtk2::Ex::FormFactory::Context->add_object for details. =item B<attr_activity_href> = HASHREF [optional] This hash defines callbacks for attributes which return the activity state of the corresonding attribute. Refer to Gtk2::Ex::FormFactory::Context->add_object for details. =item B<aggregated_by> = "object.attr" [optional] Fully qualified attribute of the parent aggregating this object Refer to Gtk2::Ex::FormFactory::Context->add_object for details. =item B<changes_attr_filter> = REGEX [optional] Refer to Gtk2::Ex::FormFactory::Context->add_object for details. =item B<object_changed> = BOOLEAN This flag indicates whether the object represented by this Proxy was changed. You may set this attribute to reset the object's changed state. =back =head1 METHODS =over 4 =item $app_object = $proxy->B<get_object> () This returns the actual application object of this Proxy, either the statical assigned instance or a dynamicly retrieved instance. =item $proxy->B<set_object> ($object) Changes the application object instance in this Proxy. All dependend Widgets on the GUI are updated accordingly. =item $app_object_attr_value = $proxy->B<get_attr> ($attr) Returns the application object's attribute B<$attr> of this Proxy. If $attr has the form "object.attr" the attribute of the correspondent object is retreived, instead of the object associated with this proxy. =item $proxy->B<set_attr> ($attr => $value) Changes the application object's attribute B<$attr> to B<$value> and updates all dependend Widgets on the GUI accordingly. If $attr has the form "object.attr" the correspondent object will be updated, instead of the object associated with this proxy. =item $proxy->B<set_attrs> ( { $attr => $value, ... } ) Changes a bunch of application object's attributes, which is passed as a hash reference with B<$attr => $value> pairs and updates all dependend Widgets on the GUI accordingly. =item $activity = $proxy->B<get_attr_activity> ($attr) Returns the current activity state of B<$attr>. =item =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Button.pm������������������������������������������0000644�0001750�0001750�00000007102�11275531745�022103� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Button; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "button" } sub has_label { 1 } sub get_clicked_hook { shift->{clicked_hook} } sub get_stock { shift->{stock} } sub get_with_repeat { shift->{with_repeat} } sub get_image { shift->{image} } sub set_clicked_hook { shift->{clicked_hook} = $_[1] } sub set_stock { shift->{stock} = $_[1] } sub set_with_repeat { shift->{with_repeat} = $_[1] } sub set_image { shift->{image} = $_[1] } sub new { my $class = shift; my %par = @_; my ($stock, $clicked_hook, $with_repeat, $image) = @par{'stock','clicked_hook','with_repeat','image'}; my $self = $class->SUPER::new(@_); $self->set_stock($stock); $self->set_image($image); $self->set_clicked_hook($clicked_hook); $self->set_with_repeat($with_repeat); return $self; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Button - A Button in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Button->new ( clicked_hook => Callback CODEREF / Closure stock => Name of a stock item for this button, with_repeat => Trigger callback continuously as long button is pressed, image => Filename of image to put on button, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This module implements a Button in a Gtk2::Ex::FormFactory framework. No application object attribute is associated with the button. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Button Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<clicked_hook> = CODEREF [optional] This is for convenience and connects the CODEREF to the clicked signal of the button. =item B<stock> = SCALAR [optional] You may specify the name of a stock item here, which should be added to the button, e.g. 'gtk-edit' for the standard Gtk Edit stock item. You may combine B<stock> and B<label> arbitrarily. =item B<with_repeat> = BOOLEAN [optional] If you set this option the B<clicked_hook> is called continuously as long as the button is pressed, with a initial short delay, just similar to keyboard repetition. =item B<image> = FILENAME [optional] Use just this image for the button. No additional label is applied. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/TextView.pm����������������������������������������0000644�0001750�0001750�00000011722�11620726762�022411� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::TextView; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "text_view" } sub get_parse_tags { shift->{parse_tags} } sub set_parse_tags { shift->{parse_tags} = $_[1] } sub new { my $class = shift; my %par = @_; my ($scrollbars, $properties, $parse_tags) = @par{'scrollbars','properties','parse_tags'}; my $self = $class->SUPER::new(@_); $scrollbars ||= [ "automatic", "automatic" ]; if ( not $properties or not exists $properties->{wrap_mode} ) { $properties->{wrap_mode} = "word-char"; } $self->set_scrollbars ($scrollbars); $self->set_properties ($properties); $self->set_parse_tags ($parse_tags); return $self; } sub object_to_widget { my $self = shift; if ( not $self->get_parse_tags ) { $self->get_gtk_widget->get_buffer->set_text($self->get_object_value); return; } my $buffer = $self->get_gtk_widget->get_buffer; $buffer->set_text(""); my $iter = $buffer->get_end_iter; my $text = $self->get_object_value; my $processed = 0; while ( $text =~ m!(.*?)(<tag\s+name=["'])(.*?)(["']\s*>)(.*?)(</tag>)!sg ) { my ($pre, $tag_open, $tag_name, $tag_rest, $content, $tag_close) = ($1, $2, $3, $4, $5, $6); $processed += length($1.$2.$3.$4.$5.$6); $buffer->insert($iter, $pre); $buffer->insert_with_tags_by_name($iter, $content, $tag_name); } my $rest = substr($text, $processed); $buffer->insert($iter, $rest); 1; } sub widget_to_object { my $self = shift; my $buffer = $self->get_gtk_widget->get_buffer; $self->set_object_value ( $buffer->get_text( $buffer->get_start_iter, $buffer->get_end_iter, 1, ) ); 1; } sub empty_widget { my $self = shift; $self->get_gtk_widget->get_buffer->set_text(""); 1; } sub backup_widget_value { my $self = shift; my $buffer = $self->get_gtk_widget->get_buffer; $self->set_backup_widget_value ( $buffer->get_text( $buffer->get_start_iter, $buffer->get_end_iter, 1, ) ); 1; } sub restore_widget_value { my $self = shift; $self->get_gtk_widget ->get_buffer ->set_text($self->get_backup_widget_value); 1; } sub get_widget_check_value { $_[0]->get_gtk_widget->get_buffer->get_text; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->get_buffer->signal_connect ( changed => sub { $self->widget_value_changed }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::TextView - A TextView in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::TextView->new ( parse_tags => Boolean to indicate tag markup in value, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a TextView in a Gtk2::Ex::FormFactory framework. The content of the TextView is the value of the associated application object attribute. By default the TextView gets automatic horizontal and vertical scrollbars and word wrapping enabled, unless you specify your own values for these settings. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::TextView Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<parse_tags> = Boolean [optional] Set this to a true value to indicate the value of this widget contains tag markup. The syntax of tag markup is as follows: <tag name="TAGNAME">Some text</tag> <tag name='TAGNAME'>Some text</tag> Text with this markup will get the correspondent GtkTextView tag applied. Please refer to the Gtk2 documentation of GtkTextTagTable to learn more about tags. Use a B<customize_hook> of your Gtk2::Ex::FormFactory::TextView objekt to attach a custom tag table to this widget. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ����������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/GtkWidget.pm���������������������������������������0000644�0001750�0001750�00000005354�10400606040�022504� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::GtkWidget; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_custom_gtk_widget { shift->{custom_gtk_widget} } sub set_custom_gtk_widget { shift->{custom_gtk_widget} = $_[1] } sub get_type { "gtk_widget" } sub new { my $class = shift; my %par = @_; my ($custom_gtk_widget) = $par{'custom_gtk_widget'}; my $self = $class->SUPER::new(@_); $self->set_custom_gtk_widget($custom_gtk_widget); return $self; } sub cleanup { my $self = shift; $self->SUPER::cleanup(@_); $self->set_custom_gtk_widget(undef); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::GtkWidget - Wrap arbitrary Gtk widgets =head1 SYNOPSIS Gtk2::Ex::FormFactory::GtkWidget->new ( custom_gtk_widget => Gtk::Widget, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION With this module you can add arbitrary Gtk widgets to a FormFactory. They're simply displayed, but their state isn't managed by Gtk2::Ex::FormFactory, because no details are known about the widget. If you need the full functionality for a custom Gtk widget you need to implement your own Gtk2::Ex::FormFactory::Widget for it. Refer to the documentation of Gtk2::Ex::FormFactory::Widget for details about implementing your own FormFactory widgets. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::GtkWidget Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<custom_gtk_widget> = Gtk::Widget [mandatory] This is a Gtk::Widget to be displays inside a FormFactory. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/CheckButton.pm�������������������������������������0000644�0001750�0001750�00000006447�10400606040�023030� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::CheckButton; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "check_button" } sub get_detach_label { shift->{detach_label} } sub set_detach_label { shift->{detach_label} = $_[1] } sub has_label { ! shift->get_detach_label } sub new { my $class = shift; my %par = @_; my ($detach_label) = $par{'detach_label'}; my $self = $class->SUPER::new(@_); $self->set_detach_label($detach_label); return $self; } sub object_to_widget { my $self = shift; $self->get_gtk_widget->set_active($self->get_object_value); 1; } sub widget_to_object { my $self = shift; $self->set_object_value ($self->get_gtk_widget->get_active); 1; } sub empty_widget { my $self = shift; $self->get_gtk_widget->set_active(0); 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value ($self->get_gtk_widget->get_active); 1; } sub restore_widget_value { my $self = shift; $self->get_gtk_widget->set_active($self->get_backup_widget_value); 1; } sub get_widget_check_value { $_[0]->get_gtk_widget->get_active; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->signal_connect ( toggled => sub { $self->widget_value_changed }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::CheckButton - A CheckButton in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::CheckButton->new ( detach_label => BOOL, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a CheckButton in a Gtk2::Ex::FormFactory framework. The state of the CheckButton is controlled by the associated application object attribute, which should has a boolean value. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::CheckButton Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<detach_label> = BOOL [optional] Set this to TRUE if the label of this checkbox should be managed by the container (e.g. a Gtk2::Ex::FormFactory::Form) instead of having the label beside the checkbox (which is the default). =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Context.pm�����������������������������������������0000644�0001750�0001750�00000066240�11275517366�022267� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Context; use strict; use Carp; use Gtk2::Ex::FormFactory::ProxyBuffered; sub get_proxies_by_name { shift->{proxies_by_name} } sub get_widgets_by_attr { shift->{widgets_by_attr} } sub get_widgets_by_object { shift->{widgets_by_object} } sub get_depend_trigger_href { shift->{depend_trigger_href} } sub get_aggregated_by_href { shift->{aggregated_by_href} } sub get_update_hooks_by_object { shift->{update_hooks_by_object} } sub get_widget_activity_href { shift->{widget_activity_href} } sub get_default_set_prefix { shift->{default_set_prefix} } sub get_default_get_prefix { shift->{default_get_prefix} } sub set_default_set_prefix { shift->{default_set_prefix} = $_[1] } sub set_default_get_prefix { shift->{default_get_prefix} = $_[1] } sub new { my $class = shift; my %par = @_; my ($default_set_prefix, $default_get_prefix) = @par{'default_set_prefix','default_get_prefix'}; $default_set_prefix = "set_" if not defined $default_set_prefix; $default_get_prefix = "get_" if not defined $default_get_prefix; my $self = bless { default_set_prefix => $default_set_prefix, default_get_prefix => $default_get_prefix, proxies_by_name => {}, # ->{"$object"} = $proxy widgets_by_attr => {}, # ->{"$object.$attr"}->{"$widget_ff_name.$widget_name"} = $widget widgets_by_object => {}, # ->{"$object"} = $widget update_hooks_by_object => {}, # ->{"$object"} = CODEREF depend_trigger_href => {}, # ->{"$master_object.$master_attr"}->{"$slave_object.$slave_attr"} = 1 aggregated_by_href => {}, # ->{"$object.$attr"}->{"$object"} = 1 widget_activity_href => {}, # ->{"$object.$attr"}->{"$widget_name"} = $widget }, $class; $self->add_object( name => "__dummy", object => bless {}, "Gtk2::Ex::FormFactory::Dummy", ); return $self; } sub norm_object { my $self = shift; my ($object) = @_; my ($o, $a) = split(/\./, $object); return $o; } sub norm_object_attr { my $self = shift; my ($object, $attr) = @_; $object = '' if ! defined $object; $attr = '' if ! defined $attr; return "" if $object eq '' && $attr eq ''; return $attr if defined $attr && $attr =~ /\./; return $object if !defined $attr || $attr eq ''; if ( $object =~ /\./ ) { my ($o, $a) = split(/\./, $object); return "$o.$attr"; } die "Illegal object.attr definition object='$object' attr='$attr'" if $object eq ''; return ($object, $attr) if wantarray; return "$object.$attr"; } sub add_object { my $self = shift; my %par = @_; my ($name, $object, $set_prefix, $get_prefix, $attr_activity_href) = @par{'name','object','set_prefix','get_prefix','attr_activity_href'}; my ($attr_depends_href, $attr_accessors_href, $update_hook) = @par{'attr_depends_href','attr_accessors_href','update_hook'}; my ($aggregated_by, $accessor, $buffered, $changes_attr_filter) = @par{'aggregated_by','accessor','buffered','changes_attr_filter'}; $set_prefix = $self->get_default_set_prefix if ! defined $set_prefix; $get_prefix = $self->get_default_get_prefix if ! defined $get_prefix; if ( $attr_depends_href ) { my $depend_trigger_href = $self->get_depend_trigger_href; foreach my $attr ( keys %{$attr_depends_href} ) { if ( not ref $attr_depends_href->{$attr} ) { my $depends_attr = $attr_depends_href->{$attr}; $depends_attr = $self->norm_object_attr($name, $depends_attr); $depend_trigger_href->{$depends_attr}->{"$name.$attr"} = 1; } elsif ( ref $attr_depends_href->{$attr} eq 'ARRAY' ) { foreach my $depends_attr ( @{$attr_depends_href->{$attr}} ) { $depends_attr = $self->norm_object_attr($name, $depends_attr); $depend_trigger_href->{$depends_attr}->{"$name.$attr"} = 1; } } else { croak "Illegal attr_depends_href value for attribute '$attr'"; } } } my $proxies_by_name = $self->get_proxies_by_name; die "Object with name '$name' already registered to this context" if $proxies_by_name->{$name}; $self->get_update_hooks_by_object->{$name} = $update_hook if $update_hook; if ( $aggregated_by ) { my ($parent_object, $parent_attr) = split(/\./, $aggregated_by); die "aggregated_by definition of object '$name' needs an attr" unless $parent_attr; my $parent_proxy = $self->get_proxy($parent_object); $parent_proxy->get_attr_aggregate_href->{$parent_attr} = $name; $self->get_aggregated_by_href->{$aggregated_by}->{$name} = 1; } my $proxy_class = $buffered ? "Gtk2::Ex::FormFactory::ProxyBuffered" : "Gtk2::Ex::FormFactory::Proxy"; return $proxies_by_name->{$name} = $proxy_class->new ( context => $self, name => $name, object => $object, set_prefix => $set_prefix, get_prefix => $get_prefix, attr_activity_href => $attr_activity_href, attr_accessors_href => $attr_accessors_href, aggregated_by => $aggregated_by, accessor => $accessor, changes_attr_filter => $changes_attr_filter, ); } sub remove_object { my $self = shift; my ($name) = @_; my $proxies_by_name = $self->get_proxies_by_name; die "Object with name '$name' not registered to this context" unless $proxies_by_name->{$name}; $proxies_by_name->{$name}->set_object(undef); delete $proxies_by_name->{$name}; 1; } sub register_widget { my $self = shift; my ($widget) = @_; my $object = $widget->get_object; if ( $widget->get_active_depends ) { my $dep = $widget->get_active_depends; $dep = [ $dep ] unless ref $dep eq 'ARRAY'; for my $oa ( @{$dep} ) { my $norm_oa = $self->norm_object_attr($oa); my $norm_o = $self->norm_object($oa); $self->get_widget_activity_href ->{$self->norm_object_attr($norm_oa)} ->{$widget->get_name} = $widget; if ( $norm_oa ne $norm_o ) { $self->get_widget_activity_href ->{$self->norm_object_attr($norm_o)} ->{$widget->get_name} = $widget; } } } my $object_attr = $self->norm_object_attr($widget->get_object, $widget->get_attr); return if $object_attr eq ''; my $widget_full_name = $widget->get_form_factory->get_name.".". $widget->get_name; $Gtk2::Ex::FormFactory::DEBUG && print "REGISTER: $object_attr => $widget_full_name\n"; $self->get_widgets_by_attr ->{$object_attr} ->{$widget_full_name} = $widget; if ( $widget->has_additional_attrs ) { my $add_attrs = $widget->has_additional_attrs; my $object = $widget->get_object; foreach my $add_attr ( @{$add_attrs} ) { my $get_attr_name_method = "get_attr_$add_attr"; my $attr = $widget->$get_attr_name_method(); $self->get_widgets_by_attr ->{$self->norm_object_attr($object, $attr)} ->{$widget_full_name} = $widget; } } $self->get_widgets_by_object ->{$widget->get_object} ->{$widget_full_name} = $widget; 1; } sub deregister_widget { my $self = shift; my ($widget) = @_; $Gtk2::Ex::FormFactory::DEBUG && print "DEREGISTER ".$widget->get_name."\n"; my $object = $widget->get_object; my $object_attr = $self->norm_object_attr($widget->get_object, $widget->get_attr); return if $object_attr eq ''; my $widget_full_name = $widget->get_form_factory->get_name.".". $widget->get_name; $Gtk2::Ex::FormFactory::DEBUG && print "DEREGISTER: $object_attr => $widget_full_name\n"; warn "Widget not registered ($object_attr => $widget_full_name)" unless $self->get_widgets_by_attr ->{$object_attr} ->{$widget_full_name}; delete $self->get_widgets_by_attr ->{$object_attr} ->{$widget_full_name}; delete $self->get_widgets_by_attr->{$object_attr} if keys %{$self->get_widgets_by_attr->{$object_attr}} == 0; if ( $widget->get_active_depends ) { my $dep = $widget->get_active_depends; $dep = [ $dep ] unless ref $dep eq 'ARRAY'; for my $oa ( @{$dep} ) { my $norm_oa = $self->norm_object_attr($oa); my $norm_o = $self->norm_object($oa); delete $self->get_widget_activity_href ->{$self->norm_object_attr($norm_oa)} ->{$widget->get_name}; if ( $norm_oa ne $norm_o ) { delete $self->get_widget_activity_href ->{$self->norm_object_attr($norm_o)} ->{$widget->get_name}; } } } if ( $widget->has_additional_attrs ) { my $add_attrs = $widget->has_additional_attrs; my $object = $widget->get_object; foreach my $add_attr ( @{$add_attrs} ) { my $get_attr_name_method = "get_attr_$add_attr"; my $attr = $widget->$get_attr_name_method(); my $norm_attr = $self->norm_object_attr($object, $attr); delete $self->get_widgets_by_attr ->{$norm_attr} ->{$widget_full_name}; delete $self->get_widgets_by_attr->{$norm_attr} if keys %{$self->get_widgets_by_attr->{$norm_attr}} == 0; } } delete $self->get_widgets_by_object ->{$widget->get_object} ->{$widget_full_name}; 1; } sub get_proxy { my $self = shift; my ($name) = @_; $name = $self->norm_object($name); my $proxy = $self->get_proxies_by_name->{$name}; croak "Object '$name' not added to this context" unless $proxy; return $proxy; } sub get_object { my $self = shift; my ($name) = @_; $name = $self->norm_object($name); my $proxy = $self->get_proxies_by_name->{$name}; croak "Object '$name' not added to this context" unless $proxy; return $proxy->get_object; } sub set_object { my $self = shift; my ($name, $object) = @_; $name = $self->norm_object($name); my $proxy = $self->get_proxies_by_name->{$name}; croak "Object $name not added to this context" unless $proxy; $proxy->set_object($object); return $object; } sub set_object_attr { my $self = shift; my ($object_name, $attr_name, $value); if ( @_ == 2 ) { ($object_name, $attr_name) = split(/\./, $_[0]); $value = $_[1]; } elsif ( @_ == 3 ) { ($object_name, $attr_name, $value) = @_; } else { croak qq[Usage: set_object_attr("object.attr","value")]. qq[ set_object_attr("object","attr","value")]; } my $proxy = $self->get_proxies_by_name->{$object_name} or die "Object '$object_name' not registered"; $proxy->set_attr($attr_name, $value); return $value; } sub get_object_attr { my $self = shift; my ($object_name, $attr_name); if ( @_ == 1 ) { ($object_name, $attr_name) = split(/\./, $_[0]); } elsif ( @_ == 2 ) { ($object_name, $attr_name) = @_; } else { croak qq[Usage: get_object_attr("object.attr")]. qq[ get_object_attr("object","attr")]; } my $proxy = $self->get_proxies_by_name->{$object_name} or die "Object '$object_name' not registered"; return $proxy->get_attr($attr_name); } sub update_object_attr_widgets { my $self = shift; my ($object, $attr) = @_; my $object_attr = $self->norm_object_attr($object, $attr); $Gtk2::Ex::FormFactory::DEBUG && print "update_object_attr_widgets($object, $attr)\n"; my $widgets_by_attr = $self->get_widgets_by_attr; my $depend_trigger_href = $self->get_depend_trigger_href; my $widget_activity_href = $self->get_widget_activity_href; foreach my $w ( values %{$widgets_by_attr->{$object_attr}} ) { $w->update; } foreach my $w ( values %{$widget_activity_href->{$object_attr}} ) { $w->update; } foreach my $update_object_attr ( keys %{$depend_trigger_href->{$object_attr}} ) { foreach my $w ( values %{$widgets_by_attr->{$update_object_attr}} ) { $w->update; } foreach my $name ( keys %{$self->get_aggregated_by_href->{$update_object_attr}} ) { $self->get_proxy($name)->update_by_aggregation; } } 1; } sub update_object_widgets { my $self = shift; my ($name) = @_; $Gtk2::Ex::FormFactory::DEBUG && print "update_object_widgets($name)\n"; $name = $self->norm_object_attr($name); my $object = $self->get_object($name); my $change_state = defined $object ? '' : 'empty,inactive'; my $widgets_by_object = $self->get_widgets_by_object; foreach my $w ( values %{$widgets_by_object->{$name}} ) { $w->update($change_state); } my $widget_activity_href = $self->get_widget_activity_href; foreach my $w ( values %{$widget_activity_href->{$name}} ) { $w->update($change_state); } my $update_hook = $self->get_update_hooks_by_object->{$name}; &$update_hook($object) if $update_hook; 1; } sub update_object_widgets_activity { my $self = shift; my ($name, $activity) = @_; warn "activity !(empty|inactive|active)" unless $activity =~ /^empty|inactive|active$/; $Gtk2::Ex::FormFactory::DEBUG && print "update_object_activity($name)\n"; my $widgets_by_object = $self->get_widgets_by_object; foreach my $w ( values %{$widgets_by_object->{$name}} ) { $w->update; } 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Context - Context in a FormFactory framework =head1 SYNOPSIS my $context = Gtk2::Ex::FormFactory::Context->new ( default_get_prefix => Default prefix for read accessors, default_set_prefix => Default prefix for write accessors, ); $context->add_object ( name => Name of the application object in this Context, aggregated_by => Object.Attribute of the parent object aggregating this object object => The application object itself or a callback which returns the object, or undef if aggregated or set later get_prefix => Prefix for read accessors, set_prefix => Prefix for write accessors, accessor => CODEREF which handles access to all object attributes attr_activity_href => Hash of CODEREFS for attributes which return activity of the corresponding attributes, attr_depends_href => Hash defining attribute dependencies, attr_accessors_href => Hash of CODEREFS which override correspondent accessors in this Context, buffered => Indicates whether changes should be buffered, changes_attr_filter => Regex for attributes which should not trigger the object's 'changed' status ); =head1 DESCRIPTION This module implements a very importent concept of the Gtk2::Ex::FormFactory framework. The Context knows of all your application objects, how attributes of the objects can be accessed (for reading and writing), which attributes probably depend on other attributes and knows of how to control the activity state of attributes resp. of the Widgets which represent these attributes. So the Context is a layer between your application objects and the GUI which represents particular attributes of your objects. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Context =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after associated FormFactory's were built. =over 4 =item B<default_get_prefix> = SCALAR [optional] Usually your application's objects use a common prefix for all attribute accessors. This defines the default prefix for read accessors and defaults to "B<get_>". =item B<default_set_prefix> = SCALAR [optional] Usually your application's objects use a common prefix for all attribute accessors. This defines the default prefix for write accessors and defaults to "B<set_>". =back =head1 METHODS =over 4 =item $context->B<add_object> (...) All your application objects must be added to the Context using this method. Parameters are passed to the method as a hash: =over 4 =item B<name> = SCALAR [mandatory] Each object in a Context need a unique name, so this parameter is mandatory. You refer to this name when you create Widgets and want to associate these with your application object's attributes. =item B<object> = BLESSED REF|CODEREF [optional] This is the application object itself, or a code reference which returns the object. Using the code reference option gives you very flexible control of what this object actually is. But also note that this may have some impact on performance, because this code reference will be called quite often. Often objects are aggregated by other objects, in that case don't set the object reference here but use the B<aggregate_by> option described below. An application object in terms of the Context may become undef, that's why the B<object> parameter is optional here. Also the code reference may return undef. Once an object gets undef, all associated widgets will be set inactive automatically. You can control per widget if it should render invisible or insensitive in that case. Refer to L<Gtk2::Ex::FormFactory::Widget> for details. =item B<aggregated_by> = "object.attr" [optional] If this object has a parent object set this option to the fully qualified attribute holding the object reference, using the object dot attribute notation: object.attr where B<object> is the name of the parent object used to register it to this Context, and B<attr> the attribute holding the reference to the object currently added to the Context. Once this attribute resp. the parent object change, the Context will be updated automatically, including all widgets depending on this widget. This way you can define your full object aggregation hierarchy and Gtk2::Ex::FormFactory takes care of all resulting dependencies on the GUI. =item B<get_prefix> = SCALAR [optional] With this parameter you can override the B<default_get_prefix> setting of this Context for this object. =item B<set_prefix> = SCALAR [optional] With this parameter you can override the B<default_set_prefix> setting of this Context for this object. =item B<accessor> = CODEREF(object,attr[,value]) [optional] If B<accessor> is set this code reference is used as a generic accessor callback for all attributes. It handles getting and setting as well. Called with two arguments the passed attribute is to be read, with three arguments, the third argument is the value which is to be assigned to the attribute. This overrides B<attr_accessors_href> described beyond. =item B<attr_accessors_href> = HASHREF [optional] Often your application object attribute values doesn't fit the data type a particular Widget expects, e.g. in case of the Gtk2::Ex::FormFactory::List widget, which expects a two dimensional array for its content. Since you need this conversion only for a particular GUI task it makes sense to implement the conversion routine in the Context instead of adding such GUI specific methods to your underlying classes, which should be as much GUI independent as possible. That's why you can override arbitrary accessors (read and write) using the B<attr_accessors_href> parameter. Key is the name of method to be overriden and constant scalar value or a code reference, which is called instead of the real method. The code reference gets your application object as the first parameter, as usual for object methods, and additionally the new value in case of write access. A short example. Here we override the accessors B<get_tracks> and B<set_tracks> of an imagnary B<disc> object, which represents an audio CD. The track title is stored as a simple array and needs to be converted to a two dimensional array as expected by Gtk2::Ex::FormFactory::List. Additionally an constant accessor is defined for a Gtk2::Ex::FormFactory::Popup showing a bunch of music genres: $context->add_object ( name => "disc", attr_accessors_href => { get_tracks => sub { my $disc = shift; #-- Convert the one dimensional array of disc #-- tracks to the two dimensional array expected #-- by Gtk2::Ex::FormFactory::List. Also the number #-- of the track is added to the first column here my @list; my $nr = 1; foreach my $track ( @{$disc->get_tracks} ) { push @list, [ $nr++, $track ]; } return\@list; }, set_tracks => sub { my $disc = shift; my ($list_ref) = @_; #-- Convert the array back (the List is editable in #-- our example, so values can be changed). my @list; foreach my $row ( @{$list_ref} ) { push @list, $row->[1]; } $disc->set_tracks(\@list); return \@list; }, genre_list => { "rock" => "Rock", "pop" => "Pop", "elec" => "Electronic", "jazz" => "Jazz", }, }, ); =item B<attr_activity_href> = HASHREF [OPTIONAL] As mentioned earlier activity of Widgets is controlled by the Gtk2::Ex::FormFactory framework. E.g. if the an object becomes undef, all associated widgets render inactive. With the B<attr_activity_href> setting you can handle activity on attribute level, not only on object level. The key of the hash is the attribute name and value is a code reference, which returns TRUE or FALSE and control the activity this way. Again an example: imagine a text entry which usually is set with a default value controlled by your application. But if the user wants to override the entry he first has to press a correpondent checkbox to activate this. $context->add_object ( name => "person", attr_activity_href => { ident_number => sub { my $person = shift; return $person->get_may_override_ident_number; }, }, attr_depends_href => { ident_number => "person.may_override_ident_number", }, ); For details about the B<attr_depends_href> option read on. =item B<attr_depends_href> = HASHREF [OPTIONAL] This hash defines dependencies between attributes. If you look at the example above you see why this is necessary. The B<ident_number> of a person may be overriden only if the B<may_override_ident_number> attribute of this person is set. Since this dependency is coded inside the code reference, Gtk2::Ex::FormFactory isn't aware of it until you add a corresponding B<attr_depends_href> entry. Now the GUI will automatically activate the Widget for the B<ident_number> attribute once B<may_override_ident_number> is set, e.g. by a CheckBox the user clicked. If an attribute depends on more than one other attributes you can use a list reference: attr_depends_href => sub { ident_number => [ "person.may_override_ident_number", "foo.bar", ], }, =item B<buffered> = BOOL [OPTIONAL] If set to TRUE this activates buffering for this object. Please refer to the BUFFERED CONTEXT OBJECTS chapter for details. =item B<changes_attr_filter> = REGEX [OPTIONAL] Gtk2::Ex::FormFactory maintains a flag indicating whether an object was changed. Under special circumstances you want specific attributes not affecting this "changed" state of an object. You can specify an regular expression here. Changes of attributes matching this expression won't touch the changes state of the object. To receive or change the object's changed state refer to the B<object_changed> attribute of Gtk2::Ex::FormFactory::Proxy. =back =item $context->B<remove_object> ( $name ) Remove the object $name from this context. =item $app_object = $context->B<get_object> ( $name ) This returns the application object registered as B<$name> to this context. =item $context->B<set_object> ( $name => $object ) This sets a new object, which was registered as B<$name> to this context. =item $context->B<get_object_attr> ( "$object.$attr" ) Retrieves the attribute named B<$attr> of the object B<$object>. =item $context->B<set_object_attr> ( "$object.$attr", $value ) Set the attribute named B<$attr> of the object B<$object> to B<$value>. Dependent widgets update automatically. =item $context->B<update_object_attr_widgets> ( $object_name, $attr_name ) Triggers updates on all GUI widgets which are associated with the attribute B<$attr_name> of the object registered as B<$object_name> to this context. You may omit B<$attr_name> and pass a fully qualified "object.attr" noted string as the first argument instead. =item $context->B<update_object_widgets> ( $object_name ) Triggers updates on all GUI widgets which are associated with the object registered as B<$object_name> to this context. =item $context->B<update_object_widgets_activity> ( $object_name, $activity ) This updates the activity state of all GUI widgets which are associated with the object registered as B<$object_name> to this context. B<$activity> is 'inactive' or 'active'. =item $proxy = $context->B<get_proxy> ( $object_name ) This returns the Gtk2::Ex::FormFactory::Proxy instance which was created for the object registered as B<$name> to this context. With the proxy you can do updates on object attributes which trigger the correspondent GUI updates as well. =back =head1 BUFFERED CONTEXT OBJECTS This feature was introduced in version 0.58 and is marked experimental so please use with care. If you set B<buffered> => 1 when adding an object to the Context a buffering Proxy will be used for this object. That means that all GUI changes in a synchronized FormFactory dialog are buffered in the proxy object. Normally all changes are commited immediately to the object, which is Ok in many situations, but makes implementing a Cancel button difficult resp. you need to care about this yourself by using a copy of the object or something like that. A FormFactory gets "buffered" if B<all> its widgets are connected to a buffered object. In that case Gtk2::Ex::Form::DialogButtons show a Cancel button automatically, even in a synchronized dialog. =head2 What's this good for? If your FormFactory doesn't has the B<sync> flag set you get Cancel button as well, since no changes are applied to the objects until the user hit the Ok button. All changes are kept in the "GUI". But such a dialog lacks of all dynamic auto-widget-updating features, e.g. setting widgets inactive under specific conditions. For very simple dialogs this is Ok, but if you need these features you need the buffering facility as well. But take care when implementing your closures for widget activity checking: they must not use the original objects! They need to access the attributes through the Context, because your original object doesn't see any GUI changes until the FormFactory is applied! All changes are buffered in the Context. If you access your objects through the Context anything works as expected. Buffering is useful as well in other situations e.g. if you're accessing remote objects over a network (for example with Event::RPC) where a method call is somewhat expensive. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Table.pm�������������������������������������������0000644�0001750�0001750�00000042672�10400606040�021646� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Table; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "table" } sub get_layout { shift->{layout} } sub get_widget_table_attach { shift->{widget_table_attach} } sub get_widget_table_align { shift->{widget_table_align} } sub get_rows { shift->{rows} } sub get_columns { shift->{columns} } sub set_layout { shift->{layout} = $_[1] } sub set_widget_table_attach { shift->{widget_table_attach} = $_[1] } sub set_widget_table_align { shift->{widget_table_align} = $_[1] } sub set_rows { shift->{rows} = $_[1] } sub set_columns { shift->{columns} = $_[1] } sub new { my $class = shift; my %par = @_; my ($layout) = $par{'layout'}; my $self = $class->SUPER::new(@_); $layout =~ s/^\s+//m; $layout =~ s/\s+$//m; $layout .= "\n"; warn "Layout of table ".$self->get_name." contains tab characters" if $layout =~ /\t/; $self->set_layout($layout); $self->calculate_layout; return $self; } =cut Table example (helps understanding the code beyond): This Column expands +--------------+>>>>>>>>>>>>+ | 1 | 2 | +--------------+ | | 3 | | +-------+------+------------+ - > 4 | 5 | 6 | | > | +------------+ | > | | 7 | | +-------+------+------------+ | All rows in this span > 8 | | 9 | | expand as well > | +------------+ | > | | 10 | | > +------+------------+ | > | 11 | | +-------+-------------------+ - * empty fields get no Widget (what's inside the field, doesn't matter) * non-empty fields get the Widgets in the order showed above, counting from left-top to right-bottom =cut sub calculate_layout { my $self = shift; #-- Get layout of this table my $layout = $self->get_layout; my @layout_rows = map { s/^\s*//; $_ } split (/\n/, $layout); #-- First we calculate the raster which forms the #-- basis of this table. %raster_x and %raster_y #-- will contain all indicies, where a column #-- border was detected. my (%raster_x, %raster_y); my $y = 0; foreach my $row ( @layout_rows ) { #-- Detect X coordinates if ( $row =~ /[|^_'~]/ ) { my $x = 0; foreach my $field ( split(/[|^_']/, $row) ) { $x+=length($field); $raster_x{$x} = 1; ++$x; } } #-- Detect Y coordinates if ( $y == 0 || $row =~ /[-=]/ ) { $raster_y{$y} = 1; } #-- Count next row ++$y; } #-- Store the number of rows the layout string has my $layout_rows = $y; #-- Sorted list of row/column indices my @raster_x = sort { $a <=> $b } keys %raster_x; my @raster_y = sort { $a <=> $b } keys %raster_y; if ( $Gtk2::Ex::FormFactory::DEBUG ) { require Data::Dumper; print "raster_x: ", Data::Dumper::Dumper(\@raster_x); print "raster_y: ", Data::Dumper::Dumper(\@raster_y); } #-- Now walk through all lines and build the table #-- attach information for each non-empty field my @widget_table_attach; my @widgets_table_align; $y = 0; foreach my $row_i ( @layout_rows ) { #-- $row is modified beyond my $row = $row_i; #-- Skip lines which have no | or ' or ^ or _ #-- (such lines just separate rows from each other #-- and are of no interest here) if ( $row !~ /[|^_']/ ) { ++$y; next; } $Gtk2::Ex::FormFactory::DEBUG && print "=============================\n"; $Gtk2::Ex::FormFactory::DEBUG && print "[$y] analyze row: '$row'\n"; #-- Track left/right indices and attach #-- column numbers my $right_x = 0; my $right_attach = 0; my $left_x = 0; my $left_attach = 0; #-- Remove the first | character, because we #-- split at | borders (otherwise we would #-- get an empty field at the first position) $row =~ s/^(.)//; #-- And remember it, since it may be a special #-- character (^ _ ') defining a vertical property. my $row_first_char = $1; #-- split row into fields. All vertical special #-- characters mark borders, plus + and |. foreach my $field ( split(/[|+'_^~]/, $row) ) { $Gtk2::Ex::FormFactory::DEBUG && print "[$y,$left_x] field='$field'\n"; #-- Calculate right index of this field $right_x = $left_x + length($field)+1; #-- Calculate the cell span of this field using #-- the @raster_x array calculated above. my $span_x = 0; foreach my $x ( @raster_x ) { ++$span_x if $x >= $left_x && $x < $right_x; last if $x > $right_x; } #-- With the cell span we know the right attach $right_attach = $left_attach + $span_x; #-- Now we know all X values, more work needs to #-- be done for the Y part #-- Process this field only if it doesn't #-- span over several rows, because then #-- it was processed already in a previous #-- iteration) if ( $y > 0 && substr($layout_rows[$y-1], $left_x+1, 1 ) =~ /[-=>%\[\]]/ ) { #-- Seek for the bottom of that field and calculate #-- top and bottom attachment my $top_attach = 0; my $bot_attach = 0; #-- Skip first line, which defines no columns my $y_seek; for ( $y_seek = 1; $y_seek < $layout_rows; ++$y_seek ) { $Gtk2::Ex::FormFactory::DEBUG && print "top_attach: y=$y y_seek=$y_seek ". "raster_y=$raster_y{$y_seek}\n"; if ( $y_seek < $y and $raster_y{$y_seek} ) { #-- If we are above the current y position and #-- hit a y raster point, increment top_ #-- and bot_attach ++$top_attach; ++$bot_attach; } elsif ( $raster_y{$y_seek} ) { #-- If we're beyond the current y position, #-- increase bot_attach if we hit a raster point ++$bot_attach; } $Gtk2::Ex::FormFactory::DEBUG && print "=> top_attach=$top_attach ". "bot_attach=$bot_attach\n". " '". substr($layout_rows[$y_seek], $left_x+1, 1)."'\n"; #-- Out here if we hit the end of the field last if $y_seek >= $y and substr($layout_rows[$y_seek], $left_x+1, 1) =~ /[-+=\[\]\%]/; $field .= "\n".substr($layout_rows[$y_seek], $left_x+1, $right_x-$left_x-1) if $y_seek > $y; } #-- Push all attach values into the list, #-- but only if the field is not empty if ( $field =~ /\S/ ) { my $hexpand = $self->get_xexpansion(\@layout_rows, $left_x+1, $y, $right_x); my $vexpand = $self->get_yexpansion(\@layout_rows, $left_x, $y, $y_seek-1), my $xalign = $self->get_xalign(\@layout_rows, $left_x+1, $y, $right_x); my $yalign = $self->get_yalign(\@layout_rows, $left_x, $y, $y_seek-1); push @widget_table_attach, [ #-- Attachments $left_attach, $right_attach, $top_attach, $bot_attach, $hexpand, $vexpand, ]; push @widgets_table_align, { xalign => $xalign, yalign => $yalign, }; if ( $Gtk2::Ex::FormFactory::DEBUG && $field =~ /\S/ ) { $hexpand = join("|",@{$hexpand}); $vexpand = join("|",@{$vexpand}); print "--------------------------\n"; print "y: $y\n"; print "x: ".($left_x+1),"\n"; print "left_attach: $left_attach\n"; print "right_attach: $right_attach\n"; print "top_attach: $top_attach\n"; print "bot_attach: $bot_attach\n"; print "field: '$field'\n"; print "hexpand: $hexpand\n"; print "vexpand: $vexpand\n"; print "xalign: $xalign\n"; print "yalign: $yalign\n"; } } } #-- Initialize left index/attach values #-- for the next field $left_x = $right_x; $left_attach = $right_attach; } #-- Count Y coordinate ++$y; } #-- Store results in the object $self->set_rows(scalar(@raster_y)-1); $self->set_columns(scalar(@raster_x-1)); $self->set_widget_table_attach(\@widget_table_attach); $self->set_widget_table_align(\@widgets_table_align); $Gtk2::Ex::FormFactory::DEBUG && print Data::Dumper::Dumper(\@widget_table_attach); 1; } sub get_xexpansion { my $self = shift; my ($layout_rows, $x, $y, $max_x) = @_; my $row = $layout_rows->[$y-1]; my $c; while ( $x <= $max_x ) { $c = substr($row, $x, 1); return ["fill","expand"] if $c =~ /[>^]/; ++$x; } return ["fill"]; } sub get_yexpansion { my $self = shift; my ($layout_rows, $x, $y, $max_y) = @_; my $c; while ( $y <= $max_y ) { $c = substr($layout_rows->[$y], $x, 1); return ["fill","expand"] if $c =~ /[>^]/; ++$y; } return ["fill"]; } sub get_xalign { my $self = shift; my ($layout_rows, $x, $y, $max_x) = @_; my $row = $layout_rows->[$y-1]; my $c; while ( $x <= $max_x ) { $c = substr($row, $x, 1); return 0 if $c eq '['; return 1 if $c eq ']'; return 0.5 if $c eq '%'; ++$x; } return -1; } sub get_yalign { my $self = shift; my ($layout_rows, $x, $y, $max_y) = @_; my $c; while ( $y <= $max_y ) { $c = substr($layout_rows->[$y], $x, 1); return 0 if $c eq "'"; return 1 if $c eq '_'; return 0.5 if $c eq '~'; ++$y; } return -1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Table - Complex table layouts made easy =head1 SYNOPSIS Gtk2::Ex::FormFactory::Table->new ( layout => "+-------%------+>>>>>>>>>>>>>>>+ | Name | | +--------------~ Image | | Keywords | | +-------+------+[--------------+ ^ ' More | Something | ^ | +-----+--------]+ _ Notes | | | Foo | +-------+------+-----+---------+ ^ Bar | Baz | +--------------+---------------+", content => [ Gtk2::Ex::FormFactory::Entry->new ( ... ), Gtk2::Ex::FormFactory::Image->new ( ... ), Gtk2::Ex::FormFactory::Entry->new ( ... ), ... ], ... Gtk2::Ex::FormFactory::Container attributes Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This module implements a simple way of defining complex table layouts inside a Gtk2::Ex::FormFactory environment. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container +--- Gtk2::Ex::FormFactory::Table Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 LAYOUT DEFINITION Take a look at the example in the SYNPOSIS. You see, you simply draw the layout of your table. But how does this work exactly? Each table is based on a straight two dimensional grid, no matter how complicated the cells span over one or more rows or columns. You see the grid when you extend all lines (horizontal and vertical) up to the borders of the table. The following graphic shows the grid by marking the imaginary lines with characters: +-------+------+>>>>>+>>>>>+ | Name | Img | +-------+------++| | Keyw | | +-------+------+-----+-----+ ^ Notes | More | Som | ^| +-----+-----+ ^ | | | Foo | +-------+------+-----+-----+ ^ Bar | Baz | +-------+------+-----+-----+ All cells of the table are attached to this grid. Gtk2::Ex::FormFactory::Table distinguishes empty and non empty cells. If only whitespace is inside the cell, it's empty, otherwise it has a widget inside. Since Gtk2::Ex::FormFactory::Table is a L<Gtk2::Ex::FormFactory::Container> it has a simple list of widgets as children. So how are these widgets assigned to the cells of the table? Short answer: from left/top to the right/bottom. Gtk2::Ex::FormFactory::Table scans your layout drawing in this direction and once it sees a new cell, it's counted as the next child of the table. Our example has this child order: +--------------+>>>>>>>>>>>+ | 1 | 2 | +--------------+ | | 3 | | +-------+------+-----------+ ^ 4 | 5 | 6 | ^ | +-----+>>>>>+ ^ | | | 7 | +-------+------+-----+-----+ ^ 8 | 9 | +--------------+-----------+ So the B<content> attribute of this table must list exactly nine widgets, otherwise you would get a runtime exception, when it comes to building the table. Ok, now it's clear how the table cells are attached to the grid of the table. But what about the size of the cells resp. their widgets and the alignment of widgets inside their cells? This answer is about funny characters ;) =head2 Cell / Widget expansion By default all cells and their widgets doesn't expand if the table expands. But you recognized the E<gt> and ^ characters? They say, that the cell and its widget should both resize with the table, by allocating all space available (> for horizontal expansion and ^ for vertical). If you want to resize just the cell, but not its widget, refer to the next chapter about widget alignments. In our example cell 2, 7 and 9 resize horizontal with the table, cell 4, 5, 6, 7, 8 and 9 vertical. Cell 1 and 3 don't resize at all, they fill the cell but stay at the cell's size, no matter how the table resizes. =head2 Widget alignment By default widgets fill their cell completely. If the cell expands the widgets expands as well. But you may want to align the widget on the left or right side, or in the middle, resp. at the top and the bottom. Once you define an alignment, the widget doesn't fill the cell anymore. Again there are some funny characters defining the alignment. For horizontal alignments the characters must be used in the B<top> border of the cell. For vertical alignment it needs to be the B<left> border of the cell. Horinzontal alignment is controlled with these characters: [ left, ] right and % middle. Vertical alignment is controlled with these characters: ' top, _ bottom and ~ middle. In the SYNPOSIS example "Image" is attached in the middle (vertical), "Notes" at the bottom and "More" at the top. "Something" is attached left, "Foo" right and "Name" centered (horizontal). =head2 Complete list of special characters This is the complete list of recognized characters and their meaning: =over 10 =item - | + = The widget fills the cell, but the cell doesn't resize with the table. That's the default, because these characters belong to the set of ASCII graphic characters used to draw the layout. =item > The cell expands horizontal. Recognized only in the top border of a cell. =item ^ The cell expands vertical. Recognized only in the left border of a cell. =item [ Widget is attached on the left and doesn't expand anymore with the cell. Recognized only in the top border of a cell. =item ] Widget is attached on the right and doesn't expand anymore with the cell. Recognized only in the top border of a cell. =item % Widget is attached in the middle (horizontal) and doesn't expand anymore with the cell. Recognized only in the top border of a cell. =item ' Widget is attached on the top and doesn't expand anymore with the cell. Recognized only in the left border of a cell. =item _ Widget is attached on the bottom and doesn't expand anymore with the cell. Recognized only in the left border of a cell. =item ~ Widget is attached in the middle (vertical) and doesn't expand anymore with the cell. Recognized only in the left border of a cell. =back =head2 Notes Some additional notes about the layout definition string. =over 4 =item B<Drawing characters> Although this should be obvious ;) In your drawing | characters (pipe symbol) mark column borders, and - or = (dash or equal sign) characters mark row borders. The + (plus) characters have no special meaning. They're just for candy. For completeness: additionally the ^ _ and ' characters mark horizontal cell borders, since these are special characters controling the vertical alignment of a cell and are placed on the vertical borders of cells. You need at least one - or = character in the top vertical border of each row, otherwise the vertical raster of your table can't be recognized correctly. This should be no problem in practice at all. =item B<TAB characters> Don't use TAB characters but true SPACE characters inside the table. You get a warning on TAB characters. =item B<Whitespace around the table> You may have arbitrary whitespace around your table, inlcuding TAB characters. It's cut off before the layout string is parsed. =back =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<layout> = SCALAR This is a string which defines the layout of this table using some sort of line art ASCII graphics. Refer to the LAYOUT DEFINITION chapter for details about the format. =back For more attributes refer to Gtk2::Ex::FormFactory::Container. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ����������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Notebook.pm����������������������������������������0000644�0001750�0001750�00000006323�10435540414�022402� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Notebook; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "notebook" } sub object_to_widget { my $self = shift; my $page = $self->get_object_value; if ( defined $page ) { #-- the page widget must be visible, otherwise #-- it can't be selected my $page_widget = $self->get_gtk_widget->get_nth_page($page); $page_widget->show; #-- now set the page $self->get_gtk_widget->set ( page => $page ); } 1; } sub widget_to_object { my $self = shift; $self->set_object_value ($self->get_gtk_widget->get ("page")); 1; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->signal_connect_after ( 'switch-page' => sub { $self->widget_value_changed; }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Notebook - A Notebook in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Notebook->new ( ... Gtk2::Ex::FormFactory::Container attributes Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a Notebook in a Gtk2::Ex::FormFactory framework. The number of the actually selected notebook page is controlled by the value of the associated application object attribute. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container +--- Gtk2::Ex::FormFactory::Notebook Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Container, but some special notes apply: =over 4 =item B<content> = ARRAYREF of Gtk2::Ex::FormFactory::Widget's [optional] Only widgets which have a B<title> attribute may be added to a NoteBook. Since Gtk2::Ex::FormFactory::Container defines the B<title> attribute all containers can be turned into a notebook page. The widget title will automatically render to the title of the page resp as the text appearing on the page's tab. You can add an icon to the Notebook tab by prefixing the widget title with a stock item name in square brackets, e.g. this way: title => "[gtk-cdrom] CDROM Contents", =back For more attributes refer to Gtk2::Ex::FormFactory::Container. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/HBox.pm��������������������������������������������0000644�0001750�0001750�00000006047�10400606040�021453� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::HBox; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "hbox" } sub get_homogenous { shift->{homogenous} } sub get_spacing { shift->{spacing} } sub get_no_frame { shift->{no_frame} } sub set_homogenous { shift->{homogenous} = $_[1] } sub set_spacing { shift->{spacing} = $_[1] } sub set_no_frame { shift->{no_frame} = $_[1] } sub new { my $class = shift; my %par = @_; my ($homogenous, $spacing, $no_frame) = @par{'homogenous','spacing','no_frame'}; my $self = $class->SUPER::new(@_); $self->set_homogenous($homogenous); $self->set_spacing($spacing); $self->set_no_frame($no_frame); return $self; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::HBox - A HBox in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::HBox->new ( homogenous => Bool, spacing => Integer, no_frame => Bool, ... Gtk2::Ex::FormFactory::Container attributes Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a HBox in a Gtk2::Ex::FormFactory framework. No application object attributes are associated with a HBox. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container +--- Gtk2::Ex::FormFactory::HBox Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<homogenous> = BOOL [optional] If set to TRUE all children in this HBox will have the same width. This is a convenience attribute for the B<homogenous> property of Gtk2::Box. =item B<spacing> = INTEGER [optional] The number of pixels between the child widgets in this HBox. This is a convenience attribute for the B<spacing> property of Gtk2::Box. =item B<no_frame> = BOOL [optional] By default a frame is added to the HBox if it has a B<title>. Set B<no_frame> to a true value to supress this. =back For more attributes refer to Gtk2::Ex::FormFactory::Container. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Combo.pm�������������������������������������������0000644�0001750�0001750�00000010111�10435553366�021661� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Combo; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "combo" } sub get_presets { shift->{presets} } sub set_presets { shift->{presets} = $_[1] } sub new { my $class = shift; my %par = @_; my ($presets) = $par{'presets'}; my $self = $class->SUPER::new(@_); $self->set_presets($presets); return $self; } sub object_to_widget { my $self = shift; my $gtk_combo = $self->get_gtk_widget; my $presets = $self->get_presets || $self->get_proxy->get_attr_presets( $self->get_attr, $self->get_name ); $gtk_combo->set_popdown_strings(@{$presets}) if ref $presets eq 'ARRAY'; $gtk_combo->entry->set_text($self->get_object_value); 1; } sub widget_to_object { my $self = shift; $self->set_object_value ($self->get_gtk_widget->entry->get_text); 1; } sub empty_widget { my $self = shift; $self->get_gtk_widget->entry->set_text(""); 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value ($self->get_gtk_widget->entry->get_text); 1; } sub restore_widget_value { my $self = shift; $self->get_gtk_widget->entry->set_text($self->get_backup_widget_value); 1; } sub get_gtk_tip_widgets { [ shift->get_gtk_widget->entry ]; } sub get_gtk_check_widget { shift->get_gtk_widget->entry; } sub get_widget_check_value { $_[0]->get_gtk_check_widget->get_text; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->entry->signal_connect ( changed => sub { $self->widget_value_changed }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Combo - A Combo in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Combo->new ( presets => List reference of preset values, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a text entry with a popdown list of presets in a Gtk2::Ex::FormFactory framework. The content of the entry is the value of the associated application object attribute. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Combo Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<presets> = LIST REF [optional] You may specify a static list of preset values for the Combo with this attribute. If B<preset> is not set, you need to implement the B<get_ATTR_presets> method as explained beyond. =back =head1 REQUIREMENTS FOR ASSOCIATED APPLICATION OBJECTS Application objects represented by a Gtk2::Ex::FormFactory::Combo may define additional methods. The naming of the methods listed beyond uses the standard B<get_> prefix for the attribute read accessor. B<ATTR> needs to be replaced by the actual name of the attribute associated with the widget. =over 4 =item B<get_ATTR_presets> This method must return a reference to an array containing the presets for this Combo box, but must be implemented only if you didn't specify a static presets list using the B<presets> attribute. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Form.pm��������������������������������������������0000644�0001750�0001750�00000005065�10445001225�021520� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Form; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "form" } sub get_label_top_align { shift->{label_top_align} } sub set_label_top_align { shift->{label_top_align} = $_[1] } sub new { my $class = shift; my %par = @_; my ($label_top_align) = $par{'label_top_align'}; my $self = $class->SUPER::new(@_); $self->set_label_top_align($label_top_align); return $self; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Form - A Form in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Form->new ( label_top_align => Align all labels at the top of their row, ... Gtk2::Ex::FormFactory::Container attributes Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a Form in a Gtk2::Ex::FormFactory framework. A Form is rendered as a two column table with labels in the first and the corresponding widgets in the second column. No application object attributes are associated with a Form. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Container +--- Gtk2::Ex::FormFactory::Form Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<label_top_align> = BOOLEAN [optional] If you set this option all form element lables are aligned to the top of their row. By default labels are centered vertically. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/HPaned.pm������������������������������������������0000644�0001750�0001750�00000004454�11620461317�021765� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::HPaned; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "hpaned" } sub object_to_widget { my $self = shift; $self->get_gtk_widget->set_position ( $self->get_object_value ); 1; } sub widget_to_object { my $self = shift; $self->set_object_value ($self->get_gtk_widget->get("position")); 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value (self->get_gtk_widget->get("position")); 1; } sub restore_widget_value { my $self = shift; self->get_gtk_widget->set_position ($self->get_backup_widget_value); 1; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->signal_connect ( move_handle => sub { $self->widget_value_changed; 1; }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::HPaned - A HPaned container in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::HPaned->new ( ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a HPaned container in a Gtk2::Ex::FormFactory framework. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::HPaned Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Widget. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/ProgressBar.pm�������������������������������������0000644�0001750�0001750�00000004563�10400606040�023045� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::ProgressBar; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "progress_bar" } sub object_to_widget { my $self = shift; my $status = $self->get_object_value; my $gtk_progress_bar = $self->get_gtk_widget; $gtk_progress_bar->set_fraction ($status->{fraction}); $gtk_progress_bar->set_text ($status->{text}); 1; } sub empty_widget { my $self = shift; my $gtk_progress_bar = $self->get_gtk_widget; $gtk_progress_bar->set_fraction (0); $gtk_progress_bar->set_text (""); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::ProgressBar - A ProgressBar in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::ProgressBar->new ( ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a ProgressBar in a Gtk2::Ex::FormFactory framework. The state of the progress bar is the value of the associated application object attribute. This value must be a hash reference defining these keys: =over 10 =item B<fraction> A float value between 0.0 and 1.0 representing the length of the progress bar. =item B<text> An optional text displayed in the progress bar. =back =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::ProgressBar Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Widget. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ���������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/ExecFlow.pm����������������������������������������0000644�0001750�0001750�00000015663�10607657764�022367� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::ExecFlow; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type {"execflow"} sub get_add_columns { shift->{add_columns} } sub get_path_by_id_href { shift->{path_by_id_href} } sub set_add_columns { shift->{add_columns} = $_[1] } sub set_path_by_id_href { shift->{path_by_id_href} = $_[1] } sub new { my $class = shift; my %par = @_; my ($add_columns) = $par{'add_columns'}; $add_columns ||= []; my $self = $class->SUPER::new(@_); $self->set_add_columns($add_columns); return $self; } sub build_widget { my $self = shift; my $model = Gtk2::TreeStore->new( "Glib::String", "Glib::String", "Glib::String", ("Glib::String") x @{$self->get_add_columns} ); my $tree_view = Gtk2::TreeView->new_with_model($model); for my $i ( 0..1+@{$self->get_add_columns} ) { my $column = Gtk2::TreeViewColumn->new_with_attributes( "col$i", Gtk2::CellRendererText->new, 'text' => $i < 2 ? $i : $i + 1 ); $tree_view->append_column($column); } $tree_view->set_headers_visible(0); $tree_view->set_rules_hint(1); $self->set_gtk_widget($tree_view); 1; } sub empty_widget { my $self = shift; $self->get_gtk_widget->get_model->clear; $self->set_path_by_id_href({}); 1; } sub object_to_widget { my $self = shift; my $job = $self->get_object_value; return unless $job; $self->empty_widget; $self->add_job_to_model($job, undef); $self->get_gtk_widget->expand_all; 1; } sub add_job_to_model { my $self = shift; my ($job, $parent_iter) = @_; my $model = $self->get_gtk_widget->get_model; my $iter = $model->append($parent_iter); $model->set($iter, 0 => $job->get_info); $model->set($iter, 1 => $job->get_progress_stats); $model->set($iter, 2 => $job->get_id); my $i = 3; foreach my $add_col ( @{$self->get_add_columns} ) { my $method = "get_$add_col"; $model->set($iter, $i => $job->$method()); ++$i; } my $path = $model->get_path($iter); $self->get_path_by_id_href->{$job->get_id} = $path; if ( $job->get_type eq 'group' ) { foreach my $child_job ( @{$job->get_jobs} ) { $self->add_job_to_model($child_job, $iter); } } 1; } sub update_job { my $self = shift; my ($job) = @_; my $path = $self->get_path_by_id_href->{$job->get_id}; return if !$path; my $model = $self->get_gtk_widget->get_model; my $iter = $model->get_iter($path); $model->set($iter, 0 => $job->get_info); $model->set($iter, 1 => $job->get_progress_stats); my $i = 3; foreach my $add_col ( @{$self->get_add_columns} ) { my $method = "get_$add_col"; $model->set($iter, $i => $job->$method()); ++$i; } 1; } sub add_job { my $self = shift; my ($job) = @_; my $group = $job->get_group; my $group_path = $self->get_path_by_id_href->{$group->get_id}; return $self->add_job_to_model($job) if !$group_path; my $model = $self->get_gtk_widget->get_model; my $iter = $model->get_iter($group_path); $self->add_job_to_model($job, $iter); $self->get_gtk_widget->expand_row($group_path, 0); $self->get_gtk_widget->expand_row( $self->get_path_by_id_href->{$job->get_id}, 1 ); 1; } sub remove_job { my $self = shift; my ($job) = @_; $self->remove_job_with_id($job->get_id); 1; } sub remove_job_with_id { my $self = shift; my ($job_id) = @_; my $path = $self->get_path_by_id_href->{$job_id}; my $model = $self->get_gtk_widget->get_model; $model->remove($model->get_iter($path)); $self->rebuild_job_path_index; 1; } sub rebuild_job_path_index { my $self = shift; my %path_by_job_id; my $model = $self->get_gtk_widget->get_model; $model->foreach(sub{ #-- Storing the Gtk2::TreePath doesn't work here, weird #-- stuff happens. So we store the string and turn it #-- into a Gtk2::TreePath after the foreach iteration. $path_by_job_id{$model->get($_[2],2)} = $_[1]->to_string; return 0; }); #-- Turn path strings into Gtk2::TreePath objects foreach my $job_id ( sort {$a <=> $b} keys %path_by_job_id ) { $path_by_job_id{$job_id} = Gtk2::TreePath->new_from_string($path_by_job_id{$job_id}); } $self->set_path_by_id_href(\%path_by_job_id); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::ExecFlow - Display a Event::ExecFlow job plan =head1 SYNOPSIS Gtk2::Ex::FormFactory::ExecFlow->new ( ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a Event::ExecFlow job plan viewer in a Gtk2::Ex::FormFactory framework using a Gtk2::TreeView to display the hierarchical job structure. Besides updating the whole TreeView by assigning a new Event::ExecFlow::Job object to the widget it offers methods for updating single jobs efficiently. The value of the associated application object attribute is an Event::ExecFlow::Job object. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::ExecFlow Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Widget. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 METHODS =over 4 =item $widget->B<add_job> ($job) This adds B<$job> to the widget. If the job is part of a job group the group must be added already to the widget. =item $widget->B<update_job> ($job) Updates the state of B<$job> in the TreeView. =item $widget->B<remove_job> ($job) Removes B<$job> from the TreeView. =item $widget->B<remove_job_with_id> ($job_id) Use this method to remove a job from the TreeView if you just know it's ID and don't have a $job object at hand. =back =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �����������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Entry.pm�������������������������������������������0000644�0001750�0001750�00000004662�10400606040�021715� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Entry; use strict; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "entry" } sub object_to_widget { my $self = shift; $self->get_gtk_widget->set_text($self->get_object_value); 1; } sub widget_to_object { my $self = shift; $self->set_object_value ($self->get_gtk_widget->get_text); 1; } sub empty_widget { my $self = shift; $self->get_gtk_widget->set_text(""); 1; } sub backup_widget_value { my $self = shift; $self->set_backup_widget_value ($self->get_gtk_widget->get_text); 1; } sub restore_widget_value { my $self = shift; $self->get_gtk_widget->set_text($self->get_backup_widget_value); 1; } sub get_widget_check_value { $_[0]->get_gtk_widget->get_text; } sub connect_changed_signal { my $self = shift; $self->get_gtk_widget->signal_connect ( changed => sub { $self->widget_value_changed }, ); 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Entry - An Entry in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Entry->new ( ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements an Entry in a Gtk2::Ex::FormFactory framework. The content of the Entry is the value of the associated application object attribute. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Entry Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Widget. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/ProxyBuffered.pm�����������������������������������0000644�0001750�0001750�00000011507�10400606040�023374� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::ProxyBuffered; use base qw(Gtk2::Ex::FormFactory::Proxy); use strict; use Carp; sub get_attr_buffer { shift->{attr_buffer} } sub set_attr_buffer { shift->{attr_buffer} = $_[1] } sub get_buffered { 1 } my $DEBUG = 0; sub new { my $class = shift; my $self = $class->SUPER::new(@_); $self->set_attr_buffer({}); return $self; } sub discard_buffer { my $self = shift; $self->set_attr_buffer({}); 1; } sub commit_buffer { my $self = shift; while ( my ($attr, $value) = each %{$self->get_attr_buffer} ) { $self->SUPER::set_attr($attr, $value); } 1; } sub set_object { my $self = shift; my ($object) = @_; $DEBUG && print "set_object: $self->{name} = $object\n"; if ( $self->{object} ne $object ) { $self->discard_buffer; } $self->SUPER::set_object($object); 1; } sub get_attr { my $self = shift; my ($attr_name) = @_; if ( $attr_name =~ /^([^.]+)\.(.*)$/ ) { $self = $self->get_context->get_proxy($1); $attr_name = $2; } $DEBUG && print "get_attr: ".$self->get_name.".$attr_name "; $DEBUG && print "FROM BUFFER\n" if exists $self->{attr_buffer}->{$attr_name}; return $self->{attr_buffer}->{$attr_name} if exists $self->{attr_buffer}->{$attr_name}; $DEBUG && print "FROM OBJECT\n"; return $self->SUPER::get_attr($attr_name); } sub set_attr { my $self = shift; my ($attr_name, $attr_value) = @_; if ( $attr_name =~ /^([^.]+)\.(.*)$/ ) { $self = $self->get_context->get_proxy($1); $attr_name = $2; } my $object = $self->get_object; my $name = $self->get_name; $DEBUG && print "set_attr: $name.$attr_name => $attr_value\n"; use Data::Dumper;print Dumper($attr_value) if ref $attr_value; $self->{attr_buffer}->{$attr_name} = $attr_value; $self->get_context ->update_object_attr_widgets($name, $attr_name, $object); my $child_object_name = $self->get_attr_aggregate_href->{$attr_name}; $self->get_context->set_object($child_object_name, $attr_value) if $child_object_name; return $attr_value; } sub set_attrs { my $self = shift; my ($attrs_href) = @_; my $object = $self->get_object; my $name = $self->get_name; my $context = $self->get_context; my ($attr_name, $attr_value, $child_object_name); while ( ($attr_name, $attr_value) = each %{$attrs_href} ) { $self->{attr_buffer}->{$attr_name} = $attr_value; $context->update_object_attr_widgets( $name, $attr_name, $object ); $child_object_name = $self->get_attr_aggregate_href->{$attr_name}; $context->set_object($child_object_name, $attr_value) if $child_object_name; } 1; } sub commit_attr { my $self = shift; my ($attr) = @_; my $attr_buffer = $self->get_attr_buffer; return unless exists $attr_buffer->{$attr}; my $value = $attr_buffer->{$attr}; $DEBUG && print "commit attr: $attr => $value\n"; $self->SUPER::set_attr($attr, $value, 1); 1; } sub discard_attr { my $self = shift; my ($attr) = @_; $DEBUG && print "discard attr: $attr"; delete $self->get_attr_buffer->{$attr}; 1; } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::ProxyBuffered - Buffering object proxy =head1 SYNOPSIS #-- Proxies are always created through #-- Gtk2::Ex::FormFactory::Context, never #-- directly by the application. Gtk2::Ex::FormFactory::ProxyBuffered->new ( Gtk2::Ex::FormFactory::Proxy attributes ); =head1 DESCRIPTION This class is derived from Gtk2::Ex::FormFactory::Proxy and buffers all changes made to object through the proxy. For details about buffering please refer to the chapter BUFFERED CONTEXT OBJECTS in Gtk2::Ex::FormFactory::Context. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Proxy +-- Gtk2::Ex::FormFactory::ProxyBuffered =head1 ATTRIBUTES This module has no additional attributes over those derived from Gtk2::Ex::FormFactory::Proxy. =back =head1 METHODS =over 4 =item $proxy->B<commit_buffer> () Commits all changes buffered in the $proxy object to the corresponding application object. =item $proxy->B<discard_buffer> () Discards all buffered changes. =back For more methods refer to L<Gtk2::Ex::FormFactory::Proxy>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/lib/Gtk2/Ex/FormFactory/Menu.pm��������������������������������������������0000644�0001750�0001750�00000014264�10400606040�021517� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package Gtk2::Ex::FormFactory::Menu; use strict; use base qw( Gtk2::Ex::FormFactory::Container ); sub get_type { "menu" } sub get_menu_tree { shift->{menu_tree} } sub get_default_callback { shift->{default_callback} } sub get_user_data { shift->{user_data} } sub get_gtk_simple_menu { shift->{gtk_simple_menu} } sub set_menu_tree { shift->{menu_tree} = $_[1] } sub set_default_callback { shift->{default_callback} = $_[1] } sub set_user_data { shift->{user_data} = $_[1] } sub set_gtk_simple_menu { shift->{gtk_simple_menu} = $_[1] } sub new { my $class = shift; my %par = @_; my ($menu_tree, $default_callback, $user_data) = @par{'menu_tree','default_callback','user_data'}; my $self = $class->SUPER::new(@_); $self->set_menu_tree($menu_tree); $self->set_default_callback($default_callback); $self->set_user_data($user_data); if ( $self->get_object eq '' ) { $self->set_object("__dummy"); } return $self; } sub cleanup { my $self = shift; $self->SUPER::cleanup(@_); $self->set_gtk_simple_menu(undef); 1; } sub build { my $self = shift; $self->SUPER::build(@_); $self->build_active_menu_items ( $self->get_menu_tree, "", ); 1; } sub build_active_menu_items { my $self = shift; my ($menu_tree, $path) = @_; my $i = 0; my ($name, $def); while ( $i < @{$menu_tree} ) { $name = $menu_tree->[$i]; $def = $menu_tree->[$i+1]; $name =~ s/_//g; if ( $def->{item_type} eq '<Branch>' ) { $self->build_active_menu_items ( $def->{children}, "$path/$name", ); } if ( $def->{object} || $def->{active_cond} ) { my $menu_item = Gtk2::Ex::FormFactory::MenuItem->new ( object => $def->{object}, active_cond => $def->{active_cond}, active_depends => $def->{active_depends}, ); $menu_item->set_form_factory($self->get_form_factory); $menu_item->set_gtk_widget( $self->get_gtk_simple_menu ->get_widget("$path/$name") ); push @{$self->get_content}, $menu_item; } $i += 2; } 1; } package Gtk2::Ex::FormFactory::MenuItem; use base qw( Gtk2::Ex::FormFactory::Widget ); sub get_type { "menu_item" } sub build_widget { 1 } 1; __END__ =head1 NAME Gtk2::Ex::FormFactory::Menu - A Menu in a FormFactory framework =head1 SYNOPSIS Gtk2::Ex::FormFactory::Menu->new ( menu_tree => Hierarchical definition of the Menu, default_callback => Default callback for menu items, user_data => User data for the default callback, ... Gtk2::Ex::FormFactory::Widget attributes ); =head1 DESCRIPTION This class implements a Menu in a Gtk2::Ex::FormFactory framework and pretty much wraps Gtk2::Ex::Simple::Menu. No application object attributes are associated with a Menu as a whole. But you may associate single Menu entries with an object. This way the correspondent entries will set insensitive automatically if the underlying object is undef and vice versa are activated once the object is defined. =head1 OBJECT HIERARCHY Gtk2::Ex::FormFactory::Intro Gtk2::Ex::FormFactory::Widget +--- Gtk2::Ex::FormFactory::Menu Gtk2::Ex::FormFactory::Layout Gtk2::Ex::FormFactory::Rules Gtk2::Ex::FormFactory::Context Gtk2::Ex::FormFactory::Proxy =head1 ATTRIBUTES Attributes are handled through the common get_ATTR(), set_ATTR() style accessors, but they are mostly passed once to the object constructor and must not be altered after the associated FormFactory was built. =over 4 =item B<menu_tree> = ARRAYREF [mandatory] This is a slightly extended menu tree definition in terms of Gtk2::Ex::Simple::Menu. You may optionally associate each entry with an application object by specifying its name with the B<object> key in the item definition hash. This way the item is active only if the correspondent object is defined. As well you can control widget activity more detailed using the B<active_cond> and B<active_depends> keys as described in the Gtk2::Ex::FormFactory::Widget manpage. A short example. This is a File menu where the 'Save' and 'Close' entries are sensitive only if a file was opened. We presume that opening a file sets the 'worksheet' object, which is registered with this name to the Context of the associated FormFactory. Additionally the 'Manage rows' entry is active only if more than three rows are selected: $menu_tree = [ _File => { item_type => <Branch>', children => [ _Open => { callback => \&open_worksheet, # sets the 'worksheet' object }, _Save => { callback => \&save_worksheet, object => 'worksheet', }, _Close => { callback => \&close_worksheet, object => 'worksheet', }, "_Manage rows" => { callback => \&manage_rows, object => 'worksheet', active_cond => sub { $worksheet->get_selected_rows_cnt > 3 }, active_depends => "worksheet.rows", ], }, ]; =item B<default_callback> = CODEREF [optional] The default callback of this menu. Refer to Gtk2::Ex::Simple::Menu for details. =item B<user_data> = SCALAR [optional] User data of the default callback of this menu. Refer to Gtk2::Ex::Simple::Menu for details. =back For more attributes refer to L<Gtk2::Ex::FormFactory::Widget>. =head1 AUTHORS Jrn Reder <joern at zyn dot de> =head1 COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. =cut ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/Makefile.PL����������������������������������������������������������������0000644�0001750�0001750�00000000762�10147623447�016063� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# $Id: Makefile.PL,v 1.1 2004/11/20 11:14:47 joern Exp $ use strict; use ExtUtils::MakeMaker; WriteMakefile( 'NAME' => 'Gtk2::Ex::FormFactory', 'VERSION_FROM' => 'lib/Gtk2/Ex/FormFactory.pm', 'PREREQ_PM' => { 'Gtk2' => 0, 'Gtk2::SimpleList' => 0, 'Gtk2::SimpleMenu' => 0, }, 'dist' => { COMPRESS => "gzip", SUFFIX => "gz", PREOP => q[tools/genreadme], POSTOP => q[mkdir -p dist; mv Gtk2-Ex-FormFactory*tar.gz dist/], }, ); ��������������Gtk2-Ex-FormFactory-0.67/t/�������������������������������������������������������������������������0000755�0001750�0001750�00000000000�11620730434�014337� 5����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/t/01.use.t�����������������������������������������������������������������0000644�0001750�0001750�00000000112�10074015620�015524� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More tests => 1; use_ok('Gtk2::Ex::FormFactory'); ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/t/02.context.t�������������������������������������������������������������0000644�0001750�0001750�00000003051�10145643676�016443� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������use strict; use Test::More tests => 13; use_ok('Gtk2::Ex::FormFactory'); my $context = Gtk2::Ex::FormFactory::Context->new ( default_get_prefix => "g_", default_set_prefix => "s_", ); ok ($context->get_default_get_prefix eq 'g_', "default_get_prefix"); ok ($context->get_default_set_prefix eq 's_', "default_get_prefix"); my $o1 = MyObj->new ( foo => "1foo_$$", bar => "1bar_$$" ); my $o2 = MyObj->new ( foo => "2foo_$$", bar => "2bar_$$" ); ok ( $context->add_object ( name => "o1", object => $o1 ), "add_object1" ); ok ( $context->add_object ( name => "o2", object => $o2 ), "add_object2" ); ok ( $context->get_object("o1") eq $o1, "get_object" ); my $o3 = MyObj->new ( foo => "3foo_$$", bar => "2bar_$$" ); ok ( $context->set_object(o1 => $o3), "set_object" ); ok ( $context->get_object("o1") eq $o3, "set_object verify" ); ok ( $context->set_object(o1 => $o1), "set_object change back" ); ok ( $context->get_proxy("o1")->get_attr("foo") eq "1foo_$$", "get_attr" ); ok ( $context->get_proxy("o1")->set_attrs ( { foo => "1bla", bar => "1foo" }, ), "set_attrs"); ok ( $context->get_proxy("o1")->get_attr("foo") eq "1bla", "set_attr verify foo"); ok ( $context->get_proxy("o1")->get_attr("bar") eq "1foo", "set_attr verify bar"); package MyObj; sub g_foo { shift->{foo} } sub g_bar { shift->{bar} } sub s_foo { shift->{foo} = $_[1] } sub s_bar { shift->{bar} = $_[1] } sub new { my $class = shift; my %par = @_; my ($foo, $bar) = @par{'foo','bar'}; my $self = bless { foo => $foo, bar => $bar }, $class; return $self; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/README���������������������������������������������������������������������0000644�0001750�0001750�00000006457�11620730434�014770� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������NAME Gtk2::Ex::FormFactory - Makes building complex GUI's easy SYNOPSIS #-- Refer to http://www.exit1.org/ for #-- a comprehensive online documentation. #-- Read Gtk2::Ex::FormFactory::Intro use Gtk2::Ex::FormFactory; my $context = Gtk2::Ex::FormFactory::Context->new; $context->add_object ( name => "worksheet", object => My::Worksheet->new, ); # derived from Gtk2::Ex::FormFactory::Layout my $layouter = My::Layout->new; # derived from Gtk2::Ex::FormFactory::Rules my $rule_checker = My::Rules->new; my $ff = Gtk2::Ex::FormFactory->new ( context => $context, layouter => $layouter, rule_checker => $rule_checker, content => [ Gtk2::Ex::FormFactory::Window->new ( title => "Worksheet Editor", content => [ Gtk2::Ex::FormFactory::Form->new ( title => "Main data", content => [ Gtk2::Ex::FormFactory::Entry->new ( label => "Worksheet title", attr => "worksheet.title", tip => "Title of this worksheet", ), #-- More widgets... ], ), Gtk2::Ex::FormFactory->DialogButtons->new, ], ), ], ); $ff->open; $ff->update; Gtk2->main; ABSTRACT With Gtk2::Ex::FormFactory you can build a GUI which consistently represents the data of your application. DESCRIPTION This is a framework which tries to make building complex GUI's easy, by offering these two main features: * Consistent looking GUI without the need to code resp. tune each widget by hand. Instead you declare the structure of your GUI, connect it to the data of your program (which should be a well defined set of objects) and control how this structure is transformed into a specific layout in a very generic way. * Automatically keep widget and object states in sync (in both directions), even with complex data structures with a lot of internal dependencies, object nesting etc. This manpage describes the facilities of Gtk2::Ex::FormFactory objects which are only a small part of the whole framework. To get a full introduction and overview of how this framework works refer to Gtk2::Ex::FormFactory::Intro. AUTHORS Jrn Reder <joern at zyn dot de> COPYRIGHT AND LICENSE Copyright 2004-2006 by Jrn Reder. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/MANIFEST�������������������������������������������������������������������0000644�0001750�0001750�00000003377�11620730100�015225� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������lib/Gtk2/Ex/FormFactory.pm lib/Gtk2/Ex/FormFactory/Button.pm lib/Gtk2/Ex/FormFactory/DialogButtons.pm lib/Gtk2/Ex/FormFactory/CheckButton.pm lib/Gtk2/Ex/FormFactory/CheckButtonGroup.pm lib/Gtk2/Ex/FormFactory/Combo.pm lib/Gtk2/Ex/FormFactory/Container.pm lib/Gtk2/Ex/FormFactory/Context.pm lib/Gtk2/Ex/FormFactory/Entry.pm lib/Gtk2/Ex/FormFactory/ExecFlow.pm lib/Gtk2/Ex/FormFactory/Expander.pm lib/Gtk2/Ex/FormFactory/Form.pm lib/Gtk2/Ex/FormFactory/GtkWidget.pm lib/Gtk2/Ex/FormFactory/HBox.pm lib/Gtk2/Ex/FormFactory/HPaned.pm lib/Gtk2/Ex/FormFactory/HSeparator.pm lib/Gtk2/Ex/FormFactory/Image.pm lib/Gtk2/Ex/FormFactory/Intro.pm lib/Gtk2/Ex/FormFactory/Label.pm lib/Gtk2/Ex/FormFactory/Layout.pm lib/Gtk2/Ex/FormFactory/List.pm lib/Gtk2/Ex/FormFactory/Loader.pm lib/Gtk2/Ex/FormFactory/Menu.pm lib/Gtk2/Ex/FormFactory/Notebook.pm lib/Gtk2/Ex/FormFactory/Popup.pm lib/Gtk2/Ex/FormFactory/ProgressBar.pm lib/Gtk2/Ex/FormFactory/Proxy.pm lib/Gtk2/Ex/FormFactory/ProxyBuffered.pm lib/Gtk2/Ex/FormFactory/RadioButton.pm lib/Gtk2/Ex/FormFactory/Rules.pm lib/Gtk2/Ex/FormFactory/TextView.pm lib/Gtk2/Ex/FormFactory/Table.pm lib/Gtk2/Ex/FormFactory/Timestamp.pm lib/Gtk2/Ex/FormFactory/ToggleButton.pm lib/Gtk2/Ex/FormFactory/VBox.pm lib/Gtk2/Ex/FormFactory/VSeparator.pm lib/Gtk2/Ex/FormFactory/VPaned.pm lib/Gtk2/Ex/FormFactory/Widget.pm lib/Gtk2/Ex/FormFactory/Window.pm lib/Gtk2/Ex/FormFactory/YesNo.pm lib/Gtk2/Ex/FormFactory/Timestamp.pm lib/Gtk2/Ex/FormFactory/GtkWidget.pm examples/README examples/cover.jpg examples/testapp.pl t/01.use.t t/02.context.t tutorial/config.pm tutorial/model.pm tutorial/music.pl tutorial/music.sql tutorial/import.pl tutorial/README Changes Makefile.PL MANIFEST LICENSE README META.yml Module meta-data (added by MakeMaker) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/examples/������������������������������������������������������������������0000755�0001750�0001750�00000000000�11620730434�015712� 5����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/examples/testapp.pl��������������������������������������������������������0000755�0001750�0001750�00000032661�10412027013�017730� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl use strict; use lib '../lib'; use Data::Dumper; $Data::Dumper::Indent = 1; use Gtk2; use Gtk2::Ex::FormFactory; main: { Gtk2->init; $Gtk2::Ex::FormFactory::DEBUG = 0; my $database = My::Database->new ( discs => [ My::Disc->new( artist => "Michael Oldfield", album => "Tubular Bells", titles => [ "Part I", "Part II", "Part III" ], broken_jewel_case => 0, genre => "instrumental", quality => 2, ), My::Disc->new( artist => "Jamiroquai", album => "A Funk Odyssey", titles => [ "Bla", "Foo", "Baz" ], broken_jewel_case => 1, genre => "soul", quality => 1, cover_filename => "cover.jpg", ), ], selected_discs_idx => [ 1 ], ); my $gui_state = My::GuiState->new( selected_page => 4, ); my $context = Gtk2::Ex::FormFactory::Context->new; $context->add_object ( name => "database", buffered => 0, object => undef, attr_accessors_href => { get_discs => sub { my $self = shift; my (@slist_data); push @slist_data, [ $_->get_artist, $_->get_album ] for @{$self->get_discs}; return \@slist_data; }, }, attr_depends_href => { selected_disc => "selected_discs_idx", } ); $context->add_object ( name => "disc", buffered => 0, aggregated_by => "database.selected_disc", attr_accessors_href => { get_titles => sub { my $self = shift; my (@slist_data, $i); push @slist_data, [ ++$i, $_ ] for @{$self->get_titles}; return \@slist_data; }, set_titles => sub { my $self = shift; my ($slist_data) = @_; my @titles; push @titles, $_->[1] for @{$slist_data}; return $self->set_titles(\@titles); }, }, attr_activity_href => { album => sub { $_[0]->get_artist ne 'Michael Oldfield'; }, }, attr_depends_href => { album => "disc.artist", }, ); $context->add_object ( name => "gui_state", object => $gui_state, ); $context->add_object ( name => "disabled", object => undef, ); $context->set_object( database => $database); my @table_childs; for ( 1..11 ) { push @table_childs, Gtk2::Ex::FormFactory::Button->new ( label => "Child $_", ); } my $ff; push @table_childs, Gtk2::Ex::FormFactory::VBox->new ( expand => 1, title => "Child $_", content => [ Gtk2::Ex::FormFactory::Button->new ( label => "Change Cursor", clicked_hook => sub { $ff->change_mouse_cursor("watch"); sleep 1; $ff->change_mouse_cursor(); 1; }, ), Gtk2::Ex::FormFactory::Button->new ( label => "Extend NB", clicked_hook => sub { extend_nb($ff); }, ), Gtk2::Ex::FormFactory::Button->new ( label => "Reduce NB", clicked_hook => sub { reduce_nb($ff); }, ), ], ); foreach my $nr ( 1 ) { $ff = Gtk2::Ex::FormFactory->new ( context => $context, sync => 1, content => [ Gtk2::Ex::FormFactory::Window->new ( name => "window_$nr", title => "FormFactory Test Application", content => [ menu => { menu_tree => [ _File => { item_type => '<Branch>', children => [ _New => { item_type => '<StockItem>', extra_data => 'gtk-new', callback => sub { print "new\n" }, accelerator => '<ctrl>N', }, _Save => { item_type => '<StockItem>', extra_data => 'gtk-save', callback => sub { print "save\n" }, accelerator => '<ctrl>S', object => "disabled", }, _Quit => { item_type => '<StockItem>', extra_data => 'gtk-quit', callback => sub { print "quit\n"; $ff->close; Gtk2->main_quit; }, accelerator => '<ctrl>Q', }, ], }, ], }, Gtk2::Ex::FormFactory::Notebook->new ( name => "notebook", attr => "gui_state.selected_page", expand => 1, content => [ Gtk2::Ex::FormFactory::VBox->new ( title => "Select Album", content => [ Gtk2::Ex::FormFactory::VBox->new ( expand => 1, content => [ Gtk2::Ex::FormFactory::List->new ( name => "discs", expand => 1, attr => "database.discs", attr_select => "database.selected_discs_idx", label => "Album Selection", tip => "Select an entry for modification", columns => [ "Artist", "Album" ], types => [ "text", "text" ], selection_mode => "multiple", properties => { 'enable-search' => 1, }, ), ], ), ], ), Gtk2::Ex::FormFactory::VBox->new ( title => "Edit Album", content => [ Gtk2::Ex::FormFactory::Form->new ( content => [ Gtk2::Ex::FormFactory::Entry->new ( attr => "disc.artist", label => "Artist", tip => "Name of the artist", rules => "not-empty", width => 100, ), Gtk2::Ex::FormFactory::Label->new ( attr => "disc.artist", label => "Artist", ), Gtk2::Ex::FormFactory::Combo->new ( attr => "disc.album", label => "Album", tip => "Name of the album, 1st view", rules => "not-empty", ), Gtk2::Ex::FormFactory::Entry->new ( attr => "disc.album", label => "Album 2nd view", tip => "Name of the album, 2nd view", rules => "not-empty", inactive => "invisible", ), Gtk2::Ex::FormFactory::YesNo->new ( attr => "disc.broken_jewel_case", label => "State of jewel case", tip => "Indicates whether the jewel ". "case is broken or not", true_label => "Broken", false_label => "Intact", ), Gtk2::Ex::FormFactory::Expander->new ( label => "Alternative jewel case widgets", content => [ Gtk2::Ex::FormFactory::Form->new ( content => [ Gtk2::Ex::FormFactory::ToggleButton->new ( attr => "disc.broken_jewel_case", label => "State of jewel case", tip => "Indicates whether the jewel ". "case is broken or not", true_label => "Broken", false_label => "Intact", ), Gtk2::Ex::FormFactory::CheckButton->new ( attr => "disc.broken_jewel_case", label => "Jewel case is broken", detach_label => 1, tip => "Indicates whether the jewel ". "case is broken or not", ), Gtk2::Ex::FormFactory::CheckButton->new ( attr => "disc.broken_jewel_case", label => "Broken", tip => "Indicates whether the jewel ". "case is broken or not", ), ], ), ], ), Gtk2::Ex::FormFactory::Popup->new ( attr => "disc.genre", label => "Genre", tip => "Select to which genre this album belongs", ), Gtk2::Ex::FormFactory::Popup->new ( attr => "disc.quality", label => "Quality", tip => "Your opinion about the quality of this album", ), Gtk2::Ex::FormFactory::HBox->new ( label => "Quality again", content => [ Gtk2::Ex::FormFactory::RadioButton->new ( attr => "disc.quality", label => "Bad", value => 0, ), Gtk2::Ex::FormFactory::RadioButton->new ( attr => "disc.quality", label => "Medium", value => 1, ), Gtk2::Ex::FormFactory::RadioButton->new ( attr => "disc.quality", label => "Good", value => 2, ), ], ), Gtk2::Ex::FormFactory::Label->new ( label => "Active for Jamiroquai", active_cond => sub { $context->get_object("disc")->get_artist =~ /Jam/; }, active_depends => "disc.artist", ), ], ), ], ), Gtk2::Ex::FormFactory::VBox->new ( title => "Edit Titles", content => [ Gtk2::Ex::FormFactory::Form->new ( content => [ Gtk2::Ex::FormFactory::List->new ( attr => "disc.titles", label => "Titles", tip => "List of all tracks on this album", columns => [ "Nr", "Title" ], types => [ "int", "text" ], editable => [ 0, 1 ], ), ], ), ], ), Gtk2::Ex::FormFactory::VBox->new ( title => "Disc Cover", content => [ Gtk2::Ex::FormFactory::Form->new ( expand => 1, content => [ Gtk2::Ex::FormFactory::Image->new ( attr => "disc.cover_filename", tip => "Cover of this album", expand_h => 1, expand_v => 1, with_frame => 1, scale_to_fit => 1, max_width => 500, max_height => 500, bgcolor => "#111155", ), ], ), ], ), Gtk2::Ex::FormFactory::VBox->new ( title => "Table Test", content => [ Gtk2::Ex::FormFactory::Table->new ( expand => 1, layout => " +[-------------+>>>>>>>>>>>]+---+ | 1 | | | +[-------------+ 2 | | | ** 4 ** | | | +-------+------+------------+ | | ^ 6 ^ *** | | | ^ ^ ** 7 ** | | | ^ ^ *** | | | ~ +-----%------+ | | ^ ^ * | | | ^ ~ *8* | | _ 5 ^ ^ * | | +-------+------+------------+ | | | ^ ** | | | | ^ ** 10 ** | | | | ^ ** | | ~ 9 | +------------+ | | | | ** 11 ** | | | +------+-----------]+ | | | 12 _ 3 | +-------+-------------------+---+ ", content => \@table_childs, ), ], ), ], ), Gtk2::Ex::FormFactory::DialogButtons->new ( buttons => { ok => 1, apply => 1 }, clicked_hook_before => sub { my ($button) = @_; print "User hit button '$button'\n"; return 1; }, clicked_hook_after => sub { my ($button) = @_; if ( $button eq 'ok' or $button eq 'cancel' ) { print Dumper($database, $gui_state); Gtk2->main_quit; } return 1; }, ), ], ), ], ); $ff->open; $ff->update; } Gtk2->main; } my $added; my @added; sub extend_nb { my ($ff) = @_; my $ff_notebook = $ff->get_widget("notebook"); ++$added; my $new_form = Gtk2::Ex::FormFactory::Form->new ( name => "new_form_$added", title => "Added $added", content => [ Gtk2::Ex::FormFactory::Entry->new ( attr => "disc.artist", label => "A test entry [$added]", ), ], ); $ff_notebook->add_child_widget($new_form); push @added, $new_form; 1; } sub reduce_nb { my ($ff) = @_; return unless $added; my $ff_notebook = $ff->get_widget("notebook"); --$added; $ff_notebook->remove_child_widget(pop @added); 1; } package My::Database; sub get_discs { shift->{discs} } sub get_selected_discs_idx { shift->{selected_discs_idx} } sub set_discs { shift->{discs} = $_[1] } sub set_selected_discs_idx { shift->{selected_discs_idx} = $_[1] } sub new { my $class = shift; my %par = @_; my ($discs, $selected_discs_idx) = @par{'discs','selected_discs_idx'}; $selected_discs_idx ||= []; my $self = bless { discs => $discs, selected_discs_idx => $selected_discs_idx, }, $class; return $self; } sub get_selected_disc { my $self = shift; my $selected_discs_idx = $self->get_selected_discs_idx; return if @{$selected_discs_idx} == 0; return $self->get_discs->[$selected_discs_idx->[0]]; } package My::Disc; sub get_artist { shift->{artist} } sub get_album { shift->{album} } sub get_titles { shift->{titles} } sub get_broken_jewel_case { shift->{broken_jewel_case} } sub get_genre { shift->{genre} } sub get_quality { shift->{quality} } sub get_cover_filename { shift->{cover_filename} } sub set_artist { shift->{artist} = $_[1] } sub set_album { shift->{album} = $_[1] } sub set_titles { shift->{titles} = $_[1] } sub set_broken_jewel_case { shift->{broken_jewel_case} = $_[1] } sub set_genre { shift->{genre} = $_[1] } sub set_quality { shift->{quality} = $_[1] } sub set_cover_filename { shift->{cover_filename} = $_[1] } sub get_album_presets { $_[0]->get_artist =~ /Oldfield/i ? [ "Amarok", "Crisis", "Tubular Bells", "Tubular Bells II", "Tubular Bells III", ]: [ "A Funk Odyssey", "Emergency On Planet Earth", "Synkronized", ]; } sub get_genre_list {{ dance => "Dance", techno => "Techno", country => "Country", soul => "Soul", instrumental => "Instrumental", }} sub get_quality_list {[ "Bad", "Medium", "Good", ]} sub new { my $class = shift; my %par = @_; my ($artist, $album, $titles, $broken_jewel_case, $genre) = @par{'artist','album','titles','broken_jewel_case','genre'}; my ($quality, $cover_filename) = @par{'quality','cover_filename'}; my $self = bless { artist => $artist, album => $album, titles => $titles, broken_jewel_case => $broken_jewel_case, genre => $genre, quality => $quality, cover_filename => $cover_filename, }, $class; return $self; } package My::GuiState; sub get_selected_page { shift->{selected_page} } sub set_selected_page { shift->{selected_page} = $_[1] } sub new { my $class = shift; my %par = @_; my ($selected_page) = $par{'selected_page'}; my $self = bless { selected_page => $selected_page, }, $class; return $self; } 1; �������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/examples/README������������������������������������������������������������0000644�0001750�0001750�00000000511�10147623447�016577� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$Id: README,v 1.1 2004/11/20 11:14:47 joern Exp $ This directory contains example application which use the Gtk2::Ex::FormFactory framework. Change into the examples/ subdirectory and start the programs from here. This works, even if you don't have Gtk2::Ex::FormFactory actually installed into your Perl system library path. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Gtk2-Ex-FormFactory-0.67/examples/cover.jpg���������������������������������������������������������0000644�0001750�0001750�00000017742�10140732265�017545� 0����������������������������������������������������������������������������������������������������ustar �joern���������������������������joern�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��H�H���Created with The GIMP�C�  !"$"$�C���"������������ �X� ���!1 "2AQaq78Rv#BGVWXb$3Ur'45DIc����������������������������������� ��?�h9Mk׍k#;BسL:�0ŖXnQ\bD@E:aS{ZʖB\@\F][Kiq;6*[\J "" """ """ """ """ *g.K+S<m}Yw_Mÿ h2j{üv؛KyO] uS5=3>9mx$WX2>=~ q/ň;̃"ǩz5V@yDрAÝנ:k7,�inozW4RmJ%? y"H&qܻ{ܟY= ۽?W:[ZA[}rT̡{#@_yXǐMߢ8XwW*O.zuV#Zpl4ǿo~ç Bxks BP5N/6c#s3!qi`ms.xBk#+9V:55chw>iDf:]h㨧 dZAhnPOm�q9uTTvRi1|�sa'J 6|+MmXusLt:=#f9s@ v;J1 x,Qѹ6�z}`qn1㵥w`ջGN{N8^9D|^ _CދNF8ַd#{ec*ik吃%=oW b5 NN-_/(; 턭hq.a<w7b"" """ """ """ ""xXľ3ŗ~%8%X~:Ms^ Мwɲv]99{|FZwkzS<ҾKm}6ǒ*Z}[�Axy ݫn\[ **=<6}?s4 T XAV܃0nv50\{߰M@7#cii॥&C,c@�=@߅{U,yBb|,}ګc{q?s[2\JNi#M)bެ͏}N;բ6id` pݿQZv1.L~NS7 {O(>mc|++б U{~ϩ I;Tx~Bw,!D褎8q#}Cz<'۾5wY]O+_ 1o` x�B܆T~Y#7i)Gp. w[YӚKvO-+*xX#c=\6V)I>OgsHrvo->P$em7cX sj�${vu#mR̓l\oe5-X>]3X�*O<Pe9ubG`olep.'=߰`@DDD@DDD@DDD@DDD@^+ݦ|k(t6hppcg�F' b�Yp63fV l;ap-h]@DDkV'ow^5eVsj瑭w3qܝ]D t?J}] Wbݚ6&8 h]@DD}׻E�#d˴`%V+ӻe$DD@DDD@DDD@DDD@DD/<nAwQ0g3f#s^l'm)+m37~ދ=,x \SR5iW27l|4#g=Fp_C4)vPԹ#-=?3tIlyMP>1&zM . .%5N1F%9$˨suX)g[3vn'vZm ktMʲu']&!~�.&i,S'F4\ҳ)&j^<I: H)3}#nnw-'!UV)bi[!;o-LlF*вmlW+=ƒE0*Y,o4T"C_nuu2'7�fߺUf[mmOoQJbݶavH}o~%b/E_Reܠp.܂lk-qSMf[+H;x_GQ˳;?#⮎Vc3C[n=[z9.M0EQd6,38).ӶG / .K.q_e}XXz4 \Dַ搃bV նٲO'c)@cByvqs@;mU,oOQEOU8m ƍZ+ �U 6Mwgz(UGU+@ѳZj �걥^M5 KBrlG)U6�K H;xGUZѿV`Xa֟('uTwfkK>6n ޡ`6;ֻq+ik.C,|s7-2EO83$ص495rw~Z!g5|pϊFzGl%T\xPP]S*)hd̒75B'ӷ_xz⚋LYy99-MF��M" """ """ "" MCQqSSJ˷DV? 6-:RӰ5Y }\9B/>WSFd} kFsF)q<#t'HIAH-Ӱx<š8 Bܦ­ q*j^QtL?3j^٩XTѴ`h/o#2w 6v }4|\Ҽ0ӌ%2)_dF{^$ʈWjXme%LDEf+$h{l._Q1+xEY;M_iHOR$4x {6 &[fَ]ST>M)n F/3C-ycpر9x2l~ 7MO>qEj;'IX='U 4.oė8h,:\NԶ9*d]CG�XnAf}eeC`ҖG j7.ϔ< 6az<,ͩdyc xF?\,kjiCuCz= qe߉}6Fpz;bïv;Y(tN*%rLowύ.K'k#gA t�~=-mZ[%=m=õ죬c[+{9_ söȃ^֫kEl54s=s@ZXjN&0nD7hewk\�YO6ŬYNɨ{S׷a9=+~妣*S=6P@i!'Es~f1Jcx˩kdvTts/5HŜ?7׊*@7I?cd;4D@DDD@DDD@DDƎoSQIU푎9z5f7Z'̰;s>h@4n;F4Hl4A3 > 64͆�g1^ Ns͟㎳a Aw΍qE3Ac۟8jAͱNc\?onaG9^2umVmLfjF �_Fp$y'f" 7xD&ǁ 6Ɨ{9infRV[BZm)X絧vzq� mt 3WcOiptu==}c/!D9bc tNF}n=cue3ŗ~%ÿ2jxd;Q]3Ou ^3?:o`՜V#(tϢ{+c$/k#y 1H^NQ3��*و2۵6J#|}i᳷|6#Wyz\w{[ʘ:z= ~}f];CdrO+mTqwscELtzyɭ�%>= �d�E2er(mp\vo0 ܍ z`93c1+w;kt]c{ ^ծksO؁ЃB"GZq.>"(Ӛ((%@9 r6q<ņUy&KpmFgsy5a;LCԖW; 6 PBc+H<7SQGq=;{n&PORX+\}8> æzK뙅 >0ԷM `7)V7pyOzB "f`:lLׇfi/ ;Dl<h3'9xu:*]K$XyekZݤkOۯx7TM5?䯔{^<ݧgnvx=h&hED3jTl;c%ۮCL]xJxJ t[餪⍥v }'9xu:*]K$XyekZݤkOۯS?]kOBo?5䠹WqkɬvI|Ϊ.Q,MsZ]zOh}UoԔhi u45T1Za V:0s AA[Ib4UVn-0 k] w*؛qq`N ՟R},0*o'ԟK?sL3 OSz7h-WWCM=|ΨXWoJK A|߭6햶vAQ Gb?KK BLiODFDU&ޭ]:1ܛE.W{T өyOC]Kt ٮ;yӑk{\Z< PlcQ2h�<Jzgm=y;820X߬eQTwTC gNh.wG@��x9MU`Ŵ5uҥ?1@35kw֗8�ܓ'&i*5=!=[?Rvtc颣{fD=4ǿu5::-H>lR;x߿Aʴfa`"-篤kN6V'Y 5Jx~G8W+g,4/yC`]Q߰T[<)ivw@6dEQ#:8A-<@;羿ϬAf-ϳ*;OTm#شֈ~ߪlϔ~Kyz;m|窥hmGTs8_Wx۟o A=q}/?|C</U3q}/?|C</TaKniiL7c�"]iFXiuC_KO dɹKz5ds=6ԭRZҶ^_)�en.&߬sKt̺� 威|ܥk Z־G9uPi]E3"##gf˞UXh?'B?ѩvc0xol!U]^~'52 20דr?9-|Q<ܼ<}vŬYaTXkGʴO͝1g=�wv}/7$2z|}SyT/}v9cj"0z l49X\Ƃ[Zvb*B*>siwD{isr1c>)*2ًq}ec6ARYhhj^((f7٥1_Q 8 3}q?Ω >SyTLQSvh?=jӷ~Gm[=|աcMjCrޖhe4:gr4=\GB* :\##KIO툃=p=}zOĮͣ5ީih+/apFkd:\>gPm:C u43'p}m->'[(/VEҖ:]D2 #6p?}uFtVYn'6$r׎s7w&FG{1# #l:gm2ҳ d3m|^ykF%[1ͪ73A/ucp_pZGMoQBil01{� y_tůZI:N&1kHiFt+{etZU{k뭵 "g^v8tni\OlG5h%l\Pr4#Q>o(BѪUYo 3q<>CԌ{P^>qK֚rsǐC3ࠤ}D`7#ڭ.jZl~2"{i4kz|^ ;߻u߾w�JcX .3nR0hr<y;%Jda8qZ;_~M7kͷ}׏Y*{4!I^w\H�!y4)ګlz�hڇ#rdL+O#ixk% (;ʲ6q|>Nܼ܍<mv>kx]РA(֟w 1֧Y&={}Gn=ĩR kC<۸EL8ILg҆j~ 2%{ƴus �:VF8=1g{7'wSOLn}tfOS4S6瘭M#.x]\5ϦeyyFF(3K7a<�z-�;=�eSM=�C}rs}q?Ω EPhUShw !}K+9DC1sIه[������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������