File indexing completed on 2024-04-28 15:24:22

0001 /*
0002  * This file is part of the HTML rendering engine for KDE.
0003  *
0004  * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
0005  *           (C) 2002 Dirk Mueller (mueller@kde.org)
0006  *
0007  * This library is free software; you can redistribute it and/or
0008  * modify it under the terms of the GNU Library General Public
0009  * License as published by the Free Software Foundation; either
0010  * version 2 of the License.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Library General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Library General Public License
0018  * along with this library; see the file COPYING.LIB.  If not, write to
0019  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021  *
0022  */
0023 #include "table_layout.h"
0024 #include "render_table.h"
0025 
0026 using namespace khtml;
0027 
0028 // #define DEBUG_LAYOUT
0029 
0030 /*
0031   The text below is from the CSS 2.1 specs.
0032 
0033   Fixed table layout
0034   ------------------
0035 
0036   With this (fast) algorithm, the horizontal layout of the table does
0037   not depend on the contents of the cells; it only depends on the
0038   table's width, the width of the columns, and borders or cell
0039   spacing.
0040 
0041   The table's width may be specified explicitly with the 'width'
0042   property. A value of 'auto' (for both 'display: table' and 'display:
0043   inline-table') means use the automatic table layout algorithm.
0044 
0045   In the fixed table layout algorithm, the width of each column is
0046   determined as follows:
0047 
0048     1. A column element with a value other than 'auto' for the 'width'
0049     property sets the width for that column.
0050 
0051     2. Otherwise, a cell in the first row with a value other than
0052     'auto' for the 'width' property sets the width for that column. If
0053     the cell spans more than one column, the width is divided over the
0054     columns.
0055 
0056     3. Any remaining columns equally divide the remaining horizontal
0057     table space (minus borders or cell spacing).
0058 
0059   The width of the table is then the greater of the value of the
0060   'width' property for the table element and the sum of the column
0061   widths (plus cell spacing or borders). If the table is wider than
0062   the columns, the extra space should be distributed over the columns.
0063 
0064   In this manner, the user agent can begin to lay out the table once
0065   the entire first row has been received. Cells in subsequent rows do
0066   not affect column widths. Any cell that has content that overflows
0067   uses the 'overflow' property to determine whether to clip the
0068   overflow content.
0069 
0070 _____________________________________________________
0071 
0072   This is not quite true when comparing to IE. IE always honors
0073   table-layout:fixed and treats a variable table width as 100%. Makes
0074   a lot of sense, and is implemented here the same way.
0075 
0076 */
0077 
0078 FixedTableLayout::FixedTableLayout(RenderTable *table)
0079     : TableLayout(table)
0080 {
0081 }
0082 
0083 FixedTableLayout::~FixedTableLayout()
0084 {
0085 }
0086 
0087 int FixedTableLayout::calcWidthArray()
0088 {
0089     int usedWidth = 0;
0090 
0091     // iterate over all <col> elements
0092     RenderObject *child = table->firstChild();
0093     int nEffCols = table->numEffCols();
0094     width.resize(nEffCols);
0095     width.fill(Length(Auto));
0096 
0097 #ifdef DEBUG_LAYOUT
0098     qDebug("FixedTableLayout::calcWidthArray()");
0099     qDebug("    col elements:");
0100 #endif
0101 
0102     int currentEffectiveColumn = 0;
0103     Length grpWidth;
0104     while (child) {
0105         if (child->isTableCol()) {
0106             RenderTableCol *col = static_cast<RenderTableCol *>(child);
0107             if (col->firstChild()) {
0108                 grpWidth = col->style()->width();
0109             } else {
0110                 Length w = col->style()->width();
0111                 if (w.isAuto()) {
0112                     w = grpWidth;
0113                 }
0114                 int effWidth = 0;
0115                 if (w.isFixed() && w.isPositive()) {
0116                     effWidth = w.value();
0117                     effWidth = qMin(effWidth, 32760);
0118                 }
0119 #ifdef DEBUG_LAYOUT
0120                 qDebug("    col element: effCol=%d, span=%d: %d w=%d type=%d",
0121                        cCol, span, effWidth,  w.rawValue(), w.type());
0122 #endif
0123                 int span = col->span();
0124                 while (span) {
0125                     int spanInCurrentEffectiveColumn;
0126                     if (currentEffectiveColumn >= nEffCols) {
0127                         table->appendColumn(span);
0128                         nEffCols++;
0129                         width.append(Length());
0130                         spanInCurrentEffectiveColumn = span;
0131                     } else {
0132                         if (span < table->spanOfEffCol(currentEffectiveColumn)) {
0133                             table->splitColumn(currentEffectiveColumn, span);
0134                             nEffCols++;
0135                             width.append(Length());
0136                         }
0137                         spanInCurrentEffectiveColumn = table->spanOfEffCol(currentEffectiveColumn);
0138                     }
0139                     if ((w.isFixed() || w.isPercent()) && w.isPositive()) {
0140                         width[currentEffectiveColumn].setRawValue(w.type(), w.rawValue() * spanInCurrentEffectiveColumn);
0141                         usedWidth += effWidth * spanInCurrentEffectiveColumn;
0142 #ifdef DEBUG_LAYOUT
0143                         qDebug("    setting effCol %d (span=%d) to width %d(type=%d)",
0144                                cCol + i, eSpan, width[cCol + i].rawValue(), width[cCol + i].type());
0145 #endif
0146                     }
0147                     span -= spanInCurrentEffectiveColumn;
0148                     currentEffectiveColumn++;
0149                 }
0150             }
0151         } else {
0152             break;
0153         }
0154 
0155         RenderObject *next = child->firstChild();
0156         if (!next) {
0157             next = child->nextSibling();
0158         }
0159         if (!next && child->parent()->isTableCol()) {
0160             next = child->parent()->nextSibling();
0161             grpWidth = Length();
0162         }
0163         child = next;
0164     }
0165 
0166 #ifdef DEBUG_LAYOUT
0167     qDebug("    first row:");
0168 #endif
0169     // iterate over the first row in case some are unspecified.
0170     RenderTableSection *section = table->head;
0171     if (!section) {
0172         section = table->firstBody;
0173     }
0174     if (!section) {
0175         section = table->foot;
0176     }
0177     if (section && section->firstChild()) {
0178         int cCol = 0;
0179         // get the first cell in the first row
0180         child = section->firstChild()->firstChild();
0181         while (child) {
0182             if (child->isTableCell()) {
0183                 RenderTableCell *cell = static_cast<RenderTableCell *>(child);
0184                 Length w = cell->styleOrColWidth();
0185                 int span = cell->colSpan();
0186                 int effWidth = 0;
0187                 // FIXME: This does not make sense (mixing percentages with absolute length)
0188                 if ((w.isFixed() || w.isPercent()) && w.isPositive()) {
0189                     effWidth = w.isPercent() ? w.rawValue() / PERCENT_SCALE_FACTOR : w.value();
0190                     effWidth = qMin(effWidth, 32760);
0191                 }
0192 #ifdef DEBUG_LAYOUT
0193                 qDebug("    table cell: effCol=%d, span=%d: %d",  cCol, span, effWidth);
0194 #endif
0195                 int usedSpan = 0;
0196                 int i = 0;
0197                 while (usedSpan < span) {
0198                     Q_ASSERT(cCol + i < nEffCols);
0199                     int eSpan = table->spanOfEffCol(cCol + i);
0200                     // only set if no col element has already set it.
0201                     if (width[cCol + i].isAuto() && !w.isAuto()) {
0202                         width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan);
0203                         usedWidth += effWidth * eSpan;
0204 #ifdef DEBUG_LAYOUT
0205                         qDebug("    setting effCol %d (span=%d) to width %d(type=%d)",
0206                                cCol + i, eSpan, width[cCol + i].rawValue(), width[cCol + i].type());
0207 #endif
0208                     }
0209 #ifdef DEBUG_LAYOUT
0210                     else {
0211                         qDebug("    width of col %d already defined (span=%d)", cCol, table->spanOfEffCol(cCol));
0212                     }
0213 #endif
0214                     usedSpan += eSpan;
0215                     i++;
0216                 }
0217                 cCol += i;
0218             } else {
0219                 Q_ASSERT(false);
0220             }
0221             child = child->nextSibling();
0222         }
0223     }
0224 
0225     return usedWidth;
0226 
0227 }
0228 
0229 void FixedTableLayout::calcMinMaxWidth()
0230 {
0231     // we might want to wait until we have all of the first row before
0232     // layouting for the first time.
0233 
0234     // only need to calculate the minimum width as the sum of the
0235     // cols/cells with a fixed width.
0236     //
0237     // The maximum width is qMax( minWidth, tableWidth ) if table
0238     // width is fixed. If table width is percent, we set maxWidth to
0239     // unlimited.
0240 
0241     int bs = table->bordersPaddingAndSpacing();
0242     int tableWidth = 0;
0243     if (table->style()->width().isFixed()) {
0244         tableWidth = table->calcBoxWidth(table->style()->width().value());
0245     }
0246 
0247     int mw = calcWidthArray() + bs;
0248     table->m_minWidth = qMin(qMax(mw, tableWidth), 0x7fff);
0249     table->m_maxWidth = table->m_minWidth;
0250 
0251     if (!tableWidth) {
0252         bool haveNonFixed = false;
0253         for (int i = 0; i < width.size(); i++) {
0254             if (!width[i].isFixed()) {
0255                 haveNonFixed = true;
0256                 break;
0257             }
0258         }
0259         if (haveNonFixed) {
0260             table->m_maxWidth = 0x7fff;
0261         }
0262     }
0263 #ifdef DEBUG_LAYOUT
0264     qDebug("FixedTableLayout::calcMinMaxWidth: minWidth=%d, maxWidth=%d", table->m_minWidth, table->m_maxWidth);
0265 #endif
0266 }
0267 
0268 void FixedTableLayout::layout()
0269 {
0270     int tableWidth = table->width() - table->bordersPaddingAndSpacing();
0271     int available = tableWidth;
0272     int nEffCols = table->numEffCols();
0273 #ifdef DEBUG_LAYOUT
0274     qDebug("FixedTableLayout::layout: tableWidth=%d, numEffCols=%d",  tableWidth, nEffCols);
0275 #endif
0276 
0277     QVector<int> calcWidth;
0278     calcWidth.resize(nEffCols);
0279     calcWidth.fill(-1);
0280 
0281     // first assign fixed width
0282     for (int i = 0; i < nEffCols; i++) {
0283         if (width[i].isFixed()) {
0284             calcWidth[i] = width[i].value();
0285             available -= width[i].value();
0286         }
0287     }
0288 
0289     // assign percent width
0290     if (available > 0) {
0291         int totalPercent = 0;
0292         for (int i = 0; i < nEffCols; i++)
0293             if (width[i].isPercent()) {
0294                 totalPercent += width[i].rawValue();
0295             }
0296 
0297         // calculate how much to distribute to percent cells.
0298         int base = tableWidth * totalPercent / (100 * PERCENT_SCALE_FACTOR);
0299         if (base > available) {
0300             base = available;
0301         }
0302 
0303 #ifdef DEBUG_LAYOUT
0304         qDebug("FixedTableLayout::layout: assigning percent width, base=%d, totalPercent=%d", base, totalPercent);
0305 #endif
0306         for (int i = 0; available > 0 && i < nEffCols; i++) {
0307             if (width[i].isPercent()) {
0308                 // totalPercent may be 0 below if all %-width specified are 0%. (#172557)
0309                 int w = totalPercent ? base * width[i].rawValue() / totalPercent : 0;
0310                 available -= w;
0311                 calcWidth[i] = w;
0312             }
0313         }
0314     }
0315 
0316     // assign variable width
0317     if (available > 0) {
0318         int totalAuto = 0;
0319         for (int i = 0; i < nEffCols; i++)
0320             if (width[i].isAuto()) {
0321                 totalAuto++;
0322             }
0323 
0324         for (int i = 0; available > 0 && i < nEffCols; i++) {
0325             if (width[i].isAuto()) {
0326                 // totalAuto may be 0 below if all the variable widths specified are 0.
0327                 int w = totalAuto ? available / totalAuto : 0;
0328                 available -= w;
0329                 calcWidth[i] = w;
0330                 totalAuto--;
0331             }
0332         }
0333     }
0334 
0335     for (int i = 0; i < nEffCols; i++)
0336         if (calcWidth[i] < 0) {
0337             calcWidth[i] = 0;    // IE gives min 1 px...
0338         }
0339 
0340     // spread extra space over columns
0341     if (available > 0) {
0342         int total = nEffCols;
0343         // still have some width to spread
0344         int i = nEffCols;
0345         while (i--) {
0346             int w = available / total;
0347             available -= w;
0348             total--;
0349             calcWidth[i] += w;
0350         }
0351     }
0352 
0353     int pos = 0;
0354     int hspacing = table->borderHSpacing();
0355     for (int i = 0; i < nEffCols; i++) {
0356 #ifdef DEBUG_LAYOUT
0357         qDebug("col %d: %d (width %d)", i, pos, calcWidth[i]);
0358 #endif
0359         table->columnPos[i] = pos;
0360         pos += calcWidth[i] + hspacing;
0361     }
0362     table->columnPos[table->columnPos.size() - 1] = pos;
0363 }
0364 
0365 // -------------------------------------------------------------------------
0366 // -------------------------------------------------------------------------
0367 
0368 AutoTableLayout::AutoTableLayout(RenderTable *table)
0369     : TableLayout(table)
0370 {
0371     effWidthDirty = true;
0372     hasPercent = false;
0373 }
0374 
0375 AutoTableLayout::~AutoTableLayout()
0376 {
0377 }
0378 
0379 /* recalculates the full structure needed to do layouting and minmax calculations.
0380    This is usually calculated on the fly, but needs to be done fully when table cells change
0381    dynamically
0382 */
0383 void AutoTableLayout::recalcColumn(int effCol)
0384 {
0385     Layout &l = layoutStruct[effCol];
0386 
0387     RenderObject *child = table->firstChild();
0388     // first we iterate over all rows.
0389 
0390     RenderTableCell *fixedContributor = nullptr;
0391     RenderTableCell *maxContributor = nullptr;
0392 
0393     while (child) {
0394         if (child->isTableSection()) {
0395             RenderTableSection *section = static_cast<RenderTableSection *>(child);
0396             int numRows = section->numRows();
0397             //RenderTableCell *last = 0;
0398             for (int i = 0; i < numRows; i++) {
0399                 RenderTableCell *cell = section->cellAt(i,  effCol);
0400                 if (cell == (RenderTableCell *) - 1) {
0401                     continue;
0402                 }
0403                 bool cellHasContent = cell && (cell->firstChild() || cell->style()->hasBorder() || cell->style()->hasPadding());
0404                 if (cellHasContent) {
0405                     l.emptyCellsOnly = false;
0406                 }
0407                 if (cell && cell->colSpan() == 1) {
0408                     // A cell originates in this column.  Ensure we have
0409                     // a min/max width of at least 1px for this column now.
0410                     l.minWidth = qMax(int(l.minWidth), 1);
0411                     l.maxWidth = qMax(int(l.maxWidth), cellHasContent ? 1 : 0);
0412 
0413                     if (!cell->minMaxKnown()) {
0414                         cell->calcMinMaxWidth();
0415                     }
0416                     if (cell->minWidth() > l.minWidth) {
0417                         l.minWidth = cell->minWidth();
0418                     }
0419                     if (cell->maxWidth() > l.maxWidth) {
0420                         l.maxWidth = cell->maxWidth();
0421                         maxContributor = cell;
0422                     }
0423 
0424                     Length w = cell->styleOrColWidth();
0425                     if (w.rawValue() > 32767) {
0426                         w.setRawValue(32767);
0427                     }
0428                     if (w.isNegative()) {
0429                         w.setValue(0);
0430                     }
0431                     switch (w.type()) {
0432                     case Fixed:
0433                         // ignore width=0
0434                         if (w.isPositive() && !l.width.isPercent()) {
0435                             int wval = cell->calcBoxWidth(w.value());
0436                             if (l.width.isFixed()) {
0437                                 // Nav/IE weirdness
0438                                 if ((wval > l.width.value()) ||
0439                                         ((l.width.value() == wval) && (maxContributor == cell))) {
0440                                     l.width.setValue(wval);
0441                                     fixedContributor = cell;
0442                                 }
0443                             } else {
0444                                 l.width = Length(wval, Fixed);
0445                                 fixedContributor = cell;
0446                             }
0447                         }
0448                         break;
0449                     case Percent:
0450                         hasPercent = true;
0451                         if (w.isPositive() && (!l.width.isPercent() || w.rawValue() > l.width.rawValue())) {
0452                             l.width = w;
0453                         }
0454                         break;
0455                     case Relative:
0456                         if (w.isAuto() || (w.isRelative() && w.value() > l.width.rawValue())) {
0457                             l.width = w;
0458                         }
0459                     default:
0460                         break;
0461                     }
0462                 } else {
0463                     if (cell && (!effCol || section->cellAt(i, effCol - 1) != cell)) {
0464                         // This spanning cell originates in this column.  Ensure we have
0465                         // a min/max width of at least 1px for this column now.
0466                         l.minWidth = qMax(int(l.minWidth), cellHasContent ? 1 : 0);
0467                         l.maxWidth = qMax(int(l.maxWidth), 1);
0468                         insertSpanCell(cell);
0469                     }
0470                     //last = cell;
0471                 }
0472             }
0473         }
0474         child = child->nextSibling();
0475     }
0476 
0477     // Nav/IE weirdness
0478     if (l.width.isFixed()) {
0479         if (table->style()->htmlHacks()
0480                 && (l.maxWidth > l.width.value()) && (fixedContributor != maxContributor)) {
0481             l.width = Length();
0482             fixedContributor = nullptr;
0483         }
0484     }
0485 
0486     l.maxWidth = qMax(l.maxWidth, int(l.minWidth));
0487 #ifdef DEBUG_LAYOUT
0488     qDebug("col %d, final min=%d, max=%d, width=%d(%d)", effCol, l.minWidth, l.maxWidth, l.width.rawValue(),  l.width.type());
0489 #endif
0490 
0491     // ### we need to add col elements aswell
0492 }
0493 
0494 void AutoTableLayout::fullRecalc()
0495 {
0496     hasPercent = false;
0497     effWidthDirty = true;
0498 
0499     int nEffCols = table->numEffCols();
0500     layoutStruct.resize(nEffCols);
0501     layoutStruct.fill(Layout());
0502     spanCells.fill(nullptr);
0503 
0504     RenderObject *child = table->firstChild();
0505     Length grpWidth;
0506     int cCol = 0;
0507     while (child) {
0508         if (child->isTableCol()) {
0509             RenderTableCol *col = static_cast<RenderTableCol *>(child);
0510             int span = col->span();
0511             if (col->firstChild()) {
0512                 grpWidth = col->style()->width();
0513             } else {
0514                 Length w = col->style()->width();
0515                 if (w.isAuto()) {
0516                     w = grpWidth;
0517                 }
0518                 if ((w.isFixed() || w.isPercent()) && w.isZero()) {
0519                     w = Length();
0520                 }
0521                 int cEffCol = table->colToEffCol(cCol);
0522 #ifdef DEBUG_LAYOUT
0523                 qDebug("    col element %d (eff=%d): Length=%d(%d), span=%d, effColSpan=%d",  cCol, cEffCol, w.rawValue(), w.type(), span, table->spanOfEffCol(cEffCol));
0524 #endif
0525                 if (!w.isAuto() && span == 1 && cEffCol < nEffCols) {
0526                     if (table->spanOfEffCol(cEffCol) == 1) {
0527                         layoutStruct[cEffCol].width = w;
0528                         if (w.isFixed() && layoutStruct[cEffCol].maxWidth < w.value()) {
0529                             layoutStruct[cEffCol].maxWidth = w.value();
0530                         }
0531                     }
0532                 }
0533                 cCol += span;
0534             }
0535         } else {
0536             break;
0537         }
0538 
0539         RenderObject *next = child->firstChild();
0540         if (!next) {
0541             next = child->nextSibling();
0542         }
0543         if (!next && child->parent()->isTableCol()) {
0544             next = child->parent()->nextSibling();
0545             grpWidth = Length();
0546         }
0547         child = next;
0548     }
0549 
0550     for (int i = 0; i < nEffCols; i++) {
0551         recalcColumn(i);
0552     }
0553 }
0554 
0555 static bool shouldScaleColumns(RenderTable *table)
0556 {
0557     // A special case.  If this table is not fixed width and contained inside
0558     // a cell, then don't bloat the maxwidth by examining percentage growth.
0559     bool scale = true;
0560     while (table) {
0561         Length tw = table->style()->width();
0562         if ((tw.isAuto() || tw.isPercent()) && !table->isPositioned()) {
0563             RenderBlock *cb = table->containingBlock();
0564             while (cb && !cb->isCanvas() && !cb->isTableCell() &&
0565                     cb->style()->width().isAuto() && !cb->isPositioned()) {
0566                 cb = cb->containingBlock();
0567             }
0568 
0569             table = nullptr;
0570             if (cb && cb->isTableCell() &&
0571                     (cb->style()->width().isAuto() || cb->style()->width().isPercent())) {
0572                 if (tw.isPercent()) {
0573                     scale = false;
0574                 } else {
0575                     RenderTableCell *cell = static_cast<RenderTableCell *>(cb);
0576                     if (cell->colSpan() > 1 || cell->table()->style()->width().isAuto()) {
0577                         scale = false;
0578                     } else {
0579                         table = cell->table();
0580                     }
0581                 }
0582             }
0583         } else {
0584             table = nullptr;
0585         }
0586     }
0587     return scale;
0588 }
0589 
0590 void AutoTableLayout::calcMinMaxWidth()
0591 {
0592 #ifdef DEBUG_LAYOUT
0593     qDebug("AutoTableLayout::calcMinMaxWidth");
0594 #endif
0595     fullRecalc();
0596 
0597     int spanMaxWidth = calcEffectiveWidth();
0598     int minWidth = 0;
0599     int maxWidth = 0;
0600     int maxPercent = 0;
0601     int maxNonPercent = 0;
0602 
0603     int remainingPercent = 100 * PERCENT_SCALE_FACTOR;
0604     for (int i = 0; i < layoutStruct.size(); i++) {
0605         minWidth += layoutStruct[i].effMinWidth;
0606         maxWidth += layoutStruct[i].effMaxWidth;
0607         if (layoutStruct[i].effWidth.isPercent()) {
0608             int percent = qMin(layoutStruct[i].effWidth.rawValue(), remainingPercent);
0609             int pw = (layoutStruct[i].effMaxWidth * 100 * PERCENT_SCALE_FACTOR) / qMax(percent, 1);
0610             remainingPercent -= percent;
0611             maxPercent = qMax(pw,  maxPercent);
0612         } else {
0613             maxNonPercent += layoutStruct[i].effMaxWidth;
0614         }
0615     }
0616 
0617     if (shouldScaleColumns(table)) {
0618         maxNonPercent = (maxNonPercent * 100 * PERCENT_SCALE_FACTOR) / qMax(remainingPercent, 1);
0619         maxWidth = qMax(maxNonPercent, maxWidth);
0620         maxWidth = qMax(maxWidth, maxPercent);
0621     }
0622 
0623     maxWidth = qMax(maxWidth, spanMaxWidth);
0624 
0625     int bs = table->bordersPaddingAndSpacing();
0626     minWidth += bs;
0627     maxWidth += bs;
0628 
0629     Length tw = table->style()->width();
0630     if (tw.isFixed() && tw.isPositive()) {
0631         int width = table->calcBoxWidth(tw.value());
0632         minWidth = qMax(minWidth, width);
0633         maxWidth = minWidth;
0634     }
0635 
0636     table->m_maxWidth = qMin(maxWidth, 0x7fff);
0637     table->m_minWidth = qMin(minWidth, 0x7fff);
0638 #ifdef DEBUG_LAYOUT
0639     qDebug("    minWidth=%d, maxWidth=%d", table->m_minWidth, table->m_maxWidth);
0640 #endif
0641 }
0642 
0643 /*
0644   This method takes care of colspans.
0645   effWidth is the same as width for cells without colspans. If we have colspans, they get modified.
0646  */
0647 int AutoTableLayout::calcEffectiveWidth()
0648 {
0649     int tMaxWidth = 0;
0650 
0651     unsigned int nEffCols = layoutStruct.size();
0652     int hspacing = table->borderHSpacing();
0653 #ifdef DEBUG_LAYOUT
0654     qDebug("AutoTableLayout::calcEffectiveWidth for %d cols", nEffCols);
0655 #endif
0656     for (unsigned int i = 0; i < nEffCols; i++) {
0657         layoutStruct[i].effWidth = layoutStruct[i].width;
0658         layoutStruct[i].effMinWidth = layoutStruct[i].minWidth;
0659         layoutStruct[i].effMaxWidth = layoutStruct[i].maxWidth;
0660     }
0661 
0662     for (int i = 0; i < spanCells.size(); i++) {
0663         RenderTableCell *cell = spanCells[i];
0664         if (!cell || cell == (RenderTableCell *) - 1) {
0665             break;
0666         }
0667         int span = cell->colSpan();
0668 
0669         Length w = cell->styleOrColWidth();
0670         if (!w.isRelative() && w.isZero()) {
0671             w = Length();    // make it Auto
0672         }
0673 
0674         int col = table->colToEffCol(cell->col());
0675         unsigned int lastCol = col;
0676         int cMinWidth = cell->minWidth() + hspacing;
0677         int cMaxWidth = cell->maxWidth() + hspacing;
0678         int totalPercent = 0;
0679         int minWidth = 0;
0680         int maxWidth = 0;
0681         bool allColsArePercent = true;
0682         bool allColsAreFixed = true;
0683         bool haveAuto = false;
0684         bool spanHasEmptyCellsOnly = true;
0685         int fixedWidth = 0;
0686 #ifdef DEBUG_LAYOUT
0687         int cSpan = span;
0688 #endif
0689         while (lastCol < nEffCols && span > 0) {
0690             switch (layoutStruct[lastCol].width.type()) {
0691             case Percent:
0692                 totalPercent += layoutStruct[lastCol].width.rawValue();
0693                 allColsAreFixed = false;
0694                 break;
0695             case Fixed:
0696                 if (layoutStruct[lastCol].width.isPositive()) {
0697                     fixedWidth += layoutStruct[lastCol].width.value();
0698                     allColsArePercent = false;
0699                     // IE resets effWidth to Auto here, but this breaks the konqueror about page and seems to be some bad
0700                     // legacy behavior anyway. mozilla doesn't do this so I decided we don't either.
0701                     break;
0702                 }
0703             // fall through
0704             case Auto:
0705                 haveAuto = true;
0706             // fall through
0707             default:
0708                 // If the column is a percentage width, do not let the spanning cell overwrite the
0709                 // width value.  This caused a mis-rendering on amazon.com.
0710                 // Sample snippet:
0711                 // <table border=2 width=100%><
0712                 //   <tr><td>1</td><td colspan=2>2-3</tr>
0713                 //   <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr>
0714                 // </table>
0715                 if (!layoutStruct[lastCol].effWidth.isPercent()) {
0716                     layoutStruct[lastCol].effWidth = Length();
0717                     allColsArePercent = false;
0718                 } else {
0719                     totalPercent += layoutStruct[lastCol].effWidth.rawValue();
0720                 }
0721                 allColsAreFixed = false;
0722             }
0723             if (!layoutStruct[lastCol].emptyCellsOnly) {
0724                 spanHasEmptyCellsOnly = false;
0725             }
0726             span -= table->spanOfEffCol(lastCol);
0727             minWidth += layoutStruct[lastCol].effMinWidth;
0728             maxWidth += layoutStruct[lastCol].effMaxWidth;
0729             lastCol++;
0730             cMinWidth -= hspacing;
0731             cMaxWidth -= hspacing;
0732         }
0733 #ifdef DEBUG_LAYOUT
0734         qDebug("    colspan cell %p at effCol %d, span %d, type %d, value %d cmin=%d min=%d fixedwidth=%d", cell, col, cSpan, w.type(), w.rawValue(), cMinWidth, minWidth, fixedWidth);
0735 #endif
0736 
0737         // adjust table max width if needed
0738         if (w.isPercent()) {
0739             if (totalPercent > w.rawValue() || allColsArePercent) {
0740                 // can't satify this condition, treat as variable
0741                 w = Length();
0742             } else {
0743                 int spanMax = qMax(maxWidth, cMaxWidth);
0744 #ifdef DEBUG_LAYOUT
0745                 qDebug("    adjusting tMaxWidth (%d): spanMax=%d, value=%d, totalPercent=%d", tMaxWidth, spanMax, w.rawValue(), totalPercent);
0746 #endif
0747                 tMaxWidth = qMax(tMaxWidth, spanMax * 100 * PERCENT_SCALE_FACTOR / w.rawValue());
0748 
0749                 // all non percent columns in the span get percent values to sum up correctly.
0750                 int percentMissing = w.rawValue() - totalPercent;
0751                 int totalWidth = 0;
0752                 for (unsigned int pos = col; pos < lastCol; pos++) {
0753                     if (!(layoutStruct[pos].width.isPercent())) {
0754                         totalWidth += layoutStruct[pos].effMaxWidth;
0755                     }
0756                 }
0757 
0758                 for (unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++) {
0759                     if (!(layoutStruct[pos].width.isPercent())) {
0760                         int percent = percentMissing * layoutStruct[pos].effMaxWidth / totalWidth;
0761 #ifdef DEBUG_LAYOUT
0762                         qDebug("   col %d: setting percent value %d effMaxWidth=%d totalWidth=%d", pos, percent, layoutStruct[pos].effMaxWidth, totalWidth);
0763 #endif
0764                         totalWidth -= layoutStruct[pos].effMaxWidth;
0765                         percentMissing -= percent;
0766                         if (percent > 0) {
0767                             layoutStruct[pos].effWidth.setRawValue(Percent, percent);
0768                         } else {
0769                             layoutStruct[pos].effWidth = Length();
0770                         }
0771                     }
0772                 }
0773 
0774             }
0775         }
0776 
0777         // make sure minWidth and maxWidth of the spanning cell are honoured
0778         if (cMinWidth > minWidth) {
0779             if (allColsAreFixed) {
0780 #ifdef DEBUG_LAYOUT
0781                 qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d accroding to fixed sum %d", col, lastCol - 1, cMinWidth, minWidth, fixedWidth);
0782 #endif
0783                 for (unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++) {
0784                     int w = qMax(int(layoutStruct[pos].effMinWidth), cMinWidth * layoutStruct[pos].width.value() / fixedWidth);
0785 #ifdef DEBUG_LAYOUT
0786                     qDebug("   col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w);
0787 #endif
0788                     fixedWidth -= layoutStruct[pos].width.value();
0789                     cMinWidth -= w;
0790                     layoutStruct[pos].effMinWidth = w;
0791                 }
0792 
0793             } else if (allColsArePercent) {
0794                 int maxw = maxWidth;
0795                 int minw = minWidth;
0796                 int cminw = cMinWidth;
0797 
0798                 for (unsigned int pos = col; maxw > 0 && pos < lastCol; pos++) {
0799                     if (layoutStruct[pos].effWidth.isPercent() && layoutStruct[pos].effWidth.isPositive() && fixedWidth <= cMinWidth) {
0800                         int w = layoutStruct[pos].effMinWidth;
0801                         w = qMax(w, cminw * layoutStruct[pos].effWidth.rawValue() / totalPercent);
0802                         w = qMin(layoutStruct[pos].effMinWidth + (cMinWidth - minw), w);
0803 #ifdef DEBUG_LAYOUT
0804                         qDebug("   col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w);
0805 #endif
0806                         maxw -= layoutStruct[pos].effMaxWidth;
0807                         minw -= layoutStruct[pos].effMinWidth;
0808                         cMinWidth -= w;
0809                         layoutStruct[pos].effMinWidth = w;
0810                     }
0811                 }
0812             } else {
0813 #ifdef DEBUG_LAYOUT
0814                 qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d", col, lastCol - 1, cMinWidth, minWidth);
0815 #endif
0816                 int maxw = maxWidth;
0817                 int minw = minWidth;
0818 
0819                 // Give min to variable first, to fixed second, and to others third.
0820                 for (unsigned int pos = col; maxw > 0 && pos < lastCol; pos++) {
0821                     if (layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth) {
0822                         int w = qMax(int(layoutStruct[pos].effMinWidth), layoutStruct[pos].width.value());
0823                         fixedWidth -= layoutStruct[pos].width.value();
0824                         minw -= layoutStruct[pos].effMinWidth;
0825 #ifdef DEBUG_LAYOUT
0826                         qDebug("   col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w);
0827 #endif
0828                         maxw -= layoutStruct[pos].effMaxWidth;
0829                         cMinWidth -= w;
0830                         layoutStruct[pos].effMinWidth = w;
0831                     }
0832                 }
0833 
0834                 for (unsigned int pos = col; maxw > 0 && pos < lastCol && minw < cMinWidth; pos++) {
0835                     if (!(layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth)) {
0836                         int w = qMax(int(layoutStruct[pos].effMinWidth), cMinWidth * layoutStruct[pos].effMaxWidth / maxw);
0837                         w = qMin(layoutStruct[pos].effMinWidth + (cMinWidth - minw), w);
0838 
0839 #ifdef DEBUG_LAYOUT
0840                         qDebug("   col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w);
0841 #endif
0842                         maxw -= layoutStruct[pos].effMaxWidth;
0843                         minw -= layoutStruct[pos].effMinWidth;
0844                         cMinWidth -= w;
0845                         layoutStruct[pos].effMinWidth = w;
0846                     }
0847                 }
0848             }
0849         }
0850         if (!w.isPercent()) {
0851             if (cMaxWidth > maxWidth) {
0852 #ifdef DEBUG_LAYOUT
0853                 qDebug("extending maxWidth of cols %d-%d to %dpx", col, lastCol - 1, cMaxWidth);
0854 #endif
0855                 for (unsigned int pos = col; maxWidth > 0 && pos < lastCol; pos++) {
0856                     int w = qMax(int(layoutStruct[pos].effMaxWidth), cMaxWidth * layoutStruct[pos].effMaxWidth / maxWidth);
0857 #ifdef DEBUG_LAYOUT
0858                     qDebug("   col %d: max=%d, effMax=%d, new=%d", pos, layoutStruct[pos].effMaxWidth, layoutStruct[pos].effMaxWidth, w);
0859 #endif
0860                     maxWidth -= layoutStruct[pos].effMaxWidth;
0861                     cMaxWidth -= w;
0862                     layoutStruct[pos].effMaxWidth = w;
0863                 }
0864             }
0865         } else {
0866             for (unsigned int pos = col; pos < lastCol; pos++) {
0867                 layoutStruct[pos].maxWidth = qMax(layoutStruct[pos].maxWidth, int(layoutStruct[pos].minWidth));
0868             }
0869         }
0870         // treat span ranges consisting of empty cells only as if they had content
0871         if (spanHasEmptyCellsOnly)
0872             for (unsigned int pos = col; pos < lastCol; pos++) {
0873                 layoutStruct[pos].emptyCellsOnly = false;
0874             }
0875     }
0876     effWidthDirty = false;
0877 
0878 //     qDebug("calcEffectiveWidth: tMaxWidth=%d",  tMaxWidth );
0879     return tMaxWidth;
0880 }
0881 
0882 /* gets all cells that originate in a column and have a cellspan > 1
0883    Sorts them by increasing cellspan
0884 */
0885 void AutoTableLayout::insertSpanCell(RenderTableCell *cell)
0886 {
0887     if (!cell || cell == (RenderTableCell *) - 1 || cell->colSpan() == 1) {
0888         return;
0889     }
0890 
0891 //     qDebug("inserting span cell %p with span %d", cell, cell->colSpan() );
0892     int size = spanCells.size();
0893     if (!size || spanCells[size - 1] != nullptr) {
0894         spanCells.resize(size + 10);
0895         for (int i = 0; i < 10; i++) {
0896             spanCells[size + i] = nullptr;
0897         }
0898         size += 10;
0899     }
0900 
0901     // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better
0902     int pos = 0;
0903     int span = cell->colSpan();
0904     while (pos < spanCells.size() && spanCells[pos] && span > spanCells[pos]->colSpan()) {
0905         pos++;
0906     }
0907     memmove(spanCells.data() + pos + 1, spanCells.data() + pos, (size - pos - 1)*sizeof(RenderTableCell *));
0908     spanCells[pos] = cell;
0909 }
0910 
0911 void AutoTableLayout::layout()
0912 {
0913     // table layout based on the values collected in the layout structure.
0914     int tableWidth = table->width() - table->bordersPaddingAndSpacing();
0915     int available = tableWidth;
0916     int nEffCols = table->numEffCols();
0917 
0918     if (nEffCols != layoutStruct.size()) {
0919         qWarning("WARNING: nEffCols is not equal to layoutstruct!");
0920         fullRecalc();
0921         nEffCols = table->numEffCols();
0922     }
0923 #ifdef DEBUG_LAYOUT
0924     qDebug("AutoTableLayout::layout()");
0925 #endif
0926 
0927     if (effWidthDirty) {
0928         calcEffectiveWidth();
0929     }
0930 
0931 #ifdef DEBUG_LAYOUT
0932     qDebug("    tableWidth=%d,  nEffCols=%d", tableWidth,  nEffCols);
0933     for (int i = 0; i < nEffCols; i++) {
0934         qDebug("    effcol %d is of type %d value %d, minWidth=%d, maxWidth=%d",
0935                i, layoutStruct[i].width.type(), layoutStruct[i].width.rawValue(),
0936                layoutStruct[i].minWidth, layoutStruct[i].maxWidth);
0937         qDebug("        effective: type %d value %d, minWidth=%d, maxWidth=%d",
0938                layoutStruct[i].effWidth.type(), layoutStruct[i].effWidth.rawValue(),
0939                layoutStruct[i].effMinWidth, layoutStruct[i].effMaxWidth);
0940     }
0941 #endif
0942 
0943     bool havePercent = false;
0944     bool haveRelative = false;
0945     int totalRelative = 0;
0946     int numAuto = 0;
0947     int numFixed = 0;
0948     int totalAuto = 0;
0949     int totalFixed = 0;
0950     int totalPercent = 0;
0951     int allocAuto = 0;
0952     int numAutoEmptyCellsOnly = 0;
0953 
0954     // fill up every cell with it's minWidth
0955     for (int i = 0; i < nEffCols; i++) {
0956         int w = layoutStruct[i].effMinWidth;
0957         layoutStruct[i].calcWidth = w;
0958         available -= w;
0959         Length &width = layoutStruct[i].effWidth;
0960         switch (width.type()) {
0961         case Percent:
0962             havePercent = true;
0963             totalPercent += width.rawValue();
0964             break;
0965         case Relative:
0966             haveRelative = true;
0967             totalRelative += width.value();
0968             break;
0969         case Fixed:
0970             numFixed++;
0971             totalFixed += layoutStruct[i].effMaxWidth;
0972             // fall through
0973             break;
0974         case Auto:
0975         case Static:
0976             if (layoutStruct[i].emptyCellsOnly) {
0977                 numAutoEmptyCellsOnly++;
0978             } else {
0979                 numAuto++;
0980                 totalAuto += layoutStruct[i].effMaxWidth;
0981                 allocAuto += w;
0982             }
0983             break;
0984         }
0985     }
0986 
0987     // allocate width to percent cols
0988     if (available > 0 && havePercent) {
0989         for (int i = 0; i < nEffCols; i++) {
0990             const Length &width = layoutStruct[i].effWidth;
0991             if (width.isPercent()) {
0992                 int w = qMax(int(layoutStruct[i].effMinWidth), width.minWidth(tableWidth));
0993                 available += layoutStruct[i].calcWidth - w;
0994                 layoutStruct[i].calcWidth = w;
0995             }
0996         }
0997         if (totalPercent > 100 * PERCENT_SCALE_FACTOR) {
0998             // remove overallocated space from the last columns
0999             int excess = tableWidth * (totalPercent - (100 * PERCENT_SCALE_FACTOR)) / (100 * PERCENT_SCALE_FACTOR);
1000             for (int i = nEffCols - 1; i >= 0; i--) {
1001                 if (layoutStruct[i].effWidth.isPercent()) {
1002                     int w = layoutStruct[i].calcWidth;
1003                     int reduction = qMin(w,  excess);
1004                     // the lines below might look inconsistent, but that's the way it's handled in mozilla
1005                     excess -= reduction;
1006                     int newWidth = qMax(int (layoutStruct[i].effMinWidth), w - reduction);
1007                     available += w - newWidth;
1008                     layoutStruct[i].calcWidth = newWidth;
1009                     //qDebug("col %d: reducing to %d px (reduction=%d)", i, newWidth, reduction );
1010                 }
1011             }
1012         }
1013     }
1014 #ifdef DEBUG_LAYOUT
1015     qDebug("percent satisfied: available is %d", available);
1016 #endif
1017 
1018     // then allocate width to fixed cols
1019     if (available > 0 && numFixed) {
1020         for (int i = 0; i < nEffCols; ++i) {
1021             const Length &width = layoutStruct[i].effWidth;
1022             if (width.isFixed() && width.value() > layoutStruct[i].calcWidth) {
1023                 available += layoutStruct[i].calcWidth - width.value();
1024                 layoutStruct[i].calcWidth = width.value();
1025             }
1026         }
1027     }
1028 #ifdef DEBUG_LAYOUT
1029     qDebug("fixed satisfied: available is %d", available);
1030 #endif
1031 
1032     // now satisfy relative
1033     if (available > 0 && haveRelative) {
1034         for (int i = 0; i < nEffCols; i++) {
1035             const Length &width = layoutStruct[i].effWidth;
1036             if (width.isRelative() && width.value()) {
1037                 // width=0* gets effMinWidth.
1038                 int w = width.value() * tableWidth / totalRelative;
1039                 available += layoutStruct[i].calcWidth - w;
1040                 layoutStruct[i].calcWidth = w;
1041             }
1042         }
1043     }
1044 
1045     // now satisfy variable
1046     if (available > 0 && numAuto) {
1047         available += allocAuto; // this gets redistributed
1048         //qDebug("redistributing %dpx to %d variable columns. totalAuto=%d",  available,  numAuto,  totalAuto );
1049         for (int i = 0; i < nEffCols; i++) {
1050             const Length &width = layoutStruct[i].effWidth;
1051             if (width.isAuto() && totalAuto != 0 && !layoutStruct[i].emptyCellsOnly) {
1052                 int w = qMax(int (layoutStruct[i].calcWidth),
1053                              available * layoutStruct[i].effMaxWidth / totalAuto);
1054                 available -= w;
1055                 totalAuto -= layoutStruct[i].effMaxWidth;
1056                 layoutStruct[i].calcWidth = w;
1057             }
1058         }
1059     }
1060 #ifdef DEBUG_LAYOUT
1061     qDebug("variable satisfied: available is %d",  available);
1062 #endif
1063 
1064     // spread over fixed columns
1065     if (available > 0 && numFixed) {
1066         // still have some width to spread, distribute to fixed columns
1067         for (int i = 0; i < nEffCols; i++) {
1068             const Length &width = layoutStruct[i].effWidth;
1069             if (width.isFixed()) {
1070                 int w = available * layoutStruct[i].effMaxWidth / totalFixed;
1071                 available -= w;
1072                 totalFixed -= layoutStruct[i].effMaxWidth;
1073                 layoutStruct[i].calcWidth += w;
1074             }
1075         }
1076     }
1077 
1078 #ifdef DEBUG_LAYOUT
1079     qDebug("after fixed distribution: available=%d",  available);
1080 #endif
1081 
1082     // spread over percent columns
1083     if (available > 0 && hasPercent && totalPercent < 100 * PERCENT_SCALE_FACTOR) {
1084         // still have some width to spread, distribute weighted to percent columns
1085         for (int i = 0; i < nEffCols; i++) {
1086             const Length &width = layoutStruct[i].effWidth;
1087             if (width.isPercent()) {
1088                 int w = available * width.rawValue() / totalPercent;
1089                 available -= w;
1090                 totalPercent -= width.rawValue();
1091                 layoutStruct[i].calcWidth += w;
1092                 if (!available || !totalPercent) {
1093                     break;
1094                 }
1095             }
1096         }
1097     }
1098 
1099 #ifdef DEBUG_LAYOUT
1100     qDebug("after percent distribution: available=%d",  available);
1101 #endif
1102 
1103     // spread over the rest
1104     if (available > 0 && nEffCols > numAutoEmptyCellsOnly) {
1105         int total = nEffCols;
1106         // still have some width to spread
1107         int i = nEffCols;
1108         while (i--) {
1109             // variable columns with empty cells only don't get any width
1110             if (layoutStruct[i].width.isAuto() && layoutStruct[i].emptyCellsOnly) {
1111                 continue;
1112             }
1113             int w = available / total;
1114             available -= w;
1115             total--;
1116             layoutStruct[i].calcWidth += w;
1117         }
1118     }
1119 
1120 #ifdef DEBUG_LAYOUT
1121     qDebug("after equal distribution: available=%d",  available);
1122 #endif
1123     // if we have overallocated, reduce every cell according to the difference between desired width and minwidth
1124     // this seems to produce to the pixel exaxt results with IE. Wonder is some of this also holds for width distributing.
1125     if (available < 0) {
1126         // Need to reduce cells with the following prioritization:
1127         // (1) Auto
1128         // (2) Relative
1129         // (3) Fixed
1130         // (4) Percent
1131         // This is basically the reverse of how we grew the cells.
1132         if (available < 0) {
1133             int mw = 0;
1134             for (int i = nEffCols - 1; i >= 0; i--) {
1135                 Length &width = layoutStruct[i].effWidth;
1136                 if (width.isAuto()) {
1137                     mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
1138                 }
1139             }
1140 
1141             for (int i = nEffCols - 1; i >= 0 && mw > 0; i--) {
1142                 Length &width = layoutStruct[i].effWidth;
1143                 if (width.isAuto()) {
1144                     int minMaxDiff = layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
1145                     int reduce = available * minMaxDiff / mw;
1146                     layoutStruct[i].calcWidth += reduce;
1147                     available -= reduce;
1148                     mw -= minMaxDiff;
1149                     if (available >= 0) {
1150                         break;
1151                     }
1152                 }
1153             }
1154         }
1155 
1156         if (available < 0 && haveRelative) {
1157             int mw = 0;
1158             for (int i = nEffCols - 1; i >= 0; i--) {
1159                 Length &width = layoutStruct[i].effWidth;
1160                 if (width.isRelative()) {
1161                     mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
1162                 }
1163             }
1164 
1165             for (int i = nEffCols - 1; i >= 0 && mw > 0; i--) {
1166                 Length &width = layoutStruct[i].effWidth;
1167                 if (width.isRelative()) {
1168                     int minMaxDiff = layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
1169                     int reduce = available * minMaxDiff / mw;
1170                     layoutStruct[i].calcWidth += reduce;
1171                     available -= reduce;
1172                     mw -= minMaxDiff;
1173                     if (available >= 0) {
1174                         break;
1175                     }
1176                 }
1177             }
1178         }
1179 
1180         if (available < 0 && numFixed) {
1181             int mw = 0;
1182             for (int i = nEffCols - 1; i >= 0; i--) {
1183                 Length &width = layoutStruct[i].effWidth;
1184                 if (width.isFixed()) {
1185                     mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
1186                 }
1187             }
1188 
1189             for (int i = nEffCols - 1; i >= 0 && mw > 0; i--) {
1190                 Length &width = layoutStruct[i].effWidth;
1191                 if (width.isFixed()) {
1192                     int minMaxDiff = layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
1193                     int reduce = available * minMaxDiff / mw;
1194                     layoutStruct[i].calcWidth += reduce;
1195                     available -= reduce;
1196                     mw -= minMaxDiff;
1197                     if (available >= 0) {
1198                         break;
1199                     }
1200                 }
1201             }
1202         }
1203 
1204         if (available < 0 && havePercent) {
1205             int mw = 0;
1206             for (int i = nEffCols - 1; i >= 0; i--) {
1207                 Length &width = layoutStruct[i].effWidth;
1208                 if (width.isPercent()) {
1209                     mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
1210                 }
1211             }
1212 
1213             for (int i = nEffCols - 1; i >= 0 && mw > 0; i--) {
1214                 Length &width = layoutStruct[i].effWidth;
1215                 if (width.isPercent()) {
1216                     int minMaxDiff = layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
1217                     int reduce = available * minMaxDiff / mw;
1218                     layoutStruct[i].calcWidth += reduce;
1219                     available -= reduce;
1220                     mw -= minMaxDiff;
1221                     if (available >= 0) {
1222                         break;
1223                     }
1224                 }
1225             }
1226         }
1227     }
1228 
1229     //qDebug( "    final available=%d", available );
1230 
1231     int pos = 0;
1232     for (int i = 0; i < nEffCols; i++) {
1233 #ifdef DEBUG_LAYOUT
1234         qDebug("col %d: %d (width %d)", i, pos, layoutStruct[i].calcWidth);
1235 #endif
1236         table->columnPos[i] = pos;
1237         pos += layoutStruct[i].calcWidth + table->borderHSpacing();
1238     }
1239     table->columnPos[table->columnPos.size() - 1] = pos;
1240 
1241 }
1242 
1243 #undef DEBUG_LAYOUT