File Coverage

blib/lib/Tk/EntrySet.pm
Criterion Covered Total %
statement 9 130 6.9
branch 0 18 0.0
condition 0 17 0.0
subroutine 3 27 11.1
pod n/a
total 12 192 6.2


line stmt bran cond sub pod time code
1             package Tk::EntrySet;
2 2     2   51158 use strict;
  2         5  
  2         92  
3 2     2   11 use warnings;
  2         5  
  2         65  
4 2     2   13 use Carp;
  2         8  
  2         4256  
5             #use Data::Dumper;
6              
7              
8             =head1 NAME
9              
10             Tk::EntrySet - display/edit a list of values in a Set of Widgets.
11              
12             =head1 SYNOPSIS
13              
14             require Tk::EntrySet;
15              
16             my $valuelist = [];
17             my $instance = $main_window->EntrySet()->pack;
18             $instance->configure(-valuelist_variable => \$valuelist);
19             $instance->valuelist([qw/foo bar baz/]);
20              
21              
22             =head1 DESCRIPTION
23              
24             Tk::EntrySet creates a Set of widgets to display/edit a list of values.
25             The widget class is configurable. Tk::EntrySet adds/removes widgets to match
26             the size of the valuelist. If a user deletes an entrywidgets content, the
27             value is deleted from the valuelist and the entry is removed from the set
28             on view update. View updates are by default bound to events.
29             This is configurable through the -callback_installer option.
30             The last widget in the Set is always empty to allow users to append values
31             to the list.
32             Tk::EntrySet is a Tk::Frame derived widget.
33              
34              
35              
36             =head1 METHODS
37              
38             B supports the following methods:
39              
40             =over 4
41              
42             =item B[qw/a list of values/]B<)>
43              
44             Get/Set the valuelist (arrayref)
45              
46             =back
47              
48             =head1 OPTIONS
49              
50             B supports the following options:
51              
52             =over 4
53              
54             =item B<-entryclass>
55              
56             A Tk widget class to be used for the entrywidgets. Defaults to 'Entry'.
57              
58             =item B<-entryoptions>
59              
60             Options to be passed to each entry on creation (arrayref).
61              
62             =item B<-getter>
63              
64             A coderef which is used by Tk::EntrySet to read the Entrywidgets content.
65             It gets passed the Entrywidget instance and is expected to return its content.
66             Defaults to
67             sub{ $_[0]->get }, which is suitable for Tk::Entry.
68              
69             =item B<-setter>
70              
71             A coderef which is used by Tk::EntrySet to write the Entrywidgets content.
72             It gets passed the Entrywidget instance and the new value. Defaults to
73             sub{ $_[0]->delete(0,'end');
74             $_[0]->insert('end',$_[1])
75             }, which is suitable for Tk::Entry.
76              
77             =item B<-callback_installer>
78              
79             A coderef which is called after each Entrywidgets instantiation.
80             The callback_installer gets passed the Entrywidget and a coderef that will
81             update the Tk::EntrySet view when called. Defaults to
82             sub{$_[0]->bind('',$_[1])}.
83              
84             =item B<-empty_is_undef>
85              
86             If set to true (default) empty strings will be treated like undef.
87             Undef elements will be removed from the list and from the EntrySet on
88             view updates.
89              
90             =item B<-unique_values>
91              
92             If set to true (default) duplicate elements will be removed on view updates.
93              
94             =item B<-valuelist>
95              
96             Get/Set the list of values (arrayref).
97              
98             =item B<-valuelist_variable>
99              
100             Ties a variable (scalarref) to the -valuelist atribute.
101             This is a Scalar Tie only.
102              
103             =item B<-changed_command>
104              
105             A Callback that is called after the valuelist is updated on user interaction.
106             By default -changed_command is triggered if the user hits in any of
107             the Entries.
108             (See -callback_installer above if you want to change that.)
109              
110              
111             =back
112              
113             =head1 Examples
114              
115             use strict;
116             use warnings;
117              
118             use Tk;
119              
120             my $mw = MainWindow->new ;
121             require Tk::EntrySet;
122              
123             my $valuelist = [];
124             my $entryset = $mw->EntrySet()->pack;
125             $entryset->configure(-valuelist_variable => \$valuelist);
126             $entryset->valuelist([qw/foo bar baz/]);
127              
128             # use another entryclass:
129              
130             my $num_set = $mw->EntrySet(-entryclass => 'NumEntry')->pack;
131             $num_set->valuelist([3,15,42]);
132              
133             # use a BrowseEntry with custom get/set/callback_installer:
134              
135             my $getter = sub{ $_[0]->Subwidget('entry')->get};
136             my $setter = sub{my $e = $_[0]->Subwidget('entry');
137             $e->delete(0,'end');
138             $e->insert('end', $_[1]);
139             };
140             my $inst = sub{$_[0]->bind('' ,$_[1]);
141             $_[0]->configure(-browsecmd => $_[1]);
142             };
143             my $mbe = $mw->EntrySet(-entryclass => 'BrowseEntry',
144             -entryoptions => [-choices => [qw/ a b c d /]],
145             -getter => $getter,
146             -setter => $setter,
147             -callback_installer => $inst,
148             )->pack(-fill => 'both',
149             -expand => 1);
150             $mbe->valuelist([qw/a c/]);
151              
152             MainLoop;
153              
154              
155              
156              
157             =head1 AUTHOR
158              
159             Christoph Lamprecht, ch.l.ngre@online.de
160              
161             =head1 COPYRIGHT AND LICENSE
162              
163             Copyright (C) 2008 by Christoph Lamprecht
164              
165             This library is free software; you can redistribute it and/or modify
166             it under the same terms as Perl itself, either Perl version 5.8.7 or,
167             at your option, any later version of Perl 5 you may have available.
168              
169              
170             =cut
171              
172             our $VERSION = '0.11';
173              
174             our @ISA = 'Tk::Frame';
175             Tk::Widget->Construct('EntrySet');
176              
177             sub default_entryclass{
178 0     0     return 'Entry';
179             }
180             sub default_getter{
181 0     0     return sub{$_[0]->get };
  0     0      
182             }
183             sub default_setter{
184             return sub{
185 0     0     $_[0]->delete(0,'end');
186 0           $_[0]->insert('end',$_[1]);
187 0     0     };
188             }
189             sub default_callback_installer{
190             return sub{
191 0     0     $_[0]->bind('',$_[1])
192 0     0     };
193             }
194              
195             sub Populate{
196 0     0     my ($self,$args) = @_;
197 0           $self->{_EntrySet}{entry_pool}= [];
198 0           $self->{_EntrySet}{entries}= [];
199 0           $self->SUPER::Populate($args);
200 0           my $default_entryclass = $self->default_entryclass;
201 0           my $default_getter = $self->default_getter;
202 0           my $default_setter = $self->default_setter;
203 0           my $default_callback_installer = $self->default_callback_installer;
204 0           $self->ConfigSpecs(-entryclass => ['PASSIVE',undef,undef,
205             $default_entryclass],
206             -entryoptions => ['PASSIVE',undef,undef,[]],
207             -getter => ['PASSIVE',undef,undef,
208             $default_getter],
209             -setter => ['PASSIVE',undef,undef,
210             $default_setter],
211             -changed_command => ['CALLBACK',undef,undef,undef],
212             -callback_installer => ['PASSIVE',undef,undef,
213             $default_callback_installer],
214             -empty_is_undef => ['PASSIVE',undef,undef,1],
215             -valuelist => ['METHOD',undef,undef,undef],
216             -unique_values => ['PASSIVE', undef,undef,1],
217             -valuelist_variable => ['METHOD',undef,undef,undef],
218             );
219 0 0         my $valuelist= exists $args->{-valuelist}
220             ? delete $args->{-valuelist}
221             : undef;
222 0 0         if( $valuelist ){
223 0     0     $self->afterIdle(sub{$self->valuelist($valuelist)});
  0            
224             }
225 0     0     $self->OnDestroy(sub{$self->untie_valuelist_variable});
  0            
226              
227             }
228              
229              
230             sub new_entry{
231 0     0     my $self = shift;
232 0           my $pool = $self->{_EntrySet}{entry_pool};
233 0           my $entry = shift @$pool;
234 0 0         unless ($entry){
235             # we haven't got one - create
236 0           my $class = $self->cget('-entryclass');
237 0           my @options = @{$self->cget('-entryoptions')};
  0            
238 0           $entry = $self->$class(@options);
239 0           my $installer = $self->cget(-callback_installer);
240             $installer->($entry,
241             sub{
242 0           $self->afterIdle(
243             sub{$self->valuelist;
244 0           $self->Callback('-changed_command');
245 0     0     });
246 0           });
247             }
248             # add entry to the active entries list
249 0           push @{$self->{_EntrySet}{entries}}, $entry;
  0            
250 0           return $entry;
251             }
252              
253             sub remove_entry{
254 0     0     my $self = shift;
255 0           my $entry = shift;
256 0 0         croak "entry does not exist" unless Tk::Exists($entry);
257             # remove from the list of active entries
258              
259 0           my $i = 0;
260 0           my @entries = @{$self->{_EntrySet}{entries}};
  0            
261 0           for my $each (@entries){
262 0 0         if($each eq $entry){
263 0           splice @{$self->{_EntrySet}{entries}},$i,1;
  0            
264 0           last;
265             }
266 0           $i++ ;
267             }
268             # add to the pool
269 0           my $pool = $self->{_EntrySet}{entry_pool};
270 0           push @$pool, $entry;
271 0           $entry->packForget;
272 0           my $last_entry = $entries[$#entries];
273 0           $last_entry->focus;
274              
275             }
276              
277              
278             sub valuelist{ # get/set valuelist (Arrayref)
279 0     0     my $self = shift;
280 0           my ($valuelist) = @_;
281 0 0         if ($valuelist){
282 0           $self->set_valuelist($valuelist);
283             }else{
284 0           $valuelist = $self->get_valuelist;
285             }
286 0           return $valuelist;
287             }
288              
289             ### set_valuelist expects an arrayref of values to set.
290             ### it creates a new entry for each value and adds an undefed
291             ### entry to the end of the list
292             sub set_valuelist{
293 0     0     my $self = shift;
294 0           my ($valuelist) = @_;
295 0           $self->clear_valuelist;
296 0           for my $value (@$valuelist, undef){
297 0           my $new = $self->new_entry;
298 0           $self->write_entry($new,$value);
299 0           $new->pack( -fill => 'x',
300             -expand => 1 );
301             }
302             }
303              
304             ### get_valuelist returns an arrayref of values
305             ### it performs a 'cleanup' deleting undefed entries
306             ### and adds an undefed entry to the end of the list
307             ### if necessary
308             sub get_valuelist{
309 0     0     my $self = shift;
310             # operate on a copy
311 0           my @entries = @{$self->{_EntrySet}{entries}};
  0            
312 0           my $valuelist = [];
313             # test index of last entry to see if we need a new one
314             # (set to undef) at the end
315 0 0 0       if (scalar @entries == 0 # we have no entry displayed yet
316             or( # or last entry has defined content:
317             defined ( $self->read_entry($entries[$#entries]) )
318             ) ){
319 0           my $new = $self->new_entry;
320 0           $self->write_entry($new,undef);
321 0           $new->pack( -fill => 'x',
322             -expand => 1 );
323             # print "adding a new entry at the bottom: $new\n";
324             } else {
325             # the last entry is empty - ignore its content
326             # for the return list
327 0           my $ignore = pop @entries;
328             # print "ignoring last entry: $ignore\n";
329             }
330 0           my $unique = $self->cget('-unique_values');
331 0           my %seen;
332 0           my $empty_is_undef = $self->cget('-empty_is_undef');
333 0           for my $entry (@entries) {
334 0           my $value = $self->read_entry($entry);
335 0 0 0       if ($empty_is_undef
      0        
336             and (defined $value)
337             and ($value eq '')){
338 0           undef $value;
339             }
340 0 0 0       if (defined $value
      0        
341             and ( (! $seen{$value}) || (! $unique) )
342             ) {
343 0           push @$valuelist , $value;
344 0           $seen{$value} = 1;
345             } else {
346             # print "removing entry[$entry] with value [$value]\n";
347 0           $self->remove_entry($entry);
348             }
349             }
350 0           return $valuelist;
351             }
352              
353             sub clear_valuelist{
354 0     0     my $self = shift;
355 0           my @entries = @{$self->{_EntrySet}{entries}};
  0            
356 0           for my $e (@entries){
357 0           $self->remove_entry($e);
358             }
359             }
360              
361             sub valuelist_variable{
362 0     0     my $self = shift;
363 0           my $varref = shift;
364 0           $self->untie_valuelist_variable;
365 0           tie ($$varref, 'ESTier', $self);
366 0           $self->{_EntrySet}{valuelist_variable_ref} = $varref;
367             }
368              
369             sub untie_valuelist_variable{
370 0     0     my $self = shift;
371 0   0       my $oldref = $self->{_EntrySet}{valuelist_variable_ref} || \0;
372 0           untie ($$oldref);
373             }
374              
375             sub read_entry{
376 0     0     my $self = shift;
377 0           my $entry = $_[0];
378 0           my $reader = $self->cget(-getter);
379 0           return $reader->($entry);
380             }
381             sub write_entry{
382 0     0     my $self = shift;
383 0           my ($entry,$value) = @_;
384 0           my $writer = $self->cget(-setter);
385 0           $writer->($entry,$value);
386             }
387              
388             package ESTier;
389              
390             sub TIESCALAR{
391 0     0     my $class = shift;
392 0           my ( $w) = @_;
393 0           my $tied = bless { es => $w,
394             }, $class;
395 0           return $tied;
396             }
397              
398             sub FETCH{
399 0     0     my $self = shift; # tied instance
400 0           return ($self->{es})->cget('-valuelist');
401             }
402              
403             sub STORE{
404 0     0     my $self = shift;
405 0           my $val = shift;
406 0           ($self->{es})->configure(-valuelist => $val);
407 0           ($self->{es})->cget('-valuelist');
408             }
409              
410             1;