| Output Recording | Contents | Index | Graph Formatting |
17 Table Formatting
CLIM provides a mechanism for tabular formatting of arbitrary output.
To employ these facilities the programmer annotates some output-generating code with advisory macros that describe the high-level formatting constraints, for example, what parts of code produce a row of the table, what parts of that produce the cells in the row.
For example, the following produces a table consisting of three columns containing a number, its square, and its cube. The output can be seen in Figure 17.1 .
(defun table-test (count stream)
(fresh-line stream)
(formatting-table (stream :x-spacing '(3 :character))
(dotimes (i count)
(formatting-row (stream)
(formatting-cell (stream :align-x :right)
(prin1 i stream))
(formatting-cell (stream :align-x :right)
(prin1 (* i i) stream))
(formatting-cell (stream :align-x :right)
(prin1 (* i i i) stream))))))

17.1 Overview of Table Formatting Facilities
In general, table formatting involves a sharing of responsibilities between
user-written code and CLIM code. Code that employs only the lower level output
facilities has full control over ``where every piece of ink goes'' in the
output. In contrast, code that employs CLIM's table formatting facilities
passes control to CLIM at a higher level. The programmer benefits by being able
to specify the appearance of output in more compact abstract terms, and by not
having to write the code that constrains the output to appear in proper tabular
form.
Tabular output consists of a rectangular array of pieces of output corresponding to the bounding rectangles of the output. Each piece of output forms the contents of a table cell . There is no restriction on the contents of a table cell; cells may contain text, graphics, even other tables. For purposes of this discussion, we draw a strong distinction between specifying what goes in a cell, and specifying how the cells are arranged to form a table.
Specifying the contents of a cell is the responsibility of the programmer. A programmer using the table formatting facilities can predict the appearance of any individual cell by simply looking at the code for that cell. A cell's appearance does not depend upon where in the table it lies, for instance. The only thing about a cell's appearance that cannot be predicted from that cell alone is the amount of space the table formatting has to introduce in order to perform the desired alignment.
Specifying the relative arrangements of cells to form a table is the responsibility of CLIM based on the advice of the programmer. The programmer advises CLIM about extra space to put between rows or columns, for instance, but does not directly control the absolute positioning of a cell's contents.
For purposes of understanding table formatting, the following model may be used.
The advice that the programmer gives to CLIM on how to assemble the table consists of the following:
17.2 Table Formatting Functions
| formatting-table | (&optional stream &key x-spacing y-spacing multiple-columns multiple-columns-x-spacing equalize-column-widths (move-cursor t ) record-type &allow-other-keys ) &body body | [Macro] |
The returned value is the output record corresponding to the table.
stream is an output recording stream to which output will be done. The stream argument is not evaluated, and must be a symbol that is bound to a stream. If stream is t (the default), *standard-output* is used. body may have zero or more declarations as its first forms.
x-spacing specifies the number of units of spacing to be inserted between columns of the table; the default is the width of a space character in the current text style. y-spacing specifies the number of units of spacing to be inserted between rows in the table; the default is the default vertical spacing of the stream. Possible values for these two options option are:
When the boolean equalize-column-widths is true , CLIM will make all of the columns have the same width (the width of the widest cell in any column in the entire table).
record-type specifies the class of output record to create. The default is standard-table-output-record . This argument should only be supplied by a programmer if there is a new class of output record that supports the table formatting protocol.
| formatting-row | (&optional stream &key record-type &allow-other-keys ) &body body | [Macro] |
stream is an output recording stream to which output will be done. The stream argument is not evaluated, and must be a symbol that is bound to a stream. If stream is t (the default), *standard-output* is used. body may have zero or more declarations as its first forms.
Once a table has had a row added to it via formatting-row , no columns may be added to it.
record-type specifies the class of output record to create. The default is standard-row-output-record . This argument should only be supplied by a programmer if there is a new class of output record that supports the row formatting protocol.
| formatting-column | (&optional stream &key record-type &allow-other-keys ) &body body | [Macro] |
stream is an output recording stream to which output will be done. The stream argument is not evaluated, and must be a symbol that is bound to a stream. If stream is t (the default), *standard-output* is used. body may have zero or more declarations as its first forms.
Once a table has had a column added to it via formatting-column , no rows may be added to it.
record-type specifies the class of output record to create. The default is standard-column-output-record . This argument should only be supplied by a programmer if there is a new class of output record that supports the column formatting protocol.
| formatting-cell | (&optional stream &key (align-x ':left ) (align-y ':baseline ) min-width min-height record-type &allow-other-keys ) &body body | [Macro] |
stream is an output recording stream to which output will be done. The stream argument is not evaluated, and must be a symbol that is bound to a stream. If stream is t (the default), *standard-output* is used. body may have zero or more declarations as its first forms.
align-x specifies how the output in a cell will be aligned relative to other cells in the same table column. The default, :left , causes the cells to be flush-left in the column. The other possible values are :right (meaning flush-right in the column) and :center (meaning centered in the column). Each cell within a column may have a different alignment; thus it is possible, for example, to have centered legends over flush-right numeric data.
align-y specifies how the output in a cell will be aligned vertically. The default, :baseline , causes textual cells to be aligned along their baselines and graphical cells to be aligned at the bottom. The other possible values are :bottom (align at the bottom of the output), :top (align at the top of the output), and :center (center the output in the cell).
min-width and min-height are used to specify minimum width or height of the cell. The default, nil , causes the cell to be only as wide or high as is necessary to contain the cell's contents. Otherwise, min-width and min-height are specified in the same way as the :x-spacing and :y-spacing arguments to formatting-table .
record-type specifies the class of output record to create. The default is standard-cell-output-record . This argument should only be supplied by a programmer if there is a new class of output record that supports the cell formatting protocol.
| formatting-item-list | (&optional stream &key x-spacing y-spacing n-columns n-rows stream-width stream-height max-width max-height initial-spacing (row-wise t ) (move-cursor t ) record-type &allow-other-keys ) &body body | [Macro] |
``Item list output'' is more strictly defined as: each row of the item list consists of a single cell. Rows are placed with the first row on top, and each succeeding row has its top aligned with the bottom of the previous row (plus the specified y-spacing ). Multiple rows and columns are constructed after laying the item list out in a single column. Item list output takes place in a normalized +y-downward coordinate system.
If row-wise is true (the default) and the item list requires multiple columns, each successive element in the item list is layed out from left to right. If row-wise is false and the item list requires multiple columns, each successive element in the item list is layed out below its predecessor, like in a telephone book.
The returned value is the output record corresponding to the table.
stream is an output recording stream to which output will be done. The stream argument is not evaluated, and must be a symbol that is bound to a stream. If stream is t (the default), *standard-output* is used. body may have zero or more declarations as its first forms.
x-spacing specifies the number of units of spacing to be inserted between
columns of the item list; the default is the width of a
When the boolean equalize-column-widths is true , CLIM will make all of the columns have the same width (the width of the widest cell in any column in the entire item list).
n-columns and n-rows specify the number of columns or rows in the item list. The default for both is nil , which causes CLIM to pick an aesthetically pleasing layout, possibly constrained by the other options. If both n-columns and n-rows are supplied and the item list contains more elements than will fit according to the specification, CLIM will format the item list as if n-rows were supplied as nil .
max-width and max-height constrain the layout of the item list. CLIM will not make the item list any wider than max-width , unless it is overridden by n-rows . It will not make the item list any taller than max-height , unless it is overridden by n-columns .
formatting-item-list normally spaces items across the entire width of the stream. When initial-spacing is true , it inserts some whitespace (about half as much space as is between each item) before the first item on each line. When it is false (the default), the initial whitespace is not inserted.
record-type specifies the class of output record to create. The default is standard-item-list-output-record . This argument should only be supplied by a programmer if there is a new class of output record that supports the item list formatting protocol.
| format-items | items &key stream printer presentation-type x-spacing y-spacing n-columns n-rows max-width max-height cell-align-x cell-align-y initial-spacing (row-wise t ) (move-cursor t ) record-type | [Function] |
stream is an output recording stream to which output will be done. It defaults to *standard-output* .
printer must be a function that takes two arguments, an item and a stream, and outputs the item on the stream. printer has dynamic extent. The default for printer is prin1 .
presentation-type is a presentation-type. When printer is not supplied, the items will be printed as if printer were
#'(lambda (item stream)
(present item presentation-type :stream stream))
When printer is supplied, each item will be enclosed in a presentation
whose type is presentation-type .
x-spacing , y-spacing , n-columns , n-rows , max-width , max-height , initial-spacing , row-wise , and move-cursor are as for formatting-item-list .
cell-align-x and cell-align-y are used to supply :align-x and :align-y to an implicitly used formatting-cell .
record-type is as for formatting-item-list .
17.3 The Table and Item List Formatting Protocols
Both table and item list formatting is implemented on top of the basic output
recording protocol, using with-new-output-record to specify the appropriate
type of output record. For example, formatting-table first collects all
the output that belongs in the table into a collection of row, column, and cell
output records, all of which are children of a single table output record.
During this phase, stream-drawing-p is bound to nil and
stream-recording-p is bound to t . When all the output has been
generated, the table layout constraint solver (adjust-table-cells or
adjust-item-list-cells ) is called to compute the table layout, taking into
account such factors as the widest cell in a given column. If the table is to
be split into multiple columns, adjust-multiple-columns is now called.
Finally, the table output record is positioned on the stream at the current text
cursor position and then displayed by calling replay on the table (or item
list) output record.
17.3.1 Table Formatting Protocol
Any output record class that implements the following generic functions is said
to support the table formatting protocol.
In the following subsections, the term ``non-table output records'' will be used to mean any output record that is not a table, row, column, cell, or item list output record. When CLIM ``skips over intervening non-table output records'', this means that it will bypass all the output records between two such table output records (such as a table and a row, or a row and a cell) that are not records of those classes (most notably, presentation output records). CLIM implementations are encouraged to detect invalid nesting of table output records, such as a row within a row, a cell within a cell, or a row within a cell. Note that this does not prohibit the nesting of calls to formatting-table , it simply requires that programmers include the inner table within one of the cells of the outer table.
| table-output-record | [Protocol Class] |
| table-output-record-p | object | [Predicate] |
| :x-spacing | [Init arg] |
| :y-spacing | [Init arg] |
| :multiple-columns-x-spacing | [Init arg] |
| :equalize-column-widths | [Init arg] |
| standard-table-output-record | [Class] |
| map-over-table-elements | function table-record type | [Generic function] |
| adjust-table-cells | table-record stream | [Generic function] |
| adjust-multiple-columns | table-record stream | [Generic function] |
17.3.2 Row and Column Formatting Protocol
Any output record class that implements the following generic functions is said
to support the row (or column) formatting protocol.
| row-output-record | [Protocol Class] |
| row-output-record-p | object | [Predicate] |
| standard-row-output-record | [Class] |
| map-over-row-cells | function row-record | [Generic function] |
| column-output-record | [Protocol Class] |
| column-output-record-p | object | [Predicate] |
| standard-column-output-record | [Class] |
| map-over-row-cells | function column-record | [Generic function] |
17.3.3 Cell Formatting Protocol
Any output record class that implements the following generic functions is said
to support the cell formatting protocol.
| cell-output-record | [Protocol Class] |
| cell-output-record-p | object | [Predicate] |
| :align-x | [Init arg] |
| :align-y | [Init arg] |
| :min-width | [Init arg] |
| :min-height | [Init arg] |
| standard-cell-output-record | [Class] |
| cell-align-x | cell | [Generic function] |
| cell-align-y | cell | [Generic function] |
| cell-min-width | cell | [Generic function] |
| cell-min-height | cell | [Generic function] |
17.3.4 Item List Formatting Protocol
| item-list-output-record | [Protocol Class] |
| item-list-output-record-p | object | [Predicate] |
| :x-spacing | [Init arg] |
| :y-spacing | [Init arg] |
| :initial-spacing | [Init arg] |
| :row-wise | [Init arg] |
| :n-rows | [Init arg] |
| :n-columns | [Init arg] |
| :max-width | [Init arg] |
| :max-height | [Init arg] |
| standard-item-list-output-record | [Class] |
| map-over-item-list-cells | function item-list-record | [Generic function] |
| adjust-item-list-cells | item-list-record stream | [Generic function] |
| Output Recording | Contents | Index | Graph Formatting |