blob: 06adf206fac018a55c74335f1ff3774f526cc00c [file] [log] [blame]
/*
* This file is part of the HTML rendering engine for KDE.
*
* Copyright (C) 2002 Lars Knoll (knoll@kde.org)
* (C) 2002 Dirk Mueller (mueller@kde.org)
* Copyright (C) 2003, 2006 Apple Computer, Inc.
*
* 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->isTableSection()) {
RenderTableSection* section = static_cast<RenderTableSection*>(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() || m_table->cellPadding() || 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);
#ifdef DEBUG_LAYOUT
qDebug("col %d, final min=%d, max=%d, width=%d(%d)", effCol, l.minWidth, l.maxWidth, l.width.value, l.width.type);
#endif
// ### 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 = static_cast<RenderTableCol*>(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);
#ifdef DEBUG_LAYOUT
qDebug(" col element %d (eff=%d): Length=%d(%d), span=%d, effColSpan=%d", cCol, cEffCol, w.value, w.type, span, m_table->spanOfEffCol(cEffCol));
#endif
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 = static_cast<RenderTableCell*>(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();
#ifdef DEBUG_LAYOUT
qDebug("AutoTableLayout::calcEffectiveWidth for %d cols", nEffCols);
#endif
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 vlaues 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.resize(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();
}
#ifdef DEBUG_LAYOUT
qDebug("AutoTableLayout::layout()");
#endif
if (m_effWidthDirty)
calcEffectiveWidth();
#ifdef DEBUG_LAYOUT
qDebug(" tableWidth=%d, nEffCols=%d", tableWidth, nEffCols);
for (int i = 0; i < nEffCols; i++) {
qDebug(" effcol %d is of type %d value %d, minWidth=%d, maxWidth=%d",
i, m_layoutStruct[i].width.type, m_layoutStruct[i].width.value,
m_layoutStruct[i].minWidth, m_layoutStruct[i].maxWidth);
qDebug(" effective: type %d value %d, minWidth=%d, maxWidth=%d",
m_layoutStruct[i].effWidth.type, m_layoutStruct[i].effWidth.value,
m_layoutStruct[i].effMinWidth, m_layoutStruct[i].effMaxWidth);
}
#endif
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(int (m_layoutStruct[i].effMinWidth), w - reduction);
available += w - newWidth;
m_layoutStruct[i].calcWidth = newWidth;
}
}
}
}
#ifdef DEBUG_LAYOUT
qDebug("percent satisfied: available is %d", available);
#endif
// 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();
}
}
}
#ifdef DEBUG_LAYOUT
qDebug("fixed satisfied: available is %d", available);
#endif
// 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;
}
}
}
#ifdef DEBUG_LAYOUT
qDebug("variable satisfied: available is %d", available);
#endif
// 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;
}
}
}
#ifdef DEBUG_LAYOUT
qDebug("after fixed distribution: available=%d", available);
#endif
// 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;
}
}
}
#ifdef DEBUG_LAYOUT
qDebug("after percent distribution: available=%d", available);
#endif
// 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].width.isAuto() && m_layoutStruct[i].emptyCellsOnly)
continue;
int w = available / total;
available -= w;
total--;
m_layoutStruct[i].calcWidth += w;
}
}
#ifdef DEBUG_LAYOUT
qDebug("after equal distribution: available=%d", available);
#endif
// if we have overallocated, reduce every cell according to the difference between desired width and minwidth
// this seems to produce to the pixel exaxt 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++) {
#ifdef DEBUG_LAYOUT
qDebug("col %d: %d (width %d)", i, pos, m_layoutStruct[i].calcWidth);
#endif
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
}