Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2002 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 2002 Dirk Mueller (mueller (at) kde.org)
      4  * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  */
     21 
     22 #include "config.h"
     23 #include "AutoTableLayout.h"
     24 
     25 #include "RenderTable.h"
     26 #include "RenderTableCell.h"
     27 #include "RenderTableCol.h"
     28 #include "RenderTableSection.h"
     29 
     30 using namespace std;
     31 
     32 namespace WebCore {
     33 
     34 AutoTableLayout::AutoTableLayout(RenderTable* table)
     35     : TableLayout(table)
     36     , m_hasPercent(false)
     37     , m_percentagesDirty(true)
     38     , m_effWidthDirty(true)
     39     , m_totalPercent(0)
     40 {
     41 }
     42 
     43 AutoTableLayout::~AutoTableLayout()
     44 {
     45 }
     46 
     47 /* recalculates the full structure needed to do layouting and minmax calculations.
     48    This is usually calculated on the fly, but needs to be done fully when table cells change
     49    dynamically
     50 */
     51 void AutoTableLayout::recalcColumn(int effCol)
     52 {
     53     Layout &l = m_layoutStruct[effCol];
     54 
     55     RenderObject* child = m_table->firstChild();
     56     // first we iterate over all rows.
     57 
     58     RenderTableCell* fixedContributor = 0;
     59     RenderTableCell* maxContributor = 0;
     60 
     61     while (child) {
     62         if (child->isTableCol())
     63             toRenderTableCol(child)->calcPrefWidths();
     64         else if (child->isTableSection()) {
     65             RenderTableSection* section = toRenderTableSection(child);
     66             int numRows = section->numRows();
     67             RenderTableCell* last = 0;
     68             for (int i = 0; i < numRows; i++) {
     69                 RenderTableSection::CellStruct current = section->cellAt(i, effCol);
     70                 RenderTableCell* cell = current.cell;
     71 
     72                 bool cellHasContent = cell && (cell->firstChild() || cell->style()->hasBorder() || cell->style()->hasPadding());
     73                 if (cellHasContent)
     74                     l.emptyCellsOnly = false;
     75 
     76                 if (current.inColSpan)
     77                     continue;
     78                 if (cell && cell->colSpan() == 1) {
     79                     // A cell originates in this column.  Ensure we have
     80                     // a min/max width of at least 1px for this column now.
     81                     l.minWidth = max(l.minWidth, cellHasContent ? 1 : 0);
     82                     l.maxWidth = max(l.maxWidth, 1);
     83                     if (cell->prefWidthsDirty())
     84                         cell->calcPrefWidths();
     85                     l.minWidth = max(cell->minPrefWidth(), l.minWidth);
     86                     if (cell->maxPrefWidth() > l.maxWidth) {
     87                         l.maxWidth = cell->maxPrefWidth();
     88                         maxContributor = cell;
     89                     }
     90 
     91                     Length w = cell->styleOrColWidth();
     92                     // FIXME: What is this arbitrary value?
     93                     if (w.rawValue() > 32760)
     94                         w.setRawValue(32760);
     95                     if (w.isNegative())
     96                         w.setValue(0);
     97                     switch (w.type()) {
     98                     case Fixed:
     99                         // ignore width=0
    100                         if (w.value() > 0 && (int)l.width.type() != Percent) {
    101                             int wval = cell->calcBorderBoxWidth(w.value());
    102                             if (l.width.isFixed()) {
    103                                 // Nav/IE weirdness
    104                                 if ((wval > l.width.value()) ||
    105                                     ((l.width.value() == wval) && (maxContributor == cell))) {
    106                                     l.width.setValue(wval);
    107                                     fixedContributor = cell;
    108                                 }
    109                             } else {
    110                                 l.width.setValue(Fixed, wval);
    111                                 fixedContributor = cell;
    112                             }
    113                         }
    114                         break;
    115                     case Percent:
    116                         m_hasPercent = true;
    117                         if (w.isPositive() && (!l.width.isPercent() || w.rawValue() > l.width.rawValue()))
    118                             l.width = w;
    119                         break;
    120                     case Relative:
    121                         // FIXME: Need to understand this case and whether it makes sense to compare values
    122                         // which are not necessarily of the same type.
    123                         if (w.isAuto() || (w.isRelative() && w.value() > l.width.rawValue()))
    124                             l.width = w;
    125                     default:
    126                         break;
    127                     }
    128                 } else {
    129                     if (cell && (!effCol || section->cellAt(i, effCol-1).cell != cell)) {
    130                         // This spanning cell originates in this column.  Ensure we have
    131                         // a min/max width of at least 1px for this column now.
    132                         l.minWidth = max(l.minWidth, cellHasContent ? 1 : 0);
    133                         l.maxWidth = max(l.maxWidth, 1);
    134                         insertSpanCell(cell);
    135                     }
    136                     last = cell;
    137                 }
    138             }
    139         }
    140         child = child->nextSibling();
    141     }
    142 
    143     // Nav/IE weirdness
    144     if (l.width.isFixed()) {
    145         if (m_table->style()->htmlHacks() && l.maxWidth > l.width.value() && fixedContributor != maxContributor) {
    146             l.width = Length();
    147             fixedContributor = 0;
    148         }
    149     }
    150 
    151     l.maxWidth = max(l.maxWidth, l.minWidth);
    152 
    153     // ### we need to add col elements as well
    154 }
    155 
    156 void AutoTableLayout::fullRecalc()
    157 {
    158     m_percentagesDirty = true;
    159     m_hasPercent = false;
    160     m_effWidthDirty = true;
    161 
    162     int nEffCols = m_table->numEffCols();
    163     m_layoutStruct.resize(nEffCols);
    164     m_layoutStruct.fill(Layout());
    165     m_spanCells.fill(0);
    166 
    167     RenderObject *child = m_table->firstChild();
    168     Length grpWidth;
    169     int cCol = 0;
    170     while (child) {
    171         if (child->isTableCol()) {
    172             RenderTableCol *col = toRenderTableCol(child);
    173             int span = col->span();
    174             if (col->firstChild()) {
    175                 grpWidth = col->style()->width();
    176             } else {
    177                 Length w = col->style()->width();
    178                 if (w.isAuto())
    179                     w = grpWidth;
    180                 if ((w.isFixed() || w.isPercent()) && w.isZero())
    181                     w = Length();
    182                 int cEffCol = m_table->colToEffCol(cCol);
    183                 if (!w.isAuto() && span == 1 && cEffCol < nEffCols) {
    184                     if (m_table->spanOfEffCol(cEffCol) == 1) {
    185                         m_layoutStruct[cEffCol].width = w;
    186                         if (w.isFixed() && m_layoutStruct[cEffCol].maxWidth < w.value())
    187                             m_layoutStruct[cEffCol].maxWidth = w.value();
    188                     }
    189                 }
    190                 cCol += span;
    191             }
    192         } else {
    193             break;
    194         }
    195 
    196         RenderObject *next = child->firstChild();
    197         if (!next)
    198             next = child->nextSibling();
    199         if (!next && child->parent()->isTableCol()) {
    200             next = child->parent()->nextSibling();
    201             grpWidth = Length();
    202         }
    203         child = next;
    204     }
    205 
    206 
    207     for (int i = 0; i < nEffCols; i++)
    208         recalcColumn(i);
    209 }
    210 
    211 static bool shouldScaleColumns(RenderTable* table)
    212 {
    213     // A special case.  If this table is not fixed width and contained inside
    214     // a cell, then don't bloat the maxwidth by examining percentage growth.
    215     bool scale = true;
    216     while (table) {
    217         Length tw = table->style()->width();
    218         if ((tw.isAuto() || tw.isPercent()) && !table->isPositioned()) {
    219             RenderBlock* cb = table->containingBlock();
    220             while (cb && !cb->isRenderView() && !cb->isTableCell() &&
    221                 cb->style()->width().isAuto() && !cb->isPositioned())
    222                 cb = cb->containingBlock();
    223 
    224             table = 0;
    225             if (cb && cb->isTableCell() &&
    226                 (cb->style()->width().isAuto() || cb->style()->width().isPercent())) {
    227                 if (tw.isPercent())
    228                     scale = false;
    229                 else {
    230                     RenderTableCell* cell = toRenderTableCell(cb);
    231                     if (cell->colSpan() > 1 || cell->table()->style()->width().isAuto())
    232                         scale = false;
    233                     else
    234                         table = cell->table();
    235                 }
    236             }
    237         }
    238         else
    239             table = 0;
    240     }
    241     return scale;
    242 }
    243 
    244 void AutoTableLayout::calcPrefWidths(int& minWidth, int& maxWidth)
    245 {
    246     fullRecalc();
    247 
    248     int spanMaxWidth = calcEffectiveWidth();
    249     minWidth = 0;
    250     maxWidth = 0;
    251     float maxPercent = 0;
    252     float maxNonPercent = 0;
    253     bool scaleColumns = shouldScaleColumns(m_table);
    254 
    255     // We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero.
    256     // FIXME: Handle the 0% cases properly.
    257     const int epsilon = 1;
    258 
    259     int remainingPercent = 100 * percentScaleFactor;
    260     for (unsigned int i = 0; i < m_layoutStruct.size(); i++) {
    261         minWidth += m_layoutStruct[i].effMinWidth;
    262         maxWidth += m_layoutStruct[i].effMaxWidth;
    263         if (scaleColumns) {
    264             if (m_layoutStruct[i].effWidth.isPercent()) {
    265                 int percent = min(m_layoutStruct[i].effWidth.rawValue(), remainingPercent);
    266                 float pw = static_cast<float>(m_layoutStruct[i].effMaxWidth) * 100 * percentScaleFactor / max(percent, epsilon);
    267                 maxPercent = max(pw,  maxPercent);
    268                 remainingPercent -= percent;
    269             } else
    270                 maxNonPercent += m_layoutStruct[i].effMaxWidth;
    271         }
    272     }
    273 
    274     if (scaleColumns) {
    275         maxNonPercent = maxNonPercent * 100 * percentScaleFactor / max(remainingPercent, epsilon);
    276         maxWidth = max(maxWidth, static_cast<int>(min(maxNonPercent, INT_MAX / 2.0f)));
    277         maxWidth = max(maxWidth, static_cast<int>(min(maxPercent, INT_MAX / 2.0f)));
    278     }
    279 
    280     maxWidth = max(maxWidth, spanMaxWidth);
    281 
    282     int bs = m_table->bordersPaddingAndSpacing();
    283     minWidth += bs;
    284     maxWidth += bs;
    285 
    286     Length tw = m_table->style()->width();
    287     if (tw.isFixed() && tw.value() > 0) {
    288         minWidth = max(minWidth, tw.value());
    289         maxWidth = minWidth;
    290     }
    291 }
    292 
    293 /*
    294   This method takes care of colspans.
    295   effWidth is the same as width for cells without colspans. If we have colspans, they get modified.
    296  */
    297 int AutoTableLayout::calcEffectiveWidth()
    298 {
    299     float tMaxWidth = 0;
    300 
    301     unsigned int nEffCols = m_layoutStruct.size();
    302     int hspacing = m_table->hBorderSpacing();
    303 
    304     for (unsigned int i = 0; i < nEffCols; i++) {
    305         m_layoutStruct[i].effWidth = m_layoutStruct[i].width;
    306         m_layoutStruct[i].effMinWidth = m_layoutStruct[i].minWidth;
    307         m_layoutStruct[i].effMaxWidth = m_layoutStruct[i].maxWidth;
    308     }
    309 
    310     for (unsigned int i = 0; i < m_spanCells.size(); i++) {
    311         RenderTableCell *cell = m_spanCells[i];
    312         if (!cell)
    313             break;
    314         int span = cell->colSpan();
    315 
    316         Length w = cell->styleOrColWidth();
    317         if (!w.isRelative() && w.isZero())
    318             w = Length(); // make it Auto
    319 
    320         int col = m_table->colToEffCol(cell->col());
    321         unsigned int lastCol = col;
    322         int cMinWidth = cell->minPrefWidth() + hspacing;
    323         float cMaxWidth = cell->maxPrefWidth() + hspacing;
    324         int totalPercent = 0;
    325         int minWidth = 0;
    326         float maxWidth = 0;
    327         bool allColsArePercent = true;
    328         bool allColsAreFixed = true;
    329         bool haveAuto = false;
    330         bool spanHasEmptyCellsOnly = true;
    331         int fixedWidth = 0;
    332         while (lastCol < nEffCols && span > 0) {
    333             switch (m_layoutStruct[lastCol].width.type()) {
    334             case Percent:
    335                 totalPercent += m_layoutStruct[lastCol].width.rawValue();
    336                 allColsAreFixed = false;
    337                 break;
    338             case Fixed:
    339                 if (m_layoutStruct[lastCol].width.value() > 0) {
    340                     fixedWidth += m_layoutStruct[lastCol].width.value();
    341                     allColsArePercent = false;
    342                     // IE resets effWidth to Auto here, but this breaks the konqueror about page and seems to be some bad
    343                     // legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither.
    344                     break;
    345                 }
    346                 // fall through
    347             case Auto:
    348                 haveAuto = true;
    349                 // fall through
    350             default:
    351                 // If the column is a percentage width, do not let the spanning cell overwrite the
    352                 // width value.  This caused a mis-rendering on amazon.com.
    353                 // Sample snippet:
    354                 // <table border=2 width=100%><
    355                 //   <tr><td>1</td><td colspan=2>2-3</tr>
    356                 //   <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr>
    357                 // </table>
    358                 if (!m_layoutStruct[lastCol].effWidth.isPercent()) {
    359                     m_layoutStruct[lastCol].effWidth = Length();
    360                     allColsArePercent = false;
    361                 }
    362                 else
    363                     totalPercent += m_layoutStruct[lastCol].effWidth.rawValue();
    364                 allColsAreFixed = false;
    365             }
    366             if (!m_layoutStruct[lastCol].emptyCellsOnly)
    367                 spanHasEmptyCellsOnly = false;
    368             span -= m_table->spanOfEffCol(lastCol);
    369             minWidth += m_layoutStruct[lastCol].effMinWidth;
    370             maxWidth += m_layoutStruct[lastCol].effMaxWidth;
    371             lastCol++;
    372             cMinWidth -= hspacing;
    373             cMaxWidth -= hspacing;
    374         }
    375 
    376         // adjust table max width if needed
    377         if (w.isPercent()) {
    378             if (totalPercent > w.rawValue() || allColsArePercent) {
    379                 // can't satify this condition, treat as variable
    380                 w = Length();
    381             } else {
    382                 float spanMax = max(maxWidth, cMaxWidth);
    383                 tMaxWidth = max(tMaxWidth, spanMax * 100 * percentScaleFactor / w.rawValue());
    384 
    385                 // all non percent columns in the span get percent values to sum up correctly.
    386                 int percentMissing = w.rawValue() - totalPercent;
    387                 float totalWidth = 0;
    388                 for (unsigned int pos = col; pos < lastCol; pos++) {
    389                     if (!(m_layoutStruct[pos].effWidth.isPercent()))
    390                         totalWidth += m_layoutStruct[pos].effMaxWidth;
    391                 }
    392 
    393                 for (unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++) {
    394                     if (!(m_layoutStruct[pos].effWidth.isPercent())) {
    395                         int percent = static_cast<int>(percentMissing * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / totalWidth);
    396                         totalWidth -= m_layoutStruct[pos].effMaxWidth;
    397                         percentMissing -= percent;
    398                         if (percent > 0)
    399                             m_layoutStruct[pos].effWidth.setRawValue(Percent, percent);
    400                         else
    401                             m_layoutStruct[pos].effWidth = Length();
    402                     }
    403                 }
    404 
    405             }
    406         }
    407 
    408         // make sure minWidth and maxWidth of the spanning cell are honoured
    409         if (cMinWidth > minWidth) {
    410             if (allColsAreFixed) {
    411                 for (unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++) {
    412                     int w = max(m_layoutStruct[pos].effMinWidth, cMinWidth * m_layoutStruct[pos].width.value() / fixedWidth);
    413                     fixedWidth -= m_layoutStruct[pos].width.value();
    414                     cMinWidth -= w;
    415                     m_layoutStruct[pos].effMinWidth = w;
    416                 }
    417 
    418             } else {
    419                 float maxw = maxWidth;
    420                 int minw = minWidth;
    421 
    422                 // Give min to variable first, to fixed second, and to others third.
    423                 for (unsigned int pos = col; maxw >= 0 && pos < lastCol; pos++) {
    424                     if (m_layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth) {
    425                         int w = max(m_layoutStruct[pos].effMinWidth, m_layoutStruct[pos].width.value());
    426                         fixedWidth -= m_layoutStruct[pos].width.value();
    427                         minw -= m_layoutStruct[pos].effMinWidth;
    428                         maxw -= m_layoutStruct[pos].effMaxWidth;
    429                         cMinWidth -= w;
    430                         m_layoutStruct[pos].effMinWidth = w;
    431                     }
    432                 }
    433 
    434                 for (unsigned int pos = col; maxw >= 0 && pos < lastCol && minw < cMinWidth; pos++) {
    435                     if (!(m_layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth)) {
    436                         int w = max(m_layoutStruct[pos].effMinWidth, static_cast<int>(maxw ? cMinWidth * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / maxw : cMinWidth));
    437                         w = min(m_layoutStruct[pos].effMinWidth+(cMinWidth-minw), w);
    438 
    439                         maxw -= m_layoutStruct[pos].effMaxWidth;
    440                         minw -= m_layoutStruct[pos].effMinWidth;
    441                         cMinWidth -= w;
    442                         m_layoutStruct[pos].effMinWidth = w;
    443                     }
    444                 }
    445             }
    446         }
    447         if (!(w.isPercent())) {
    448             if (cMaxWidth > maxWidth) {
    449                 for (unsigned int pos = col; maxWidth >= 0 && pos < lastCol; pos++) {
    450                     int w = max(m_layoutStruct[pos].effMaxWidth, static_cast<int>(maxWidth ? cMaxWidth * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / maxWidth : cMaxWidth));
    451                     maxWidth -= m_layoutStruct[pos].effMaxWidth;
    452                     cMaxWidth -= w;
    453                     m_layoutStruct[pos].effMaxWidth = w;
    454                 }
    455             }
    456         } else {
    457             for (unsigned int pos = col; pos < lastCol; pos++)
    458                 m_layoutStruct[pos].maxWidth = max(m_layoutStruct[pos].maxWidth, m_layoutStruct[pos].minWidth);
    459         }
    460         // treat span ranges consisting of empty cells only as if they had content
    461         if (spanHasEmptyCellsOnly)
    462             for (unsigned int pos = col; pos < lastCol; pos++)
    463                 m_layoutStruct[pos].emptyCellsOnly = false;
    464     }
    465     m_effWidthDirty = false;
    466 
    467     return static_cast<int>(min(tMaxWidth, INT_MAX / 2.0f));
    468 }
    469 
    470 /* gets all cells that originate in a column and have a cellspan > 1
    471    Sorts them by increasing cellspan
    472 */
    473 void AutoTableLayout::insertSpanCell(RenderTableCell *cell)
    474 {
    475     if (!cell || cell->colSpan() == 1)
    476         return;
    477 
    478     int size = m_spanCells.size();
    479     if (!size || m_spanCells[size-1] != 0) {
    480         m_spanCells.grow(size + 10);
    481         for (int i = 0; i < 10; i++)
    482             m_spanCells[size+i] = 0;
    483         size += 10;
    484     }
    485 
    486     // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better
    487     unsigned int pos = 0;
    488     int span = cell->colSpan();
    489     while (pos < m_spanCells.size() && m_spanCells[pos] && span > m_spanCells[pos]->colSpan())
    490         pos++;
    491     memmove(m_spanCells.data()+pos+1, m_spanCells.data()+pos, (size-pos-1)*sizeof(RenderTableCell *));
    492     m_spanCells[pos] = cell;
    493 }
    494 
    495 
    496 void AutoTableLayout::layout()
    497 {
    498 #ifdef ANDROID_LAYOUT
    499     if (m_table->isSingleColumn())
    500         return;
    501 #endif
    502     // table layout based on the values collected in the layout structure.
    503     int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing();
    504     int available = tableWidth;
    505     int nEffCols = m_table->numEffCols();
    506 
    507     if (nEffCols != (int)m_layoutStruct.size()) {
    508         fullRecalc();
    509         nEffCols = m_table->numEffCols();
    510     }
    511 
    512     if (m_effWidthDirty)
    513         calcEffectiveWidth();
    514 
    515     bool havePercent = false;
    516     bool haveRelative = false;
    517     int totalRelative = 0;
    518     int numAuto = 0;
    519     int numFixed = 0;
    520     float totalAuto = 0;
    521     float totalFixed = 0;
    522     int totalPercent = 0;
    523     int allocAuto = 0;
    524     int numAutoEmptyCellsOnly = 0;
    525 
    526     // fill up every cell with its minWidth
    527     for (int i = 0; i < nEffCols; i++) {
    528         int w = m_layoutStruct[i].effMinWidth;
    529         m_layoutStruct[i].calcWidth = w;
    530         available -= w;
    531         Length& width = m_layoutStruct[i].effWidth;
    532         switch (width.type()) {
    533         case Percent:
    534             havePercent = true;
    535             totalPercent += width.rawValue();
    536             break;
    537         case Relative:
    538             haveRelative = true;
    539             totalRelative += width.value();
    540             break;
    541         case Fixed:
    542             numFixed++;
    543             totalFixed += m_layoutStruct[i].effMaxWidth;
    544             // fall through
    545             break;
    546         case Auto:
    547         case Static:
    548             if (m_layoutStruct[i].emptyCellsOnly)
    549                 numAutoEmptyCellsOnly++;
    550             else {
    551                 numAuto++;
    552                 totalAuto += m_layoutStruct[i].effMaxWidth;
    553                 allocAuto += w;
    554             }
    555             break;
    556         default:
    557             break;
    558         }
    559     }
    560 
    561     // allocate width to percent cols
    562     if (available > 0 && havePercent) {
    563         for (int i = 0; i < nEffCols; i++) {
    564             Length &width = m_layoutStruct[i].effWidth;
    565             if (width.isPercent()) {
    566                 int w = max(int(m_layoutStruct[i].effMinWidth), width.calcMinValue(tableWidth));
    567                 available += m_layoutStruct[i].calcWidth - w;
    568                 m_layoutStruct[i].calcWidth = w;
    569             }
    570         }
    571         if (totalPercent > 100 * percentScaleFactor) {
    572             // remove overallocated space from the last columns
    573             int excess = tableWidth*(totalPercent - 100 * percentScaleFactor) / (100 * percentScaleFactor);
    574             for (int i = nEffCols-1; i >= 0; i--) {
    575                 if (m_layoutStruct[i].effWidth.isPercent()) {
    576                     int w = m_layoutStruct[i].calcWidth;
    577                     int reduction = min(w,  excess);
    578                     // the lines below might look inconsistent, but that's the way it's handled in mozilla
    579                     excess -= reduction;
    580                     int newWidth = max(static_cast<int>(m_layoutStruct[i].effMinWidth), w - reduction);
    581                     available += w - newWidth;
    582                     m_layoutStruct[i].calcWidth = newWidth;
    583                 }
    584             }
    585         }
    586     }
    587 
    588     // then allocate width to fixed cols
    589     if (available > 0) {
    590         for (int i = 0; i < nEffCols; ++i) {
    591             Length &width = m_layoutStruct[i].effWidth;
    592             if (width.isFixed() && width.value() > m_layoutStruct[i].calcWidth) {
    593                 available += m_layoutStruct[i].calcWidth - width.value();
    594                 m_layoutStruct[i].calcWidth = width.value();
    595             }
    596         }
    597     }
    598 
    599     // now satisfy relative
    600     if (available > 0) {
    601         for (int i = 0; i < nEffCols; i++) {
    602             Length &width = m_layoutStruct[i].effWidth;
    603             if (width.isRelative() && width.value() != 0) {
    604                 // width=0* gets effMinWidth.
    605                 int w = width.value() * tableWidth / totalRelative;
    606                 available += m_layoutStruct[i].calcWidth - w;
    607                 m_layoutStruct[i].calcWidth = w;
    608             }
    609         }
    610     }
    611 
    612     // now satisfy variable
    613     if (available > 0 && numAuto) {
    614         available += allocAuto; // this gets redistributed
    615         for (int i = 0; i < nEffCols; i++) {
    616             Length &width = m_layoutStruct[i].effWidth;
    617             if (width.isAuto() && totalAuto != 0 && !m_layoutStruct[i].emptyCellsOnly) {
    618                 int w = max(m_layoutStruct[i].calcWidth, static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effMaxWidth) / totalAuto));
    619                 available -= w;
    620                 totalAuto -= m_layoutStruct[i].effMaxWidth;
    621                 m_layoutStruct[i].calcWidth = w;
    622             }
    623         }
    624     }
    625 
    626     // spread over fixed columns
    627     if (available > 0 && numFixed) {
    628         // still have some width to spread, distribute to fixed columns
    629         for (int i = 0; i < nEffCols; i++) {
    630             Length &width = m_layoutStruct[i].effWidth;
    631             if (width.isFixed()) {
    632                 int w = static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effMaxWidth) / totalFixed);
    633                 available -= w;
    634                 totalFixed -= m_layoutStruct[i].effMaxWidth;
    635                 m_layoutStruct[i].calcWidth += w;
    636             }
    637         }
    638     }
    639 
    640     // spread over percent colums
    641     if (available > 0 && m_hasPercent && totalPercent < 100 * percentScaleFactor) {
    642         // still have some width to spread, distribute weighted to percent columns
    643         for (int i = 0; i < nEffCols; i++) {
    644             Length &width = m_layoutStruct[i].effWidth;
    645             if (width.isPercent()) {
    646                 int w = available * width.rawValue() / totalPercent;
    647                 available -= w;
    648                 totalPercent -= width.rawValue();
    649                 m_layoutStruct[i].calcWidth += w;
    650                 if (!available || !totalPercent) break;
    651             }
    652         }
    653     }
    654 
    655     // spread over the rest
    656     if (available > 0 && nEffCols > numAutoEmptyCellsOnly) {
    657         int total = nEffCols - numAutoEmptyCellsOnly;
    658         // still have some width to spread
    659         int i = nEffCols;
    660         while (i--) {
    661             // variable columns with empty cells only don't get any width
    662             if (m_layoutStruct[i].effWidth.isAuto() && m_layoutStruct[i].emptyCellsOnly)
    663                 continue;
    664             int w = available / total;
    665             available -= w;
    666             total--;
    667             m_layoutStruct[i].calcWidth += w;
    668         }
    669     }
    670 
    671     // If we have overallocated, reduce every cell according to the difference between desired width and minwidth
    672     // this seems to produce to the pixel exact results with IE. Wonder is some of this also holds for width distributing.
    673     if (available < 0) {
    674         // Need to reduce cells with the following prioritization:
    675         // (1) Auto
    676         // (2) Relative
    677         // (3) Fixed
    678         // (4) Percent
    679         // This is basically the reverse of how we grew the cells.
    680         if (available < 0) {
    681             int mw = 0;
    682             for (int i = nEffCols-1; i >= 0; i--) {
    683                 Length &width = m_layoutStruct[i].effWidth;
    684                 if (width.isAuto())
    685                     mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth;
    686             }
    687 
    688             for (int i = nEffCols-1; i >= 0 && mw > 0; i--) {
    689                 Length &width = m_layoutStruct[i].effWidth;
    690                 if (width.isAuto()) {
    691                     int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth;
    692                     int reduce = available * minMaxDiff / mw;
    693                     m_layoutStruct[i].calcWidth += reduce;
    694                     available -= reduce;
    695                     mw -= minMaxDiff;
    696                     if (available >= 0)
    697                         break;
    698                 }
    699             }
    700         }
    701 
    702         if (available < 0) {
    703             int mw = 0;
    704             for (int i = nEffCols-1; i >= 0; i--) {
    705                 Length& width = m_layoutStruct[i].effWidth;
    706                 if (width.isRelative())
    707                     mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth;
    708             }
    709 
    710             for (int i = nEffCols-1; i >= 0 && mw > 0; i--) {
    711                 Length& width = m_layoutStruct[i].effWidth;
    712                 if (width.isRelative()) {
    713                     int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth;
    714                     int reduce = available * minMaxDiff / mw;
    715                     m_layoutStruct[i].calcWidth += reduce;
    716                     available -= reduce;
    717                     mw -= minMaxDiff;
    718                     if (available >= 0)
    719                         break;
    720                 }
    721             }
    722         }
    723 
    724         if (available < 0) {
    725             int mw = 0;
    726             for (int i = nEffCols-1; i >= 0; i--) {
    727                 Length& width = m_layoutStruct[i].effWidth;
    728                 if (width.isFixed())
    729                     mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth;
    730             }
    731 
    732             for (int i = nEffCols-1; i >= 0 && mw > 0; i--) {
    733                 Length& width = m_layoutStruct[i].effWidth;
    734                 if (width.isFixed()) {
    735                     int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth;
    736                     int reduce = available * minMaxDiff / mw;
    737                     m_layoutStruct[i].calcWidth += reduce;
    738                     available -= reduce;
    739                     mw -= minMaxDiff;
    740                     if (available >= 0)
    741                         break;
    742                 }
    743             }
    744         }
    745 
    746         if (available < 0) {
    747             int mw = 0;
    748             for (int i = nEffCols-1; i >= 0; i--) {
    749                 Length& width = m_layoutStruct[i].effWidth;
    750                 if (width.isPercent())
    751                     mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth;
    752             }
    753 
    754             for (int i = nEffCols-1; i >= 0 && mw > 0; i--) {
    755                 Length& width = m_layoutStruct[i].effWidth;
    756                 if (width.isPercent()) {
    757                     int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth;
    758                     int reduce = available * minMaxDiff / mw;
    759                     m_layoutStruct[i].calcWidth += reduce;
    760                     available -= reduce;
    761                     mw -= minMaxDiff;
    762                     if (available >= 0)
    763                         break;
    764                 }
    765             }
    766         }
    767     }
    768 
    769     int pos = 0;
    770     for (int i = 0; i < nEffCols; i++) {
    771         m_table->columnPositions()[i] = pos;
    772         pos += m_layoutStruct[i].calcWidth + m_table->hBorderSpacing();
    773     }
    774     m_table->columnPositions()[m_table->columnPositions().size() - 1] = pos;
    775 }
    776 
    777 
    778 void AutoTableLayout::calcPercentages() const
    779 {
    780     unsigned totalPercent = 0;
    781     for (unsigned i = 0; i < m_layoutStruct.size(); i++) {
    782         if (m_layoutStruct[i].width.isPercent())
    783             totalPercent += m_layoutStruct[i].width.rawValue();
    784     }
    785     m_totalPercent = totalPercent / percentScaleFactor;
    786     m_percentagesDirty = false;
    787 }
    788 
    789 #undef DEBUG_LAYOUT
    790 
    791 }
    792