/*
* Copyright (C) 2002 Lars Knoll (knoll@kde.org)
* (C) 2002 Dirk Mueller (mueller@kde.org)
* Copyright (C) 2003, 2006, 2008, 2010 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_effectiveLogicalWidthDirty(true)
{
}
AutoTableLayout::~AutoTableLayout()
{
}
void AutoTableLayout::recalcColumn(int effCol)
{
Layout& columnLayout = m_layoutStruct[effCol];
RenderTableCell* fixedContributor = 0;
RenderTableCell* maxContributor = 0;
for (RenderObject* child = m_table->firstChild(); child; child = child->nextSibling()) {
if (child->isTableCol())
toRenderTableCol(child)->computePreferredLogicalWidths();
else if (child->isTableSection()) {
RenderTableSection* section = toRenderTableSection(child);
int numRows = section->numRows();
for (int i = 0; i < numRows; i++) {
RenderTableSection::CellStruct current = section->cellAt(i, effCol);
RenderTableCell* cell = current.primaryCell();
bool cellHasContent = cell && !current.inColSpan && (cell->firstChild() || cell->style()->hasBorder() || cell->style()->hasPadding());
if (cellHasContent)
columnLayout.emptyCellsOnly = false;
if (current.inColSpan || !cell)
continue;
if (cell->colSpan() == 1) {
// A cell originates in this column. Ensure we have
// a min/max width of at least 1px for this column now.
columnLayout.minLogicalWidth = max(columnLayout.minLogicalWidth, cellHasContent ? 1 : 0);
columnLayout.maxLogicalWidth = max(columnLayout.maxLogicalWidth, 1);
if (cell->preferredLogicalWidthsDirty())
cell->computePreferredLogicalWidths();
columnLayout.minLogicalWidth = max(cell->minPreferredLogicalWidth(), columnLayout.minLogicalWidth);
if (cell->maxPreferredLogicalWidth() > columnLayout.maxLogicalWidth) {
columnLayout.maxLogicalWidth = cell->maxPreferredLogicalWidth();
maxContributor = cell;
}
Length cellLogicalWidth = cell->styleOrColLogicalWidth();
// FIXME: What is this arbitrary value?
if (cellLogicalWidth.value() > 32760)
cellLogicalWidth.setValue(32760);
if (cellLogicalWidth.isNegative())
cellLogicalWidth.setValue(0);
switch (cellLogicalWidth.type()) {
case Fixed:
// ignore width=0
if (cellLogicalWidth.value() > 0 && columnLayout.logicalWidth.type() != Percent) {
int logicalWidth = cell->computeBorderBoxLogicalWidth(cellLogicalWidth.value());
if (columnLayout.logicalWidth.isFixed()) {
// Nav/IE weirdness
if ((logicalWidth > columnLayout.logicalWidth.value()) ||
((columnLayout.logicalWidth.value() == logicalWidth) && (maxContributor == cell))) {
columnLayout.logicalWidth.setValue(logicalWidth);
fixedContributor = cell;
}
} else {
columnLayout.logicalWidth.setValue(Fixed, logicalWidth);
fixedContributor = cell;
}
}
break;
case Percent:
m_hasPercent = true;
if (cellLogicalWidth.isPositive() && (!columnLayout.logicalWidth.isPercent() || cellLogicalWidth.value() > columnLayout.logicalWidth.value()))
columnLayout.logicalWidth = cellLogicalWidth;
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 (cellLogicalWidth.isAuto() || (cellLogicalWidth.isRelative() && cellLogicalWidth.value() > columnLayout.logicalWidth.value()))
columnLayout.logicalWidth = cellLogicalWidth;
default:
break;
}
} else if (!effCol || section->primaryCellAt(i, effCol - 1) != cell) {
// This spanning cell originates in this column. Ensure we have
// a min/max width of at least 1px for this column now.
columnLayout.minLogicalWidth = max(columnLayout.minLogicalWidth, cellHasContent ? 1 : 0);
columnLayout.maxLogicalWidth = max(columnLayout.maxLogicalWidth, 1);
insertSpanCell(cell);
}
}
}
}
// Nav/IE weirdness
if (columnLayout.logicalWidth.isFixed()) {
if (m_table->document()->inQuirksMode() && columnLayout.maxLogicalWidth > columnLayout.logicalWidth.value() && fixedContributor != maxContributor) {
columnLayout.logicalWidth = Length();
fixedContributor = 0;
}
}
columnLayout.maxLogicalWidth = max(columnLayout.maxLogicalWidth, columnLayout.minLogicalWidth);
}
void AutoTableLayout::fullRecalc()
{
m_hasPercent = false;
m_effectiveLogicalWidthDirty = true;
int nEffCols = m_table->numEffCols();
m_layoutStruct.resize(nEffCols);
m_layoutStruct.fill(Layout());
m_spanCells.fill(0);
RenderObject* child = m_table->firstChild();
Length groupLogicalWidth;
int currentColumn = 0;
while (child && child->isTableCol()) {
RenderTableCol* col = toRenderTableCol(child);
int span = col->span();
if (col->firstChild())
groupLogicalWidth = col->style()->logicalWidth();
else {
Length colLogicalWidth = col->style()->logicalWidth();
if (colLogicalWidth.isAuto())
colLogicalWidth = groupLogicalWidth;
if ((colLogicalWidth.isFixed() || colLogicalWidth.isPercent()) && colLogicalWidth.isZero())
colLogicalWidth = Length();
int effCol = m_table->colToEffCol(currentColumn);
if (!colLogicalWidth.isAuto() && span == 1 && effCol < nEffCols && m_table->spanOfEffCol(effCol) == 1) {
m_layoutStruct[effCol].logicalWidth = colLogicalWidth;
if (colLogicalWidth.isFixed() && m_layoutStruct[effCol].maxLogicalWidth < colLogicalWidth.value())
m_layoutStruct[effCol].maxLogicalWidth = colLogicalWidth.value();
}
currentColumn += span;
}
RenderObject* next = child->firstChild();
if (!next)
next = child->nextSibling();
if (!next && child->parent()->isTableCol()) {
next = child->parent()->nextSibling();
groupLogicalWidth = Length();
}
child = next;
}
for (int i = 0; i < nEffCols; i++)
recalcColumn(i);
}
// FIXME: This needs to be adapted for vertical writing modes.
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::computePreferredLogicalWidths(int& minWidth, int& maxWidth)
{
fullRecalc();
int spanMaxLogicalWidth = calcEffectiveLogicalWidth();
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 float epsilon = 1 / 128.0f;
float remainingPercent = 100;
for (size_t i = 0; i < m_layoutStruct.size(); ++i) {
minWidth += m_layoutStruct[i].effectiveMinLogicalWidth;
maxWidth += m_layoutStruct[i].effectiveMaxLogicalWidth;
if (scaleColumns) {
if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) {
float percent = min(static_cast<float>(m_layoutStruct[i].effectiveLogicalWidth.percent()), remainingPercent);
float logicalWidth = static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) * 100 / max(percent, epsilon);
maxPercent = max(logicalWidth, maxPercent);
remainingPercent -= percent;
} else
maxNonPercent += m_layoutStruct[i].effectiveMaxLogicalWidth;
}
}
if (scaleColumns) {
maxNonPercent = maxNonPercent * 100 / 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, spanMaxLogicalWidth);
int bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection();
minWidth += bordersPaddingAndSpacing;
maxWidth += bordersPaddingAndSpacing;
Length tableLogicalWidth = m_table->style()->logicalWidth();
if (tableLogicalWidth.isFixed() && tableLogicalWidth.value() > 0) {
minWidth = max(minWidth, tableLogicalWidth.value());
maxWidth = minWidth;
} else if (!remainingPercent && maxNonPercent) {
// if there was no remaining percent, maxWidth is invalid.
maxWidth = intMaxForLength;
}
}
/*
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::calcEffectiveLogicalWidth()
{
float maxLogicalWidth = 0;
size_t nEffCols = m_layoutStruct.size();
int spacingInRowDirection = m_table->hBorderSpacing();
for (size_t i = 0; i < nEffCols; ++i) {
m_layoutStruct[i].effectiveLogicalWidth = m_layoutStruct[i].logicalWidth;
m_layoutStruct[i].effectiveMinLogicalWidth = m_layoutStruct[i].minLogicalWidth;
m_layoutStruct[i].effectiveMaxLogicalWidth = m_layoutStruct[i].maxLogicalWidth;
}
for (size_t i = 0; i < m_spanCells.size(); ++i) {
RenderTableCell* cell = m_spanCells[i];
if (!cell)
break;
int span = cell->colSpan();
Length cellLogicalWidth = cell->styleOrColLogicalWidth();
if (!cellLogicalWidth.isRelative() && cellLogicalWidth.isZero())
cellLogicalWidth = Length(); // make it Auto
int effCol = m_table->colToEffCol(cell->col());
size_t lastCol = effCol;
int cellMinLogicalWidth = cell->minPreferredLogicalWidth() + spacingInRowDirection;
float cellMaxLogicalWidth = cell->maxPreferredLogicalWidth() + spacingInRowDirection;
float totalPercent = 0;
int spanMinLogicalWidth = 0;
float spanMaxLogicalWidth = 0;
bool allColsArePercent = true;
bool allColsAreFixed = true;
bool haveAuto = false;
bool spanHasEmptyCellsOnly = true;
int fixedWidth = 0;
while (lastCol < nEffCols && span > 0) {
Layout& columnLayout = m_layoutStruct[lastCol];
switch (columnLayout.logicalWidth.type()) {
case Percent:
totalPercent += columnLayout.logicalWidth.percent();
allColsAreFixed = false;
break;
case Fixed:
if (columnLayout.logicalWidth.value() > 0) {
fixedWidth += columnLayout.logicalWidth.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 (!columnLayout.effectiveLogicalWidth.isPercent()) {
columnLayout.effectiveLogicalWidth = Length();
allColsArePercent = false;
} else
totalPercent += columnLayout.effectiveLogicalWidth.percent();
allColsAreFixed = false;
}
if (!columnLayout.emptyCellsOnly)
spanHasEmptyCellsOnly = false;
span -= m_table->spanOfEffCol(lastCol);
spanMinLogicalWidth += columnLayout.effectiveMinLogicalWidth;
spanMaxLogicalWidth += columnLayout.effectiveMaxLogicalWidth;
lastCol++;
cellMinLogicalWidth -= spacingInRowDirection;
cellMaxLogicalWidth -= spacingInRowDirection;
}
// adjust table max width if needed
if (cellLogicalWidth.isPercent()) {
if (totalPercent > cellLogicalWidth.percent() || allColsArePercent) {
// can't satify this condition, treat as variable
cellLogicalWidth = Length();
} else {
maxLogicalWidth = max(maxLogicalWidth, static_cast<float>(max(spanMaxLogicalWidth, cellMaxLogicalWidth) * 100 / cellLogicalWidth.percent()));
// all non percent columns in the span get percent values to sum up correctly.
float percentMissing = cellLogicalWidth.percent() - totalPercent;
float totalWidth = 0;
for (unsigned pos = effCol; pos < lastCol; ++pos) {
if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent())
totalWidth += m_layoutStruct[pos].effectiveMaxLogicalWidth;
}
for (unsigned pos = effCol; pos < lastCol && totalWidth > 0; ++pos) {
if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent()) {
float percent = percentMissing * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / totalWidth;
totalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
percentMissing -= percent;
if (percent > 0)
m_layoutStruct[pos].effectiveLogicalWidth.setValue(Percent, percent);
else
m_layoutStruct[pos].effectiveLogicalWidth = Length();
}
}
}
}
// make sure minWidth and maxWidth of the spanning cell are honoured
if (cellMinLogicalWidth > spanMinLogicalWidth) {
if (allColsAreFixed) {
for (unsigned pos = effCol; fixedWidth > 0 && pos < lastCol; ++pos) {
int cellLogicalWidth = max(m_layoutStruct[pos].effectiveMinLogicalWidth, cellMinLogicalWidth * m_layoutStruct[pos].logicalWidth.value() / fixedWidth);
fixedWidth -= m_layoutStruct[pos].logicalWidth.value();
cellMinLogicalWidth -= cellLogicalWidth;
m_layoutStruct[pos].effectiveMinLogicalWidth = cellLogicalWidth;
}
} else {
float remainingMaxLogicalWidth = spanMaxLogicalWidth;
int remainingMinLogicalWidth = spanMinLogicalWidth;
// Give min to variable first, to fixed second, and to others third.
for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol; ++pos) {
if (m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth) {
int colMinLogicalWidth = max(m_layoutStruct[pos].effectiveMinLogicalWidth, m_layoutStruct[pos].logicalWidth.value());
fixedWidth -= m_layoutStruct[pos].logicalWidth.value();
remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth;
remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
cellMinLogicalWidth -= colMinLogicalWidth;
m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth;
}
}
for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol && remainingMinLogicalWidth < cellMinLogicalWidth; ++pos) {
if (!(m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth)) {
int colMinLogicalWidth = max(m_layoutStruct[pos].effectiveMinLogicalWidth, static_cast<int>(remainingMaxLogicalWidth ? cellMinLogicalWidth * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / remainingMaxLogicalWidth : cellMinLogicalWidth));
colMinLogicalWidth = min(m_layoutStruct[pos].effectiveMinLogicalWidth + (cellMinLogicalWidth - remainingMinLogicalWidth), colMinLogicalWidth);
remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth;
cellMinLogicalWidth -= colMinLogicalWidth;
m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth;
}
}
}
}
if (!cellLogicalWidth.isPercent()) {
if (cellMaxLogicalWidth > spanMaxLogicalWidth) {
for (unsigned pos = effCol; spanMaxLogicalWidth >= 0 && pos < lastCol; ++pos) {
int colMaxLogicalWidth = max(m_layoutStruct[pos].effectiveMaxLogicalWidth, static_cast<int>(spanMaxLogicalWidth ? cellMaxLogicalWidth * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / spanMaxLogicalWidth : cellMaxLogicalWidth));
spanMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
cellMaxLogicalWidth -= colMaxLogicalWidth;
m_layoutStruct[pos].effectiveMaxLogicalWidth = colMaxLogicalWidth;
}
}
} else {
for (unsigned pos = effCol; pos < lastCol; ++pos)
m_layoutStruct[pos].maxLogicalWidth = max(m_layoutStruct[pos].maxLogicalWidth, m_layoutStruct[pos].minLogicalWidth);
}
// treat span ranges consisting of empty cells only as if they had content
if (spanHasEmptyCellsOnly) {
for (unsigned pos = effCol; pos < lastCol; ++pos)
m_layoutStruct[pos].emptyCellsOnly = false;
}
}
m_effectiveLogicalWidthDirty = false;
return static_cast<int>(min(maxLogicalWidth, 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)
{
ASSERT_ARG(cell, cell && cell->colSpan() != 1);
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()
{
#ifdef ANDROID_LAYOUT
if (m_table->isSingleColumn())
return;
#endif
// table layout based on the values collected in the layout structure.
int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection();
int available = tableLogicalWidth;
size_t nEffCols = m_table->numEffCols();
if (nEffCols != m_layoutStruct.size()) {
fullRecalc();
nEffCols = m_table->numEffCols();
}
if (m_effectiveLogicalWidthDirty)
calcEffectiveLogicalWidth();
bool havePercent = false;
int totalRelative = 0;
int numAuto = 0;
int numFixed = 0;
float totalAuto = 0;
float totalFixed = 0;
float totalPercent = 0;
int allocAuto = 0;
unsigned numAutoEmptyCellsOnly = 0;
// fill up every cell with its minWidth
for (size_t i = 0; i < nEffCols; ++i) {
int cellLogicalWidth = m_layoutStruct[i].effectiveMinLogicalWidth;
m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
available -= cellLogicalWidth;
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
switch (logicalWidth.type()) {
case Percent:
havePercent = true;
totalPercent += logicalWidth.percent();
break;
case Relative:
totalRelative += logicalWidth.value();
break;
case Fixed:
numFixed++;
totalFixed += m_layoutStruct[i].effectiveMaxLogicalWidth;
// fall through
break;
case Auto:
if (m_layoutStruct[i].emptyCellsOnly)
numAutoEmptyCellsOnly++;
else {
numAuto++;
totalAuto += m_layoutStruct[i].effectiveMaxLogicalWidth;
allocAuto += cellLogicalWidth;
}
break;
default:
break;
}
}
// allocate width to percent cols
if (available > 0 && havePercent) {
for (size_t i = 0; i < nEffCols; ++i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isPercent()) {
int cellLogicalWidth = max(m_layoutStruct[i].effectiveMinLogicalWidth, logicalWidth.calcMinValue(tableLogicalWidth));
available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth;
m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
}
}
if (totalPercent > 100) {
// remove overallocated space from the last columns
int excess = tableLogicalWidth * (totalPercent - 100) / 100;
for (int i = nEffCols - 1; i >= 0; --i) {
if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) {
int cellLogicalWidth = m_layoutStruct[i].computedLogicalWidth;
int reduction = min(cellLogicalWidth, excess);
// the lines below might look inconsistent, but that's the way it's handled in mozilla
excess -= reduction;
int newLogicalWidth = max(m_layoutStruct[i].effectiveMinLogicalWidth, cellLogicalWidth - reduction);
available += cellLogicalWidth - newLogicalWidth;
m_layoutStruct[i].computedLogicalWidth = newLogicalWidth;
}
}
}
}
// then allocate width to fixed cols
if (available > 0) {
for (size_t i = 0; i < nEffCols; ++i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isFixed() && logicalWidth.value() > m_layoutStruct[i].computedLogicalWidth) {
available += m_layoutStruct[i].computedLogicalWidth - logicalWidth.value();
m_layoutStruct[i].computedLogicalWidth = logicalWidth.value();
}
}
}
// now satisfy relative
if (available > 0) {
for (size_t i = 0; i < nEffCols; ++i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isRelative() && logicalWidth.value() != 0) {
// width=0* gets effMinWidth.
int cellLogicalWidth = logicalWidth.value() * tableLogicalWidth / totalRelative;
available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth;
m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
}
}
}
// now satisfy variable
if (available > 0 && numAuto) {
available += allocAuto; // this gets redistributed
for (size_t i = 0; i < nEffCols; ++i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isAuto() && totalAuto && !m_layoutStruct[i].emptyCellsOnly) {
int cellLogicalWidth = max(m_layoutStruct[i].computedLogicalWidth, static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalAuto));
available -= cellLogicalWidth;
totalAuto -= m_layoutStruct[i].effectiveMaxLogicalWidth;
m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
}
}
}
// spread over fixed columns
if (available > 0 && numFixed) {
for (size_t i = 0; i < nEffCols; ++i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isFixed()) {
int cellLogicalWidth = static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalFixed);
available -= cellLogicalWidth;
totalFixed -= m_layoutStruct[i].effectiveMaxLogicalWidth;
m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth;
}
}
}
// spread over percent colums
if (available > 0 && m_hasPercent && totalPercent < 100) {
for (size_t i = 0; i < nEffCols; ++i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isPercent()) {
int cellLogicalWidth = available * logicalWidth.percent() / totalPercent;
available -= cellLogicalWidth;
totalPercent -= logicalWidth.percent();
m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth;
if (!available || !totalPercent)
break;
}
}
}
// spread over the rest
if (available > 0 && nEffCols > numAutoEmptyCellsOnly) {
int total = nEffCols - numAutoEmptyCellsOnly;
// still have some width to spread
for (int i = nEffCols - 1; i >= 0; --i) {
// variable columns with empty cells only don't get any width
if (m_layoutStruct[i].effectiveLogicalWidth.isAuto() && m_layoutStruct[i].emptyCellsOnly)
continue;
int cellLogicalWidth = available / total;
available -= cellLogicalWidth;
total--;
m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth;
}
}
// 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 logicalWidthBeyondMin = 0;
for (int i = nEffCols - 1; i >= 0; --i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isAuto())
logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
}
for (int i = nEffCols - 1; i >= 0 && logicalWidthBeyondMin > 0; --i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isAuto()) {
int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
int reduce = available * minMaxDiff / logicalWidthBeyondMin;
m_layoutStruct[i].computedLogicalWidth += reduce;
available -= reduce;
logicalWidthBeyondMin -= minMaxDiff;
if (available >= 0)
break;
}
}
}
if (available < 0) {
int logicalWidthBeyondMin = 0;
for (int i = nEffCols - 1; i >= 0; --i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isRelative())
logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
}
for (int i = nEffCols - 1; i >= 0 && logicalWidthBeyondMin > 0; --i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isRelative()) {
int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
int reduce = available * minMaxDiff / logicalWidthBeyondMin;
m_layoutStruct[i].computedLogicalWidth += reduce;
available -= reduce;
logicalWidthBeyondMin -= minMaxDiff;
if (available >= 0)
break;
}
}
}
if (available < 0) {
int logicalWidthBeyondMin = 0;
for (int i = nEffCols - 1; i >= 0; --i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isFixed())
logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
}
for (int i = nEffCols - 1; i >= 0 && logicalWidthBeyondMin > 0; --i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isFixed()) {
int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
int reduce = available * minMaxDiff / logicalWidthBeyondMin;
m_layoutStruct[i].computedLogicalWidth += reduce;
available -= reduce;
logicalWidthBeyondMin -= minMaxDiff;
if (available >= 0)
break;
}
}
}
if (available < 0) {
int logicalWidthBeyondMin = 0;
for (int i = nEffCols - 1; i >= 0; --i) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isPercent())
logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
}
for (int i = nEffCols-1; i >= 0 && logicalWidthBeyondMin > 0; i--) {
Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
if (logicalWidth.isPercent()) {
int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
int reduce = available * minMaxDiff / logicalWidthBeyondMin;
m_layoutStruct[i].computedLogicalWidth += reduce;
available -= reduce;
logicalWidthBeyondMin -= minMaxDiff;
if (available >= 0)
break;
}
}
}
}
int pos = 0;
for (size_t i = 0; i < nEffCols; ++i) {
m_table->columnPositions()[i] = pos;
pos += m_layoutStruct[i].computedLogicalWidth + m_table->hBorderSpacing();
}
m_table->columnPositions()[m_table->columnPositions().size() - 1] = pos;
}
}