2006-05-20 Mitz Pettel <opendarwin.org@mitzpettel.com>
[WebKit-https.git] / WebCore / rendering / RenderTableSection.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 "RenderTableSection.h"
30 #include "RenderTableCell.h"
31 #include "RenderTableCol.h"
32 #include "RenderTableRow.h"
33 #include "RenderTableCol.h"
34 #include "Document.h"
35 #include "HTMLNames.h"
36 #include "KWQTextStream.h"
37
38 using namespace std;
39
40 namespace WebCore {
41
42 using namespace HTMLNames;
43
44 RenderTableSection::RenderTableSection(Node* node)
45     : RenderContainer(node)
46 {
47     // init RenderObject attributes
48     setInline(false);   // our object is not Inline
49     gridRows = 0;
50     cCol = 0;
51     cRow = -1;
52     needCellRecalc = false;
53     m_outerBorderLeft = 0;
54     m_outerBorderRight = 0;
55     m_outerBorderTop = 0;
56     m_outerBorderBottom = 0;
57 }
58
59 RenderTableSection::~RenderTableSection()
60 {
61     clearGrid();
62 }
63
64 void RenderTableSection::destroy()
65 {
66     // recalc cell info because RenderTable has unguarded pointers
67     // stored that point to this RenderTableSection.
68     if (table())
69         table()->setNeedSectionRecalc();
70
71     RenderContainer::destroy();
72 }
73
74 void RenderTableSection::setStyle(RenderStyle* _style)
75 {
76     // we don't allow changing this one
77     if (style())
78         _style->setDisplay(style()->display());
79     else if (_style->display() != TABLE_FOOTER_GROUP && _style->display() != TABLE_HEADER_GROUP)
80         _style->setDisplay(TABLE_ROW_GROUP);
81
82     RenderContainer::setStyle(_style);
83 }
84
85 void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild)
86 {
87     bool isTableSection = element() && (element()->hasTagName(theadTag) || element()->hasTagName(tbodyTag) || element()->hasTagName(tfootTag));
88
89     if (!child->isTableRow()) {
90         if (isTableSection && child->element() && child->element()->hasTagName(formTag)) {
91             RenderContainer::addChild(child, beforeChild);
92             return;
93         }
94
95         RenderObject* last = beforeChild;
96         if (!last)
97             last = lastChild();
98         if (last && last->isAnonymous()) {
99             last->addChild(child);
100             return;
101         }
102
103         // If beforeChild is inside an anonymous cell/row, insert into the cell or into
104         // the anonymous row containing it, if there is one.
105         RenderObject* lastBox = last;
106         while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow())
107             lastBox = lastBox->parent();
108         if (lastBox && lastBox->isAnonymous()) {
109             lastBox->addChild(child, beforeChild);
110             return;
111         }
112
113         RenderObject* row = new (renderArena()) RenderTableRow(document() /* anonymous table */);
114         RenderStyle* newStyle = new (renderArena()) RenderStyle();
115         newStyle->inheritFrom(style());
116         newStyle->setDisplay(TABLE_ROW);
117         row->setStyle(newStyle);
118         addChild(row, beforeChild);
119         row->addChild(child);
120         return;
121     }
122
123     if (beforeChild)
124         setNeedCellRecalc();
125
126     ++cRow;
127     cCol = 0;
128
129     ensureRows(cRow + 1);
130
131     if (!beforeChild) {
132         grid[cRow].height = child->style()->height();
133         if (grid[cRow].height.isRelative())
134             grid[cRow].height = Length();
135     }
136
137     RenderContainer::addChild(child, beforeChild);
138 }
139
140 bool RenderTableSection::ensureRows(int numRows)
141 {
142     int nRows = gridRows;
143     if (numRows > nRows) {
144         if (numRows > static_cast<int>(grid.size()))
145             if (!grid.resize(numRows*2+1))
146                 return false;
147
148         gridRows = numRows;
149         int nCols = table()->numEffCols();
150         CellStruct emptyCellStruct;
151         emptyCellStruct.cell = 0;
152         emptyCellStruct.inColSpan = false;
153         for (int r = nRows; r < numRows; r++) {
154             grid[r].row = new Row(nCols);
155             grid[r].row->fill(emptyCellStruct);
156             grid[r].rowRenderer = 0;
157             grid[r].baseLine = 0;
158             grid[r].height = Length();
159         }
160     }
161
162     return true;
163 }
164
165 void RenderTableSection::addCell(RenderTableCell *cell, RenderObject* row)
166 {
167     int rSpan = cell->rowSpan();
168     int cSpan = cell->colSpan();
169     DeprecatedArray<RenderTable::ColumnStruct> &columns = table()->columns;
170     int nCols = columns.size();
171
172     // ### mozilla still seems to do the old HTML way, even for strict DTD
173     // (see the annotation on table cell layouting in the CSS specs and the testcase below:
174     // <TABLE border>
175     // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
176     // <TR><TD colspan="2">5
177     // </TABLE>
178
179     while (cCol < nCols && (cellAt(cRow, cCol).cell || cellAt(cRow, cCol).inColSpan))
180         cCol++;
181
182     if (rSpan == 1) {
183         // we ignore height settings on rowspan cells
184         Length height = cell->style()->height();
185         if (height.value() > 0 || (height.isRelative() && height.value() >= 0)) {
186             Length cRowHeight = grid[cRow].height;
187             switch (height.type()) {
188                 case Percent:
189                     if (!(cRowHeight.isPercent()) ||
190                         (cRowHeight.isPercent() && cRowHeight.value() < height.value()))
191                         grid[cRow].height = height;
192                         break;
193                 case Fixed:
194                     if (cRowHeight.type() < Percent ||
195                         (cRowHeight.isFixed() && cRowHeight.value() < height.value()))
196                         grid[cRow].height = height;
197                     break;
198                 case Relative:
199                 default:
200                     break;
201             }
202         }
203     }
204
205     // make sure we have enough rows
206     if (!ensureRows(cRow + rSpan))
207         return;
208
209     grid[cRow].rowRenderer = row;
210
211     int col = cCol;
212     // tell the cell where it is
213     CellStruct currentCell;
214     currentCell.cell = cell;
215     currentCell.inColSpan = false;
216     while (cSpan) {
217         int currentSpan;
218         if (cCol >= nCols) {
219             table()->appendColumn(cSpan);
220             currentSpan = cSpan;
221         } else {
222             if (cSpan < columns[cCol].span)
223                 table()->splitColumn(cCol, cSpan);
224             currentSpan = columns[cCol].span;
225         }
226
227         for (int r = 0; r < rSpan; r++) {
228             CellStruct& c = cellAt(cRow + r, cCol);
229             if (currentCell.cell && !c.cell)
230                 c.cell = currentCell.cell;
231             if (currentCell.inColSpan)
232                 c.inColSpan = true;
233         }
234         cCol++;
235         cSpan -= currentSpan;
236         currentCell.cell = 0;
237         currentCell.inColSpan = true;
238     }
239     if (cell) {
240         cell->setRow(cRow);
241         cell->setCol(table()->effColToCol(col));
242     }
243 }
244
245
246
247 void RenderTableSection::setCellWidths()
248 {
249     DeprecatedArray<int> &columnPos = table()->columnPos;
250
251     int rows = gridRows;
252     for (int i = 0; i < rows; i++) {
253         Row &row = *grid[i].row;
254         int cols = row.size();
255         for (int j = 0; j < cols; j++) {
256             CellStruct current = row[j];
257             RenderTableCell *cell = current.cell;
258
259             if (!cell)
260                 continue;
261             int endCol = j;
262             int cspan = cell->colSpan();
263             while (cspan && endCol < cols) {
264                 cspan -= table()->columns[endCol].span;
265                 endCol++;
266             }
267             int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing();
268             int oldWidth = cell->width();
269             if (w != oldWidth) {
270                 bool neededLayout = cell->selfNeedsLayout();
271                 cell->setNeedsLayout(true);
272                 if (!neededLayout && !selfNeedsLayout() && cell->checkForRepaintDuringLayout())
273                     cell->repaintObjectsBeforeLayout();
274                 cell->setWidth(w);
275             }
276         }
277     }
278 }
279
280
281 void RenderTableSection::calcRowHeight()
282 {
283     int indx;
284     RenderTableCell *cell;
285
286     int totalRows = gridRows;
287     int spacing = table()->vBorderSpacing();
288
289     rowPos.resize(totalRows + 1);
290     rowPos[0] = spacing;
291
292     for (int r = 0; r < totalRows; r++) {
293         rowPos[r + 1] = 0;
294
295         int baseline = 0;
296         int bdesc = 0;
297         int ch = grid[r].height.calcMinValue(0);
298         int pos = rowPos[r + 1] + ch + spacing;
299
300         if (pos > rowPos[r + 1])
301             rowPos[r + 1] = pos;
302
303         Row *row = grid[r].row;
304         int totalCols = row->size();
305         int totalRows = gridRows;
306
307         for (int c = 0; c < totalCols; c++) {
308             CellStruct current = cellAt(r, c);
309             cell = current.cell;
310             if (!cell || current.inColSpan)
311                 continue;
312             if (r < totalRows - 1 && cellAt(r + 1, c).cell == cell)
313                 continue;
314
315             if ((indx = r - cell->rowSpan() + 1) < 0)
316                 indx = 0;
317
318             if (cell->overrideSize() != -1) {
319                 cell->setOverrideSize(-1);
320                 cell->setChildNeedsLayout(true, false);
321                 cell->layoutIfNeeded();
322             }
323             
324             // Explicit heights use the border box in quirks mode.  In strict mode do the right
325             // thing and actually add in the border and padding.
326             ch = cell->style()->height().calcValue(0) + 
327                 (cell->style()->htmlHacks() ? 0 : (cell->paddingTop() + cell->paddingBottom() +
328                                                    cell->borderTop() + cell->borderBottom()));
329             if (cell->height() > ch)
330                 ch = cell->height();
331
332             pos = rowPos[ indx ] + ch + spacing;
333
334             if (pos > rowPos[r + 1])
335                 rowPos[r + 1] = pos;
336
337             // find out the baseline
338             EVerticalAlign va = cell->style()->verticalAlign();
339             if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP
340                 || va == SUPER || va == SUB) {
341                 int b = cell->baselinePosition();
342                 if (b > cell->borderTop() + cell->paddingTop()) {
343                     if (b > baseline)
344                         baseline = b;
345
346                     int td = rowPos[indx] + ch - b;
347                     if (td > bdesc)
348                         bdesc = td;
349                 }
350             }
351         }
352
353         //do we have baseline aligned elements?
354         if (baseline) {
355             // increase rowheight if baseline requires
356             int bRowPos = baseline + bdesc  + spacing ; // + 2*padding
357             if (rowPos[r + 1] < bRowPos)
358                 rowPos[r + 1] = bRowPos;
359
360             grid[r].baseLine = baseline;
361         }
362
363         if (rowPos[r + 1] < rowPos[r])
364             rowPos[r + 1] = rowPos[r];
365     }
366 }
367
368 int RenderTableSection::layoutRows(int toAdd)
369 {
370     int rHeight;
371     int rindx;
372     int totalRows = gridRows;
373     int hspacing = table()->hBorderSpacing();
374     int vspacing = table()->vBorderSpacing();
375     
376     // Set the width of our section now.  The rows will also be this width.
377     m_width = table()->contentWidth();
378     if (table()->collapseBorders())
379         recalcOuterBorder();
380     
381     if (toAdd && totalRows && (rowPos[totalRows] || !nextSibling())) {
382
383         int totalHeight = rowPos[totalRows] + toAdd;
384
385         int dh = toAdd;
386         int totalPercent = 0;
387         int numAuto = 0;
388         for (int r = 0; r < totalRows; r++) {
389             if (grid[r].height.isAuto())
390                 numAuto++;
391             else if (grid[r].height.isPercent())
392                 totalPercent += grid[r].height.value();
393         }
394         if (totalPercent) {
395             // try to satisfy percent
396             int add = 0;
397             if (totalPercent > 100)
398                 totalPercent = 100;
399             int rh = rowPos[1] - rowPos[0];
400             for (int r = 0; r < totalRows; r++) {
401                 if (totalPercent > 0 && grid[r].height.isPercent()) {
402                     int toAdd = min(dh, (totalHeight * grid[r].height.value() / 100) - rh);
403                     // If toAdd is negative, then we don't want to shrink the row (this bug
404                     // affected Outlook Web Access).
405                     toAdd = max(0, toAdd);
406                     add += toAdd;
407                     dh -= toAdd;
408                     totalPercent -= grid[r].height.value();
409                 }
410                 if (r < totalRows - 1)
411                     rh = rowPos[r + 2] - rowPos[r + 1];
412                 rowPos[r + 1] += add;
413             }
414         }
415         if (numAuto) {
416             // distribute over variable cols
417             int add = 0;
418             for (int r = 0; r < totalRows; r++) {
419                 if (numAuto > 0 && grid[r].height.isAuto()) {
420                     int toAdd = dh/numAuto;
421                     add += toAdd;
422                     dh -= toAdd;
423                     numAuto--;
424                 }
425                 rowPos[r + 1] += add;
426             }
427         }
428         if (dh > 0 && rowPos[totalRows]) {
429             // if some left overs, distribute equally.
430             int tot = rowPos[totalRows];
431             int add = 0;
432             int prev = rowPos[0];
433             for (int r = 0; r < totalRows; r++) {
434                 //weight with the original height
435                 add += dh * (rowPos[r + 1] - prev) / tot;
436                 prev = rowPos[r + 1];
437                 rowPos[r + 1] += add;
438             }
439         }
440     }
441
442     int leftOffset = hspacing;
443
444     int nEffCols = table()->numEffCols();
445     for (int r = 0; r < totalRows; r++) {
446         Row *row = grid[r].row;
447         int totalCols = row->size();
448         
449         // Set the row's x/y position and width/height.
450         if (grid[r].rowRenderer) {
451             grid[r].rowRenderer->setPos(0, rowPos[r]);
452             grid[r].rowRenderer->setWidth(m_width);
453             grid[r].rowRenderer->setHeight(rowPos[r+1] - rowPos[r] - vspacing);
454         }
455
456         for (int c = 0; c < nEffCols; c++) {
457             CellStruct current = cellAt(r, c);
458             RenderTableCell* cell = current.cell;
459             
460             if (!cell)
461                 continue;
462             if (r < totalRows - 1 && cell == cellAt(r + 1, c).cell)
463                 continue;
464
465             if ((rindx = r-cell->rowSpan() + 1) < 0)
466                 rindx = 0;
467
468             rHeight = rowPos[r + 1] - rowPos[rindx] - vspacing;
469             
470             // Force percent height children to lay themselves out again.
471             // This will cause these children to grow to fill the cell.
472             // FIXME: There is still more work to do here to fully match WinIE (should
473             // it become necessary to do so).  In quirks mode, WinIE behaves like we
474             // do, but it will clip the cells that spill out of the table section.  In
475             // strict mode, Mozilla and WinIE both regrow the table to accommodate the
476             // new height of the cell (thus letting the percentages cause growth one
477             // time only).  We may also not be handling row-spanning cells correctly.
478             //
479             // Note also the oddity where replaced elements always flex, and yet blocks/tables do
480             // not necessarily flex.  WinIE is crazy and inconsistent, and we can't hope to
481             // match the behavior perfectly, but we'll continue to refine it as we discover new
482             // bugs. :)
483             bool cellChildrenFlex = false;
484             bool flexAllChildren = cell->style()->height().isFixed() || 
485                 (!table()->style()->height().isAuto() && rHeight != cell->height());
486
487             for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) {
488                 if (!o->isText() && o->style()->height().isPercent() && (o->isReplaced() || o->scrollsOverflow() || flexAllChildren)) {
489                     // Tables with no sections do not flex.
490                     if (!o->isTable() || static_cast<RenderTable*>(o)->hasSections()) {
491                         o->setNeedsLayout(true, false);
492                         cell->setChildNeedsLayout(true, false);
493                         cellChildrenFlex = true;
494                     }
495                 }
496             }
497             if (cellChildrenFlex) {
498                 // Alignment within a cell is based off the calculated
499                 // height, which becomes irrelevant once the cell has
500                 // been resized based off its percentage. -dwh
501                 cell->setCellTopExtra(0);
502                 cell->setCellBottomExtra(0);
503
504                 cell->setOverrideSize(max(0, 
505                                            rHeight - cell->borderTop() - cell->paddingTop() - 
506                                                      cell->borderBottom() - cell->paddingBottom()));
507                 cell->layoutIfNeeded();
508             } else {
509                 EVerticalAlign va = cell->style()->verticalAlign();
510                 int te = 0;
511                 switch (va) {
512                     case SUB:
513                     case SUPER:
514                     case TEXT_TOP:
515                     case TEXT_BOTTOM:
516                     case BASELINE:
517                         te = getBaseline(r) - cell->baselinePosition() ;
518                         break;
519                     case TOP:
520                         te = 0;
521                         break;
522                     case MIDDLE:
523                         te = (rHeight - cell->height())/2;
524                         break;
525                     case BOTTOM:
526                         te = rHeight - cell->height();
527                         break;
528                     default:
529                         break;
530                 }
531                 
532                 int oldTe = cell->borderTopExtra();
533                 int oldBe = cell->borderBottomExtra();
534                 
535                 int be = rHeight - cell->height() - te;
536                 cell->setCellTopExtra(te);
537                 cell->setCellBottomExtra(be);
538                 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout() && (te != oldTe || be > oldBe))
539                     cell->repaint();
540             }
541             
542             int oldCellX = cell->xPos();
543             int oldCellY = cell->yPos() - cell->borderTopExtra();
544         
545             if (style()->direction() == RTL) {
546                 cell->setPos(
547                     table()->columnPos[(int)totalCols] -
548                     table()->columnPos[table()->colToEffCol(cell->col()+cell->colSpan())] +
549                     leftOffset,
550                     rowPos[rindx]);
551             } else
552                 cell->setPos(table()->columnPos[c] + leftOffset, rowPos[rindx]);
553
554             // If the cell moved, we have to repaint it as well as any floating/positioned
555             // descendants.  An exception is if we need a layout.  In this case, we know we're going to
556             // repaint ourselves (and the cell) anyway.
557             if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
558                 cell->repaintDuringLayoutIfMoved(oldCellX, oldCellY);
559         }
560     }
561
562     m_height = rowPos[totalRows];
563     return m_height;
564 }
565
566 int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
567 {
568     int bottom = RenderContainer::lowestPosition(includeOverflowInterior, includeSelf);
569     if (!includeOverflowInterior && hasOverflowClip())
570         return bottom;
571
572     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
573         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
574             if (cell->isTableCell()) {
575                 int bp = cell->yPos() + cell->lowestPosition(false);
576                 bottom = max(bottom, bp);
577         }
578     }
579     
580     return bottom;
581 }
582
583 int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
584 {
585     int right = RenderContainer::rightmostPosition(includeOverflowInterior, includeSelf);
586     if (!includeOverflowInterior && hasOverflowClip())
587         return right;
588
589     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
590         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
591             if (cell->isTableCell()) {
592                 int rp = cell->xPos() + cell->rightmostPosition(false);
593                 right = max(right, rp);
594         }
595     }
596     
597     return right;
598 }
599
600 int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
601 {
602     int left = RenderContainer::leftmostPosition(includeOverflowInterior, includeSelf);
603     if (!includeOverflowInterior && hasOverflowClip())
604         return left;
605     
606     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
607         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
608             if (cell->isTableCell()) {
609                 int lp = cell->xPos() + cell->leftmostPosition(false);
610                 left = min(left, lp);
611         }
612     }
613     
614     return left;
615 }
616
617 int RenderTableSection::calcOuterBorderTop() const
618 {
619     int totalCols = table()->numEffCols();
620     if (gridRows == 0 || totalCols == 0)
621         return 0;
622
623     unsigned borderWidth = 0;
624
625     const BorderValue& sb = style()->borderTop();
626     if (sb.style() == BHIDDEN)
627         return -1;
628     if (sb.style() > BHIDDEN)
629         borderWidth = sb.width;
630
631     const BorderValue& rb = firstChild()->style()->borderTop();
632     if (rb.style() == BHIDDEN)
633         return -1;
634     if (rb.style() > BHIDDEN && rb.width > borderWidth)
635         borderWidth = rb.width;
636
637     bool allHidden = true;
638     for (int c = 0; c < totalCols; c++) {
639         const CellStruct& current = cellAt(0, c);
640         if (current.inColSpan || !current.cell)
641             continue;
642         const BorderValue& cb = current.cell->style()->borderTop();
643         // FIXME: Don't repeat for the same col group
644         RenderTableCol* colGroup = table()->colElement(c);
645         if (colGroup) {
646             const BorderValue& gb = colGroup->style()->borderTop();
647             if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
648                 continue;
649             else
650                 allHidden = false;
651             if (gb.style() > BHIDDEN && gb.width > borderWidth)
652                 borderWidth = gb.width;
653             if (cb.style() > BHIDDEN && cb.width > borderWidth)
654                 borderWidth = cb.width;
655         } else {
656             if (cb.style() == BHIDDEN)
657                 continue;
658             else
659                 allHidden = false;
660             if (cb.style() > BHIDDEN && cb.width > borderWidth)
661                 borderWidth = cb.width;
662         }
663     }
664     if (allHidden)
665         return -1;
666
667     return borderWidth / 2;
668 }
669
670 int RenderTableSection::calcOuterBorderBottom() const
671 {
672     int totalCols = table()->numEffCols();
673     if (gridRows == 0 || totalCols == 0)
674         return 0;
675
676     unsigned borderWidth = 0;
677
678     const BorderValue& sb = style()->borderBottom();
679     if (sb.style() == BHIDDEN)
680         return -1;
681     if (sb.style() > BHIDDEN)
682         borderWidth = sb.width;
683
684     const BorderValue& rb = lastChild()->style()->borderBottom();
685     if (rb.style() == BHIDDEN)
686         return -1;
687     if (rb.style() > BHIDDEN && rb.width > borderWidth)
688         borderWidth = rb.width;
689
690     bool allHidden = true;
691     for (int c = 0; c < totalCols; c++) {
692         const CellStruct& current = cellAt(gridRows - 1, c);
693         if (current.inColSpan || !current.cell)
694             continue;
695         const BorderValue& cb = current.cell->style()->borderBottom();
696         // FIXME: Don't repeat for the same col group
697         RenderTableCol* colGroup = table()->colElement(c);
698         if (colGroup) {
699             const BorderValue& gb = colGroup->style()->borderBottom();
700             if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
701                 continue;
702             else
703                 allHidden = false;
704             if (gb.style() > BHIDDEN && gb.width > borderWidth)
705                 borderWidth = gb.width;
706             if (cb.style() > BHIDDEN && cb.width > borderWidth)
707                 borderWidth = cb.width;
708         } else {
709             if (cb.style() == BHIDDEN)
710                 continue;
711             else
712                 allHidden = false;
713             if (cb.style() > BHIDDEN && cb.width > borderWidth)
714                 borderWidth = cb.width;
715         }
716     }
717     if (allHidden)
718         return -1;
719
720     return (borderWidth + 1) / 2;
721 }
722
723 int RenderTableSection::calcOuterBorderLeft(bool rtl) const
724 {
725     int totalCols = table()->numEffCols();
726     if (gridRows == 0 || totalCols == 0)
727         return 0;
728
729     unsigned borderWidth = 0;
730
731     const BorderValue& sb = style()->borderLeft();
732     if (sb.style() == BHIDDEN)
733         return -1;
734     if (sb.style() > BHIDDEN)
735         borderWidth = sb.width;
736
737     int leftmostColumn = rtl ? totalCols - 1 : 0;
738     RenderTableCol* colGroup = table()->colElement(leftmostColumn);
739     if (colGroup) {
740         const BorderValue& gb = colGroup->style()->borderLeft();
741         if (gb.style() == BHIDDEN)
742             return -1;
743         if (gb.style() > BHIDDEN && gb.width > borderWidth)
744             borderWidth = gb.width;
745     }
746
747     bool allHidden = true;
748     for (int r = 0; r < gridRows; r++) {
749         const CellStruct& current = cellAt(r, leftmostColumn);
750         if (!current.cell)
751             continue;
752         // FIXME: Don't repeat for the same cell
753         const BorderValue& cb = current.cell->style()->borderLeft();
754         const BorderValue& rb = current.cell->parent()->style()->borderLeft();
755         if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
756             continue;
757         else
758             allHidden = false;
759         if (cb.style() > BHIDDEN && cb.width > borderWidth)
760             borderWidth = cb.width;
761         if (rb.style() > BHIDDEN && rb.width > borderWidth)
762             borderWidth = rb.width;
763     }
764     if (allHidden)
765         return -1;
766
767     return borderWidth / 2;
768 }
769
770 int RenderTableSection::calcOuterBorderRight(bool rtl) const
771 {
772     int totalCols = table()->numEffCols();
773     if (gridRows == 0 || totalCols == 0)
774         return 0;
775
776     unsigned borderWidth = 0;
777
778     const BorderValue& sb = style()->borderRight();
779     if (sb.style() == BHIDDEN)
780         return -1;
781     if (sb.style() > BHIDDEN)
782         borderWidth = sb.width;
783
784     int rightmostColumn = rtl ? 0 : totalCols - 1;
785     RenderTableCol* colGroup = table()->colElement(rightmostColumn);
786     if (colGroup) {
787         const BorderValue& gb = colGroup->style()->borderRight();
788         if (gb.style() == BHIDDEN)
789             return -1;
790         if (gb.style() > BHIDDEN && gb.width > borderWidth)
791             borderWidth = gb.width;
792     }
793
794     bool allHidden = true;
795     for (int r = 0; r < gridRows; r++) {
796         const CellStruct& current = cellAt(r, rightmostColumn);
797         if (!current.cell)
798             continue;
799         // FIXME: Don't repeat for the same cell
800         const BorderValue& cb = current.cell->style()->borderRight();
801         const BorderValue& rb = current.cell->parent()->style()->borderRight();
802         if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
803             continue;
804         else
805             allHidden = false;
806         if (cb.style() > BHIDDEN && cb.width > borderWidth)
807             borderWidth = cb.width;
808         if (rb.style() > BHIDDEN && rb.width > borderWidth)
809             borderWidth = rb.width;
810     }
811     if (allHidden)
812         return -1;
813
814     return (borderWidth + 1) / 2;
815 }
816
817 void RenderTableSection::recalcOuterBorder()
818 {
819     bool rtl = table()->style()->direction() == RTL;
820     m_outerBorderTop = calcOuterBorderTop();
821     m_outerBorderBottom = calcOuterBorderBottom();
822     m_outerBorderLeft = calcOuterBorderLeft(rtl);
823     m_outerBorderRight = calcOuterBorderRight(rtl);
824 }
825
826
827 void RenderTableSection::paint(PaintInfo& i, int tx, int ty)
828 {
829     unsigned int totalRows = gridRows;
830     unsigned int totalCols = table()->columns.size();
831
832     if (totalRows == 0 || totalCols == 0)
833         return;
834
835     tx += m_x;
836     ty += m_y;
837
838     // check which rows and cols are visible and only paint these
839     // ### fixme: could use a binary search here
840     PaintPhase paintPhase = i.phase;
841     int x = i.r.x();
842     int y = i.r.y();
843     int w = i.r.width();
844     int h = i.r.height();
845
846     int os = 2 * maximalOutlineSize(paintPhase);
847     unsigned int startrow = 0;
848     unsigned int endrow = totalRows;
849     for (; startrow < totalRows; startrow++)
850         if (ty + rowPos[startrow+1] >= y - os)
851             break;
852     if (startrow == totalRows && ty + rowPos[totalRows] + table()->outerBorderBottom() >= y - os)
853         startrow--;
854
855     for (; endrow > 0; endrow--)
856         if ( ty + rowPos[endrow-1] <= y + h + os)
857             break;
858     if (endrow == 0 && ty + rowPos[0] - table()->outerBorderTop() <= y + h + os)
859         endrow++;
860
861     unsigned int startcol = 0;
862     unsigned int endcol = totalCols;
863     if (style()->direction() == LTR) {
864         for (; startcol < totalCols; startcol++) {
865             if (tx + table()->columnPos[startcol + 1] >= x - os)
866                 break;
867         }
868         if (startcol == totalCols && tx + table()->columnPos[totalCols] + table()->outerBorderRight() >= x - os)
869             startcol--;
870         
871         for (; endcol > 0; endcol--) {
872             if (tx + table()->columnPos[endcol - 1] <= x + w + os)
873                 break;
874         }
875         if (endcol == 0 && tx + table()->columnPos[0] - table()->outerBorderLeft() <= y + w + os)
876             endcol++;
877     }
878
879     if (startcol < endcol) {
880         // draw the cells
881         for (unsigned int r = startrow; r < endrow; r++) {
882             unsigned int c = startcol;
883             // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it
884             while (c && cellAt(r, c).inColSpan)
885                 c--;
886             for (; c < endcol; c++) {
887                 CellStruct current = cellAt(r, c);
888                 RenderTableCell *cell = current.cell;
889                     
890                 // Cells must always paint in the order in which they appear taking into account
891                 // their upper left originating row/column.  For cells with rowspans, avoid repainting
892                 // if we've already seen the cell.
893                 if (!cell || (r > startrow && (cellAt(r-1, c).cell == cell)))
894                     continue;
895
896                 if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) {
897                     // We need to handle painting a stack of backgrounds.  This stack (from bottom to top) consists of
898                     // the column group, column, row group, row, and then the cell.
899                     RenderObject* col = table()->colElement(c);
900                     RenderObject* colGroup = 0;
901                     if (col) {
902                         RenderStyle *style = col->parent()->style();
903                         if (style->display() == TABLE_COLUMN_GROUP)
904                             colGroup = col->parent();
905                     }
906                     RenderObject* row = cell->parent();
907                     
908                     // Column groups and columns first.
909                     // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
910                     // the stack, since we have already opened a transparency layer (potentially) for the table row group.
911                     // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
912                     // cell.
913                     cell->paintBackgroundsBehindCell(i, tx, ty, colGroup);
914                     cell->paintBackgroundsBehindCell(i, tx, ty, col);
915                     
916                     // Paint the row group next.
917                     cell->paintBackgroundsBehindCell(i, tx, ty, this);
918                     
919                     // Paint the row next, but only if it doesn't have a layer.  If a row has a layer, it will be responsible for
920                     // painting the row background for the cell.
921                     if (!row->layer())
922                         cell->paintBackgroundsBehindCell(i, tx, ty, row);
923                 }
924
925                 if ((!cell->layer() && !cell->parent()->layer()) || i.phase == PaintPhaseCollapsedTableBorders)
926                     cell->paint(i, tx, ty);
927             }
928         }
929     }
930 }
931
932 void RenderTableSection::recalcCells()
933 {
934     cCol = 0;
935     cRow = -1;
936     clearGrid();
937     gridRows = 0;
938
939     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
940         if (row->isTableRow()) {
941             cRow++;
942             cCol = 0;
943             ensureRows(cRow + 1);
944             for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
945                 if (cell->isTableCell())
946                     addCell(static_cast<RenderTableCell *>(cell), row);
947         }
948     }
949     needCellRecalc = false;
950     setNeedsLayout(true);
951 }
952
953 void RenderTableSection::clearGrid()
954 {
955     int rows = gridRows;
956     while (rows--)
957         delete grid[rows].row;
958 }
959
960 int RenderTableSection::numColumns() const
961 {
962     int result = 0;
963     
964     for (int r = 0; r < gridRows; ++r) {
965         for (int c = result; c < table()->numEffCols(); ++c) {
966             const CellStruct& cell = cellAt(r, c);
967             if (cell.cell || cell.inColSpan)
968                 result = c;
969         }
970     }
971     
972     return result + 1;
973 }
974
975 RenderObject* RenderTableSection::removeChildNode(RenderObject* child)
976 {
977     setNeedCellRecalc();
978     return RenderContainer::removeChildNode(child);
979 }
980
981 // Hit Testing
982 bool RenderTableSection::nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction action)
983 {
984     // Table sections cannot ever be hit tested.  Effectively they do not exist.
985     // Just forward to our children always.
986     tx += m_x;
987     ty += m_y;
988
989     for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
990         // FIXME: We have to skip over inline flows, since they can show up inside table rows
991         // at the moment (a demoted inline <form> for example). If we ever implement a
992         // table-specific hit-test method (which we should do for performance reasons anyway),
993         // then we can remove this check.
994         if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, x, y, tx, ty, action)) {
995             setInnerNode(info);
996             return true;
997         }
998     }
999     
1000     return false;
1001 }
1002
1003 #ifndef NDEBUG
1004 void RenderTableSection::dump(QTextStream *stream, DeprecatedString ind) const
1005 {
1006     *stream << endl << ind << "grid=(" << gridRows << "," << table()->numEffCols() << ")" << endl << ind;
1007     for (int r = 0; r < gridRows; r++) {
1008         for (int c = 0; c < table()->numEffCols(); c++) {
1009             if (cellAt( r, c).cell && !cellAt(r, c).inColSpan)
1010                 *stream << "(" << cellAt(r, c).cell->row() << "," << cellAt(r, c).cell->col() << ","
1011                         << cellAt(r, c).cell->rowSpan() << "," << cellAt(r, c).cell->colSpan() << ") ";
1012             else
1013                 *stream << cellAt(r, c).cell << "null cell ";
1014         }
1015         *stream << endl << ind;
1016     }
1017     RenderContainer::dump(stream,ind);
1018 }
1019 #endif
1020
1021 }