# khtml Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:34:45 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001