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.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