summaryrefslogtreecommitdiffstats
path: root/tdehtml/rendering/table_layout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdehtml/rendering/table_layout.cpp')
-rw-r--r--tdehtml/rendering/table_layout.cpp1193
1 files changed, 1193 insertions, 0 deletions
diff --git a/tdehtml/rendering/table_layout.cpp b/tdehtml/rendering/table_layout.cpp
new file mode 100644
index 000000000..eb8153331
--- /dev/null
+++ b/tdehtml/rendering/table_layout.cpp
@@ -0,0 +1,1193 @@
+/*
+ * 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)
+ *
+ * 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 "table_layout.h"
+#include "render_table.h"
+
+#include <kglobal.h>
+
+using namespace tdehtml;
+
+// #define DEBUG_LAYOUT
+
+/*
+ The text below is from the CSS 2.1 specs.
+
+ Fixed table layout
+ ------------------
+
+ With this (fast) algorithm, the horizontal layout of the table does
+ not depend on the contents of the cells; it only depends on the
+ table's width, the width of the columns, and borders or cell
+ spacing.
+
+ The table's width may be specified explicitly with the 'width'
+ property. A value of 'auto' (for both 'display: table' and 'display:
+ inline-table') means use the automatic table layout algorithm.
+
+ In the fixed table layout algorithm, the width of each column is
+ determined as follows:
+
+ 1. A column element with a value other than 'auto' for the 'width'
+ property sets the width for that column.
+
+ 2. Otherwise, a cell in the first row with a value other than
+ 'auto' for the 'width' property sets the width for that column. If
+ the cell spans more than one column, the width is divided over the
+ columns.
+
+ 3. Any remaining columns equally divide the remaining horizontal
+ table space (minus borders or cell spacing).
+
+ The width of the table is then the greater of the value of the
+ 'width' property for the table element and the sum of the column
+ widths (plus cell spacing or borders). If the table is wider than
+ the columns, the extra space should be distributed over the columns.
+
+
+ In this manner, the user agent can begin to lay out the table once
+ the entire first row has been received. Cells in subsequent rows do
+ not affect column widths. Any cell that has content that overflows
+ uses the 'overflow' property to determine whether to clip the
+ overflow content.
+
+_____________________________________________________
+
+ This is not quite true when comparing to IE. IE always honors
+ table-layout:fixed and treats a variable table width as 100%. Makes
+ a lot of sense, and is implemented here the same way.
+
+*/
+
+FixedTableLayout::FixedTableLayout( RenderTable *table )
+ : TableLayout ( table )
+{
+}
+
+FixedTableLayout::~FixedTableLayout()
+{
+}
+
+int FixedTableLayout::calcWidthArray()
+{
+ int usedWidth = 0;
+
+ // iterate over all <col> elements
+ RenderObject *child = table->firstChild();
+ int cCol = 0;
+ int nEffCols = table->numEffCols();
+ width.resize( nEffCols );
+ width.fill( Length( Variable ) );
+
+#ifdef DEBUG_LAYOUT
+ tqDebug("FixedTableLayout::calcWidthArray()" );
+ tqDebug(" col elements:");
+#endif
+
+ Length grpWidth;
+ 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.isVariable() )
+ w = grpWidth;
+ int effWidth = 0;
+ if ( w.isFixed() && w.value() > 0 ) {
+ effWidth = w.value();
+ effWidth = KMIN( effWidth, 32760 );
+ }
+#ifdef DEBUG_LAYOUT
+ tqDebug(" col element: effCol=%d, span=%d: %d w=%d type=%d",
+ cCol, span, effWidth, w.value(), w.type());
+#endif
+ int usedSpan = 0;
+ int i = 0;
+ while ( usedSpan < span ) {
+ if( cCol + i >= nEffCols ) {
+ table->appendColumn( span - usedSpan );
+ nEffCols++;
+ width.resize( nEffCols );
+ width[nEffCols-1] = Length();
+ }
+ int eSpan = table->spanOfEffCol( cCol+i );
+ if ( (w.isFixed() || w.isPercent()) && w.value() > 0 ) {
+ width[cCol+i] = Length( w.value() * eSpan, w.type() );
+ usedWidth += effWidth * eSpan;
+#ifdef DEBUG_LAYOUT
+ tqDebug(" setting effCol %d (span=%d) to width %d(type=%d)",
+ cCol+i, eSpan, width[cCol+i].value(), width[cCol+i].type() );
+#endif
+ }
+ usedSpan += eSpan;
+ i++;
+ }
+ cCol += i;
+ }
+ } else {
+ break;
+ }
+
+ RenderObject *next = child->firstChild();
+ if ( !next )
+ next = child->nextSibling();
+ if ( !next && child->parent()->isTableCol() ) {
+ next = child->parent()->nextSibling();
+ grpWidth = Length();
+ }
+ child = next;
+ }
+
+#ifdef DEBUG_LAYOUT
+ tqDebug(" first row:");
+#endif
+ // iterate over the first row in case some are unspecified.
+ RenderTableSection *section = table->head;
+ if ( !section )
+ section = table->firstBody;
+ if ( !section )
+ section = table->foot;
+ if ( section && section->firstChild() ) {
+ cCol = 0;
+ // get the first cell in the first row
+ child = section->firstChild()->firstChild();
+ while ( child ) {
+ if ( child->isTableCell() ) {
+ RenderTableCell *cell = static_cast<RenderTableCell *>(child);
+ Length w = cell->styleOrColWidth();
+ int span = cell->colSpan();
+ int effWidth = 0;
+ if ( (w.isFixed() || w.isPercent()) && w.value() > 0 ) {
+ effWidth = w.value();
+ effWidth = kMin( effWidth, 32760 );
+ }
+#ifdef DEBUG_LAYOUT
+ tqDebug(" table cell: effCol=%d, span=%d: %d", cCol, span, effWidth);
+#endif
+ int usedSpan = 0;
+ int i = 0;
+ while ( usedSpan < span ) {
+ Q_ASSERT( cCol + i < nEffCols );
+ int eSpan = table->spanOfEffCol( cCol+i );
+ // only set if no col element has already set it.
+ if ( width[cCol+i].isVariable() && !w.isVariable() ) {
+ width[cCol+i] = Length( w.value()*eSpan, w.type() );
+ usedWidth += effWidth*eSpan;
+#ifdef DEBUG_LAYOUT
+ tqDebug(" setting effCol %d (span=%d) to width %d(type=%d)",
+ cCol+i, eSpan, width[cCol+i].value(), width[cCol+i].type() );
+#endif
+ }
+#ifdef DEBUG_LAYOUT
+ else {
+ tqDebug(" width of col %d already defined (span=%d)", cCol, table->spanOfEffCol( cCol ) );
+ }
+#endif
+ usedSpan += eSpan;
+ i++;
+ }
+ cCol += i;
+ } else {
+ Q_ASSERT( false );
+ }
+ child = child->nextSibling();
+ }
+ }
+
+ return usedWidth;
+
+}
+
+void FixedTableLayout::calcMinMaxWidth()
+{
+ // we might want to wait until we have all of the first row before
+ // layouting for the first time.
+
+ // only need to calculate the minimum width as the sum of the
+ // cols/cells with a fixed width.
+ //
+ // The maximum width is kMax( minWidth, tableWidth ) if table
+ // width is fixed. If table width is percent, we set maxWidth to
+ // unlimited.
+
+ int bs = table->bordersPaddingAndSpacing();
+ int tableWidth = 0;
+ if (table->style()->width().isFixed()) {
+ tableWidth = table->calcBoxWidth(table->style()->width().value());
+ }
+
+ int mw = calcWidthArray() + bs;
+ table->m_minWidth = kMin( kMax( mw, tableWidth ), 0x7fff );
+ table->m_maxWidth = table->m_minWidth;
+
+ if ( !tableWidth ) {
+ bool haveNonFixed = false;
+ for ( unsigned int i = 0; i < width.size(); i++ ) {
+ if ( !width[i].isFixed() ) {
+ haveNonFixed = true;
+ break;
+ }
+ }
+ if ( haveNonFixed )
+ table->m_maxWidth = 0x7fff;
+ }
+#ifdef DEBUG_LAYOUT
+ tqDebug("FixedTableLayout::calcMinMaxWidth: minWidth=%d, maxWidth=%d", table->m_minWidth, table->m_maxWidth );
+#endif
+}
+
+void FixedTableLayout::layout()
+{
+ int tableWidth = table->width() - table->bordersPaddingAndSpacing();
+ int available = tableWidth;
+ int nEffCols = table->numEffCols();
+#ifdef DEBUG_LAYOUT
+ tqDebug("FixedTableLayout::layout: tableWidth=%d, numEffCols=%d", tableWidth, nEffCols);
+#endif
+
+
+ TQMemArray<int> calcWidth;
+ calcWidth.resize( nEffCols );
+ calcWidth.fill( -1 );
+
+ // first assign fixed width
+ for ( int i = 0; i < nEffCols; i++ ) {
+ if ( width[i].isFixed() ) {
+ calcWidth[i] = width[i].value();
+ available -= width[i].value();
+ }
+ }
+
+ // assign percent width
+ if ( available > 0 ) {
+ int totalPercent = 0;
+ for ( int i = 0; i < nEffCols; i++ )
+ if ( width[i].isPercent() )
+ totalPercent += width[i].value();
+
+ // calculate how much to distribute to percent cells.
+ int base = tableWidth * totalPercent / 100;
+ if ( base > available )
+ base = available;
+
+#ifdef DEBUG_LAYOUT
+ tqDebug("FixedTableLayout::layout: assigning percent width, base=%d, totalPercent=%d", base, totalPercent);
+#endif
+ for ( int i = 0; available > 0 && i < nEffCols; i++ ) {
+ if ( width[i].isPercent() ) {
+ // totalPercent may be 0 below if all %-width specifed are 0%. (#172557)
+ int w = totalPercent ? base * width[i].value() / totalPercent : 0;
+ available -= w;
+ calcWidth[i] = w;
+ }
+ }
+ }
+
+ // assign variable width
+ if ( available > 0 ) {
+ int totalVariable = 0;
+ for ( int i = 0; i < nEffCols; i++ )
+ if ( width[i].isVariable() )
+ totalVariable++;
+
+ for ( int i = 0; available > 0 && i < nEffCols; i++ ) {
+ if ( width[i].isVariable() ) {
+ // totalVariable may be 0 below if all the variable widths specified are 0.
+ int w = totalVariable ? available / totalVariable : 0;
+ available -= w;
+ calcWidth[i] = w;
+ totalVariable--;
+ }
+ }
+ }
+
+ for ( int i = 0; i < nEffCols; i++ )
+ if ( calcWidth[i] < 0 )
+ calcWidth[i] = 0; // IE gives min 1 px...
+
+ // spread extra space over columns
+ if ( available > 0 ) {
+ int total = nEffCols;
+ // still have some width to spread
+ int i = nEffCols;
+ while ( i-- ) {
+ int w = available / total;
+ available -= w;
+ total--;
+ calcWidth[i] += w;
+ }
+ }
+
+ int pos = 0;
+ int hspacing = table->borderHSpacing();
+ for ( int i = 0; i < nEffCols; i++ ) {
+#ifdef DEBUG_LAYOUT
+ tqDebug("col %d: %d (width %d)", i, pos, calcWidth[i] );
+#endif
+ table->columnPos[i] = pos;
+ pos += calcWidth[i] + hspacing;
+ }
+ table->columnPos[table->columnPos.size()-1] = pos;
+}
+
+// -------------------------------------------------------------------------
+// -------------------------------------------------------------------------
+
+
+AutoTableLayout::AutoTableLayout( RenderTable* table )
+ : TableLayout( table )
+{
+ percentagesDirty = true;
+ effWidthDirty = true;
+ total_percent = 0;
+ hasPercent = false;
+}
+
+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 = layoutStruct[effCol];
+
+ RenderObject *child = 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++ ) {
+ RenderTableCell *cell = section->cellAt( i, effCol );
+ if ( cell == (RenderTableCell *)-1 )
+ 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 = kMax(int( l.minWidth ), 1);
+ l.maxWidth = kMax(int( l.maxWidth ), 1);
+
+ if ( !cell->minMaxKnown() )
+ cell->calcMinMaxWidth();
+ if ( cell->minWidth() > l.minWidth )
+ l.minWidth = cell->minWidth();
+ if ( cell->maxWidth() > l.maxWidth ) {
+ l.maxWidth = cell->maxWidth();
+ maxContributor = cell;
+ }
+
+ Length w = cell->styleOrColWidth();
+ w.l.value = kMin( 32767, kMax( 0, w.value() ) );
+ switch( w.type() ) {
+ case Fixed:
+ // ignore width=0
+ if ( w.value() > 0 && !l.width.isPercent() ) {
+ int wval = cell->calcBoxWidth(w.value());
+ if ( l.width.isFixed() ) {
+ // Nav/IE weirdness
+ if ((wval > l.width.value()) ||
+ ((l.width.value() == wval) && (maxContributor == cell))) {
+ l.width.l.value = wval;
+ fixedContributor = cell;
+ }
+ } else {
+ l.width = Length( wval, Fixed );
+ fixedContributor = cell;
+ }
+ }
+ break;
+ case Percent:
+ hasPercent = true;
+ if ( w.value() > 0 && (!l.width.isPercent() || w.value() > l.width.value() ) )
+ l.width = w;
+ break;
+ case Relative:
+ if ( w.isVariable() || (w.isRelative() && w.value() > l.width.value() ) )
+ l.width = w;
+ default:
+ break;
+ }
+ } else {
+ if ( cell && (!effCol || section->cellAt( 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.
+ l.minWidth = kMax(int( l.minWidth ), 1);
+ l.maxWidth = kMax(int( l.maxWidth ), 1);
+ insertSpanCell( cell );
+ }
+ last = cell;
+ }
+ }
+ }
+ child = child->nextSibling();
+ }
+
+ // Nav/IE weirdness
+ if ( l.width.isFixed() ) {
+ if ( table->style()->htmlHacks()
+ && (l.maxWidth > l.width.value()) && (fixedContributor != maxContributor)) {
+ l.width = Length();
+ fixedContributor = 0;
+ }
+ }
+
+ l.maxWidth = kMax(l.maxWidth, int(l.minWidth));
+#ifdef DEBUG_LAYOUT
+ tqDebug("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 aswell
+}
+
+
+void AutoTableLayout::fullRecalc()
+{
+ percentagesDirty = true;
+ hasPercent = false;
+ effWidthDirty = true;
+
+ int nEffCols = table->numEffCols();
+ layoutStruct.resize( nEffCols );
+ layoutStruct.fill( Layout() );
+ spanCells.fill( 0 );
+
+ RenderObject *child = 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.isVariable() )
+ w = grpWidth;
+ if ( (w.isFixed() && w.value() == 0) ||
+ (w.isPercent() && w.value() == 0) )
+ w = Length();
+ int cEffCol = table->colToEffCol( cCol );
+#ifdef DEBUG_LAYOUT
+ tqDebug(" col element %d (eff=%d): Length=%d(%d), span=%d, effColSpan=%d", cCol, cEffCol, w.value(), w.type(), span, table->spanOfEffCol(cEffCol ) );
+#endif
+ if ( !w.isVariable() && span == 1 && cEffCol < nEffCols ) {
+ if ( table->spanOfEffCol( cEffCol ) == 1 ) {
+ layoutStruct[cEffCol].width = w;
+ if (w.isFixed() && layoutStruct[cEffCol].maxWidth < w.value())
+ 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.isVariable() || tw.isPercent()) && !table->isPositioned()) {
+ RenderBlock* cb = table->containingBlock();
+ while (cb && !cb->isCanvas() && !cb->isTableCell() &&
+ cb->style()->width().isVariable() && !cb->isPositioned())
+ cb = cb->containingBlock();
+
+ table = 0;
+ if (cb && cb->isTableCell() &&
+ (cb->style()->width().isVariable() || cb->style()->width().isPercent())) {
+ if (tw.isPercent())
+ scale = false;
+ else {
+ RenderTableCell* cell = static_cast<RenderTableCell*>(cb);
+ if (cell->colSpan() > 1 || cell->table()->style()->width().isVariable())
+ scale = false;
+ else
+ table = cell->table();
+ }
+ }
+ }
+ else
+ table = 0;
+ }
+ return scale;
+}
+
+void AutoTableLayout::calcMinMaxWidth()
+{
+#ifdef DEBUG_LAYOUT
+ tqDebug("AutoTableLayout::calcMinMaxWidth");
+#endif
+ fullRecalc();
+
+ int spanMaxWidth = calcEffectiveWidth();
+ int minWidth = 0;
+ int maxWidth = 0;
+ int maxPercent = 0;
+ int maxNonPercent = 0;
+
+ int remainingPercent = 100;
+ for ( unsigned int i = 0; i < layoutStruct.size(); i++ ) {
+ minWidth += layoutStruct[i].effMinWidth;
+ maxWidth += layoutStruct[i].effMaxWidth;
+ if ( layoutStruct[i].effWidth.isPercent() ) {
+ int percent = kMin(layoutStruct[i].effWidth.value(), remainingPercent);
+ int pw = ( layoutStruct[i].effMaxWidth * 100) / kMax(percent, 1);
+ remainingPercent -= percent;
+ maxPercent = kMax( pw, maxPercent );
+ } else {
+ maxNonPercent += layoutStruct[i].effMaxWidth;
+ }
+ }
+
+ if (shouldScaleColumns(table)) {
+ maxNonPercent = (maxNonPercent * 100 + 50) / kMax(remainingPercent, 1);
+ maxWidth = kMax( maxNonPercent, maxWidth );
+ maxWidth = kMax( maxWidth, maxPercent );
+ }
+
+ maxWidth = kMax( maxWidth, spanMaxWidth );
+
+ int bs = table->bordersPaddingAndSpacing();
+ minWidth += bs;
+ maxWidth += bs;
+
+ Length tw = table->style()->width();
+ if ( tw.isFixed() && tw.value() > 0 ) {
+ int width = table->calcBoxWidth(tw.value());
+ minWidth = kMax( minWidth, width );
+ maxWidth = minWidth;
+ }
+
+ table->m_maxWidth = kMin(maxWidth, 0x7fff);
+ table->m_minWidth = kMin(minWidth, 0x7fff);
+#ifdef DEBUG_LAYOUT
+ tqDebug(" minWidth=%d, maxWidth=%d", table->m_minWidth, table->m_maxWidth );
+#endif
+}
+
+/*
+ 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()
+{
+ int tMaxWidth = 0;
+
+ unsigned int nEffCols = layoutStruct.size();
+ int hspacing = table->borderHSpacing();
+#ifdef DEBUG_LAYOUT
+ tqDebug("AutoTableLayout::calcEffectiveWidth for %d cols", nEffCols );
+#endif
+ for ( unsigned int i = 0; i < nEffCols; i++ ) {
+ layoutStruct[i].effWidth = layoutStruct[i].width;
+ layoutStruct[i].effMinWidth = layoutStruct[i].minWidth;
+ layoutStruct[i].effMaxWidth = layoutStruct[i].maxWidth;
+ }
+
+ for ( unsigned int i = 0; i < spanCells.size(); i++ ) {
+ RenderTableCell *cell = spanCells[i];
+ if ( !cell || cell == (RenderTableCell *)-1 )
+ break;
+ int span = cell->colSpan();
+
+ Length w = cell->styleOrColWidth();
+ if ( !w.isRelative() && w.value() == 0 )
+ w = Length(); // make it Variable
+
+ int col = table->colToEffCol( cell->col() );
+ unsigned int lastCol = col;
+ int cMinWidth = cell->minWidth() + hspacing;
+ int cMaxWidth = cell->maxWidth() + hspacing;
+ int totalPercent = 0;
+ int minWidth = 0;
+ int maxWidth = 0;
+ bool allColsArePercent = true;
+ bool allColsAreFixed = true;
+ bool haveVariable = false;
+ int fixedWidth = 0;
+#ifdef DEBUG_LAYOUT
+ int cSpan = span;
+#endif
+ while ( lastCol < nEffCols && span > 0 ) {
+ switch( layoutStruct[lastCol].width.type() ) {
+ case Percent:
+ totalPercent += layoutStruct[lastCol].width.value();
+ allColsAreFixed = false;
+ break;
+ case Fixed:
+ if (layoutStruct[lastCol].width.value() > 0) {
+ fixedWidth += layoutStruct[lastCol].width.value();
+ allColsArePercent = false;
+ // IE resets effWidth to Variable here, but this breaks the konqueror about page and seems to be some bad
+ // legacy behavior anyway. mozilla doesn't do this so I decided we don't either.
+ break;
+ }
+ // fall through
+ case Variable:
+ haveVariable = 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 (!layoutStruct[lastCol].effWidth.isPercent()) {
+ layoutStruct[lastCol].effWidth = Length();
+ allColsArePercent = false;
+ }
+ else
+ totalPercent += layoutStruct[lastCol].effWidth.value();
+ allColsAreFixed = false;
+ }
+ span -= table->spanOfEffCol( lastCol );
+ minWidth += layoutStruct[lastCol].effMinWidth;
+ maxWidth += layoutStruct[lastCol].effMaxWidth;
+ lastCol++;
+ cMinWidth -= hspacing;
+ cMaxWidth -= hspacing;
+ }
+#ifdef DEBUG_LAYOUT
+ tqDebug(" colspan cell %p at effCol %d, span %d, type %d, value %d cmin=%d min=%d fixedwidth=%d", cell, col, cSpan, w.type(), w.value(), cMinWidth, minWidth, fixedWidth );
+#endif
+
+ // adjust table max width if needed
+ if ( w.isPercent() ) {
+ if ( totalPercent > w.value() || allColsArePercent ) {
+ // can't satify this condition, treat as variable
+ w = Length();
+ } else {
+ int spanMax = kMax( maxWidth, cMaxWidth );
+#ifdef DEBUG_LAYOUT
+ tqDebug(" adjusting tMaxWidth (%d): spanMax=%d, value=%d, totalPercent=%d", tMaxWidth, spanMax, w.value(), totalPercent );
+#endif
+ tMaxWidth = kMax( tMaxWidth, spanMax * 100 / w.value() );
+
+ // all non percent columns in the span get percent values to sum up correctly.
+ int percentMissing = w.value() - totalPercent;
+ int totalWidth = 0;
+ for ( unsigned int pos = col; pos < lastCol; pos++ ) {
+ if ( !(layoutStruct[pos].width.isPercent() ) )
+ totalWidth += layoutStruct[pos].effMaxWidth;
+ }
+
+ for ( unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++ ) {
+ if ( !(layoutStruct[pos].width.isPercent() ) ) {
+ int percent = percentMissing * layoutStruct[pos].effMaxWidth / totalWidth;
+#ifdef DEBUG_LAYOUT
+ tqDebug(" col %d: setting percent value %d effMaxWidth=%d totalWidth=%d", pos, percent, layoutStruct[pos].effMaxWidth, totalWidth );
+#endif
+ totalWidth -= layoutStruct[pos].effMaxWidth;
+ percentMissing -= percent;
+ if ( percent > 0 )
+ layoutStruct[pos].effWidth = Length( percent, Percent );
+ else
+ layoutStruct[pos].effWidth = Length();
+ }
+ }
+
+ }
+ }
+
+ // make sure minWidth and maxWidth of the spanning cell are honoured
+ if ( cMinWidth > minWidth ) {
+ if ( allColsAreFixed ) {
+#ifdef DEBUG_LAYOUT
+ tqDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d accroding to fixed sum %d", col, lastCol-1, cMinWidth, minWidth, fixedWidth );
+#endif
+ for ( unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++ ) {
+ int w = kMax( int( layoutStruct[pos].effMinWidth ), cMinWidth * layoutStruct[pos].width.value() / fixedWidth );
+#ifdef DEBUG_LAYOUT
+ tqDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
+#endif
+ fixedWidth -= layoutStruct[pos].width.value();
+ cMinWidth -= w;
+ layoutStruct[pos].effMinWidth = w;
+ }
+
+ } else if ( allColsArePercent ) {
+ int maxw = maxWidth;
+ int minw = minWidth;
+ int cminw = cMinWidth;
+
+ for ( unsigned int pos = col; maxw > 0 && pos < lastCol; pos++ ) {
+ if ( layoutStruct[pos].effWidth.isPercent() && layoutStruct[pos].effWidth.value()>0 && fixedWidth <= cMinWidth) {
+ int w = layoutStruct[pos].effMinWidth;
+ w = kMax( w, cminw*layoutStruct[pos].effWidth.value()/totalPercent );
+ w = kMin(layoutStruct[pos].effMinWidth+(cMinWidth-minw), w);
+#ifdef DEBUG_LAYOUT
+ tqDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
+#endif
+ maxw -= layoutStruct[pos].effMaxWidth;
+ minw -= layoutStruct[pos].effMinWidth;
+ cMinWidth -= w;
+ layoutStruct[pos].effMinWidth = w;
+ }
+ }
+ } else {
+#ifdef DEBUG_LAYOUT
+ tqDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d", col, lastCol-1, cMinWidth, minWidth );
+#endif
+ int 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 ( layoutStruct[pos].width.isFixed() && haveVariable && fixedWidth <= cMinWidth ) {
+ int w = kMax( int( layoutStruct[pos].effMinWidth ), layoutStruct[pos].width.value() );
+ fixedWidth -= layoutStruct[pos].width.value();
+ minw -= layoutStruct[pos].effMinWidth;
+#ifdef DEBUG_LAYOUT
+ tqDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
+#endif
+ maxw -= layoutStruct[pos].effMaxWidth;
+ cMinWidth -= w;
+ layoutStruct[pos].effMinWidth = w;
+ }
+ }
+
+ for ( unsigned int pos = col; maxw > 0 && pos < lastCol && minw < cMinWidth; pos++ ) {
+ if ( !(layoutStruct[pos].width.isFixed() && haveVariable && fixedWidth <= cMinWidth) ) {
+ int w = kMax( int( layoutStruct[pos].effMinWidth ), cMinWidth * layoutStruct[pos].effMaxWidth / maxw );
+ w = kMin(layoutStruct[pos].effMinWidth+(cMinWidth-minw), w);
+
+#ifdef DEBUG_LAYOUT
+ tqDebug(" col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
+#endif
+ maxw -= layoutStruct[pos].effMaxWidth;
+ minw -= layoutStruct[pos].effMinWidth;
+ cMinWidth -= w;
+ layoutStruct[pos].effMinWidth = w;
+ }
+ }
+ }
+ }
+ if ( !w.isPercent() ) {
+ if ( cMaxWidth > maxWidth ) {
+#ifdef DEBUG_LAYOUT
+ tqDebug("extending maxWidth of cols %d-%d to %dpx", col, lastCol-1, cMaxWidth );
+#endif
+ for ( unsigned int pos = col; maxWidth > 0 && pos < lastCol; pos++ ) {
+ int w = kMax( int( layoutStruct[pos].effMaxWidth ), cMaxWidth * layoutStruct[pos].effMaxWidth / maxWidth );
+#ifdef DEBUG_LAYOUT
+ tqDebug(" col %d: max=%d, effMax=%d, new=%d", pos, layoutStruct[pos].effMaxWidth, layoutStruct[pos].effMaxWidth, w );
+#endif
+ maxWidth -= layoutStruct[pos].effMaxWidth;
+ cMaxWidth -= w;
+ layoutStruct[pos].effMaxWidth = w;
+ }
+ }
+ } else {
+ for ( unsigned int pos = col; pos < lastCol; pos++ )
+ layoutStruct[pos].maxWidth = kMax(layoutStruct[pos].maxWidth, int(layoutStruct[pos].minWidth) );
+ }
+ }
+ effWidthDirty = false;
+
+// tqDebug("calcEffectiveWidth: tMaxWidth=%d", tMaxWidth );
+ return tMaxWidth;
+}
+
+/* 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 == (RenderTableCell *)-1 || cell->colSpan() == 1 )
+ return;
+
+// tqDebug("inserting span cell %p with span %d", cell, cell->colSpan() );
+ int size = spanCells.size();
+ if ( !size || spanCells[size-1] != 0 ) {
+ spanCells.resize( size + 10 );
+ for ( int i = 0; i < 10; i++ )
+ 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 < spanCells.size() && spanCells[pos] && span > spanCells[pos]->colSpan() )
+ pos++;
+ memmove( spanCells.data()+pos+1, spanCells.data()+pos, (size-pos-1)*sizeof( RenderTableCell * ) );
+ spanCells[pos] = cell;
+}
+
+
+void AutoTableLayout::layout()
+{
+ // table layout based on the values collected in the layout structure.
+ int tableWidth = table->width() - table->bordersPaddingAndSpacing();
+ int available = tableWidth;
+ int nEffCols = table->numEffCols();
+
+ if ( nEffCols != (int)layoutStruct.size() ) {
+ tqWarning("WARNING: nEffCols is not equal to layoutstruct!" );
+ fullRecalc();
+ nEffCols = table->numEffCols();
+ }
+#ifdef DEBUG_LAYOUT
+ tqDebug("AutoTableLayout::layout()");
+#endif
+
+ if ( effWidthDirty )
+ calcEffectiveWidth();
+
+#ifdef DEBUG_LAYOUT
+ tqDebug(" tableWidth=%d, nEffCols=%d", tableWidth, nEffCols );
+ for ( int i = 0; i < nEffCols; i++ ) {
+ tqDebug(" effcol %d is of type %d value %d, minWidth=%d, maxWidth=%d",
+ i, layoutStruct[i].width.type(), layoutStruct[i].width.value(),
+ layoutStruct[i].minWidth, layoutStruct[i].maxWidth );
+ tqDebug(" effective: type %d value %d, minWidth=%d, maxWidth=%d",
+ layoutStruct[i].effWidth.type(), layoutStruct[i].effWidth.value(),
+ layoutStruct[i].effMinWidth, layoutStruct[i].effMaxWidth );
+ }
+#endif
+
+ bool havePercent = false;
+ bool haveRelative = false;
+ int totalRelative = 0;
+ int numVariable = 0;
+ int numFixed = 0;
+ int totalVariable = 0;
+ int totalFixed = 0;
+ int totalPercent = 0;
+ int allocVariable = 0;
+
+ // fill up every cell with it's minWidth
+ for ( int i = 0; i < nEffCols; i++ ) {
+ int w = layoutStruct[i].effMinWidth;
+ layoutStruct[i].calcWidth = w;
+ available -= w;
+ Length& width = layoutStruct[i].effWidth;
+ switch( width.type()) {
+ case Percent:
+ havePercent = true;
+ totalPercent += width.value();
+ break;
+ case Relative:
+ haveRelative = true;
+ totalRelative += width.value();
+ break;
+ case Fixed:
+ numFixed++;
+ totalFixed += layoutStruct[i].effMaxWidth;
+ // fall through
+ break;
+ case Variable:
+ case Static:
+ numVariable++;
+ totalVariable += layoutStruct[i].effMaxWidth;
+ allocVariable += w;
+ }
+ }
+
+ // allocate width to percent cols
+ if ( available > 0 && havePercent ) {
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isPercent() ) {
+ int w = kMax ( int( layoutStruct[i].effMinWidth ), width.minWidth( tableWidth ) );
+ available += layoutStruct[i].calcWidth - w;
+ layoutStruct[i].calcWidth = w;
+ }
+ }
+ if ( totalPercent > 100 ) {
+ // remove overallocated space from the last columns
+ int excess = tableWidth*(totalPercent-100)/100;
+ for ( int i = nEffCols-1; i >= 0; i-- ) {
+ if ( layoutStruct[i].effWidth.isPercent() ) {
+ int w = layoutStruct[i].calcWidth;
+ int reduction = kMin( w, excess );
+ // the lines below might look inconsistent, but that's the way it's handled in mozilla
+ excess -= reduction;
+ int newWidth = kMax( int (layoutStruct[i].effMinWidth), w - reduction );
+ available += w - newWidth;
+ layoutStruct[i].calcWidth = newWidth;
+ //tqDebug("col %d: reducing to %d px (reduction=%d)", i, newWidth, reduction );
+ }
+ }
+ }
+ }
+#ifdef DEBUG_LAYOUT
+ tqDebug("percent satisfied: available is %d", available);
+#endif
+
+ // then allocate width to fixed cols
+ if ( available > 0 ) {
+ for ( int i = 0; i < nEffCols; ++i ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isFixed() && width.value() > layoutStruct[i].calcWidth ) {
+ available += layoutStruct[i].calcWidth - width.value();
+ layoutStruct[i].calcWidth = width.value();
+ }
+ }
+ }
+#ifdef DEBUG_LAYOUT
+ tqDebug("fixed satisfied: available is %d", available);
+#endif
+
+ // now satisfy relative
+ if ( available > 0 ) {
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isRelative() && width.value() ) {
+ // width=0* gets effMinWidth.
+ int w = width.value()*tableWidth/totalRelative;
+ available += layoutStruct[i].calcWidth - w;
+ layoutStruct[i].calcWidth = w;
+ }
+ }
+ }
+
+ // now satisfy variable
+ if ( available > 0 && numVariable ) {
+ available += allocVariable; // this gets redistributed
+ //tqDebug("redistributing %dpx to %d variable columns. totalVariable=%d", available, numVariable, totalVariable );
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isVariable() && totalVariable != 0 ) {
+ int w = kMax( int ( layoutStruct[i].calcWidth ),
+ available * layoutStruct[i].effMaxWidth / totalVariable );
+ available -= w;
+ totalVariable -= layoutStruct[i].effMaxWidth;
+ layoutStruct[i].calcWidth = w;
+ }
+ }
+ }
+#ifdef DEBUG_LAYOUT
+ tqDebug("variable satisfied: available is %d", available );
+#endif
+
+ // spread over fixed colums
+ if ( available > 0 && numFixed) {
+ // still have some width to spread, distribute to fixed columns
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isFixed() ) {
+ int w = available * layoutStruct[i].effMaxWidth / totalFixed;
+ available -= w;
+ totalFixed -= layoutStruct[i].effMaxWidth;
+ layoutStruct[i].calcWidth += w;
+ }
+ }
+ }
+
+#ifdef DEBUG_LAYOUT
+ tqDebug("after fixed distribution: available=%d", available );
+#endif
+
+ // spread over percent colums
+ if ( available > 0 && hasPercent && totalPercent < 100) {
+ // still have some width to spread, distribute weighted to percent columns
+ for ( int i = 0; i < nEffCols; i++ ) {
+ const Length &width = layoutStruct[i].effWidth;
+ if ( width.isPercent() ) {
+ int w = available * width.value() / totalPercent;
+ available -= w;
+ totalPercent -= width.value();
+ layoutStruct[i].calcWidth += w;
+ if (!available || !totalPercent) break;
+ }
+ }
+ }
+
+#ifdef DEBUG_LAYOUT
+ tqDebug("after percent distribution: available=%d", available );
+#endif
+
+ // spread over the rest
+ if ( available > 0 ) {
+ int total = nEffCols;
+ // still have some width to spread
+ int i = nEffCols;
+ while ( i-- ) {
+ int w = available / total;
+ available -= w;
+ total--;
+ layoutStruct[i].calcWidth += w;
+ }
+ }
+
+#ifdef DEBUG_LAYOUT
+ tqDebug("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) Variable
+ // (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 = layoutStruct[i].effWidth;
+ if (width.isVariable())
+ mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
+ }
+
+ for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isVariable()) {
+ int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
+ int reduce = available * minMaxDiff / mw;
+ 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 = layoutStruct[i].effWidth;
+ if (width.isRelative())
+ mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
+ }
+
+ for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isRelative()) {
+ int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
+ int reduce = available * minMaxDiff / mw;
+ 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 = layoutStruct[i].effWidth;
+ if (width.isFixed())
+ mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
+ }
+
+ for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isFixed()) {
+ int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
+ int reduce = available * minMaxDiff / mw;
+ 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 = layoutStruct[i].effWidth;
+ if (width.isPercent())
+ mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
+ }
+
+ for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
+ Length &width = layoutStruct[i].effWidth;
+ if (width.isPercent()) {
+ int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
+ int reduce = available * minMaxDiff / mw;
+ layoutStruct[i].calcWidth += reduce;
+ available -= reduce;
+ mw -= minMaxDiff;
+ if ( available >= 0 )
+ break;
+ }
+ }
+ }
+ }
+
+ //tqDebug( " final available=%d", available );
+
+ int pos = 0;
+ for ( int i = 0; i < nEffCols; i++ ) {
+#ifdef DEBUG_LAYOUT
+ tqDebug("col %d: %d (width %d)", i, pos, layoutStruct[i].calcWidth );
+#endif
+ table->columnPos[i] = pos;
+ pos += layoutStruct[i].calcWidth + table->borderHSpacing();
+ }
+ table->columnPos[table->columnPos.size()-1] = pos;
+
+}
+
+
+void AutoTableLayout::calcPercentages() const
+{
+ total_percent = 0;
+ for ( unsigned int i = 0; i < layoutStruct.size(); i++ ) {
+ if ( layoutStruct[i].width.isPercent() )
+ total_percent += layoutStruct[i].width.value();
+ }
+ percentagesDirty = false;
+}
+
+#undef DEBUG_LAYOUT