1 /*
2 * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
3 * (C) 2002 Dirk Mueller (mueller@kde.org)
4 * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved.
5 *
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.
10 *
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.
15 *
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.
20 */
21
22 #include "config.h"
23 #include "AutoTableLayout.h"
24
25 #include "RenderTable.h"
26 #include "RenderTableCell.h"
27 #include "RenderTableCol.h"
28 #include "RenderTableSection.h"
29
30 using namespace std;
31
32 namespace WebCore {
33
AutoTableLayout(RenderTable * table)34 AutoTableLayout::AutoTableLayout(RenderTable* table)
35 : TableLayout(table)
36 , m_hasPercent(false)
37 , m_percentagesDirty(true)
38 , m_effWidthDirty(true)
39 , m_totalPercent(0)
40 {
41 }
42
~AutoTableLayout()43 AutoTableLayout::~AutoTableLayout()
44 {
45 }
46
47 /* recalculates the full structure needed to do layouting and minmax calculations.
48 This is usually calculated on the fly, but needs to be done fully when table cells change
49 dynamically
50 */
recalcColumn(int effCol)51 void AutoTableLayout::recalcColumn(int effCol)
52 {
53 Layout &l = m_layoutStruct[effCol];
54
55 RenderObject* child = m_table->firstChild();
56 // first we iterate over all rows.
57
58 RenderTableCell* fixedContributor = 0;
59 RenderTableCell* maxContributor = 0;
60
61 while (child) {
62 if (child->isTableCol())
63 toRenderTableCol(child)->calcPrefWidths();
64 else if (child->isTableSection()) {
65 RenderTableSection* section = toRenderTableSection(child);
66 int numRows = section->numRows();
67 RenderTableCell* last = 0;
68 for (int i = 0; i < numRows; i++) {
69 RenderTableSection::CellStruct current = section->cellAt(i, effCol);
70 RenderTableCell* cell = current.cell;
71
72 bool cellHasContent = cell && (cell->firstChild() || cell->style()->hasBorder() || cell->style()->hasPadding());
73 if (cellHasContent)
74 l.emptyCellsOnly = false;
75
76 if (current.inColSpan)
77 continue;
78 if (cell && cell->colSpan() == 1) {
79 // A cell originates in this column. Ensure we have
80 // a min/max width of at least 1px for this column now.
81 l.minWidth = max(l.minWidth, cellHasContent ? 1 : 0);
82 l.maxWidth = max(l.maxWidth, 1);
83 if (cell->prefWidthsDirty())
84 cell->calcPrefWidths();
85 l.minWidth = max(cell->minPrefWidth(), l.minWidth);
86 if (cell->maxPrefWidth() > l.maxWidth) {
87 l.maxWidth = cell->maxPrefWidth();
88 maxContributor = cell;
89 }
90
91 Length w = cell->styleOrColWidth();
92 // FIXME: What is this arbitrary value?
93 if (w.rawValue() > 32760)
94 w.setRawValue(32760);
95 if (w.isNegative())
96 w.setValue(0);
97 switch (w.type()) {
98 case Fixed:
99 // ignore width=0
100 if (w.value() > 0 && (int)l.width.type() != Percent) {
101 int wval = cell->calcBorderBoxWidth(w.value());
102 if (l.width.isFixed()) {
103 // Nav/IE weirdness
104 if ((wval > l.width.value()) ||
105 ((l.width.value() == wval) && (maxContributor == cell))) {
106 l.width.setValue(wval);
107 fixedContributor = cell;
108 }
109 } else {
110 l.width.setValue(Fixed, wval);
111 fixedContributor = cell;
112 }
113 }
114 break;
115 case Percent:
116 m_hasPercent = true;
117 if (w.isPositive() && (!l.width.isPercent() || w.rawValue() > l.width.rawValue()))
118 l.width = w;
119 break;
120 case Relative:
121 // FIXME: Need to understand this case and whether it makes sense to compare values
122 // which are not necessarily of the same type.
123 if (w.isAuto() || (w.isRelative() && w.value() > l.width.rawValue()))
124 l.width = w;
125 default:
126 break;
127 }
128 } else {
129 if (cell && (!effCol || section->cellAt(i, effCol-1).cell != cell)) {
130 // This spanning cell originates in this column. Ensure we have
131 // a min/max width of at least 1px for this column now.
132 l.minWidth = max(l.minWidth, cellHasContent ? 1 : 0);
133 l.maxWidth = max(l.maxWidth, 1);
134 insertSpanCell(cell);
135 }
136 last = cell;
137 }
138 }
139 }
140 child = child->nextSibling();
141 }
142
143 // Nav/IE weirdness
144 if (l.width.isFixed()) {
145 if (m_table->style()->htmlHacks() && l.maxWidth > l.width.value() && fixedContributor != maxContributor) {
146 l.width = Length();
147 fixedContributor = 0;
148 }
149 }
150
151 l.maxWidth = max(l.maxWidth, l.minWidth);
152
153 // ### we need to add col elements as well
154 }
155
fullRecalc()156 void AutoTableLayout::fullRecalc()
157 {
158 m_percentagesDirty = true;
159 m_hasPercent = false;
160 m_effWidthDirty = true;
161
162 int nEffCols = m_table->numEffCols();
163 m_layoutStruct.resize(nEffCols);
164 m_layoutStruct.fill(Layout());
165 m_spanCells.fill(0);
166
167 RenderObject *child = m_table->firstChild();
168 Length grpWidth;
169 int cCol = 0;
170 while (child) {
171 if (child->isTableCol()) {
172 RenderTableCol *col = toRenderTableCol(child);
173 int span = col->span();
174 if (col->firstChild()) {
175 grpWidth = col->style()->width();
176 } else {
177 Length w = col->style()->width();
178 if (w.isAuto())
179 w = grpWidth;
180 if ((w.isFixed() || w.isPercent()) && w.isZero())
181 w = Length();
182 int cEffCol = m_table->colToEffCol(cCol);
183 if (!w.isAuto() && span == 1 && cEffCol < nEffCols) {
184 if (m_table->spanOfEffCol(cEffCol) == 1) {
185 m_layoutStruct[cEffCol].width = w;
186 if (w.isFixed() && m_layoutStruct[cEffCol].maxWidth < w.value())
187 m_layoutStruct[cEffCol].maxWidth = w.value();
188 }
189 }
190 cCol += span;
191 }
192 } else {
193 break;
194 }
195
196 RenderObject *next = child->firstChild();
197 if (!next)
198 next = child->nextSibling();
199 if (!next && child->parent()->isTableCol()) {
200 next = child->parent()->nextSibling();
201 grpWidth = Length();
202 }
203 child = next;
204 }
205
206
207 for (int i = 0; i < nEffCols; i++)
208 recalcColumn(i);
209 }
210
shouldScaleColumns(RenderTable * table)211 static bool shouldScaleColumns(RenderTable* table)
212 {
213 // A special case. If this table is not fixed width and contained inside
214 // a cell, then don't bloat the maxwidth by examining percentage growth.
215 bool scale = true;
216 while (table) {
217 Length tw = table->style()->width();
218 if ((tw.isAuto() || tw.isPercent()) && !table->isPositioned()) {
219 RenderBlock* cb = table->containingBlock();
220 while (cb && !cb->isRenderView() && !cb->isTableCell() &&
221 cb->style()->width().isAuto() && !cb->isPositioned())
222 cb = cb->containingBlock();
223
224 table = 0;
225 if (cb && cb->isTableCell() &&
226 (cb->style()->width().isAuto() || cb->style()->width().isPercent())) {
227 if (tw.isPercent())
228 scale = false;
229 else {
230 RenderTableCell* cell = toRenderTableCell(cb);
231 if (cell->colSpan() > 1 || cell->table()->style()->width().isAuto())
232 scale = false;
233 else
234 table = cell->table();
235 }
236 }
237 }
238 else
239 table = 0;
240 }
241 return scale;
242 }
243
calcPrefWidths(int & minWidth,int & maxWidth)244 void AutoTableLayout::calcPrefWidths(int& minWidth, int& maxWidth)
245 {
246 fullRecalc();
247
248 int spanMaxWidth = calcEffectiveWidth();
249 minWidth = 0;
250 maxWidth = 0;
251 float maxPercent = 0;
252 float maxNonPercent = 0;
253 bool scaleColumns = shouldScaleColumns(m_table);
254
255 // We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero.
256 // FIXME: Handle the 0% cases properly.
257 const int epsilon = 1;
258
259 int remainingPercent = 100 * percentScaleFactor;
260 for (unsigned int i = 0; i < m_layoutStruct.size(); i++) {
261 minWidth += m_layoutStruct[i].effMinWidth;
262 maxWidth += m_layoutStruct[i].effMaxWidth;
263 if (scaleColumns) {
264 if (m_layoutStruct[i].effWidth.isPercent()) {
265 int percent = min(m_layoutStruct[i].effWidth.rawValue(), remainingPercent);
266 float pw = static_cast<float>(m_layoutStruct[i].effMaxWidth) * 100 * percentScaleFactor / max(percent, epsilon);
267 maxPercent = max(pw, maxPercent);
268 remainingPercent -= percent;
269 } else
270 maxNonPercent += m_layoutStruct[i].effMaxWidth;
271 }
272 }
273
274 if (scaleColumns) {
275 maxNonPercent = maxNonPercent * 100 * percentScaleFactor / max(remainingPercent, epsilon);
276 maxWidth = max(maxWidth, static_cast<int>(min(maxNonPercent, INT_MAX / 2.0f)));
277 maxWidth = max(maxWidth, static_cast<int>(min(maxPercent, INT_MAX / 2.0f)));
278 }
279
280 maxWidth = max(maxWidth, spanMaxWidth);
281
282 int bs = m_table->bordersPaddingAndSpacing();
283 minWidth += bs;
284 maxWidth += bs;
285
286 Length tw = m_table->style()->width();
287 if (tw.isFixed() && tw.value() > 0) {
288 minWidth = max(minWidth, tw.value());
289 maxWidth = minWidth;
290 }
291 }
292
293 /*
294 This method takes care of colspans.
295 effWidth is the same as width for cells without colspans. If we have colspans, they get modified.
296 */
calcEffectiveWidth()297 int AutoTableLayout::calcEffectiveWidth()
298 {
299 float tMaxWidth = 0;
300
301 unsigned int nEffCols = m_layoutStruct.size();
302 int hspacing = m_table->hBorderSpacing();
303
304 for (unsigned int i = 0; i < nEffCols; i++) {
305 m_layoutStruct[i].effWidth = m_layoutStruct[i].width;
306 m_layoutStruct[i].effMinWidth = m_layoutStruct[i].minWidth;
307 m_layoutStruct[i].effMaxWidth = m_layoutStruct[i].maxWidth;
308 }
309
310 for (unsigned int i = 0; i < m_spanCells.size(); i++) {
311 RenderTableCell *cell = m_spanCells[i];
312 if (!cell)
313 break;
314 int span = cell->colSpan();
315
316 Length w = cell->styleOrColWidth();
317 if (!w.isRelative() && w.isZero())
318 w = Length(); // make it Auto
319
320 int col = m_table->colToEffCol(cell->col());
321 unsigned int lastCol = col;
322 int cMinWidth = cell->minPrefWidth() + hspacing;
323 float cMaxWidth = cell->maxPrefWidth() + hspacing;
324 int totalPercent = 0;
325 int minWidth = 0;
326 float maxWidth = 0;
327 bool allColsArePercent = true;
328 bool allColsAreFixed = true;
329 bool haveAuto = false;
330 bool spanHasEmptyCellsOnly = true;
331 int fixedWidth = 0;
332 while (lastCol < nEffCols && span > 0) {
333 switch (m_layoutStruct[lastCol].width.type()) {
334 case Percent:
335 totalPercent += m_layoutStruct[lastCol].width.rawValue();
336 allColsAreFixed = false;
337 break;
338 case Fixed:
339 if (m_layoutStruct[lastCol].width.value() > 0) {
340 fixedWidth += m_layoutStruct[lastCol].width.value();
341 allColsArePercent = false;
342 // IE resets effWidth to Auto here, but this breaks the konqueror about page and seems to be some bad
343 // legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither.
344 break;
345 }
346 // fall through
347 case Auto:
348 haveAuto = true;
349 // fall through
350 default:
351 // If the column is a percentage width, do not let the spanning cell overwrite the
352 // width value. This caused a mis-rendering on amazon.com.
353 // Sample snippet:
354 // <table border=2 width=100%><
355 // <tr><td>1</td><td colspan=2>2-3</tr>
356 // <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr>
357 // </table>
358 if (!m_layoutStruct[lastCol].effWidth.isPercent()) {
359 m_layoutStruct[lastCol].effWidth = Length();
360 allColsArePercent = false;
361 }
362 else
363 totalPercent += m_layoutStruct[lastCol].effWidth.rawValue();
364 allColsAreFixed = false;
365 }
366 if (!m_layoutStruct[lastCol].emptyCellsOnly)
367 spanHasEmptyCellsOnly = false;
368 span -= m_table->spanOfEffCol(lastCol);
369 minWidth += m_layoutStruct[lastCol].effMinWidth;
370 maxWidth += m_layoutStruct[lastCol].effMaxWidth;
371 lastCol++;
372 cMinWidth -= hspacing;
373 cMaxWidth -= hspacing;
374 }
375
376 // adjust table max width if needed
377 if (w.isPercent()) {
378 if (totalPercent > w.rawValue() || allColsArePercent) {
379 // can't satify this condition, treat as variable
380 w = Length();
381 } else {
382 float spanMax = max(maxWidth, cMaxWidth);
383 tMaxWidth = max(tMaxWidth, spanMax * 100 * percentScaleFactor / w.rawValue());
384
385 // all non percent columns in the span get percent vlaues to sum up correctly.
386 int percentMissing = w.rawValue() - totalPercent;
387 float totalWidth = 0;
388 for (unsigned int pos = col; pos < lastCol; pos++) {
389 if (!(m_layoutStruct[pos].effWidth.isPercent()))
390 totalWidth += m_layoutStruct[pos].effMaxWidth;
391 }
392
393 for (unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++) {
394 if (!(m_layoutStruct[pos].effWidth.isPercent())) {
395 int percent = static_cast<int>(percentMissing * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / totalWidth);
396 totalWidth -= m_layoutStruct[pos].effMaxWidth;
397 percentMissing -= percent;
398 if (percent > 0)
399 m_layoutStruct[pos].effWidth.setRawValue(Percent, percent);
400 else
401 m_layoutStruct[pos].effWidth = Length();
402 }
403 }
404
405 }
406 }
407
408 // make sure minWidth and maxWidth of the spanning cell are honoured
409 if (cMinWidth > minWidth) {
410 if (allColsAreFixed) {
411 for (unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++) {
412 int w = max(m_layoutStruct[pos].effMinWidth, cMinWidth * m_layoutStruct[pos].width.value() / fixedWidth);
413 fixedWidth -= m_layoutStruct[pos].width.value();
414 cMinWidth -= w;
415 m_layoutStruct[pos].effMinWidth = w;
416 }
417
418 } else {
419 float maxw = maxWidth;
420 int minw = minWidth;
421
422 // Give min to variable first, to fixed second, and to others third.
423 for (unsigned int pos = col; maxw >= 0 && pos < lastCol; pos++) {
424 if (m_layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth) {
425 int w = max(m_layoutStruct[pos].effMinWidth, m_layoutStruct[pos].width.value());
426 fixedWidth -= m_layoutStruct[pos].width.value();
427 minw -= m_layoutStruct[pos].effMinWidth;
428 maxw -= m_layoutStruct[pos].effMaxWidth;
429 cMinWidth -= w;
430 m_layoutStruct[pos].effMinWidth = w;
431 }
432 }
433
434 for (unsigned int pos = col; maxw >= 0 && pos < lastCol && minw < cMinWidth; pos++) {
435 if (!(m_layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth)) {
436 int w = max(m_layoutStruct[pos].effMinWidth, static_cast<int>(maxw ? cMinWidth * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / maxw : cMinWidth));
437 w = min(m_layoutStruct[pos].effMinWidth+(cMinWidth-minw), w);
438
439 maxw -= m_layoutStruct[pos].effMaxWidth;
440 minw -= m_layoutStruct[pos].effMinWidth;
441 cMinWidth -= w;
442 m_layoutStruct[pos].effMinWidth = w;
443 }
444 }
445 }
446 }
447 if (!(w.isPercent())) {
448 if (cMaxWidth > maxWidth) {
449 for (unsigned int pos = col; maxWidth >= 0 && pos < lastCol; pos++) {
450 int w = max(m_layoutStruct[pos].effMaxWidth, static_cast<int>(maxWidth ? cMaxWidth * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / maxWidth : cMaxWidth));
451 maxWidth -= m_layoutStruct[pos].effMaxWidth;
452 cMaxWidth -= w;
453 m_layoutStruct[pos].effMaxWidth = w;
454 }
455 }
456 } else {
457 for (unsigned int pos = col; pos < lastCol; pos++)
458 m_layoutStruct[pos].maxWidth = max(m_layoutStruct[pos].maxWidth, m_layoutStruct[pos].minWidth);
459 }
460 // treat span ranges consisting of empty cells only as if they had content
461 if (spanHasEmptyCellsOnly)
462 for (unsigned int pos = col; pos < lastCol; pos++)
463 m_layoutStruct[pos].emptyCellsOnly = false;
464 }
465 m_effWidthDirty = false;
466
467 return static_cast<int>(min(tMaxWidth, INT_MAX / 2.0f));
468 }
469
470 /* gets all cells that originate in a column and have a cellspan > 1
471 Sorts them by increasing cellspan
472 */
insertSpanCell(RenderTableCell * cell)473 void AutoTableLayout::insertSpanCell(RenderTableCell *cell)
474 {
475 if (!cell || cell->colSpan() == 1)
476 return;
477
478 int size = m_spanCells.size();
479 if (!size || m_spanCells[size-1] != 0) {
480 m_spanCells.grow(size + 10);
481 for (int i = 0; i < 10; i++)
482 m_spanCells[size+i] = 0;
483 size += 10;
484 }
485
486 // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better
487 unsigned int pos = 0;
488 int span = cell->colSpan();
489 while (pos < m_spanCells.size() && m_spanCells[pos] && span > m_spanCells[pos]->colSpan())
490 pos++;
491 memmove(m_spanCells.data()+pos+1, m_spanCells.data()+pos, (size-pos-1)*sizeof(RenderTableCell *));
492 m_spanCells[pos] = cell;
493 }
494
495
layout()496 void AutoTableLayout::layout()
497 {
498 #ifdef ANDROID_LAYOUT
499 if (m_table->isSingleColumn())
500 return;
501 #endif
502 // table layout based on the values collected in the layout structure.
503 int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing();
504 int available = tableWidth;
505 int nEffCols = m_table->numEffCols();
506
507 if (nEffCols != (int)m_layoutStruct.size()) {
508 fullRecalc();
509 nEffCols = m_table->numEffCols();
510 }
511
512 if (m_effWidthDirty)
513 calcEffectiveWidth();
514
515 bool havePercent = false;
516 bool haveRelative = false;
517 int totalRelative = 0;
518 int numAuto = 0;
519 int numFixed = 0;
520 float totalAuto = 0;
521 float totalFixed = 0;
522 int totalPercent = 0;
523 int allocAuto = 0;
524 int numAutoEmptyCellsOnly = 0;
525
526 // fill up every cell with its minWidth
527 for (int i = 0; i < nEffCols; i++) {
528 int w = m_layoutStruct[i].effMinWidth;
529 m_layoutStruct[i].calcWidth = w;
530 available -= w;
531 Length& width = m_layoutStruct[i].effWidth;
532 switch (width.type()) {
533 case Percent:
534 havePercent = true;
535 totalPercent += width.rawValue();
536 break;
537 case Relative:
538 haveRelative = true;
539 totalRelative += width.value();
540 break;
541 case Fixed:
542 numFixed++;
543 totalFixed += m_layoutStruct[i].effMaxWidth;
544 // fall through
545 break;
546 case Auto:
547 case Static:
548 if (m_layoutStruct[i].emptyCellsOnly)
549 numAutoEmptyCellsOnly++;
550 else {
551 numAuto++;
552 totalAuto += m_layoutStruct[i].effMaxWidth;
553 allocAuto += w;
554 }
555 break;
556 default:
557 break;
558 }
559 }
560
561 // allocate width to percent cols
562 if (available > 0 && havePercent) {
563 for (int i = 0; i < nEffCols; i++) {
564 Length &width = m_layoutStruct[i].effWidth;
565 if (width.isPercent()) {
566 int w = max(int(m_layoutStruct[i].effMinWidth), width.calcMinValue(tableWidth));
567 available += m_layoutStruct[i].calcWidth - w;
568 m_layoutStruct[i].calcWidth = w;
569 }
570 }
571 if (totalPercent > 100 * percentScaleFactor) {
572 // remove overallocated space from the last columns
573 int excess = tableWidth*(totalPercent - 100 * percentScaleFactor) / (100 * percentScaleFactor);
574 for (int i = nEffCols-1; i >= 0; i--) {
575 if (m_layoutStruct[i].effWidth.isPercent()) {
576 int w = m_layoutStruct[i].calcWidth;
577 int reduction = min(w, excess);
578 // the lines below might look inconsistent, but that's the way it's handled in mozilla
579 excess -= reduction;
580 int newWidth = max(static_cast<int>(m_layoutStruct[i].effMinWidth), w - reduction);
581 available += w - newWidth;
582 m_layoutStruct[i].calcWidth = newWidth;
583 }
584 }
585 }
586 }
587
588 // then allocate width to fixed cols
589 if (available > 0) {
590 for (int i = 0; i < nEffCols; ++i) {
591 Length &width = m_layoutStruct[i].effWidth;
592 if (width.isFixed() && width.value() > m_layoutStruct[i].calcWidth) {
593 available += m_layoutStruct[i].calcWidth - width.value();
594 m_layoutStruct[i].calcWidth = width.value();
595 }
596 }
597 }
598
599 // now satisfy relative
600 if (available > 0) {
601 for (int i = 0; i < nEffCols; i++) {
602 Length &width = m_layoutStruct[i].effWidth;
603 if (width.isRelative() && width.value() != 0) {
604 // width=0* gets effMinWidth.
605 int w = width.value() * tableWidth / totalRelative;
606 available += m_layoutStruct[i].calcWidth - w;
607 m_layoutStruct[i].calcWidth = w;
608 }
609 }
610 }
611
612 // now satisfy variable
613 if (available > 0 && numAuto) {
614 available += allocAuto; // this gets redistributed
615 for (int i = 0; i < nEffCols; i++) {
616 Length &width = m_layoutStruct[i].effWidth;
617 if (width.isAuto() && totalAuto != 0 && !m_layoutStruct[i].emptyCellsOnly) {
618 int w = max(m_layoutStruct[i].calcWidth, static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effMaxWidth) / totalAuto));
619 available -= w;
620 totalAuto -= m_layoutStruct[i].effMaxWidth;
621 m_layoutStruct[i].calcWidth = w;
622 }
623 }
624 }
625
626 // spread over fixed columns
627 if (available > 0 && numFixed) {
628 // still have some width to spread, distribute to fixed columns
629 for (int i = 0; i < nEffCols; i++) {
630 Length &width = m_layoutStruct[i].effWidth;
631 if (width.isFixed()) {
632 int w = static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effMaxWidth) / totalFixed);
633 available -= w;
634 totalFixed -= m_layoutStruct[i].effMaxWidth;
635 m_layoutStruct[i].calcWidth += w;
636 }
637 }
638 }
639
640 // spread over percent colums
641 if (available > 0 && m_hasPercent && totalPercent < 100 * percentScaleFactor) {
642 // still have some width to spread, distribute weighted to percent columns
643 for (int i = 0; i < nEffCols; i++) {
644 Length &width = m_layoutStruct[i].effWidth;
645 if (width.isPercent()) {
646 int w = available * width.rawValue() / totalPercent;
647 available -= w;
648 totalPercent -= width.rawValue();
649 m_layoutStruct[i].calcWidth += w;
650 if (!available || !totalPercent) break;
651 }
652 }
653 }
654
655 // spread over the rest
656 if (available > 0 && nEffCols > numAutoEmptyCellsOnly) {
657 int total = nEffCols - numAutoEmptyCellsOnly;
658 // still have some width to spread
659 int i = nEffCols;
660 while (i--) {
661 // variable columns with empty cells only don't get any width
662 if (m_layoutStruct[i].effWidth.isAuto() && m_layoutStruct[i].emptyCellsOnly)
663 continue;
664 int w = available / total;
665 available -= w;
666 total--;
667 m_layoutStruct[i].calcWidth += w;
668 }
669 }
670
671 // if we have overallocated, reduce every cell according to the difference between desired width and minwidth
672 // this seems to produce to the pixel exaxt results with IE. Wonder is some of this also holds for width distributing.
673 if (available < 0) {
674 // Need to reduce cells with the following prioritization:
675 // (1) Auto
676 // (2) Relative
677 // (3) Fixed
678 // (4) Percent
679 // This is basically the reverse of how we grew the cells.
680 if (available < 0) {
681 int mw = 0;
682 for (int i = nEffCols-1; i >= 0; i--) {
683 Length &width = m_layoutStruct[i].effWidth;
684 if (width.isAuto())
685 mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth;
686 }
687
688 for (int i = nEffCols-1; i >= 0 && mw > 0; i--) {
689 Length &width = m_layoutStruct[i].effWidth;
690 if (width.isAuto()) {
691 int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth;
692 int reduce = available * minMaxDiff / mw;
693 m_layoutStruct[i].calcWidth += reduce;
694 available -= reduce;
695 mw -= minMaxDiff;
696 if (available >= 0)
697 break;
698 }
699 }
700 }
701
702 if (available < 0) {
703 int mw = 0;
704 for (int i = nEffCols-1; i >= 0; i--) {
705 Length& width = m_layoutStruct[i].effWidth;
706 if (width.isRelative())
707 mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth;
708 }
709
710 for (int i = nEffCols-1; i >= 0 && mw > 0; i--) {
711 Length& width = m_layoutStruct[i].effWidth;
712 if (width.isRelative()) {
713 int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth;
714 int reduce = available * minMaxDiff / mw;
715 m_layoutStruct[i].calcWidth += reduce;
716 available -= reduce;
717 mw -= minMaxDiff;
718 if (available >= 0)
719 break;
720 }
721 }
722 }
723
724 if (available < 0) {
725 int mw = 0;
726 for (int i = nEffCols-1; i >= 0; i--) {
727 Length& width = m_layoutStruct[i].effWidth;
728 if (width.isFixed())
729 mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth;
730 }
731
732 for (int i = nEffCols-1; i >= 0 && mw > 0; i--) {
733 Length& width = m_layoutStruct[i].effWidth;
734 if (width.isFixed()) {
735 int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth;
736 int reduce = available * minMaxDiff / mw;
737 m_layoutStruct[i].calcWidth += reduce;
738 available -= reduce;
739 mw -= minMaxDiff;
740 if (available >= 0)
741 break;
742 }
743 }
744 }
745
746 if (available < 0) {
747 int mw = 0;
748 for (int i = nEffCols-1; i >= 0; i--) {
749 Length& width = m_layoutStruct[i].effWidth;
750 if (width.isPercent())
751 mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth;
752 }
753
754 for (int i = nEffCols-1; i >= 0 && mw > 0; i--) {
755 Length& width = m_layoutStruct[i].effWidth;
756 if (width.isPercent()) {
757 int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth;
758 int reduce = available * minMaxDiff / mw;
759 m_layoutStruct[i].calcWidth += reduce;
760 available -= reduce;
761 mw -= minMaxDiff;
762 if (available >= 0)
763 break;
764 }
765 }
766 }
767 }
768
769 int pos = 0;
770 for (int i = 0; i < nEffCols; i++) {
771 m_table->columnPositions()[i] = pos;
772 pos += m_layoutStruct[i].calcWidth + m_table->hBorderSpacing();
773 }
774 m_table->columnPositions()[m_table->columnPositions().size() - 1] = pos;
775 }
776
777
calcPercentages() const778 void AutoTableLayout::calcPercentages() const
779 {
780 unsigned totalPercent = 0;
781 for (unsigned i = 0; i < m_layoutStruct.size(); i++) {
782 if (m_layoutStruct[i].width.isPercent())
783 totalPercent += m_layoutStruct[i].width.rawValue();
784 }
785 m_totalPercent = totalPercent / percentScaleFactor;
786 m_percentagesDirty = false;
787 }
788
789 #undef DEBUG_LAYOUT
790
791 }
792