home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / SpreadsheetHideRows.pm < prev    next >
Encoding:
Perl POD Document  |  2002-12-06  |  16.8 KB  |  679 lines

  1. =head1 NAME
  2.  
  3. Tk::TableMatrix::SpreadsheetHideRows - Table Display with selectable hide/un-hide of rows
  4.  
  5. =head1 SYNOPSIS
  6.  
  7.   use Tk;
  8.   use Tk::TableMatrix::SpreadsheetHideRows
  9.  
  10.  
  11.  
  12.   my $t = $top->Scrolled('SpreadsheetHideRows', 
  13.                     -selectorCol => 3,
  14.                   -expandData => $hashRef,
  15.                   -rows => 21, -cols => 11, 
  16.                               -width => 6, -height => 6,
  17.                   -titlerows => 1, -titlecols => 1,
  18.                   -variable => $arrayVar,
  19.                   -selectmode => 'extended',
  20.                   -resizeborders => 'both',
  21.                   -titlerows => 1,
  22.                   -titlecols => 1,
  23.                   -bg => 'white',
  24.                     );
  25.  
  26. =head1 DESCRIPTION
  27.  
  28. L<Tk::TableMatrix::SpreadsheetHideRows> is a L<Tk::TableMatrix::Spreadsheet>-derived widget that implements
  29. a Spreadsheet-like display of tabular information, where some of the rows in the table
  30. can be expanded/hidden by clicking a '+/-' selector in the row. This can be used to display
  31. top-level information in a table, while allowing the user to expand certain table rows to 
  32. view detail-level information. 
  33.  
  34. See demos/SpreadsheetHideRows in the source distribution for a simple example of this widget
  35.  
  36. =head1 Widget-specific Options 
  37.  
  38. In addition the standard L<Tk::TableMatrix> widget options. The following options are implemented:
  39.  
  40. =over 1
  41.  
  42. =item  -selectorCol
  43.  
  44. Column number where the +/- selector will appear. Clicking on the +/- selector
  45. will expand/hide the detail information in the table for a particular row.
  46.  
  47. =item  -selectorColWidth
  48.  
  49. Width of the column used to display the +/- selector. Defaults to 2
  50.  
  51. =item -expandData
  52.  
  53. Hash ref defining the detail-level data displayed when a row is expanded (by clicking
  54. the +/- selector). This hash ref should have the following structure:
  55.  
  56.   $expandData = {  
  57.       row1 => {  tag  => 'detailDataTag',
  58.                data => $detailData,
  59.            spans=> $spanData,
  60.            expandData => $subLevelData
  61.         },
  62.     row2 => { 
  63.        .
  64.        .
  65.   }
  66.   
  67.   Where: 
  68.     row1, row2, ...         Row numbers that will be expandable.
  69.     tag => 'detailDataTag'  Tag name that will be applied to the detail data.
  70.                     (optional)
  71.     $detailData              2D Array of detail-data to be displayed when 
  72.                      the row is expanded.
  73.                  e.g. [ [ r1c1, r1c2, r1c3 ],
  74.                         [ r2c1, r2c2, r2,c3] ]
  75.     $spans                   1D array of span information (optional) to be 
  76.                      used for display of the detail information.
  77.                  e.g.  [ col2 =>  "rows,cols", col4 =>  "rows,cols", ... ]
  78.  
  79.     $subLevelData            Optional Recursive expandData used to hold detail-data of detail-data.
  80.  
  81.  
  82.  
  83. =back
  84.  
  85. =head1 MEMBER DATA
  86.  
  87. The following items are stored as member data
  88.  
  89. =over 1
  90.  
  91. =item defaultCursor
  92.  
  93. Name of the mouse cursor pointer that is used for normal (i.e. non-title, non-indicator) cells in the widget. 
  94. This is set to the value of the $widget->cget(-cursor) option when the widget is created. 
  95.  
  96. =item indRowCols
  97.  
  98. Hash ref of Row/Cols indexes where there are indicators stores. This is a quick
  99. lookup hash built from I<_expandData>.
  100.  
  101. =item _expandData
  102.  
  103. Internal version of the I<expandData> hash. Any sub-detail data (i.e. expand data 
  104. that is at lower levels of I<expandData>) that is visible is placed at the top level of this hash, for
  105. keeping track of the visible I<expandData>.
  106.  
  107. =back
  108.  
  109. =head1 Widget Methods 
  110.  
  111. In addition the standard L<Tk::TableMatrix> widget method. The following methods are implemented:
  112.  
  113.  
  114. =cut
  115.  
  116. package Tk::TableMatrix::SpreadsheetHideRows;
  117.  
  118. use Carp;
  119.  
  120.  
  121. use Tk;
  122. use Tk::TableMatrix::Spreadsheet;
  123. use Tk::Derived;
  124.  
  125. use base qw/ Tk::Derived Tk::TableMatrix::Spreadsheet/;
  126.  
  127. $VERSION = '1.01';
  128.  
  129.  
  130. Tk::Widget->Construct("SpreadsheetHideRows");
  131.  
  132.  
  133. sub ClassInit{
  134.     my ($class,$mw) = @_;
  135.  
  136.     $class->SUPER::ClassInit($mw);
  137.     
  138.     
  139.  
  140. };
  141.  
  142.  
  143. sub Populate {
  144.     my ($self, $args) = @_;
  145.     
  146.     $self->ConfigSpecs(
  147.        -selectorCol     =>     [qw/METHOD selectorCol     SelectorCol/,    undef],
  148.        -selectorColWidth=>     [qw/PASSIVE selectorColWidth     SelectorColWidth/,    2],
  149.        -expandData      =>     [qw/METHOD expandData     ExpandData/,    {}],
  150.    );
  151.  
  152.     
  153.     $self->SUPER::Populate($args);
  154.     
  155.     $self->tagConfigure('plus', -image =>  $self->Getimage("plus"), -showtext => 0);
  156.     $self->tagConfigure('minus', -image =>  $self->Getimage("minus"), -showtext => 0);
  157.     
  158.     $self->{normalCursor} = $self->cget('-cursor'); # get the default cursor
  159.  
  160.  
  161. }
  162.  
  163. =head2 showDetail
  164.  
  165. Shows (i.e. expands the table) the detail data for a given row. This method is called
  166. when a user clicks on an indicator that is not already expanded.
  167.  
  168. B<Usage:>
  169.  
  170.  $widget->showDetail($row);  
  171.  
  172.  # Shows the detail data for row number $row
  173.  
  174. =cut
  175.  
  176. sub showDetail{
  177.  
  178.     my $self = shift;
  179.     
  180.     my $row = shift;
  181.     
  182.     my $selectorCol = $self->cget(-selectorCol);
  183.     
  184.     my $index = "$row,$selectorCol"; # make index for the cell to be expanded
  185.     
  186.     my $indRowCols = $self->{indRowCols};
  187.     
  188.     $self->tagCell('minus', $index);
  189.     $indRowCols->{$index} = '-';
  190.     
  191.     # Get the detail data and insert:
  192.     my $expandData = $self->{'_expandData'};
  193.     my $detailData = $expandData->{$row};
  194.     my $detailArray = $detailData->{data};
  195.     
  196.     my $noRows = scalar( @$detailArray);
  197.     
  198.     # InsertRows:
  199.     $self->insertRows($row,$noRows);
  200.     
  201.     # Adjust Spans:
  202.     $self->adjustSpans($row,$noRows);
  203.     
  204.     #insert data
  205.     my $colorigin =  $self->cget(-colorigin);
  206.     my $rowNum = $row+1;
  207.     foreach my $rowData( @$detailArray ){
  208.         #my @rowArray = @$rowData; 
  209.         #grep s/([\{\}])/\\$1/g, @rowArray; # backslash any existing '{' chars, so they don't get interpreted as field chars
  210.         my $insertData = "{".join("}{", @$rowData)."}"; # make insert data look like tcl array, so it
  211.                                 # gets put in different cells
  212.         $self->set('row', "$rowNum,$colorigin", $insertData);
  213.         $rowNum++;
  214.     }
  215.     
  216.     # Apply Tags, if any:
  217.     my $tag;
  218.     if( defined( $detailData->{tag})){
  219.         $tag = $detailData->{tag};
  220.         my $startRow = $row+1;
  221.         my $noRows = @$detailArray;
  222.         my $stopRow = scalar(@$detailArray) + $startRow - 1;
  223.         my @tagRows = ($startRow..$stopRow);
  224.         $self->tagRow($tag,@tagRows);
  225.     }
  226.  
  227.     # Apply Spans, if any:
  228.     my $spans;
  229.     if( defined( $detailData->{spans})){
  230.         $spans = $detailData->{spans};
  231.         
  232.         my $spanSize = scalar(@$spans);
  233.         #Error Checking, spans array should be a multiple of 2
  234.         if( ($spanSize % 2) < 1){
  235.         
  236.             my $startRow = $row+1;
  237.             my $noRows = @$detailArray;
  238.             my $stopRow = scalar(@$detailArray) + $startRow - 1;
  239.             foreach my $spanRow($startRow..$stopRow){
  240.                 # build an array to feed to spans, change column number for row.col index
  241.                 #   (every 2rd item in the array).
  242.                 my @spanArray = map $_ % 2 ? $spans->[$_] : "$spanRow,".$spans->[$_], (0..($spanSize-1));
  243.                 $self->spans(@spanArray);
  244.             }
  245.             
  246.         }else{
  247.             warn("Spans array for row $row, is not a multiple of 2\n");
  248.         }
  249.             
  250.     }
  251.  
  252.     
  253.     
  254.     
  255.     # Now Update the internal arrays for the inserted rows ###
  256.     my %expandDataNew;
  257.     foreach my $rowIndex(keys %$expandData){
  258.         if($rowIndex > $row){ # adjust rows greater than the current row
  259.             $expandDataNew{$rowIndex+$noRows} = $expandData->{$rowIndex};
  260.         }
  261.         else{
  262.             $expandDataNew{$rowIndex} = $expandData->{$rowIndex};
  263.         }
  264.     }
  265.     # Copy new to existing:
  266.     %$expandData = %expandDataNew;
  267.     
  268.     
  269.     my %indRowColsNew;
  270.     foreach my $rcindex(keys %$indRowCols){
  271.         
  272.         my ($rowIndex,$colIndex) = split(',',$rcindex);
  273.         if($rowIndex > $row){ # adjust rows greater than the current row
  274.             my $newRow = $rowIndex+$noRows;
  275.             $indRowColsNew{"$newRow,$colIndex"} = $indRowCols->{$rcindex};
  276.         }
  277.         else{
  278.             $indRowColsNew{$rcindex} = $indRowCols->{$rcindex};
  279.         }
  280.     }
  281.     # Copy new to existing:
  282.     %$indRowCols = %indRowColsNew;
  283.     
  284.     # Take care of any lower-level detail data:
  285.     my $subDetail;
  286.     if( defined( $detailData->{expandData})){
  287.         $subDetail = $detailData->{expandData};
  288.         
  289.         foreach my $subRow( keys %$subDetail){
  290.             
  291.             my $realRow = $row+$subRow;
  292.             my $index = "$realRow,$selectorCol";
  293.             $self->tagCell('plus', $index);
  294.             $indRowCols->{$index} = '+'; # update internal array
  295.             
  296.             # put subdetail data to top level, adjusting the relative row
  297.             # numbers to real row numbers:
  298.             #my %adjustedSubDetail;
  299.             #foreach my $subKey(keys %$subDetail){
  300.             #    $adjustedSubDetail{$subKey+$row} = $subDetail->{$subKey};
  301.             #}
  302.             $expandData->{$realRow} = $subDetail->{$subRow}; 
  303.         }
  304.  
  305.     }
  306.  
  307.     
  308.     
  309. }
  310.  
  311. =head2 hideDetail
  312.  
  313. Hides the detail data for a given row. This method is called
  314. when a user clicks on an indicator that is already expanded.
  315.  
  316. B<Usage:>
  317.  
  318.  $widget->hideDetail($row);  
  319.  
  320.  # Hides the detail data for row number $row
  321.  
  322. =cut
  323.  
  324. sub hideDetail{
  325.  
  326.     my $self = shift;
  327.     
  328.     my $row = shift;
  329.     my $expandData = shift;
  330.     my $detailData = $expandData->{$row};
  331.  
  332.     my $selectorCol = $self->cget(-selectorCol);
  333.     
  334.     my $index = "$row,$selectorCol"; # make index for the cell to be hidden
  335.     
  336.     my $indRowCols = $self->{indRowCols};
  337.  
  338.     # hide any sublevel data first:
  339.     my $lowerLevelHideRows = 0;
  340.     if( defined( $detailData->{expandData})){ # sublevel data exists
  341.         my $subLevelData = $detailData->{expandData};
  342.         # convert sublevel data to absolute rows
  343.         my $convertedSubData = {};
  344.         foreach my $rowNum(keys %$subLevelData){
  345.             $convertedSubData->{$rowNum+$row} = $subLevelData->{$rowNum};
  346.         }
  347.         #Hide lower level data, if showing
  348.         my $subLevelIndex;
  349.         foreach my $rowNum (sort {$a<=>$b} keys %$convertedSubData){
  350.             $subLevelIndex = "$rowNum,$selectorCol";
  351.             if( $indRowCols->{$subLevelIndex} eq '-'){
  352.                 $lowerLevelHideRows += $self->hideDetail($rowNum,$convertedSubData);
  353.             }
  354.         }
  355.     }
  356.                 
  357.     
  358.     $self->tagCell('plus', $index);
  359.     $indRowCols->{$index} = '+';
  360.     
  361.     
  362.     # Get the detail data and hide:
  363.     my $detailArray = $detailData->{data};
  364.     
  365.     my $noRows = scalar( @$detailArray);
  366.  
  367.     # unapply any spans (This is not auto-handled by the row delete command, so we
  368.     #  have to do it here manually)
  369.     my $spans;
  370.     if( defined( $detailData->{spans})){
  371.         $spans = $detailData->{spans};
  372.         
  373.         my $spanSize = scalar(@$spans);
  374.         #Error Checking, spans array should be a multiple of 2
  375.         if( ($spanSize % 2) < 1){
  376.         
  377.             my $startRow = $row+1;
  378.             my $noRows = @$detailArray;
  379.             my $stopRow = scalar(@$detailArray) + $startRow - 1;
  380.             foreach my $spanRow($startRow..$stopRow){
  381.                 # build an array to feed to spans, change column number for row.col index
  382.                 #   (every 2rd item in the array).
  383.                 my @spanArray = map $_ % 2 ? '0,0' : "$spanRow,".$spans->[$_], (0..($spanSize-1));
  384.                 $self->spans(@spanArray);
  385.             }
  386.             
  387.         }else{
  388.             warn("Spans array for row $row, is not a multiple of 2\n");
  389.         }
  390.             
  391.     }
  392.  
  393.  
  394.     # Move Any existing spans that are at rows > $row+$noRows to where the should be, now that rows
  395.     #  have been deleted
  396.     $self->adjustSpans($row+$noRows,-$noRows);
  397.  
  398.     # deleteRows:
  399.     $self->deleteRows($row+1,$noRows);
  400.  
  401.     my %indRowColsNew;
  402.     foreach my $rcindex(keys %$indRowCols){
  403.         
  404.         my ($rowIndex,$colIndex) = split(',',$rcindex);
  405.         if($rowIndex > $row){ # adjust rows greater than the current row
  406.             my $newRow = $rowIndex-$noRows;
  407.             $indRowColsNew{"$newRow,$colIndex"} = $indRowCols->{$rcindex};
  408.         }
  409.         else{
  410.             $indRowColsNew{$rcindex} = $indRowCols->{$rcindex};
  411.         }
  412.     }
  413.     # Copy new to existing:
  414.     %$indRowCols = %indRowColsNew;    
  415.     
  416.     
  417.     $noRows += $lowerLevelHideRows; # Include the lower level detail rows hidden in the internall array update
  418.  
  419.  
  420.     # Now Update the internal arrays for the deleted rows ###
  421.     my %expandDataNew;
  422.     foreach my $rowIndex(keys %$expandData){
  423.         if($rowIndex > ($row+$noRows)){ # adjust rows greater than the current row + detail data
  424.             $expandDataNew{$rowIndex-$noRows} = $expandData->{$rowIndex};
  425.         }
  426.         elsif($rowIndex<= $row){ # rows less than or equal just get copied
  427.             $expandDataNew{$rowIndex} = $expandData->{$rowIndex};
  428.         }
  429.         #else nothing, expand data that is in the detail data that is being hidden doesn't get copied
  430.     }
  431.     # Copy new to existing:
  432.     %$expandData = %expandDataNew;
  433.         
  434.     return $noRows;
  435.  
  436.  
  437. }
  438.  
  439. #----------------------------------------------
  440. # Sub called when -expandData option changes
  441. #
  442. sub expandData{
  443.     my ($self, $expandData) = @_;
  444.  
  445.  
  446.  
  447.     if(! defined($expandData)){ # Handle case where $widget->cget(-expandData) is called
  448.  
  449.         return $self->{Configure}{-expandData}
  450.                 
  451.     }
  452.  
  453.     $self->clearSelectors;
  454.  
  455.     my $selectorCol = $self->cget(-selectorCol);
  456.     
  457.     # Create internal copy of expand Data for us to mess with
  458.     my $expandData_int = {};
  459.     %$expandData_int = %$expandData;
  460.     $self->{'_expandData'} = $expandData_int;
  461.     
  462.     # update the indRowCols quick lookup hash:
  463.     $self->updateIndRowCols($expandData, $selectorCol);
  464.     
  465.     $self->setSelectors;
  466.  
  467.     
  468. }
  469.  
  470.  
  471.  
  472.  
  473. #----------------------------------------------
  474. # Sub called when -selectorCol option changes
  475. #
  476. sub selectorCol{
  477.     my ($self, $selectorCol) = @_;
  478.  
  479.  
  480.  
  481.     if(! defined($selectorCol)){ # Handle case where $widget->cget(-selectorCol) is called
  482.         #
  483.         # Set default if not defined yet
  484.         my $selCol;
  485.         unless( defined($self->{Configure}{-selectorCol})){
  486.             $selCol = $self->{Configure}{-selectorCol} = 0;
  487.         }
  488.         else{
  489.             $selCol = $self->{Configure}{-selectorCol};
  490.         }
  491.         
  492.         return $selCol;
  493.                 
  494.     }
  495.  
  496.     ###### Get Old Selector Col and undo Here ?????###
  497.     $self->clearSelectors;
  498.     
  499.     my $expandData = $self->cget('-expandData');
  500.     
  501.     # update the indRowCols quick lookup hash:
  502.     $self->updateIndRowCols($expandData, $selectorCol);
  503.     
  504.     $self->setSelectors;
  505.     
  506. }
  507.  
  508. # Method used to clear the selectors defined in the current indRowCols hash
  509. sub setSelectors{
  510.     my $self = shift;
  511.     
  512.     my $indRowCols = $self->{indRowCols};
  513.     
  514.     my @pluses = grep $indRowCols->{$_} eq '+', keys %$indRowCols;
  515.     my @minuses = grep $indRowCols->{$_} eq '-', keys %$indRowCols;
  516.     
  517.     $self->tagCell('plus', @pluses);
  518.     $self->tagCell('minus', @minuses);
  519.     
  520.     my $selectorCol = $self->cget('-selectorCol');
  521.     my $selectorColWidth = $self->cget(-selectorColWidth)  || 2; # set to '2' (the default), incase this called before the defaults have been set
  522.     $self->colWidth($selectorCol, $selectorColWidth);
  523.     
  524. }
  525.  
  526.  
  527.  
  528.  
  529. # Method used to clear the selectors defined in the current indRowCols hash
  530. sub clearSelectors{
  531.     my $self = shift;
  532.     
  533.     my @indRowCols = keys %{$self->{indRowCols}};
  534.     if( @indRowCols){
  535.         $self->tagCell('', keys %{$self->{indRowCols}});
  536.         
  537.         # Get selectorCol from first entry
  538.         my ($row,$col) = split(',',$indRowCols[0]);
  539.         $self->colWidth($col, 'default');
  540.     }
  541.     
  542. }
  543.         
  544.  
  545. ### Method to update indRowCols, based on the expandData and selectorCol
  546. sub updateIndRowCols{
  547.  
  548.     my $self = shift;
  549.     
  550.     my($expandData, $selectorCol) = @_;
  551.     
  552.     my $indRowCols = {};
  553.     
  554.     foreach (keys %$expandData){
  555.         $indRowCols->{"$_,$selectorCol"} = '+';
  556.     }
  557.     
  558.     $self->{indRowCols} = $indRowCols;
  559.     return $indRowCols;
  560.     
  561. }
  562.     
  563. # General Motion routine. Calls cellEnter if the pointer has entered another
  564. #  cell.
  565.  
  566. sub GeneralMotion{
  567.  
  568.     my $self  = shift;
  569.     my $Ev = $self->XEvent;
  570.  
  571.     my $rc = $self->index('@' . $Ev->x.",".$Ev->y);
  572.  
  573.     $self->SUPER::GeneralMotion;
  574.     
  575.     my ($row,$col) = split(',',$rc);
  576.  
  577.     my @border = $self->border('mark',$Ev->x,$Ev->y);
  578.     if( scalar(@border) == 0 &&  (!($self->{lastrc}) || $rc ne $self->{lastrc})){ # call cellEnter if cell number has changed and we aren't on a border
  579.         $self->{lastrc} = $rc;
  580.         $self->cellEnter($row,$col);
  581.     }
  582.     
  583.  
  584.         
  585. }
  586.  
  587. # Method called with the pointer goes over a different cell
  588. #  Sets the cursor to a top-right arrow if over
  589. #  the selectorCol
  590.  
  591. sub cellEnter{
  592.  
  593.     my $self  = shift;
  594.     my ($row,$col) = @_;
  595.  
  596.     #print "Entered '$row,$col'\n";
  597.     
  598.     
  599.     my $rowColResizeDrag = $self->{rowColResizeDrag};  # Flag = 1 if cursor has been changed for a row/col resize
  600.     
  601.     unless($rowColResizeDrag){
  602.     
  603.         my $indRowCols = $self->{indRowCols};
  604.         
  605.         if( defined( $indRowCols->{"$row,$col"})){ 
  606.             #print "Setting ind cursor\n";
  607.             $self->configure(-cursor => 'top_left_arrow');
  608.         }
  609.         else{
  610.             #print "Setting old cursor back '".$self->{normalCursor}."'\n";
  611.             $self->configure(-cursor => $self->{normalCursor});
  612.         }
  613.     }
  614.  
  615.         
  616. }
  617.  
  618.         
  619. #############################################################
  620. ## Over-ridden beginselect. Epands cell if +/- cell selected
  621. sub BeginSelect{
  622.     my $self  = shift;
  623.     my $rc = shift;
  624.     
  625.     my $indRowCols = $self->{indRowCols}; # get quick lookup hash
  626.     my $state;
  627.     if( defined($indRowCols->{$rc})) {
  628.         $state = $indRowCols->{$rc};
  629.         my ($row,$col) = split(',',$rc);
  630.         if( $state eq '-'){
  631.             $self->hideDetail($row, $self->{'_expandData'});
  632.         }
  633.         else{
  634.             $self->showDetail($row);
  635.         }
  636.  
  637.         return;
  638.     }
  639.     
  640.     # print "Calling inherited BeginSelect\n";
  641.     $self->SUPER::BeginSelect($rc);
  642.     
  643. }
  644.  
  645.  
  646. #------------------- 
  647. #  Method Called to adjust spans starting at  $row by $noRows
  648. #
  649. #   If noRows is greater than 0 then the spans are adjusted up by $noRows
  650. #   If noRows is negative, then spans are adjusted down by $noRows
  651. #
  652. #  This method is needed becase the rowinsert/delete methods of TableMatrix don't
  653. #  automatically adjust the spans 
  654. sub adjustSpans{
  655.     
  656.     my $self = shift;
  657.     my ($row,$noRows) = @_;
  658.     
  659.     my %spans = $self->spans; # Get All Spans
  660.     my %spansFilterd; # filtered for row > $row
  661.     my $minRowFiltered = $row;
  662.     my @filteredIndexes = grep  { my ($r,$c) = split(',',$_); $r >= $minRowFiltered}    keys %spans;
  663.     my %unapplySpans; # temp hash used to unapply spans:
  664.     @unapplySpans{@filteredIndexes} = map '0,0', @filteredIndexes;
  665.     $self->spans(%unapplySpans);  # unapply the spans the filtered spans:
  666.     my %adjustedSpans;
  667.     foreach (@filteredIndexes){
  668.         my ($r,$c) = split(',',$_);
  669.         $adjustedSpans{($r+$noRows).",$c"} = $spans{$_};
  670.     }
  671.     
  672.     # Apply adjusted Spans:
  673.     $self->spans(%adjustedSpans);
  674.     
  675. }
  676.  
  677. 1;
  678.  
  679.