Annotation of loncom/xml/lontable.pm, revision 1.6

1.1       foxr        1: # The LearningOnline Network with CAPA
                      2: #  Generating TeX tables.
                      3: #
1.6     ! foxr        4: # $Id: lontable.pm,v 1.5 2008/12/09 11:50:08 foxr Exp $
1.1       foxr        5: # 
                      6: #
                      7: # Copyright Michigan State University Board of Trustees
                      8: #
                      9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                     10: #
                     11: # LON-CAPA is free software; you can redistribute it and/or modify
                     12: # it under the terms of the GNU General Public License as published by
                     13: # the Free Software Foundation; either version 2 of the License, or
                     14: # (at your option) any later version.
                     15: #
                     16: # LON-CAPA is distributed in the hope that it will be useful,
                     17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     19: # GNU General Public License for more details.
                     20: #
                     21: # You should have received a copy of the GNU General Public License
                     22: # along with LON-CAPA; if not, write to the Free Software
                     23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     24: #
                     25: # /home/httpd/html/adm/gpl.txt
                     26: #
                     27: # http://www.lon-capa.org/
                     28: ## Copyright for TtHfunc and TtMfunc by Ian Hutchinson. 
                     29: # TtHfunc and TtMfunc (the "Code") may be compiled and linked into 
                     30: # binary executable programs or libraries distributed by the 
                     31: # Michigan State University (the "Licensee"), but any binaries so 
                     32: # distributed are hereby licensed only for use in the context
                     33: # of a program or computational system for which the Licensee is the 
                     34: # primary author or distributor, and which performs substantial 
                     35: # additional tasks beyond the translation of (La)TeX into HTML.
                     36: # The C source of the Code may not be distributed by the Licensee
                     37: # to any other parties under any circumstances.
                     38: #
                     39: 
                     40: # This module is a support packkage that helps londefdef generate
                     41: # LaTeX tables using the LaTeX::Table package.  A prerequisite is that
1.5       foxr       42: # the print generator must have added the following to the LaTeX 
1.1       foxr       43: #
                     44: #  \usepackage{xtab}
                     45: #  \usepackage{booktabs}
                     46: #  \usepackage{array}
                     47: #  \usepackage{colortbl}
                     48: #  \usepackage{xcolor}
                     49: #
                     50: #  These packages are installed in the packaged LaTeX distributions we know of as of
                     51: #  11/24/2008
                     52: #
                     53: 
                     54: 
                     55: 
                     56: package Apache::lontable;
                     57: use strict;
                     58: use LaTeX::Table;
                     59: 
                     60: 
                     61: =pod
                     62: 
                     63: =head1  lontable Table generation assistant for the LaTeX target
                     64: 
                     65: This module contains support software for generating tables in LaTeX output mode 
                     66: In this implementation, we use the LaTeX::Table package to do the actual final formatting.
                     67: Each table creates a new object.  Table objects can have global properties configured.
                     68: The main operations on a table object are:
                     69: 
                     70: =over 3
                     71: 
                     72: =item start_row  
                     73: 
                     74: Opens a new table row.
                     75: 
                     76: =item end_row
                     77: 
                     78: Closes a table row.
                     79: 
                     80: =item configure_row
                     81: 
                     82: Modifies a configuration item in the currently open row.
                     83: 
                     84: =item generate
                     85: 
                     86: Returns the generated table string.
                     87: 
                     88: =item configure
                     89: 
                     90: Configures a table's global configuration.
                     91: 
1.3       foxr       92: =item add_cell
                     93: 
                     94: Add and configure a cell to the current row.6
                     95: 
1.1       foxr       96: =back
                     97: 
                     98: =cut
                     99: 
                    100: =pod
                    101: 
                    102: =head2 new - create a new object.
                    103: 
                    104: Create a new table object.  Any of the raw table configuration items can be
                    105: modified by this.  These configuration items include:
                    106: 
                    107:   my $table = lontable::new(\%config_hash)
                    108: 
                    109: =over3
                    110: 
                    111: =item alignment
                    112: 
                    113: Table alignment.  Some table styles support this but not all.
                    114: 
                    115: =item tableborder
                    116: 
                    117: If true, a border is drawn around the table.
                    118: 
                    119: =item cellborder
                    120: 
                    121: If true, borders are drawn around the cells inside a table.
                    122: 
                    123: =item caption
                    124: 
                    125: The table caption text.
                    126: 
                    127: =item theme
                    128: 
                    129: The theme of the table to use.  Defaults to Zurich.  Themes we know about are:
                    130: NYC, NYC2, Zurich, Berlin, Dresden, Houston, Miami, plain, Paris.  Other themes can be added
                    131: to the LaTeX::Table package, and they will become supported automatically, as theme names are
                    132: not error checked.  Any use of a non-existent theme is reported by the LaTeX::Table package
                    133: when the table text is generated.
                    134: 
                    135: =back
                    136: 
                    137: =head3 Member data
                    138: 
                    139: The object hash has the following members:
                    140: 
                    141: =over 3
                    142: 
                    143: =item column_count 
                    144: 
                    145: Maintained internally, the number of colums in the widest row.
                    146: 
                    147: =item alignment
                    148: 
                    149: Table alignment (configurable) "left", "center", or "right".
                    150: 
                    151: =item outer_border
                    152: 
                    153: True if a border should be drawn around the entire table (configurable)
                    154: 
                    155: =item inner_borders
                    156: 
                    157: True if a border should be drawn around all cells (configurable).
                    158: 
                    159: =item caption
                    160: 
                    161: Table caption (configurable).
                    162: 
                    163: =item theme
                    164: 
                    165: Theme desired (configurable).
                    166: 
1.6     ! foxr      167: =item width
        !           168: 
        !           169: If defined, the width of the table (should be supplied
        !           170: in fraction of column width e.g. .75 for 75%.
        !           171: 
1.1       foxr      172: =item row_open 
                    173: 
                    174: True if a row is open and not yet closed.
                    175: 
                    176: =item rows
                    177: 
                    178: Array of row data. This is an array of hashes described below.
                    179: 
                    180: =back
                    181: 
                    182: =head3 Row data.
                    183: 
                    184: Each row of table data is an element of the rows hash array.  Hash elements are
                    185: 
                    186: =over 3
                    187: 
                    188: 
                    189: =item default_halign 
1.3       foxr      190: 0
1.1       foxr      191: Default horizontal alignment for cells in this row.
                    192: 
                    193: =item default_valign
                    194: 
                    195: Default vertical alignment for cells in this row (may be ignored).
                    196: 
1.6     ! foxr      197: =item cell_width
        !           198:  
        !           199: The width of the row in cells.  This is the sum of the column spans 
        !           200: of the cells in the row.
        !           201: 
1.1       foxr      202: =item cells
                    203: 
                    204: Array of hashes where each element represents the data for a cell.
                    205: The contents of each element of this hash are described below:
                    206: 
                    207: =over 3
                    208: 
1.3       foxr      209: =item header
                    210: 
                    211: If present, the row is a 'header' that is it was made via the
                    212: <th> tag.
                    213: 
1.1       foxr      214: =item halign
                    215: 
                    216: If present, overrides the row default horizontal alignment.
                    217: 
                    218: =item valign
                    219: 
                    220: if present, override the row default vertical alignment.
                    221: 
                    222: =item rowspan
                    223: 
                    224: If present, indicates the number of rows this cell spans.
                    225: 
                    226: =item colspan
                    227: 
                    228: If present indicates the number of columns this cell spans.
                    229: Note that a cell can span both rows and columns.
                    230: 
1.6     ! foxr      231: =item start_col
        !           232: 
        !           233: The starting column of the cell in the table grid.
        !           234: 
1.1       foxr      235: =item contents
                    236: 
                    237: The contents of the cell.
                    238: 
                    239: =back
                    240: 
                    241: =back
                    242: 
                    243: =cut
                    244: 
                    245: sub new {
                    246:     my ($class, $configuration) = @_;
                    247: 
                    248:     #  Initialize the object member data with the default values
                    249:     #  then override with any stuff in $configuration.
                    250: 
                    251:     my $self = {
                    252: 	alignment      => "left",
                    253: 	outer_border   => 0,
1.2       foxr      254: 	inner_border  => 0,
1.1       foxr      255: 	caption        => "",
                    256: 	theme          => "Zurich",
                    257: 	column_count   => 0,
                    258: 	row_open       => 0,
                    259: 	rows           => [],
                    260:     };
                    261: 
                    262:     foreach my $key (keys %$configuration) {
                    263: 	$self->{$key} = $$configuration{$key};
                    264:     }
                    265: 
                    266:     bless($self, $class);
                    267: 
                    268:     return $self;
                    269: }
                    270: 
1.3       foxr      271: 
1.1       foxr      272: #-------------------------------------------------------------------------
                    273: #
                    274: #  Methods that get/set table global configuration.
1.2       foxr      275: #
                    276: 
                    277: =pod
                    278: 
                    279: =head2 Gets/set alignment.  
                    280: 
                    281: If the method is passed a new alignment value, that replaces the current one.
                    282: Regardless, the current alignment is used:
                    283: 
                    284: =head3 Examples:
                    285: 
                    286:  my $align = $table->alignment(); # Return current alignment
                    287:  $table->alignment("center");     # Attempt centered alignment.
                    288: 
                    289: =cut
                    290: 
                    291: sub alignment {
                    292:     my ($self, $new_value) = @_;
                    293: 
                    294:     if (defined($new_value)) {
1.5       foxr      295: 	$self->{'alignment'} = $new_value;
1.2       foxr      296:     }
1.5       foxr      297:     return $self->{'alignment'};
1.2       foxr      298: }
                    299: 
                    300: =pod
                    301: 
                    302: =head2 table_border
                    303: 
                    304: Set or get the presence of an outer border in the table.
                    305: If passed a parameter, that parameter replaces the current request
                    306: for or not for an outer border. Regardless, the function returns
                    307: the final value of the outer_border request.
                    308: 
                    309: =head3 Examples:
                    310: 
                    311:   $table->table_border(1);      # Request an outer border.
                    312:   my $outer_requested = $table->table_border();
                    313: 
                    314: =cut
                    315: 
                    316: sub table_border {
                    317:     my ($self, $new_value) = @_;
                    318: 
                    319:     if (defined($new_value)) {
1.5       foxr      320: 	$self->{'outer_border'} = $new_value;
1.2       foxr      321:     }
1.5       foxr      322:     return $self->{'outer_border'};
1.2       foxr      323: }
                    324: 
                    325: 
                    326: =pod
                    327: 
                    328: =head2 cell_border
                    329: 
                    330: Set or get the presence of a request for cells to have borders
                    331: drawn around them.  If a paramter is passed, it will be treated as
                    332: a new value for the cell border configuration.  Regardless,the final
                    333: value of that configuration parameter is returned.
                    334: 
                    335: =head3 Examples:
                    336: 
1.4       foxr      337:  my $cell_border = $table->cell_border(); # ask if cell borders are requested.
1.2       foxr      338:  $table->cell_border(1);	# Request cell borders.
                    339: 
                    340: =cut
                    341: 
1.4       foxr      342: sub cell_border {
1.2       foxr      343:     my ($self, $new_value) = @_;
                    344: 
                    345:     if (defined($new_value)) {
1.5       foxr      346: 	$self->{'inner_border'} = $new_value;
1.2       foxr      347:     }
1.5       foxr      348:     return $self->{'inner_border'};
1.2       foxr      349: }
                    350: 
                    351: =pod
                    352: 
                    353: =head2 caption
                    354: 
                    355: Gets and/or sets the caption string for the table.  The caption string appears to label
                    356: the table.  If a parameter is supplied it will become the new caption string.k
                    357: 
                    358: =head3 Examples:
                    359: 
                    360: 
                    361:   $my caption = $table->caption();
                    362:   $table->caption("This is the new table caption");
                    363: 
                    364: =cut
                    365: 
                    366: sub caption {
                    367:     my ($self, $new_value) = @_;
                    368: 
                    369:     if (defined($new_value)) {
1.5       foxr      370: 	$self->{'caption'} = $new_value;
1.2       foxr      371:     }
                    372: 
1.5       foxr      373:     return $self->{'caption'};
1.2       foxr      374: }
                    375: 
                    376: =pod
                    377: 
                    378: =head2 theme
                    379: 
                    380: Gets and optionally sets the table theme.  The table theme describes how the
                    381: table will be typset by the table package.  If a parameter is supplied it
                    382: will be the new theme selection.
                    383: 
                    384: =head3 Examples:
1.1       foxr      385: 
1.2       foxr      386:   my $theme = $table->theme();
                    387:   $table->theme("Dresden");
                    388: 
                    389: =cut
                    390: 
                    391: sub theme {
                    392:     my ($self, $new_value) = @_;
                    393: 
                    394:     if (defined($new_value)) {
1.5       foxr      395: 	$self->{'theme'} = $new_value;
1.2       foxr      396:     }
1.5       foxr      397:     return $self->{'theme'};
1.2       foxr      398: }
                    399: 
                    400: =pod
                    401: 
                    402: =head2 start_row
                    403: 
                    404: Begins a new row in the table.  If a row is already open, that row is
                    405: closed off prior to starting the new row.  Rows can have the following attributes
                    406: which are specified by an optional hash passed in to this function.
                    407: 
                    408: =over 3
                    409: 
                    410: =item default_halign
                    411: 
                    412: The default horizontal alignment of the row. This can be "left", "center", or "right"
                    413: 
                    414: =item default_valign
                    415: 
                    416: The default vertical alignment of the row.  This can be "top", "center", or "bottom"
                    417: 
                    418: =back
                    419: 
                    420: =head3 Examples:
                    421: 
                    422:   $table_start_row();                  # no attributes.
                    423:   $table_start({default_halign => "center",
                    424:                 default_valign => "bottom"}); # Create setting the attrbutes.
                    425: 
                    426: =cut
                    427: 
                    428: sub start_row {
1.5       foxr      429:     my ($self, $config) = @_;
1.2       foxr      430: 
1.5       foxr      431:     if ($self->{'row_open'}) { 
1.4       foxr      432: 	$self->end_row();
1.2       foxr      433:     }
                    434:     my $row_hash = {
                    435: 	default_halign => "left",
                    436: 	default_valign => "top",
1.6     ! foxr      437: 	cell_width     =>  0,
1.2       foxr      438: 	cells          => []
                    439:     };
                    440: 
                    441:     # Override the defaults if the config hash is present:
                    442: 
1.5       foxr      443:     if (defined($config)) {
                    444: 	foreach my $key  (keys %$config) {
                    445: 	    $row_hash->{$key} = $config->{$key};
1.2       foxr      446: 	}
                    447:     }
1.5       foxr      448: 
1.2       foxr      449:     
1.5       foxr      450:     my $rows = $self->{'rows'};
1.2       foxr      451:     push(@$rows, $row_hash);
                    452: 
1.5       foxr      453:     $self->{"row_open"} = 1;	# Row is now open and ready for business.
1.2       foxr      454: }
                    455: 
                    456: =pod
                    457: 
                    458: =head2  end_row 
                    459: 
                    460: Closes off a row.  Once closed, cells cannot be added to this row again.
                    461: 
                    462: =head3 Examples:
                    463: 
1.4       foxr      464:    $table->end_row();
1.2       foxr      465: 
                    466: 
                    467: =cut
                    468: 
1.4       foxr      469: sub end_row {
1.2       foxr      470:     my ($self) = @_;
                    471: 
1.5       foxr      472:     if ($self->{'row_open'}) {
1.2       foxr      473: 	
                    474: 	# Mostly we need to determine if this row has the maximum
                    475: 	# cell count of any row in existence in the table:
                    476: 
1.6     ! foxr      477: 	my $row        = $self->{'rows'}->[-1];
1.5       foxr      478: 	my $cells      = $row->{'cells'};
1.3       foxr      479: 
1.6     ! foxr      480: 	if ($row->{'cell_width'} > $self->{'column_count'}) {
        !           481: 	    $self->{'column_count'} = $row->{'cell_width'};
1.2       foxr      482: 	}
                    483: 
1.5       foxr      484: 	$self->{'row_open'} = 0;;
1.2       foxr      485:     }
                    486: }
                    487: 
                    488: =pod
                    489: 
1.3       foxr      490: =head2 configure_row
                    491: 
                    492: Modify the configuration of a row.   If a row is not open, a new one will be opened.
                    493: 
                    494: =head3 Parameters:
1.2       foxr      495: 
1.3       foxr      496: config_hash - A hash that contains new values for the set of row confiuguration 
                    497: items to be modified.  There is currently no check/penalty for items that are not in
                    498: the set of defined configuration properties which are:
1.2       foxr      499: 
1.3       foxr      500: =over 2
                    501: 
                    502: =item default_halign
                    503: 
                    504: The default horizontal alignment for text in  cells in the row.  This can be any of:
                    505: "left", "right" or "center".
                    506: 
                    507: =item default_valign
                    508: 
                    509: The default vertical alignment for text in cells in the row.  This can be any of:
                    510: 
                    511: "top", "bottom" or "center"
                    512: 
                    513: =back 
1.2       foxr      514: 
                    515: =cut
                    516: 
1.3       foxr      517: sub configure_row {
                    518:     my ($self, $config) = @_;
1.2       foxr      519: 
1.5       foxr      520:     if (!$self->{'row_open'}) {
1.3       foxr      521: 	$self->start_row();
                    522:     }
                    523:     
1.5       foxr      524:     my $row = $self->{'rows'}[-1];
1.3       foxr      525:     foreach my $config_item (keys %$config) {
                    526: 	$row->{$config_item} = $config->{$config_item};
                    527:     }
1.2       foxr      528: }
1.1       foxr      529: 
                    530: 
1.3       foxr      531: =pod
                    532: 
                    533: =head2 add_cell
                    534: 
                    535: Add a new cell to a row.  If there is a row above us, we need to 
                    536: watch out for row spans that may force additional blank cell entries
                    537: to fill in the span. 
                    538: 
                    539: =head3 Parameters:
                    540: 
                    541: =over 2
                    542: 
                    543: =item text
                    544: 
                    545: Text to put in the cell.
                    546: 
                    547: =item cell_config
                    548: 
                    549: Hash of configuration options that override the defaults.   The recognized options,
                    550: and their defaults are:
                    551: 
                    552: =over 2
                    553: 
                    554: =item halign 
                    555: 
                    556: If nonblank overrides the row's default for the cell's horizontal alignment.
                    557: 
                    558: =item valign
                    559: 
                    560: If nonblank, overrides the row's default for the cdell's vertical alignment.
                    561: 
                    562: =item rowspan
                    563: 
                    564: Number of rows the cell spans.
                    565: 
                    566: =item colspan
                    567: 
                    568: Number of columns the cell spans.
                    569: 
                    570: =back
                    571: 
                    572: =cut
                    573: 
                    574: sub add_cell {
                    575:     my ($self, $text, $config) = @_;
                    576: 
                    577:     # If a row is not open, we must open it:
                    578: 
1.5       foxr      579:     if (!$self->{'row_open'}) {
1.3       foxr      580: 	$self->start_row();
                    581:     }
1.6     ! foxr      582:     my $rows          = $self->{'rows'};
        !           583:     my $current_row   = $rows->[-1];
1.5       foxr      584:     my $current_cells = $current_row->{'cells'}; 
1.6     ! foxr      585:     my $last_coord    = $current_row->{'cell_width'};
1.3       foxr      586: 
1.6     ! foxr      587:     #  We have to worry about row spans if there is a prior row:
1.3       foxr      588: 
1.6     ! foxr      589:     if (scalar(@$rows) > 1) {
1.3       foxr      590: 
1.6     ! foxr      591: 	my $last_row = $rows->[-2];
        !           592: 	if ($last_coord < $last_row->{'cell_width'}) {
        !           593: 	    my $prior_coord       = 0;
        !           594: 	    my $prior_cell_index  = 0;
        !           595: 	    while ($prior_coord <= $last_coord) {
        !           596: 		
        !           597: 		# Pull a cell down if it's coord matches our start coord
        !           598: 		# And there's a row span > 1.
        !           599: 		# Having done so, we adjust our $last_coord to match the
        !           600: 		# end point of the pulled down cell.
        !           601: 
        !           602: 		my $prior_cell = $last_row->{'cells'}->[$prior_cell_index];
        !           603: 		if (($prior_cell->{'start_col'} == $last_coord) &&
        !           604: 		    ($prior_cell->{'rowspan'}  > 1)) {
        !           605: 		    
        !           606: 		    #  Need to drop the cell down
        !           607: 
        !           608: 		    my %dropped_down_cell = %$prior_cell;
        !           609: 		    $dropped_down_cell{'rowspan'}--;
        !           610: 		    $dropped_down_cell{'contents'} = '';
        !           611: 
        !           612: 		    push(@$current_cells, \%dropped_down_cell);
        !           613: 		    $last_coord += $dropped_down_cell{'colspan'};
        !           614: 		    $current_row->{'cell_width'} = $last_coord;
        !           615: 		    
        !           616: 		}
        !           617: 		$prior_coord += $prior_cell->{'colspan'};
        !           618: 		$prior_cell_index++;
        !           619: 	    }
1.3       foxr      620: 	}
1.6     ! foxr      621: 
1.3       foxr      622:     }
1.6     ! foxr      623: 
1.3       foxr      624:     #
                    625:     # Now we're ready to build up our cell:
                    626: 
                    627:     my $cell = {
                    628: 	rowspan    => 1,
                    629: 	colspan    => 1,
1.6     ! foxr      630: 	start_col  => $last_coord,
1.3       foxr      631: 	contents   => $text
                    632:     };
                    633:     
                    634:     if (defined($config)) {
                    635: 	foreach my $key (keys(%$config)) {
                    636: 	    $cell->{$key} = $config->{$key};
                    637: 	}
                    638:     }
1.6     ! foxr      639:     $current_row->{'cell_width'} += $cell->{'colspan'};
        !           640: 
1.3       foxr      641:     push(@$current_cells, $cell);
                    642: }
                    643: 
1.6     ! foxr      644: =pod
        !           645: 
        !           646: =head2 generate
        !           647: 
        !           648: Call this when the structures for the table have been built.
        !           649: This will generate and return the table object that can be used
        !           650: to generate the table.  Returning the table object allows for
        !           651: a certain amount of testing to be done on the generated table.
        !           652: The caller can then ask the table object to generate LaTeX.
        !           653: 
        !           654: =cut
        !           655: sub generate {
        !           656:     my ($this) = @_;
        !           657: 
        !           658:     my $table = LaTeX::Table->new();
        !           659: 
        !           660:     # Build up the data:
        !           661: 
        !           662:     my @data;
        !           663:     my $rows      = $this->{'rows'};
        !           664:     my $row_count = scalar(@$rows);
        !           665:     my $inner_border = $this->{'inner_border'};
        !           666:     my $outer_border = $this->{'outer_border'};
        !           667:     my $column_count = $this->{'column_count'};
        !           668: 
        !           669:     for (my $row = 0; $row < $row_count; $row++) {
        !           670: 	my @row;
        !           671: 	my $cells      = $rows->[$row]->{'cells'};
        !           672: 	my $cell_count = scalar(@$cells);
        !           673: 	my $startcol   = 1;
        !           674: 	my @underlines;		# Array of \cline cells if cellborder on.
        !           675: 
        !           676: 	for (my $cell  = 0; $cell < $cell_count; $cell++) {
        !           677: 	    my $contents = $cells->[$cell]->{'contents'};
        !           678: 	    my $cspan    = $cells->[$cell]->{'colspan'};
        !           679: 	    my $nextcol  = $startcol + $cspan;
        !           680: 	    if ($cspan > 1) {
        !           681: 		$contents = '\multicolumn{'.$cspan.'}{|l|}{'.$contents.'}';
        !           682: 	    }
        !           683: 	    if ($inner_border && ($cells->[$cell]->{'rowspan'} == 1)) {
        !           684: 		my $lastcol = $nextcol -1;
        !           685: 		push(@underlines, "\\cline{$startcol-$lastcol}");
        !           686: 	    }
        !           687: 	    $startcol = $nextcol;
        !           688: 	    # Rowspans should take care of themselves.
        !           689: 	    
        !           690: 
        !           691: 	    push(@row, $contents);
        !           692: 
        !           693: 	}
        !           694: 	push(@data, \@row);
        !           695: 	if ($inner_border) {
        !           696: 	    for (my $i =0; $i < scalar(@underlines); $i++) {
        !           697: 		push(@data, [$underlines[$i]]);
        !           698: 	    }
        !           699: 	}
        !           700: 
        !           701:     }
        !           702:     $table->set_data(\@data);
        !           703:     
        !           704:     my $coldef = "";
        !           705:     if ($outer_border || $inner_border) {
        !           706: 	$coldef .= '|';
        !           707:     }
        !           708:     for (my $i =0; $i < $column_count; $i++) {
        !           709: 	$coldef .= 'l';
        !           710: 	if ($inner_border || 
        !           711: 	    ($outer_border && ($i == $column_count-1))) {
        !           712: 	    $coldef .= '|';
        !           713: 	}
        !           714:     }
        !           715:     $table->{'coldef'} = $coldef;
        !           716: 
        !           717:     # Return the table:
        !           718: 
        !           719:     return $table;
        !           720: 
        !           721: }
        !           722: #----------------------------------------------------------------------------
1.5       foxr      723: # The following methods allow for testability.
1.4       foxr      724: 
                    725: 
                    726: sub get_object_attribute {
                    727:     my ($self, $attribute) = @_;
                    728:     return $self->{$attribute};
                    729: }
                    730: 
1.5       foxr      731: sub get_row {
                    732:     my ($self, $row) = @_;
                    733:     my $rows = $self->{'rows'};	  # ref to an array....
                    734:     return $rows->[$row];         # ref to the row hash for the selected row.
                    735: }
1.1       foxr      736: #   Mandatory initialization.
1.4       foxr      737: BEGIN{
                    738: }
1.1       foxr      739: 
                    740: 1;
                    741: __END__

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