# Copyright (c) 1995-2003 Nick Ing-Simmons. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. package Tk::Table; use strict; use vars qw($VERSION); $VERSION = sprintf '4.%03d', q$Revision: #12 $ =~ /\D(\d+)\s*$/; use Tk::Pretty; use AutoLoader; use base qw(Tk::Frame); Construct Tk::Widget 'Table'; # Constants for QueueLayout flags sub _SlaveSize () { 1 } # Slave has asked for change of width or height sub _SlaveChange () { 2 } # We lost or gained a slave sub _ViewChange () { 4 } # xview or yview called sub _ConfigEvent () { 8 } # Table has changed size sub _ScrollBars () { 32 } # Scrollabrs came or went sub _RowColCount () { 16 } # rows or columns configured sub ClassInit { my ($class,$mw) = @_; $mw->bind($class,'',['QueueLayout',_ConfigEvent]); $mw->bind($class,'', 'NoOp'); $mw->XYscrollBind($class); return $class; } sub _view { my ($t,$s,$page,$a,$op,$num,$type) = @_; if ($op eq 'moveto') { $$s = int(@$a*$num); } else { $num *= ($page/2) if ($type eq 'pages'); $$s += $num; } $$s = 0 if ($$s < 0); $t->QueueLayout(_ViewChange); } sub xview { my $t = shift; $t->_view(\$t->{Left},$t->cget('-columns'),$t->{Width},@_); } sub yview { my $t = shift; $t->_view(\$t->{Top},$t->cget('-rows'),$t->{Height},@_); } sub FocusChildren { my $t = shift; return () if ($t->cget('-takefocus')); return $t->SUPER::FocusChildren; } sub Populate { my ($t,$args) = @_; $t->SUPER::Populate($args); $t->ConfigSpecs('-scrollbars' => [METHOD => 'scrollbars','Scrollbars','nw'], '-takefocus' => [SELF => 'takeFocus','TakeFocus',1], '-rows' => [METHOD => 'rows','Rows',10], '-fixedrows' => [METHOD => 'fixedRows','FixedRows',0], '-columns' => [METHOD => 'columns','Columns',10], '-fixedcolumns' => [METHOD => 'fixedColumn','FixedColumns',0], '-highlightthickness' => [SELF => 'highlightThickness','HighlightThickness',2] ); $t->_init; } sub sizeN { my ($n,$a) = @_; my $max = 0; my $i = 0; my $sum = 0; while ($i < @$a && $i < $n) { my $n = $a->[$i++]; $a->[$i-1] = $n = 0 unless (defined $n); $sum += $n; } $max = $sum if ($sum > $max); while ($i < @$a) { $sum = $sum-$a->[$i-$n]+$a->[$i]; $max = $sum if ($sum > $max); $i++; } return $max; } sub total { my ($a) = @_; my $total = 0; my $x; foreach $x (@{$a}) { $total += $x; } return $total; } sub constrain { my ($sb,$a,$pixels,$fixed) = @_; my $n = $$sb+$fixed; my $total = 0; my $i; $n = @$a if ($n > @$a); $n = $fixed if ($n < $fixed); for ($i= 0; $i < $fixed; $i++) { (defined($a->[$i])) && ($total += $a->[$i]); } for ($i=$n; $total < $pixels && $i < @$a; $i++) { $a->[$i] ||= 0; $total += $a->[$i]; } while ($n > $fixed) { if (($total += $a->[--$n]) > $pixels) { $n++; last; } } $$sb = $n-$fixed; } sub Layout { my ($t) = @_; return unless Tk::Exists($t); my $rows = @{$t->{Row}}; my $bw = $t->cget(-highlightthickness); my $frows = $t->cget(-fixedrows); my $fcols = $t->cget(-fixedcolumns); my $sb = $t->cget(-scrollbars); my $H = $t->Height; my $W = $t->Width; my $tadj = $bw; my $badj = $bw; my $ladj = $bw; my $radj = $bw; my @xs = ($W,0,0,0); my @ys = (0,$H,0,0); my $xsb; my $ysb; my $why = $t->{LayoutPending}; $t->{LayoutPending} = 0; if ($sb =~ /[ns]/) { $t->{xsb} = $t->Scrollbar(-orient => 'horizontal', -command => ['xview' => $t]) unless (defined $t->{xsb}); $xsb = $t->{xsb}; $xs[3] = $xsb->ReqHeight; if ($sb =~ /n/) { $xs[1] = $tadj; $tadj += $xs[3]; } else { $badj += $xs[3]; $xs[1] = $H-$badj; } } else { $t->{xsb}->UnmapWindow if (defined $t->{xsb}); } if ($sb =~ /[ew]/) { $t->{ysb} = $t->Scrollbar(-orient => 'vertical', -command => ['yview' => $t]) unless (defined $t->{ysb}); $ysb = $t->{ysb}; $ys[2] = $ysb->ReqWidth; if ($sb =~ /w/) { $ys[0] = $ladj; $ladj += $ys[2]; } else { $radj += $ys[2]; $ys[0] = $W-$radj; } } else { $t->{ysb}->UnmapWindow if (defined $t->{ysb}); } constrain(\$t->{Top}, $t->{Height},$H-($tadj+$badj),$frows); constrain(\$t->{Left},$t->{Width}, $W-($ladj+$radj),$fcols); my $top = $t->{Top}+$frows; my $left = $t->{Left}+$fcols; if ($why & (_ScrollBars|_RowColCount|_SlaveSize)) { # Width and/or Height of element or # number of rows and/or columns or # scrollbar presence has changed my $w = sizeN($t->cget('-columns'),$t->{Width})+$radj+$ladj; my $h = sizeN($t->cget('-rows'),$t->{Height})+$tadj+$badj; $t->GeometryRequest($w,$h); } if ($rows) { my $cols = @{$t->{Width}}; my $yhwm = $top-$frows; my $xhwm = $left-$fcols; my $y = $tadj; my $r; for ($r = 0; $r < $rows; $r++) { my $h = $t->{Height}[$r]; next unless defined $h; if (($r < $top && $r >= $frows) || ($y+$h > $H-$badj)) { if (defined $t->{Row}[$r]) { my $c; for ($c = 0; $c < @{$t->{Row}[$r]}; $c++) { my $s = $t->{Row}[$r][$c]; if (defined $s) { $s->UnmapWindow; if ($why & 1) { my $w = $t->{Width}[$c]; $s->ResizeWindow($w,$h); } } } } } else { my $hwm = $left-$fcols; my $sh = 0; my $x = $ladj; my $c; $ys[1] = $y if ($y < $ys[1] && $r >= $frows); for ($c = 0; $c <$cols; $c++) { my $s = $t->{Row}[$r][$c]; my $w = $t->{Width}[$c]; if (($c < $left && $c >= $fcols) || ($x+$w > $W-$radj) ) { if (defined $s) { $s->UnmapWindow; $s->ResizeWindow($w,$h) if ($why & 1); } } else { $xs[0] = $x if ($x < $xs[0] && $c >= $fcols); if (defined $s) { if ($why & 1) { $s->MoveResizeWindow($x,$y,$w,$h); } else { $s->MoveWindow($x,$y); } $s->MapWindow; } $x += $w; if ($c >= $fcols) { $hwm++; $sh += $w } } } $xhwm = $hwm if ($hwm > $xhwm); $xs[2] = $sh if ($sh > $xs[2]); $y += $h; if ($r >= $frows) { $ys[3] += $h; $yhwm++; } } } $t->{Bottom} = $yhwm; $t->{Right} = $xhwm; if (defined $xsb && $xs[2] > 0) { $xsb->MoveResizeWindow(@xs); $cols -= $fcols; if ($cols > 0) { $xsb->set($t->{Left}/$cols,$t->{Right}/$cols); $xsb->MapWindow; } } if (defined $ysb && $ys[3] > 0) { $ysb->MoveResizeWindow(@ys); $rows -= $frows; if ($rows > 0) { $ysb->set($t->{Top}/$rows,$t->{Bottom}/$rows); $ysb->MapWindow; } } } } sub QueueLayout { my ($m,$why) = @_; $m->afterIdle(['Layout',$m]) unless ($m->{LayoutPending}); $m->{LayoutPending} |= $why; } sub SlaveGeometryRequest { my ($m,$s) = @_; my ($row,$col) = @{$m->{Slave}{$s->PathName}}; my $sw = $s->ReqWidth; my $sh = $s->ReqHeight; my $sz = 0; if ($sw > $m->{Width}[$col]) { $m->{Width}[$col] = $sw; $m->QueueLayout(_SlaveSize); $sz++; } if ( (not defined ($m->{Height}[$row])) or $sh > $m->{Height}[$row]) { $m->{Height}[$row] = $sh; $m->QueueLayout(_SlaveSize); $sz++; } if (!$sz) { $s->ResizeWindow($m->{Width}[$col],$m->{Height}[$row]); } } sub get { my ($t,$row,$col) = @_; return $t->{Row}[$row][$col]; } sub LostSlave { my ($t,$s) = @_; my $info = delete $t->{Slave}{$s->PathName}; if (defined $info) { my ($row,$col) = @$info; $t->{Row}[$row][$col] = undef; $s->UnmapWindow; } else { $t->BackTrace('Cannot find' . $s->PathName); } $t->QueueLayout(_SlaveChange); } sub clear { my $self = shift; my $rows = $self->cget(-rows); my $cols = $self->cget(-columns); foreach my $r (1 .. $rows) { foreach my $c (1 .. $cols) { my $old = $self->get( $r, $c ); next unless $old; $self->LostSlave($old); $old->destroy; } } $self->_init; $self->QueueLayout(_SlaveSize); } sub _init { my $self = shift; $self->{'Width'} = []; $self->{'Height'} = []; $self->{'Row'} = []; $self->{'Slave'} = {}; $self->{'Top'} = 0; $self->{'Left'} = 0; $self->{'Bottom'} = 0; $self->{'Right'} = 0; $self->{LayoutPending} = 0; } sub put { my ($t,$row,$col,$w) = @_; $w = $t->Label(-text => $w) unless (ref $w); $t->ManageGeometry($w); unless (defined $t->{Row}[$row]) { $t->{Row}[$row] = []; $t->{Height}[$row] = 0; } unless (defined $t->{Width}[$col]) { $t->{Width}[$col] = 0; } my $old = $t->{Row}[$row][$col]; if (defined $old) { $old->UnmanageGeometry; $t->LostSlave($old); } $t->{Row}[$row][$col] = $w; $t->{Slave}{$w->PathName} = [$row,$col]; $t->SlaveGeometryRequest($w); $t->QueueLayout(_SlaveChange); return $old; } # # configure methods # sub scrollbars { my ($t,$v) = @_; if (@_ > 1) { $t->_configure(-scrollbars => $v); $t->QueueLayout(_ScrollBars); } return $t->_cget('-scrollbars'); } sub rows { my ($t,$r) = @_; if (@_ > 1) { $t->_configure(-rows => $r); if ($t->{Row} && @{$t->{Row}} > $r) { for my $y ($r .. $#{$t->{Row}}) { for my $s (@{$t->{Row}[$y]}) { $s->destroy if $s; } } splice @{ $t->{Row} }, $r; } $t->QueueLayout(_RowColCount); } return $t->_cget('-rows'); } sub fixedrows { my ($t,$r) = @_; if (@_ > 1) { $t->_configure(-fixedrows => $r); $t->QueueLayout(_RowColCount); } return $t->_cget('-fixedrows'); } sub columns { my ($t,$r) = @_; if (@_ > 1) { $t->_configure(-columns => $r); if ($t->{Row}) { for my $row (@{$t->{Row}}) { for my $s (@$row[$r .. $#$row]) { $s->destroy if $s; } { # FIXME? - Steve was getting warnings : # splice() offset past end of array local $^W = 0; splice @$row, $r; } } } $t->QueueLayout(_RowColCount); } return $t->_cget('-columns'); } sub fixedcolumns { my ($t,$r) = @_; if (@_ > 1) { $t->_configure(-fixedcolumns => $r); $t->QueueLayout(_RowColCount); } return $t->_cget('-fixedcolumns'); } 1; __END__ sub Create { my $t = shift; my $r = shift; my $c = shift; my $kind = shift; $t->put($r,$c,$t->$kind(@_)); } sub totalColumns { scalar @{shift->{'Width'}}; } sub totalRows { scalar @{shift->{'Height'}}; } sub Posn { my ($t,$s) = @_; my $info = $t->{Slave}{$s->PathName}; return (wantarray) ? @$info : $info; } sub see { my $t = shift; my ($row,$col) = (@_ == 2) ? @_ : @{$t->{Slave}{$_[0]->PathName}}; my $see = 1; if (($row -= $t->cget('-fixedrows')) >= 0) { if ($row < $t->{Top}) { $t->{Top} = $row; $t->QueueLayout(_ViewChange); $see = 0; } elsif ($row >= $t->{Bottom}) { $t->{Top} += ($row - $t->{Bottom}+1); $t->QueueLayout(_ViewChange); $see = 0; } } if (($col -= $t->cget('-fixedcolumns')) >= 0) { if ($col < $t->{Left}) { $t->{Left} = $col; $t->QueueLayout(_ViewChange); $see = 0; } elsif ($col >= $t->{Right}) { $t->{Left} += ($col - $t->{Right}+1); $t->QueueLayout(_ViewChange); $see = 0; } } return $see; } =cut