2 * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
3 * (C) 2002 Dirk Mueller (mueller@kde.org)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 #include "FixedTableLayout.h"
25 #include "RenderTable.h"
26 #include "RenderTableCell.h"
27 #include "RenderTableCol.h"
28 #include "RenderTableSection.h"
31 The text below is from the CSS 2.1 specs.
35 With this (fast) algorithm, the horizontal layout of the table does
36 not depend on the contents of the cells; it only depends on the
37 table's width, the width of the columns, and borders or cell
40 The table's width may be specified explicitly with the 'width'
41 property. A value of 'auto' (for both 'display: table' and 'display:
42 inline-table') means use the automatic table layout algorithm.
44 In the fixed table layout algorithm, the width of each column is
45 determined as follows:
47 1. A column element with a value other than 'auto' for the 'width'
48 property sets the width for that column.
50 2. Otherwise, a cell in the first row with a value other than
51 'auto' for the 'width' property sets the width for that column. If
52 the cell spans more than one column, the width is divided over the
55 3. Any remaining columns equally divide the remaining horizontal
56 table space (minus borders or cell spacing).
58 The width of the table is then the greater of the value of the
59 'width' property for the table element and the sum of the column
60 widths (plus cell spacing or borders). If the table is wider than
61 the columns, the extra space should be distributed over the columns.
64 In this manner, the user agent can begin to lay out the table once
65 the entire first row has been received. Cells in subsequent rows do
66 not affect column widths. Any cell that has content that overflows
67 uses the 'overflow' property to determine whether to clip the
75 FixedTableLayout::FixedTableLayout(RenderTable* table)
80 int FixedTableLayout::calcWidthArray(int)
84 // iterate over all <col> elements
85 unsigned nEffCols = m_table->numEffCols();
86 m_width.resize(nEffCols);
87 m_width.fill(Length(Auto));
89 unsigned currentEffectiveColumn = 0;
90 for (RenderTableCol* col = m_table->firstColumn(); col; col = col->nextColumn()) {
91 col->computePreferredLogicalWidths();
93 // Width specified by column-groups that have column child does not affect column width in fixed layout tables
94 if (col->isTableColumnGroupWithColumnChildren())
97 Length colStyleLogicalWidth = col->style()->logicalWidth();
98 int effectiveColWidth = 0;
99 if (colStyleLogicalWidth.isFixed() && colStyleLogicalWidth.value() > 0)
100 effectiveColWidth = colStyleLogicalWidth.value();
102 unsigned span = col->span();
104 unsigned spanInCurrentEffectiveColumn;
105 if (currentEffectiveColumn >= nEffCols) {
106 m_table->appendColumn(span);
108 m_width.append(Length());
109 spanInCurrentEffectiveColumn = span;
111 if (span < m_table->spanOfEffCol(currentEffectiveColumn)) {
112 m_table->splitColumn(currentEffectiveColumn, span);
114 m_width.append(Length());
116 spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn);
118 if ((colStyleLogicalWidth.isFixed() || colStyleLogicalWidth.isPercent()) && colStyleLogicalWidth.isPositive()) {
119 m_width[currentEffectiveColumn] = colStyleLogicalWidth;
120 m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn;
121 usedWidth += effectiveColWidth * spanInCurrentEffectiveColumn;
123 span -= spanInCurrentEffectiveColumn;
124 currentEffectiveColumn++;
128 // Iterate over the first row in case some are unspecified.
129 RenderTableSection* section = m_table->topNonEmptySection();
133 unsigned currentColumn = 0;
135 RenderObject* firstRow = section->firstChild();
136 for (RenderObject* child = firstRow->firstChild(); child; child = child->nextSibling()) {
137 if (!child->isTableCell())
140 RenderTableCell* cell = toRenderTableCell(child);
141 if (cell->preferredLogicalWidthsDirty())
142 cell->computePreferredLogicalWidths();
144 Length logicalWidth = cell->styleOrColLogicalWidth();
145 unsigned span = cell->colSpan();
146 int fixedBorderBoxLogicalWidth = 0;
147 if (logicalWidth.isFixed() && logicalWidth.isPositive()) {
148 fixedBorderBoxLogicalWidth = cell->computeBorderBoxLogicalWidth(logicalWidth.value());
149 logicalWidth.setValue(fixedBorderBoxLogicalWidth);
152 unsigned usedSpan = 0;
153 while (usedSpan < span && currentColumn < nEffCols) {
154 float eSpan = m_table->spanOfEffCol(currentColumn);
155 // Only set if no col element has already set it.
156 if (m_width[currentColumn].isAuto() && logicalWidth.type() != Auto) {
157 m_width[currentColumn] = logicalWidth;
158 m_width[currentColumn] *= eSpan / span;
159 usedWidth += fixedBorderBoxLogicalWidth * eSpan / span;
169 void FixedTableLayout::computePreferredLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth)
171 // FIXME: This entire calculation is incorrect for both minwidth and maxwidth.
173 // we might want to wait until we have all of the first row before
174 // layouting for the first time.
176 // only need to calculate the minimum width as the sum of the
177 // cols/cells with a fixed width.
179 // The maximum width is max(minWidth, tableWidth).
180 int bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection();
182 int tableLogicalWidth = m_table->style()->logicalWidth().isFixed() ? m_table->style()->logicalWidth().value() - bordersPaddingAndSpacing : 0;
183 int mw = calcWidthArray(tableLogicalWidth) + bordersPaddingAndSpacing;
185 minWidth = max(mw, tableLogicalWidth);
188 // This quirk is very similar to one that exists in RenderBlock::calcBlockPrefWidths().
189 // Here's the example for this one:
191 <table style="width:100%; background-color:red"><tr><td>
192 <table style="background-color:blue"><tr><td>
193 <table style="width:100%; background-color:green; table-layout:fixed"><tr><td>
199 // In this example, the two inner tables should be as large as the outer table.
200 // We can achieve this effect by making the maxwidth of fixed tables with percentage
201 // widths be infinite.
202 if (m_table->document()->inQuirksMode() && m_table->style()->logicalWidth().isPercent() && maxWidth < tableMaxWidth)
203 maxWidth = tableMaxWidth;
206 void FixedTableLayout::layout()
208 int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection();
209 unsigned nEffCols = m_table->numEffCols();
211 // FIXME: It is possible to be called without having properly updated our internal representation.
212 // This means that our preferred logical widths were not recomputed as expected.
213 if (nEffCols != m_width.size()) {
214 calcWidthArray(tableLogicalWidth);
215 // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups).
216 nEffCols = m_table->numEffCols();
219 Vector<int> calcWidth(nEffCols, 0);
221 unsigned numAuto = 0;
222 unsigned autoSpan = 0;
223 int totalFixedWidth = 0;
224 int totalPercentWidth = 0;
225 float totalPercent = 0;
227 // Compute requirements and try to satisfy fixed and percent widths.
228 // Percentages are of the table's width, so for example
229 // for a table width of 100px with columns (40px, 10%), the 10% compute
230 // to 10px here, and will scale up to 20px in the final (80px, 20px).
231 for (unsigned i = 0; i < nEffCols; i++) {
232 if (m_width[i].isFixed()) {
233 calcWidth[i] = m_width[i].value();
234 totalFixedWidth += calcWidth[i];
235 } else if (m_width[i].isPercent()) {
236 calcWidth[i] = valueForLength(m_width[i], tableLogicalWidth);
237 totalPercentWidth += calcWidth[i];
238 totalPercent += m_width[i].percent();
239 } else if (m_width[i].isAuto()) {
241 autoSpan += m_table->spanOfEffCol(i);
245 int hspacing = m_table->hBorderSpacing();
246 int totalWidth = totalFixedWidth + totalPercentWidth;
247 if (!numAuto || totalWidth > tableLogicalWidth) {
248 // If there are no auto columns, or if the total is too wide, take
249 // what we have and scale it to fit as necessary.
250 if (totalWidth != tableLogicalWidth) {
251 // Fixed widths only scale up
252 if (totalFixedWidth && totalWidth < tableLogicalWidth) {
254 for (unsigned i = 0; i < nEffCols; i++) {
255 if (m_width[i].isFixed()) {
256 calcWidth[i] = calcWidth[i] * tableLogicalWidth / totalWidth;
257 totalFixedWidth += calcWidth[i];
262 totalPercentWidth = 0;
263 for (unsigned i = 0; i < nEffCols; i++) {
264 if (m_width[i].isPercent()) {
265 calcWidth[i] = m_width[i].percent() * (tableLogicalWidth - totalFixedWidth) / totalPercent;
266 totalPercentWidth += calcWidth[i];
270 totalWidth = totalFixedWidth + totalPercentWidth;
273 // Divide the remaining width among the auto columns.
274 ASSERT(autoSpan >= numAuto);
275 int remainingWidth = tableLogicalWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto);
277 for (unsigned i = 0; i < nEffCols; i++) {
278 if (m_width[i].isAuto()) {
279 unsigned span = m_table->spanOfEffCol(i);
280 int w = remainingWidth * span / autoSpan;
281 calcWidth[i] = w + hspacing * (span - 1);
287 ASSERT(autoSpan >= span);
291 // Last one gets the remainder.
293 calcWidth[lastAuto] += remainingWidth;
294 totalWidth = tableLogicalWidth;
297 if (totalWidth < tableLogicalWidth) {
298 // Spread extra space over columns.
299 int remainingWidth = tableLogicalWidth - totalWidth;
300 int total = nEffCols;
302 int w = remainingWidth / total;
304 calcWidth[--total] += w;
307 calcWidth[nEffCols - 1] += remainingWidth;
311 for (unsigned i = 0; i < nEffCols; i++) {
312 m_table->columnPositions()[i] = pos;
313 pos += calcWidth[i] + hspacing;
315 int colPositionsSize = m_table->columnPositions().size();
316 if (colPositionsSize > 0)
317 m_table->columnPositions()[colPositionsSize - 1] = pos;
320 } // namespace WebCore