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 RenderObject* child = m_table->firstChild();
86 int nEffCols = m_table->numEffCols();
87 m_width.resize(nEffCols);
88 m_width.fill(Length(Auto));
90 int currentEffectiveColumn = 0;
92 while (child && child->isTableCol()) {
93 RenderTableCol* col = toRenderTableCol(child);
94 if (col->firstChild())
95 grpWidth = col->style()->logicalWidth();
97 Length w = col->style()->logicalWidth();
101 if (w.isFixed() && w.value() > 0)
102 effWidth = w.value();
104 int span = col->span();
106 int spanInCurrentEffectiveColumn;
107 if (currentEffectiveColumn >= nEffCols) {
108 m_table->appendColumn(span);
110 m_width.append(Length());
111 spanInCurrentEffectiveColumn = span;
113 if (span < m_table->spanOfEffCol(currentEffectiveColumn)) {
114 m_table->splitColumn(currentEffectiveColumn, span);
116 m_width.append(Length());
118 spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn);
120 if ((w.isFixed() || w.isPercent()) && w.isPositive()) {
121 m_width[currentEffectiveColumn] = w;
122 m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn;
123 usedWidth += effWidth * spanInCurrentEffectiveColumn;
125 span -= spanInCurrentEffectiveColumn;
126 currentEffectiveColumn++;
129 col->computePreferredLogicalWidths();
131 RenderObject* next = child->firstChild();
133 next = child->nextSibling();
134 if (!next && child->parent()->isTableCol()) {
135 next = child->parent()->nextSibling();
141 // Iterate over the first row in case some are unspecified.
142 RenderTableSection* section = m_table->topNonEmptySection();
145 RenderObject* firstRow = section->firstChild();
146 child = firstRow->firstChild();
148 if (child->isTableCell()) {
149 RenderTableCell* cell = toRenderTableCell(child);
150 if (cell->preferredLogicalWidthsDirty())
151 cell->computePreferredLogicalWidths();
153 Length w = cell->styleOrColLogicalWidth();
154 int span = cell->colSpan();
156 if (w.isFixed() && w.isPositive())
157 effWidth = w.value();
161 while (usedSpan < span && cCol + i < nEffCols) {
162 float eSpan = m_table->spanOfEffCol(cCol + i);
163 // Only set if no col element has already set it.
164 if (m_width[cCol + i].isAuto() && w.type() != Auto) {
165 m_width[cCol + i] = w;
166 m_width[cCol + i] *= eSpan / span;
167 usedWidth += effWidth * eSpan / span;
174 child = child->nextSibling();
181 // Use a very large value (in effect infinite). But not too large!
182 // numeric_limits<int>::max() will too easily overflow widths.
183 // Keep this in synch with BLOCK_MAX_WIDTH in RenderBlock.cpp
184 #define TABLE_MAX_WIDTH 15000
186 void FixedTableLayout::computePreferredLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth)
188 // FIXME: This entire calculation is incorrect for both minwidth and maxwidth.
190 // we might want to wait until we have all of the first row before
191 // layouting for the first time.
193 // only need to calculate the minimum width as the sum of the
194 // cols/cells with a fixed width.
196 // The maximum width is max(minWidth, tableWidth).
197 LayoutUnit bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection();
199 LayoutUnit tableLogicalWidth = m_table->style()->logicalWidth().isFixed() ? m_table->style()->logicalWidth().value() - bordersPaddingAndSpacing : 0;
200 LayoutUnit mw = calcWidthArray(tableLogicalWidth) + bordersPaddingAndSpacing;
202 minWidth = max(mw, tableLogicalWidth);
205 // This quirk is very similar to one that exists in RenderBlock::calcBlockPrefWidths().
206 // Here's the example for this one:
208 <table style="width:100%; background-color:red"><tr><td>
209 <table style="background-color:blue"><tr><td>
210 <table style="width:100%; background-color:green; table-layout:fixed"><tr><td>
216 // In this example, the two inner tables should be as large as the outer table.
217 // We can achieve this effect by making the maxwidth of fixed tables with percentage
218 // widths be infinite.
219 if (m_table->document()->inQuirksMode() && m_table->style()->logicalWidth().isPercent() && maxWidth < TABLE_MAX_WIDTH)
220 maxWidth = TABLE_MAX_WIDTH;
223 void FixedTableLayout::layout()
225 LayoutUnit tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection();
226 int nEffCols = m_table->numEffCols();
227 Vector<int> calcWidth(nEffCols, 0);
230 LayoutUnit autoSpan = 0;
231 LayoutUnit totalFixedWidth = 0;
232 LayoutUnit totalPercentWidth = 0;
233 float totalPercent = 0;
235 // Compute requirements and try to satisfy fixed and percent widths.
236 // Percentages are of the table's width, so for example
237 // for a table width of 100px with columns (40px, 10%), the 10% compute
238 // to 10px here, and will scale up to 20px in the final (80px, 20px).
239 for (int i = 0; i < nEffCols; i++) {
240 if (m_width[i].isFixed()) {
241 calcWidth[i] = m_width[i].value();
242 totalFixedWidth += calcWidth[i];
243 } else if (m_width[i].isPercent()) {
244 calcWidth[i] = m_width[i].calcValue(tableLogicalWidth);
245 totalPercentWidth += calcWidth[i];
246 totalPercent += m_width[i].percent();
247 } else if (m_width[i].isAuto()) {
249 autoSpan += m_table->spanOfEffCol(i);
253 LayoutUnit hspacing = m_table->hBorderSpacing();
254 LayoutUnit totalWidth = totalFixedWidth + totalPercentWidth;
255 if (!numAuto || totalWidth > tableLogicalWidth) {
256 // If there are no auto columns, or if the total is too wide, take
257 // what we have and scale it to fit as necessary.
258 if (totalWidth != tableLogicalWidth) {
259 // Fixed widths only scale up
260 if (totalFixedWidth && totalWidth < tableLogicalWidth) {
262 for (int i = 0; i < nEffCols; i++) {
263 if (m_width[i].isFixed()) {
264 calcWidth[i] = calcWidth[i] * tableLogicalWidth / totalWidth;
265 totalFixedWidth += calcWidth[i];
270 totalPercentWidth = 0;
271 for (int i = 0; i < nEffCols; i++) {
272 if (m_width[i].isPercent()) {
273 calcWidth[i] = m_width[i].percent() * (tableLogicalWidth - totalFixedWidth) / totalPercent;
274 totalPercentWidth += calcWidth[i];
278 totalWidth = totalFixedWidth + totalPercentWidth;
281 // Divide the remaining width among the auto columns.
282 LayoutUnit remainingWidth = tableLogicalWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto);
284 for (int i = 0; i < nEffCols; i++) {
285 if (m_width[i].isAuto()) {
286 LayoutUnit span = m_table->spanOfEffCol(i);
287 LayoutUnit w = remainingWidth * span / autoSpan;
288 calcWidth[i] = w + hspacing * (span - 1);
297 // Last one gets the remainder.
299 calcWidth[lastAuto] += remainingWidth;
300 totalWidth = tableLogicalWidth;
303 if (totalWidth < tableLogicalWidth) {
304 // Spread extra space over columns.
305 LayoutUnit remainingWidth = tableLogicalWidth - totalWidth;
306 int total = nEffCols;
308 LayoutUnit w = remainingWidth / total;
310 calcWidth[--total] += w;
313 calcWidth[nEffCols - 1] += remainingWidth;
317 for (int i = 0; i < nEffCols; i++) {
318 m_table->columnPositions()[i] = pos;
319 pos += calcWidth[i] + hspacing;
321 LayoutUnit colPositionsSize = m_table->columnPositions().size();
322 if (colPositionsSize > 0)
323 m_table->columnPositions()[colPositionsSize - 1] = pos;
326 } // namespace WebCore