Fix for 3833123, setting a cell's colspan does not update rendering like it should.
[WebKit-https.git] / WebCore / khtml / rendering / render_table.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1997 Martin Jones (mjones@kde.org)
5  *           (C) 1997 Torben Weis (weis@kde.org)
6  *           (C) 1998 Waldo Bastian (bastian@kde.org)
7  *           (C) 1999 Lars Knoll (knoll@kde.org)
8  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
9  * Copyright (C) 2003 Apple Computer, Inc.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public License
22  * along with this library; see the file COPYING.LIB.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  */
26
27 //#define TABLE_DEBUG
28 //#define TABLE_PRINT
29 //#define DEBUG_LAYOUT
30 //#define BOX_DEBUG
31 #include "rendering/render_table.h"
32 #include "rendering/table_layout.h"
33 #include "html/html_tableimpl.h"
34 #include "misc/htmltags.h"
35 #include "misc/htmlattrs.h"
36 #include "xml/dom_docimpl.h"
37
38 #include <kglobal.h>
39
40 #include <qapplication.h>
41 #include <qstyle.h>
42
43 #include <kdebug.h>
44 #include <assert.h>
45
46 #include "khtmlview.h"
47
48 using namespace khtml;
49 using namespace DOM;
50
51 RenderTable::RenderTable(DOM::NodeImpl* node)
52     : RenderBlock(node)
53 {
54
55     tCaption = 0;
56     head = foot = firstBody = 0;
57     tableLayout = 0;
58     m_currentBorder = 0;
59     
60     rules = None;
61     frame = Void;
62     has_col_elems = false;
63     hspacing = 0;
64     vspacing = 0;
65     padding = 0;
66     needSectionRecalc = false;
67     padding = 0;
68
69     columnPos.resize( 2 );
70     columnPos.fill( 0 );
71     columns.resize( 1 );
72     columns.fill( ColumnStruct() );
73
74     columnPos[0] = 0;
75 }
76
77 RenderTable::~RenderTable()
78 {
79     delete tableLayout;
80 }
81
82 void RenderTable::setStyle(RenderStyle *_style)
83 {
84     ETableLayout oldTableLayout = style() ? style()->tableLayout() : TAUTO;
85     RenderBlock::setStyle(_style);
86
87     // In the collapsed border model, there is no cell spacing.
88     hspacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing();
89     vspacing = collapseBorders() ? 0 : style()->verticalBorderSpacing();
90     columnPos[0] = hspacing;
91
92     if ( !tableLayout || style()->tableLayout() != oldTableLayout ) {
93         delete tableLayout;
94
95         // According to the CSS2 spec, you only use fixed table layout if an
96         // explicit width is specified on the table.  Auto width implies auto table layout.
97         if (style()->tableLayout() == TFIXED && !style()->width().isVariable()) {
98             tableLayout = new FixedTableLayout(this);
99 #ifdef DEBUG_LAYOUT
100             kdDebug( 6040 ) << "using fixed table layout" << endl;
101 #endif
102         } else
103             tableLayout = new AutoTableLayout(this);
104     }
105 }
106
107 void RenderTable::addChild(RenderObject *child, RenderObject *beforeChild)
108 {
109 #ifdef DEBUG_LAYOUT
110     kdDebug( 6040 ) << renderName() << "(Table)::addChild( " << child->renderName() << ", " <<
111                        (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
112 #endif
113     RenderObject *o = child;
114
115     if (child->element() && child->element()->id() == ID_FORM) {
116         RenderContainer::addChild(child,beforeChild);
117         return;
118     }
119
120     switch(child->style()->display())
121     {
122     case TABLE_CAPTION:
123         tCaption = static_cast<RenderBlock *>(child);
124         break;
125     case TABLE_COLUMN:
126     case TABLE_COLUMN_GROUP:
127         has_col_elems = true;
128         break;
129     case TABLE_HEADER_GROUP:
130         if ( !head )
131             head = static_cast<RenderTableSection *>(child);
132         else if ( !firstBody )
133             firstBody = static_cast<RenderTableSection *>(child);
134         break;
135     case TABLE_FOOTER_GROUP:
136         if ( !foot ) {
137             foot = static_cast<RenderTableSection *>(child);
138             break;
139         }
140         // fall through
141     case TABLE_ROW_GROUP:
142         if(!firstBody)
143             firstBody = static_cast<RenderTableSection *>(child);
144         break;
145     default:
146         if ( !beforeChild && lastChild() &&
147              lastChild()->isTableSection() && lastChild()->isAnonymous() ) {
148             o = lastChild();
149         } else {
150             RenderObject *lastBox = beforeChild;
151             while ( lastBox && lastBox->parent()->isAnonymous() &&
152                     !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION )
153                 lastBox = lastBox->parent();
154             if ( lastBox && lastBox->isAnonymous() ) {
155                 lastBox->addChild( child, beforeChild );
156                 return;
157             } else {
158                 if ( beforeChild && !beforeChild->isTableSection() )
159                     beforeChild = 0;
160                 //kdDebug( 6040 ) << this <<" creating anonymous table section beforeChild="<< beforeChild << endl;
161                 o = new (renderArena()) RenderTableSection(document() /* anonymous */);
162                 RenderStyle *newStyle = new (renderArena()) RenderStyle();
163                 newStyle->inheritFrom(style());
164                 newStyle->setDisplay(TABLE_ROW_GROUP);
165                 o->setStyle(newStyle);
166                 addChild(o, beforeChild);
167             }
168         }
169         o->addChild(child);
170         child->setNeedsLayoutAndMinMaxRecalc();
171         return;
172     }
173     RenderContainer::addChild(child,beforeChild);
174 }
175
176
177
178 void RenderTable::calcWidth()
179 {
180     if ( isPositioned() ) {
181         calcAbsoluteHorizontal();
182     }
183
184     RenderBlock *cb = containingBlock();
185     int availableWidth = cb->contentWidth();
186
187     LengthType widthType = style()->width().type;
188     if (widthType > Relative && style()->width().value > 0) {
189         // Percent or fixed table
190         m_width = style()->width().minWidth( availableWidth );
191         if(m_minWidth > m_width) m_width = m_minWidth;
192     }
193     else {
194         // An auto width table should shrink to fit within the line width if necessary in order to 
195         // avoid overlapping floats.
196         availableWidth = cb->lineWidth( m_y );
197         
198         // Subtract out any fixed margins from our available width for auto width tables.
199         int marginTotal = 0;
200         if (style()->marginLeft().type != Variable)
201             marginTotal += style()->marginLeft().width(availableWidth);
202         if (style()->marginRight().type != Variable)
203             marginTotal += style()->marginRight().width(availableWidth);
204             
205         // Subtract out our margins to get the available content width.
206         int availContentWidth = kMax(0, availableWidth - marginTotal);
207         
208         // Ensure we aren't bigger than our max width or smaller than our min width.
209         m_width = kMin(availContentWidth, m_maxWidth);
210     }
211     
212     m_width = kMax(m_width, m_minWidth);
213
214     // Finally, with our true width determined, compute our margins for real.
215     m_marginRight = 0;
216     m_marginLeft = 0;
217     calcHorizontalMargins(style()->marginLeft(),style()->marginRight(),availableWidth);
218 }
219
220 void RenderTable::layout()
221 {
222     KHTMLAssert( needsLayout() );
223     KHTMLAssert( minMaxKnown() );
224     KHTMLAssert( !needSectionRecalc );
225
226     if (posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
227         // All we have to is lay out our positioned objects.
228         layoutPositionedObjects(true);
229         setNeedsLayout(false);
230         return;
231     }
232
233     QRect oldBounds, oldFullBounds;
234     bool checkForRepaint = checkForRepaintDuringLayout();
235     if (checkForRepaint)
236         getAbsoluteRepaintRectIncludingFloats(oldBounds, oldFullBounds);
237     
238     //kdDebug( 6040 ) << renderName() << "(Table)"<< this << " ::layout0() width=" << width() << ", needsLayout=" << needsLayout() << endl;
239     
240     m_height = 0;
241     initMaxMarginValues();
242     
243     //int oldWidth = m_width;
244     calcWidth();
245
246     // the optimisation below doesn't work since the internal table
247     // layout could have changed.  we need to add a flag to the table
248     // layout that tells us if something has changed in the min max
249     // calculations to do it correctly.
250 //     if ( oldWidth != m_width || columns.size() + 1 != columnPos.size() )
251     tableLayout->layout();
252
253 #ifdef DEBUG_LAYOUT
254     kdDebug( 6040 ) << renderName() << "(Table)::layout1() width=" << width() << ", marginLeft=" << marginLeft() << " marginRight=" << marginRight() << endl;
255 #endif
256
257     setCellWidths();
258
259     // layout child objects
260     int calculatedHeight = 0;
261
262     RenderObject *child = firstChild();
263     while( child ) {
264         if ( child->needsLayout() && !(child->element() && child->element()->id() == ID_FORM))
265             child->layout();
266         if ( child->isTableSection() ) {
267             static_cast<RenderTableSection *>(child)->calcRowHeight();
268             calculatedHeight += static_cast<RenderTableSection *>(child)->layoutRows( 0 );
269         }
270         child = child->nextSibling();
271     }
272
273     // ### collapse caption margin
274     if(tCaption && tCaption->style()->captionSide() != CAPBOTTOM) {
275         tCaption->setPos(tCaption->marginLeft(), m_height);
276         m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
277     }
278
279     int bpTop = borderTop() + (collapseBorders() ? 0 : paddingTop());
280     int bpBottom = borderBottom() + (collapseBorders() ? 0 : paddingBottom());
281     
282     m_height += bpTop;
283
284     int oldHeight = m_height;
285     calcHeight();
286     int newHeight = m_height;
287     m_height = oldHeight;
288
289     Length h = style()->height();
290     int th = 0;
291     if (isPositioned())
292         th = newHeight; // FIXME: Leave this alone for now but investigate later.
293     else if (h.isFixed())
294         th = h.value - (bpTop + bpBottom);  // Tables size as though CSS height includes border/padding.
295     else if (h.isPercent())
296         th = calcPercentageHeight(h);
297     th = kMax(0, th);
298
299     // layout rows
300     if ( th > calculatedHeight ) {
301         // we have to redistribute that height to get the constraint correctly
302         // just force the first body to the height needed
303         // ### FIXME This should take height constraints on all table sections into account and distribute
304         // accordingly. For now this should be good enough
305         if (firstBody) {
306             firstBody->calcRowHeight();
307             firstBody->layoutRows( th - calculatedHeight );
308         }
309     }
310     
311     int bl = borderLeft();
312     if (!collapseBorders())
313         bl += paddingLeft();
314
315     // position the table sections
316     if ( head ) {
317         head->setPos(bl, m_height);
318         m_height += head->height();
319     }
320     RenderObject *body = firstBody;
321     while ( body ) {
322         if ( body != head && body != foot && body->isTableSection() ) {
323             body->setPos(bl, m_height);
324             m_height += body->height();
325         }
326         body = body->nextSibling();
327     }
328     if ( foot ) {
329         foot->setPos(bl, m_height);
330         m_height += foot->height();
331     }
332
333     m_height += bpBottom;
334                
335     if(tCaption && tCaption->style()->captionSide()==CAPBOTTOM) {
336         tCaption->setPos(tCaption->marginLeft(), m_height);
337         m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
338     }
339
340     //kdDebug(0) << "table height: " << m_height << endl;
341
342     // table can be containing block of positioned elements.
343     // ### only pass true if width or height changed.
344     layoutPositionedObjects( true );
345
346     // Repaint with our new bounds if they are different from our old bounds.
347     if (checkForRepaint)
348         repaintAfterLayoutIfNeeded(oldBounds, oldFullBounds);
349     
350     m_overflowHeight = kMax(m_overflowHeight, m_height);
351     m_overflowWidth = kMax(m_overflowWidth, m_width);
352
353     setNeedsLayout(false);
354 }
355
356 void RenderTable::setCellWidths()
357 {
358 #ifdef DEBUG_LAYOUT
359     kdDebug( 6040 ) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()" << endl;
360 #endif
361
362     RenderObject *child = firstChild();
363     while( child ) {
364         if ( child->isTableSection() )
365             static_cast<RenderTableSection *>(child)->setCellWidths();
366         child = child->nextSibling();
367     }
368 }
369
370 void RenderTable::paint(PaintInfo& i, int _tx, int _ty)
371 {
372     _tx += xPos();
373     _ty += yPos();
374
375     PaintAction paintAction = i.phase;
376     
377 #ifdef TABLE_PRINT
378     kdDebug( 6040 ) << "RenderTable::paint() w/h = (" << width() << "/" << height() << ")" << endl;
379 #endif
380     
381     int os = 2*maximalOutlineSize(paintAction);
382     if ((_ty >= i.r.y() + i.r.height() + os) || (_ty + height() <= i.r.y() - os)) return;
383     if ((_tx >= i.r.x() + i.r.width() + os) || (_tx + width() <= i.r.x() - os)) return;
384
385 #ifdef TABLE_PRINT
386     kdDebug( 6040 ) << "RenderTable::paint(2) " << _tx << "/" << _ty << " (" << _y << "/" << _h << ")" << endl;
387 #endif
388
389     if ((paintAction == PaintActionBlockBackground || paintAction == PaintActionChildBlockBackground)
390         && shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE)
391         paintBoxDecorations(i, _tx, _ty);
392
393     // We're done.  We don't bother painting any children.
394     if (paintAction == PaintActionBlockBackground)
395         return;
396     // We don't paint our own background, but we do let the kids paint their backgrounds.
397     if (paintAction == PaintActionChildBlockBackgrounds)
398         paintAction = PaintActionChildBlockBackground;
399     PaintInfo paintInfo(i.p, i.r, paintAction, paintingRootForChildren(i));
400     
401     for (RenderObject *child = firstChild(); child; child = child->nextSibling())
402         if (child->isTableSection() || child == tCaption)
403             child->paint(paintInfo, _tx, _ty);
404
405     if (collapseBorders() && paintAction == PaintActionChildBlockBackground && style()->visibility() == VISIBLE) {
406         // Collect all the unique border styles that we want to paint in a sorted list.  Once we
407         // have all the styles sorted, we then do individual passes, painting each style of border
408         // from lowest precedence to highest precedence.
409         paintInfo.phase = PaintActionCollapsedTableBorders;
410         QValueList<CollapsedBorderValue> borderStyles;
411         collectBorders(borderStyles);
412         QValueListIterator<CollapsedBorderValue> it = borderStyles.begin();
413         QValueListIterator<CollapsedBorderValue> end = borderStyles.end();
414         for (; it != end; ++it) {
415             m_currentBorder = &(*it);
416             for (RenderObject* child = firstChild(); child; child = child->nextSibling())
417                 if (child->isTableSection())
418                     child->paint(paintInfo, _tx, _ty);
419         }
420     }
421         
422 #ifdef BOX_DEBUG
423     outlineBox(i.p, _tx, _ty, "blue");
424 #endif
425 }
426
427 void RenderTable::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
428 {
429     int w = width();
430     int h = height();
431     
432     // Account for the caption.
433     if (tCaption) {
434         int captionHeight = (tCaption->height() + tCaption->marginBottom() +  tCaption->marginTop());
435         h -= captionHeight;
436         if (tCaption->style()->captionSide() != CAPBOTTOM)
437             _ty += captionHeight;
438     }
439
440     int my = kMax(_ty, i.r.y());
441     int mh;
442     if (_ty < i.r.y())
443         mh= kMax(0, h - (i.r.y() - _ty));
444     else
445         mh = kMin(i.r.height(), h);
446     
447     paintBackground(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
448     
449     if (style()->hasBorder() && !collapseBorders())
450         paintBorder(i.p, _tx, _ty, w, h, style());
451 }
452
453 void RenderTable::calcMinMaxWidth()
454 {
455     KHTMLAssert( !minMaxKnown() );
456
457     if ( needSectionRecalc )
458         recalcSections();
459
460 #ifdef DEBUG_LAYOUT
461     kdDebug( 6040 ) << renderName() << "(Table " << this << ")::calcMinMaxWidth()" <<  endl;
462 #endif
463
464     tableLayout->calcMinMaxWidth();
465
466     if (tCaption && tCaption->minWidth() > m_minWidth)
467         m_minWidth = tCaption->minWidth();
468
469     setMinMaxKnown();
470 #ifdef DEBUG_LAYOUT
471     kdDebug( 6040 ) << renderName() << " END: (Table " << this << ")::calcMinMaxWidth() min = " << m_minWidth << " max = " << m_maxWidth <<  endl;
472 #endif
473 }
474
475 void RenderTable::splitColumn( int pos, int firstSpan )
476 {
477     // we need to add a new columnStruct
478     int oldSize = columns.size();
479     columns.resize( oldSize + 1 );
480     int oldSpan = columns[pos].span;
481 //     qDebug("splitColumn( %d,%d ), oldSize=%d, oldSpan=%d", pos, firstSpan, oldSize, oldSpan );
482     KHTMLAssert( oldSpan > firstSpan );
483     columns[pos].span = firstSpan;
484     memmove( columns.data()+pos+1, columns.data()+pos, (oldSize-pos)*sizeof(ColumnStruct) );
485     columns[pos+1].span = oldSpan - firstSpan;
486
487     // change width of all rows.
488     RenderObject *child = firstChild();
489     while ( child ) {
490         if ( child->isTableSection() ) {
491             RenderTableSection *section = static_cast<RenderTableSection *>(child);
492             int size = section->numRows();
493             int row = 0;
494             if ( section->cCol > pos )
495                 section->cCol++;
496             while ( row < size ) {
497                 section->grid[row].row->resize( oldSize+1 );
498                 RenderTableSection::Row &r = *section->grid[row].row;
499                 memmove( r.data()+pos+1, r.data()+pos, (oldSize-pos)*sizeof( RenderTableCell * ) );
500 //              qDebug("moving from %d to %d, num=%d", pos, pos+1, (oldSize-pos-1) );
501                 r[pos+1] = r[pos] ? (RenderTableCell *)-1 : 0;
502                 row++;
503             }
504         }
505         child = child->nextSibling();
506     }
507     columnPos.resize( numEffCols()+1 );
508     setNeedsLayoutAndMinMaxRecalc();
509 }
510
511 void RenderTable::appendColumn( int span )
512 {
513     // easy case.
514     int pos = columns.size();
515 //     qDebug("appendColumn( %d ), size=%d", span, pos );
516     int newSize = pos + 1;
517     columns.resize( newSize );
518     columns[pos].span = span;
519     //qDebug("appending column at %d, span %d", pos,  span );
520
521     // change width of all rows.
522     RenderObject *child = firstChild();
523     while ( child ) {
524         if ( child->isTableSection() ) {
525             RenderTableSection *section = static_cast<RenderTableSection *>(child);
526             int size = section->numRows();
527             int row = 0;
528             while ( row < size ) {
529                 section->grid[row].row->resize( newSize );
530                 section->cellAt( row, pos ) = 0;
531                 row++;
532             }
533
534         }
535         child = child->nextSibling();
536     }
537     columnPos.resize( numEffCols()+1 );
538     setNeedsLayoutAndMinMaxRecalc();
539 }
540
541 RenderTableCol *RenderTable::colElement( int col ) {
542     if ( !has_col_elems )
543         return 0;
544     RenderObject *child = firstChild();
545     int cCol = 0;
546     while ( child ) {
547         if ( child->isTableCol() ) {
548             RenderTableCol *colElem = static_cast<RenderTableCol *>(child);
549             int span = colElem->span();
550             if ( !colElem->firstChild() ) {
551                 cCol += span;
552                 if ( cCol > col )
553                     return colElem;
554             }
555
556             RenderObject *next = child->firstChild();
557             if ( !next )
558                 next = child->nextSibling();
559             if ( !next && child->parent()->isTableCol() )
560                 next = child->parent()->nextSibling();
561             child = next;
562         } else
563             break;
564     }
565     return 0;
566 }
567
568 void RenderTable::recalcSections()
569 {
570     tCaption = 0;
571     head = foot = firstBody = 0;
572     has_col_elems = false;
573
574     RenderObject *child = firstChild();
575     // We need to get valid pointers to caption, head, foot and firstbody again
576     while (child) {
577         switch (child->style()->display()) {
578         case TABLE_CAPTION:
579             if (!tCaption) {
580                 tCaption = static_cast<RenderBlock*>(child);
581                 tCaption->setNeedsLayout(true);
582             }
583             break;
584         case TABLE_COLUMN:
585         case TABLE_COLUMN_GROUP:
586             has_col_elems = true;
587             break;
588         case TABLE_HEADER_GROUP: {
589             RenderTableSection *section = static_cast<RenderTableSection *>(child);
590             if ( !head )
591                 head = section;
592             else if ( !firstBody )
593                 firstBody = section;
594             if ( section->needCellRecalc )
595                 section->recalcCells();
596             break;
597         }
598         case TABLE_FOOTER_GROUP: {
599             RenderTableSection *section = static_cast<RenderTableSection *>(child);
600             if ( !foot )
601                 foot = section;
602             else if ( !firstBody )
603                 firstBody = section;
604             if ( section->needCellRecalc )
605                 section->recalcCells();
606             break;
607         }
608         case TABLE_ROW_GROUP: {
609             RenderTableSection *section = static_cast<RenderTableSection *>(child);
610             if ( !firstBody )
611                 firstBody = section;
612             if ( section->needCellRecalc )
613                 section->recalcCells();
614         }
615         default:
616             break;
617         }
618         child = child->nextSibling();
619     }
620     needSectionRecalc = false;
621     setNeedsLayout(true);
622 }
623
624 RenderObject* RenderTable::removeChildNode(RenderObject* child)
625 {
626     setNeedSectionRecalc();
627     return RenderContainer::removeChildNode( child );
628 }
629
630 int RenderTable::borderLeft() const
631 {
632     if (collapseBorders()) {
633         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
634         // but I'm working to get this changed.  For now, follow the spec.
635         return 0;
636     }
637     return RenderBlock::borderLeft();
638 }
639     
640 int RenderTable::borderRight() const
641 {
642     if (collapseBorders()) {
643         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
644         // but I'm working to get this changed.  For now, follow the spec.
645         return 0;
646     }
647     return RenderBlock::borderRight();
648 }
649
650 int RenderTable::borderTop() const
651 {
652     if (collapseBorders()) {
653         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
654         // but I'm working to get this changed.  For now, follow the spec.
655         return 0;
656     }
657     return RenderBlock::borderTop();
658 }
659
660 int RenderTable::borderBottom() const
661 {
662     if (collapseBorders()) {
663         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
664         // but I'm working to get this changed.  For now, follow the spec.
665         return 0;
666     }
667     return RenderBlock::borderBottom();
668 }
669
670 RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
671 {
672     // Find the section and row to look in
673     int r = cell->row();
674     RenderTableSection* section = 0;
675     int rAbove = -1;
676     if (r > 0) {
677         // cell is not in the first row, so use the above row in its own section
678         section = cell->section();
679         rAbove = r-1;
680     } else {
681         // cell is at top of a section, use last row in previous section
682         for (RenderObject *prevSection = cell->section()->previousSibling();
683              prevSection && rAbove < 0;
684              prevSection = prevSection->previousSibling()) {
685             if (prevSection->isTableSection()) {
686                 section = static_cast<RenderTableSection *>(prevSection);
687                 if (section->numRows() > 0)
688                     rAbove = section->numRows()-1;
689             }
690         }
691     }
692
693     // Look up the cell in the section's grid, which requires effective col index
694     if (section && rAbove >= 0) {
695         int effCol = colToEffCol(cell->col());
696         RenderTableCell* aboveCell;
697         // If we hit a span back up to a real cell.
698         do {
699             aboveCell = section->cellAt(rAbove, effCol);
700             effCol--;
701         } while (aboveCell == (RenderTableCell *)-1 && effCol >=0);
702         return (aboveCell == (RenderTableCell *)-1) ? 0 : aboveCell;
703     } else {
704         return 0;
705     }
706 }
707
708 RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
709 {
710     // Find the section and row to look in
711     int r = cell->row() + cell->rowSpan() - 1;
712     RenderTableSection* section = 0;
713     int rBelow = -1;
714     if (r < cell->section()->numRows()-1) {
715         // The cell is not in the last row, so use the next row in the section.
716         section = cell->section();
717         rBelow= r+1;
718     } else {
719         // The cell is at the bottom of a section. Use the first row in the next section.
720         for (RenderObject* nextSection = cell->section()->nextSibling();
721              nextSection && rBelow < 0;
722              nextSection = nextSection->nextSibling()) 
723         {
724             if (nextSection->isTableSection()) {
725                 section = static_cast<RenderTableSection *>(nextSection);
726                 if (section->numRows() > 0)
727                     rBelow = 0;
728             }
729         }
730     }
731     
732     // Look up the cell in the section's grid, which requires effective col index
733     if (section && rBelow >= 0) {
734         int effCol = colToEffCol(cell->col());
735         RenderTableCell* belowCell;
736         // If we hit a colspan back up to a real cell.
737         do {
738             belowCell = section->cellAt(rBelow, effCol);
739             effCol--;
740         } while (belowCell == (RenderTableCell *)-1 && effCol >=0);
741         return (belowCell == (RenderTableCell *)-1) ? 0 : belowCell;
742     } else {
743         return 0;
744     }    
745 }
746
747 RenderTableCell* RenderTable::cellLeft(const RenderTableCell* cell) const
748 {
749     RenderTableSection* section = cell->section();
750     int effCol = colToEffCol(cell->col());
751     if (effCol == 0)
752         return 0;
753     
754     // If we hit a colspan back up to a real cell.
755     RenderTableCell* prevCell;
756     do {
757         prevCell = section->cellAt(cell->row(), effCol-1);
758         effCol--;
759     } while (prevCell == (RenderTableCell *)-1 && effCol >=0);
760     return (prevCell == (RenderTableCell *)-1) ? 0 : prevCell;
761 }
762
763 RenderTableCell* RenderTable::cellRight(const RenderTableCell* cell) const
764 {
765     int effCol = colToEffCol(cell->col()+cell->colSpan());
766     if (effCol >= numEffCols())
767         return 0;
768     RenderTableCell* result = cell->section()->cellAt(cell->row(), effCol);
769     return (result == (RenderTableCell*)-1) ? 0 : result;
770 }
771
772 RenderBlock* RenderTable::firstLineBlock() const
773 {
774     return 0;
775 }
776
777 void RenderTable::updateFirstLetter()
778 {}
779
780 #ifndef NDEBUG
781 void RenderTable::dump(QTextStream *stream, QString ind) const
782 {
783     if (tCaption)
784         *stream << " tCaption";
785     if (head)
786         *stream << " head";
787     if (foot)
788         *stream << " foot";
789
790     *stream << endl << ind << "cspans:";
791     for ( unsigned int i = 0; i < columns.size(); i++ )
792         *stream << " " << columns[i].span;
793     *stream << endl << ind;
794
795     RenderBlock::dump(stream,ind);
796 }
797 #endif
798
799 // --------------------------------------------------------------------------
800
801 RenderTableSection::RenderTableSection(DOM::NodeImpl* node)
802     : RenderContainer(node)
803 {
804     // init RenderObject attributes
805     setInline(false);   // our object is not Inline
806     gridRows = 0;
807     cCol = 0;
808     cRow = -1;
809     needCellRecalc = false;
810 }
811
812 RenderTableSection::~RenderTableSection()
813 {
814     clearGrid();
815 }
816
817 void RenderTableSection::detach()
818 {
819     // recalc cell info because RenderTable has unguarded pointers
820     // stored that point to this RenderTableSection.
821     if (table())
822         table()->setNeedSectionRecalc();
823
824     RenderContainer::detach();
825 }
826
827 void RenderTableSection::setStyle(RenderStyle* _style)
828 {
829     // we don't allow changing this one
830     if (style())
831         _style->setDisplay(style()->display());
832     else if (_style->display() != TABLE_FOOTER_GROUP && _style->display() != TABLE_HEADER_GROUP)
833         _style->setDisplay(TABLE_ROW_GROUP);
834
835     RenderContainer::setStyle(_style);
836 }
837
838 void RenderTableSection::addChild(RenderObject *child, RenderObject *beforeChild)
839 {
840 #ifdef DEBUG_LAYOUT
841     kdDebug( 6040 ) << renderName() << "(TableSection)::addChild( " << child->renderName()  << ", beforeChild=" <<
842                        (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
843 #endif
844     RenderObject *row = child;
845
846     if (child->element() && child->element()->id() == ID_FORM) {
847         RenderContainer::addChild(child,beforeChild);
848         return;
849     }
850     
851     if ( !child->isTableRow() ) {
852
853         if( !beforeChild )
854             beforeChild = lastChild();
855
856         if( beforeChild && beforeChild->isAnonymous() )
857             row = beforeChild;
858         else {
859             RenderObject *lastBox = beforeChild;
860             while ( lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow() )
861                 lastBox = lastBox->parent();
862             if ( lastBox && lastBox->isAnonymous() ) {
863                 lastBox->addChild( child, beforeChild );
864                 return;
865             } else {
866                 //kdDebug( 6040 ) << "creating anonymous table row" << endl;
867                 row = new (renderArena()) RenderTableRow(document() /* anonymous table */);
868                 RenderStyle *newStyle = new (renderArena()) RenderStyle();
869                 newStyle->inheritFrom(style());
870                 newStyle->setDisplay( TABLE_ROW );
871                 row->setStyle(newStyle);
872                 addChild(row, beforeChild);
873             }
874         }
875         row->addChild(child);
876         child->setNeedsLayoutAndMinMaxRecalc();
877         return;
878     }
879
880     if (beforeChild)
881         setNeedCellRecalc();
882
883     cRow++;
884     cCol = 0;
885
886     ensureRows( cRow+1 );
887
888     if (!beforeChild) {
889         grid[cRow].height = child->style()->height();
890         if ( grid[cRow].height.type == Relative )
891             grid[cRow].height = Length();
892     }
893
894
895     RenderContainer::addChild(child,beforeChild);
896 }
897
898 void RenderTableSection::ensureRows(int numRows)
899 {
900     int nRows = gridRows;
901     if (numRows > nRows) {
902         if (numRows > static_cast<int>(grid.size()))
903             grid.resize(numRows*2+1);
904
905         gridRows = numRows;
906         int nCols = table()->numEffCols();
907         for (int r = nRows; r < numRows; r++ ) {
908             grid[r].row = new Row(nCols);
909             grid[r].row->fill(0);
910             grid[r].baseLine = 0;
911             grid[r].height = Length();
912         }
913     }
914
915 }
916
917 void RenderTableSection::addCell( RenderTableCell *cell )
918 {
919     int rSpan = cell->rowSpan();
920     int cSpan = cell->colSpan();
921     QMemArray<RenderTable::ColumnStruct> &columns = table()->columns;
922     int nCols = columns.size();
923
924     // ### mozilla still seems to do the old HTML way, even for strict DTD
925     // (see the annotation on table cell layouting in the CSS specs and the testcase below:
926     // <TABLE border>
927     // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
928     // <TR><TD colspan="2">5
929     // </TABLE>
930 #if 0
931     // find empty space for the cell
932     bool found = false;
933     while ( !found ) {
934         found = true;
935         while ( cCol < nCols && cellAt( cRow, cCol ) )
936             cCol++;
937         int pos = cCol;
938         int span = 0;
939         while ( pos < nCols && span < cSpan ) {
940             if ( cellAt( cRow, pos ) ) {
941                 found = false;
942                 cCol = pos;
943                 break;
944             }
945             span += columns[pos].span;
946             pos++;
947         }
948     }
949 #else
950     while ( cCol < nCols && cellAt( cRow, cCol ) )
951         cCol++;
952 #endif
953
954 //       qDebug("adding cell at %d/%d span=(%d/%d)",  cRow, cCol, rSpan, cSpan );
955
956     if ( rSpan == 1 ) {
957         // we ignore height settings on rowspan cells
958         Length height = cell->style()->height();
959         if ( height.value > 0 || (height.type == Relative && height.value >= 0) ) {
960             Length cRowHeight = grid[cRow].height;
961             switch( height.type ) {
962             case Percent:
963                 if ( !(cRowHeight.type == Percent) ||
964                      ( cRowHeight.type == Percent && cRowHeight.value < height.value ) )
965                     grid[cRow].height = height;
966                      break;
967             case Fixed:
968                 if ( cRowHeight.type < Percent ||
969                      ( cRowHeight.type == Fixed && cRowHeight.value < height.value ) )
970                     grid[cRow].height = height;
971                 break;
972             case Relative:
973 #if 0
974                 // we treat this as variable. This is correct according to HTML4, as it only specifies length for the height.
975                 if ( cRowHeight.type == Variable ||
976                      ( cRowHeight.type == Relative && cRowHeight.value < height.value ) )
977                      grid[cRow].height = height;
978                      break;
979 #endif
980             default:
981                 break;
982             }
983         }
984     }
985
986     // make sure we have enough rows
987     ensureRows( cRow + rSpan );
988
989     int col = cCol;
990     // tell the cell where it is
991     RenderTableCell *set = cell;
992     while ( cSpan ) {
993         int currentSpan;
994         if ( cCol >= nCols ) {
995             table()->appendColumn( cSpan );
996             currentSpan = cSpan;
997         } else {
998             if ( cSpan < columns[cCol].span )
999                 table()->splitColumn( cCol, cSpan );
1000             currentSpan = columns[cCol].span;
1001         }
1002         int r = 0;
1003         while ( r < rSpan ) {
1004             if ( !cellAt( cRow + r, cCol ) ) {
1005 //              qDebug("    adding cell at %d, %d",  cRow + r, cCol );
1006                 cellAt( cRow + r, cCol ) = set;
1007             }
1008             r++;
1009         }
1010         cCol++;
1011         cSpan -= currentSpan;
1012         set = (RenderTableCell *)-1;
1013     }
1014     if ( cell ) {
1015         cell->setRow( cRow );
1016         cell->setCol( table()->effColToCol( col ) );
1017     }
1018 }
1019
1020
1021
1022 void RenderTableSection::setCellWidths()
1023 {
1024 #ifdef DEBUG_LAYOUT
1025     kdDebug( 6040 ) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()" << endl;
1026 #endif
1027     QMemArray<int> &columnPos = table()->columnPos;
1028
1029     int rows = gridRows;
1030     for ( int i = 0; i < rows; i++ ) {
1031         Row &row = *grid[i].row;
1032         int cols = row.size();
1033         for ( int j = 0; j < cols; j++ ) {
1034             RenderTableCell *cell = row[j];
1035 //          qDebug("cell[%d,%d] = %p", i, j, cell );
1036             if ( !cell || cell == (RenderTableCell *)-1 )
1037                 continue;
1038             int endCol = j;
1039             int cspan = cell->colSpan();
1040             while ( cspan && endCol < cols ) {
1041                 cspan -= table()->columns[endCol].span;
1042                 endCol++;
1043             }
1044             int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing();
1045 #ifdef DEBUG_LAYOUT
1046             kdDebug( 6040 ) << "setting width of cell " << cell << " " << cell->row() << "/" << cell->col() << " to " << w << " colspan=" << cell->colSpan() << " start=" << j << " end=" << endCol << endl;
1047 #endif
1048             int oldWidth = cell->width();
1049             if ( w != oldWidth ) {
1050                 cell->setNeedsLayout(true);
1051                 cell->setWidth( w );
1052             }
1053         }
1054     }
1055 }
1056
1057
1058 void RenderTableSection::calcRowHeight()
1059 {
1060     int indx;
1061     RenderTableCell *cell;
1062
1063     int totalRows = gridRows;
1064     int spacing = table()->vBorderSpacing();
1065
1066     rowPos.resize( totalRows + 1 );
1067     rowPos[0] = spacing;
1068
1069     for ( int r = 0; r < totalRows; r++ ) {
1070         rowPos[r+1] = 0;
1071
1072         int baseline=0;
1073         int bdesc = 0;
1074 //      qDebug("height of row %d is %d/%d", r, grid[r].height.value, grid[r].height.type );
1075         int ch = grid[r].height.minWidth( 0 );
1076         int pos = rowPos[ r+1 ] + ch + spacing;
1077
1078         if ( pos > rowPos[r+1] )
1079             rowPos[r+1] = pos;
1080
1081         Row *row = grid[r].row;
1082         int totalCols = row->size();
1083         int totalRows = gridRows;
1084
1085         for ( int c = 0; c < totalCols; c++ ) {
1086             cell = cellAt(r, c);
1087             if ( !cell || cell == (RenderTableCell *)-1 )
1088                 continue;
1089             if ( r < totalRows - 1 && cellAt(r+1, c) == cell )
1090                 continue;
1091
1092             if ( ( indx = r - cell->rowSpan() + 1 ) < 0 )
1093                 indx = 0;
1094
1095             if (cell->overrideSize() != -1) {
1096                 cell->setOverrideSize(-1);
1097                 cell->setChildNeedsLayout(true, false);
1098                 cell->layoutIfNeeded();
1099             }
1100             
1101             // Explicit heights use the border box in quirks mode.  In strict mode do the right
1102             // thing and actually add in the border and padding.
1103             ch = cell->style()->height().width(0) + 
1104                 (cell->style()->htmlHacks() ? 0 : (cell->paddingTop() + cell->paddingBottom() +
1105                                                    cell->borderTop() + cell->borderBottom()));
1106             if (cell->height() > ch)
1107                 ch = cell->height();
1108
1109             pos = rowPos[ indx ] + ch + spacing;
1110
1111             if ( pos > rowPos[r+1] )
1112                 rowPos[r+1] = pos;
1113
1114             // find out the baseline
1115             EVerticalAlign va = cell->style()->verticalAlign();
1116             if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP
1117                 || va == SUPER || va == SUB)
1118             {
1119                 int b=cell->baselinePosition();
1120
1121                 if (b>baseline)
1122                     baseline=b;
1123
1124                 int td = rowPos[ indx ] + ch - b;
1125                 if (td>bdesc)
1126                     bdesc = td;
1127             }
1128         }
1129
1130         //do we have baseline aligned elements?
1131         if (baseline) {
1132             // increase rowheight if baseline requires
1133             int bRowPos = baseline + bdesc  + spacing ; // + 2*padding
1134             if (rowPos[r+1]<bRowPos)
1135                 rowPos[r+1]=bRowPos;
1136
1137             grid[r].baseLine = baseline;
1138         }
1139
1140         if ( rowPos[r+1] < rowPos[r] )
1141             rowPos[r+1] = rowPos[r];
1142 //      qDebug("rowpos(%d)=%d",  r, rowPos[r] );
1143     }
1144 }
1145
1146 int RenderTableSection::layoutRows( int toAdd )
1147 {
1148     int rHeight;
1149     int rindx;
1150     int totalRows = gridRows;
1151     int hspacing = table()->hBorderSpacing();
1152     int vspacing = table()->vBorderSpacing();
1153     
1154     if (toAdd && totalRows && (rowPos[totalRows] || !nextSibling())) {
1155
1156         int totalHeight = rowPos[totalRows] + toAdd;
1157 //      qDebug("layoutRows: totalHeight = %d",  totalHeight );
1158
1159         int dh = toAdd;
1160         int totalPercent = 0;
1161         int numVariable = 0;
1162         for ( int r = 0; r < totalRows; r++ ) {
1163             if ( grid[r].height.type == Variable )
1164                 numVariable++;
1165             else if ( grid[r].height.type == Percent )
1166                 totalPercent += grid[r].height.value;
1167         }
1168         if ( totalPercent ) {
1169 //          qDebug("distributing %d over percent rows totalPercent=%d", dh,  totalPercent );
1170             // try to satisfy percent
1171             int add = 0;
1172             if ( totalPercent > 100 )
1173                 totalPercent = 100;
1174             int rh = rowPos[1]-rowPos[0];
1175             for ( int r = 0; r < totalRows; r++ ) {
1176                 if ( totalPercent > 0 && grid[r].height.type == Percent ) {
1177                     int toAdd = kMin(dh, (totalHeight * grid[r].height.value / 100)-rh);
1178                     // If toAdd is negative, then we don't want to shrink the row (this bug
1179                     // affected Outlook Web Access).
1180                     toAdd = QMAX(0, toAdd);
1181                     add += toAdd;
1182                     dh -= toAdd;
1183                     totalPercent -= grid[r].height.value;
1184 //                  qDebug( "adding %d to row %d", toAdd, r );
1185                 }
1186                 if ( r < totalRows-1 )
1187                     rh = rowPos[r+2] - rowPos[r+1];
1188                 rowPos[r+1] += add;
1189             }
1190         }
1191         if ( numVariable ) {
1192             // distribute over variable cols
1193 //          qDebug("distributing %d over variable rows numVariable=%d", dh,  numVariable );
1194             int add = 0;
1195             for ( int r = 0; r < totalRows; r++ ) {
1196                 if ( numVariable > 0 && grid[r].height.type == Variable ) {
1197                     int toAdd = dh/numVariable;
1198                     add += toAdd;
1199                     dh -= toAdd;
1200                     numVariable--;
1201                 }
1202                 rowPos[r+1] += add;
1203             }
1204         }
1205         if (dh>0 && rowPos[totalRows]) {
1206             // if some left overs, distribute equally.
1207             int tot=rowPos[totalRows];
1208             int add=0;
1209             int prev=rowPos[0];
1210             for ( int r = 0; r < totalRows; r++ ) {
1211                 //weight with the original height
1212                 add+=dh*(rowPos[r+1]-prev)/tot;
1213                 prev=rowPos[r+1];
1214                 rowPos[r+1]+=add;
1215             }
1216         }
1217     }
1218
1219     int leftOffset = hspacing;
1220
1221     int nEffCols = table()->numEffCols();
1222     for ( int r = 0; r < totalRows; r++ )
1223     {
1224         Row *row = grid[r].row;
1225         int totalCols = row->size();
1226         for ( int c = 0; c < nEffCols; c++ )
1227         {
1228             RenderTableCell *cell = cellAt(r, c);
1229             if (!cell || cell == (RenderTableCell *)-1 )
1230                 continue;
1231             if ( r < totalRows - 1 && cell == cellAt(r+1, c) )
1232                 continue;
1233
1234             if ( ( rindx = r-cell->rowSpan()+1 ) < 0 )
1235                 rindx = 0;
1236
1237             rHeight = rowPos[r+1] - rowPos[rindx] - vspacing;
1238             
1239             // Force percent height children to lay themselves out again.
1240             // This will cause these children to grow to fill the cell.
1241             // FIXME: There is still more work to do here to fully match WinIE (should
1242             // it become necessary to do so).  In quirks mode, WinIE behaves like we
1243             // do, but it will clip the cells that spill out of the table section.  In
1244             // strict mode, Mozilla and WinIE both regrow the table to accommodate the
1245             // new height of the cell (thus letting the percentages cause growth one
1246             // time only).  We may also not be handling row-spanning cells correctly.
1247             //
1248             // Note also the oddity where replaced elements always flex, and yet blocks/tables do
1249             // not necessarily flex.  WinIE is crazy and inconsistent, and we can't hope to
1250             // match the behavior perfectly, but we'll continue to refine it as we discover new
1251             // bugs. :)
1252             bool cellChildrenFlex = false;
1253             bool flexAllChildren = cell->style()->height().isFixed() || 
1254                 (!table()->style()->height().isVariable() && rHeight != cell->height());
1255             RenderObject* o = cell->firstChild();
1256             while (o) {
1257                 if (!o->isText() && o->style()->height().isPercent() && (o->isReplaced() || flexAllChildren)) {
1258                     // Tables with no sections do not flex.
1259                     if (!o->isTable() || static_cast<RenderTable*>(o)->hasSections()) {
1260                         o->setNeedsLayout(true, false);
1261                         cell->setChildNeedsLayout(true, false);
1262                         cellChildrenFlex = true;
1263                     }
1264                 }
1265                 o = o->nextSibling();
1266             }
1267             if (cellChildrenFlex) {
1268                 cell->setOverrideSize(kMax(0, 
1269                                            rHeight - cell->borderTop() - cell->paddingTop() - 
1270                                                      cell->borderBottom() - cell->paddingBottom()));
1271                 cell->layoutIfNeeded();
1272
1273                 // Alignment within a cell is based off the calculated
1274                 // height, which becomes irrelevant once the cell has
1275                 // been resized based off its percentage. -dwh
1276                 cell->setCellTopExtra(0);
1277                 cell->setCellBottomExtra(0);
1278             }
1279             else {
1280 #ifdef DEBUG_LAYOUT
1281             kdDebug( 6040 ) << "setting position " << r << "/" << c << ": "
1282                             << table()->columnPos[c] /*+ padding */ << "/" << rowPos[rindx] << " height=" << rHeight<< endl;
1283 #endif
1284
1285             EVerticalAlign va = cell->style()->verticalAlign();
1286             int te=0;
1287             switch (va)
1288             {
1289             case SUB:
1290             case SUPER:
1291             case TEXT_TOP:
1292             case TEXT_BOTTOM:
1293             case BASELINE:
1294                 te = getBaseline(r) - cell->baselinePosition() ;
1295                 break;
1296             case TOP:
1297                 te = 0;
1298                 break;
1299             case MIDDLE:
1300                 te = (rHeight - cell->height())/2;
1301                 break;
1302             case BOTTOM:
1303                 te = rHeight - cell->height();
1304                 break;
1305             default:
1306                 break;
1307             }
1308 #ifdef DEBUG_LAYOUT
1309             //            kdDebug( 6040 ) << "CELL " << cell << " te=" << te << ", be=" << rHeight - cell->height() - te << ", rHeight=" << rHeight << ", valign=" << va << endl;
1310 #endif
1311             cell->setCellTopExtra( te );
1312             cell->setCellBottomExtra( rHeight - cell->height() - te);
1313             }
1314             
1315             int oldCellX = cell->xPos();
1316             int oldCellY = cell->yPos();
1317         
1318             if (style()->direction()==RTL) {
1319                 cell->setPos(
1320                     table()->columnPos[(int)totalCols] -
1321                     table()->columnPos[table()->colToEffCol(cell->col()+cell->colSpan())] +
1322                     leftOffset,
1323                     rowPos[rindx] );
1324             } else {
1325                 cell->setPos( table()->columnPos[c] + leftOffset, rowPos[rindx] );
1326             }
1327
1328             // If the cell moved, we have to repaint it as well as any floating/positioned
1329             // descendants.  An exception is if we need a layout.  In this case, we know we're going to
1330             // repaint ourselves (and the cell) anyway.
1331             if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
1332                 cell->repaintDuringLayoutIfMoved(oldCellX, oldCellY);
1333         }
1334     }
1335
1336     m_height = rowPos[totalRows];
1337     return m_height;
1338 }
1339
1340
1341 void RenderTableSection::paint(PaintInfo& i, int tx, int ty)
1342 {
1343     unsigned int totalRows = gridRows;
1344     unsigned int totalCols = table()->columns.size();
1345
1346     tx += m_x;
1347     ty += m_y;
1348
1349     // check which rows and cols are visible and only paint these
1350     // ### fixme: could use a binary search here
1351     PaintAction paintAction = i.phase;
1352     int x = i.r.x(); int y = i.r.y(); int w = i.r.width(); int h = i.r.height();
1353
1354     int os = 2*maximalOutlineSize(paintAction);
1355     unsigned int startrow = 0;
1356     unsigned int endrow = totalRows;
1357     for ( ; startrow < totalRows; startrow++ ) {
1358         if ( ty + rowPos[startrow+1] >= y - os)
1359             break;
1360     }
1361     for ( ; endrow > 0; endrow-- ) {
1362         if ( ty + rowPos[endrow-1] <= y + h + os)
1363             break;
1364     }
1365     unsigned int startcol = 0;
1366     unsigned int endcol = totalCols;
1367     if ( style()->direction() == LTR ) {
1368         for ( ; startcol < totalCols; startcol++ ) {
1369             if ( tx + table()->columnPos[startcol+1] >= x - os)
1370                 break;
1371         }
1372         for ( ; endcol > 0; endcol-- ) {
1373             if ( tx + table()->columnPos[endcol-1] <= x + w + os)
1374                 break;
1375         }
1376     }
1377     
1378     if ( startcol < endcol ) {
1379         // draw the cells
1380         for ( unsigned int r = startrow; r < endrow; r++ ) {
1381             unsigned int c = startcol;
1382             // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it
1383             while ( c && cellAt( r, c ) == (RenderTableCell *)-1 )
1384                 c--;
1385             for ( ; c < endcol; c++ ) {
1386                 RenderTableCell *cell = cellAt(r, c);
1387                 if (!cell || cell == (RenderTableCell *)-1 )
1388                     continue;
1389                 
1390                 // Cells must always paint in the order in which they appear taking into account
1391                 // their upper left originating row/column.  For cells with rowspans, avoid repainting
1392                 // if we've already seen the cell.
1393                 if (r > startrow && (cellAt(r-1, c) == cell))
1394                     continue;
1395
1396 #ifdef TABLE_PRINT
1397                 kdDebug( 6040 ) << "painting cell " << r << "/" << c << endl;
1398 #endif
1399                 cell->paint(i, tx, ty);
1400             }
1401         }
1402     }
1403 }
1404
1405 void RenderTableSection::recalcCells()
1406 {
1407     cCol = 0;
1408     cRow = -1;
1409     clearGrid();
1410     gridRows = 0;
1411
1412     RenderObject *row = firstChild();
1413     while ( row ) {
1414         cRow++;
1415         cCol = 0;
1416         ensureRows( cRow+1 );
1417         RenderObject *cell = row->firstChild();
1418         while ( cell ) {
1419             if ( cell->isTableCell() )
1420                 addCell( static_cast<RenderTableCell *>(cell) );
1421             cell = cell->nextSibling();
1422         }
1423         row = row->nextSibling();
1424     }
1425     needCellRecalc = false;
1426     setNeedsLayout(true);
1427 }
1428
1429 void RenderTableSection::clearGrid()
1430 {
1431     int rows = gridRows;
1432     while ( rows-- ) {
1433         delete grid[rows].row;
1434     }
1435 }
1436
1437 RenderObject* RenderTableSection::removeChildNode(RenderObject* child)
1438 {
1439     setNeedCellRecalc();
1440     return RenderContainer::removeChildNode( child );
1441 }
1442
1443 #ifndef NDEBUG
1444 void RenderTableSection::dump(QTextStream *stream, QString ind) const
1445 {
1446     *stream << endl << ind << "grid=(" << grid.size() << "," << table()->numEffCols() << ")" << endl << ind;
1447     for ( unsigned int r = 0; r < grid.size(); r++ ) {
1448         for ( int c = 0; c < table()->numEffCols(); c++ ) {
1449             if ( cellAt( r,  c ) && cellAt( r, c ) != (RenderTableCell *)-1 )
1450                 *stream << "(" << cellAt( r, c )->row() << "," << cellAt( r, c )->col() << ","
1451                         << cellAt(r, c)->rowSpan() << "," << cellAt(r, c)->colSpan() << ") ";
1452             else
1453                 *stream << cellAt( r, c ) << "null cell ";
1454         }
1455         *stream << endl << ind;
1456     }
1457     RenderContainer::dump(stream,ind);
1458 }
1459 #endif
1460
1461 // -------------------------------------------------------------------------
1462
1463 RenderTableRow::RenderTableRow(DOM::NodeImpl* node)
1464     : RenderContainer(node)
1465 {
1466     // init RenderObject attributes
1467     setInline(false);   // our object is not Inline
1468 }
1469
1470 void RenderTableRow::detach()
1471 {
1472     RenderTableSection *s = section();
1473     if (s) {
1474         s->setNeedCellRecalc();
1475     }
1476     RenderContainer::detach();
1477 }
1478
1479 void RenderTableRow::setStyle(RenderStyle* style)
1480 {
1481     style->setDisplay(TABLE_ROW);
1482     RenderContainer::setStyle(style);
1483 }
1484
1485 void RenderTableRow::addChild(RenderObject *child, RenderObject *beforeChild)
1486 {
1487 #ifdef DEBUG_LAYOUT
1488     kdDebug( 6040 ) << renderName() << "(TableRow)::addChild( " << child->renderName() << " )"  << ", " <<
1489                        (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
1490 #endif
1491     if (child->element() && child->element()->id() == ID_FORM) {
1492         RenderContainer::addChild(child,beforeChild);
1493         return;
1494     }
1495
1496     RenderTableCell *cell;
1497
1498     if ( !child->isTableCell() ) {
1499         RenderObject *last = beforeChild;
1500         if ( !last )
1501             last = lastChild();
1502         RenderTableCell *cell = 0;
1503         if( last && last->isAnonymous() && last->isTableCell() )
1504             cell = static_cast<RenderTableCell *>(last);
1505         else {
1506             cell = new (renderArena()) RenderTableCell(document() /* anonymous object */);
1507             RenderStyle *newStyle = new (renderArena()) RenderStyle();
1508             newStyle->inheritFrom(style());
1509             newStyle->setDisplay( TABLE_CELL );
1510             cell->setStyle(newStyle);
1511             addChild(cell, beforeChild);
1512         }
1513         cell->addChild(child);
1514         child->setNeedsLayoutAndMinMaxRecalc();
1515         return;
1516     } else
1517         cell = static_cast<RenderTableCell *>(child);
1518
1519     static_cast<RenderTableSection *>(parent())->addCell( cell );
1520
1521     RenderContainer::addChild(cell,beforeChild);
1522
1523     if ( ( beforeChild || nextSibling()) && section() )
1524         section()->setNeedCellRecalc();
1525 }
1526
1527 RenderObject* RenderTableRow::removeChildNode(RenderObject* child)
1528 {
1529 // RenderTableCell detach should do it
1530 //     if ( section() )
1531 //      section()->setNeedCellRecalc();
1532     return RenderContainer::removeChildNode( child );
1533 }
1534
1535 #ifndef NDEBUG
1536 void RenderTableRow::dump(QTextStream *stream, QString ind) const
1537 {
1538     RenderContainer::dump(stream,ind);
1539 }
1540 #endif
1541
1542 void RenderTableRow::layout()
1543 {
1544     KHTMLAssert( needsLayout() );
1545     KHTMLAssert( minMaxKnown() );
1546
1547     RenderObject *child = firstChild();
1548     while( child ) {
1549         if (child->isTableCell()) {
1550             RenderTableCell *cell = static_cast<RenderTableCell *>(child);
1551             if (child->needsLayout()) {
1552                 cell->calcVerticalMargins();
1553                 cell->layout();
1554                 cell->setCellTopExtra(0);
1555                 cell->setCellBottomExtra(0);
1556             }
1557         }
1558         child = child->nextSibling();
1559     }
1560     setNeedsLayout(false);
1561 }
1562
1563 QRect RenderTableRow::getAbsoluteRepaintRect()
1564 {
1565     // For now, just repaint the whole table.
1566     // FIXME: Find a better way to do this.
1567     RenderTable* parentTable = table();
1568     if (parentTable)
1569         return parentTable->getAbsoluteRepaintRect();
1570     else
1571         return QRect();
1572 }
1573
1574 // -------------------------------------------------------------------------
1575
1576 RenderTableCell::RenderTableCell(DOM::NodeImpl* _node)
1577   : RenderBlock(_node)
1578 {
1579   _col = -1;
1580   _row = -1;
1581   cSpan = rSpan = 1;
1582   updateFromElement();
1583   setShouldPaintBackgroundOrBorder(true);
1584   _topExtra = 0;
1585   _bottomExtra = 0;
1586   m_percentageHeight = 0;
1587 }
1588
1589 void RenderTableCell::detach()
1590 {
1591     if (parent() && section())
1592         section()->setNeedCellRecalc();
1593
1594     RenderBlock::detach();
1595 }
1596
1597 void RenderTableCell::updateFromElement()
1598 {
1599     int oldRSpan = rSpan;
1600     int oldCSpan = cSpan;
1601     DOM::NodeImpl* node = element();
1602     if (node && (node->id() == ID_TD || node->id() == ID_TH)) {
1603         DOM::HTMLTableCellElementImpl *tc = static_cast<DOM::HTMLTableCellElementImpl *>(node);
1604         cSpan = tc->colSpan();
1605         rSpan = tc->rowSpan();
1606     }
1607     if (parent() && oldRSpan != rSpan || oldCSpan != cSpan)
1608         setNeedsLayoutAndMinMaxRecalc();
1609 }
1610     
1611 void RenderTableCell::calcMinMaxWidth()
1612 {
1613     RenderBlock::calcMinMaxWidth();
1614     if (element() && style()->whiteSpace() == NORMAL) {
1615         // See if nowrap was set.
1616         DOMString nowrap = static_cast<ElementImpl*>(element())->getAttribute(ATTR_NOWRAP);
1617         if (!nowrap.isNull() && style()->width().isFixed())
1618             // Nowrap is set, but we didn't actually use it because of the
1619             // fixed width set on the cell.  Even so, it is a WinIE/Moz trait
1620             // to make the minwidth of the cell into the fixed width.  They do this
1621             // even in strict mode, so do not make this a quirk.  Affected the top
1622             // of hiptop.com.
1623             if (m_minWidth < style()->width().value)
1624                 m_minWidth = style()->width().value;
1625     }
1626 }
1627
1628 void RenderTableCell::calcWidth()
1629 {
1630 }
1631
1632 void RenderTableCell::setWidth( int width )
1633 {
1634     if ( width != m_width ) {
1635         m_width = width;
1636         m_widthChanged = true;
1637     }
1638 }
1639
1640 void RenderTableCell::layout()
1641 {
1642     layoutBlock(m_widthChanged);
1643     m_widthChanged = false;
1644 }
1645
1646 void RenderTableCell::computeAbsoluteRepaintRect(QRect& r, bool f)
1647 {
1648     r.setY(r.y() + _topExtra);
1649     RenderBlock::computeAbsoluteRepaintRect(r, f);
1650 }
1651
1652 bool RenderTableCell::absolutePosition(int &xPos, int &yPos, bool f)
1653 {
1654     bool ret = RenderBlock::absolutePosition(xPos, yPos, f);
1655     if (ret)
1656       yPos += _topExtra;
1657     return ret;
1658 }
1659
1660 short RenderTableCell::baselinePosition( bool ) const
1661 {
1662     RenderObject *o = firstChild();
1663     int offset = paddingTop() + borderTop();
1664     if ( !o ) return offset;
1665     while ( o->firstChild() ) {
1666         if ( !o->isInline() )
1667             offset += o->paddingTop() + o->borderTop();
1668         o = o->firstChild();
1669     }
1670     offset += o->baselinePosition( true );
1671     return offset;
1672 }
1673
1674
1675 void RenderTableCell::setStyle( RenderStyle *style )
1676 {
1677     style->setDisplay(TABLE_CELL);
1678
1679     if (style->whiteSpace() == KHTML_NOWRAP) {
1680         // Figure out if we are really nowrapping or if we should just
1681         // use normal instead.  If the width of the cell is fixed, then
1682         // we don't actually use NOWRAP. 
1683         if (style->width().isFixed())
1684             style->setWhiteSpace(NORMAL);
1685         else
1686             style->setWhiteSpace(NOWRAP);
1687     }
1688
1689     RenderBlock::setStyle( style );
1690     setShouldPaintBackgroundOrBorder(true);
1691 }
1692
1693 bool RenderTableCell::requiresLayer() {
1694     // FIXME: This is only here until we figure out how to position
1695     // table cells properly when they have layers.
1696     return false;
1697 }
1698
1699 // The following rules apply for resolving conflicts and figuring out which border
1700 // to use.
1701 // (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting 
1702 // borders. Any border with this value suppresses all borders at this location.
1703 // (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all 
1704 // the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is 
1705 // the default value for the border style.)
1706 // (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders 
1707 // are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred 
1708 // in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'.
1709 // (4) If border styles differ only in color, then a style set on a cell wins over one on a row, 
1710 // which wins over a row group, column, column group and, lastly, table. It is undefined which color 
1711 // is used when two elements of the same type disagree.
1712 static CollapsedBorderValue compareBorders(const CollapsedBorderValue& border1, 
1713                                            const CollapsedBorderValue& border2)
1714 {
1715     // Sanity check the values passed in.  If either is null, return the other.
1716     if (!border2.exists()) return border1;
1717     if (!border1.exists()) return border2;
1718     
1719     // Rule #1 above.
1720     if (border1.style() == BHIDDEN || border2.style() == BHIDDEN)
1721         return CollapsedBorderValue(); // No border should exist at this location.
1722     
1723     // Rule #2 above.  A style of 'none' has lowest priority and always loses to any other border.
1724     if (border2.style() == BNONE) return border1;
1725     if (border1.style() == BNONE) return border2;
1726     
1727     // The first part of rule #3 above. Wider borders win.
1728     if (border1.width() != border2.width())
1729         return border1.width() > border2.width() ? border1 : border2;
1730     
1731     // The borders have equal width.  Sort by border style.
1732     if (border1.style() != border2.style())
1733         return border1.style() > border2.style() ? border1 : border2;
1734     
1735     // The border have the same width and style.  Rely on precedence (cell over row over row group, etc.)
1736     return border1.precedence >= border2.precedence ? border1 : border2;
1737 }
1738
1739 CollapsedBorderValue RenderTableCell::collapsedLeftBorder() const
1740 {
1741     // For border left, we need to check, in order of precedence:
1742     // (1) Our left border.
1743     CollapsedBorderValue result(&style()->borderLeft(), BCELL);
1744     
1745     // (2) The previous cell's right border.
1746     RenderTableCell* prevCell = table()->cellLeft(this);
1747     if (prevCell) {
1748         result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderRight(), BCELL));
1749         if (!result.exists()) return result;
1750     }
1751     else if (col() == 0) {
1752         // (3) Our row's left border.
1753         result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderLeft(), BROW));
1754         if (!result.exists()) return result;
1755         
1756         // (4) Our row group's left border.
1757         result = compareBorders(result, CollapsedBorderValue(&section()->style()->borderLeft(), BROWGROUP));
1758         if (!result.exists()) return result;
1759     }
1760     
1761     // (5) Our column's left border.
1762     RenderTableCol* colElt = table()->colElement(col());
1763     if (colElt) {
1764         result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
1765         if (!result.exists()) return result;
1766     }
1767     
1768     // (6) The previous column's right border.
1769     if (col() > 0) {
1770         colElt = table()->colElement(col()-1);
1771         if (colElt) {
1772             result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
1773             if (!result.exists()) return result;
1774         }
1775     }
1776     
1777     if (col() == 0) {
1778         // (7) The table's left border.
1779         result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderLeft(), BTABLE));
1780         if (!result.exists()) return result;
1781     }
1782     
1783     return result;
1784 }
1785
1786 CollapsedBorderValue RenderTableCell::collapsedRightBorder() const
1787 {
1788     RenderTable* tableElt = table();
1789     bool inLastColumn = false;
1790     int effCol = tableElt->colToEffCol(col()+colSpan()-1);
1791     if (effCol == tableElt->numEffCols()-1)
1792         inLastColumn = true;
1793     
1794     // For border right, we need to check, in order of precedence:
1795     // (1) Our right border.
1796     CollapsedBorderValue result = CollapsedBorderValue(&style()->borderRight(), BCELL);
1797     
1798     // (2) The next cell's left border.
1799     if (!inLastColumn) {
1800         RenderTableCell* nextCell = tableElt->cellRight(this);
1801         if (nextCell) {
1802             result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderLeft(), BCELL));
1803             if (!result.exists()) return result;
1804         }
1805     }
1806     else {
1807         // (3) Our row's right border.
1808         result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderRight(), BROW));
1809         if (!result.exists()) return result;
1810         
1811         // (4) Our row group's right border.
1812         result = compareBorders(result, CollapsedBorderValue(&section()->style()->borderRight(), BROWGROUP));
1813         if (!result.exists()) return result;
1814     }
1815     
1816     // (5) Our column's right border.
1817     RenderTableCol* colElt = table()->colElement(col()+colSpan()-1);
1818     if (colElt) {
1819         result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
1820         if (!result.exists()) return result;
1821     }
1822     
1823     // (6) The next column's left border.
1824     if (!inLastColumn) {
1825         colElt = tableElt->colElement(col()+colSpan());
1826         if (colElt) {
1827             result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
1828             if (!result.exists()) return result;
1829         }
1830     }
1831     else {
1832         // (7) The table's right border.
1833         result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderRight(), BTABLE));
1834         if (!result.exists()) return result;
1835     }
1836     
1837     return result;
1838 }
1839
1840 CollapsedBorderValue RenderTableCell::collapsedTopBorder() const
1841 {
1842     // For border top, we need to check, in order of precedence:
1843     // (1) Our top border.
1844     CollapsedBorderValue result = CollapsedBorderValue(&style()->borderTop(), BCELL);
1845     
1846     RenderTableCell* prevCell = table()->cellAbove(this);
1847     if (prevCell) {
1848         // (2) A previous cell's bottom border.
1849         result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderBottom(), BCELL));
1850         if (!result.exists()) return result;
1851     }
1852     
1853     // (3) Our row's top border.
1854     result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderTop(), BROW));
1855     if (!result.exists()) return result;
1856     
1857     // (4) The previous row's bottom border.
1858     if (prevCell) {
1859         RenderObject* prevRow = 0;
1860         if (prevCell->section() == section())
1861             prevRow = parent()->previousSibling();
1862         else
1863             prevRow = prevCell->section()->lastChild();
1864     
1865         if (prevRow) {
1866             result = compareBorders(result, CollapsedBorderValue(&prevRow->style()->borderBottom(), BROW));
1867             if (!result.exists()) return result;
1868         }
1869     }
1870     
1871     // Now check row groups.
1872     RenderObject* currSection = parent()->parent();
1873     if (row() == 0) {
1874         // (5) Our row group's top border.
1875         result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP));
1876         if (!result.exists()) return result;
1877         
1878         // (6) Previous row group's bottom border.
1879         for (currSection = currSection->previousSibling(); currSection;
1880              currSection = currSection->previousSibling()) {
1881             if (currSection->isTableSection()) {
1882                 RenderTableSection* section = static_cast<RenderTableSection*>(currSection);
1883                 result = compareBorders(result, CollapsedBorderValue(&section->style()->borderBottom(), BROWGROUP));
1884                 if (!result.exists()) return result;
1885             }
1886         }
1887     }
1888     
1889     if (!currSection) {
1890         // (8) Our column's top border.
1891         RenderTableCol* colElt = table()->colElement(col());
1892         if (colElt) {
1893             result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderTop(), BCOL));
1894             if (!result.exists()) return result;
1895         }
1896         
1897         // (9) The table's top border.
1898         result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderTop(), BTABLE));
1899         if (!result.exists()) return result;
1900     }
1901     
1902     return result;
1903 }
1904
1905 CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const
1906 {
1907     // For border top, we need to check, in order of precedence:
1908     // (1) Our bottom border.
1909     CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBottom(), BCELL);
1910     
1911     RenderTableCell* nextCell = table()->cellBelow(this);
1912     if (nextCell) {
1913         // (2) A following cell's top border.
1914         result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderTop(), BCELL));
1915         if (!result.exists()) return result;
1916     }
1917     
1918     // (3) Our row's bottom border. (FIXME: Deal with rowspan!)
1919     result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderBottom(), BROW));
1920     if (!result.exists()) return result;
1921     
1922     // (4) The next row's top border.
1923     if (nextCell) {
1924         result = compareBorders(result, CollapsedBorderValue(&nextCell->parent()->style()->borderTop(), BROW));
1925         if (!result.exists()) return result;
1926     }
1927     
1928     // Now check row groups.
1929     RenderObject* currSection = parent()->parent();
1930     if (row()+rowSpan() >= static_cast<RenderTableSection*>(currSection)->numRows()) {
1931         // (5) Our row group's bottom border.
1932         result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP));
1933         if (!result.exists()) return result;
1934         
1935         // (6) Following row group's top border.
1936         for (currSection = currSection->nextSibling(); currSection;
1937              currSection = currSection->nextSibling()) {
1938             if (currSection->isTableSection()) {
1939                 RenderTableSection* section = static_cast<RenderTableSection*>(currSection);
1940                 result = compareBorders(result, CollapsedBorderValue(&section->style()->borderTop(), BROWGROUP));
1941                 if (!result.exists()) return result;
1942             }
1943         }
1944     }
1945     
1946     if (!currSection) {
1947         // (8) Our column's bottom border.
1948         RenderTableCol* colElt = table()->colElement(col());
1949         if (colElt) {
1950             result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderBottom(), BCOL));
1951             if (!result.exists()) return result;
1952         }
1953         
1954         // (9) The table's bottom border.
1955         result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderBottom(), BTABLE));
1956         if (!result.exists()) return result;
1957     }
1958     
1959     return result;    
1960 }
1961
1962 int RenderTableCell::borderLeft() const
1963 {
1964     if (table()->collapseBorders()) {
1965         CollapsedBorderValue border = collapsedLeftBorder();
1966         if (border.exists())
1967             return int(border.width()/2.0+0.5); // Give the extra pixel to top and left.
1968         return 0;
1969     }
1970     return RenderBlock::borderLeft();
1971 }
1972     
1973 int RenderTableCell::borderRight() const
1974 {
1975     if (table()->collapseBorders()) {
1976         CollapsedBorderValue border = collapsedRightBorder();
1977         if (border.exists())
1978             return border.width()/2;
1979         return 0;
1980     }
1981     return RenderBlock::borderRight();
1982 }
1983
1984 int RenderTableCell::borderTop() const
1985 {
1986     if (table()->collapseBorders()) {
1987         CollapsedBorderValue border = collapsedTopBorder();
1988         if (border.exists())
1989             return int(border.width()/2.0+0.5); // Give the extra pixel to top and left.
1990         return 0;
1991     }
1992     return RenderBlock::borderTop();
1993 }
1994
1995 int RenderTableCell::borderBottom() const
1996 {
1997     if (table()->collapseBorders()) {
1998         CollapsedBorderValue border = collapsedBottomBorder();
1999         if (border.exists())
2000             return border.width()/2;
2001         return 0;
2002     }
2003     return RenderBlock::borderBottom();
2004 }
2005
2006 #ifdef BOX_DEBUG
2007 #include <qpainter.h>
2008
2009 static void outlineBox(QPainter *p, int _tx, int _ty, int w, int h)
2010 {
2011     p->setPen(QPen(QColor("yellow"), 3, Qt::DotLine));
2012     p->setBrush( Qt::NoBrush );
2013     p->drawRect(_tx, _ty, w, h );
2014 }
2015 #endif
2016
2017 void RenderTableCell::paint(PaintInfo& i, int _tx, int _ty)
2018 {
2019
2020 #ifdef TABLE_PRINT
2021     kdDebug( 6040 ) << renderName() << "(RenderTableCell)::paint() w/h = (" << width() << "/" << height() << ")" << " _y/_h=" << _y << "/" << _h << endl;
2022 #endif
2023
2024     _tx += m_x;
2025     _ty += m_y;
2026
2027     // check if we need to do anything at all...
2028     int os = 2*maximalOutlineSize(i.phase);
2029     if ((_ty >= i.r.y() + i.r.height() + os) || (_ty + _topExtra + m_height + _bottomExtra <= i.r.y() - os))
2030         return;
2031     
2032     if (i.phase == PaintActionCollapsedTableBorders && style()->visibility() == VISIBLE) {
2033         int w = width();
2034         int h = height() + borderTopExtra() + borderBottomExtra();
2035         paintCollapsedBorder(i.p, _tx, _ty, w, h);
2036     }
2037     else
2038         RenderBlock::paintObject(i, _tx, _ty + _topExtra);
2039
2040 #ifdef BOX_DEBUG
2041     ::outlineBox( i.p, _tx, _ty, width(), height() + borderTopExtra() + borderBottomExtra());
2042 #endif
2043 }
2044
2045 static EBorderStyle collapsedBorderStyle(EBorderStyle style)
2046 {
2047     if (style == OUTSET)
2048         style = GROOVE;
2049     else if (style == INSET)
2050         style = RIDGE;
2051     return style;
2052 }
2053
2054 struct CollapsedBorder {
2055     CollapsedBorder(){}
2056     
2057     CollapsedBorderValue border;
2058     RenderObject::BorderSide side;
2059     bool shouldPaint;
2060     int x1;
2061     int y1;
2062     int x2;
2063     int y2;
2064     EBorderStyle style;
2065 };
2066
2067 class CollapsedBorders
2068 {
2069 public:
2070     CollapsedBorders(int i) :count(0) {}
2071     
2072     void addBorder(const CollapsedBorderValue& b, RenderObject::BorderSide s, bool paint, 
2073                    int _x1, int _y1, int _x2, int _y2,
2074                    EBorderStyle _style)
2075     {
2076         if (b.exists() && paint) {
2077             borders[count].border = b;
2078             borders[count].side = s;
2079             borders[count].shouldPaint = paint;
2080             borders[count].x1 = _x1;
2081             borders[count].x2 = _x2;
2082             borders[count].y1 = _y1;
2083             borders[count].y2 = _y2;
2084             borders[count].style = _style;
2085             count++;
2086         }
2087     }
2088
2089     CollapsedBorder* nextBorder() {
2090         for (int i = 0; i < count; i++) {
2091             if (borders[i].border.exists() && borders[i].shouldPaint) {
2092                 borders[i].shouldPaint = false;
2093                 return &borders[i];
2094             }
2095         }
2096         
2097         return 0;
2098     }
2099     
2100     CollapsedBorder borders[4];
2101     int count;
2102 };
2103
2104 static void addBorderStyle(QValueList<CollapsedBorderValue>& borderStyles, CollapsedBorderValue borderValue)
2105 {
2106     if (!borderValue.exists() || borderStyles.contains(borderValue))
2107         return;
2108     
2109     QValueListIterator<CollapsedBorderValue> it = borderStyles.begin();
2110     QValueListIterator<CollapsedBorderValue> end = borderStyles.end();
2111     for (; it != end; ++it) {
2112         CollapsedBorderValue result = compareBorders(*it, borderValue);
2113         if (result == *it) {
2114             borderStyles.insert(it, borderValue);
2115             return;
2116         }
2117     }
2118
2119     borderStyles.append(borderValue);
2120 }
2121
2122 void RenderTableCell::collectBorders(QValueList<CollapsedBorderValue>& borderStyles)
2123 {
2124     addBorderStyle(borderStyles, collapsedLeftBorder());
2125     addBorderStyle(borderStyles, collapsedRightBorder());
2126     addBorderStyle(borderStyles, collapsedTopBorder());
2127     addBorderStyle(borderStyles, collapsedBottomBorder());
2128 }
2129
2130 void RenderTableCell::paintCollapsedBorder(QPainter* p, int _tx, int _ty, int w, int h)
2131 {
2132     if (!table()->currentBorderStyle())
2133         return;
2134     
2135     CollapsedBorderValue leftVal = collapsedLeftBorder();
2136     CollapsedBorderValue rightVal = collapsedRightBorder();
2137     CollapsedBorderValue topVal = collapsedTopBorder();
2138     CollapsedBorderValue bottomVal = collapsedBottomBorder();
2139      
2140     // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location.
2141     int topWidth = topVal.width();
2142     int bottomWidth = bottomVal.width();
2143     int leftWidth = leftVal.width();
2144     int rightWidth = rightVal.width();
2145     
2146     _tx -= leftWidth/2;
2147     _ty -= topWidth/2;
2148     w += leftWidth/2 + int(rightWidth/2.0+0.5);
2149     h += topWidth/2 + int(bottomWidth/2.0+0.5);
2150     
2151     bool tt = topVal.isTransparent();
2152     bool bt = bottomVal.isTransparent();
2153     bool rt = rightVal.isTransparent();
2154     bool lt = leftVal.isTransparent();
2155     
2156     EBorderStyle ts = collapsedBorderStyle(topVal.style());
2157     EBorderStyle bs = collapsedBorderStyle(bottomVal.style());
2158     EBorderStyle ls = collapsedBorderStyle(leftVal.style());
2159     EBorderStyle rs = collapsedBorderStyle(rightVal.style());
2160     
2161     bool render_t = ts > BHIDDEN && !tt;
2162     bool render_l = ls > BHIDDEN && !lt;
2163     bool render_r = rs > BHIDDEN && !rt;
2164     bool render_b = bs > BHIDDEN && !bt;
2165
2166     // We never paint diagonals at the joins.  We simply let the border with the highest
2167     // precedence paint on top of borders with lower precedence.  
2168     CollapsedBorders borders(4);
2169     borders.addBorder(topVal, BSTop, render_t, _tx, _ty, _tx + w, _ty + topWidth, ts);
2170     borders.addBorder(bottomVal, BSBottom, render_b, _tx, _ty + h - bottomWidth, _tx + w, _ty + h, bs);
2171     borders.addBorder(leftVal, BSLeft, render_l, _tx, _ty, _tx + leftWidth, _ty + h, ls);
2172     borders.addBorder(rightVal, BSRight, render_r, _tx + w - rightWidth, _ty, _tx + w, _ty + h, rs);
2173     
2174     for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) {
2175         if (border->border == *table()->currentBorderStyle())
2176             drawBorder(p, border->x1, border->y1, border->x2, border->y2, border->side, 
2177                        border->border.color(), style()->color(), border->style, 0, 0);
2178     }
2179 }
2180
2181 QRect RenderTableCell::getAbsoluteRepaintRect()
2182 {
2183     int ow = style() ? style()->outlineSize() : 0;
2184     QRect r(-ow, -ow - borderTopExtra(), 
2185             overflowWidth(false)+ow*2, overflowHeight(false)+borderTopExtra()+borderBottomExtra()+ow*2);
2186     computeAbsoluteRepaintRect(r);
2187     return r;
2188 }
2189
2190 void RenderTableCell::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
2191 {
2192     RenderTable* tableElt = table();
2193     if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild())
2194         return;
2195     
2196     int w = width();
2197     int h = height() + borderTopExtra() + borderBottomExtra();
2198     _ty -= borderTopExtra();
2199
2200     QColor c = style()->backgroundColor();
2201     if ( !c.isValid() && parent() ) // take from row
2202         c = parent()->style()->backgroundColor();
2203     if ( !c.isValid() && parent() && parent()->parent() ) // take from rowgroup
2204         c = parent()->parent()->style()->backgroundColor();
2205     if ( !c.isValid() ) {
2206         // see if we have a col or colgroup for this
2207         RenderTableCol *col = table()->colElement( _col );
2208         if ( col ) {
2209             c = col->style()->backgroundColor();
2210             if ( !c.isValid() ) {
2211                 // try column group
2212                 RenderStyle *style = col->parent()->style();
2213                 if ( style->display() == TABLE_COLUMN_GROUP )
2214                     c = style->backgroundColor();
2215             }
2216         }
2217     }
2218
2219     // FIXME: This code is just plain wrong.  Rows and columns should paint their backgrounds
2220     // independent from the cell.
2221     // ### get offsets right in case the bgimage is inherited.
2222     const BackgroundLayer* bgLayer = style()->backgroundLayers();
2223     if (!bgLayer->hasImage() && parent())
2224         bgLayer = parent()->style()->backgroundLayers();
2225     if (!bgLayer->hasImage() && parent() && parent()->parent())
2226         bgLayer = parent()->parent()->style()->backgroundLayers();
2227     if (!bgLayer->hasImage()) {
2228         // see if we have a col or colgroup for this
2229         RenderTableCol* col = table()->colElement(_col);
2230         if (col) {
2231             bgLayer = col->style()->backgroundLayers();
2232             if (!bgLayer->hasImage()) {
2233                 // try column group
2234                 RenderStyle *style = col->parent()->style();
2235                 if (style->display() == TABLE_COLUMN_GROUP)
2236                     bgLayer = style->backgroundLayers();
2237             }
2238         }
2239     }
2240
2241     int my = kMax(_ty, i.r.y());
2242     int end = kMin(i.r.y() + i.r.height(), _ty + h);
2243     int mh = end - my;
2244
2245     if (bgLayer->hasImage() || c.isValid())
2246         paintBackground(i.p, c, bgLayer, my, mh, _tx, _ty, w, h);
2247
2248     if (style()->hasBorder() && !tableElt->collapseBorders())
2249         paintBorder(i.p, _tx, _ty, w, h, style());
2250 }
2251
2252
2253 #ifndef NDEBUG
2254 void RenderTableCell::dump(QTextStream *stream, QString ind) const
2255 {
2256     *stream << " row=" << _row;
2257     *stream << " col=" << _col;
2258     *stream << " rSpan=" << rSpan;
2259     *stream << " cSpan=" << cSpan;
2260 //    *stream << " nWrap=" << nWrap;
2261
2262     RenderBlock::dump(stream,ind);
2263 }
2264 #endif
2265
2266 // -------------------------------------------------------------------------
2267
2268 RenderTableCol::RenderTableCol(DOM::NodeImpl* node)
2269     : RenderContainer(node)
2270 {
2271     // init RenderObject attributes
2272     setInline(true);   // our object is not Inline
2273
2274     _span = 1;
2275     updateFromElement();
2276 }
2277
2278 void RenderTableCol::updateFromElement()
2279 {
2280   DOM::NodeImpl *node = element();
2281   if ( node && (node->id() == ID_COL || node->id() == ID_COLGROUP) ) {
2282       DOM::HTMLTableColElementImpl *tc = static_cast<DOM::HTMLTableColElementImpl *>(node);
2283       _span = tc->span();
2284   } else
2285       _span = ! ( style() && style()->display() == TABLE_COLUMN_GROUP );
2286 }
2287
2288 bool RenderTableCol::canHaveChildren() const
2289 {
2290     // cols cannot have children.  This is actually necessary to fix a bug
2291     // with libraries.uc.edu, which makes a <p> be a table-column.
2292     return style()->display() == TABLE_COLUMN_GROUP;
2293 }
2294
2295 void RenderTableCol::addChild(RenderObject *child, RenderObject *beforeChild)
2296 {
2297 #ifdef DEBUG_LAYOUT
2298     //kdDebug( 6040 ) << renderName() << "(Table)::addChild( " << child->renderName() << " )"  << ", " <<
2299     //                   (beforeChild ? beforeChild->renderName() : 0) << " )" << endl;
2300 #endif
2301
2302     KHTMLAssert(child->style()->display() == TABLE_COLUMN);
2303
2304     // these have to come before the table definition!
2305     RenderContainer::addChild(child,beforeChild);
2306 }
2307
2308 #ifndef NDEBUG
2309 void RenderTableCol::dump(QTextStream *stream, QString ind) const
2310 {
2311     *stream << " _span=" << _span;
2312     RenderContainer::dump(stream,ind);
2313 }
2314 #endif
2315
2316 #undef TABLE_DEBUG
2317 #undef DEBUG_LAYOUT
2318 #undef BOX_DEBUG