2 * This file is part of the DOM implementation for KDE.
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.
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.
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.
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.
29 //#define DEBUG_LAYOUT
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"
40 #include <qapplication.h>
46 #include "khtmlview.h"
48 using namespace khtml;
51 RenderTable::RenderTable(DOM::NodeImpl* node)
56 head = foot = firstBody = 0;
62 has_col_elems = false;
66 needSectionRecalc = false;
69 columnPos.resize( 2 );
72 columns.fill( ColumnStruct() );
77 RenderTable::~RenderTable()
82 void RenderTable::setStyle(RenderStyle *_style)
84 ETableLayout oldTableLayout = style() ? style()->tableLayout() : TAUTO;
85 RenderBlock::setStyle(_style);
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;
92 if ( !tableLayout || style()->tableLayout() != oldTableLayout ) {
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);
100 kdDebug( 6040 ) << "using fixed table layout" << endl;
103 tableLayout = new AutoTableLayout(this);
107 void RenderTable::addChild(RenderObject *child, RenderObject *beforeChild)
110 kdDebug( 6040 ) << renderName() << "(Table)::addChild( " << child->renderName() << ", " <<
111 (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
113 RenderObject *o = child;
115 if (child->element() && child->element()->id() == ID_FORM) {
116 RenderContainer::addChild(child,beforeChild);
120 switch(child->style()->display())
123 tCaption = static_cast<RenderBlock *>(child);
126 case TABLE_COLUMN_GROUP:
127 has_col_elems = true;
129 case TABLE_HEADER_GROUP:
131 head = static_cast<RenderTableSection *>(child);
132 else if ( !firstBody )
133 firstBody = static_cast<RenderTableSection *>(child);
135 case TABLE_FOOTER_GROUP:
137 foot = static_cast<RenderTableSection *>(child);
141 case TABLE_ROW_GROUP:
143 firstBody = static_cast<RenderTableSection *>(child);
146 if ( !beforeChild && lastChild() &&
147 lastChild()->isTableSection() && lastChild()->isAnonymous() ) {
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 );
158 if ( beforeChild && !beforeChild->isTableSection() )
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);
170 child->setNeedsLayoutAndMinMaxRecalc();
173 RenderContainer::addChild(child,beforeChild);
178 void RenderTable::calcWidth()
180 if ( isPositioned() ) {
181 calcAbsoluteHorizontal();
184 RenderBlock *cb = containingBlock();
185 int availableWidth = cb->contentWidth();
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;
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 );
198 // Subtract out any fixed margins from our available width for auto width tables.
200 if (style()->marginLeft().type != Variable)
201 marginTotal += style()->marginLeft().width(availableWidth);
202 if (style()->marginRight().type != Variable)
203 marginTotal += style()->marginRight().width(availableWidth);
205 // Subtract out our margins to get the available content width.
206 int availContentWidth = kMax(0, availableWidth - marginTotal);
208 // Ensure we aren't bigger than our max width or smaller than our min width.
209 m_width = kMin(availContentWidth, m_maxWidth);
212 m_width = kMax(m_width, m_minWidth);
214 // Finally, with our true width determined, compute our margins for real.
217 calcHorizontalMargins(style()->marginLeft(),style()->marginRight(),availableWidth);
220 void RenderTable::layout()
222 KHTMLAssert( needsLayout() );
223 KHTMLAssert( minMaxKnown() );
224 KHTMLAssert( !needSectionRecalc );
226 if (posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
227 // All we have to is lay out our positioned objects.
228 layoutPositionedObjects(true);
229 setNeedsLayout(false);
233 QRect oldBounds, oldFullBounds;
234 bool checkForRepaint = checkForRepaintDuringLayout();
236 getAbsoluteRepaintRectIncludingFloats(oldBounds, oldFullBounds);
238 //kdDebug( 6040 ) << renderName() << "(Table)"<< this << " ::layout0() width=" << width() << ", needsLayout=" << needsLayout() << endl;
240 m_height = m_overflowHeight = 0;
241 initMaxMarginValues();
243 //int oldWidth = m_width;
245 m_overflowWidth = m_width;
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();
255 kdDebug( 6040 ) << renderName() << "(Table)::layout1() width=" << width() << ", marginLeft=" << marginLeft() << " marginRight=" << marginRight() << endl;
260 // layout child objects
261 int calculatedHeight = 0;
263 RenderObject *child = firstChild();
265 if ( child->needsLayout() && !(child->element() && child->element()->id() == ID_FORM))
267 if ( child->isTableSection() ) {
268 static_cast<RenderTableSection *>(child)->calcRowHeight();
269 calculatedHeight += static_cast<RenderTableSection *>(child)->layoutRows( 0 );
271 child = child->nextSibling();
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();
280 int bpTop = borderTop() + (collapseBorders() ? 0 : paddingTop());
281 int bpBottom = borderBottom() + (collapseBorders() ? 0 : paddingBottom());
285 int oldHeight = m_height;
287 int newHeight = m_height;
288 m_height = oldHeight;
290 Length h = style()->height();
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);
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
307 firstBody->calcRowHeight();
308 firstBody->layoutRows( th - calculatedHeight );
310 else if (!style()->htmlHacks()) {
311 // Completely empty tables (with no sections or anything) should at least honor specified height
317 int bl = borderLeft();
318 if (!collapseBorders())
321 // position the table sections
323 head->setPos(bl, m_height);
324 m_height += head->height();
326 RenderObject *body = firstBody;
328 if ( body != head && body != foot && body->isTableSection() ) {
329 body->setPos(bl, m_height);
330 m_height += body->height();
332 body = body->nextSibling();
335 foot->setPos(bl, m_height);
336 m_height += foot->height();
339 m_height += bpBottom;
341 if(tCaption && tCaption->style()->captionSide()==CAPBOTTOM) {
342 tCaption->setPos(tCaption->marginLeft(), m_height);
343 m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
346 //kdDebug(0) << "table height: " << m_height << endl;
348 // table can be containing block of positioned elements.
349 // ### only pass true if width or height changed.
350 layoutPositionedObjects( true );
352 // Repaint with our new bounds if they are different from our old bounds.
354 repaintAfterLayoutIfNeeded(oldBounds, oldFullBounds);
356 m_overflowHeight = kMax(m_overflowHeight, m_height);
357 m_overflowWidth = kMax(m_overflowWidth, m_width);
359 setNeedsLayout(false);
362 void RenderTable::setCellWidths()
365 kdDebug( 6040 ) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()" << endl;
368 RenderObject *child = firstChild();
370 if ( child->isTableSection() )
371 static_cast<RenderTableSection *>(child)->setCellWidths();
372 child = child->nextSibling();
376 void RenderTable::paint(PaintInfo& i, int _tx, int _ty)
381 PaintAction paintAction = i.phase;
384 kdDebug( 6040 ) << "RenderTable::paint() w/h = (" << width() << "/" << height() << ")" << endl;
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;
392 kdDebug( 6040 ) << "RenderTable::paint(2) " << _tx << "/" << _ty << " (" << _y << "/" << _h << ")" << endl;
395 if ((paintAction == PaintActionBlockBackground || paintAction == PaintActionChildBlockBackground)
396 && shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE)
397 paintBoxDecorations(i, _tx, _ty);
399 // We're done. We don't bother painting any children.
400 if (paintAction == PaintActionBlockBackground)
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));
407 for (RenderObject *child = firstChild(); child; child = child->nextSibling())
408 if (child->isTableSection() || child == tCaption)
409 child->paint(paintInfo, _tx, _ty);
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);
429 outlineBox(i.p, _tx, _ty, "blue");
433 void RenderTable::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
438 // Account for the caption.
440 int captionHeight = (tCaption->height() + tCaption->marginBottom() + tCaption->marginTop());
442 if (tCaption->style()->captionSide() != CAPBOTTOM)
443 _ty += captionHeight;
446 int my = kMax(_ty, i.r.y());
449 mh= kMax(0, h - (i.r.y() - _ty));
451 mh = kMin(i.r.height(), h);
453 paintBackground(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
455 if (style()->hasBorder() && !collapseBorders())
456 paintBorder(i.p, _tx, _ty, w, h, style());
459 void RenderTable::calcMinMaxWidth()
461 KHTMLAssert( !minMaxKnown() );
463 if ( needSectionRecalc )
467 kdDebug( 6040 ) << renderName() << "(Table " << this << ")::calcMinMaxWidth()" << endl;
470 tableLayout->calcMinMaxWidth();
472 if (tCaption && tCaption->minWidth() > m_minWidth)
473 m_minWidth = tCaption->minWidth();
477 kdDebug( 6040 ) << renderName() << " END: (Table " << this << ")::calcMinMaxWidth() min = " << m_minWidth << " max = " << m_maxWidth << endl;
481 void RenderTable::splitColumn( int pos, int firstSpan )
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;
493 // change width of all rows.
494 RenderObject *child = firstChild();
496 if ( child->isTableSection() ) {
497 RenderTableSection *section = static_cast<RenderTableSection *>(child);
498 int size = section->numRows();
500 if ( section->cCol > pos )
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;
511 child = child->nextSibling();
513 columnPos.resize( numEffCols()+1 );
514 setNeedsLayoutAndMinMaxRecalc();
517 void RenderTable::appendColumn( int span )
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 );
527 // change width of all rows.
528 RenderObject *child = firstChild();
530 if ( child->isTableSection() ) {
531 RenderTableSection *section = static_cast<RenderTableSection *>(child);
532 int size = section->numRows();
534 while ( row < size ) {
535 section->grid[row].row->resize( newSize );
536 section->cellAt( row, pos ) = 0;
541 child = child->nextSibling();
543 columnPos.resize( numEffCols()+1 );
544 setNeedsLayoutAndMinMaxRecalc();
547 RenderTableCol *RenderTable::colElement( int col ) {
548 if ( !has_col_elems )
550 RenderObject *child = firstChild();
553 if ( child->isTableCol() ) {
554 RenderTableCol *colElem = static_cast<RenderTableCol *>(child);
555 int span = colElem->span();
556 if ( !colElem->firstChild() ) {
562 RenderObject *next = child->firstChild();
564 next = child->nextSibling();
565 if ( !next && child->parent()->isTableCol() )
566 next = child->parent()->nextSibling();
574 void RenderTable::recalcSections()
577 head = foot = firstBody = 0;
578 has_col_elems = false;
580 RenderObject *child = firstChild();
581 // We need to get valid pointers to caption, head, foot and firstbody again
583 switch (child->style()->display()) {
586 tCaption = static_cast<RenderBlock*>(child);
587 tCaption->setNeedsLayout(true);
591 case TABLE_COLUMN_GROUP:
592 has_col_elems = true;
594 case TABLE_HEADER_GROUP: {
595 RenderTableSection *section = static_cast<RenderTableSection *>(child);
598 else if ( !firstBody )
600 if ( section->needCellRecalc )
601 section->recalcCells();
604 case TABLE_FOOTER_GROUP: {
605 RenderTableSection *section = static_cast<RenderTableSection *>(child);
608 else if ( !firstBody )
610 if ( section->needCellRecalc )
611 section->recalcCells();
614 case TABLE_ROW_GROUP: {
615 RenderTableSection *section = static_cast<RenderTableSection *>(child);
618 if ( section->needCellRecalc )
619 section->recalcCells();
624 child = child->nextSibling();
626 needSectionRecalc = false;
627 setNeedsLayout(true);
630 RenderObject* RenderTable::removeChildNode(RenderObject* child)
632 setNeedSectionRecalc();
633 return RenderContainer::removeChildNode( child );
636 int RenderTable::borderLeft() const
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.
643 return RenderBlock::borderLeft();
646 int RenderTable::borderRight() const
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.
653 return RenderBlock::borderRight();
656 int RenderTable::borderTop() const
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.
663 return RenderBlock::borderTop();
666 int RenderTable::borderBottom() const
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.
673 return RenderBlock::borderBottom();
676 RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
678 // Find the section and row to look in
680 RenderTableSection* section = 0;
683 // cell is not in the first row, so use the above row in its own section
684 section = cell->section();
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;
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.
705 aboveCell = section->cellAt(rAbove, effCol);
707 } while (aboveCell == (RenderTableCell *)-1 && effCol >=0);
708 return (aboveCell == (RenderTableCell *)-1) ? 0 : aboveCell;
714 RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
716 // Find the section and row to look in
717 int r = cell->row() + cell->rowSpan() - 1;
718 RenderTableSection* section = 0;
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();
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())
730 if (nextSection->isTableSection()) {
731 section = static_cast<RenderTableSection *>(nextSection);
732 if (section->numRows() > 0)
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.
744 belowCell = section->cellAt(rBelow, effCol);
746 } while (belowCell == (RenderTableCell *)-1 && effCol >=0);
747 return (belowCell == (RenderTableCell *)-1) ? 0 : belowCell;
753 RenderTableCell* RenderTable::cellLeft(const RenderTableCell* cell) const
755 RenderTableSection* section = cell->section();
756 int effCol = colToEffCol(cell->col());
760 // If we hit a colspan back up to a real cell.
761 RenderTableCell* prevCell;
763 prevCell = section->cellAt(cell->row(), effCol-1);
765 } while (prevCell == (RenderTableCell *)-1 && effCol >=0);
766 return (prevCell == (RenderTableCell *)-1) ? 0 : prevCell;
769 RenderTableCell* RenderTable::cellRight(const RenderTableCell* cell) const
771 int effCol = colToEffCol(cell->col()+cell->colSpan());
772 if (effCol >= numEffCols())
774 RenderTableCell* result = cell->section()->cellAt(cell->row(), effCol);
775 return (result == (RenderTableCell*)-1) ? 0 : result;
778 RenderBlock* RenderTable::firstLineBlock() const
783 void RenderTable::updateFirstLetter()
787 void RenderTable::dump(QTextStream *stream, QString ind) const
790 *stream << " tCaption";
796 *stream << endl << ind << "cspans:";
797 for ( unsigned int i = 0; i < columns.size(); i++ )
798 *stream << " " << columns[i].span;
799 *stream << endl << ind;
801 RenderBlock::dump(stream,ind);
805 // --------------------------------------------------------------------------
807 RenderTableSection::RenderTableSection(DOM::NodeImpl* node)
808 : RenderContainer(node)
810 // init RenderObject attributes
811 setInline(false); // our object is not Inline
815 needCellRecalc = false;
818 RenderTableSection::~RenderTableSection()
823 void RenderTableSection::detach()
825 // recalc cell info because RenderTable has unguarded pointers
826 // stored that point to this RenderTableSection.
828 table()->setNeedSectionRecalc();
830 RenderContainer::detach();
833 void RenderTableSection::setStyle(RenderStyle* _style)
835 // we don't allow changing this one
837 _style->setDisplay(style()->display());
838 else if (_style->display() != TABLE_FOOTER_GROUP && _style->display() != TABLE_HEADER_GROUP)
839 _style->setDisplay(TABLE_ROW_GROUP);
841 RenderContainer::setStyle(_style);
844 void RenderTableSection::addChild(RenderObject *child, RenderObject *beforeChild)
847 kdDebug( 6040 ) << renderName() << "(TableSection)::addChild( " << child->renderName() << ", beforeChild=" <<
848 (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
850 RenderObject *row = child;
852 if (child->element() && child->element()->id() == ID_FORM) {
853 RenderContainer::addChild(child,beforeChild);
857 if ( !child->isTableRow() ) {
860 beforeChild = lastChild();
862 if( beforeChild && beforeChild->isAnonymous() )
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 );
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);
881 row->addChild(child);
882 child->setNeedsLayoutAndMinMaxRecalc();
892 ensureRows( cRow+1 );
895 grid[cRow].height = child->style()->height();
896 if ( grid[cRow].height.type == Relative )
897 grid[cRow].height = Length();
901 RenderContainer::addChild(child,beforeChild);
904 void RenderTableSection::ensureRows(int numRows)
906 int nRows = gridRows;
907 if (numRows > nRows) {
908 if (numRows > static_cast<int>(grid.size()))
909 grid.resize(numRows*2+1);
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();
923 void RenderTableSection::addCell( RenderTableCell *cell )
925 int rSpan = cell->rowSpan();
926 int cSpan = cell->colSpan();
927 QMemArray<RenderTable::ColumnStruct> &columns = table()->columns;
928 int nCols = columns.size();
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:
933 // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
934 // <TR><TD colspan="2">5
937 // find empty space for the cell
941 while ( cCol < nCols && cellAt( cRow, cCol ) )
945 while ( pos < nCols && span < cSpan ) {
946 if ( cellAt( cRow, pos ) ) {
951 span += columns[pos].span;
956 while ( cCol < nCols && cellAt( cRow, cCol ) )
960 // qDebug("adding cell at %d/%d span=(%d/%d)", cRow, cCol, rSpan, cSpan );
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 ) {
969 if ( !(cRowHeight.type == Percent) ||
970 ( cRowHeight.type == Percent && cRowHeight.value < height.value ) )
971 grid[cRow].height = height;
974 if ( cRowHeight.type < Percent ||
975 ( cRowHeight.type == Fixed && cRowHeight.value < height.value ) )
976 grid[cRow].height = height;
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;
992 // make sure we have enough rows
993 ensureRows( cRow + rSpan );
996 // tell the cell where it is
997 RenderTableCell *set = cell;
1000 if ( cCol >= nCols ) {
1001 table()->appendColumn( cSpan );
1002 currentSpan = cSpan;
1004 if ( cSpan < columns[cCol].span )
1005 table()->splitColumn( cCol, cSpan );
1006 currentSpan = columns[cCol].span;
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;
1017 cSpan -= currentSpan;
1018 set = (RenderTableCell *)-1;
1021 cell->setRow( cRow );
1022 cell->setCol( table()->effColToCol( col ) );
1028 void RenderTableSection::setCellWidths()
1031 kdDebug( 6040 ) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()" << endl;
1033 QMemArray<int> &columnPos = table()->columnPos;
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 )
1045 int cspan = cell->colSpan();
1046 while ( cspan && endCol < cols ) {
1047 cspan -= table()->columns[endCol].span;
1050 int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing();
1052 kdDebug( 6040 ) << "setting width of cell " << cell << " " << cell->row() << "/" << cell->col() << " to " << w << " colspan=" << cell->colSpan() << " start=" << j << " end=" << endCol << endl;
1054 int oldWidth = cell->width();
1055 if ( w != oldWidth ) {
1056 cell->setNeedsLayout(true);
1057 cell->setWidth( w );
1064 void RenderTableSection::calcRowHeight()
1067 RenderTableCell *cell;
1069 int totalRows = gridRows;
1070 int spacing = table()->vBorderSpacing();
1072 rowPos.resize( totalRows + 1 );
1073 rowPos[0] = spacing;
1075 for ( int r = 0; r < totalRows; r++ ) {
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;
1084 if ( pos > rowPos[r+1] )
1087 Row *row = grid[r].row;
1088 int totalCols = row->size();
1089 int totalRows = gridRows;
1091 for ( int c = 0; c < totalCols; c++ ) {
1092 cell = cellAt(r, c);
1093 if ( !cell || cell == (RenderTableCell *)-1 )
1095 if ( r < totalRows - 1 && cellAt(r+1, c) == cell )
1098 if ( ( indx = r - cell->rowSpan() + 1 ) < 0 )
1101 if (cell->overrideSize() != -1) {
1102 cell->setOverrideSize(-1);
1103 cell->setChildNeedsLayout(true, false);
1104 cell->layoutIfNeeded();
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();
1115 pos = rowPos[ indx ] + ch + spacing;
1117 if ( pos > rowPos[r+1] )
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)
1125 int b=cell->baselinePosition();
1126 if (b > cell->borderTop() + cell->paddingTop()) {
1130 int td = rowPos[ indx ] + ch - b;
1137 //do we have baseline aligned elements?
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;
1144 grid[r].baseLine = baseline;
1147 if ( rowPos[r+1] < rowPos[r] )
1148 rowPos[r+1] = rowPos[r];
1149 // qDebug("rowpos(%d)=%d", r, rowPos[r] );
1153 int RenderTableSection::layoutRows( int toAdd )
1157 int totalRows = gridRows;
1158 int hspacing = table()->hBorderSpacing();
1159 int vspacing = table()->vBorderSpacing();
1161 if (toAdd && totalRows && (rowPos[totalRows] || !nextSibling())) {
1163 int totalHeight = rowPos[totalRows] + toAdd;
1164 // qDebug("layoutRows: totalHeight = %d", totalHeight );
1167 int totalPercent = 0;
1168 int numVariable = 0;
1169 for ( int r = 0; r < totalRows; r++ ) {
1170 if ( grid[r].height.type == Variable )
1172 else if ( grid[r].height.type == Percent )
1173 totalPercent += grid[r].height.value;
1175 if ( totalPercent ) {
1176 // qDebug("distributing %d over percent rows totalPercent=%d", dh, totalPercent );
1177 // try to satisfy percent
1179 if ( 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);
1190 totalPercent -= grid[r].height.value;
1191 // qDebug( "adding %d to row %d", toAdd, r );
1193 if ( r < totalRows-1 )
1194 rh = rowPos[r+2] - rowPos[r+1];
1198 if ( numVariable ) {
1199 // distribute over variable cols
1200 // qDebug("distributing %d over variable rows numVariable=%d", dh, numVariable );
1202 for ( int r = 0; r < totalRows; r++ ) {
1203 if ( numVariable > 0 && grid[r].height.type == Variable ) {
1204 int toAdd = dh/numVariable;
1212 if (dh>0 && rowPos[totalRows]) {
1213 // if some left overs, distribute equally.
1214 int tot=rowPos[totalRows];
1217 for ( int r = 0; r < totalRows; r++ ) {
1218 //weight with the original height
1219 add+=dh*(rowPos[r+1]-prev)/tot;
1226 int leftOffset = hspacing;
1228 int nEffCols = table()->numEffCols();
1229 for ( int r = 0; r < totalRows; r++ )
1231 Row *row = grid[r].row;
1232 int totalCols = row->size();
1233 for ( int c = 0; c < nEffCols; c++ )
1235 RenderTableCell *cell = cellAt(r, c);
1236 if (!cell || cell == (RenderTableCell *)-1 )
1238 if ( r < totalRows - 1 && cell == cellAt(r+1, c) )
1241 if ( ( rindx = r-cell->rowSpan()+1 ) < 0 )
1244 rHeight = rowPos[r+1] - rowPos[rindx] - vspacing;
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.
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
1259 bool cellChildrenFlex = false;
1260 bool flexAllChildren = cell->style()->height().isFixed() ||
1261 (!table()->style()->height().isVariable() && rHeight != cell->height());
1262 RenderObject* o = cell->firstChild();
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;
1272 o = o->nextSibling();
1274 if (cellChildrenFlex) {
1275 cell->setOverrideSize(kMax(0,
1276 rHeight - cell->borderTop() - cell->paddingTop() -
1277 cell->borderBottom() - cell->paddingBottom()));
1278 cell->layoutIfNeeded();
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);
1288 kdDebug( 6040 ) << "setting position " << r << "/" << c << ": "
1289 << table()->columnPos[c] /*+ padding */ << "/" << rowPos[rindx] << " height=" << rHeight<< endl;
1292 EVerticalAlign va = cell->style()->verticalAlign();
1301 te = getBaseline(r) - cell->baselinePosition() ;
1307 te = (rHeight - cell->height())/2;
1310 te = rHeight - cell->height();
1316 // kdDebug( 6040 ) << "CELL " << cell << " te=" << te << ", be=" << rHeight - cell->height() - te << ", rHeight=" << rHeight << ", valign=" << va << endl;
1318 cell->setCellTopExtra( te );
1319 cell->setCellBottomExtra( rHeight - cell->height() - te);
1322 int oldCellX = cell->xPos();
1323 int oldCellY = cell->yPos();
1325 if (style()->direction()==RTL) {
1327 table()->columnPos[(int)totalCols] -
1328 table()->columnPos[table()->colToEffCol(cell->col()+cell->colSpan())] +
1332 cell->setPos( table()->columnPos[c] + leftOffset, rowPos[rindx] );
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);
1343 m_height = rowPos[totalRows];
1348 void RenderTableSection::paint(PaintInfo& i, int tx, int ty)
1350 unsigned int totalRows = gridRows;
1351 unsigned int totalCols = table()->columns.size();
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();
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)
1368 for ( ; endrow > 0; endrow-- ) {
1369 if ( ty + rowPos[endrow-1] <= y + h + os)
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)
1379 for ( ; endcol > 0; endcol-- ) {
1380 if ( tx + table()->columnPos[endcol-1] <= x + w + os)
1385 if ( startcol < endcol ) {
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 )
1392 for ( ; c < endcol; c++ ) {
1393 RenderTableCell *cell = cellAt(r, c);
1394 if (!cell || cell == (RenderTableCell *)-1 )
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))
1404 kdDebug( 6040 ) << "painting cell " << r << "/" << c << endl;
1406 cell->paint(i, tx, ty);
1412 void RenderTableSection::recalcCells()
1419 RenderObject *row = firstChild();
1423 ensureRows( cRow+1 );
1424 RenderObject *cell = row->firstChild();
1426 if ( cell->isTableCell() )
1427 addCell( static_cast<RenderTableCell *>(cell) );
1428 cell = cell->nextSibling();
1430 row = row->nextSibling();
1432 needCellRecalc = false;
1433 setNeedsLayout(true);
1436 void RenderTableSection::clearGrid()
1438 int rows = gridRows;
1440 delete grid[rows].row;
1444 RenderObject* RenderTableSection::removeChildNode(RenderObject* child)
1446 setNeedCellRecalc();
1447 return RenderContainer::removeChildNode( child );
1451 void RenderTableSection::dump(QTextStream *stream, QString ind) const
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() << ") ";
1460 *stream << cellAt( r, c ) << "null cell ";
1462 *stream << endl << ind;
1464 RenderContainer::dump(stream,ind);
1468 // -------------------------------------------------------------------------
1470 RenderTableRow::RenderTableRow(DOM::NodeImpl* node)
1471 : RenderContainer(node)
1473 // init RenderObject attributes
1474 setInline(false); // our object is not Inline
1477 void RenderTableRow::detach()
1479 RenderTableSection *s = section();
1481 s->setNeedCellRecalc();
1483 RenderContainer::detach();
1486 void RenderTableRow::setStyle(RenderStyle* style)
1488 style->setDisplay(TABLE_ROW);
1489 RenderContainer::setStyle(style);
1492 void RenderTableRow::addChild(RenderObject *child, RenderObject *beforeChild)
1495 kdDebug( 6040 ) << renderName() << "(TableRow)::addChild( " << child->renderName() << " )" << ", " <<
1496 (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
1498 if (child->element() && child->element()->id() == ID_FORM) {
1499 RenderContainer::addChild(child,beforeChild);
1503 RenderTableCell *cell;
1505 if ( !child->isTableCell() ) {
1506 RenderObject *last = beforeChild;
1509 RenderTableCell *cell = 0;
1510 if( last && last->isAnonymous() && last->isTableCell() )
1511 cell = static_cast<RenderTableCell *>(last);
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);
1520 cell->addChild(child);
1521 child->setNeedsLayoutAndMinMaxRecalc();
1524 cell = static_cast<RenderTableCell *>(child);
1526 static_cast<RenderTableSection *>(parent())->addCell( cell );
1528 RenderContainer::addChild(cell,beforeChild);
1530 if ( ( beforeChild || nextSibling()) && section() )
1531 section()->setNeedCellRecalc();
1534 RenderObject* RenderTableRow::removeChildNode(RenderObject* child)
1536 // RenderTableCell detach should do it
1538 // section()->setNeedCellRecalc();
1539 return RenderContainer::removeChildNode( child );
1543 void RenderTableRow::dump(QTextStream *stream, QString ind) const
1545 RenderContainer::dump(stream,ind);
1549 void RenderTableRow::layout()
1551 KHTMLAssert( needsLayout() );
1552 KHTMLAssert( minMaxKnown() );
1554 RenderObject *child = firstChild();
1556 if (child->isTableCell()) {
1557 RenderTableCell *cell = static_cast<RenderTableCell *>(child);
1558 if (child->needsLayout()) {
1559 cell->calcVerticalMargins();
1561 cell->setCellTopExtra(0);
1562 cell->setCellBottomExtra(0);
1565 child = child->nextSibling();
1567 setNeedsLayout(false);
1570 QRect RenderTableRow::getAbsoluteRepaintRect()
1572 // For now, just repaint the whole table.
1573 // FIXME: Find a better way to do this.
1574 RenderTable* parentTable = table();
1576 return parentTable->getAbsoluteRepaintRect();
1581 // -------------------------------------------------------------------------
1583 RenderTableCell::RenderTableCell(DOM::NodeImpl* _node)
1584 : RenderBlock(_node)
1589 updateFromElement();
1590 setShouldPaintBackgroundOrBorder(true);
1593 m_percentageHeight = 0;
1596 void RenderTableCell::detach()
1598 if (parent() && section())
1599 section()->setNeedCellRecalc();
1601 RenderBlock::detach();
1604 void RenderTableCell::updateFromElement()
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();
1614 if ((oldRSpan != rSpan || oldCSpan != cSpan) && style() && parent())
1615 setNeedsLayoutAndMinMaxRecalc();
1618 void RenderTableCell::calcMinMaxWidth()
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
1630 if (m_minWidth < style()->width().value)
1631 m_minWidth = style()->width().value;
1635 void RenderTableCell::calcWidth()
1639 void RenderTableCell::setWidth( int width )
1641 if ( width != m_width ) {
1643 m_widthChanged = true;
1647 void RenderTableCell::layout()
1649 layoutBlock(m_widthChanged);
1650 m_widthChanged = false;
1653 void RenderTableCell::computeAbsoluteRepaintRect(QRect& r, bool f)
1655 r.setY(r.y() + _topExtra);
1656 RenderBlock::computeAbsoluteRepaintRect(r, f);
1659 bool RenderTableCell::absolutePosition(int &xPos, int &yPos, bool f)
1661 bool ret = RenderBlock::absolutePosition(xPos, yPos, f);
1667 short RenderTableCell::baselinePosition( bool ) const
1669 RenderObject* o = firstChild();
1670 int offset = paddingTop() + borderTop();
1671 if (!o) return offset + contentHeight();
1672 while (o->firstChild()) {
1674 offset += o->paddingTop() + o->borderTop();
1675 o = o->firstChild();
1679 return paddingTop() + borderTop() + contentHeight();
1681 offset += o->baselinePosition( true );
1686 void RenderTableCell::setStyle( RenderStyle *style )
1688 style->setDisplay(TABLE_CELL);
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);
1697 style->setWhiteSpace(NOWRAP);
1700 RenderBlock::setStyle( style );
1701 setShouldPaintBackgroundOrBorder(true);
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.
1710 // The following rules apply for resolving conflicts and figuring out which border
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)
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;
1731 if (border1.style() == BHIDDEN || border2.style() == BHIDDEN)
1732 return CollapsedBorderValue(); // No border should exist at this location.
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;
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;
1742 // The borders have equal width. Sort by border style.
1743 if (border1.style() != border2.style())
1744 return border1.style() > border2.style() ? border1 : border2;
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;
1750 CollapsedBorderValue RenderTableCell::collapsedLeftBorder() const
1752 // For border left, we need to check, in order of precedence:
1753 // (1) Our left border.
1754 CollapsedBorderValue result(&style()->borderLeft(), BCELL);
1756 // (2) The previous cell's right border.
1757 RenderTableCell* prevCell = table()->cellLeft(this);
1759 result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderRight(), BCELL));
1760 if (!result.exists()) return result;
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;
1767 // (4) Our row group's left border.
1768 result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderLeft(), BROWGROUP));
1769 if (!result.exists()) return result;
1772 // (5) Our column's left border.
1773 RenderTableCol* colElt = table()->colElement(col());
1775 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
1776 if (!result.exists()) return result;
1779 // (6) The previous column's right border.
1781 colElt = table()->colElement(col()-1);
1783 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
1784 if (!result.exists()) return result;
1789 // (7) The table's left border.
1790 result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderLeft(), BTABLE));
1791 if (!result.exists()) return result;
1797 CollapsedBorderValue RenderTableCell::collapsedRightBorder() const
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;
1805 // For border right, we need to check, in order of precedence:
1806 // (1) Our right border.
1807 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderRight(), BCELL);
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;
1818 // (3) Our row's right border.
1819 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderRight(), BROW));
1820 if (!result.exists()) return result;
1822 // (4) Our row group's right border.
1823 result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderRight(), BROWGROUP));
1824 if (!result.exists()) return result;
1827 // (5) Our column's right border.
1828 RenderTableCol* colElt = table()->colElement(col()+colSpan()-1);
1830 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
1831 if (!result.exists()) return result;
1834 // (6) The next column's left border.
1835 if (!inLastColumn) {
1836 colElt = tableElt->colElement(col()+colSpan());
1838 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
1839 if (!result.exists()) return result;
1843 // (7) The table's right border.
1844 result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderRight(), BTABLE));
1845 if (!result.exists()) return result;
1851 CollapsedBorderValue RenderTableCell::collapsedTopBorder() const
1853 // For border top, we need to check, in order of precedence:
1854 // (1) Our top border.
1855 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderTop(), BCELL);
1857 RenderTableCell* prevCell = table()->cellAbove(this);
1859 // (2) A previous cell's bottom border.
1860 result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderBottom(), BCELL));
1861 if (!result.exists()) return result;
1864 // (3) Our row's top border.
1865 result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderTop(), BROW));
1866 if (!result.exists()) return result;
1868 // (4) The previous row's bottom border.
1870 RenderObject* prevRow = 0;
1871 if (prevCell->section() == section())
1872 prevRow = parent()->previousSibling();
1874 prevRow = prevCell->section()->lastChild();
1877 result = compareBorders(result, CollapsedBorderValue(&prevRow->style()->borderBottom(), BROW));
1878 if (!result.exists()) return result;
1882 // Now check row groups.
1883 RenderObject* currSection = parent()->parent();
1885 // (5) Our row group's top border.
1886 result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP));
1887 if (!result.exists()) return result;
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(§ion->style()->borderBottom(), BROWGROUP));
1895 if (!result.exists()) return result;
1901 // (8) Our column's top border.
1902 RenderTableCol* colElt = table()->colElement(col());
1904 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderTop(), BCOL));
1905 if (!result.exists()) return result;
1908 // (9) The table's top border.
1909 result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderTop(), BTABLE));
1910 if (!result.exists()) return result;
1916 CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const
1918 // For border top, we need to check, in order of precedence:
1919 // (1) Our bottom border.
1920 CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBottom(), BCELL);
1922 RenderTableCell* nextCell = table()->cellBelow(this);
1924 // (2) A following cell's top border.
1925 result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderTop(), BCELL));
1926 if (!result.exists()) return result;
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;
1933 // (4) The next row's top border.
1935 result = compareBorders(result, CollapsedBorderValue(&nextCell->parent()->style()->borderTop(), BROW));
1936 if (!result.exists()) return result;
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;
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(§ion->style()->borderTop(), BROWGROUP));
1952 if (!result.exists()) return result;
1958 // (8) Our column's bottom border.
1959 RenderTableCol* colElt = table()->colElement(col());
1961 result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderBottom(), BCOL));
1962 if (!result.exists()) return result;
1965 // (9) The table's bottom border.
1966 result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderBottom(), BTABLE));
1967 if (!result.exists()) return result;
1973 int RenderTableCell::borderLeft() const
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.
1981 return RenderBlock::borderLeft();
1984 int RenderTableCell::borderRight() const
1986 if (table()->collapseBorders()) {
1987 CollapsedBorderValue border = collapsedRightBorder();
1988 if (border.exists())
1989 return border.width()/2;
1992 return RenderBlock::borderRight();
1995 int RenderTableCell::borderTop() const
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.
2003 return RenderBlock::borderTop();
2006 int RenderTableCell::borderBottom() const
2008 if (table()->collapseBorders()) {
2009 CollapsedBorderValue border = collapsedBottomBorder();
2010 if (border.exists())
2011 return border.width()/2;
2014 return RenderBlock::borderBottom();
2018 #include <qpainter.h>
2020 static void outlineBox(QPainter *p, int _tx, int _ty, int w, int h)
2022 p->setPen(QPen(QColor("yellow"), 3, Qt::DotLine));
2023 p->setBrush( Qt::NoBrush );
2024 p->drawRect(_tx, _ty, w, h );
2028 void RenderTableCell::paint(PaintInfo& i, int _tx, int _ty)
2032 kdDebug( 6040 ) << renderName() << "(RenderTableCell)::paint() w/h = (" << width() << "/" << height() << ")" << " _y/_h=" << _y << "/" << _h << endl;
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))
2043 if (i.phase == PaintActionCollapsedTableBorders && style()->visibility() == VISIBLE) {
2045 int h = height() + borderTopExtra() + borderBottomExtra();
2046 paintCollapsedBorder(i.p, _tx, _ty, w, h);
2049 RenderBlock::paintObject(i, _tx, _ty + _topExtra);
2052 ::outlineBox( i.p, _tx, _ty, width(), height() + borderTopExtra() + borderBottomExtra());
2056 static EBorderStyle collapsedBorderStyle(EBorderStyle style)
2058 if (style == OUTSET)
2060 else if (style == INSET)
2065 struct CollapsedBorder {
2068 CollapsedBorderValue border;
2069 RenderObject::BorderSide side;
2078 class CollapsedBorders
2081 CollapsedBorders(int i) :count(0) {}
2083 void addBorder(const CollapsedBorderValue& b, RenderObject::BorderSide s, bool paint,
2084 int _x1, int _y1, int _x2, int _y2,
2085 EBorderStyle _style)
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;
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;
2111 CollapsedBorder borders[4];
2115 static void addBorderStyle(QValueList<CollapsedBorderValue>& borderStyles, CollapsedBorderValue borderValue)
2117 if (!borderValue.exists() || borderStyles.contains(borderValue))
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);
2130 borderStyles.append(borderValue);
2133 void RenderTableCell::collectBorders(QValueList<CollapsedBorderValue>& borderStyles)
2135 addBorderStyle(borderStyles, collapsedLeftBorder());
2136 addBorderStyle(borderStyles, collapsedRightBorder());
2137 addBorderStyle(borderStyles, collapsedTopBorder());
2138 addBorderStyle(borderStyles, collapsedBottomBorder());
2141 void RenderTableCell::paintCollapsedBorder(QPainter* p, int _tx, int _ty, int w, int h)
2143 if (!table()->currentBorderStyle())
2146 CollapsedBorderValue leftVal = collapsedLeftBorder();
2147 CollapsedBorderValue rightVal = collapsedRightBorder();
2148 CollapsedBorderValue topVal = collapsedTopBorder();
2149 CollapsedBorderValue bottomVal = collapsedBottomBorder();
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();
2159 w += leftWidth/2 + int(rightWidth/2.0+0.5);
2160 h += topWidth/2 + int(bottomWidth/2.0+0.5);
2162 bool tt = topVal.isTransparent();
2163 bool bt = bottomVal.isTransparent();
2164 bool rt = rightVal.isTransparent();
2165 bool lt = leftVal.isTransparent();
2167 EBorderStyle ts = collapsedBorderStyle(topVal.style());
2168 EBorderStyle bs = collapsedBorderStyle(bottomVal.style());
2169 EBorderStyle ls = collapsedBorderStyle(leftVal.style());
2170 EBorderStyle rs = collapsedBorderStyle(rightVal.style());
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;
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);
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);
2192 QRect RenderTableCell::getAbsoluteRepaintRect()
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);
2201 void RenderTableCell::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
2203 RenderTable* tableElt = table();
2204 if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild())
2208 int h = height() + borderTopExtra() + borderBottomExtra();
2209 _ty -= borderTopExtra();
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 );
2220 c = col->style()->backgroundColor();
2221 if ( !c.isValid() ) {
2223 RenderStyle *style = col->parent()->style();
2224 if ( style->display() == TABLE_COLUMN_GROUP )
2225 c = style->backgroundColor();
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);
2242 bgLayer = col->style()->backgroundLayers();
2243 if (!bgLayer->hasImage()) {
2245 RenderStyle *style = col->parent()->style();
2246 if (style->display() == TABLE_COLUMN_GROUP)
2247 bgLayer = style->backgroundLayers();
2252 int my = kMax(_ty, i.r.y());
2253 int end = kMin(i.r.y() + i.r.height(), _ty + h);
2256 if (bgLayer->hasImage() || c.isValid())
2257 paintBackground(i.p, c, bgLayer, my, mh, _tx, _ty, w, h);
2259 if (style()->hasBorder() && !tableElt->collapseBorders())
2260 paintBorder(i.p, _tx, _ty, w, h, style());
2265 void RenderTableCell::dump(QTextStream *stream, QString ind) const
2267 *stream << " row=" << _row;
2268 *stream << " col=" << _col;
2269 *stream << " rSpan=" << rSpan;
2270 *stream << " cSpan=" << cSpan;
2271 // *stream << " nWrap=" << nWrap;
2273 RenderBlock::dump(stream,ind);
2277 // -------------------------------------------------------------------------
2279 RenderTableCol::RenderTableCol(DOM::NodeImpl* node)
2280 : RenderContainer(node)
2282 // init RenderObject attributes
2283 setInline(true); // our object is not Inline
2286 updateFromElement();
2289 void RenderTableCol::updateFromElement()
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);
2298 _span = !(style() && style()->display() == TABLE_COLUMN_GROUP);
2299 if (_span != oldSpan && style() && parent())
2300 setNeedsLayoutAndMinMaxRecalc();
2303 bool RenderTableCol::canHaveChildren() const
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;
2310 void RenderTableCol::addChild(RenderObject *child, RenderObject *beforeChild)
2313 //kdDebug( 6040 ) << renderName() << "(Table)::addChild( " << child->renderName() << " )" << ", " <<
2314 // (beforeChild ? beforeChild->renderName() : 0) << " )" << endl;
2317 KHTMLAssert(child->style()->display() == TABLE_COLUMN);
2319 // these have to come before the table definition!
2320 RenderContainer::addChild(child,beforeChild);
2324 void RenderTableCol::dump(QTextStream *stream, QString ind) const
2326 *stream << " _span=" << _span;
2327 RenderContainer::dump(stream,ind);