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