File Coverage

blib/lib/Tk/Columns.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             #======================================================================#
2             # User invokes this to add a column
3             #======================================================================#
4             package Tk::ColumnButton;
5              
6 1     1   3089 use Tk::Frame;
  0            
  0            
7             use Tk;
8              
9             use base qw (Tk::Frame);
10             use vars qw ($VERSION);
11             use strict;
12             use Carp;
13              
14             $VERSION = '0.02';
15              
16             Tk::Widget->Construct ('ColumnButton');
17              
18             sub Populate
19             {
20             my $this = shift;
21              
22             my $l_Parent = $this->parent();
23              
24             $this->SUPER::Populate (@_);
25              
26             $this->{'m_TrimElements'} = [];
27              
28             my $l_Image = $this->Component
29             (
30             'Label' => 'Image',
31             '-relief' => 'flat',
32             '-anchor' => 'w',
33             '-width' => 0,
34             '-padx' => 0,
35             );
36              
37             my $l_Label = $this->Component
38             (
39             'Label' => 'Label',
40             '-relief' => 'flat',
41             '-anchor' => 'w',
42             '-padx' => 0,
43             );
44              
45             my $l_Default =
46             {
47             '-listfont' => $l_Parent->cget ('-listfont') || $l_Label->cget ('-font'),
48             '-listbackground' => $l_Parent->cget ('-listbackground') || 'white',
49             '-listforeground' => $l_Parent->cget ('-listforeground') || 'black',
50             '-listselectmode' => $l_Parent->cget ('-selectmode') || 'browse',
51             '-selectcommand' => $l_Parent->cget ('-selectcommand') || undef,
52             '-buttoncommand' => $l_Parent->cget ('-buttoncommand') || undef,
53             '-foreground' => $l_Parent->cget ('-buttonforeground') || undef,
54             '-background' => $l_Parent->cget ('-buttonbackground') || undef,
55             '-sortcommand' => $l_Parent->cget ('-sortcommand') || undef,
56             '-borderwidth' => $l_Parent->cget ('-borderwidth') || 2,
57             '-trimcount' => $l_Parent->cget ('-trimcount') || 2,
58             '-font' => $l_Parent->cget ('-buttonfont') || undef,
59             '-image' => $l_Parent->cget ('-image') || undef,
60             '-sort' => $l_Parent->cget ('-sort') || 'true',
61             };
62              
63             $this->ConfigSpecs
64             (
65             '-background' => [['METHOD', 'CHILDREN', 'SELF'], 'background', 'Background', $l_Default->{'-background'}],
66             '-listselectmode' => ['METHOD', 'listselectmode', 'ListSelectMode', $l_Default->{'-listselectmode'}],
67             '-listbackground' => ['METHOD', 'listbackground', 'ListBackground', $l_Default->{'-listbackground'}],
68             '-listforeground' => ['METHOD', 'listforeground', 'ListForeground', $l_Default->{'-listforeground'}],
69             '-selectcommand' => ['PASSIVE', 'selectcommand', 'SelectCommand', $l_Default->{'-selectcommand'}],
70             '-buttoncommand' => ['PASSIVE', 'buttoncommand', 'ButtonCommand', $l_Default->{'-buttoncommand'}],
71             '-sortcommand' => ['PASSIVE', 'sortcommand', 'Sortcommand', $l_Default->{'-sortcommand'}],
72             '-borderwidth' => ['SELF', 'borderwidth', 'Borderwidth', $l_Default->{'-borderwidth'}],
73             '-foreground' => ['METHOD', 'foreground', 'Foreground', $l_Default->{'-foreground'}],
74             '-trimcount' => ['METHOD', 'trimcount', 'TrimCount', $l_Default->{'-trimcount'}],
75             '-listfont' => ['METHOD', 'listfont', 'ListFont', $l_Default->{'-listfont'}],
76             '-image' => [$l_Image, 'image', 'Image', $l_Default->{'-image'}],
77             '-sort' => ['PASSIVE', 'sort', 'Sort', $l_Default->{'-sort'}],
78             '-font' => [$l_Label, 'font', 'Font', $l_Default->{'-font'}],
79             );
80              
81             $this->ConfigSpecs
82             (
83             '-text' => [$l_Label, 'text', 'Text', '(No Title)'],
84             '-relief' => ['SELF', 'relief', 'Relief', 'raised'],
85             '-slave' => ['METHOD', 'slave', 'Slave', undef],
86             '-zoom' => ['METHOD', 'zoom', 'Zoom', undef],
87             '-width' => [$l_Label],
88             'DEFAULT' => [$l_Label],
89             );
90              
91             $this->ConfigSpecs
92             (
93             '-buttonbackground' => '-background',
94             '-buttonforeground' => '-foreground',
95             '-slavecolor' => '-listbackground',
96             '-sortfunction' => '-sortcommand',
97             '-buttoncolor' => '-background',
98             '-sortmethod' => '-sortcommand',
99             '-command' => '-selectcommand',
100             '-color' => '-background',
101             '-buttonFont' => '-font',
102             '-bg' => '-background',
103             '-fg' => '-foreground',
104             );
105              
106             $l_Image->pack
107             (
108             '-expand' => 'true',
109             '-side' => 'left',
110             '-fill' => 'both',
111             '-anchor' => 'nw',
112             '-ipadx' => 0,
113             '-padx' => 0,
114             );
115              
116             $l_Label->pack
117             (
118             '-expand' => 'true',
119             '-side' => 'left',
120             '-fill' => 'both',
121             '-anchor' => 'nw',
122             '-ipadx' => 0,
123             '-padx' => 0,
124             );
125              
126             $this->OnDestroy
127             (
128             sub
129             {
130             my $l_Slave = $this->{'Configure'}{'-slave'};
131             return unless defined ($l_Slave) && Exists ($l_Slave);
132             $l_Slave->destroy();
133             }
134             );
135              
136             $l_Label->bind ('' => [\&ButtonPress, $this]);
137             $l_Label->bind ('' => [\&ButtonRelease, $this]);
138             $l_Image->bind ('' => [\&ButtonRelease, $this]);
139             $l_Image->bind ('' => [\&ButtonPress, $this]);
140             $this->DoWhenIdle (['UpdateSizeInfo', $this]);
141             $this->configure ('-highlightthickness' => 0);
142             return $this;
143             }
144              
145             #-----------------------------Event-Handlers----------------------------------#
146              
147             sub UpdateSizeInfo
148             {
149             my ($this) = @_;
150             my $l_Label = $this->Subwidget ('Label');
151             my $l_Image = $this->Subwidget ('Image');
152             $l_Label->{'m_OriginalWidth'} = $l_Label->reqwidth() if ($l_Label->reqwidth() > 1);
153             $l_Image->{'m_OriginalWidth'} = $l_Image->reqwidth() if ($l_Image->reqwidth() > 1);
154             }
155              
156             sub SlaveInvoke
157             {
158             my ($this, $p_Slave) = @_;
159             my $l_Command = $this->cget ('-selectcommand');
160             return unless (defined ($l_Command) && defined ($p_Slave));
161             &{$l_Command} (($p_Slave->curselection())[0]);
162             }
163              
164             sub ResizeStart
165             {
166             $_[1]->{'Configure'}{'-zoom'} = $_[1]->{'m_Zoomed'} = undef;
167             $_[1]->{'m_X'} = $_[1]->pointerx() - $_[1]->rootx();
168             }
169              
170             sub ResizeEnd
171             {
172             my ($p_EventWidget, $this) = @_;
173              
174             return unless defined ($this->{m_X});
175              
176             my $l_Label = $this->Subwidget ('Label');
177             my $l_Width = $l_Label->width() + $this->pointerx() - $this->rootx() - $this->{'m_X'};
178              
179             $l_Label->GeometryRequest ($l_Width > 0 ? $l_Width : 0, $l_Label->reqheight());
180             $this->DoWhenIdle ([\&SlaveUpdate, $this]);
181             $this->{'Configure'}{'-zoom'} = undef;
182             $this->UpdateSizeInfo();
183             $this->{'m_X'} = undef;
184             }
185              
186             sub Zoom
187             {
188             $_[1]->DoWhenIdle (['configure', $_[1], '-zoom' => (defined ($_[2]) ? $_[2] : ! $_[1]->cget ('-zoom'))]);
189             }
190              
191             sub SlaveUpdate
192             {
193             my ($this, $p_Slave) = (@_, $_[0]->cget ('-slave'));
194              
195             my $l_Label = $this->Subwidget ('Label');
196             my $l_Image = $this->Subwidget ('Image');
197              
198             if ($this->{'Configure'}{'-zoom'} && $l_Label->reqwidth() > 1)
199             {
200             $l_Label->GeometryRequest (0, $l_Label->reqheight());
201             $this->update();
202             }
203              
204             #================================
205             # Correct Label and Image sizes
206             #================================
207              
208             if ($l_Label->reqwidth() <= 1 && $l_Image->reqwidth() > 1)
209             {
210             $l_Image->GeometryRequest (0, $l_Image->reqheight());
211             $this->update();
212             }
213             elsif ($l_Label->reqwidth() > 1 && $l_Image->reqwidth() <= 1)
214             {
215             $l_Image->GeometryRequest ($l_Image->{'m_OriginalWidth'}, $l_Image->reqheight());
216             $this->update();
217             }
218              
219             if ($l_Label->reqwidth() <= 1 && ! $this->{'m_Minimized'})
220             {
221             foreach my $l_Child ($this->children())
222             {
223             next unless ($l_Child->name() =~ /^[Tt]rimElement_/);
224              
225             unless ($l_Child->name() =~ /^[Tt]rimElement_0$/)
226             {
227             $l_Child->packForget();
228             }
229             }
230              
231             $this->{'m_Minimized'} = 1;
232             $this->update();
233             }
234             elsif ($this->{'m_Minimized'} && $l_Label->reqwidth() > 1)
235             {
236             foreach my $l_Child ($this->children())
237             {
238             next unless ($l_Child->name() =~ /^[Tt]rimElement_/);
239              
240             unless ($l_Child->name() =~ /^[Tt]rimElement_0$/)
241             {
242             $l_Child->pack
243             (
244             '-expand' => 'false',
245             '-side' => 'right',
246             '-anchor' => 'ne',
247             '-fill' => 'y',
248             '-ipadx' => 0,
249             '-padx' => 0,
250             '-pady' => 1,
251             );
252             }
253             }
254              
255             $this->{'m_Minimized'} = 0;
256             $this->update();
257             }
258              
259             #=====================================================
260             # Slave the listbox to the current width of the button
261             #=====================================================
262             if (defined ($p_Slave))
263             {
264             $p_Slave->GeometryRequest ($this->reqwidth(), $p_Slave->reqheight());
265             $p_Slave->update();
266             }
267              
268             $this->update();
269             }
270              
271             sub ButtonPress
272             {
273             if ($_[1]->{'Configure'}{'-sort'} || ref ($_[1]->{'Configure'}{'-buttoncommand'}) eq 'CODE')
274             {
275             $_[0]->DoWhenIdle (['configure', $_[1], '-relief', 'sunken']);
276             $_[1]->{'m_Sunken'} = 1;
277             }
278             }
279              
280             sub ButtonRelease
281             {
282             if ($_[1]->{'Configure'}{'-sort'})
283             {
284             $_[0]->DoWhenIdle (['SortColumn', $_[1]]);
285             }
286             elsif (ref ($_[1]->{'Configure'}{'-buttoncommand'}) eq 'CODE')
287             {
288             &{$_[1]->{'Configure'}{'-buttoncommand'}} ($_[1], $_[1]->cget ('-slave'));
289             }
290              
291             if ($_[1]->{'m_Sunken'})
292             {
293             $_[0]->DoWhenIdle (['configure', $_[1], '-relief', 'raised']);
294             $_[1]->{'m_Sunken'} = undef;
295             }
296             }
297              
298             sub SortColumn
299             {
300             my $this = shift;
301              
302             my $l_Listbox = $this->{'Configure'}{'-slave'};
303              
304             return unless (defined ($l_Listbox) && ref ($l_Listbox) eq 'Tk::TiedListbox');
305              
306             my $l_SortCommand = $this->{'Configure'}{'-sortcommand'};
307             my @l_SortedKeys = $l_Listbox->get (0, 'end');
308             my @l_NewOrder;
309             my %l_Keys;
310              
311             for (my $l_Index = 0; $l_Index <= $#l_SortedKeys; ++$l_Index)
312             {
313             push (@{$l_Keys {$l_SortedKeys [$l_Index]}}, $l_Index);
314             }
315              
316             if (lc ($l_SortCommand) eq 'numeric')
317             {
318             $l_SortCommand = '$a <=> $b';
319             }
320             elsif (! defined ($l_SortCommand))
321             {
322             $l_SortCommand = 'uc ($a) cmp uc ($b)';
323             }
324              
325             @l_SortedKeys = sort {eval $l_SortCommand} (keys %l_Keys);
326              
327             unless ($l_Listbox->{'m_Reverse'} = ! $l_Listbox->{'m_Reverse'})
328             {
329             @l_SortedKeys = reverse (@l_SortedKeys);
330             }
331              
332             foreach my $l_Key (@l_SortedKeys)
333             {
334             push (@l_NewOrder, @{$l_Keys {$l_Key}});
335             }
336              
337             foreach my $l_Button ($this->parent()->buttons())
338             {
339             my $l_Listbox = $l_Button->cget ('-slave');
340              
341             next unless defined ($l_Listbox);
342              
343             my @l_Contents = $l_Listbox->get (0, 'end');
344             my @l_NewContents;
345              
346             foreach my $l_NewIndex (@l_NewOrder)
347             {
348             push (@l_NewContents, $l_Contents [$l_NewIndex]);
349             }
350              
351             $l_Listbox->delete (0, 'end');
352             $l_Listbox->insert ('end', @l_NewContents);
353             $l_Button->DoWhenIdle ([\&SlaveUpdate, $l_Button, $l_Listbox]);
354             }
355             }
356              
357             #------------------------------- Private methods -----------------------------#
358              
359             sub __slaveconfigure
360             {
361             my ($this, $p_Option, $p_Value) = (shift, @_);
362              
363             my $l_Slave = $this->{'Configure'}{'-slave'};
364              
365             if (defined ($p_Value) && defined ($l_Slave))
366             {
367             my $l_Method = ($l_Slave->isa ('Tk::Listbox') ? 'Tk::Listbox::configure' : 'configure');
368             $this->{'Configure'}{$p_Option} = $p_Value;
369             $p_Option =~ s/^\-list/-/;
370             $this->DoWhenIdle ([$l_Method, $l_Slave, $p_Option, $p_Value]);
371             $this->DoWhenIdle ([\&SlaveUpdate, $this]);
372             }
373              
374             return $this->{'Configure'}{$p_Option};
375             }
376              
377             #-----------------------------'METHOD'-type-settings--------------------------#
378              
379             sub slave
380             {
381             my ($this, $p_Slave) = @_;
382              
383             if (defined ($p_Slave) && Exists ($p_Slave))
384             {
385             ($this->{'Configure'}{'-slave'} = $p_Slave)->bind ('' => sub {$this->SlaveInvoke ($p_Slave);});
386              
387             $this->configure
388             (
389             '-listfont' => $this->cget ('-listfont'),
390             '-listforeground' => $this->cget ('-listforeground'),
391             '-listbackground' => $this->cget ('-listbackground'),
392             '-listselectmode' => $this->cget ('-listselectmode'),
393             );
394             }
395              
396             return $this->{'Configure'}{'-slave'};
397             }
398              
399             sub trimcount
400             {
401             my ($this, $p_TrimCount) = (shift, @_);
402              
403             if (defined ($p_TrimCount) && $p_TrimCount >= 0)
404             {
405             my @l_TrimElements = @{$this->{m_TrimElements}};
406              
407             $p_TrimCount = 12 if ($p_TrimCount > 12);
408              
409             while ($p_TrimCount > $#l_TrimElements + 1)
410             {
411             my $l_Widget = $this->Component
412             (
413             'Frame' => 'TrimElement_'.($#l_TrimElements + 1),
414             '-cursor' => 'sb_h_double_arrow',
415             '-background' => 'white',
416             '-relief' => 'raised',
417             '-borderwidth' => 1,
418             '-width' => 2,
419             );
420              
421             $l_Widget->pack
422             (
423             '-expand' => 'false',
424             '-side' => 'right',
425             '-anchor' => 'ne',
426             '-fill' => 'y',
427             '-ipadx' => 0,
428             '-padx' => 0,
429             '-pady' => 1,
430             );
431              
432             $l_Widget->bind ('' => [\&ResizeEnd, $this]);
433             $l_Widget->bind ('' => [\&ResizeStart, $this]);
434             $l_Widget->bind ('' => [\&Zoom, $this]);
435             push @l_TrimElements, $l_Widget;
436             }
437              
438             while ($p_TrimCount <= $#l_TrimElements)
439             {
440             (pop @l_TrimElements)->destroy();
441             }
442              
443             $this->{m_TrimElements} = [@l_TrimElements];
444             }
445              
446             return $#{$this->{m_TrimElements}} + 1;
447             }
448              
449             sub background
450             {
451             my ($this, $p_Color) = (shift, @_);
452              
453             if (defined ($p_Color))
454             {
455             my $l_OptionColor = $this->option ('get', 'background', 'Background') || $p_Color;
456             my $l_ConfigColor = ${$this->ConfigSpecs()->{'-background'}}[3] || $p_Color;
457             my $l_ParentColor = $this->parent()->cget ('-buttonbackground');
458              
459             my @l_Compare =
460             (
461             sprintf ("#%02x%02x%02x", $this->rgb ($p_Color)),
462             sprintf ("#%02x%02x%02x", $this->rgb ($l_ConfigColor)),
463             sprintf ("#%02x%02x%02x", $this->rgb ($l_OptionColor)),
464             );
465              
466             $this->{'Configure'}{'-background'} = $p_Color =
467             (
468             defined ($l_ParentColor) &&
469             $l_Compare [0] eq $l_Compare [1] &&
470             $l_Compare [0] eq $l_Compare [2] &&
471             ! defined ($this->{'m_Initialized'}) ?
472             $l_ParentColor :
473             $p_Color
474             );
475              
476             foreach my $l_Child ($this->children())
477             {
478             $l_Child->configure ('-background' => $p_Color);
479             }
480              
481             $this->DoWhenIdle (['configure', $this, '-background' => $p_Color]) unless ($this->{'m_Initialized'} >= 2);
482             $this->DoWhenIdle ([\&SlaveUpdate, $this]);
483             ++$this->{'m_Initialized'};
484             }
485              
486             return $this->{'Configure'}{'-background'};
487             }
488              
489             sub foreground
490             {
491             my ($this, $p_Color) = (shift, @_);
492              
493             my $l_Label = $this->Subwidget ('Label');
494              
495             if (defined ($p_Color) && defined ($l_Label))
496             {
497             my $l_OptionColor = $this->option ('get', 'foreground', 'Foreground') || $p_Color;
498             my $l_ConfigColor = ${$this->ConfigSpecs()->{'-foreground'}}[3] || $p_Color;
499             my $l_ParentColor = $this->parent()->cget ('-buttonforeground');
500              
501             my @l_Compare =
502             (
503             sprintf ("#%02x%02x%02x", $this->rgb ($p_Color)),
504             sprintf ("#%02x%02x%02x", $this->rgb ($l_ConfigColor)),
505             sprintf ("#%02x%02x%02x", $this->rgb ($l_OptionColor)),
506             );
507              
508             $this->{'Configure'}{'-foreground'} = $p_Color =
509             (
510             defined ($l_ParentColor) &&
511             $l_Compare [0] eq $l_Compare [1] &&
512             $l_Compare [0] eq $l_Compare [2] &&
513             ! defined ($l_Label->{'m_Initialized'}) ?
514             $l_ParentColor :
515             $p_Color
516             );
517              
518             $l_Label->configure ('-foreground' => $p_Color);
519             $this->DoWhenIdle ([\&SlaveUpdate, $this]);
520             $l_Label->{'m_Initialized'} = 1;
521             }
522              
523             return $this->{'Configure'}{'-foreground'};
524             }
525              
526             sub listbackground
527             {
528             return shift->__slaveconfigure ('-listbackground', @_);
529             }
530              
531             sub listforeground
532             {
533             return shift->__slaveconfigure ('-listforeground', @_);
534             }
535              
536             sub listfont
537             {
538             return shift->__slaveconfigure ('-listfont', @_);
539             }
540              
541             sub listselectmode
542             {
543             return shift->__slaveconfigure ('-listselectmode', @_);
544             }
545              
546             sub zoom
547             {
548             my ($this, $p_State) = @_;
549              
550             my $l_Label = $this->Subwidget ('Label');
551              
552             if (defined ($p_State) && defined ($l_Label))
553             {
554             $this->{'Configure'}{'-zoom'} = $p_State;
555             $l_Label->GeometryRequest ($p_State ? 0 : $l_Label->{'m_OriginalWidth'}, $l_Label->reqheight());
556             $this->DoWhenIdle ([\&SlaveUpdate, $this]);
557             }
558              
559             return $this->{'Configure'}{'-zoom'};
560             }
561              
562             1;
563              
564             #======================================================================#
565             # This is a private class used only by Columns
566             #======================================================================#
567             package Tk::__ButtonContainer;
568              
569             use strict;
570              
571             use Carp;
572              
573             use Tk::Frame;
574              
575             use base qw (Tk::Frame);
576              
577             Tk::Widget->Construct ('__ButtonContainer');
578              
579             sub Populate
580             {
581             my $this = shift;
582              
583             $this->SUPER::Populate (@_);
584              
585             $this->{'m_ButtonList'} = [];
586              
587             $this->ConfigSpecs
588             (
589             '-selectcommand' => ['METHOD', 'selectcommand', 'SelectCommand', undef],
590             '-buttoncommand' => ['METHOD', 'buttoncommand', 'ButtonCommand', undef],
591             '-sortcommand' => ['METHOD', 'sortcommand', 'SortCommand', undef],
592             '-borderwidth' => ['METHOD', 'borderwidth', 'BorderWidth', undef],
593             '-trimcount' => ['METHOD', 'trimcount', 'TrimCount', undef],
594             '-image' => ['METHOD', 'image', 'Image', undef],
595             '-sort' => ['METHOD', 'sort', 'Sort', undef],
596             '-zoom' => ['METHOD', 'zoom', 'Zoom', undef],
597             '-background' => ['METHOD'],
598             '-foreground' => ['METHOD'],
599             '-master' => ['PASSIVE'],
600             '-font' => ['METHOD'],
601             );
602              
603             $this->ConfigSpecs
604             (
605             '-buttonbackground' => '-background',
606             '-buttonforeground' => '-foreground',
607             '-buttoncolor' => '-background',
608             '-command' => '-selectcommand',
609             '-color' => '-background',
610             '-buttonfont' => '-font',
611             );
612              
613             $this->gridRowconfigure (0, '-weight' => 0);
614              
615             return $this;
616             }
617              
618             #-----------------------------Event-Handlers----------------------------------#
619              
620             sub NoticeChild
621             {
622             my ($this, $p_Child) = (shift, @_);
623              
624             return unless ($p_Child->class() eq 'ColumnButton');
625              
626             push (@{$this->{'m_ButtonList'}}, $p_Child);
627              
628             my $l_ColumnIndex = $#{$this->{'m_ButtonList'}};
629              
630             $p_Child->grid
631             (
632             '-column' => $l_ColumnIndex,
633             '-sticky' => 'nsew',
634             '-row' => 0,
635             '-ipadx' => 0,
636             '-padx' => 0,
637             );
638              
639             for (my $l_Index = 0; $l_Index <= $l_ColumnIndex; ++$l_Index)
640             {
641             $this->gridColumnconfigure ($l_Index, '-weight' => 0);
642             }
643              
644             $this->gridColumnconfigure ($l_ColumnIndex, '-weight' => 1);
645              
646             if (defined ($this->{'Configure'}{'-master'}))
647             {
648             $this->{'Configure'}{'-master'}->NoticeChild (@_);
649             }
650             }
651              
652             sub SlaveUpdate
653             {
654             foreach my $l_Button ($_[0]->buttons())
655             {
656             $l_Button->SlaveUpdate() if (defined ($l_Button));
657             }
658             }
659              
660             sub AdjustButtonList
661             {
662             my @l_Array;
663              
664             foreach my $l_Button ($_[0]->buttons())
665             {
666             push (@l_Array, $l_Button) if (Exists ($l_Button));
667             }
668              
669             return @{$_[0]->{'m_ButtonList'} = \@l_Array};
670             }
671              
672             #------------------------------- Private methods -----------------------------#
673              
674             sub __configall
675             {
676             if (defined ($_[2]))
677             {
678             $_[0]->{'Configure'}{$_[1]} = $_[2];
679              
680             foreach my $l_Button ($_[0]->buttons())
681             {
682             $l_Button->configure ($_[1] => $_[2]);
683             # $_[0]->DoWhenIdle (['configure', $l_Button, $_[1] => $_[2]]);
684             }
685             }
686              
687             return ($_[0]->{'Configure'}{$_[1]});
688             }
689              
690             #-----------------------------'METHOD'-type-settings--------------------------#
691              
692             sub buttoncommand {return shift->__configall ('-buttoncommand', @_);}
693             sub selectcommand {return shift->__configall ('-selectcommand', @_);}
694             sub sortcommand {return shift->__configall ('-sortcommand', @_);}
695             sub borderwidth {return shift->__configall ('-borderwidth', @_);}
696             sub background {return shift->__configall ('-background', @_);}
697             sub foreground {return shift->__configall ('-foreground', @_);}
698             sub trimcount {return shift->__configall ('-trimcount', @_);}
699             sub font {return shift->__configall ('-font', @_);}
700             sub sort {return shift->__configall ('-sort', @_);}
701             sub zoom {return shift->__configall ('-zoom', @_);}
702             sub image {return shift->__configall ('-image', @_);}
703              
704             #------------------------------- Public methods -----------------------------#
705              
706             sub buttons
707             {
708             return @{$_[0]->{'m_ButtonList'}};
709             }
710              
711             sub labels
712             {
713             return map {$_->cget ('-text')} ($_[0]->buttons());
714             }
715              
716             sub buttonhash
717             {
718             return {map {$_->cget ('-text'), $_} ($_[0]->buttons())};
719             }
720              
721             *hash = \&Tk::__ButtonContainer::listhash;
722              
723             sub listhash
724             {
725             return {map {$_->cget ('-text'), $_->cget ('-slave')} ($_[0]->buttons())};
726             }
727              
728             sub buttoncontainer
729             {
730             return $_[0];
731             }
732              
733             *buttonwidth = \&Tk::__ButtonContainer::columnwidth;
734             *width = \&Tk::__ButtonContainer::columnwidth;
735              
736             sub columnwidth
737             {
738             my ($this, $p_Column, $p_Width) = @_;
739            
740             my $l_Button = $this->indexedbutton ($p_Column);
741              
742             return unless defined ($l_Button);
743              
744             return $l_Button->cget ('-width') unless ($p_Width >= 0 && $p_Width <= 1024);
745              
746             $l_Button->configure ('-width' => $p_Width);
747              
748             return $p_Width;
749             }
750              
751             1;
752              
753             #======================================================================#
754             # This is a private class used only by the Columns
755             #======================================================================#
756             package Tk::__ListContainer;
757              
758             use Tk::TiedListbox;
759             use Tk::Frame;
760             use Tk;
761              
762             use base qw (Tk::Frame);
763             use strict;
764             use Carp;
765              
766             Tk::Widget->Construct ('__ListContainer');
767              
768             sub Populate
769             {
770             my $this = shift;
771              
772             $this->SUPER::Populate (@_);
773              
774             $this->{'m_Lists'} = [];
775              
776             $this->ConfigSpecs
777             (
778             '-background' => ['METHOD', 'background', 'Background', 'white'],
779             '-selectmode' => ['METHOD', 'selectmode', 'SelectMode', 'single'],
780             '-foreground' => ['METHOD', 'foreground', 'Foreground', 'black'],
781             '-master' => ['PASSIVE'],
782             '-font' => ['METHOD'],
783             );
784              
785             $this->ConfigSpecs
786             (
787             '-listforeground' => '-foreground',
788             '-listbackground' => '-background',
789             '-listselectmode' => '-selectmode',
790             '-listcolor' => '-background',
791             '-color' => '-background',
792             '-listfont' => '-font',
793             );
794              
795             return $this;
796             }
797              
798             #-----------------------------Event-Handlers----------------------------------#
799              
800             sub NoticeChild
801             {
802             my ($this, $p_Child) = (shift, @_);
803              
804             my $l_Length = ($#{$this->{'m_Lists'}} > -1 ? ${$this->{'m_Lists'}}[0] : $p_Child)->size();
805             my @l_ListArray;
806              
807             foreach my $l_Slave ($this->lists())
808             {
809             $l_Slave->pack ('-expand' => 'false');
810             }
811              
812             for (my $l_Index = 0; $l_Index < $l_Length; ++$l_Index)
813             {
814             push (@l_ListArray, undef);
815             }
816              
817             $p_Child->DoWhenIdle (['insert', $p_Child, 'end', @l_ListArray]) if ($#l_ListArray > -1);
818             push (@{$this->{'m_Lists'}}, $p_Child);
819             @l_ListArray = ();
820              
821             foreach my $l_Slave ($this->lists())
822             {
823             push (@l_ListArray, $l_Slave) if ($l_Slave->class() eq 'Listbox');
824             }
825              
826             $p_Child->pack ('-side' => 'left', '-anchor' => 'nw', '-expand' => 'true', '-fill' => 'both', '-padx' => 0);
827             $l_ListArray[0]->tie ('all', [@l_ListArray [1..$#l_ListArray]]);
828             $this->eventGenerate ('');
829             }
830              
831             #------------------------------- Private methods -----------------------------#
832              
833             sub __configall
834             {
835             my ($this, $p_Option, $p_Value) = @_;
836              
837             if (defined ($p_Value))
838             {
839             $this->{'Configure'}{$p_Option} = $p_Value;
840              
841             foreach my $l_List ($this->lists())
842             {
843             $this->DoWhenIdle
844             (
845             [
846             $l_List->isa ('Tk::Listbox') ? 'Tk::Listbox::configure' : 'configure',
847             $l_List,
848             $p_Option => $p_Value
849             ]
850             );
851             }
852              
853             $this->DoWhenIdle (sub {$this->SlaveUpdate();});
854             }
855              
856             return ($this->{'Configure'}{$p_Option});
857             }
858              
859             #-----------------------------'METHOD'-type-settings--------------------------#
860              
861             sub background {return shift->__configall ('-background', @_);}
862             sub foreground {return shift->__configall ('-foreground', @_);}
863             sub selectmode {return shift->__configall ('-selectmode', @_);}
864             sub font {return shift->__configall ('-font', @_);}
865              
866             #------------------------------- Public methods -----------------------------#
867              
868             sub lists
869             {
870             return @{$_[0]->{'m_Lists'}};
871             }
872              
873             sub size
874             {
875             return ($#{$_[0]->{'m_Lists'}} > -1 ? ${$_[0]->{'m_Lists'}}[0]->size() : 0);
876             }
877              
878             sub rows
879             {
880             return $_[0]->size();
881             }
882              
883             sub listcontainer
884             {
885             return $_[0];
886             }
887              
888             sub selection
889             {
890             ${$_[0]->{'m_Lists'}}[0]->selection (@_) if ($#{$_[0]->{'m_Lists'}} > -1);
891             }
892              
893             sub curselection
894             {
895             return ($#{$_[0]->{'m_Lists'}} > -1 ? ${$_[0]->{'m_Lists'}}[0]->curselection() : ());
896             }
897              
898             sub activate
899             {
900             ${$_[0]->{'m_Lists'}}[0]->activate (@_) if ($#{$_[0]->{'m_Lists'}} > -1);
901             }
902              
903             sub nearest
904             {
905             return ($#{$_[0]->{'m_Lists'}} > -1 ? ${$_[0]->{'m_Lists'}}[0]->nearest (@_) : undef);
906             }
907              
908             sub see
909             {
910             ${$_[0]->{'m_Lists'}}[0]->see (@_) if ($#{$_[0]->{'m_Lists'}} > -1);
911             }
912              
913             1;
914              
915             #======================================================================#
916             #
917             #======================================================================#
918             package Tk::Columns;
919              
920             use Tk::Frame;
921             use Tk::Pane;
922             use Tk;
923              
924             use base qw (Tk::Frame);
925             use strict;
926             use Carp;
927              
928             Tk::Widget->Construct ('Columns');
929              
930             sub ClassInit
931             {
932             $_[1]->bind ($_[0], '', ['CheckScrollbars']);
933             $_[1]->bind ($_[0], '', ['CheckScrollbars']);
934             }
935              
936             sub Populate
937             {
938             my $this = shift;
939              
940             $this->SUPER::Populate (@_);
941              
942             my $l_ButtonPane = $this->Component
943             (
944             'Pane' => 'ButtonPane',
945             '-sticky' => 'nsew',
946             '-borderwidth' => 0,
947             );
948              
949             my $l_SlavePane = $this->Component
950             (
951             'Pane' => 'SlavePane',
952             '-sticky' => 'nsew',
953             '-borderwidth' => 0,
954             );
955              
956             my $l_HScrollbar = $this->Component
957             (
958             'Scrollbar' => 'HScroll',
959             '-elementborderwidth' => 1,
960             '-orient' => 'horizontal',
961             );
962              
963             my $l_VScrollbar = $this->Component
964             (
965             'Scrollbar' => 'VScroll',
966             '-elementborderwidth' => 1,
967             '-orient' => 'vertical',
968             );
969              
970             my $l_ButtonContainer = $l_ButtonPane->Component
971             (
972             '__ButtonContainer' => 'ButtonContainer',
973             '-master' => $this,
974             );
975              
976             my $l_ListContainer = $l_SlavePane->Component
977             (
978             '__ListContainer' => 'ListContainer',
979             '-background' => 'white',
980             '-borderwidth' => 0,
981             '-master' => $this,
982             );
983              
984             my $l_UR = $this->Frame
985             (
986             '-relief' => 'raised',
987             '-borderwidth' => 0,
988             '-height' => 0,
989             '-width' => 0,
990             );
991              
992             my $l_Shadow = $l_UR->Frame
993             (
994             '-background' => $l_UR->Darken ($l_UR->cget ('-background'), 50),
995             '-relief' => 'flat',
996             '-borderwidth' => 1,
997             '-height' => 0,
998             '-width' => 1,
999             );
1000              
1001             my $l_BR = $this->Frame
1002             (
1003             '-relief' => 'flat',
1004             '-borderwidth' => 0,
1005             '-height' => 0,
1006             '-width' => 0,
1007             );
1008              
1009             $this->ConfigSpecs
1010             (
1011             '-buttonbackground' => [$l_ButtonContainer],
1012             '-buttonforeground' => [$l_ButtonContainer],
1013             '-buttoncommand' => [$l_ButtonContainer],
1014             '-selectcommand' => [$l_ButtonContainer],
1015             '-listforeground' => [$l_ListContainer],
1016             '-listbackground' => [$l_ListContainer],
1017             '-borderwidth' => [$l_ButtonContainer],
1018             '-buttoncolor' => [$l_ButtonContainer],
1019             '-buttonfont' => [$l_ButtonContainer],
1020             '-trimcount' => [$l_ButtonContainer],
1021             '-selectmode' => [$l_ListContainer],
1022             '-background' => [$l_ListContainer],
1023             '-foreground' => [$l_ListContainer],
1024             '-command' => [$l_ButtonContainer],
1025             '-listcolor' => [$l_ListContainer],
1026             '-listfont' => [$l_ListContainer],
1027             '-image' => [$l_ButtonContainer],
1028             '-zoom' => [$l_ButtonContainer],
1029             '-font' => [$l_ListContainer],
1030             '-columns' => ['METHOD'],
1031             'DEFAULT' => [$l_ButtonContainer],
1032             );
1033              
1034             $this->ConfigSpecs
1035             (
1036             '-command' => '-selectcommand',
1037             '-columnlabels' => '-columns',
1038             '-font' => '-listfont',
1039             '-bg' => '-background',
1040             );
1041              
1042             $l_ButtonContainer->ConfigSpecs
1043             (
1044             '-listbackground' => [$l_ListContainer],
1045             '-listforeground' => [$l_ListContainer],
1046             '-listselectmode' => [$l_ListContainer],
1047             '-listfont' => [$l_ListContainer],
1048             );
1049              
1050             $this->Delegates
1051             (
1052             'buttoncontainer' => $l_ButtonContainer,
1053             'SlaveUpdate' => $l_ButtonContainer,
1054             'columnwidth' => $l_ButtonContainer,
1055             'buttonwidth' => $l_ButtonContainer,
1056             'buttonhash' => $l_ButtonContainer,
1057             'Construct' => $l_ButtonContainer,
1058             'listhash' => $l_ButtonContainer,
1059             'DEFAULT' => $l_ButtonContainer,
1060             'buttons' => $l_ButtonContainer,
1061             'labels' => $l_ButtonContainer,
1062             'width' => $l_ButtonContainer,
1063             'hash' => $l_ButtonContainer,
1064             );
1065              
1066             $this->Delegates
1067             (
1068             'listcontainer' => $l_ListContainer,
1069             'curselection' => $l_ListContainer,
1070             'selection' => $l_ListContainer,
1071             'activate' => $l_ListContainer,
1072             'nearest' => $l_ListContainer,
1073             'lists' => $l_ListContainer,
1074             'size' => $l_ListContainer,
1075             'rows' => $l_ListContainer,
1076             'see' => $l_ListContainer,
1077             );
1078              
1079             $l_ListContainer->Delegates
1080             (
1081             'SlaveUpdate' => $l_ButtonContainer,
1082             'buttoncontainer' => $l_ButtonContainer,
1083             'buttons' => $l_ButtonContainer,
1084             );
1085              
1086             $l_HScrollbar->configure
1087             (
1088             '-command' => sub
1089             {
1090             $l_ButtonPane->xview (@_);
1091             $l_SlavePane->xview (@_);
1092             }
1093             );
1094              
1095             $l_ButtonPane->configure ('-xscrollcommand' => sub {$l_HScrollbar->set (@_);});
1096             $l_SlavePane->configure ('-xscrollcommand' => sub {$l_HScrollbar->set (@_);});
1097             $l_ButtonContainer->bind ('', sub {$this->CheckScrollbars();});
1098             $l_ListContainer->bind ('', sub {$this->CheckScrollbars();});
1099             $this->GridConfigure();
1100              
1101             $l_ButtonContainer->pack ('-side' => 'left', '-anchor' => 'nw', '-expand' => 'true', '-fill' => 'x');
1102             $l_ListContainer->pack ('-side' => 'left', '-anchor' => 'nw', '-expand' => 'true', '-fill' => 'both');
1103             $l_Shadow->pack ('-side' => 'left', '-anchor' => 'nw', '-expand' => 'false', '-fill' => 'y',);
1104             $l_ButtonPane->grid ('-sticky' => 'nsew', '-column' => 0, '-row' => 0);
1105             $l_SlavePane->grid ('-sticky' => 'nsew', '-column' => 0, '-row' => 1);
1106             $l_HScrollbar->grid ('-sticky' => 'nsew', '-column' => 0, '-row' => 2);
1107             $l_VScrollbar->grid ('-sticky' => 'nsew', '-column' => 1, '-row' => 1);
1108             $l_UR->grid ('-sticky' => 'nsew', '-column' => 1, '-row' => 0);
1109             $l_BR->grid ('-sticky' => 'nsew', '-column' => 1, '-row' => 2);
1110              
1111             return $this;
1112             }
1113              
1114             #-----------------------------Event-Handlers----------------------------------#
1115              
1116             sub GridConfigure
1117             {
1118             $_[0]->gridColumnconfigure ( 0, '-minsize' => 0, '-weight' => 1);
1119             $_[0]->gridColumnconfigure ( 1, '-minsize' => 0, '-weight' => 0);
1120             $_[0]->gridRowconfigure ( 0, '-weight' => 0);
1121             $_[0]->gridRowconfigure ( 1, '-minsize' => 0, '-weight' => 1);
1122             $_[0]->gridRowconfigure ( 2, '-minsize' => 0, '-weight' => 0);
1123             }
1124              
1125             sub CheckScrollbars
1126             {
1127             my $l_HScrollbar = $_[0]->Subwidget ('HScroll');
1128             my $l_VScrollbar = $_[0]->Subwidget ('VScroll');
1129              
1130             if ($l_VScrollbar->Needed() && ! $l_VScrollbar->IsMapped())
1131             {
1132             $l_VScrollbar->grid ('-sticky' => 'nsew', '-column' => 1, '-row' => 1);
1133             }
1134             elsif (! $l_VScrollbar->Needed() && $l_VScrollbar->IsMapped())
1135             {
1136             $l_VScrollbar->gridForget();
1137             }
1138              
1139             if ($l_HScrollbar->Needed() && ! $l_HScrollbar->IsMapped())
1140             {
1141             $l_HScrollbar->grid ('-sticky' => 'nsew', '-column' => 0, '-row' => 2);
1142             }
1143             elsif (! $l_HScrollbar->Needed() && $l_HScrollbar->IsMapped())
1144             {
1145             $l_HScrollbar->gridForget();
1146             }
1147             }
1148              
1149             sub NoticeChild
1150             {
1151             my ($this, $p_Child) = (shift, @_);
1152              
1153             return unless ($p_Child->class() eq 'ColumnButton');
1154              
1155             my $l_List = $this->listcontainer()->Listbox
1156             (
1157             '-highlightthickness' => 0,
1158             '-exportselection' => 0,
1159             '-borderwidth' => 0,
1160             '-relief' => 'flat',
1161             );
1162              
1163             $this->DoWhenIdle (['configure', $p_Child, '-slave' => $l_List]);
1164             $this->DoWhenIdle (['ScrollbarBind', $this]);
1165             }
1166              
1167             sub ScrollbarBind
1168             {
1169             my $this = shift;
1170              
1171             return if (defined ($this->{'m_PrimaryList'}) && Exists ($this->{'m_PrimaryList'}));
1172              
1173             my $l_PrimaryList = $this->{'m_PrimaryList'} = ($this->lists())[0];
1174             my $l_VScrollbar = $this->Subwidget ('VScroll');
1175              
1176             return unless (defined ($l_PrimaryList) && defined ($l_VScrollbar));
1177              
1178             $l_VScrollbar->configure
1179             (
1180             '-command' => sub {$l_PrimaryList->yview (@_);}
1181             );
1182              
1183             $l_PrimaryList->configure
1184             (
1185             '-yscrollcommand' => sub {$l_VScrollbar->set (@_);},
1186             );
1187             }
1188              
1189             #------------------------------- Private methods -----------------------------#
1190              
1191             sub __insert
1192             {
1193             my ($this, $p_Where, @p_Data) = @_;
1194              
1195             my @l_ColumnList = $this->lists();
1196             my $l_ColumnHash = $this->hash();
1197              
1198             my $l_Cursor = 0;
1199             my @l_ColumnData;
1200             my %l_Indices;
1201              
1202             #=============================
1203             # Build mutual cross references
1204             #=============================
1205              
1206             foreach my $l_Key (keys %{$l_ColumnHash})
1207             {
1208             for (my $l_Index = 0; $l_Index <= $#l_ColumnList; ++$l_Index)
1209             {
1210             $l_Indices {$l_Key} = $l_Index if ($l_ColumnList [$l_Index] eq $l_ColumnHash->{$l_Key});
1211             }
1212             }
1213              
1214             #=============================
1215             # Build Columnar data arrays
1216             #=============================
1217              
1218             foreach my $l_Datum (@p_Data)
1219             {
1220             if ((ref ($l_Datum) eq 'HASH' || ref ($l_Datum) eq 'ARRAY') && $l_Cursor > 0)
1221             {
1222             while ($l_Cursor <= $#l_ColumnList)
1223             {
1224             push (@{$l_ColumnData [$l_Cursor++]}, '');
1225             }
1226              
1227             $l_Cursor = 0;
1228             }
1229              
1230             if (ref ($l_Datum) eq 'HASH')
1231             {
1232             foreach my $l_Key (keys %{$l_ColumnHash})
1233             {
1234             push (@{$l_ColumnData [$l_Indices {$l_Key}]}, $l_Datum->{$l_Key});
1235             }
1236             }
1237             elsif (ref ($l_Datum) eq 'ARRAY')
1238             {
1239             for (my $l_Index = 0; $l_Index <= $#l_ColumnList; ++$l_Index)
1240             {
1241             push (@{$l_ColumnData [$l_Index]}, ${$l_Datum}[$l_Index]);
1242             }
1243             }
1244             else
1245             {
1246             push (@{$l_ColumnData [$l_Cursor++]}, $l_Datum);
1247             }
1248             }
1249              
1250             while ($l_Cursor <= $#l_ColumnList && $l_Cursor > 0)
1251             {
1252             push (@{$l_ColumnData [$l_Cursor++]}, '');
1253             }
1254              
1255             #=============================
1256             # Insert Column data by column
1257             #=============================
1258             for (my $l_Index = 0; $l_Index <= $#l_ColumnList; ++$l_Index)
1259             {
1260             next if ($p_Where eq '');
1261             $l_ColumnList [$l_Index]->insert ($p_Where, @{$l_ColumnData [$l_Index]});
1262             }
1263              
1264             $this->buttoncontainer()->SlaveUpdate();
1265             }
1266              
1267             sub __update
1268             {
1269             my ($this, $p_Code, @p_Contents) = (shift, @_);
1270              
1271             $this->__delete ($p_Code, $p_Code);
1272             $this->__insert ($p_Code, @p_Contents);
1273             }
1274              
1275             sub __delete
1276             {
1277             my $this = shift;
1278              
1279             foreach my $l_Column ($this->lists())
1280             {
1281             next unless ($_[0] ne '');
1282             $l_Column->delete (@_);
1283             }
1284             }
1285              
1286             #-----------------------------'METHOD'-type-settings--------------------------#
1287              
1288             *columnlabels = \&Tk::Columns::columns;
1289              
1290             sub columns
1291             {
1292             my $this = shift;
1293              
1294             foreach my $l_Item (@{$_[0]})
1295             {
1296             if (ref ($l_Item) eq 'ARRAY')
1297             {
1298             $this->addcolumn (@{$l_Item});
1299             }
1300             elsif (ref ($l_Item) eq '')
1301             {
1302             $this->addcolumn ('-text' => $l_Item);
1303             }
1304             }
1305             }
1306              
1307             #------------------------------- Public methods -----------------------------#
1308              
1309             *Column = \&Tk::Columns::addcolumn;
1310             *Button = \&Tk::Columns::addcolumn;
1311             *column = \&Tk::Columns::addcolumn;
1312             *button = \&Tk::Columns::addcolumn;
1313              
1314             sub addcolumn
1315             {
1316             return shift->ColumnButton (@_);
1317             }
1318              
1319             sub insert
1320             {
1321             $_[0]->DoWhenIdle (['__insert', @_]);
1322             }
1323              
1324             *replace = \&Tk::Columns::update;
1325              
1326             sub update
1327             {
1328             $_[0]->DoWhenIdle (['__update', @_]);
1329             }
1330              
1331             sub delete
1332             {
1333             $_[0]->DoWhenIdle (['__delete', @_]);
1334             }
1335              
1336             sub bbox
1337             {
1338             my $l_Listbox = $_[0]->indexedlist ($_[2] || 0);
1339              
1340             return (defined ($l_Listbox) ? $l_Listbox->bbox ($_[1]) : ());
1341             }
1342              
1343             sub get # This returns a list of references to row data arrays
1344             {
1345             my $this = shift;
1346             my @l_Return;
1347              
1348             foreach my $l_Listbox ($this->lists())
1349             {
1350             my $l_Index = 0;
1351              
1352             foreach my $l_Datum ($l_Listbox->get (@_))
1353             {
1354             push (@{$l_Return [$l_Index++]}, $l_Datum);
1355             }
1356             }
1357              
1358             return (@l_Return);
1359             }
1360              
1361             sub index
1362             {
1363             my ($l_Column) = (shift->lists());
1364              
1365             return (defined ($l_Column) ? $l_Column->index (@_) : undef);
1366             }
1367              
1368             sub indexedbutton
1369             {
1370             return
1371             (
1372             ($_[1] =~ /^[0-9][0-9]*$/ || ! defined ($_[1])) ?
1373             ($_[0]->buttons())[int ($_[1])] :
1374             $_[0]->buttonhash()->{$_[1]}
1375             );
1376             }
1377              
1378             sub indexedlist
1379             {
1380             return
1381             (
1382             ($_[1] =~ /^[0-9][0-9]*$/ || ! defined ($_[1])) ?
1383             ($_[0]->lists())[int ($_[1])] :
1384             $_[0]->listhash()->{$_[1]}
1385             );
1386             }
1387              
1388             1;
1389              
1390             __END__