khtml Library API Documentation

table_layout.cpp

00001 /*
00002  * This file is part of the HTML rendering engine for KDE.
00003  *
00004  * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
00005  *           (C) 2002 Dirk Mueller (mueller@kde.org)
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Library General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2 of the License.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Library General Public License
00018  * along with this library; see the file COPYING.LIB.  If not, write to
00019  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00020  * Boston, MA 02111-1307, USA.
00021  *
00022  * $Id: table_layout.cpp,v 1.24.2.3 2003/07/18 16:06:39 staikos Exp $
00023  */
00024 #include "table_layout.h"
00025 #include "render_table.h"
00026 
00027 #include <kglobal.h>
00028 
00029 using namespace khtml;
00030 
00031 // #define DEBUG_LAYOUT
00032 
00033 /*
00034   The text below is from the CSS 2.1 specs.
00035 
00036   Fixed table layout
00037   ------------------
00038 
00039   With this (fast) algorithm, the horizontal layout of the table does
00040   not depend on the contents of the cells; it only depends on the
00041   table's width, the width of the columns, and borders or cell
00042   spacing.
00043 
00044   The table's width may be specified explicitly with the 'width'
00045   property. A value of 'auto' (for both 'display: table' and 'display:
00046   inline-table') means use the automatic table layout algorithm.
00047 
00048   In the fixed table layout algorithm, the width of each column is
00049   determined as follows:
00050 
00051     1. A column element with a value other than 'auto' for the 'width'
00052     property sets the width for that column.
00053 
00054     2.Otherwise, a cell in the first row with a value other than
00055     'auto' for the 'width' property sets the width for that column. If
00056     the cell spans more than one column, the width is divided over the
00057     columns.
00058 
00059     3. Any remaining columns equally divide the remaining horizontal
00060     table space (minus borders or cell spacing).
00061 
00062   The width of the table is then the greater of the value of the
00063   'width' property for the table element and the sum of the column
00064   widths (plus cell spacing or borders). If the table is wider than
00065   the columns, the extra space should be distributed over the columns.
00066 
00067 
00068   In this manner, the user agent can begin to lay out the table once
00069   the entire first row has been received. Cells in subsequent rows do
00070   not affect column widths. Any cell that has content that overflows
00071   uses the 'overflow' property to determine whether to clip the
00072   overflow content.
00073 
00074 _____________________________________________________
00075 
00076   This is not quite true when comparing to IE. IE always honours
00077   table-layout:fixed and treats a variable table width as 100%. Makes
00078   a lot of sense, and is implemented here the same way.
00079 
00080 */
00081 
00082 FixedTableLayout::FixedTableLayout( RenderTable *table )
00083     : TableLayout ( table )
00084 {
00085 }
00086 
00087 FixedTableLayout::~FixedTableLayout()
00088 {
00089 }
00090 
00091 int FixedTableLayout::calcWidthArray( int tableWidth )
00092 {
00093     int usedWidth = 0;
00094 
00095     // iterate over all <col> elements
00096     RenderObject *child = table->firstChild();
00097     int cCol = 0;
00098     int nEffCols = table->numEffCols();
00099     width.resize( nEffCols );
00100     width.fill( Length( Variable ) );
00101 
00102 #ifdef DEBUG_LAYOUT
00103     qDebug("FixedTableLayout::calcWidthArray( %d )", tableWidth );
00104     qDebug("    col elements:");
00105 #endif
00106 
00107     Length grpWidth;
00108     while ( child ) {
00109     if ( child->isTableCol() ) {
00110         RenderTableCol *col = static_cast<RenderTableCol *>(child);
00111         int span = col->span();
00112         if ( col->firstChild() ) {
00113         grpWidth = col->style()->width();
00114         } else {
00115         Length w = col->style()->width();
00116         if ( w.isVariable() )
00117             w = grpWidth;
00118         int effWidth = 0;
00119         if ( w.isFixed() && w.value() > 0 ) {
00120                     effWidth = w.value();
00121             effWidth = KMIN( effWidth, 32760 );
00122         }
00123 #ifdef DEBUG_LAYOUT
00124         qDebug("    col element: effCol=%d, span=%d: %d w=%d type=%d",
00125                cCol, span, effWidth,  w.value, w.type);
00126 #endif
00127         int usedSpan = 0;
00128         int i = 0;
00129         while ( usedSpan < span ) {
00130             if( cCol + i >= nEffCols ) {
00131             table->appendColumn( span - usedSpan );
00132             nEffCols++;
00133             width.resize( nEffCols );
00134             width[nEffCols-1] = Length();
00135             }
00136             int eSpan = table->spanOfEffCol( cCol+i );
00137             if ( (w.isFixed() || w.isPercent()) && w.value() > 0 ) {
00138             width[cCol+i] = Length( w.value() * eSpan, w.type() );
00139             usedWidth += effWidth * eSpan;
00140 #ifdef DEBUG_LAYOUT
00141             qDebug("    setting effCol %d (span=%d) to width %d(type=%d)",
00142                    cCol+i, eSpan, width[cCol+i].value, width[cCol+i].type );
00143 #endif
00144             }
00145             usedSpan += eSpan;
00146             i++;
00147         }
00148         cCol += i;
00149         }
00150     } else {
00151         break;
00152     }
00153 
00154     RenderObject *next = child->firstChild();
00155     if ( !next )
00156         next = child->nextSibling();
00157     if ( !next && child->parent()->isTableCol() ) {
00158         next = child->parent()->nextSibling();
00159         grpWidth = Length();
00160     }
00161     child = next;
00162     }
00163 
00164 #ifdef DEBUG_LAYOUT
00165     qDebug("    first row:");
00166 #endif
00167     // iterate over the first row in case some are unspecified.
00168     RenderTableSection *section = table->head;
00169     if ( !section )
00170     section = table->firstBody;
00171     if ( !section )
00172     section = table->foot;
00173     if ( section && section->firstChild() ) {
00174     cCol = 0;
00175     // get the first cell in the first row
00176     child = section->firstChild()->firstChild();
00177     while ( child ) {
00178         if ( child->isTableCell() ) {
00179         RenderTableCell *cell = static_cast<RenderTableCell *>(child);
00180         Length w = cell->style()->width();
00181         int span = cell->colSpan();
00182         int effWidth = 0;
00183         if ( (w.isFixed() || w.isPercent()) && w.value() > 0 ) {
00184                     effWidth = w.value();
00185             effWidth = kMin( effWidth, 32760 );
00186         }
00187 #ifdef DEBUG_LAYOUT
00188         qDebug("    table cell: effCol=%d, span=%d: %d",  cCol, span, effWidth);
00189 #endif
00190         int usedSpan = 0;
00191         int i = 0;
00192         while ( usedSpan < span ) {
00193             Q_ASSERT( cCol + i < nEffCols );
00194             int eSpan = table->spanOfEffCol( cCol+i );
00195             // only set if no col element has already set it.
00196             if ( width[cCol+i].isVariable() && !w.isVariable() ) {
00197             width[cCol+i] = Length( w.value()*eSpan, w.type() );
00198             usedWidth += effWidth*eSpan;
00199 #ifdef DEBUG_LAYOUT
00200             qDebug("    setting effCol %d (span=%d) to width %d(type=%d)",
00201                    cCol+i, eSpan, width[cCol+i].value, width[cCol+i].type );
00202 #endif
00203             }
00204 #ifdef DEBUG_LAYOUT
00205             else {
00206             qDebug("    width of col %d already defined (span=%d)", cCol, table->spanOfEffCol( cCol ) );
00207             }
00208 #endif
00209             usedSpan += eSpan;
00210             i++;
00211         }
00212         cCol += i;
00213         } else {
00214         Q_ASSERT( false );
00215         }
00216         child = child->nextSibling();
00217     }
00218     }
00219 
00220     return usedWidth;
00221 
00222 }
00223 
00224 void FixedTableLayout::calcMinMaxWidth()
00225 {
00226     // we might want to wait until we have all of the first row before
00227     // layouting for the first time.
00228 
00229     // only need to calculate the minimum width as the sum of the
00230     // cols/cells with a fixed width.
00231     //
00232     // The maximum width is kMax( minWidth, tableWidth ) if table
00233     // width is fixed. If table width is percent, we set maxWidth to
00234     // unlimited.
00235 
00236     int bs = table->bordersAndSpacing();
00237     table->m_minWidth = 0;
00238     table->m_maxWidth = 0;
00239     short tableWidth = table->style()->width().isFixed() ? table->style()->width().value() - bs : 0;
00240 
00241     table->m_minWidth = calcWidthArray( tableWidth );
00242     table->m_minWidth += bs;
00243 
00244     table->m_minWidth = kMax( table->m_minWidth, tableWidth );
00245     table->m_maxWidth = table->m_minWidth;
00246     if ( !tableWidth ) {
00247     bool haveNonFixed = false;
00248     for ( unsigned int i = 0; i < width.size(); i++ ) {
00249         if ( !width[i].isFixed() ) {
00250         haveNonFixed = true;
00251         break;
00252         }
00253     }
00254     if ( haveNonFixed )
00255         table->m_maxWidth = 0x7fff;
00256     }
00257 #ifdef DEBUG_LAYOUT
00258     qDebug("FixedTableLayout::calcMinMaxWidth: minWidth=%d, maxWidth=%d", table->m_minWidth, table->m_maxWidth );
00259 #endif
00260 }
00261 
00262 void FixedTableLayout::layout()
00263 {
00264     int tableWidth = table->width() - table->bordersAndSpacing();
00265     int available = tableWidth;
00266     int nEffCols = table->numEffCols();
00267 #ifdef DEBUG_LAYOUT
00268     qDebug("FixedTableLayout::layout: tableWidth=%d, numEffCols=%d",  tableWidth, nEffCols);
00269 #endif
00270 
00271 
00272     QMemArray<short> calcWidth;
00273     calcWidth.resize( nEffCols );
00274     calcWidth.fill( -1 );
00275 
00276     // first assign  fixed width
00277     for ( int i = 0; i < nEffCols; i++ ) {
00278     if ( width[i].isFixed() ) {
00279         calcWidth[i] = width[i].value();
00280         available -= width[i].value();
00281     }
00282     }
00283 
00284     // assign  percent width
00285     if ( available > 0 ) {
00286     int totalPercent = 0;
00287     for ( int i = 0; i < nEffCols; i++ )
00288         if ( width[i].isPercent() )
00289         totalPercent += width[i].value();
00290 
00291     // calculate how much to distribute to percent cells.
00292     int base = tableWidth * totalPercent / 100;
00293     if ( base > available )
00294         base = available;
00295     else
00296         totalPercent = 100;
00297 
00298 #ifdef DEBUG_LAYOUT
00299     qDebug("FixedTableLayout::layout: assigning percent width, base=%d, totalPercent=%d", base, totalPercent);
00300 #endif
00301         for ( int i = 0; available > 0 && i < nEffCols; i++ ) {
00302             if ( width[i].isPercent() ) {
00303                 int w = base * width[i].value() / totalPercent;
00304                 available -= w;
00305                 calcWidth[i] = w;
00306             }
00307         }
00308     }
00309 
00310     // assign  variable width
00311     if ( available > 0 ) {
00312     int totalVariable = 0;
00313     for ( int i = 0; i < nEffCols; i++ )
00314         if ( width[i].isVariable() )
00315         totalVariable++;
00316 
00317         for ( int i = 0; available > 0 && i < nEffCols; i++ ) {
00318             if ( width[i].isVariable() ) {
00319                 int w = available / totalVariable;
00320                 available -= w;
00321                 calcWidth[i] = w;
00322         totalVariable--;
00323             }
00324         }
00325     }
00326 
00327     for ( int i = 0; i < nEffCols; i++ )
00328     if ( calcWidth[i] <= 0 )
00329         calcWidth[i] = 0; // IE gives min 1 px...
00330 
00331     int pos = 0;
00332     int spacing = table->cellSpacing();
00333     for ( int i = 0; i < nEffCols; i++ ) {
00334 #ifdef DEBUG_LAYOUT
00335     qDebug("col %d: %d (width %d)", i, pos, calcWidth[i] );
00336 #endif
00337     table->columnPos[i] = pos;
00338     pos += calcWidth[i] + spacing;
00339     }
00340     table->columnPos[table->columnPos.size()-1] = pos;
00341 }
00342 
00343 // -------------------------------------------------------------------------
00344 // -------------------------------------------------------------------------
00345 
00346 
00347 AutoTableLayout::AutoTableLayout( RenderTable* table )
00348     : TableLayout( table )
00349 {
00350     percentagesDirty = true;
00351     effWidthDirty = true;
00352     total_percent = 0;
00353     hasPercent = false;
00354 }
00355 
00356 AutoTableLayout::~AutoTableLayout()
00357 {
00358 }
00359 
00360 /* recalculates the full structure needed to do layouting and minmax calculations.
00361    This is usually calculated on the fly, but needs to be done fully when table cells change
00362    dynamically
00363 */
00364 void AutoTableLayout::recalcColumn( int effCol )
00365 {
00366     Layout &l = layoutStruct[effCol];
00367 
00368     RenderObject *child = table->firstChild();
00369     // first we iterate over all rows.
00370 
00371     RenderTableCell *fixedContributor = 0;
00372     RenderTableCell *maxContributor = 0;
00373 
00374     while ( child ) {
00375     if ( child->isTableSection() ) {
00376         RenderTableSection *section = static_cast<RenderTableSection *>(child);
00377         int numRows = section->numRows();
00378         RenderTableCell *last = 0;
00379         for ( int i = 0; i < numRows; i++ ) {
00380         RenderTableCell *cell = section->cellAt( i,  effCol );
00381         if ( cell == (RenderTableCell *)-1 )
00382             continue;
00383         if ( cell && cell->colSpan() == 1 ) {
00384             if ( !cell->minMaxKnown() )
00385             cell->calcMinMaxWidth();
00386             if ( cell->minWidth() > l.minWidth )
00387             l.minWidth = cell->minWidth();
00388             if ( cell->maxWidth() > l.maxWidth ) {
00389             l.maxWidth = cell->maxWidth();
00390             maxContributor = cell;
00391             }
00392 
00393             Length w = cell->style()->width();
00394                     w.l.value = kMin( 32767, kMax( 0, w.value() ) );
00395             switch( w.type() ) {
00396             case Fixed:
00397             // ignore width=0
00398             if ( w.value() > 0 && !l.width.isPercent() ) {
00399                             // ### we should use box'es paddings here I guess.
00400                             int wval = w.value() + table->cellPadding() * 2;
00401                 if ( l.width.isFixed() ) {
00402                                 // Nav/IE weirdness
00403                 if ((wval > l.width.value()) ||
00404                     ((l.width.value() == wval) && (maxContributor == cell))) {
00405                     l.width.l.value = wval;
00406                     fixedContributor = cell;
00407                 }
00408                 } else {
00409                                 l.width = Length( wval, Fixed );
00410                 fixedContributor = cell;
00411                 }
00412             }
00413             break;
00414             case Percent:
00415                         hasPercent = true;
00416                         if ( w.value() > 0 && (!l.width.isPercent() || w.value() > l.width.value() ) )
00417                             l.width = w;
00418             break;
00419             case Relative:
00420             if ( w.isVariable() || (w.isRelative() && w.value() > l.width.value() ) )
00421                 l.width = w;
00422             default:
00423             break;
00424             }
00425         } else {
00426             if ( !effCol || section->cellAt( i, effCol-1 ) != cell )
00427             insertSpanCell( cell );
00428             last = cell;
00429         }
00430         }
00431     }
00432     child = child->nextSibling();
00433     }
00434 
00435     // Nav/IE weirdness
00436     if ( l.width.isFixed() ) {
00437     if ( table->style()->htmlHacks()
00438          && (l.maxWidth > l.width.value()) && (fixedContributor != maxContributor)) {
00439         l.width = Length();
00440         fixedContributor = 0;
00441     }
00442     }
00443 
00444     l.maxWidth = kMax(l.maxWidth, l.minWidth);
00445 #ifdef DEBUG_LAYOUT
00446     qDebug("col %d, final min=%d, max=%d, width=%d(%d)", effCol, l.minWidth, l.maxWidth, l.width.value,  l.width.type );
00447 #endif
00448 
00449     // ### we need to add col elements aswell
00450 }
00451 
00452 
00453 void AutoTableLayout::fullRecalc()
00454 {
00455     percentagesDirty = true;
00456     hasPercent = false;
00457     effWidthDirty = true;
00458 
00459     int nEffCols = table->numEffCols();
00460     layoutStruct.resize( nEffCols );
00461     layoutStruct.fill( Layout() );
00462     spanCells.fill( 0 );
00463 
00464     RenderObject *child = table->firstChild();
00465     Length grpWidth;
00466     int cCol = 0;
00467     while ( child ) {
00468     if ( child->isTableCol() ) {
00469         RenderTableCol *col = static_cast<RenderTableCol *>(child);
00470         int span = col->span();
00471         if ( col->firstChild() ) {
00472         grpWidth = col->style()->width();
00473         } else {
00474         Length w = col->style()->width();
00475         if ( w.isVariable() )
00476             w = grpWidth;
00477         if ( (w.isFixed() && w.value() == 0) ||
00478              (w.isPercent() && w.value() == 0) )
00479             w = Length();
00480         int cEffCol = table->colToEffCol( cCol );
00481 #ifdef DEBUG_LAYOUT
00482         qDebug("    col element %d (eff=%d): Length=%d(%d), span=%d, effColSpan=%d",  cCol, cEffCol, w.value, w.type, span, table->spanOfEffCol(cEffCol ) );
00483 #endif
00484         if ( !w.isVariable() && span == 1 && cEffCol < nEffCols ) {
00485             if ( table->spanOfEffCol( cEffCol ) == 1 ) {
00486             layoutStruct[cEffCol].width = w;
00487                         if (w.isFixed() && layoutStruct[cEffCol].maxWidth < w.value())
00488                             layoutStruct[cEffCol].maxWidth = w.value();
00489                     }
00490         }
00491         cCol += span;
00492         }
00493     } else {
00494         break;
00495     }
00496 
00497     RenderObject *next = child->firstChild();
00498     if ( !next )
00499         next = child->nextSibling();
00500     if ( !next && child->parent()->isTableCol() ) {
00501         next = child->parent()->nextSibling();
00502         grpWidth = Length();
00503     }
00504     child = next;
00505     }
00506 
00507 
00508     for ( int i = 0; i < nEffCols; i++ )
00509     recalcColumn( i );
00510 }
00511 
00512 
00513 void AutoTableLayout::calcMinMaxWidth()
00514 {
00515 #ifdef DEBUG_LAYOUT
00516     qDebug("AutoTableLayout::calcMinMaxWidth");
00517 #endif
00518     fullRecalc();
00519 
00520     int spanMaxWidth = calcEffectiveWidth();
00521     int minWidth = 0;
00522     int maxWidth = 0;
00523     int maxPercent = 0;
00524     int maxNonPercent = 0;
00525 
00526     for ( unsigned int i = 0; i < layoutStruct.size(); i++ ) {
00527     minWidth += layoutStruct[i].effMinWidth;
00528     maxWidth += layoutStruct[i].effMaxWidth;
00529     if ( layoutStruct[i].effWidth.isPercent() ) {
00530         int pw = ( layoutStruct[i].effMaxWidth * 100) / layoutStruct[i].effWidth.value();
00531         maxPercent = kMax( pw,  maxPercent );
00532     } else {
00533         maxNonPercent += layoutStruct[i].effMaxWidth;
00534     }
00535     }
00536 
00537     int totalpct = totalPercent();
00538     if ( totalpct < 100 ) {
00539     maxNonPercent = (maxNonPercent * 100 + 50) / (100-totalpct);
00540     maxWidth = kMax( maxNonPercent,  maxWidth );
00541     }
00542     maxWidth = kMax( maxWidth, maxPercent );
00543     maxWidth = kMax( maxWidth, spanMaxWidth );
00544 
00545     int bs = table->bordersAndSpacing();
00546     minWidth += bs;
00547     maxWidth += bs;
00548 
00549     Length tw = table->style()->width();
00550     if ( tw.isFixed() && tw.value() > 0 ) {
00551     minWidth = kMax( minWidth, int( tw.value() ) );
00552     maxWidth = minWidth;
00553     }
00554 
00555     table->m_maxWidth = maxWidth;
00556     table->m_minWidth = minWidth;
00557 #ifdef DEBUG_LAYOUT
00558     qDebug("    minWidth=%d, maxWidth=%d", table->m_minWidth, table->m_maxWidth );
00559 #endif
00560 }
00561 
00562 /*
00563   This method takes care of colspans.
00564   effWidth is the same as width for cells without colspans. If we have colspans, they get modified.
00565  */
00566 int AutoTableLayout::calcEffectiveWidth()
00567 {
00568     int tMaxWidth = 0;
00569 
00570     unsigned int nEffCols = layoutStruct.size();
00571     int spacing = table->cellSpacing();
00572 #ifdef DEBUG_LAYOUT
00573     qDebug("AutoTableLayout::calcEffectiveWidth for %d cols", nEffCols );
00574 #endif
00575     for ( unsigned int i = 0; i < nEffCols; i++ ) {
00576     layoutStruct[i].effWidth = layoutStruct[i].width;
00577     layoutStruct[i].effMinWidth = layoutStruct[i].minWidth;
00578     layoutStruct[i].effMaxWidth = layoutStruct[i].maxWidth;
00579     }
00580 
00581     for ( unsigned int i = 0; i < spanCells.size(); i++ ) {
00582     RenderTableCell *cell = spanCells[i];
00583     if ( !cell || cell == (RenderTableCell *)-1 )
00584         break;
00585     int span = cell->colSpan();
00586 
00587     Length w = cell->style()->width();
00588     if ( !w.isRelative() && w.value() == 0 )
00589         w = Length(); // make it Variable
00590 
00591     int col = table->colToEffCol( cell->col() );
00592     unsigned int lastCol = col;
00593     int cMinWidth = cell->minWidth() + spacing;
00594     int cMaxWidth = cell->maxWidth() + spacing;
00595     int totalPercent = 0;
00596     int minWidth = 0;
00597     int maxWidth = 0;
00598     bool allColsArePercent = true;
00599     bool allColsAreFixed = true;
00600     bool haveVariable = false;
00601     int fixedWidth = 0;
00602 #ifdef DEBUG_LAYOUT
00603     int cSpan = span;
00604 #endif
00605     while ( lastCol < nEffCols && span > 0 ) {
00606         switch( layoutStruct[lastCol].width.type() ) {
00607         case Percent:
00608         totalPercent += layoutStruct[lastCol].width.value();
00609         allColsAreFixed = false;
00610         break;
00611         case Fixed:
00612                 if (layoutStruct[lastCol].width.value() > 0) {
00613                     fixedWidth += layoutStruct[lastCol].width.value();
00614                     allColsArePercent = false;
00615                     // IE resets effWidth to Variable here, but this breaks the konqueror about page and seems to be some bad
00616                     // legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither.
00617                     break;
00618                 }
00619                 // fall through
00620         case Variable:
00621         haveVariable = true;
00622         // fall through
00623         default:
00624         layoutStruct[lastCol].effWidth = Length();
00625         allColsArePercent = false;
00626         allColsAreFixed = false;
00627         }
00628         span -= table->spanOfEffCol( lastCol );
00629         minWidth += layoutStruct[lastCol].effMinWidth;
00630         maxWidth += layoutStruct[lastCol].effMaxWidth;
00631         lastCol++;
00632         cMinWidth -= spacing;
00633         cMaxWidth -= spacing;
00634     }
00635 #ifdef DEBUG_LAYOUT
00636     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.value, cMinWidth, minWidth, fixedWidth );
00637 #endif
00638 
00639     // adjust table max width if needed
00640     if ( w.isPercent() ) {
00641         if ( totalPercent > w.value() || allColsArePercent ) {
00642         // can't satify this condition, treat as variable
00643         w = Length();
00644         } else {
00645         int spanMax = QMAX( maxWidth, cMaxWidth );
00646 #ifdef DEBUG_LAYOUT
00647         qDebug("    adjusting tMaxWidth (%d): spanMax=%d, value=%d, totalPercent=%d", tMaxWidth, spanMax, w.value(), totalPercent );
00648 #endif
00649         tMaxWidth = QMAX( tMaxWidth, spanMax * 100 / w.value() );
00650 
00651         // all non percent columns in the span get percent vlaues to sum up correctly.
00652         int percentMissing = w.value() - totalPercent;
00653         int totalWidth = 0;
00654         for ( unsigned int pos = col; pos < lastCol; pos++ ) {
00655             if ( !(layoutStruct[pos].width.isPercent() ) )
00656             totalWidth += layoutStruct[pos].effMaxWidth;
00657         }
00658 
00659         for ( unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++ ) {
00660             if ( !(layoutStruct[pos].width.isPercent() ) ) {
00661             int percent = percentMissing * layoutStruct[pos].effMaxWidth / totalWidth;
00662 #ifdef DEBUG_LAYOUT
00663             qDebug("   col %d: setting percent value %d effMaxWidth=%d totalWidth=%d", pos, percent, layoutStruct[pos].effMaxWidth, totalWidth );
00664 #endif
00665             totalWidth -= layoutStruct[pos].effMaxWidth;
00666             percentMissing -= percent;
00667             if ( percent > 0 )
00668                 layoutStruct[pos].effWidth = Length( percent, Percent );
00669             else
00670                 layoutStruct[pos].effWidth = Length();
00671             }
00672         }
00673 
00674         }
00675     }
00676 
00677     // make sure minWidth and maxWidth of the spanning cell are honoured
00678     if ( cMinWidth > minWidth ) {
00679         if ( allColsAreFixed ) {
00680 #ifdef DEBUG_LAYOUT
00681         qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d accroding to fixed sum %d", col, lastCol-1, cMinWidth, minWidth, fixedWidth );
00682 #endif
00683         for ( unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++ ) {
00684             int w = QMAX( layoutStruct[pos].effMinWidth, cMinWidth * layoutStruct[pos].width.value() / fixedWidth );
00685 #ifdef DEBUG_LAYOUT
00686             qDebug("   col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
00687 #endif
00688             fixedWidth -= layoutStruct[pos].width.value();
00689             cMinWidth -= w;
00690             layoutStruct[pos].effMinWidth = w;
00691         }
00692 
00693         } else {
00694 #ifdef DEBUG_LAYOUT
00695         qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d", col, lastCol-1, cMinWidth, minWidth );
00696 #endif
00697         int maxw = maxWidth;
00698         for ( unsigned int pos = col; minWidth > 0 && pos < lastCol; pos++ ) {
00699 
00700             int w;
00701             if ( layoutStruct[pos].width.isFixed() && haveVariable && fixedWidth <= cMinWidth ) {
00702             w = QMAX( layoutStruct[pos].effMinWidth, layoutStruct[pos].width.value() );
00703             fixedWidth -= layoutStruct[pos].width.value();
00704             } else {
00705             w = QMAX( layoutStruct[pos].effMinWidth, cMinWidth * layoutStruct[pos].effMaxWidth / maxw );
00706             }
00707 #ifdef DEBUG_LAYOUT
00708             qDebug("   col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
00709 #endif
00710             maxw -= layoutStruct[pos].effMaxWidth;
00711             cMinWidth -= w;
00712             layoutStruct[pos].effMinWidth = w;
00713         }
00714         }
00715     }
00716     if ( !w.isPercent() ) {
00717         if ( cMaxWidth > maxWidth ) {
00718 #ifdef DEBUG_LAYOUT
00719         qDebug("extending maxWidth of cols %d-%d to %dpx", col, lastCol-1, cMaxWidth );
00720 #endif
00721         for ( unsigned int pos = col; maxWidth > 0 && pos < lastCol; pos++ ) {
00722             int w = QMAX( layoutStruct[pos].effMaxWidth, cMaxWidth * layoutStruct[pos].effMaxWidth / maxWidth );
00723 #ifdef DEBUG_LAYOUT
00724             qDebug("   col %d: max=%d, effMax=%d, new=%d", pos, layoutStruct[pos].effMaxWidth, layoutStruct[pos].effMaxWidth, w );
00725 #endif
00726             maxWidth -= layoutStruct[pos].effMaxWidth;
00727             cMaxWidth -= w;
00728             layoutStruct[pos].effMaxWidth = w;
00729         }
00730         }
00731     } else {
00732         for ( unsigned int pos = col; pos < lastCol; pos++ )
00733         layoutStruct[pos].maxWidth = QMAX(layoutStruct[pos].maxWidth, layoutStruct[pos].minWidth );
00734     }
00735     }
00736     effWidthDirty = false;
00737 
00738 //     qDebug("calcEffectiveWidth: tMaxWidth=%d",  tMaxWidth );
00739     return tMaxWidth;
00740 }
00741 
00742 /* gets all cells that originate in a column and have a cellspan > 1
00743    Sorts them by increasing cellspan
00744 */
00745 void AutoTableLayout::insertSpanCell( RenderTableCell *cell )
00746 {
00747     if ( !cell || cell == (RenderTableCell *)-1 || cell->colSpan() == 1 )
00748     return;
00749 
00750 //     qDebug("inserting span cell %p with span %d", cell, cell->colSpan() );
00751     int size = spanCells.size();
00752     if ( !size || spanCells[size-1] != 0 ) {
00753     spanCells.resize( size + 10 );
00754     for ( int i = 0; i < 10; i++ )
00755         spanCells[size+i] = 0;
00756     size += 10;
00757     }
00758 
00759     // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better
00760     unsigned int pos = 0;
00761     int span = cell->colSpan();
00762     while ( pos < spanCells.size() && spanCells[pos] && span > spanCells[pos]->colSpan() )
00763     pos++;
00764     memmove( spanCells.data()+pos+1, spanCells.data()+pos, (size-pos-1)*sizeof( RenderTableCell * ) );
00765     spanCells[pos] = cell;
00766 }
00767 
00768 
00769 void AutoTableLayout::layout()
00770 {
00771     // table layout based on the values collected in the layout structure.
00772     int tableWidth = table->width() - table->bordersAndSpacing();
00773     int available = tableWidth;
00774     int nEffCols = table->numEffCols();
00775 
00776     if ( nEffCols != (int)layoutStruct.size() ) {
00777     qWarning("WARNING: nEffCols is not equal to layoutstruct!" );
00778     fullRecalc();
00779     nEffCols = table->numEffCols();
00780     }
00781 #ifdef DEBUG_LAYOUT
00782     qDebug("AutoTableLayout::layout()");
00783 #endif
00784 
00785     if ( effWidthDirty )
00786     calcEffectiveWidth();
00787 
00788 #ifdef DEBUG_LAYOUT
00789     qDebug("    tableWidth=%d,  nEffCols=%d", tableWidth,  nEffCols );
00790     for ( int i = 0; i < nEffCols; i++ ) {
00791     qDebug("    effcol %d is of type %d value %d, minWidth=%d, maxWidth=%d",
00792            i, layoutStruct[i].width.type, layoutStruct[i].width.value,
00793            layoutStruct[i].minWidth, layoutStruct[i].maxWidth );
00794     qDebug("        effective: type %d value %d, minWidth=%d, maxWidth=%d",
00795            layoutStruct[i].effWidth.type, layoutStruct[i].effWidth.value,
00796            layoutStruct[i].effMinWidth, layoutStruct[i].effMaxWidth );
00797     }
00798 #endif
00799 
00800     bool havePercent = false;
00801     bool haveRelative = false;
00802     int totalRelative = 0;
00803     int numVariable = 0;
00804     int numFixed = 0;
00805     int totalVariable = 0;
00806     int totalFixed = 0;
00807     int totalPercent = 0;
00808     int allocVariable = 0;
00809 
00810     // fill up every cell with it's minWidth
00811     for ( int i = 0; i < nEffCols; i++ ) {
00812     int w = layoutStruct[i].effMinWidth;
00813     layoutStruct[i].calcWidth = w;
00814     available -= w;
00815     Length& width = layoutStruct[i].effWidth;
00816         switch( width.type()) {
00817         case Percent:
00818         havePercent = true;
00819         totalPercent += width.value();
00820             break;
00821         case Relative:
00822         haveRelative = true;
00823         totalRelative += width.value();
00824             break;
00825         case Fixed:
00826             numFixed++;
00827             totalFixed += layoutStruct[i].effMaxWidth;
00828             // fall through
00829             break;
00830         case Variable:
00831         case Static:
00832             numVariable++;
00833             totalVariable += layoutStruct[i].effMaxWidth;
00834             allocVariable += w;
00835         }
00836     }
00837 
00838     // then allocate width to fixed cols
00839     if ( available > 0 ) {
00840     for ( int i = 0; i < nEffCols; ++i ) {
00841         Length w = layoutStruct[i].effWidth;
00842         if ( w.isFixed() && w.value() > layoutStruct[i].calcWidth ) {
00843         available += layoutStruct[i].calcWidth - w.value();
00844         layoutStruct[i].calcWidth = w.value();
00845             }
00846     }
00847     }
00848 #ifdef DEBUG_LAYOUT
00849     qDebug("fixed satisfied: available is %d", available);
00850 #endif
00851 
00852     // then allocate width to percent cols
00853     if ( available > 0 && havePercent ) {
00854     for ( int i = 0; i < nEffCols; i++ ) {
00855         Length width = layoutStruct[i].effWidth;
00856         if ( width.isPercent() ) {
00857                 int w = kMax ( int( layoutStruct[i].effMinWidth ), width.minWidth( tableWidth ) );
00858         available += layoutStruct[i].calcWidth - w;
00859         layoutStruct[i].calcWidth = w;
00860         }
00861     }
00862     if ( totalPercent > 100 ) {
00863         // remove overallocated space from the last columns
00864         int excess = tableWidth*(totalPercent-100)/100;
00865         for ( int i = nEffCols-1; i >= 0; i-- ) {
00866         if ( layoutStruct[i].effWidth.isPercent() ) {
00867             int w = layoutStruct[i].calcWidth;
00868             int reduction = kMin( w,  excess );
00869             // the lines below might look inconsistent, but that's the way it's handled in mozilla
00870             excess -= reduction;
00871             int newWidth = kMax( int (layoutStruct[i].effMinWidth), w - reduction );
00872             available += w - newWidth;
00873             layoutStruct[i].calcWidth = newWidth;
00874             //qDebug("col %d: reducing to %d px (reduction=%d)", i, newWidth, reduction );
00875         }
00876         }
00877     }
00878     }
00879 #ifdef DEBUG_LAYOUT
00880     qDebug("percent satisfied: available is %d", available);
00881 #endif
00882 
00883     // now satisfy relative
00884     if ( available > 0 ) {
00885     for ( int i = 0; i < nEffCols; i++ ) {
00886         Length &width = layoutStruct[i].effWidth;
00887         if ( width.isRelative() && width.value() ) {
00888         // width=0* gets effMinWidth.
00889         int w = width.value()*tableWidth/totalRelative;
00890         available += layoutStruct[i].calcWidth - w;
00891         layoutStruct[i].calcWidth = w;
00892         }
00893     }
00894     }
00895 
00896     // now satisfy variable
00897     if ( available > 0 && numVariable ) {
00898     available += allocVariable; // this gets redistributed
00899     //qDebug("redistributing %dpx to %d variable columns. totalVariable=%d",  available,  numVariable,  totalVariable );
00900     for ( int i = 0; i < nEffCols; i++ ) {
00901         Length &width = layoutStruct[i].effWidth;
00902         if ( width.isVariable() && totalVariable != 0 ) {
00903         int w = kMax( int ( layoutStruct[i].calcWidth ),
00904                               available * layoutStruct[i].effMaxWidth / totalVariable );
00905         available -= w;
00906         totalVariable -= layoutStruct[i].effMaxWidth;
00907         layoutStruct[i].calcWidth = w;
00908         }
00909     }
00910     }
00911 #ifdef DEBUG_LAYOUT
00912     qDebug("variable satisfied: available is %d",  available );
00913 #endif
00914 
00915     // spread over fixed colums
00916     if ( available > 0 && numFixed) {
00917         // still have some width to spread, distribute to fixed columns
00918         for ( int i = 0; i < nEffCols; i++ ) {
00919             Length &width = layoutStruct[i].effWidth;
00920             if ( width.isFixed() ) {
00921                 int w = available * layoutStruct[i].effMaxWidth / totalFixed;
00922                 available -= w;
00923                 totalFixed -= layoutStruct[i].effMaxWidth;
00924                 layoutStruct[i].calcWidth += w;
00925             }
00926         }
00927     }
00928 
00929 #ifdef DEBUG_LAYOUT
00930     qDebug("after fixed distribution: available=%d",  available );
00931 #endif
00932 
00933     // spread over percent colums
00934     if ( available > 0 && hasPercent && totalPercent < 100) {
00935         // still have some width to spread, distribute weighted to percent columns
00936         for ( int i = 0; i < nEffCols; i++ ) {
00937             Length &width = layoutStruct[i].effWidth;
00938             if ( width.isPercent() ) {
00939                 int w = available * width.value() / totalPercent;
00940                 available -= w;
00941                 totalPercent -= width.value();
00942                 layoutStruct[i].calcWidth += w;
00943                 if (!available || !totalPercent) break;
00944             }
00945         }
00946     }
00947 
00948 #ifdef DEBUG_LAYOUT
00949     qDebug("after percent distribution: available=%d",  available );
00950 #endif
00951 
00952     // spread over the rest
00953     if ( available > 0 ) {
00954         int total = nEffCols;
00955         // still have some width to spread
00956         int i = nEffCols;
00957         while (  i-- ) {
00958             int w = available / total;
00959             available -= w;
00960             total--;
00961             layoutStruct[i].calcWidth += w;
00962         }
00963     }
00964 
00965 #ifdef DEBUG_LAYOUT
00966     qDebug("after equal distribution: available=%d",  available );
00967 #endif
00968     // if we have overallocated, reduce every cell according to the difference between desired width and minwidth
00969     // this seems to produce to the pixel exaxt results with IE. Wonder is some of this also holds for width distributing.
00970     if ( available < 0 ) {
00971     int mw = 0;
00972     for ( int i = nEffCols-1; i >= 0; i-- )
00973         mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
00974     for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
00975         int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
00976         int reduce = available * minMaxDiff / mw;
00977         layoutStruct[i].calcWidth += reduce;
00978         available -= reduce;
00979         mw -= minMaxDiff;
00980         if ( available >= 0 )
00981         break;
00982     }
00983     }
00984 
00985     //qDebug( "    final available=%d", available );
00986 
00987     int pos = 0;
00988     for ( int i = 0; i < nEffCols; i++ ) {
00989 #ifdef DEBUG_LAYOUT
00990     qDebug("col %d: %d (width %d)", i, pos, layoutStruct[i].calcWidth );
00991 #endif
00992     table->columnPos[i] = pos;
00993     pos += layoutStruct[i].calcWidth + table->cellSpacing();
00994     }
00995     table->columnPos[table->columnPos.size()-1] = pos;
00996 
00997 }
00998 
00999 
01000 void AutoTableLayout::calcPercentages() const
01001 {
01002     total_percent = 0;
01003     for ( unsigned int i = 0; i < layoutStruct.size(); i++ ) {
01004     if ( layoutStruct[i].width.isPercent() )
01005         total_percent += layoutStruct[i].width.value();
01006     }
01007     percentagesDirty = false;
01008 }
01009 
01010 #undef DEBUG_LAYOUT
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:16:40 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001