fa42ca765fc26041440b7cda7f1b8e32aec156cd
[WebKit-https.git] / WebCore / rendering / RenderTable.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, 2004, 2005, 2006 Apple Computer, Inc.
10  * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27
28 #include "config.h"
29 #include "RenderTable.h"
30
31 #include "AutoTableLayout.h"
32 #include "Document.h"
33 #include "FixedTableLayout.h"
34 #include "HTMLNames.h"
35 #include "KWQTextStream.h"
36 #include "RenderTableCell.h"
37 #include "RenderTableCol.h"
38 #include "RenderTableSection.h"
39
40 using namespace std;
41
42 namespace WebCore {
43
44 using namespace HTMLNames;
45
46 RenderTable::RenderTable(Node* node)
47     : RenderBlock(node)
48 {
49     tCaption = 0;
50     head = foot = firstBody = 0;
51     tableLayout = 0;
52     m_currentBorder = 0;
53     
54     rules = None;
55     frame = Void;
56     has_col_elems = false;
57     hspacing = 0;
58     vspacing = 0;
59     padding = 0;
60     needSectionRecalc = false;
61     padding = 0;
62
63     columnPos.resize(2);
64     columnPos.fill(0);
65     columns.resize(1);
66     columns.fill(ColumnStruct());
67
68     columnPos[0] = 0;
69 }
70
71 RenderTable::~RenderTable()
72 {
73     delete tableLayout;
74 }
75
76 void RenderTable::setStyle(RenderStyle *_style)
77 {
78     ETableLayout oldTableLayout = style() ? style()->tableLayout() : TAUTO;
79     RenderBlock::setStyle(_style);
80
81     // In the collapsed border model, there is no cell spacing.
82     hspacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing();
83     vspacing = collapseBorders() ? 0 : style()->verticalBorderSpacing();
84     columnPos[0] = hspacing;
85
86     if (!tableLayout || style()->tableLayout() != oldTableLayout) {
87         delete tableLayout;
88
89         // According to the CSS2 spec, you only use fixed table layout if an
90         // explicit width is specified on the table.  Auto width implies auto table layout.
91         if (style()->tableLayout() == TFIXED && !style()->width().isAuto())
92             tableLayout = new FixedTableLayout(this);
93         else
94             tableLayout = new AutoTableLayout(this);
95     }
96 }
97
98 void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild)
99 {
100     bool wrapInAnonymousSection = true;
101     bool isTableElement = element() && element()->hasTagName(tableTag);
102
103     switch (child->style()->display()) {
104         case TABLE_CAPTION:
105             if (child->isRenderBlock())
106                 tCaption = static_cast<RenderBlock *>(child);
107             wrapInAnonymousSection = false;
108             break;
109         case TABLE_COLUMN:
110         case TABLE_COLUMN_GROUP:
111             has_col_elems = true;
112             wrapInAnonymousSection = false;
113             break;
114         case TABLE_HEADER_GROUP:
115             if (!head) {
116                 if (child->isTableSection())
117                     head = static_cast<RenderTableSection *>(child);
118             } else if (!firstBody) {
119                 if (child->isTableSection())
120                     firstBody = static_cast<RenderTableSection *>(child);
121             }
122             wrapInAnonymousSection = false;
123             break;
124         case TABLE_FOOTER_GROUP:
125             if (!foot) {
126                 if (child->isTableSection())
127                     foot = static_cast<RenderTableSection *>(child);
128                 wrapInAnonymousSection = false;
129                 break;
130             }
131             // fall through
132         case TABLE_ROW_GROUP:
133             if (!firstBody)
134                 if (child->isTableSection())
135                     firstBody = static_cast<RenderTableSection *>(child);
136             wrapInAnonymousSection = false;
137             break;
138         case TABLE_CELL:
139         case TABLE_ROW:
140             wrapInAnonymousSection = true;
141             break;
142         case BLOCK:
143         case BOX:
144         case COMPACT:
145         case INLINE:
146         case INLINE_BLOCK:
147         case INLINE_BOX:
148         case INLINE_TABLE:
149         case LIST_ITEM:
150         case NONE:
151         case RUN_IN:
152         case TABLE:
153             // Allow a form to just sit at the top level.
154             wrapInAnonymousSection = !isTableElement || !child->element() || !child->element()->hasTagName(formTag);
155             break;
156         }
157
158     if (!wrapInAnonymousSection) {
159         RenderContainer::addChild(child, beforeChild);
160         return;
161     }
162
163     if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) {
164         lastChild()->addChild(child);
165         return;
166     }
167
168     RenderObject *lastBox = beforeChild;
169     RenderObject *nextToLastBox = beforeChild;
170     while (lastBox && lastBox->parent()->isAnonymous() &&
171             !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION) {
172         nextToLastBox = lastBox;
173         lastBox = lastBox->parent();
174     }
175     if (lastBox && lastBox->isAnonymous()) {
176         lastBox->addChild(child, nextToLastBox);
177         return;
178     }
179
180     if (beforeChild && !beforeChild->isTableSection())
181         beforeChild = 0;
182     RenderTableSection* section = new (renderArena()) RenderTableSection(document() /* anonymous */);
183     RenderStyle* newStyle = new (renderArena()) RenderStyle();
184     newStyle->inheritFrom(style());
185     newStyle->setDisplay(TABLE_ROW_GROUP);
186     section->setStyle(newStyle);
187     addChild(section, beforeChild);
188     section->addChild(child);
189 }
190
191 void RenderTable::calcWidth()
192 {
193     if (isPositioned())
194         calcAbsoluteHorizontal();
195
196     RenderBlock *cb = containingBlock();
197     int availableWidth = cb->contentWidth();
198
199     LengthType widthType = style()->width().type();
200     if (widthType > Relative && style()->width().value() > 0) {
201         // Percent or fixed table
202         m_width = style()->width().calcMinValue(availableWidth);
203         if (m_minWidth > m_width)
204             m_width = m_minWidth;
205     } else {
206         // An auto width table should shrink to fit within the line width if necessary in order to 
207         // avoid overlapping floats.
208         availableWidth = cb->lineWidth(m_y);
209         
210         // Subtract out any fixed margins from our available width for auto width tables.
211         int marginTotal = 0;
212         if (!style()->marginLeft().isAuto())
213             marginTotal += style()->marginLeft().calcValue(availableWidth);
214         if (!style()->marginRight().isAuto())
215             marginTotal += style()->marginRight().calcValue(availableWidth);
216             
217         // Subtract out our margins to get the available content width.
218         int availContentWidth = max(0, availableWidth - marginTotal);
219         
220         // Ensure we aren't bigger than our max width or smaller than our min width.
221         m_width = min(availContentWidth, m_maxWidth);
222     }
223     
224     m_width = max(m_width, m_minWidth);
225
226     // Finally, with our true width determined, compute our margins for real.
227     m_marginRight = 0;
228     m_marginLeft = 0;
229     calcHorizontalMargins(style()->marginLeft(),style()->marginRight(),availableWidth);
230 }
231
232 void RenderTable::layout()
233 {
234     KHTMLAssert(needsLayout());
235     KHTMLAssert(minMaxKnown());
236     KHTMLAssert(!needSectionRecalc);
237
238     if (posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
239         // All we have to is lay out our positioned objects.
240         layoutPositionedObjects(true);
241         setNeedsLayout(false);
242         return;
243     }
244
245     IntRect oldBounds, oldFullBounds;
246     bool checkForRepaint = checkForRepaintDuringLayout();
247     if (checkForRepaint)
248         getAbsoluteRepaintRectIncludingFloats(oldBounds, oldFullBounds);
249     
250     m_height = m_overflowHeight = 0;
251     initMaxMarginValues();
252     
253     //int oldWidth = m_width;
254     calcWidth();
255     m_overflowWidth = m_width;
256
257     // the optimisation below doesn't work since the internal table
258     // layout could have changed.  we need to add a flag to the table
259     // layout that tells us if something has changed in the min max
260     // calculations to do it correctly.
261 //     if ( oldWidth != m_width || columns.size() + 1 != columnPos.size() )
262     tableLayout->layout();
263
264     setCellWidths();
265
266     // layout child objects
267     int calculatedHeight = 0;
268
269     RenderObject *child = firstChild();
270     while (child) {
271         // FIXME: What about a form that has a display value that makes it a table section?
272         if (child->needsLayout() && !(child->element() && child->element()->hasTagName(formTag)))
273             child->layout();
274         if (child->isTableSection()) {
275             static_cast<RenderTableSection *>(child)->calcRowHeight();
276             calculatedHeight += static_cast<RenderTableSection *>(child)->layoutRows(0);
277         }
278         child = child->nextSibling();
279     }
280
281     // ### collapse caption margin
282     if (tCaption && tCaption->style()->captionSide() != CAPBOTTOM) {
283         tCaption->setPos(tCaption->marginLeft(), m_height);
284         m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
285     }
286
287     int bpTop = borderTop() + (collapseBorders() ? 0 : paddingTop());
288     int bpBottom = borderBottom() + (collapseBorders() ? 0 : paddingBottom());
289     
290     m_height += bpTop;
291
292     int oldHeight = m_height;
293     calcHeight();
294     int newHeight = m_height;
295     m_height = oldHeight;
296
297     Length h = style()->height();
298     int th = 0;
299     if (isPositioned())
300         th = newHeight; // FIXME: Leave this alone for now but investigate later.
301     else if (h.isFixed())
302         th = h.value() - (bpTop + bpBottom);  // Tables size as though CSS height includes border/padding.
303     else if (h.isPercent())
304         th = calcPercentageHeight(h);
305     th = max(0, th);
306
307     // layout rows
308     if (th > calculatedHeight) {
309         // we have to redistribute that height to get the constraint correctly
310         // just force the first body to the height needed
311         // ### FIXME This should take height constraints on all table sections into account and distribute
312         // accordingly. For now this should be good enough
313         if (firstBody) {
314             firstBody->calcRowHeight();
315             firstBody->layoutRows(th - calculatedHeight);
316         }
317         else if (!style()->htmlHacks()) {
318             // Completely empty tables (with no sections or anything) should at least honor specified height
319             // in strict mode.
320             m_height += th;
321         }
322     }
323     
324     int bl = borderLeft();
325     if (!collapseBorders())
326         bl += paddingLeft();
327
328     // position the table sections
329     if (head) {
330         head->setPos(bl, m_height);
331         m_height += head->height();
332     }
333     for (RenderObject *body = firstBody; body; body = body->nextSibling()) {
334         if (body != head && body != foot && body->isTableSection()) {
335             body->setPos(bl, m_height);
336             m_height += body->height();
337         }
338     }
339     if (foot) {
340         foot->setPos(bl, m_height);
341         m_height += foot->height();
342     }
343
344     m_height += bpBottom;
345                
346     if (tCaption && tCaption->style()->captionSide()==CAPBOTTOM) {
347         tCaption->setPos(tCaption->marginLeft(), m_height);
348         m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
349     }
350
351     // table can be containing block of positioned elements.
352     // ### only pass true if width or height changed.
353     layoutPositionedObjects( true );
354
355     // Repaint with our new bounds if they are different from our old bounds.
356     if (checkForRepaint)
357         repaintAfterLayoutIfNeeded(oldBounds, oldFullBounds);
358     
359     m_overflowHeight = max(m_overflowHeight, m_height);
360     m_overflowWidth = max(m_overflowWidth, m_width);
361
362     setNeedsLayout(false);
363 }
364
365 void RenderTable::setCellWidths()
366 {
367     for (RenderObject *child = firstChild(); child; child = child->nextSibling())
368         if ( child->isTableSection() )
369             static_cast<RenderTableSection *>(child)->setCellWidths();
370 }
371
372 void RenderTable::paint(PaintInfo& i, int _tx, int _ty)
373 {
374     _tx += xPos();
375     _ty += yPos();
376
377     PaintPhase paintPhase = i.phase;
378     
379     int os = 2*maximalOutlineSize(paintPhase);
380     if (_ty >= i.r.bottom() + os || _ty + height() <= i.r.y() - os)
381         return;
382     if (_tx >= i.r.right() + os || _tx + width() <= i.r.x() - os)
383         return;
384
385     if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground)
386         && shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE)
387         paintBoxDecorations(i, _tx, _ty);
388
389     // We're done.  We don't bother painting any children.
390     if (paintPhase == PaintPhaseBlockBackground)
391         return;
392
393     // We don't paint our own background, but we do let the kids paint their backgrounds.
394     if (paintPhase == PaintPhaseChildBlockBackgrounds)
395         paintPhase = PaintPhaseChildBlockBackground;
396     PaintInfo paintInfo(i);
397     paintInfo.phase = paintPhase;
398     
399     for (RenderObject *child = firstChild(); child; child = child->nextSibling())
400         if (!child->layer() && (child->isTableSection() || child == tCaption))
401             child->paint(paintInfo, _tx, _ty);
402
403     if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) {
404         // Collect all the unique border styles that we want to paint in a sorted list.  Once we
405         // have all the styles sorted, we then do individual passes, painting each style of border
406         // from lowest precedence to highest precedence.
407         paintInfo.phase = PaintPhaseCollapsedTableBorders;
408         DeprecatedValueList<CollapsedBorderValue> borderStyles;
409         collectBorders(borderStyles);
410         DeprecatedValueListIterator<CollapsedBorderValue> it = borderStyles.begin();
411         DeprecatedValueListIterator<CollapsedBorderValue> end = borderStyles.end();
412         for (; it != end; ++it) {
413             m_currentBorder = &(*it);
414             for (RenderObject* child = firstChild(); child; child = child->nextSibling())
415                 if (child->isTableSection())
416                     child->paint(paintInfo, _tx, _ty);
417         }
418     }
419         
420 #ifdef BOX_DEBUG
421     outlineBox(i.p, _tx, _ty, "blue");
422 #endif
423 }
424
425 void RenderTable::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
426 {
427     int w = width();
428     int h = height();
429     
430     // Account for the caption.
431     if (tCaption) {
432         int captionHeight = (tCaption->height() + tCaption->marginBottom() +  tCaption->marginTop());
433         h -= captionHeight;
434         if (tCaption->style()->captionSide() != CAPBOTTOM)
435             _ty += captionHeight;
436     }
437
438     int my = max(_ty, i.r.y());
439     int mh;
440     if (_ty < i.r.y())
441         mh= max(0, h - (i.r.y() - _ty));
442     else
443         mh = min(i.r.height(), h);
444     
445     paintBackground(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
446     
447     if (style()->hasBorder() && !collapseBorders())
448         paintBorder(i.p, _tx, _ty, w, h, style());
449 }
450
451 void RenderTable::calcMinMaxWidth()
452 {
453     KHTMLAssert(!minMaxKnown());
454
455     if (needSectionRecalc)
456         recalcSections();
457
458     tableLayout->calcMinMaxWidth();
459
460     if (tCaption && tCaption->minWidth() > m_minWidth)
461         m_minWidth = tCaption->minWidth();
462
463     setMinMaxKnown();
464 }
465
466 void RenderTable::splitColumn(int pos, int firstSpan)
467 {
468     // we need to add a new columnStruct
469     int oldSize = columns.size();
470     columns.resize(oldSize + 1);
471     int oldSpan = columns[pos].span;
472     KHTMLAssert(oldSpan > firstSpan);
473     columns[pos].span = firstSpan;
474     memmove(columns.data() + pos + 1, columns.data() + pos, (oldSize-pos)*sizeof(ColumnStruct));
475     columns[pos+1].span = oldSpan - firstSpan;
476
477     // change width of all rows.
478     for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
479         if (child->isTableSection()) {
480             RenderTableSection *section = static_cast<RenderTableSection *>(child);
481             if (section->cCol > pos)
482                 section->cCol++;
483             int size = section->numRows();
484             for (int row = 0; row < size; ++row) {
485                 section->grid[row].row->resize(oldSize + 1);
486                 RenderTableSection::Row &r = *section->grid[row].row;
487                 memmove(r.data() + pos + 1, r.data() + pos, (oldSize - pos) * sizeof(RenderTableSection::CellStruct));
488                 r[pos + 1].cell = 0;
489                 r[pos + 1].inColSpan = r[pos].inColSpan || r[pos].cell;
490             }
491         }
492     }
493     columnPos.resize(numEffCols() + 1);
494     setNeedsLayoutAndMinMaxRecalc();
495 }
496
497 void RenderTable::appendColumn(int span)
498 {
499     // easy case.
500     int pos = columns.size();
501     int newSize = pos + 1;
502     columns.resize(newSize);
503     columns[pos].span = span;
504
505     // change width of all rows.
506     for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
507         if (child->isTableSection()) {
508             RenderTableSection *section = static_cast<RenderTableSection *>(child);
509             int size = section->numRows();
510             for (int row = 0; row < size; ++row) {
511                 section->grid[row].row->resize(newSize);
512                 RenderTableSection::CellStruct& c = section->cellAt(row, pos);
513                 c.cell = 0;
514                 c.inColSpan = false;
515             }
516         }
517     }
518     columnPos.resize(numEffCols() + 1);
519     setNeedsLayoutAndMinMaxRecalc();
520 }
521
522 RenderTableCol *RenderTable::colElement(int col)
523 {
524     if (!has_col_elems)
525         return 0;
526     RenderObject *child = firstChild();
527     int cCol = 0;
528     while (child) {
529         if (child->isTableCol()) {
530             RenderTableCol *colElem = static_cast<RenderTableCol *>(child);
531             int span = colElem->span();
532             if (!colElem->firstChild()) {
533                 cCol += span;
534                 if (cCol > col)
535                     return colElem;
536             }
537
538             RenderObject *next = child->firstChild();
539             if (!next)
540                 next = child->nextSibling();
541             if (!next && child->parent()->isTableCol())
542                 next = child->parent()->nextSibling();
543             child = next;
544         } else if (child == tCaption)
545             child = child->nextSibling();
546         else
547             break;
548     }
549     return 0;
550 }
551
552 void RenderTable::recalcSectionsIfNeeded()
553 {
554     if (needSectionRecalc)
555         recalcSections();
556 }
557
558 void RenderTable::recalcSections()
559 {
560     tCaption = 0;
561     head = foot = firstBody = 0;
562     has_col_elems = false;
563
564     // We need to get valid pointers to caption, head, foot and firstbody again
565     for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
566         switch (child->style()->display()) {
567             case TABLE_CAPTION:
568                 if (!tCaption && child->isRenderBlock()) {
569                     tCaption = static_cast<RenderBlock*>(child);
570                     tCaption->setNeedsLayout(true);
571                 }
572                 break;
573             case TABLE_COLUMN:
574             case TABLE_COLUMN_GROUP:
575                 has_col_elems = true;
576                 break;
577             case TABLE_HEADER_GROUP:
578                 if (child->isTableSection()) {
579                     RenderTableSection *section = static_cast<RenderTableSection *>(child);
580                     if (!head)
581                         head = section;
582                     else if (!firstBody)
583                         firstBody = section;
584                     if (section->needCellRecalc)
585                         section->recalcCells();
586                 }
587                 break;
588             case TABLE_FOOTER_GROUP:
589                 if (child->isTableSection()) {
590                     RenderTableSection *section = static_cast<RenderTableSection *>(child);
591                     if (!foot)
592                         foot = section;
593                     else if (!firstBody)
594                         firstBody = section;
595                     if (section->needCellRecalc)
596                         section->recalcCells();
597                 }
598                 break;
599             case TABLE_ROW_GROUP:
600                 if (child->isTableSection()) {
601                     RenderTableSection *section = static_cast<RenderTableSection *>(child);
602                     if (!firstBody)
603                         firstBody = section;
604                     if (section->needCellRecalc)
605                         section->recalcCells();
606                 }
607                 break;
608             default:
609                 break;
610         }
611     }
612
613     // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
614     int maxCols = 0;
615     for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
616         if (child->isTableSection()) {
617             RenderTableSection *section = static_cast<RenderTableSection *>(child);
618             int sectionCols = section->numColumns();
619             if (sectionCols > maxCols)
620                 maxCols = sectionCols;
621         }
622     }
623     
624     columns.resize(maxCols);
625     columnPos.resize(maxCols+1);
626     
627     needSectionRecalc = false;
628     setNeedsLayout(true);
629 }
630
631 RenderObject* RenderTable::removeChildNode(RenderObject* child)
632 {
633     setNeedSectionRecalc();
634     return RenderContainer::removeChildNode(child);
635 }
636
637 int RenderTable::borderLeft() const
638 {
639     if (collapseBorders()) {
640         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
641         // but I'm working to get this changed.  For now, follow the spec.
642         return 0;
643     }
644     return RenderBlock::borderLeft();
645 }
646     
647 int RenderTable::borderRight() const
648 {
649     if (collapseBorders()) {
650         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
651         // but I'm working to get this changed.  For now, follow the spec.
652         return 0;
653     }
654     return RenderBlock::borderRight();
655 }
656
657 int RenderTable::borderTop() const
658 {
659     if (collapseBorders()) {
660         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
661         // but I'm working to get this changed.  For now, follow the spec.
662         return 0;
663     }
664     return RenderBlock::borderTop();
665 }
666
667 int RenderTable::borderBottom() const
668 {
669     if (collapseBorders()) {
670         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
671         // but I'm working to get this changed.  For now, follow the spec.
672         return 0;
673     }
674     return RenderBlock::borderBottom();
675 }
676
677 RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
678 {
679     // Find the section and row to look in
680     int r = cell->row();
681     RenderTableSection* section = 0;
682     int rAbove = -1;
683     if (r > 0) {
684         // cell is not in the first row, so use the above row in its own section
685         section = cell->section();
686         rAbove = r-1;
687     } else {
688         // cell is at top of a section, use last row in previous section
689         for (RenderObject *prevSection = cell->section()->previousSibling();
690              prevSection && rAbove < 0;
691              prevSection = prevSection->previousSibling()) {
692             if (prevSection->isTableSection()) {
693                 section = static_cast<RenderTableSection *>(prevSection);
694                 if (section->numRows() > 0)
695                     rAbove = section->numRows()-1;
696             }
697         }
698     }
699
700     // Look up the cell in the section's grid, which requires effective col index
701     if (section && rAbove >= 0) {
702         int effCol = colToEffCol(cell->col());
703         RenderTableSection::CellStruct aboveCell;
704         // If we hit a span back up to a real cell.
705         do {
706             aboveCell = section->cellAt(rAbove, effCol);
707             effCol--;
708         } while (!aboveCell.cell && aboveCell.inColSpan && effCol >=0);
709         return aboveCell.cell;
710     } else
711         return 0;
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             if (nextSection->isTableSection()) {
730                 section = static_cast<RenderTableSection *>(nextSection);
731                 if (section->numRows() > 0)
732                     rBelow = 0;
733             }
734         }
735     }
736     
737     // Look up the cell in the section's grid, which requires effective col index
738     if (section && rBelow >= 0) {
739         int effCol = colToEffCol(cell->col());
740         RenderTableSection::CellStruct belowCell;
741         // If we hit a colspan back up to a real cell.
742         do {
743             belowCell = section->cellAt(rBelow, effCol);
744             effCol--;
745         } while (!belowCell.cell && belowCell.inColSpan && effCol >=0);
746         return belowCell.cell;
747     } else
748         return 0;
749 }
750
751 RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
752 {
753     RenderTableSection* section = cell->section();
754     int effCol = colToEffCol(cell->col());
755     if (effCol == 0)
756         return 0;
757     
758     // If we hit a colspan back up to a real cell.
759     RenderTableSection::CellStruct prevCell;
760     do {
761         prevCell = section->cellAt(cell->row(), effCol-1);
762         effCol--;
763     } while (!prevCell.cell && prevCell.inColSpan && effCol >=0);
764     return prevCell.cell;
765 }
766
767 RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const
768 {
769     int effCol = colToEffCol(cell->col() + cell->colSpan());
770     if (effCol >= numEffCols())
771         return 0;
772     return cell->section()->cellAt(cell->row(), effCol).cell;
773 }
774
775 RenderBlock* RenderTable::firstLineBlock() const
776 {
777     return 0;
778 }
779
780 void RenderTable::updateFirstLetter()
781 {
782 }
783
784 IntRect RenderTable::getOverflowClipRect(int tx, int ty)
785 {
786     IntRect rect = RenderBlock::getOverflowClipRect(tx, ty);
787     
788     // If we have a caption, expand the clip to include the caption.
789     // FIXME: Technically this is wrong, but it's virtually impossible to fix this
790     // for real until captions have been re-written.
791     // FIXME: This code assumes (like all our other caption code) that only top/bottom are
792     // supported.  When we actually support left/right and stop mapping them to top/bottom,
793     // we might have to hack this code first (depending on what order we do these bug fixes in).
794     if (tCaption) {
795         rect.setHeight(height());
796         rect.setY(ty);
797     }
798
799     return rect;
800 }
801
802 #ifndef NDEBUG
803 void RenderTable::dump(QTextStream *stream, DeprecatedString ind) const
804 {
805     if (tCaption)
806         *stream << " tCaption";
807     if (head)
808         *stream << " head";
809     if (foot)
810         *stream << " foot";
811
812     *stream << endl << ind << "cspans:";
813     for ( unsigned int i = 0; i < columns.size(); i++ )
814         *stream << " " << columns[i].span;
815     *stream << endl << ind;
816
817     RenderBlock::dump(stream,ind);
818 }
819 #endif
820
821 }