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.1 2003/01/13 22:04:49 mueller 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.type == Fixed && w.value > 0 ) {
00120                     effWidth = w.value;
00121             effWidth = QMIN( 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.type == Fixed || w.type == Percent) && 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 ) {
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.type == Fixed || w.type == Percent) && w.value > 0 ) {
00184                     effWidth = w.value;
00185             effWidth = QMIN( 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].type == Variable && w.type != Variable ) {
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().type == Fixed ? 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].type == Fixed) ) {
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].type == Fixed ) {
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].type == Percent )
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].type == Percent ) {
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].type == Variable )
00315         totalVariable++;
00316 
00317         for ( int i = 0; available > 0 && i < nEffCols; i++ ) {
00318             if ( width[i].type == Variable ) {
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             if ( w.value > 32760 )
00395             w.value = 32760;
00396             if ( w.value < 0 )
00397             w.value = 0;
00398             switch( w.type ) {
00399             case Fixed:
00400             // ignore width=0
00401             if ( w.value > 0 && (int)l.width.type != Percent ) {
00402                             // ### we should use box'es paddings here I guess.
00403                             int wval = w.value + table->cellPadding() * 2;
00404                 if ( l.width.type == Fixed ) {
00405                                 // Nav/IE weirdness
00406                 if ((wval > l.width.value) ||
00407                     ((l.width.value == wval) && (maxContributor == cell))) {
00408                     l.width.value = wval;
00409                     fixedContributor = cell;
00410                 }
00411                 } else {
00412                                 l.width.type = Fixed;
00413                 l.width.value = wval;
00414                 fixedContributor = cell;
00415                 }
00416             }
00417             break;
00418             case Percent:
00419                         hasPercent = true;
00420                         if ( w.value > 0 && (l.width.type != Percent || w.value > l.width.value ) )
00421                             l.width = w;
00422             break;
00423             case Relative:
00424             if ( w.type == Variable || (w.type == Relative && w.value > l.width.value ) )
00425                 l.width = w;
00426             default:
00427             break;
00428             }
00429         } else {
00430             if ( !effCol || section->cellAt( i, effCol-1 ) != cell )
00431             insertSpanCell( cell );
00432             last = cell;
00433         }
00434         }
00435     }
00436     child = child->nextSibling();
00437     }
00438 
00439     // Nav/IE weirdness
00440     if ( l.width.type == Fixed ) {
00441     if ( table->style()->htmlHacks()
00442          && (l.maxWidth > l.width.value) && (fixedContributor != maxContributor)) {
00443         l.width = Length();
00444         fixedContributor = 0;
00445     }
00446     }
00447 
00448     l.maxWidth = kMax(l.maxWidth, l.minWidth);
00449 #ifdef DEBUG_LAYOUT
00450     qDebug("col %d, final min=%d, max=%d, width=%d(%d)", effCol, l.minWidth, l.maxWidth, l.width.value,  l.width.type );
00451 #endif
00452 
00453     // ### we need to add col elements aswell
00454 }
00455 
00456 
00457 void AutoTableLayout::fullRecalc()
00458 {
00459     percentagesDirty = true;
00460     hasPercent = false;
00461     effWidthDirty = true;
00462 
00463     int nEffCols = table->numEffCols();
00464     layoutStruct.resize( nEffCols );
00465     layoutStruct.fill( Layout() );
00466     spanCells.fill( 0 );
00467 
00468     RenderObject *child = table->firstChild();
00469     Length grpWidth;
00470     int cCol = 0;
00471     while ( child ) {
00472     if ( child->isTableCol() ) {
00473         RenderTableCol *col = static_cast<RenderTableCol *>(child);
00474         int span = col->span();
00475         if ( col->firstChild() ) {
00476         grpWidth = col->style()->width();
00477         } else {
00478         Length w = col->style()->width();
00479         if ( w.isVariable() )
00480             w = grpWidth;
00481         if ( (w.type == Fixed && w.value == 0) ||
00482              (w.type == Percent && w.value == 0) )
00483             w = Length();
00484         int cEffCol = table->colToEffCol( cCol );
00485 #ifdef DEBUG_LAYOUT
00486         qDebug("    col element %d (eff=%d): Length=%d(%d), span=%d, effColSpan=%d",  cCol, cEffCol, w.value, w.type, span, table->spanOfEffCol(cEffCol ) );
00487 #endif
00488         if ( (int)w.type != Variable && span == 1 && cEffCol < nEffCols ) {
00489             if ( table->spanOfEffCol( cEffCol ) == 1 ) {
00490             layoutStruct[cEffCol].width = w;
00491                         if (w.isFixed() && layoutStruct[cEffCol].maxWidth < w.value)
00492                             layoutStruct[cEffCol].maxWidth = w.value;
00493                     }
00494         }
00495         cCol += span;
00496         }
00497     } else {
00498         break;
00499     }
00500 
00501     RenderObject *next = child->firstChild();
00502     if ( !next )
00503         next = child->nextSibling();
00504     if ( !next && child->parent()->isTableCol() ) {
00505         next = child->parent()->nextSibling();
00506         grpWidth = Length();
00507     }
00508     child = next;
00509     }
00510 
00511 
00512     for ( int i = 0; i < nEffCols; i++ )
00513     recalcColumn( i );
00514 }
00515 
00516 
00517 void AutoTableLayout::calcMinMaxWidth()
00518 {
00519 #ifdef DEBUG_LAYOUT
00520     qDebug("AutoTableLayout::calcMinMaxWidth");
00521 #endif
00522     fullRecalc();
00523 
00524     int spanMaxWidth = calcEffectiveWidth();
00525     int minWidth = 0;
00526     int maxWidth = 0;
00527     int maxPercent = 0;
00528     int maxNonPercent = 0;
00529 
00530     for ( unsigned int i = 0; i < layoutStruct.size(); i++ ) {
00531     minWidth += layoutStruct[i].effMinWidth;
00532     maxWidth += layoutStruct[i].effMaxWidth;
00533     if ( layoutStruct[i].effWidth.type == Percent ) {
00534         int pw = ( layoutStruct[i].effMaxWidth * 100) / layoutStruct[i].effWidth.value;
00535         maxPercent = kMax( pw,  maxPercent );
00536     } else {
00537         maxNonPercent += layoutStruct[i].effMaxWidth;
00538     }
00539     }
00540 
00541     int totalpct = totalPercent();
00542     if ( totalpct < 100 ) {
00543     maxNonPercent = (maxNonPercent * 100 + 50) / (100-totalpct);
00544     maxWidth = kMax( maxNonPercent,  maxWidth );
00545     }
00546     maxWidth = kMax( maxWidth, maxPercent );
00547     maxWidth = kMax( maxWidth, spanMaxWidth );
00548 
00549     int bs = table->bordersAndSpacing();
00550     minWidth += bs;
00551     maxWidth += bs;
00552 
00553     Length tw = table->style()->width();
00554     if ( tw.isFixed() && tw.value > 0 ) {
00555     minWidth = kMax( minWidth, int( tw.value ) );
00556     maxWidth = minWidth;
00557     }
00558 
00559     table->m_maxWidth = maxWidth;
00560     table->m_minWidth = minWidth;
00561 #ifdef DEBUG_LAYOUT
00562     qDebug("    minWidth=%d, maxWidth=%d", table->m_minWidth, table->m_maxWidth );
00563 #endif
00564 }
00565 
00566 /*
00567   This method takes care of colspans.
00568   effWidth is the same as width for cells without colspans. If we have colspans, they get modified.
00569  */
00570 int AutoTableLayout::calcEffectiveWidth()
00571 {
00572     int tMaxWidth = 0;
00573 
00574     unsigned int nEffCols = layoutStruct.size();
00575     int spacing = table->cellSpacing();
00576 #ifdef DEBUG_LAYOUT
00577     qDebug("AutoTableLayout::calcEffectiveWidth for %d cols", nEffCols );
00578 #endif
00579     for ( unsigned int i = 0; i < nEffCols; i++ ) {
00580     layoutStruct[i].effWidth = layoutStruct[i].width;
00581     layoutStruct[i].effMinWidth = layoutStruct[i].minWidth;
00582     layoutStruct[i].effMaxWidth = layoutStruct[i].maxWidth;
00583     }
00584 
00585     for ( unsigned int i = 0; i < spanCells.size(); i++ ) {
00586     RenderTableCell *cell = spanCells[i];
00587     if ( !cell || cell == (RenderTableCell *)-1 )
00588         break;
00589     int span = cell->colSpan();
00590 
00591     Length w = cell->style()->width();
00592     if ( !(w.type == Relative) && w.value == 0 )
00593         w = Length(); // make it Variable
00594 
00595     int col = table->colToEffCol( cell->col() );
00596     unsigned int lastCol = col;
00597     int cMinWidth = cell->minWidth() + spacing;
00598     int cMaxWidth = cell->maxWidth() + spacing;
00599     int totalPercent = 0;
00600     int minWidth = 0;
00601     int maxWidth = 0;
00602     bool allColsArePercent = true;
00603     bool allColsAreFixed = true;
00604     bool haveVariable = false;
00605     int fixedWidth = 0;
00606 #ifdef DEBUG_LAYOUT
00607     int cSpan = span;
00608 #endif
00609     while ( lastCol < nEffCols && span > 0 ) {
00610         switch( layoutStruct[lastCol].width.type ) {
00611         case Percent:
00612         totalPercent += layoutStruct[lastCol].width.value;
00613         allColsAreFixed = false;
00614         break;
00615         case Fixed:
00616                 if (layoutStruct[lastCol].width.value > 0) {
00617                     fixedWidth += layoutStruct[lastCol].width.value;
00618                     allColsArePercent = false;
00619                     // IE resets effWidth to Variable here, but this breaks the konqueror about page and seems to be some bad
00620                     // legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither.
00621                     break;
00622                 }
00623                 // fall through
00624         case Variable:
00625         haveVariable = true;
00626         // fall through
00627         default:
00628         layoutStruct[lastCol].effWidth = Length();
00629         allColsArePercent = false;
00630         allColsAreFixed = false;
00631         }
00632         span -= table->spanOfEffCol( lastCol );
00633         minWidth += layoutStruct[lastCol].effMinWidth;
00634         maxWidth += layoutStruct[lastCol].effMaxWidth;
00635         lastCol++;
00636         cMinWidth -= spacing;
00637         cMaxWidth -= spacing;
00638     }
00639 #ifdef DEBUG_LAYOUT
00640     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 );
00641 #endif
00642 
00643     // adjust table max width if needed
00644     if ( w.type == Percent ) {
00645         if ( totalPercent > w.value || allColsArePercent ) {
00646         // can't satify this condition, treat as variable
00647         w = Length();
00648         } else {
00649         int spanMax = QMAX( maxWidth, cMaxWidth );
00650 #ifdef DEBUG_LAYOUT
00651         qDebug("    adjusting tMaxWidth (%d): spanMax=%d, value=%d, totalPercent=%d", tMaxWidth, spanMax, w.value, totalPercent );
00652 #endif
00653         tMaxWidth = QMAX( tMaxWidth, spanMax * 100 / w.value );
00654 
00655         // all non percent columns in the span get percent vlaues to sum up correctly.
00656         int percentMissing = w.value - totalPercent;
00657         int totalWidth = 0;
00658         for ( unsigned int pos = col; pos < lastCol; pos++ ) {
00659             if ( !(layoutStruct[pos].width.type == Percent ) )
00660             totalWidth += layoutStruct[pos].effMaxWidth;
00661         }
00662 
00663         for ( unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++ ) {
00664             if ( !(layoutStruct[pos].width.type == Percent ) ) {
00665             int percent = percentMissing * layoutStruct[pos].effMaxWidth / totalWidth;
00666 #ifdef DEBUG_LAYOUT
00667             qDebug("   col %d: setting percent value %d effMaxWidth=%d totalWidth=%d", pos, percent, layoutStruct[pos].effMaxWidth, totalWidth );
00668 #endif
00669             totalWidth -= layoutStruct[pos].effMaxWidth;
00670             percentMissing -= percent;
00671             if ( percent > 0 )
00672                 layoutStruct[pos].effWidth = Length( percent, Percent );
00673             else
00674                 layoutStruct[pos].effWidth = Length();
00675             }
00676         }
00677 
00678         }
00679     }
00680 
00681     // make sure minWidth and maxWidth of the spanning cell are honoured
00682     if ( cMinWidth > minWidth ) {
00683         if ( allColsAreFixed ) {
00684 #ifdef DEBUG_LAYOUT
00685         qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d accroding to fixed sum %d", col, lastCol-1, cMinWidth, minWidth, fixedWidth );
00686 #endif
00687         for ( unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++ ) {
00688             int w = QMAX( layoutStruct[pos].effMinWidth, cMinWidth * layoutStruct[pos].width.value / fixedWidth );
00689 #ifdef DEBUG_LAYOUT
00690             qDebug("   col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
00691 #endif
00692             fixedWidth -= layoutStruct[pos].width.value;
00693             cMinWidth -= w;
00694             layoutStruct[pos].effMinWidth = w;
00695         }
00696 
00697         } else {
00698 #ifdef DEBUG_LAYOUT
00699         qDebug("extending minWidth of cols %d-%d to %dpx currentMin=%d", col, lastCol-1, cMinWidth, minWidth );
00700 #endif
00701         int maxw = maxWidth;
00702         for ( unsigned int pos = col; minWidth > 0 && pos < lastCol; pos++ ) {
00703 
00704             int w;
00705             if ( layoutStruct[pos].width.type == Fixed && haveVariable && fixedWidth <= cMinWidth ) {
00706             w = QMAX( layoutStruct[pos].effMinWidth, layoutStruct[pos].width.value );
00707             fixedWidth -= layoutStruct[pos].width.value;
00708             } else {
00709             w = QMAX( layoutStruct[pos].effMinWidth, cMinWidth * layoutStruct[pos].effMaxWidth / maxw );
00710             }
00711 #ifdef DEBUG_LAYOUT
00712             qDebug("   col %d: min=%d, effMin=%d, new=%d", pos, layoutStruct[pos].effMinWidth, layoutStruct[pos].effMinWidth, w );
00713 #endif
00714             maxw -= layoutStruct[pos].effMaxWidth;
00715             cMinWidth -= w;
00716             layoutStruct[pos].effMinWidth = w;
00717         }
00718         }
00719     }
00720     if ( !(w.type == Percent ) ) {
00721         if ( cMaxWidth > maxWidth ) {
00722 #ifdef DEBUG_LAYOUT
00723         qDebug("extending maxWidth of cols %d-%d to %dpx", col, lastCol-1, cMaxWidth );
00724 #endif
00725         for ( unsigned int pos = col; maxWidth > 0 && pos < lastCol; pos++ ) {
00726             int w = QMAX( layoutStruct[pos].effMaxWidth, cMaxWidth * layoutStruct[pos].effMaxWidth / maxWidth );
00727 #ifdef DEBUG_LAYOUT
00728             qDebug("   col %d: max=%d, effMax=%d, new=%d", pos, layoutStruct[pos].effMaxWidth, layoutStruct[pos].effMaxWidth, w );
00729 #endif
00730             maxWidth -= layoutStruct[pos].effMaxWidth;
00731             cMaxWidth -= w;
00732             layoutStruct[pos].effMaxWidth = w;
00733         }
00734         }
00735     } else {
00736         for ( unsigned int pos = col; pos < lastCol; pos++ )
00737         layoutStruct[pos].maxWidth = QMAX(layoutStruct[pos].maxWidth, layoutStruct[pos].minWidth );
00738     }
00739     }
00740     effWidthDirty = false;
00741 
00742 //     qDebug("calcEffectiveWidth: tMaxWidth=%d",  tMaxWidth );
00743     return tMaxWidth;
00744 }
00745 
00746 /* gets all cells that originate in a column and have a cellspan > 1
00747    Sorts them by increasing cellspan
00748 */
00749 void AutoTableLayout::insertSpanCell( RenderTableCell *cell )
00750 {
00751     if ( !cell || cell == (RenderTableCell *)-1 || cell->colSpan() == 1 )
00752     return;
00753 
00754 //     qDebug("inserting span cell %p with span %d", cell, cell->colSpan() );
00755     int size = spanCells.size();
00756     if ( !size || spanCells[size-1] != 0 ) {
00757     spanCells.resize( size + 10 );
00758     for ( int i = 0; i < 10; i++ )
00759         spanCells[size+i] = 0;
00760     size += 10;
00761     }
00762 
00763     // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better
00764     unsigned int pos = 0;
00765     int span = cell->colSpan();
00766     while ( pos < spanCells.size() && spanCells[pos] && span > spanCells[pos]->colSpan() )
00767     pos++;
00768     memmove( spanCells.data()+pos+1, spanCells.data()+pos, (size-pos-1)*sizeof( RenderTableCell * ) );
00769     spanCells[pos] = cell;
00770 }
00771 
00772 
00773 void AutoTableLayout::layout()
00774 {
00775     // table layout based on the values collected in the layout structure.
00776     int tableWidth = table->width() - table->bordersAndSpacing();
00777     int available = tableWidth;
00778     int nEffCols = table->numEffCols();
00779 
00780     if ( nEffCols != (int)layoutStruct.size() ) {
00781     qWarning("WARNING: nEffCols is not equal to layoutstruct!" );
00782     fullRecalc();
00783     nEffCols = table->numEffCols();
00784     }
00785 #ifdef DEBUG_LAYOUT
00786     qDebug("AutoTableLayout::layout()");
00787 #endif
00788 
00789     if ( effWidthDirty )
00790     calcEffectiveWidth();
00791 
00792 #ifdef DEBUG_LAYOUT
00793     qDebug("    tableWidth=%d,  nEffCols=%d", tableWidth,  nEffCols );
00794     for ( int i = 0; i < nEffCols; i++ ) {
00795     qDebug("    effcol %d is of type %d value %d, minWidth=%d, maxWidth=%d",
00796            i, layoutStruct[i].width.type, layoutStruct[i].width.value,
00797            layoutStruct[i].minWidth, layoutStruct[i].maxWidth );
00798     qDebug("        effective: type %d value %d, minWidth=%d, maxWidth=%d",
00799            layoutStruct[i].effWidth.type, layoutStruct[i].effWidth.value,
00800            layoutStruct[i].effMinWidth, layoutStruct[i].effMaxWidth );
00801     }
00802 #endif
00803 
00804     bool havePercent = false;
00805     bool haveRelative = false;
00806     int totalRelative = 0;
00807     int numVariable = 0;
00808     int numFixed = 0;
00809     int totalVariable = 0;
00810     int totalFixed = 0;
00811     int totalPercent = 0;
00812     int allocVariable = 0;
00813 
00814     // fill up every cell with it's minWidth
00815     for ( int i = 0; i < nEffCols; i++ ) {
00816     int w = layoutStruct[i].effMinWidth;
00817     layoutStruct[i].calcWidth = w;
00818     available -= w;
00819     Length& width = layoutStruct[i].effWidth;
00820         switch( width.type) {
00821         case Percent:
00822         havePercent = true;
00823         totalPercent += width.value;
00824             break;
00825         case Relative:
00826         haveRelative = true;
00827         totalRelative += width.value;
00828             break;
00829         case Fixed:
00830             numFixed++;
00831             totalFixed += layoutStruct[i].effMaxWidth;
00832             // fall through
00833             break;
00834         case Variable:
00835         case Static:
00836             numVariable++;
00837             totalVariable += layoutStruct[i].effMaxWidth;
00838             allocVariable += w;
00839         }
00840     }
00841 
00842     // then allocate width to fixed cols
00843     if ( available > 0 ) {
00844     for ( int i = 0; i < nEffCols; ++i ) {
00845         Length &width = layoutStruct[i].effWidth;
00846         if ( width.type == Fixed && width.value > layoutStruct[i].calcWidth ) {
00847         available += layoutStruct[i].calcWidth - width.value;
00848         layoutStruct[i].calcWidth = width.value;
00849             }
00850     }
00851     }
00852 #ifdef DEBUG_LAYOUT
00853     qDebug("fixed satisfied: available is %d", available);
00854 #endif
00855 
00856     // then allocate width to percent cols
00857     if ( available > 0 && havePercent ) {
00858     for ( int i = 0; i < nEffCols; i++ ) {
00859         Length &width = layoutStruct[i].effWidth;
00860         if ( width.type == Percent ) {
00861                 int w = kMax ( int( layoutStruct[i].effMinWidth ), width.minWidth( tableWidth ) );
00862         available += layoutStruct[i].calcWidth - w;
00863         layoutStruct[i].calcWidth = w;
00864         }
00865     }
00866     if ( totalPercent > 100 ) {
00867         // remove overallocated space from the last columns
00868         int excess = tableWidth*(totalPercent-100)/100;
00869         for ( int i = nEffCols-1; i >= 0; i-- ) {
00870         if ( layoutStruct[i].effWidth.type == Percent ) {
00871             int w = layoutStruct[i].calcWidth;
00872             int reduction = kMin( w,  excess );
00873             // the lines below might look inconsistent, but that's the way it's handled in mozilla
00874             excess -= reduction;
00875             int newWidth = kMax( int (layoutStruct[i].effMinWidth), w - reduction );
00876             available += w - newWidth;
00877             layoutStruct[i].calcWidth = newWidth;
00878             //qDebug("col %d: reducing to %d px (reduction=%d)", i, newWidth, reduction );
00879         }
00880         }
00881     }
00882     }
00883 #ifdef DEBUG_LAYOUT
00884     qDebug("percent satisfied: available is %d", available);
00885 #endif
00886 
00887     // now satisfy relative
00888     if ( available > 0 ) {
00889     for ( int i = 0; i < nEffCols; i++ ) {
00890         Length &width = layoutStruct[i].effWidth;
00891         if ( width.type == Relative && width.value != 0 ) {
00892         // width=0* gets effMinWidth.
00893         int w = width.value*tableWidth/totalRelative;
00894         available += layoutStruct[i].calcWidth - w;
00895         layoutStruct[i].calcWidth = w;
00896         }
00897     }
00898     }
00899 
00900     // now satisfy variable
00901     if ( available > 0 && numVariable ) {
00902     available += allocVariable; // this gets redistributed
00903     //qDebug("redistributing %dpx to %d variable columns. totalVariable=%d",  available,  numVariable,  totalVariable );
00904     for ( int i = 0; i < nEffCols; i++ ) {
00905         Length &width = layoutStruct[i].effWidth;
00906         if ( width.type == Variable && totalVariable != 0 ) {
00907         int w = kMax( int ( layoutStruct[i].calcWidth ),
00908                               available * layoutStruct[i].effMaxWidth / totalVariable );
00909         available -= w;
00910         totalVariable -= layoutStruct[i].effMaxWidth;
00911         layoutStruct[i].calcWidth = w;
00912         }
00913     }
00914     }
00915 #ifdef DEBUG_LAYOUT
00916     qDebug("variable satisfied: available is %d",  available );
00917 #endif
00918 
00919     // spread over fixed colums
00920     if ( available > 0 && numFixed) {
00921         // still have some width to spread, distribute to fixed columns
00922         for ( int i = 0; i < nEffCols; i++ ) {
00923             Length &width = layoutStruct[i].effWidth;
00924             if ( width.isFixed() ) {
00925                 int w = available * layoutStruct[i].effMaxWidth / totalFixed;
00926                 available -= w;
00927                 totalFixed -= layoutStruct[i].effMaxWidth;
00928                 layoutStruct[i].calcWidth += w;
00929             }
00930         }
00931     }
00932 
00933 #ifdef DEBUG_LAYOUT
00934     qDebug("after fixed distribution: available=%d",  available );
00935 #endif
00936 
00937     // spread over percent colums
00938     if ( available > 0 && hasPercent && totalPercent < 100) {
00939         // still have some width to spread, distribute weighted to percent columns
00940         for ( int i = 0; i < nEffCols; i++ ) {
00941             Length &width = layoutStruct[i].effWidth;
00942             if ( width.isPercent() ) {
00943                 int w = available * width.value / totalPercent;
00944                 available -= w;
00945                 totalPercent -= width.value;
00946                 layoutStruct[i].calcWidth += w;
00947                 if (!available || !totalPercent) break;
00948             }
00949         }
00950     }
00951 
00952 #ifdef DEBUG_LAYOUT
00953     qDebug("after percent distribution: available=%d",  available );
00954 #endif
00955 
00956     // spread over the rest
00957     if ( available > 0 ) {
00958         int total = nEffCols;
00959         // still have some width to spread
00960         int i = nEffCols;
00961         while (  i-- ) {
00962             int w = available / total;
00963             available -= w;
00964             total--;
00965             layoutStruct[i].calcWidth += w;
00966         }
00967     }
00968 
00969 #ifdef DEBUG_LAYOUT
00970     qDebug("after equal distribution: available=%d",  available );
00971 #endif
00972     // if we have overallocated, reduce every cell according to the difference between desired width and minwidth
00973     // this seems to produce to the pixel exaxt results with IE. Wonder is some of this also holds for width distributing.
00974     if ( available < 0 ) {
00975     int mw = 0;
00976     for ( int i = nEffCols-1; i >= 0; i-- )
00977         mw += layoutStruct[i].calcWidth - layoutStruct[i].effMinWidth;
00978     for ( int i = nEffCols-1; i >= 0 && mw > 0; i-- ) {
00979         int minMaxDiff = layoutStruct[i].calcWidth-layoutStruct[i].effMinWidth;
00980         int reduce = available * minMaxDiff / mw;
00981         layoutStruct[i].calcWidth += reduce;
00982         available -= reduce;
00983         mw -= minMaxDiff;
00984         if ( available >= 0 )
00985         break;
00986     }
00987     }
00988 
00989     //qDebug( "    final available=%d", available );
00990 
00991     int pos = 0;
00992     for ( int i = 0; i < nEffCols; i++ ) {
00993 #ifdef DEBUG_LAYOUT
00994     qDebug("col %d: %d (width %d)", i, pos, layoutStruct[i].calcWidth );
00995 #endif
00996     table->columnPos[i] = pos;
00997     pos += layoutStruct[i].calcWidth + table->cellSpacing();
00998     }
00999     table->columnPos[table->columnPos.size()-1] = pos;
01000 
01001 }
01002 
01003 
01004 void AutoTableLayout::calcPercentages() const
01005 {
01006     total_percent = 0;
01007     for ( unsigned int i = 0; i < layoutStruct.size(); i++ ) {
01008     if ( layoutStruct[i].width.type == Percent )
01009         total_percent += layoutStruct[i].width.value;
01010     }
01011     percentagesDirty = false;
01012 }
01013 
01014 #undef DEBUG_LAYOUT
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:22:43 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001