Diff for /loncom/xml/lontable.pm between versions 1.1 and 1.7

version 1.1, 2008/11/24 11:56:53 version 1.7, 2008/12/29 11:57:37
Line 39 Line 39
   
 # This module is a support packkage that helps londefdef generate  # This module is a support packkage that helps londefdef generate
 # LaTeX tables using the LaTeX::Table package.  A prerequisite is that  # LaTeX tables using the LaTeX::Table package.  A prerequisite is that
 # the print generator must have added the following to the LaTeX header:  # the print generator must have added the following to the LaTeX 
 #  #
 #  \usepackage{xtab}  #  \usepackage{xtab}
 #  \usepackage{booktabs}  #  \usepackage{booktabs}
Line 77  Opens a new table row. Line 77  Opens a new table row.
   
 Closes a table row.  Closes a table row.
   
 =item start_header  
   
 Starts a new row that has the header attribute (e.g. <th> tagged row).  
 header rows are ended with an end_row just like any ordinary row.  
   
 =item configure_row  =item configure_row
   
 Modifies a configuration item in the currently open row.  Modifies a configuration item in the currently open row.
Line 94  Returns the generated table string. Line 89  Returns the generated table string.
   
 Configures a table's global configuration.  Configures a table's global configuration.
   
   =item add_cell
   
   Add and configure a cell to the current row.6
   
 =back  =back
   
 =cut  =cut
Line 109  modified by this.  These configuration i Line 108  modified by this.  These configuration i
   
 =over3  =over3
   
   
 =item alignment  =item alignment
   
 Table alignment.  Some table styles support this but not all.  Table alignment.  Some table styles support this but not all.
Line 133  to the LaTeX::Table package, and they wi Line 133  to the LaTeX::Table package, and they wi
 not error checked.  Any use of a non-existent theme is reported by the LaTeX::Table package  not error checked.  Any use of a non-existent theme is reported by the LaTeX::Table package
 when the table text is generated.  when the table text is generated.
   
   =item width
   
   The width of the table.  This can be expressed as fractions of full width, or in any
   TeX unit measure e.g. 0.75 for 75% of the width, or 10.8cm  This forces the table to the
   tabularx environment.
   
 =back  =back
   
 =head3 Member data  =head3 Member data
Line 165  Table caption (configurable). Line 171  Table caption (configurable).
   
 Theme desired (configurable).  Theme desired (configurable).
   
   =item width
   
   If defined, the width of the table (should be supplied
   in fraction of column width e.g. .75 for 75%.
   
 =item row_open   =item row_open 
   
 True if a row is open and not yet closed.  True if a row is open and not yet closed.
Line 181  Each row of table data is an element of Line 192  Each row of table data is an element of
   
 =over 3  =over 3
   
 =item is_header  
   
 True if the user wants to format this row like a header.  This row will be used to generate  
 the table header.  All header rows will be gathered together into the table header.  If there  
 are multiple table headers interspersed with non table header data, this can lead to some   
 surprises.  
   
 =item default_halign   =item default_halign 
   0
 Default horizontal alignment for cells in this row.  Default horizontal alignment for cells in this row.
   
 =item default_valign  =item default_valign
   
 Default vertical alignment for cells in this row (may be ignored).  Default vertical alignment for cells in this row (may be ignored).
   
   =item cell_width
    
   The width of the row in cells.  This is the sum of the column spans 
   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.
Line 203  The contents of each element of this has Line 213  The contents of each element of this has
   
 =over 3  =over 3
   
   =item header
   
   If present, the row is a 'header' that is it was made via the
   <th> tag.
   
 =item halign  =item halign
   
 If present, overrides the row default horizontal alignment.  If present, overrides the row default horizontal alignment.
Line 220  If present, indicates the number of rows Line 235  If present, indicates the number of rows
 If present indicates the number of columns this cell spans.  If present indicates the number of columns this cell spans.
 Note that a cell can span both rows and columns.  Note that a cell can span both rows and columns.
   
   =item start_col
   
   The starting column of the cell in the table grid.
   
 =item contents  =item contents
   
 The contents of the cell.  The contents of the cell.
Line 239  sub new { Line 258  sub new {
     my $self = {      my $self = {
  alignment      => "left",   alignment      => "left",
  outer_border   => 0,   outer_border   => 0,
  inner_borders  => 0,   inner_border  => 0,
  caption        => "",   caption        => "",
  theme          => "Zurich",   theme          => "Zurich",
  column_count   => 0,   column_count   => 0,
Line 256  sub new { Line 275  sub new {
     return $self;      return $self;
 }  }
   
   
 #-------------------------------------------------------------------------  #-------------------------------------------------------------------------
 #  #
 #  Methods that get/set table global configuration.  #  Methods that get/set table global configuration.
   #
   
   =pod
   
   =head2 Gets/set alignment.  
   
   If the method is passed a new alignment value, that replaces the current one.
   Regardless, the current alignment is used:
   
   =head3 Examples:
   
    my $align = $table->alignment(); # Return current alignment
    $table->alignment("center");     # Attempt centered alignment.
   
   =cut
   
   sub alignment {
       my ($self, $new_value) = @_;
   
       if (defined($new_value)) {
    $self->{'alignment'} = $new_value;
       }
       return $self->{'alignment'};
   }
   
   =pod
   
   =head2 table_border
   
   Set or get the presence of an outer border in the table.
   If passed a parameter, that parameter replaces the current request
   for or not for an outer border. Regardless, the function returns
   the final value of the outer_border request.
   
   =head3 Examples:
   
     $table->table_border(1);      # Request an outer border.
     my $outer_requested = $table->table_border();
   
   =cut
   
   sub table_border {
       my ($self, $new_value) = @_;
   
       if (defined($new_value)) {
    $self->{'outer_border'} = $new_value;
       }
       return $self->{'outer_border'};
   }
   
   
   =pod
   
   =head2 cell_border
   
   Set or get the presence of a request for cells to have borders
   drawn around them.  If a paramter is passed, it will be treated as
   a new value for the cell border configuration.  Regardless,the final
   value of that configuration parameter is returned.
   
   =head3 Examples:
   
    my $cell_border = $table->cell_border(); # ask if cell borders are requested.
    $table->cell_border(1); # Request cell borders.
   
   =cut
   
   sub cell_border {
       my ($self, $new_value) = @_;
   
       if (defined($new_value)) {
    $self->{'inner_border'} = $new_value;
       }
       return $self->{'inner_border'};
   }
   
   =pod
   
   =head2 caption
   
   Gets and/or sets the caption string for the table.  The caption string appears to label
   the table.  If a parameter is supplied it will become the new caption string.k
   
   =head3 Examples:
   
   
     $my caption = $table->caption();
     $table->caption("This is the new table caption");
   
   =cut
   
   sub caption {
       my ($self, $new_value) = @_;
   
       if (defined($new_value)) {
    $self->{'caption'} = $new_value;
       }
   
       return $self->{'caption'};
   }
   
   =pod
   
   =head2 theme
   
   Gets and optionally sets the table theme.  The table theme describes how the
   table will be typset by the table package.  If a parameter is supplied it
   will be the new theme selection.
   
   =head3 Examples:
   
     my $theme = $table->theme();
     $table->theme("Dresden");
   
   =cut
   
   sub theme {
       my ($self, $new_value) = @_;
   
       if (defined($new_value)) {
    $self->{'theme'} = $new_value;
       }
       return $self->{'theme'};
   }
   
   =pod
   
   =head 2 width
   
   Gets and optionally sets the width of the table.
   
   =head 3 Examples:
   
    $table->width("0.8");    # 80% of the column width.
    my $newwidth = $table->width("10cm");   # 10cm width returns "10cm".
   
   =cut
   sub width {
       my ($self, $new_value) = @_;
       if (defined($new_value)) {
    $self->{'width'} = $new_value;
       }
       return $self->{'width'}; # Could be undef.
   }
   
   =pod
   
   =head2 start_row
   
   Begins a new row in the table.  If a row is already open, that row is
   closed off prior to starting the new row.  Rows can have the following attributes
   which are specified by an optional hash passed in to this function.
   
   =over 3
   
   =item default_halign
   
   The default horizontal alignment of the row. This can be "left", "center", or "right"
   
   =item default_valign
   
   The default vertical alignment of the row.  This can be "top", "center", or "bottom"
   
   =back
   
   =head3 Examples:
   
     $table_start_row();                  # no attributes.
     $table_start({default_halign => "center",
                   default_valign => "bottom"}); # Create setting the attrbutes.
   
   =cut
   
   sub start_row {
       my ($self, $config) = @_;
   
       if ($self->{'row_open'}) { 
    $self->end_row();
       }
       my $row_hash = {
    default_halign => "left",
    default_valign => "top",
    cell_width     =>  0,
    cells          => []
       };
   
       # Override the defaults if the config hash is present:
   
       if (defined($config)) {
    foreach my $key  (keys %$config) {
       $row_hash->{$key} = $config->{$key};
    }
       }
   
       
       my $rows = $self->{'rows'};
       push(@$rows, $row_hash);
   
       $self->{"row_open"} = 1; # Row is now open and ready for business.
   }
   
   =pod
   
   =head2  end_row 
   
   Closes off a row.  Once closed, cells cannot be added to this row again.
   
   =head3 Examples:
   
      $table->end_row();
   
   
   =cut
   
   sub end_row {
       my ($self) = @_;
   
       if ($self->{'row_open'}) {
   
    # Mostly we need to determine if this row has the maximum
    # cell count of any row in existence in the table:
   
    my $row        = $self->{'rows'}->[-1];
    my $cells      = $row->{'cells'};
   
    if ($row->{'cell_width'} > $self->{'column_count'}) {
       $self->{'column_count'} = $row->{'cell_width'};
    }
   
    $self->{'row_open'} = 0;;
       }
   }
   
   =pod
   
   =head2 configure_row
   
   Modify the configuration of a row.   If a row is not open, a new one will be opened.
   
   =head3 Parameters:
   
   config_hash - A hash that contains new values for the set of row confiuguration 
   items to be modified.  There is currently no check/penalty for items that are not in
   the set of defined configuration properties which are:
   
   =over 2
   
   =item default_halign
   
   The default horizontal alignment for text in  cells in the row.  This can be any of:
   "left", "right" or "center".
   
   =item default_valign
   
   The default vertical alignment for text in cells in the row.  This can be any of:
   
   "top", "bottom" or "center"
   
   =back 
   
   =cut
   
   sub configure_row {
       my ($self, $config) = @_;
   
       if (!$self->{'row_open'}) {
    $self->start_row();
       }
       
       my $row = $self->{'rows'}[-1];
       foreach my $config_item (keys %$config) {
    $row->{$config_item} = $config->{$config_item};
       }
   }
   
   
   =pod
   
   =head2 add_cell
   
   Add a new cell to a row.  If there is a row above us, we need to 
   watch out for row spans that may force additional blank cell entries
   to fill in the span. 
   
   =head3 Parameters:
   
   =over 2
   
   =item text
   
   Text to put in the cell.
   
   =item cell_config
   
   Hash of configuration options that override the defaults.   The recognized options,
   and their defaults are:
   
   =over 2
   
   =item halign 
   
   If nonblank overrides the row's default for the cell's horizontal alignment.
   
   =item valign
   
   If nonblank, overrides the row's default for the cdell's vertical alignment.
   
   =item rowspan
   
   Number of rows the cell spans.
   
   =item colspan
   
   Number of columns the cell spans.
   
   =back
   
   =cut
   
   sub add_cell {
       my ($self, $text, $config) = @_;
   
       # If a row is not open, we must open it:
   
       if (!$self->{'row_open'}) {
    $self->start_row();
       }
       my $rows          = $self->{'rows'};
       my $current_row   = $rows->[-1];
       my $current_cells = $current_row->{'cells'}; 
       my $last_coord    = $current_row->{'cell_width'};
   
       #  We have to worry about row spans if there is a prior row:
   
       if (scalar(@$rows) > 1) {
   
    my $last_row = $rows->[-2];
    if ($last_coord < $last_row->{'cell_width'}) {
       my $prior_coord       = 0;
       my $prior_cell_index  = 0;
       while ($prior_coord <= $last_coord) {
   
    # Pull a cell down if it's coord matches our start coord
    # And there's a row span > 1.
    # Having done so, we adjust our $last_coord to match the
    # end point of the pulled down cell.
   
    my $prior_cell = $last_row->{'cells'}->[$prior_cell_index];
    if (($prior_cell->{'start_col'} == $last_coord) &&
       ($prior_cell->{'rowspan'}  > 1)) {
       
       #  Need to drop the cell down
   
       my %dropped_down_cell = %$prior_cell;
       $dropped_down_cell{'rowspan'}--;
       $dropped_down_cell{'contents'} = '';
   
       push(@$current_cells, \%dropped_down_cell);
       $last_coord += $dropped_down_cell{'colspan'};
       $current_row->{'cell_width'} = $last_coord;
       
    }
    $prior_coord += $prior_cell->{'colspan'};
    $prior_cell_index++;
       }
    }
   
       }
   
       #
       # Now we're ready to build up our cell:
   
       my $cell = {
    rowspan    => 1,
    colspan    => 1,
    start_col  => $last_coord,
    contents   => $text
       };
       
       if (defined($config)) {
    foreach my $key (keys(%$config)) {
       $cell->{$key} = $config->{$key};
    }
       }
       $current_row->{'cell_width'} += $cell->{'colspan'};
   
       push(@$current_cells, $cell);
   }
   
   =pod
   
   =head2 generate
   
   Call this when the structures for the table have been built.
   This will generate and return the table object that can be used
   to generate the table.  Returning the table object allows for
   a certain amount of testing to be done on the generated table.
   The caller can then ask the table object to generate LaTeX.
   
   =cut
   sub generate {
       my ($this) = @_;
   
       my $table = LaTeX::Table->new();
   
       # Add the caption if supplied.
   
       if ($this->{'caption'} ne "") {
    $table->set_caption($this->caption);
       }
   
       
       # Set the width if defined:
   
       if (defined ($this->{'width'})) {
    $table->set_width($this->{'width'});
    $table->set_width_environment('tabularx');
       }
   
       # Build up the data:
   
       my @data;
       my $rows      = $this->{'rows'};
       my $row_count = scalar(@$rows);
       my $inner_border = $this->{'inner_border'};
       my $outer_border = $this->{'outer_border'};
       my $column_count = $this->{'column_count'};
   
       for (my $row = 0; $row < $row_count; $row++) {
    my @row;
    my $cells      = $rows->[$row]->{'cells'};
    my $def_halign = $rows->[$row]->{'default_halign'};
    my $cell_count = scalar(@$cells);
    my $startcol   = 1;
    my @underlines; # Array of \cline cells if cellborder on.
   
    for (my $cell  = 0; $cell < $cell_count; $cell++) {
       my $contents = $cells->[$cell]->{'contents'};
   
       #
       #  Cell alignment is the default alignment unless
       #  explicitly specified in the cell.
       #  NOTE: at this point I don't know how to do vert alignment.
       #
   
       my $halign   = $def_halign;
       if (defined ($cells->[$cell]->{'halign'})) {
    $halign = $cells->[$cell]->{'halign'};
       }
   
       # Create the horizontal alignment character:
   
       my $col_align = 'l';
       if ($halign eq 'right') {
    $col_align = 'r';
       }
       if ($halign eq 'center') {
    $col_align = 'c';
       }
       if ($inner_border || ($outer_border && ($cell == 0))) {
    $col_align = '|'.$col_align;
       }
       if ($inner_border || ($outer_border && ($cell == ($cell_count -1)))) {
    $col_align = $col_align.'|';
       }
   
       #factor in spans:
   
       my $cspan    = $cells->[$cell]->{'colspan'};
       my $nextcol  = $startcol + $cspan;
       $contents = '\multicolumn{'.$cspan.'}{'.$col_align.'}{'.$contents.'}';
       if ($inner_border && ($cells->[$cell]->{'rowspan'} == 1)) {
    my $lastcol = $nextcol -1;
    push(@underlines, "\\cline{$startcol-$lastcol}");
       }
       $startcol = $nextcol;
       # Rowspans should take care of themselves.
       
   
       push(@row, $contents);
   
    }
    push(@data, \@row);
    if ($inner_border) {
       for (my $i =0; $i < scalar(@underlines); $i++) {
    push(@data, [$underlines[$i]]);
       }
    }
   
       }
       $table->set_data(\@data);
       
       my $coldef = "";
       if ($outer_border || $inner_border) {
    $coldef .= '|';
       }
       for (my $i =0; $i < $column_count; $i++) {
    $coldef .= 'l';
    if ($inner_border || 
       ($outer_border && ($i == $column_count-1))) {
       $coldef .= '|';
    }
       }
       $table->{'coldef'} = $coldef;
   
       # Return the table:
   
       return $table;
   
   }
   #----------------------------------------------------------------------------
   # The following methods allow for testability.
   
   
   sub get_object_attribute {
       my ($self, $attribute) = @_;
       return $self->{$attribute};
   }
   
   sub get_row {
       my ($self, $row) = @_;
       my $rows = $self->{'rows'};  # ref to an array....
       return $rows->[$row];         # ref to the row hash for the selected row.
   }
 #   Mandatory initialization.  #   Mandatory initialization.
   BEGIN{
   }
   
 1;  1;
 __END__  __END__

Removed from v.1.1  
changed lines
  Added in v.1.7


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