/* | |
* Copyright (C) 2002 Lars Knoll (knoll@kde.org) | |
* (C) 2002 Dirk Mueller (mueller@kde.org) | |
* Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Library General Public | |
* License as published by the Free Software Foundation; either | |
* version 2 of the License. | |
* | |
* This library is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Library General Public License for more details. | |
* | |
* You should have received a copy of the GNU Library General Public License | |
* along with this library; see the file COPYING.LIB. If not, write to | |
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
* Boston, MA 02110-1301, USA. | |
*/ | |
#include "config.h" | |
#include "AutoTableLayout.h" | |
#include "RenderTable.h" | |
#include "RenderTableCell.h" | |
#include "RenderTableCol.h" | |
#include "RenderTableSection.h" | |
using namespace std; | |
namespace WebCore { | |
AutoTableLayout::AutoTableLayout(RenderTable* table) | |
: TableLayout(table) | |
, m_hasPercent(false) | |
, m_percentagesDirty(true) | |
, m_effWidthDirty(true) | |
, m_totalPercent(0) | |
{ | |
} | |
AutoTableLayout::~AutoTableLayout() | |
{ | |
} | |
/* recalculates the full structure needed to do layouting and minmax calculations. | |
This is usually calculated on the fly, but needs to be done fully when table cells change | |
dynamically | |
*/ | |
void AutoTableLayout::recalcColumn(int effCol) | |
{ | |
Layout &l = m_layoutStruct[effCol]; | |
RenderObject* child = m_table->firstChild(); | |
// first we iterate over all rows. | |
RenderTableCell* fixedContributor = 0; | |
RenderTableCell* maxContributor = 0; | |
while (child) { | |
if (child->isTableCol()) | |
toRenderTableCol(child)->calcPrefWidths(); | |
else if (child->isTableSection()) { | |
RenderTableSection* section = toRenderTableSection(child); | |
int numRows = section->numRows(); | |
RenderTableCell* last = 0; | |
for (int i = 0; i < numRows; i++) { | |
RenderTableSection::CellStruct current = section->cellAt(i, effCol); | |
RenderTableCell* cell = current.cell; | |
bool cellHasContent = cell && (cell->firstChild() || cell->style()->hasBorder() || cell->style()->hasPadding()); | |
if (cellHasContent) | |
l.emptyCellsOnly = false; | |
if (current.inColSpan) | |
continue; | |
if (cell && cell->colSpan() == 1) { | |
// A cell originates in this column. Ensure we have | |
// a min/max width of at least 1px for this column now. | |
l.minWidth = max(l.minWidth, cellHasContent ? 1 : 0); | |
l.maxWidth = max(l.maxWidth, 1); | |
if (cell->prefWidthsDirty()) | |
cell->calcPrefWidths(); | |
l.minWidth = max(cell->minPrefWidth(), l.minWidth); | |
if (cell->maxPrefWidth() > l.maxWidth) { | |
l.maxWidth = cell->maxPrefWidth(); | |
maxContributor = cell; | |
} | |
Length w = cell->styleOrColWidth(); | |
// FIXME: What is this arbitrary value? | |
if (w.rawValue() > 32760) | |
w.setRawValue(32760); | |
if (w.isNegative()) | |
w.setValue(0); | |
switch (w.type()) { | |
case Fixed: | |
// ignore width=0 | |
if (w.value() > 0 && (int)l.width.type() != Percent) { | |
int wval = cell->calcBorderBoxWidth(w.value()); | |
if (l.width.isFixed()) { | |
// Nav/IE weirdness | |
if ((wval > l.width.value()) || | |
((l.width.value() == wval) && (maxContributor == cell))) { | |
l.width.setValue(wval); | |
fixedContributor = cell; | |
} | |
} else { | |
l.width.setValue(Fixed, wval); | |
fixedContributor = cell; | |
} | |
} | |
break; | |
case Percent: | |
m_hasPercent = true; | |
if (w.isPositive() && (!l.width.isPercent() || w.rawValue() > l.width.rawValue())) | |
l.width = w; | |
break; | |
case Relative: | |
// FIXME: Need to understand this case and whether it makes sense to compare values | |
// which are not necessarily of the same type. | |
if (w.isAuto() || (w.isRelative() && w.value() > l.width.rawValue())) | |
l.width = w; | |
default: | |
break; | |
} | |
} else { | |
if (cell && (!effCol || section->cellAt(i, effCol-1).cell != cell)) { | |
// This spanning cell originates in this column. Ensure we have | |
// a min/max width of at least 1px for this column now. | |
l.minWidth = max(l.minWidth, cellHasContent ? 1 : 0); | |
l.maxWidth = max(l.maxWidth, 1); | |
insertSpanCell(cell); | |
} | |
last = cell; | |
} | |
} | |
} | |
child = child->nextSibling(); | |
} | |
// Nav/IE weirdness | |
if (l.width.isFixed()) { | |
if (m_table->style()->htmlHacks() && l.maxWidth > l.width.value() && fixedContributor != maxContributor) { | |
l.width = Length(); | |
fixedContributor = 0; | |
} | |
} | |
l.maxWidth = max(l.maxWidth, l.minWidth); | |
// ### we need to add col elements as well | |
} | |
void AutoTableLayout::fullRecalc() | |
{ | |
m_percentagesDirty = true; | |
m_hasPercent = false; | |
m_effWidthDirty = true; | |
int nEffCols = m_table->numEffCols(); | |
m_layoutStruct.resize(nEffCols); | |
m_layoutStruct.fill(Layout()); | |
m_spanCells.fill(0); | |
RenderObject *child = m_table->firstChild(); | |
Length grpWidth; | |
int cCol = 0; | |
while (child) { | |
if (child->isTableCol()) { | |
RenderTableCol *col = toRenderTableCol(child); | |
int span = col->span(); | |
if (col->firstChild()) { | |
grpWidth = col->style()->width(); | |
} else { | |
Length w = col->style()->width(); | |
if (w.isAuto()) | |
w = grpWidth; | |
if ((w.isFixed() || w.isPercent()) && w.isZero()) | |
w = Length(); | |
int cEffCol = m_table->colToEffCol(cCol); | |
if (!w.isAuto() && span == 1 && cEffCol < nEffCols) { | |
if (m_table->spanOfEffCol(cEffCol) == 1) { | |
m_layoutStruct[cEffCol].width = w; | |
if (w.isFixed() && m_layoutStruct[cEffCol].maxWidth < w.value()) | |
m_layoutStruct[cEffCol].maxWidth = w.value(); | |
} | |
} | |
cCol += span; | |
} | |
} else { | |
break; | |
} | |
RenderObject *next = child->firstChild(); | |
if (!next) | |
next = child->nextSibling(); | |
if (!next && child->parent()->isTableCol()) { | |
next = child->parent()->nextSibling(); | |
grpWidth = Length(); | |
} | |
child = next; | |
} | |
for (int i = 0; i < nEffCols; i++) | |
recalcColumn(i); | |
} | |
static bool shouldScaleColumns(RenderTable* table) | |
{ | |
// A special case. If this table is not fixed width and contained inside | |
// a cell, then don't bloat the maxwidth by examining percentage growth. | |
bool scale = true; | |
while (table) { | |
Length tw = table->style()->width(); | |
if ((tw.isAuto() || tw.isPercent()) && !table->isPositioned()) { | |
RenderBlock* cb = table->containingBlock(); | |
while (cb && !cb->isRenderView() && !cb->isTableCell() && | |
cb->style()->width().isAuto() && !cb->isPositioned()) | |
cb = cb->containingBlock(); | |
table = 0; | |
if (cb && cb->isTableCell() && | |
(cb->style()->width().isAuto() || cb->style()->width().isPercent())) { | |
if (tw.isPercent()) | |
scale = false; | |
else { | |
RenderTableCell* cell = toRenderTableCell(cb); | |
if (cell->colSpan() > 1 || cell->table()->style()->width().isAuto()) | |
scale = false; | |
else | |
table = cell->table(); | |
} | |
} | |
} | |
else | |
table = 0; | |
} | |
return scale; | |
} | |
void AutoTableLayout::calcPrefWidths(int& minWidth, int& maxWidth) | |
{ | |
fullRecalc(); | |
int spanMaxWidth = calcEffectiveWidth(); | |
minWidth = 0; | |
maxWidth = 0; | |
float maxPercent = 0; | |
float maxNonPercent = 0; | |
bool scaleColumns = shouldScaleColumns(m_table); | |
// We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero. | |
// FIXME: Handle the 0% cases properly. | |
const int epsilon = 1; | |
int remainingPercent = 100 * percentScaleFactor; | |
for (unsigned int i = 0; i < m_layoutStruct.size(); i++) { | |
minWidth += m_layoutStruct[i].effMinWidth; | |
maxWidth += m_layoutStruct[i].effMaxWidth; | |
if (scaleColumns) { | |
if (m_layoutStruct[i].effWidth.isPercent()) { | |
int percent = min(m_layoutStruct[i].effWidth.rawValue(), remainingPercent); | |
float pw = static_cast<float>(m_layoutStruct[i].effMaxWidth) * 100 * percentScaleFactor / max(percent, epsilon); | |
maxPercent = max(pw, maxPercent); | |
remainingPercent -= percent; | |
} else | |
maxNonPercent += m_layoutStruct[i].effMaxWidth; | |
} | |
} | |
if (scaleColumns) { | |
maxNonPercent = maxNonPercent * 100 * percentScaleFactor / max(remainingPercent, epsilon); | |
maxWidth = max(maxWidth, static_cast<int>(min(maxNonPercent, INT_MAX / 2.0f))); | |
maxWidth = max(maxWidth, static_cast<int>(min(maxPercent, INT_MAX / 2.0f))); | |
} | |
maxWidth = max(maxWidth, spanMaxWidth); | |
int bs = m_table->bordersPaddingAndSpacing(); | |
minWidth += bs; | |
maxWidth += bs; | |
Length tw = m_table->style()->width(); | |
if (tw.isFixed() && tw.value() > 0) { | |
minWidth = max(minWidth, tw.value()); | |
maxWidth = minWidth; | |
} | |
} | |
/* | |
This method takes care of colspans. | |
effWidth is the same as width for cells without colspans. If we have colspans, they get modified. | |
*/ | |
int AutoTableLayout::calcEffectiveWidth() | |
{ | |
float tMaxWidth = 0; | |
unsigned int nEffCols = m_layoutStruct.size(); | |
int hspacing = m_table->hBorderSpacing(); | |
for (unsigned int i = 0; i < nEffCols; i++) { | |
m_layoutStruct[i].effWidth = m_layoutStruct[i].width; | |
m_layoutStruct[i].effMinWidth = m_layoutStruct[i].minWidth; | |
m_layoutStruct[i].effMaxWidth = m_layoutStruct[i].maxWidth; | |
} | |
for (unsigned int i = 0; i < m_spanCells.size(); i++) { | |
RenderTableCell *cell = m_spanCells[i]; | |
if (!cell) | |
break; | |
int span = cell->colSpan(); | |
Length w = cell->styleOrColWidth(); | |
if (!w.isRelative() && w.isZero()) | |
w = Length(); // make it Auto | |
int col = m_table->colToEffCol(cell->col()); | |
unsigned int lastCol = col; | |
int cMinWidth = cell->minPrefWidth() + hspacing; | |
float cMaxWidth = cell->maxPrefWidth() + hspacing; | |
int totalPercent = 0; | |
int minWidth = 0; | |
float maxWidth = 0; | |
bool allColsArePercent = true; | |
bool allColsAreFixed = true; | |
bool haveAuto = false; | |
bool spanHasEmptyCellsOnly = true; | |
int fixedWidth = 0; | |
while (lastCol < nEffCols && span > 0) { | |
switch (m_layoutStruct[lastCol].width.type()) { | |
case Percent: | |
totalPercent += m_layoutStruct[lastCol].width.rawValue(); | |
allColsAreFixed = false; | |
break; | |
case Fixed: | |
if (m_layoutStruct[lastCol].width.value() > 0) { | |
fixedWidth += m_layoutStruct[lastCol].width.value(); | |
allColsArePercent = false; | |
// IE resets effWidth to Auto here, but this breaks the konqueror about page and seems to be some bad | |
// legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither. | |
break; | |
} | |
// fall through | |
case Auto: | |
haveAuto = true; | |
// fall through | |
default: | |
// If the column is a percentage width, do not let the spanning cell overwrite the | |
// width value. This caused a mis-rendering on amazon.com. | |
// Sample snippet: | |
// <table border=2 width=100%>< | |
// <tr><td>1</td><td colspan=2>2-3</tr> | |
// <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr> | |
// </table> | |
if (!m_layoutStruct[lastCol].effWidth.isPercent()) { | |
m_layoutStruct[lastCol].effWidth = Length(); | |
allColsArePercent = false; | |
} | |
else | |
totalPercent += m_layoutStruct[lastCol].effWidth.rawValue(); | |
allColsAreFixed = false; | |
} | |
if (!m_layoutStruct[lastCol].emptyCellsOnly) | |
spanHasEmptyCellsOnly = false; | |
span -= m_table->spanOfEffCol(lastCol); | |
minWidth += m_layoutStruct[lastCol].effMinWidth; | |
maxWidth += m_layoutStruct[lastCol].effMaxWidth; | |
lastCol++; | |
cMinWidth -= hspacing; | |
cMaxWidth -= hspacing; | |
} | |
// adjust table max width if needed | |
if (w.isPercent()) { | |
if (totalPercent > w.rawValue() || allColsArePercent) { | |
// can't satify this condition, treat as variable | |
w = Length(); | |
} else { | |
float spanMax = max(maxWidth, cMaxWidth); | |
tMaxWidth = max(tMaxWidth, spanMax * 100 * percentScaleFactor / w.rawValue()); | |
// all non percent columns in the span get percent values to sum up correctly. | |
int percentMissing = w.rawValue() - totalPercent; | |
float totalWidth = 0; | |
for (unsigned int pos = col; pos < lastCol; pos++) { | |
if (!(m_layoutStruct[pos].effWidth.isPercent())) | |
totalWidth += m_layoutStruct[pos].effMaxWidth; | |
} | |
for (unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++) { | |
if (!(m_layoutStruct[pos].effWidth.isPercent())) { | |
int percent = static_cast<int>(percentMissing * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / totalWidth); | |
totalWidth -= m_layoutStruct[pos].effMaxWidth; | |
percentMissing -= percent; | |
if (percent > 0) | |
m_layoutStruct[pos].effWidth.setRawValue(Percent, percent); | |
else | |
m_layoutStruct[pos].effWidth = Length(); | |
} | |
} | |
} | |
} | |
// make sure minWidth and maxWidth of the spanning cell are honoured | |
if (cMinWidth > minWidth) { | |
if (allColsAreFixed) { | |
for (unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++) { | |
int w = max(m_layoutStruct[pos].effMinWidth, cMinWidth * m_layoutStruct[pos].width.value() / fixedWidth); | |
fixedWidth -= m_layoutStruct[pos].width.value(); | |
cMinWidth -= w; | |
m_layoutStruct[pos].effMinWidth = w; | |
} | |
} else { | |
float maxw = maxWidth; | |
int minw = minWidth; | |
// Give min to variable first, to fixed second, and to others third. | |
for (unsigned int pos = col; maxw >= 0 && pos < lastCol; pos++) { | |
if (m_layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth) { | |
int w = max(m_layoutStruct[pos].effMinWidth, m_layoutStruct[pos].width.value()); | |
fixedWidth -= m_layoutStruct[pos].width.value(); | |
minw -= m_layoutStruct[pos].effMinWidth; | |
maxw -= m_layoutStruct[pos].effMaxWidth; | |
cMinWidth -= w; | |
m_layoutStruct[pos].effMinWidth = w; | |
} | |
} | |
for (unsigned int pos = col; maxw >= 0 && pos < lastCol && minw < cMinWidth; pos++) { | |
if (!(m_layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth)) { | |
int w = max(m_layoutStruct[pos].effMinWidth, static_cast<int>(maxw ? cMinWidth * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / maxw : cMinWidth)); | |
w = min(m_layoutStruct[pos].effMinWidth+(cMinWidth-minw), w); | |
maxw -= m_layoutStruct[pos].effMaxWidth; | |
minw -= m_layoutStruct[pos].effMinWidth; | |
cMinWidth -= w; | |
m_layoutStruct[pos].effMinWidth = w; | |
} | |
} | |
} | |
} | |
if (!(w.isPercent())) { | |
if (cMaxWidth > maxWidth) { | |
for (unsigned int pos = col; maxWidth >= 0 && pos < lastCol; pos++) { | |
int w = max(m_layoutStruct[pos].effMaxWidth, static_cast<int>(maxWidth ? cMaxWidth * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / maxWidth : cMaxWidth)); | |
maxWidth -= m_layoutStruct[pos].effMaxWidth; | |
cMaxWidth -= w; | |
m_layoutStruct[pos].effMaxWidth = w; | |
} | |
} | |
} else { | |
for (unsigned int pos = col; pos < lastCol; pos++) | |
m_layoutStruct[pos].maxWidth = max(m_layoutStruct[pos].maxWidth, m_layoutStruct[pos].minWidth); | |
} | |
// treat span ranges consisting of empty cells only as if they had content | |
if (spanHasEmptyCellsOnly) | |
for (unsigned int pos = col; pos < lastCol; pos++) | |
m_layoutStruct[pos].emptyCellsOnly = false; | |
} | |
m_effWidthDirty = false; | |
return static_cast<int>(min(tMaxWidth, INT_MAX / 2.0f)); | |
} | |
/* gets all cells that originate in a column and have a cellspan > 1 | |
Sorts them by increasing cellspan | |
*/ | |
void AutoTableLayout::insertSpanCell(RenderTableCell *cell) | |
{ | |
if (!cell || cell->colSpan() == 1) | |
return; | |
int size = m_spanCells.size(); | |
if (!size || m_spanCells[size-1] != 0) { | |
m_spanCells.grow(size + 10); | |
for (int i = 0; i < 10; i++) | |
m_spanCells[size+i] = 0; | |
size += 10; | |
} | |
// add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better | |
unsigned int pos = 0; | |
int span = cell->colSpan(); | |
while (pos < m_spanCells.size() && m_spanCells[pos] && span > m_spanCells[pos]->colSpan()) | |
pos++; | |
memmove(m_spanCells.data()+pos+1, m_spanCells.data()+pos, (size-pos-1)*sizeof(RenderTableCell *)); | |
m_spanCells[pos] = cell; | |
} | |
void AutoTableLayout::layout() | |
{ | |
// table layout based on the values collected in the layout structure. | |
int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing(); | |
int available = tableWidth; | |
int nEffCols = m_table->numEffCols(); | |
if (nEffCols != (int)m_layoutStruct.size()) { | |
fullRecalc(); | |
nEffCols = m_table->numEffCols(); | |
} | |
if (m_effWidthDirty) | |
calcEffectiveWidth(); | |
bool havePercent = false; | |
bool haveRelative = false; | |
int totalRelative = 0; | |
int numAuto = 0; | |
int numFixed = 0; | |
float totalAuto = 0; | |
float totalFixed = 0; | |
int totalPercent = 0; | |
int allocAuto = 0; | |
int numAutoEmptyCellsOnly = 0; | |
// fill up every cell with its minWidth | |
for (int i = 0; i < nEffCols; i++) { | |
int w = m_layoutStruct[i].effMinWidth; | |
m_layoutStruct[i].calcWidth = w; | |
available -= w; | |
Length& width = m_layoutStruct[i].effWidth; | |
switch (width.type()) { | |
case Percent: | |
havePercent = true; | |
totalPercent += width.rawValue(); | |
break; | |
case Relative: | |
haveRelative = true; | |
totalRelative += width.value(); | |
break; | |
case Fixed: | |
numFixed++; | |
totalFixed += m_layoutStruct[i].effMaxWidth; | |
// fall through | |
break; | |
case Auto: | |
case Static: | |
if (m_layoutStruct[i].emptyCellsOnly) | |
numAutoEmptyCellsOnly++; | |
else { | |
numAuto++; | |
totalAuto += m_layoutStruct[i].effMaxWidth; | |
allocAuto += w; | |
} | |
break; | |
default: | |
break; | |
} | |
} | |
// allocate width to percent cols | |
if (available > 0 && havePercent) { | |
for (int i = 0; i < nEffCols; i++) { | |
Length &width = m_layoutStruct[i].effWidth; | |
if (width.isPercent()) { | |
int w = max(int(m_layoutStruct[i].effMinWidth), width.calcMinValue(tableWidth)); | |
available += m_layoutStruct[i].calcWidth - w; | |
m_layoutStruct[i].calcWidth = w; | |
} | |
} | |
if (totalPercent > 100 * percentScaleFactor) { | |
// remove overallocated space from the last columns | |
int excess = tableWidth*(totalPercent - 100 * percentScaleFactor) / (100 * percentScaleFactor); | |
for (int i = nEffCols-1; i >= 0; i--) { | |
if (m_layoutStruct[i].effWidth.isPercent()) { | |
int w = m_layoutStruct[i].calcWidth; | |
int reduction = min(w, excess); | |
// the lines below might look inconsistent, but that's the way it's handled in mozilla | |
excess -= reduction; | |
int newWidth = max(static_cast<int>(m_layoutStruct[i].effMinWidth), w - reduction); | |
available += w - newWidth; | |
m_layoutStruct[i].calcWidth = newWidth; | |
} | |
} | |
} | |
} | |
// then allocate width to fixed cols | |
if (available > 0) { | |
for (int i = 0; i < nEffCols; ++i) { | |
Length &width = m_layoutStruct[i].effWidth; | |
if (width.isFixed() && width.value() > m_layoutStruct[i].calcWidth) { | |
available += m_layoutStruct[i].calcWidth - width.value(); | |
m_layoutStruct[i].calcWidth = width.value(); | |
} | |
} | |
} | |
// now satisfy relative | |
if (available > 0) { | |
for (int i = 0; i < nEffCols; i++) { | |
Length &width = m_layoutStruct[i].effWidth; | |
if (width.isRelative() && width.value() != 0) { | |
// width=0* gets effMinWidth. | |
int w = width.value() * tableWidth / totalRelative; | |
available += m_layoutStruct[i].calcWidth - w; | |
m_layoutStruct[i].calcWidth = w; | |
} | |
} | |
} | |
// now satisfy variable | |
if (available > 0 && numAuto) { | |
available += allocAuto; // this gets redistributed | |
for (int i = 0; i < nEffCols; i++) { | |
Length &width = m_layoutStruct[i].effWidth; | |
if (width.isAuto() && totalAuto != 0 && !m_layoutStruct[i].emptyCellsOnly) { | |
int w = max(m_layoutStruct[i].calcWidth, static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effMaxWidth) / totalAuto)); | |
available -= w; | |
totalAuto -= m_layoutStruct[i].effMaxWidth; | |
m_layoutStruct[i].calcWidth = w; | |
} | |
} | |
} | |
// spread over fixed columns | |
if (available > 0 && numFixed) { | |
// still have some width to spread, distribute to fixed columns | |
for (int i = 0; i < nEffCols; i++) { | |
Length &width = m_layoutStruct[i].effWidth; | |
if (width.isFixed()) { | |
int w = static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effMaxWidth) / totalFixed); | |
available -= w; | |
totalFixed -= m_layoutStruct[i].effMaxWidth; | |
m_layoutStruct[i].calcWidth += w; | |
} | |
} | |
} | |
// spread over percent colums | |
if (available > 0 && m_hasPercent && totalPercent < 100 * percentScaleFactor) { | |
// still have some width to spread, distribute weighted to percent columns | |
for (int i = 0; i < nEffCols; i++) { | |
Length &width = m_layoutStruct[i].effWidth; | |
if (width.isPercent()) { | |
int w = available * width.rawValue() / totalPercent; | |
available -= w; | |
totalPercent -= width.rawValue(); | |
m_layoutStruct[i].calcWidth += w; | |
if (!available || !totalPercent) break; | |
} | |
} | |
} | |
// spread over the rest | |
if (available > 0 && nEffCols > numAutoEmptyCellsOnly) { | |
int total = nEffCols - numAutoEmptyCellsOnly; | |
// still have some width to spread | |
int i = nEffCols; | |
while (i--) { | |
// variable columns with empty cells only don't get any width | |
if (m_layoutStruct[i].effWidth.isAuto() && m_layoutStruct[i].emptyCellsOnly) | |
continue; | |
int w = available / total; | |
available -= w; | |
total--; | |
m_layoutStruct[i].calcWidth += w; | |
} | |
} | |
// If we have overallocated, reduce every cell according to the difference between desired width and minwidth | |
// this seems to produce to the pixel exact results with IE. Wonder is some of this also holds for width distributing. | |
if (available < 0) { | |
// Need to reduce cells with the following prioritization: | |
// (1) Auto | |
// (2) Relative | |
// (3) Fixed | |
// (4) Percent | |
// This is basically the reverse of how we grew the cells. | |
if (available < 0) { | |
int mw = 0; | |
for (int i = nEffCols-1; i >= 0; i--) { | |
Length &width = m_layoutStruct[i].effWidth; | |
if (width.isAuto()) | |
mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth; | |
} | |
for (int i = nEffCols-1; i >= 0 && mw > 0; i--) { | |
Length &width = m_layoutStruct[i].effWidth; | |
if (width.isAuto()) { | |
int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth; | |
int reduce = available * minMaxDiff / mw; | |
m_layoutStruct[i].calcWidth += reduce; | |
available -= reduce; | |
mw -= minMaxDiff; | |
if (available >= 0) | |
break; | |
} | |
} | |
} | |
if (available < 0) { | |
int mw = 0; | |
for (int i = nEffCols-1; i >= 0; i--) { | |
Length& width = m_layoutStruct[i].effWidth; | |
if (width.isRelative()) | |
mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth; | |
} | |
for (int i = nEffCols-1; i >= 0 && mw > 0; i--) { | |
Length& width = m_layoutStruct[i].effWidth; | |
if (width.isRelative()) { | |
int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth; | |
int reduce = available * minMaxDiff / mw; | |
m_layoutStruct[i].calcWidth += reduce; | |
available -= reduce; | |
mw -= minMaxDiff; | |
if (available >= 0) | |
break; | |
} | |
} | |
} | |
if (available < 0) { | |
int mw = 0; | |
for (int i = nEffCols-1; i >= 0; i--) { | |
Length& width = m_layoutStruct[i].effWidth; | |
if (width.isFixed()) | |
mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth; | |
} | |
for (int i = nEffCols-1; i >= 0 && mw > 0; i--) { | |
Length& width = m_layoutStruct[i].effWidth; | |
if (width.isFixed()) { | |
int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth; | |
int reduce = available * minMaxDiff / mw; | |
m_layoutStruct[i].calcWidth += reduce; | |
available -= reduce; | |
mw -= minMaxDiff; | |
if (available >= 0) | |
break; | |
} | |
} | |
} | |
if (available < 0) { | |
int mw = 0; | |
for (int i = nEffCols-1; i >= 0; i--) { | |
Length& width = m_layoutStruct[i].effWidth; | |
if (width.isPercent()) | |
mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth; | |
} | |
for (int i = nEffCols-1; i >= 0 && mw > 0; i--) { | |
Length& width = m_layoutStruct[i].effWidth; | |
if (width.isPercent()) { | |
int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth; | |
int reduce = available * minMaxDiff / mw; | |
m_layoutStruct[i].calcWidth += reduce; | |
available -= reduce; | |
mw -= minMaxDiff; | |
if (available >= 0) | |
break; | |
} | |
} | |
} | |
} | |
int pos = 0; | |
for (int i = 0; i < nEffCols; i++) { | |
m_table->columnPositions()[i] = pos; | |
pos += m_layoutStruct[i].calcWidth + m_table->hBorderSpacing(); | |
} | |
m_table->columnPositions()[m_table->columnPositions().size() - 1] = pos; | |
} | |
void AutoTableLayout::calcPercentages() const | |
{ | |
unsigned totalPercent = 0; | |
for (unsigned i = 0; i < m_layoutStruct.size(); i++) { | |
if (m_layoutStruct[i].width.isPercent()) | |
totalPercent += m_layoutStruct[i].width.rawValue(); | |
} | |
m_totalPercent = totalPercent / percentScaleFactor; | |
m_percentagesDirty = false; | |
} | |
#undef DEBUG_LAYOUT | |
} |