Diff for /loncom/xml/lontable.pm between versions 1.16 and 1.22

version 1.16, 2011/04/05 10:02:58 version 1.22, 2014/12/15 00:52:40
Line 108  modified by this.  These configuration i Line 108  modified by this.  These configuration i
   
   my $table = lontable::new(\%config_hash)    my $table = lontable::new(\%config_hash)
   
 =over3  =over 3
   
   
 =item alignment  =item alignment
Line 211  of the cells in the row. Line 211  of the cells in the row.
   
 =item cells  =item cells
   
   
 Array of hashes where each element represents the data for a cell.  Array of hashes where each element represents the data for a cell.
 The contents of each element of this hash are described below:  The contents of each element of this hash are described below:
   
 =over 3  
   
 =item header  =item header
   
 If present, the row is a 'header' that is it was made via the  If present, the row is a 'header' that is it was made via the
Line 248  The contents of the cell. Line 247  The contents of the cell.
   
 =back  =back
   
 =back  
   
 =cut  =cut
   
 sub new {  sub new {
     my ($class, $configuration) = @_;      my ($class, $configuration) = @_;
   
       if ($tracing) {&Apache::lonnet::logthis("new table"); }
     #  Initialize the object member data with the default values      #  Initialize the object member data with the default values
     #  then override with any stuff in $configuration.      #  then override with any stuff in $configuration.
   
Line 267  sub new { Line 265  sub new {
  theme          => "plain",   theme          => "plain",
  column_count   => 0,   column_count   => 0,
  row_open       => 0,   row_open       => 0,
  rows           => [],   rows           => {
  col_widths      => {}      'body'     => [],
               'head'     => [],
       'foot'     => []
    },
    col_widths      => {},
    part           => 'body',     # one of 'body', 'head', 'foot'.
    colgroups      => []      # Stores information about column groups.
   
     };      };
   
     foreach my $key (keys %$configuration) {      foreach my $key (keys(%$configuration)) {
  $self->{$key} = $$configuration{$key};   $self->{$key} = $$configuration{$key};
     }      }
   
Line 359  Valid values for the parameter are: Line 364  Valid values for the parameter are:
   
 =item 3 - Border at the left and right sides of the cell.  =item 3 - Border at the left and right sides of the cell.
   
   =item 4 - Border around groups (colgroups as well as thead/tfoot/tbody).
   
 =over -2   
   =back
   
 =head3 Examples:  =head3 Examples:
   
Line 430  sub theme { Line 437  sub theme {
   
 =pod  =pod
   
 =head 2 width  =head2 width
   
 Gets and optionally sets the width of the table.  Gets and optionally sets the width of the table.
   
 =head 3 Examples:  =head3 Examples:
   
  my $newwidth = $table->width("10cm");   # 10cm width returns "10cm".   my $newwidth = $table->width("10cm");   # 10cm width returns "10cm".
   
Line 493  sub start_row { Line 500  sub start_row {
     # Override the defaults if the config hash is present:      # Override the defaults if the config hash is present:
   
     if (defined($config)) {      if (defined($config)) {
  foreach my $key  (keys %$config) {   foreach my $key  (keys(%$config)) {
     $row_hash->{$key} = $config->{$key};      $row_hash->{$key} = $config->{$key};
  }   }
     }      }
   
           
     my $rows = $self->{'rows'};      my $rows = $self->{'rows'}->{$self->{'part'}};
     push(@$rows, $row_hash);      push(@$rows, $row_hash);
   
     $self->{"row_open"} = 1; # Row is now open and ready for business.      $self->{"row_open"} = 1; # Row is now open and ready for business.
Line 525  sub end_row { Line 532  sub end_row {
   
  # Mostly we need to determine if this row has the maximum   # Mostly we need to determine if this row has the maximum
  # cell count of any row in existence in the table:   # cell count of any row in existence in the table:
   
   
  my $row        = $self->{'rows'}->[-1];   my $row        = $self->{'rows'}->{$self->{'part'}}->[-1];
  my $cells      = $row->{'cells'};   my $cells      = $row->{'cells'};
   
  if ($row->{'cell_width'} > $self->{'column_count'}) {   if ($row->{'cell_width'} > $self->{'column_count'}) {
Line 574  sub configure_row { Line 582  sub configure_row {
  $self->start_row();   $self->start_row();
     }      }
           
     my $row = $self->{'rows'}[-1];      my $row = $self->{'rows'}->{$self->{'part'}}->[-1];
     foreach my $config_item (keys %$config) {      foreach my $config_item (keys(%$config)) {
  $row->{$config_item} = $config->{$config_item};   $row->{$config_item} = $config->{$config_item};
     }      }
 }  }
Line 629  Note as well that if width specification Line 637  Note as well that if width specification
   
 =back  =back
   
   =back 
   
 =cut  =cut
   
 sub add_cell {  sub add_cell {
Line 641  sub add_cell { Line 651  sub add_cell {
     if (!$self->{'row_open'}) {      if (!$self->{'row_open'}) {
  $self->start_row();   $self->start_row();
     }      }
     my $rows          = $self->{'rows'};      my $rows          = $self->{'rows'}->{$self->{'part'}};
     my $current_row   = $rows->[-1];      my $current_row   = $rows->[-1];
     my $current_cells = $current_row->{'cells'};       my $current_cells = $current_row->{'cells'}; 
     my $last_coord    = $current_row->{'cell_width'};      my $last_coord    = $current_row->{'cell_width'};
Line 754  sub append_cell_text { Line 764  sub append_cell_text {
     my ($this, $text) = @_;      my ($this, $text) = @_;
   
     if($tracing) {&Apache::lonnet::logthis("append_cell_text: $text"); }      if($tracing) {&Apache::lonnet::logthis("append_cell_text: $text"); }
     my $rows         = $this->{'rows'};      my $rows         = $this->{'rows'}->{$this->{'part'}};
     my $current_row  = $rows->[-1];      my $current_row  = $rows->[-1];
     my $cells        = $current_row->{'cells'};      my $cells        = $current_row->{'cells'};
     my $current_cell = $cells->[-1];      my $current_cell = $cells->[-1];
     $current_cell->{'contents'} .= $text;      $current_cell->{'contents'} .= $text;
           
 }  }
   #-------------------------- Support for row/column groups.   ----
   
   =pod 
   
   =head2 start_head 
   
   starts the table head.  This corresponds to the <thead> tag in 
   html/xml.  All rows defined in this group will be
   collected and placed at the front of the table come rendering time.
   Furthermore, if the table has group borders enabled, a rule will be
   rendered following and preceding this group of rows.
   
   =cut
   
   sub start_head {
       my ($this) = @_;
       if ($tracing) { &Apache::lonnet::logthis("start_head"); }
       $this->{'part'}  = 'head';
   }
   
   =pod     
   
   =head2 end_head   
   
   Ends a table head.  This corresponds to the
   </thead> closing tag in html/xml.
   
   =cut
   
   sub end_head {
       my ($this) = @_;
       if ($tracing) { &Apache::lonnet::logthis("end_head"); }
       $this->{'part'} = 'body';
   }
   
   =pod
   
   =head2 start_foot
   
   Starts the table footer.  All of the rows generated in the footer will
   be rendered at the bottom of the table.  This sub corresponds to the
   <tfoot> tag in html/xml.  If the table has group borders enabled, a rule
   will be rendered at the top and bottom of the set of columns in this
   group
   
   =cut
   
   sub start_foot {
       my ($this) = @_;
       if ($tracing) { &Apache::lonnet::logthis("start_foot"); }
       $this->{'part'}   = 'foot';
   }
   
   =pod
   
   =head2 end_foot
   
   Ends the set of rows in the table footer.  This corresponds to the
   </tfoot> end tag in xml/html.
   
   =cut
   
   sub end_foot {
       my ($this) = @_;
       if ($tracing) { &Apache::lonnet::logthis("end_foot") }
       $this->{'part'}  = 'body';
   }
   
   =pod
   
   =head2 start_body
   
   Starts the set of rows that will be in the table body.   Note that if
   we are not in the header or footer, body rows are implied.
   This correspondes to the presence of a <tbody> tag in html/xml.
   If group borders are on, a rule will be rendered at the top and bottom
   of the body rows.
   
   =cut
   
   sub start_body {
       my ($this) = @_;
       if ($tracing) { &Apache::lonnet::logthis("start_body"); }
       $this->{'part'}  = 'body';
   }
   
   =pod
    
   =head2 end_body
   
   Ends the set of rows in a table body.  Note that in the event we are not
   in  the header or footer groups this code assumes we are in the body
   group.  I believe this is a good match to how mot browsers render.
   
   =cut
   
   sub end_body {
       my ($this) = @_;
       if ($tracing) { &Apache::lonnet::logthis("end_body"); }
   
   }
   
   =pod
   
   =head2 define_colgroup
   
   Define a column group  a column group corresponds to the
   <cgroup> tag in Html/Xml. A column group as we implement it has
   the following properties tht will be shared amongst all cells in the
   columns in the group unless overidden in the specific oell definition:
   
   =over 2
   
   =item span 
   
   The number of columns in the column group.  This defaults to 1.
   
   =item halign
   
   Horizontal alignment of the cells.  This defaults to left.
   Other values are left, center, right (justify and char are 
   accepted but treated as left).
     
   =item valign
   
   Vertical alignment of the cells.  This defaults to middle.
   Other values are top middle, bottom, (baseline is accepted and
   treated as top).
   
   =back   
   
   If group borders are turned on, a rule will be rendered
   at the left and right side of the column group.
   
   =head3 parameters
   
   =over 2
   
   =item definition
   
   This is a hash that contains any of the keys described above that
   define the column group.
   
   =back
   
   
   =head3 Example
   
    $table->define_colgroup({
       'span'    => 2,
       'halign'  => 'center'
                            })
   
   
   
   =cut
   
   sub define_colgroup {
       my ($this, $attributes)  = @_;
       if ($tracing) { &Apache::lonnet::logthis("col_group"); }
       my $colgroups = $this->{'colgroups'};
       push(@$colgroups, $attributes); # Colgroups always add at end.
   
   
   }
   
   #------------------------- Render the table ---------------------
   
 =pod  =pod
   
Line 774  a certain amount of testing to be done o Line 950  a certain amount of testing to be done o
 The caller can then ask the table object to generate LaTeX.  The caller can then ask the table object to generate LaTeX.
   
 =cut  =cut
   
 sub generate {  sub generate {
     my ($this) = @_;      my ($this) = @_;
     my $useP   = 0;      my $useP   = 0;
Line 784  sub generate { Line 961  sub generate {
     if($tracing) {&Apache::lonnet::logthis("generate"); }      if($tracing) {&Apache::lonnet::logthis("generate"); }
     my $table = Apache::lonlatextable->new();      my $table = Apache::lonlatextable->new();
   
       my $inner_border = $this->{'inner_border'};
       my $outer_border = $this->{'outer_border'};
       my $column_count = $this->{'column_count'};
   
     # Add the caption if supplied.      my $cell_ul_border = (($inner_border == 1) || ($inner_border == 2)) ? 1 : 0;
       my $cell_lr_border = (($inner_border == 1) || ($inner_border == 3)) ? 1 : 0;
       my $part_border   = ($inner_border == 4);
    
    
         # Add the caption if supplied.
   
     if ($this->{'caption'} ne "") {      if ($this->{'caption'} ne "") {
  $table->set_caption($this->caption);   $table->set_caption($this->caption);
Line 811  sub generate { Line 996  sub generate {
   
  my $specified_width = 0.0;   my $specified_width = 0.0;
  my $specified_cols   = 0;   my $specified_cols   = 0;
  foreach my $col (keys %$colwidths) {   foreach my $col (keys(%$colwidths)) {
     $specified_width = $specified_width + $colwidths->{$col};      $specified_width = $specified_width + $colwidths->{$col};
     $specified_cols++;      $specified_cols++;
  }   }
Line 841  sub generate { Line 1026  sub generate {
     $default_width = ($tablewidth/$unspecified_cols);                     # 'fair' default width.      $default_width = ($tablewidth/$unspecified_cols);                     # 'fair' default width.
     my $width_remaining = $tablewidth - $default_width*$unspecified_cols; # What's left for the specified cols.      my $width_remaining = $tablewidth - $default_width*$unspecified_cols; # What's left for the specified cols.
     my $reduction       = $tablewidth/$width_remaining;                    # Reduction fraction for specified cols      my $reduction       = $tablewidth/$width_remaining;                    # Reduction fraction for specified cols
     foreach my $col (keys %$colwidths) {      foreach my $col (keys(%$colwidths)) {
  $colwidths->{$col} = $colwidths->{$col}/$reduction;   $colwidths->{$col} = $colwidths->{$col}/$reduction;
     }      }
           
         }          }
     }      }
       # If rule is groups. we need to have a 
       # list of the column numbers at which a column ends...
       # and the coldef needs to start with a |
       #
       my @colgroup_ends;
       my $colgroup_col = 0;
       my $group = 0;
       my $coldef = "";
       if ($outer_border || $cell_lr_border) {
    $coldef .= '|';
       }
       if ($part_border) {
    $coldef .= '|';
    my $colgroup_col = 0;
    my $colgroups = $this->{'colgroups'};
    foreach my $group (@$colgroups) {
       if (defined $group->{'span'}) {
    $colgroup_col += $group->{'span'};
       } else {
    $colgroup_col++;
       }
       push(@colgroup_ends, $colgroup_col);
    }
     
       }
       $this->render_part('head', $table, $useP, $default_width, 
          \@colgroup_ends);
       $this->render_part('body', $table, $useP, $default_width,
    \@colgroup_ends);
       $this->render_part('foot', $table, $useP, $default_width,
    \@colgroup_ends);
   
   
   
   
   
       
       for (my $i =0; $i < $column_count; $i++) {
    if ($useP) {
       $coldef .= "p{$default_width $colunits}";
    } else {
       $coldef .= 'l';
    }
    if ($cell_lr_border || 
       ($outer_border && ($i == $column_count-1))) {
       $coldef .= '|';
    }
    if ($part_border && ($i == ($colgroup_ends[$group]-1)))  {
       $coldef .= '|';
       $group++;
    }
       }
       $table->{'coldef'} = $coldef;
   
       # Return the table:
   
       if ($tracing) { &Apache::lonnet::logthis("Leaving generate"); }
   
   
       return $table;
   
   }
   
   
   #---------------------------------------------------------------------------
   #
   #  Private methods:
   #
   
   # 
   # Convert size with units -> size in cm.
   # The resulting size is floating point with no  units so that it can be used in
   # computation.  Note that an illegal or missing unit is treated silently as
   #  cm for now.
   #
   sub size_to_cm {
       my ($this, $size_spec) = @_;
       my ($size, $units) = split(/ /, $size_spec);
       if (lc($units) eq 'mm') {
    return $size / 10.0;
       }
       if (lc($units) eq 'in') {
    return $size * 2.54;
       }
       
       return $size; # Default is cm.
   }
   
   #
   #  Render a part of the table.  The valid table parts are
   #  head, body and foot.  These corresopnd to the set of rows
   #  define within <thead></thead>, <tbody></tbody> and <tfoot></tfoot>
   #  respectively.
   #
   sub render_part {
       my ($this, $part, $table, $useP,
    $default_width, $colgroup_ends) = @_;
   
       if ($tracing) { &Apache::lonnet::logthis("render_part: $part") };
   
       # Do nothing if that part of the table is empty:
   
       if ($this->{'rows'}->{$part} == undef) {
    if ($tracing) {&Apache::lonnet::logthis("$part is empty"); }
    return;
       }
   
       my @cgends = @$colgroup_ends;
     # Build up the data:      # Build up the data:
   
     my @data;      my @data;
     my $rows      = $this->{'rows'};      my $colwidths        = $this->{'col_widths'};
       my $rows      = $this->{'rows'}->{$part}; 
     my $row_count = scalar(@$rows);      my $row_count = scalar(@$rows);
     my $inner_border = $this->{'inner_border'};      my $inner_border = $this->{'inner_border'};
     my $outer_border = $this->{'outer_border'};      my $outer_border = $this->{'outer_border'};
Line 862  sub generate { Line 1151  sub generate {
   
     my $cell_ul_border = (($inner_border == 1) || ($inner_border == 2)) ? 1 : 0;      my $cell_ul_border = (($inner_border == 1) || ($inner_border == 2)) ? 1 : 0;
     my $cell_lr_border = (($inner_border == 1) || ($inner_border == 3)) ? 1 : 0;      my $cell_lr_border = (($inner_border == 1) || ($inner_border == 3)) ? 1 : 0;
        my $part_border   = ($inner_border == 4);
       my $colunits    = 'cm'; # All units in cm.
   
     # Add a top line if the outer or inner border is enabled:      # Add a top line if the outer or inner border is enabled:
       # or if group rules are on.
       #
   
     if ($outer_border || $cell_ul_border) {      if ($outer_border || $cell_ul_border || $part_border) {
  push(@data, ["\\cline{1-$column_count}"]);        push(@data, ["\\cline{1-$column_count}"]);     
   
     }      }
Line 878  sub generate { Line 1171  sub generate {
  my $startcol   = 1;   my $startcol   = 1;
  my @underlines; # Array of \cline cells if cellborder on.   my @underlines; # Array of \cline cells if cellborder on.
   
    my $colgroup_count = @cgends; # Number of column groups.
    my $cgroup         = 0;     # Group we are on.
    my $cgstart        = 0;     # Where the next cgroup starts.
   
  for (my $cell  = 0; $cell < $cell_count; $cell++) {   for (my $cell  = 0; $cell < $cell_count; $cell++) {
     my $contents = $cells->[$cell]->{'contents'};      my $contents = $cells->[$cell]->{'contents'};
       
     #      #
     #  Cell alignment is the default alignment unless      #  Cell alignment is the default alignment unless
     #  explicitly specified in the cell.      #  explicitly specified in the cell.
Line 931  sub generate { Line 1226  sub generate {
     if ($cell_lr_border || ($outer_border && ($cell == ($cell_count -1)))) {      if ($cell_lr_border || ($outer_border && ($cell == ($cell_count -1)))) {
  $col_align = $col_align.'|';   $col_align = $col_align.'|';
     }      }
       if ($part_border)  {
    if ($cell == $cgstart) {
       $col_align = '|' . $col_align;
       if ($cgroup < $colgroup_count) {
    $cgstart = $cgends[$cgroup];
    $cgroup++;
       } else {
    $cgstart = 1000000; # TODO: Get this logic right
       }
       if ($cell == ($cell_count - 1) &&
    ($cell == ($cgstart-1))) {
    $col_align = $col_align . '|'; # last col ends colgrp.
       }
    }
       }
   
     #factor in spans:      #factor in spans:
   
     my $cspan    = $cells->[$cell]->{'colspan'};      my $cspan    = $cells->[$cell]->{'colspan'};
     my $nextcol  = $startcol + $cspan;      my $nextcol  = $startcol + $cspan;
       
       # At this point this col is the start of the span.
       # nextcol is the end of the span.
   
     # If we can avoid the \multicolumn directive that's best as      # If we can avoid the \multicolumn directive that's best as
     # that makes some things like \parpic invalid in LaTeX which      # that makes some things like \parpic invalid in LaTeX which
Line 957  sub generate { Line 1270  sub generate {
  push(@underlines, "\\cline{$startcol-$lastcol}");   push(@underlines, "\\cline{$startcol-$lastcol}");
     }      }
     $startcol = $nextcol;      $startcol = $nextcol;
   
     # Rowspans should take care of themselves.      # Rowspans should take care of themselves.
           
     push(@row, $contents);      push(@row, $contents);
Line 974  sub generate { Line 1288  sub generate {
     # Add bottom border if necessary: if the inner border was on, the loops above      # Add bottom border if necessary: if the inner border was on, the loops above
     # will have done a bottom line under the last cell.      # will have done a bottom line under the last cell.
     #      #
     if ($outer_border && !$cell_ul_border) {      if (($outer_border || $part_border) && !$cell_ul_border) {
  push(@data, ["\\cline{1-$column_count}"]);        push(@data, ["\\cline{1-$column_count}"]);     
   
     }      }
     $table->set_data(\@data);      $table->set_data(\@data);    
       
     my $coldef = "";  
     if ($outer_border || $cell_lr_border) {  
  $coldef .= '|';  
     }  
     for (my $i =0; $i < $column_count; $i++) {  
  if ($useP) {  
     $coldef .= "p{$default_width $colunits}";  
  } else {  
     $coldef .= 'l';  
  }  
  if ($cell_lr_border ||   
     ($outer_border && ($i == $column_count-1))) {  
     $coldef .= '|';  
  }  
     }  
     $table->{'coldef'} = $coldef;  
   
     # Return the table:  
   
     if ($tracing) { &Apache::lonnet::logthis("Leaving generate"); }  
   
   
     return $table;  
   
 }  }
 #---------------------------------------------------------------------------  
 #  
 #  Private methods:  
 #  
   
 #   
 # Convert size with units -> size in cm.  
 # The resulting size is floating point with no  units so that it can be used in  
 # computation.  Note that an illegal or missing unit is treated silently as  
 #  cm for now.  
 #  
 sub size_to_cm {  
     my ($this, $size_spec) = @_;  
     my ($size, $units) = split(/ /, $size_spec);  
     if (lc($units) eq 'mm') {  
  return $size / 10.0;  
     }  
     if (lc($units) eq 'in') {  
  return $size * 2.54;  
     }  
       
     return $size; # Default is cm.  
 }  
 #----------------------------------------------------------------------------  #----------------------------------------------------------------------------
 # The following methods allow for testability.  # The following methods allow for testability.
   
Line 1042  sub get_row { Line 1309  sub get_row {
     my ($self, $row) = @_;      my ($self, $row) = @_;
     if ($tracing > 1) { &Apache::lonnet::logthis("get_row"); }      if ($tracing > 1) { &Apache::lonnet::logthis("get_row"); }
   
     my $rows = $self->{'rows'};  # ref to an array....      my $rows = $self->{'rows'}->{$self->{'part'}};  # ref to an array....
     return $rows->[$row];         # ref to the row hash for the selected row.      return $rows->[$row];         # ref to the row hash for the selected row.
 }  }
   
Line 1052  BEGIN{ Line 1319  BEGIN{
   
 1;  1;
 __END__  __END__
    

Removed from v.1.16  
changed lines
  Added in v.1.22


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>