aboutsummaryrefslogtreecommitdiff
path: root/dw/table.hh
diff options
context:
space:
mode:
Diffstat (limited to 'dw/table.hh')
-rw-r--r--dw/table.hh486
1 files changed, 486 insertions, 0 deletions
diff --git a/dw/table.hh b/dw/table.hh
new file mode 100644
index 00000000..ec2bacc8
--- /dev/null
+++ b/dw/table.hh
@@ -0,0 +1,486 @@
+#ifndef __DW_TABLE_HH__
+#define __DW_TABLE_HH__
+
+#include "core.hh"
+#include "tablecell.hh"
+#include "../lout/misc.hh"
+
+namespace dw {
+
+/**
+ * \brief A Widget for rendering tables.
+ *
+ * <h3>Introduction</h3>
+ *
+ * The dw::Table widget is used to render HTML tables.
+ *
+ * Each cell is itself an own widget. Any widget may be used, however, in
+ * dillo, only instances of dw::Textblock and dw::TableCell are used as
+ * children of dw::Table.
+ *
+ *
+ * <h3>Sizes</h3>
+ *
+ * <h4>General</h4>
+ *
+ * The following diagram shows the dependencies between the different
+ * functions, which are related to size calculation. Click on the boxes
+ * for more informations.
+ *
+ * \dot
+ * digraph G {
+ * node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"];
+ * edge [arrowhead="open", arrowtail="none", labelfontname=Helvetica,
+ * labelfontsize=10, color="#404040", labelfontcolor="#000080",
+ * fontname=Helvetica, fontsize=10];
+ * fontname=Helvetica; fontsize=10;
+ *
+ * sizeRequestImpl [color="#0000ff", URL="\ref dw::Table::sizeRequestImpl"];
+ * sizeAllocateImpl [color="#0000ff",
+ * URL="\ref dw::Table::sizeAllocateImpl"];
+ * getExtremesImpl [color="#0000ff", URL="\ref dw::Table::getExtremesImpl"];
+ *
+ * subgraph cluster_sizes {
+ * style="dashed"; color="#8080c0";
+ * calcCellSizes [URL="\ref dw::Table::calcCellSizes"];
+ * forceCalcCellSizes [URL="\ref dw::Table::forceCalcCellSizes"];
+ * }
+ *
+ * subgraph cluster_extremes {
+ * style="dashed"; color="#8080c0";
+ * calcColumnExtremes [URL="\ref dw::Table::calcColumnExtremes"];
+ * forceCalcColumnExtremes[URL="\ref dw::Table::forceCalcColumnExtremes"];
+ * }
+ *
+ * sizeRequestImpl -> forceCalcCellSizes [label="[B]"];
+ * sizeAllocateImpl -> calcCellSizes [label="[A]"];
+ * getExtremesImpl -> forceCalcColumnExtremes [label="[B]"];
+ *
+ * forceCalcCellSizes -> calcColumnExtremes;
+ *
+ * calcCellSizes -> forceCalcCellSizes [style="dashed", label="[C]"];
+ * calcColumnExtremes -> forceCalcColumnExtremes [style="dashed",
+ * label="[C]"];
+ * }
+ * \enddot
+ *
+ * [A] In this case, the new calculation is \em not forced, but only
+ * done, when necessary.
+ *
+ * [B] In this case, the new calculation is allways necessary, since [C]
+ * is the case.
+ *
+ * [C] Whether this function is called, depends on NEEDS_RESIZE /
+ * EXTREMES_CHANGED.
+ *
+ *
+ * <h4>Apportionment</h4>
+ *
+ * \sa\ref rounding-errors
+ *
+ * Given two array \f$e_{i,\min}\f$ and \f$e_{i,\max}\f$, which
+ * represent the column minima and maxima, and a total width \f$W\f$, \em
+ * apportionment means to calculate column widths \f$w_{i}\f$, with
+ *
+ * \f[e_{i,\min} \le w_{i} \le e_{i,\max}\f]
+ *
+ * and
+ *
+ * \f[\sum w_{i} = W\f]
+ *
+ * There are different algorithms for apportionment, a simple one is
+ * recommended in the HTML 4.0.1 specification
+ * (http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.5.2.2):
+ *
+ * \f[w_{i} = e_{i,\min} +
+ * {e_{i,\max} - e_{i,\min}\over\sum e_{i,\max} - \sum e_{i,\min}}
+ * (W - \sum e_{i,\min})\f]
+ *
+ * This one is used currently, but another one will be used soon, which is
+ * described below. The rest of this chapter is independant of the exact
+ * apportionment algorithm.
+ *
+ * When referring to the apportionment function, we will call it
+ * \f$a_i (W, (e_{i,\min}), (e_{i,\min}))\f$ and write
+ * something like this:
+ *
+ * \f[w_{i} = a_i (W, (e_{i,\min}), (e_{i,\max})) \f]
+ *
+ * It is implemented by dw::Table::apportion.
+ *
+ * <h4>Column Extremes</h4>
+ *
+ * \sa\ref rounding-errors
+ *
+ * The sizes, which all other sizes depend on, are column extremes, which
+ * define, how wide a column may be at min and at max. They are
+ * calculated in the following way:
+ *
+ * <ol>
+ * <li> First, only cells with colspan = 1 are regarded:
+ *
+ * \f[ e_{\hbox{base},i,\min} = \max \{ e_{\hbox{cell},i,j,\min} \} \f]
+ * \f[ e_{\hbox{base},i,\max} = \max \{ e_{\hbox{cell},i,j,\max} \} \f]
+ *
+ * only for cells \f$(i, j)\f$ with colspan = 1.
+ *
+ * <li> Then,
+ * \f$e_{\hbox{span},i,\min}\f$ (but not \f$e_{\hbox{span},i,\max}\f$)
+ * are calculated from cells with colspan > 1. (In the following formulas,
+ * the cell at \f$(i_1, j)\f$ always span from \f$i_1\f$ to \f$i_2\f$.)
+ * If the minimal width of the column exeeds the sum of the column minima
+ * calculated in the last step:
+ *
+ * \f[e_{\hbox{cell},i_1,j,\min} >
+ * \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\min}\f]
+ *
+ * then the minimal width of this cell is apportioned to the columns:
+ *
+ * <ul>
+ * <li> If the minimal width of this cell also exeeds the sum of the
+ * column maxima:
+ *
+ * \f[e_{\hbox{cell},i_1,j,\min} >
+ * \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}\f]
+ *
+ * then \f$e_{\hbox{cell},i_1,j,\min}\f$ is apportioned in a simple
+ * way:
+ *
+ * \f[e_{\hbox{span},i,j,\min} =
+ * e_{\hbox{base},i,\max}
+ * {e_{\hbox{span},i,j,\min} \over
+ * \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}}\f]
+ *
+ * <li> Otherwise, the apportionment function is used:
+ *
+ * \f[e_{\hbox{span},i,j,\min} =
+ * a_i (e_{\hbox{cell},i_1,j,\min},
+ * (e_{\hbox{cell},i_1,j,\min} \ldots
+ * e_{\hbox{cell},i_2,j,\min}),
+ * (e_{\hbox{cell},i_1,j,\max} \ldots
+ * e_{\hbox{cell},i_2,j,\max}))\f]
+ * </ul>
+ *
+ * After this, \f$e_{\hbox{span},i,\min}\f$ is then the maximum of all
+ * \f$e_{\hbox{span},i,j,\min}\f$.
+ *
+ * <li> Finally, the maximum of both is used.
+ * \f[ e_{i,\min} =
+ * \max \{ e_{\hbox{base},i,\min}, e_{\hbox{span},i,\min} \} \f]
+ * \f[ e_{i,\max} =
+ * \max \{ e_{\hbox{base},i,\max}, e_{i,\min} \} \f]
+ * For the maxima, there is no \f$e_{\hbox{span},i,\max}\f$, but it has to
+ * be assured, that the maximum is always greater or equal than/to the
+ * minimum.
+ * </ol>
+ *
+ * Generally, if absolute widths are speficied, they are, instead of the
+ * results of dw::core::Widget::getExtremes, taken for the minimal and
+ * maximal width of a cell (minus the box difference, i.e. the difference
+ * between content size and widget size). If the content width
+ * specification is smaller than the minimal content width of the widget
+ * (determined by dw::core::Widget::getExtremes), the latter is used
+ * instead.
+ *
+ * If percentage widths are specified, they are also collected, as column
+ * maxima. A similar method as for the extremes is used, for cells with
+ * colspan > 1:
+ *
+ * \f[w_{\hbox{span},i,j,\%} =
+ * a_i (w_{\hbox{cell},i_1,j,\%},
+ * (e_{\hbox{cell},i_1,j,\min} \ldots e_{\hbox{cell},i_2,j,\min}),
+ * (e_{\hbox{cell},i_1,j,\max} \ldots e_{\hbox{cell},i_2,j,\max}))\f]
+ *
+ * <h4>Cell Sizes</h4>
+ *
+ * <h5>Determining the Width of the Table</h5>
+ *
+ * The total width is
+ *
+ * <ul>
+ * <li> the specified absolute width of the table, when given, or
+ * <li> the available width (set by dw::Table::setWidth) times the specifies
+ * percentage width pf t(at max 100%), if the latter is given, or
+ * <li> otherwise the available width.
+ * </ul>
+ *
+ * In any case, it is corrected, if it is less than the minimal width
+ * (but not if it is greater than the maximal width).
+ *
+ * \bug The parantheses is not fully clear, look at the old code.
+ *
+ * Details on differences because of styles are omitted. Below, this
+ * total width is called \f$W\f$.
+ *
+ * <h5>Evaluating percentages</h5>
+ *
+ * The following algorithms are used to solve collisions between
+ * different size specifications (absolute and percentage). Generally,
+ * inherent sizes and specified absolute sizes are preferred.
+ *
+ * <ol>
+ * <li> First, calculate the sum of the minimal widths, for columns, where
+ * no percentage width has been specified. The difference to the total
+ * width is at max available to the columns with percentage width
+ * specifications:
+ *
+ * \f[W_{\hbox{columns}_\%,\hbox{available}} = W - \sum e_{i,\min}\f]
+ *
+ * with only those columns \f$i\f$ with no percentage width specification.
+ *
+ * <li> Then, calculate the sum of the widths, which the columns with
+ * percentage width specification would allocate, when fully adhering to
+ * then:
+ *
+ * \f[W_{\hbox{columns}_\%,\hbox{best}} = W \sum w_{i,\%}\f]
+ *
+ * with only those columns \f$i\f$ with a percentage width specification.
+ *
+ * <li> Two cases are distinguished:
+ *
+ * <ul>
+ * <li> \f$W_{\hbox{columns}_\%,\hbox{available}} \ge
+ * W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the
+ * percentage widths can be used without any modification, by
+ * setting the extremes:
+ *
+ * \f[e_{i,\min} = e_{i,\max} = W w_{i,\%}\f]
+ *
+ * for only those columns \f$i\f$ with a percentage width
+ * specification.
+ *
+ * <li> \f$W_{\hbox{columns}_\%,\hbox{available}} <
+ * W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the widths
+ * for these columns must be cut down:
+ *
+ * \f[e_{i,\min} = e_{i,\max} =
+ * w_{i,\%}
+ * {W_{\hbox{columns}_\%,\hbox{available}} \over
+ * w_{\hbox{total},\%}}\f]
+ *
+ * with
+ *
+ * \f[w_{\hbox{total},\%} = \sum w_{i,\%}\f]
+ *
+ * in both cases for only those columns \f$i\f$ with a percentage
+ * width specification.
+ * </ul>
+ * </ol>
+ *
+ * (\f$e_{i,\min}\f$ and \f$e_{i,\max}\f$ are set \em temporarily here,
+ * the notation should be a bit clearer.)
+ *
+ *
+ * <h5>Column Widths</h5>
+ *
+ * The column widths are now simply calculated by applying the
+ * apportenment function.
+ *
+ *
+ * <h5>Row Heights</h5>
+ *
+ * ...
+ *
+ * <h3>Alternative Apportionment Algorithm</h3>
+ *
+ * The algorithm described here tends to result in more homogeneous column
+ * widths.
+ *
+ * The following rule lead to well-defined \f$w_{i}\f$: All columns
+ * \f$i\f$ have have the same width \f$w\f$, except:
+ * <ul>
+ * <li> \f$w < e_{i,\min}\f$, or
+ * <li> \f$w > e_{i,\max}\f$.
+ * </ul>
+ *
+ * Furthermore, \f$w\f$ is
+ * <ul>
+ * <li> less than all \f$e_{i,\min}\f$ of columns not having \f$w\f$ as
+ * width, and
+ * <li> greater than all \f$e_{i,\min}\f$ of columns not having \f$w\f$ as
+ * width.
+ * </ul>
+ *
+ * Of course, \f$\sum w_{i} = W\f$ must be the case.
+ *
+ * Based on an initial value \f$w = {W\over n}\f$, \f$w\f$ can iteratively
+ * adjusted, based on these rules.
+ *
+ *
+ * <h3>Borders, Paddings, Spacing</h3>
+ *
+ * Currently, DwTable supports only the separated borders model (see CSS
+ * specification). Borders, paddings, spacing is done by creating
+ * dw::core::style::Style structures with values equivalent to following CSS:
+ *
+ * <pre>
+ * TABLE {
+ * border: outset \em table-border;
+ * border-collapse: separate;
+ * border-spacing: \em table-cellspacing;
+ * background-color: \em table-bgcolor;
+ * }
+ *
+ * TD TH {
+ * border: inset \em table-border;
+ * padding: \em table-cellspacing;
+ * background-color: \em td/th-bgcolor;
+ * }
+ * </pre>
+ *
+ * Here, \em foo-bar refers to the attribute \em bar of the tag \em foo foo.
+ * Look at the HTML parser for more details.
+ */
+class Table: public core::Widget
+{
+private:
+
+ struct Child
+ {
+ enum {
+ CELL, // cell starts here
+ SPAN_SPACE // part of a spanning cell
+ } type;
+ union {
+ struct {
+ core::Widget *widget;
+ int colspanOrig, colspanEff, rowspan;
+ } cell;
+ struct {
+ int startCol, startRow; // where the cell starts
+ } spanSpace;
+ };
+ };
+
+ class TableIterator: public core::Iterator
+ {
+ private:
+ int index;
+
+ public:
+ TableIterator (Table *table, core::Content::Type mask, bool atEnd);
+ TableIterator (Table *table, core::Content::Type mask, int index);
+
+ object::Object *clone();
+ int compareTo(misc::Comparable *other);
+
+ bool next ();
+ bool prev ();
+ void highlight (int start, int end, core::HighlightLayer layer);
+ void unhighlight (int direction, core::HighlightLayer layer);
+ void getAllocation (int start, int end, core::Allocation *allocation);
+ };
+
+ friend class TableIterator;
+
+ bool limitTextWidth, rowClosed;
+ int availWidth, availAscent, availDescent; // set by set...
+
+ int numRows, numCols, curRow, curCol;
+ misc::SimpleVector<Child*> *children;
+
+ int redrawX, redrawY;
+
+ /**
+ * \brief The extremes of all columns.
+ */
+ misc::SimpleVector<core::Extremes> *colExtremes;
+
+ /**
+ * \brief The widths of all columns.
+ */
+ misc::SimpleVector<int> *colWidths;
+
+ /**
+ * Row cumulative height array: cumHeight->size() is numRows + 1,
+ * cumHeight->get(0) is 0, cumHeight->get(numRows) is the total table
+ * height.
+ */
+ misc::SimpleVector<int> *cumHeight;
+ /**
+ * If a Cell has rowspan > 1, it goes into this array
+ */
+ misc::SimpleVector<int> *rowSpanCells;
+ /**
+ * If a Cell has colspan > 1, it goes into this array
+ */
+ misc::SimpleVector<int> *colSpanCells;
+ misc::SimpleVector<int> *baseline;
+
+ misc::SimpleVector<core::style::Style*> *rowStyle;
+
+ /**
+ * hasColPercent becomes true when any cell specifies a percentage width.
+ * A negative value in colPercents means LEN_AUTO or LEN_ABS.
+ */
+ enum { LEN_AUTO = -1, LEN_ABS = -2};
+ int hasColPercent;
+ misc::SimpleVector<float> *colPercents;
+
+ inline bool childDefined(int n)
+ {
+ return n < children->size() && children->get(n) != NULL &&
+ children->get(n)->type != Child::SPAN_SPACE;
+ }
+
+ void reallocChildren (int newNumCols, int newNumRows);
+
+ void calcCellSizes ();
+ void forceCalcCellSizes ();
+ void apportionRowSpan ();
+
+ void calcColumnExtremes ();
+ void forceCalcColumnExtremes ();
+
+ void apportion2 (int totalWidth, int forceTotalWidth);
+ void apportion_percentages2 (int totalWidth, int forceTotalWidth);
+
+ void setCumHeight (int row, int value)
+ {
+ if (value != cumHeight->get (row)) {
+ redrawY = misc::min ( redrawY, value );
+ cumHeight->set (row, value);
+ }
+ }
+
+ inline void setColWidth (int col, int value)
+ {
+ if (value != colWidths->get (col)) {
+ redrawX = misc::min (redrawX, value);
+ colWidths->set (col, value);
+ }
+ }
+
+protected:
+ void sizeRequestImpl (core::Requisition *requisition);
+ void getExtremesImpl (core::Extremes *extremes);
+ void sizeAllocateImpl (core::Allocation *allocation);
+ void resizeDrawImpl ();
+
+ void setWidth (int width);
+ void setAscent (int ascent);
+ void setDescent (int descent);
+ void draw (core::View *view, core::Rectangle *area);
+
+ //bool buttonPressImpl (core::EventButton *event);
+ //bool buttonReleaseImpl (core::EventButton *event);
+ //bool motionNotifyImpl (core::EventMotion *event);
+
+ void removeChild (Widget *child);
+
+public:
+ static int CLASS_ID;
+
+ Table(bool limitTextWidth);
+ ~Table();
+
+ core::Iterator *iterator (core::Content::Type mask, bool atEnd);
+
+ void addCell (Widget *widget, int colspan, int rowspan);
+ void addRow (core::style::Style *style);
+ TableCell *getCellRef ();
+};
+
+} // namespace dw
+
+#endif // __DW_TABLE_HH__