#include "rendering/render_table.h"
#include "rendering/table_layout.h"
#include "html/html_tableimpl.h"
#include "misc/htmltags.h"
#include "misc/htmlattrs.h"
#include <kglobal.h>
#include <qapplication.h>
#include <qstyle.h>
#include <kdebug.h>
#include <assert.h>
using namespace khtml;
using namespace DOM;
RenderTable::RenderTable(DOM::NodeImpl* node)
: RenderBlock(node)
{
tCaption = 0;
head = foot = firstBody = 0;
tableLayout = 0;
rules = None;
frame = Void;
has_col_elems = false;
spacing = 0;
padding = 0;
needSectionRecalc = false;
padding = 0;
columnPos.resize( 2 );
columnPos.fill( 0 );
columns.resize( 1 );
columns.fill( ColumnStruct() );
columnPos[0] = 0;
}
RenderTable::~RenderTable()
{
delete tableLayout;
}
void RenderTable::setStyle(RenderStyle *_style)
{
ETableLayout oldTableLayout = style() ? style()->tableLayout() : TAUTO;
if ( _style->display() == INLINE ) _style->setDisplay(INLINE_TABLE);
if ( _style->display() != INLINE_TABLE ) _style->setDisplay(TABLE);
RenderBlock::setStyle(_style);
setInline(style()->display()==INLINE_TABLE && !isPositioned());
setReplaced(style()->display()==INLINE_TABLE);
spacing = style()->borderSpacing();
columnPos[0] = spacing;
if ( !tableLayout || style()->tableLayout() != oldTableLayout ) {
delete tableLayout;
if (style()->tableLayout() == TFIXED && !style()->width().isVariable()) {
tableLayout = new FixedTableLayout(this);
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << "using fixed table layout" << endl;
#endif
} else
tableLayout = new AutoTableLayout(this);
}
}
void RenderTable::addChild(RenderObject *child, RenderObject *beforeChild)
{
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(Table)::addChild( " << child->renderName() << ", " <<
(beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
#endif
RenderObject *o = child;
if (child->element() && child->element()->id() == ID_FORM) {
RenderContainer::addChild(child,beforeChild);
return;
}
switch(child->style()->display())
{
case TABLE_CAPTION:
tCaption = static_cast<RenderBlock *>(child);
break;
case TABLE_COLUMN:
case TABLE_COLUMN_GROUP:
has_col_elems = true;
break;
case TABLE_HEADER_GROUP:
if ( !head )
head = static_cast<RenderTableSection *>(child);
else if ( !firstBody )
firstBody = static_cast<RenderTableSection *>(child);
break;
case TABLE_FOOTER_GROUP:
if ( !foot ) {
foot = static_cast<RenderTableSection *>(child);
break;
}
case TABLE_ROW_GROUP:
if(!firstBody)
firstBody = static_cast<RenderTableSection *>(child);
break;
default:
if ( !beforeChild && lastChild() &&
lastChild()->isTableSection() && lastChild()->isAnonymousBox() ) {
o = lastChild();
} else {
RenderObject *lastBox = beforeChild;
while ( lastBox && lastBox->parent()->isAnonymousBox() &&
!lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION )
lastBox = lastBox->parent();
if ( lastBox && lastBox->isAnonymousBox() ) {
lastBox->addChild( child, beforeChild );
return;
} else {
if ( beforeChild && !beforeChild->isTableSection() )
beforeChild = 0;
o = new (renderArena()) RenderTableSection(0 );
RenderStyle *newStyle = new RenderStyle();
newStyle->inheritFrom(style());
newStyle->setDisplay(TABLE_ROW_GROUP);
o->setStyle(newStyle);
o->setIsAnonymousBox(true);
addChild(o, beforeChild);
}
}
o->addChild(child);
child->setNeedsLayoutAndMinMaxRecalc();
return;
}
RenderContainer::addChild(child,beforeChild);
}
void RenderTable::calcWidth()
{
if ( isPositioned() ) {
calcAbsoluteHorizontal();
}
RenderBlock *cb = containingBlock();
int availableWidth = cb->contentWidth();
LengthType widthType = style()->width().type;
if(widthType > Relative && style()->width().value > 0) {
m_width = style()->width().minWidth( availableWidth );
if(m_minWidth > m_width) m_width = m_minWidth;
} else {
m_width = KMIN(short( availableWidth ),m_maxWidth);
}
if (style()->width().isVariable() && style()->flowAroundFloats()) {
availableWidth = cb->lineWidth( m_y );
m_width = QMIN( availableWidth, m_width );
}
m_width = KMAX (m_width, m_minWidth);
m_marginRight=0;
m_marginLeft=0;
calcHorizontalMargins(style()->marginLeft(),style()->marginRight(),availableWidth);
}
void RenderTable::layout()
{
KHTMLAssert( needsLayout() );
KHTMLAssert( minMaxKnown() );
KHTMLAssert( !needSectionRecalc );
m_height = 0;
initMaxMarginValues();
calcWidth();
tableLayout->layout();
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(Table)::layout1() width=" << width() << ", marginLeft=" << marginLeft() << " marginRight=" << marginRight() << endl;
#endif
setCellWidths();
int calculatedHeight = 0;
RenderObject *child = firstChild();
while( child ) {
if ( child->needsLayout() && !(child->element() && child->element()->id() == ID_FORM))
child->layout();
if ( child->isTableSection() ) {
static_cast<RenderTableSection *>(child)->calcRowHeight();
calculatedHeight += static_cast<RenderTableSection *>(child)->layoutRows( 0 );
}
child = child->nextSibling();
}
if(tCaption && tCaption->style()->captionSide() != CAPBOTTOM) {
tCaption->setPos(tCaption->marginLeft(), m_height);
m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
}
m_height += borderTop();
Length h = style()->height();
int th=0;
if (h.isFixed())
th = h.value;
else if (h.isPercent())
{
RenderObject* c = containingBlock();
for ( ;
!c->isCanvas() && !c->isBody() && !c->isTableCell() && !c->isPositioned() && !c->isFloating();
c = c->containingBlock()) {
Length ch = c->style()->height();
if (ch.isFixed()) {
th = h.width(ch.value);
break;
}
}
if (c->isTableCell()) {
RenderTableCell* cell = static_cast<RenderTableCell*>(c);
int cellHeight = cell->getCellPercentageHeight();
if (cellHeight && cell->style()->height().isFixed())
th = h.width(cellHeight);
}
else {
Length ch = c->style()->height();
if (ch.isFixed())
th = h.width(ch.value);
else {
th = h.width(viewRect().height() - c->marginBottom() - c->marginTop());
setOverhangingContents();
}
}
}
if ( th > calculatedHeight ) {
if (firstBody) {
firstBody->calcRowHeight();
firstBody->layoutRows( th - calculatedHeight );
}
}
int bl = borderLeft();
if ( head ) {
head->setPos(bl, m_height);
m_height += head->height();
}
RenderObject *body = firstBody;
while ( body ) {
if ( body != head && body != foot && body->isTableSection() ) {
body->setPos(bl, m_height);
m_height += body->height();
}
body = body->nextSibling();
}
if ( foot ) {
foot->setPos(bl, m_height);
m_height += foot->height();
}
m_height += borderBottom();
if(tCaption && tCaption->style()->captionSide()==CAPBOTTOM) {
tCaption->setPos(tCaption->marginLeft(), m_height);
m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
}
calcHeight();
layoutPositionedObjects( true );
setNeedsLayout(false);
}
void RenderTable::setCellWidths()
{
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()" << endl;
#endif
RenderObject *child = firstChild();
while( child ) {
if ( child->isTableSection() )
static_cast<RenderTableSection *>(child)->setCellWidths();
child = child->nextSibling();
}
}
void RenderTable::paint( QPainter *p, int _x, int _y,
int _w, int _h, int _tx, int _ty, PaintAction paintAction)
{
if (needsLayout())
return;
_tx += xPos();
_ty += yPos();
#ifdef TABLE_PRINT
kdDebug( 6040 ) << "RenderTable::paint() w/h = (" << width() << "/" << height() << ")" << endl;
#endif
if (!overhangingContents() && !isRelPositioned() && !isPositioned())
{
if((_ty > _y + _h) || (_ty + height() < _y)) return;
if((_tx > _x + _w) || (_tx + width() < _x)) return;
}
#ifdef TABLE_PRINT
kdDebug( 6040 ) << "RenderTable::paint(2) " << _tx << "/" << _ty << " (" << _y << "/" << _h << ")" << endl;
#endif
if ((paintAction == PaintActionElementBackground || paintAction == PaintActionChildBackground)
&& style()->visibility() == VISIBLE) {
paintBoxDecorations(p, _x, _y, _w, _h, _tx, _ty);
}
if (paintAction == PaintActionElementBackground)
return;
if (paintAction == PaintActionChildBackgrounds)
paintAction = PaintActionChildBackground;
RenderObject *child = firstChild();
while( child ) {
if ( child->isTableSection() || child == tCaption )
child->paint( p, _x, _y, _w, _h, _tx, _ty, paintAction );
child = child->nextSibling();
}
#ifdef BOX_DEBUG
outlineBox(p, _tx, _ty, "blue");
#endif
}
void RenderTable::calcMinMaxWidth()
{
KHTMLAssert( !minMaxKnown() );
if ( needSectionRecalc )
recalcSections();
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(Table " << this << ")::calcMinMaxWidth()" << endl;
#endif
tableLayout->calcMinMaxWidth();
if (tCaption && tCaption->minWidth() > m_minWidth)
m_minWidth = tCaption->minWidth();
setMinMaxKnown();
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << " END: (Table " << this << ")::calcMinMaxWidth() min = " << m_minWidth << " max = " << m_maxWidth << endl;
#endif
}
void RenderTable::close()
{
setNeedsLayoutAndMinMaxRecalc();
}
int RenderTable::borderTopExtra()
{
if (tCaption && tCaption->style()->captionSide()!=CAPBOTTOM)
return -(tCaption->height() + tCaption->marginBottom() + tCaption->marginTop());
else
return 0;
}
int RenderTable::borderBottomExtra()
{
if (tCaption && tCaption->style()->captionSide()==CAPBOTTOM)
return -(tCaption->height() + tCaption->marginBottom() + tCaption->marginTop());
else
return 0;
}
void RenderTable::splitColumn( int pos, int firstSpan )
{
int oldSize = columns.size();
columns.resize( oldSize + 1 );
int oldSpan = columns[pos].span;
KHTMLAssert( oldSpan > firstSpan );
columns[pos].span = firstSpan;
memmove( columns.data()+pos+1, columns.data()+pos, (oldSize-pos)*sizeof(ColumnStruct) );
columns[pos+1].span = oldSpan - firstSpan;
RenderObject *child = firstChild();
while ( child ) {
if ( child->isTableSection() ) {
RenderTableSection *section = static_cast<RenderTableSection *>(child);
int size = section->grid.size();
int row = 0;
if ( section->cCol > pos )
section->cCol++;
while ( row < size ) {
section->grid[row].row->resize( oldSize+1 );
RenderTableSection::Row &r = *section->grid[row].row;
memmove( r.data()+pos+1, r.data()+pos, (oldSize-pos)*sizeof( RenderTableCell * ) );
r[pos+1] = r[pos] ? (RenderTableCell *)-1 : 0;
row++;
}
}
child = child->nextSibling();
}
columnPos.resize( numEffCols()+1 );
setNeedsLayoutAndMinMaxRecalc();
}
void RenderTable::appendColumn( int span )
{
int pos = columns.size();
int newSize = pos + 1;
columns.resize( newSize );
columns[pos].span = span;
RenderObject *child = firstChild();
while ( child ) {
if ( child->isTableSection() ) {
RenderTableSection *section = static_cast<RenderTableSection *>(child);
int size = section->grid.size();
int row = 0;
while ( row < size ) {
section->grid[row].row->resize( newSize );
section->cellAt( row, pos ) = 0;
row++;
}
}
child = child->nextSibling();
}
columnPos.resize( numEffCols()+1 );
setNeedsLayoutAndMinMaxRecalc();
}
RenderTableCol *RenderTable::colElement( int col ) {
if ( !has_col_elems )
return 0;
RenderObject *child = firstChild();
int cCol = 0;
while ( child ) {
if ( child->isTableCol() ) {
RenderTableCol *colElem = static_cast<RenderTableCol *>(child);
int span = colElem->span();
if ( !colElem->firstChild() ) {
cCol += span;
if ( cCol > col )
return colElem;
}
RenderObject *next = child->firstChild();
if ( !next )
next = child->nextSibling();
if ( !next && child->parent()->isTableCol() )
next = child->parent()->nextSibling();
child = next;
} else
break;
}
return 0;
}
void RenderTable::recalcSections()
{
tCaption = 0;
head = foot = firstBody = 0;
has_col_elems = false;
RenderObject *child = firstChild();
while (child) {
switch (child->style()->display()) {
case TABLE_CAPTION:
if (!tCaption) {
tCaption = static_cast<RenderBlock*>(child);
tCaption->setNeedsLayout(true);
}
break;
case TABLE_COLUMN:
case TABLE_COLUMN_GROUP:
has_col_elems = true;
break;
case TABLE_HEADER_GROUP: {
RenderTableSection *section = static_cast<RenderTableSection *>(child);
if ( !head )
head = section;
else if ( !firstBody )
firstBody = section;
if ( section->needCellRecalc )
section->recalcCells();
break;
}
case TABLE_FOOTER_GROUP: {
RenderTableSection *section = static_cast<RenderTableSection *>(child);
if ( !foot )
foot = section;
else if ( !firstBody )
firstBody = section;
if ( section->needCellRecalc )
section->recalcCells();
break;
}
case TABLE_ROW_GROUP: {
RenderTableSection *section = static_cast<RenderTableSection *>(child);
if ( !firstBody )
firstBody = section;
if ( section->needCellRecalc )
section->recalcCells();
}
default:
break;
}
child = child->nextSibling();
}
needSectionRecalc = false;
setNeedsLayout(true);
}
RenderObject* RenderTable::removeChildNode(RenderObject* child)
{
setNeedSectionRecalc();
return RenderContainer::removeChildNode( child );
}
#if APPLE_CHANGES
RenderTableCell* RenderTable::cellAbove(RenderTableCell* cell) const
{
int r = cell->row();
RenderTableSection *section;
int rAbove = 0;
if (r > 0) {
section = cell->section();
rAbove = r-1;
} else {
RenderObject *prevSection = cell->section()->previousSibling();
while (prevSection && !prevSection->isTableSection()) {
prevSection = prevSection->previousSibling();
}
section = static_cast<RenderTableSection *>(prevSection);
if (section) {
rAbove = section->numRows()-1;
}
}
if (section && rAbove >= 0) {
int effCol = colToEffCol(cell->col());
RenderTableCell* aboveCell;
do {
aboveCell = section->cellAt(rAbove, effCol);
effCol--;
} while (aboveCell == (RenderTableCell *)-1 && effCol >=0);
return (aboveCell == (RenderTableCell *)-1) ? 0 : aboveCell;
} else {
return 0;
}
}
#endif
#ifndef NDEBUG
void RenderTable::dump(QTextStream *stream, QString ind) const
{
if (tCaption)
*stream << " tCaption";
if (head)
*stream << " head";
if (foot)
*stream << " foot";
*stream << endl << ind << "cspans:";
for ( unsigned int i = 0; i < columns.size(); i++ )
*stream << " " << columns[i].span;
*stream << endl << ind;
RenderBlock::dump(stream,ind);
}
#endif
RenderTableSection::RenderTableSection(DOM::NodeImpl* node)
: RenderBox(node)
{
setInline(false); cCol = 0;
cRow = -1;
needCellRecalc = false;
}
RenderTableSection::~RenderTableSection()
{
clearGrid();
}
void RenderTableSection::detach(RenderArena* arena)
{
if (table())
table()->setNeedSectionRecalc();
RenderBox::detach(arena);
}
void RenderTableSection::setStyle(RenderStyle* _style)
{
if (style())
_style->setDisplay(style()->display());
else if (_style->display() != TABLE_FOOTER_GROUP && _style->display() != TABLE_HEADER_GROUP)
_style->setDisplay(TABLE_ROW_GROUP);
RenderBox::setStyle(_style);
}
void RenderTableSection::addChild(RenderObject *child, RenderObject *beforeChild)
{
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(TableSection)::addChild( " << child->renderName() << ", beforeChild=" <<
(beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
#endif
RenderObject *row = child;
if (child->element() && child->element()->id() == ID_FORM) {
RenderContainer::addChild(child,beforeChild);
return;
}
if ( !child->isTableRow() ) {
if( !beforeChild )
beforeChild = lastChild();
if( beforeChild && beforeChild->isAnonymousBox() )
row = beforeChild;
else {
RenderObject *lastBox = beforeChild;
while ( lastBox && lastBox->parent()->isAnonymousBox() && !lastBox->isTableRow() )
lastBox = lastBox->parent();
if ( lastBox && lastBox->isAnonymousBox() ) {
lastBox->addChild( child, beforeChild );
return;
} else {
row = new (renderArena()) RenderTableRow(0 );
RenderStyle *newStyle = new RenderStyle();
newStyle->inheritFrom(style());
newStyle->setDisplay( TABLE_ROW );
row->setStyle(newStyle);
row->setIsAnonymousBox(true);
addChild(row, beforeChild);
}
}
row->addChild(child);
child->setNeedsLayoutAndMinMaxRecalc();
return;
}
if (beforeChild)
setNeedCellRecalc();
cRow++;
cCol = 0;
ensureRows( cRow+1 );
if (!beforeChild) {
grid[cRow].height = child->style()->height();
if ( grid[cRow].height.type == Relative )
grid[cRow].height = Length();
}
RenderContainer::addChild(child,beforeChild);
}
void RenderTableSection::ensureRows( int numRows )
{
int nRows = grid.size();
int nCols = table()->numEffCols();
if ( numRows > nRows ) {
grid.resize( numRows );
for ( int r = nRows; r < numRows; r++ ) {
grid[r].row = new Row( nCols );
grid[r].row->fill( 0 );
grid[r].baseLine = 0;
grid[r].height = Length();
}
}
}
void RenderTableSection::addCell( RenderTableCell *cell )
{
int rSpan = cell->rowSpan();
int cSpan = cell->colSpan();
QMemArray<RenderTable::ColumnStruct> &columns = table()->columns;
int nCols = columns.size();
#if 0
bool found = false;
while ( !found ) {
found = true;
while ( cCol < nCols && cellAt( cRow, cCol ) )
cCol++;
int pos = cCol;
int span = 0;
while ( pos < nCols && span < cSpan ) {
if ( cellAt( cRow, pos ) ) {
found = false;
cCol = pos;
break;
}
span += columns[pos].span;
pos++;
}
}
#else
while ( cCol < nCols && cellAt( cRow, cCol ) )
cCol++;
#endif
if ( rSpan == 1 ) {
Length height = cell->style()->height();
if ( height.value > 0 || (height.type == Relative && height.value >= 0) ) {
Length cRowHeight = grid[cRow].height;
switch( height.type ) {
case Percent:
if ( !(cRowHeight.type == Percent) ||
( cRowHeight.type == Percent && cRowHeight.value < height.value ) )
grid[cRow].height = height;
break;
case Fixed:
if ( cRowHeight.type < Percent ||
( cRowHeight.type == Fixed && cRowHeight.value < height.value ) )
grid[cRow].height = height;
break;
case Relative:
#if 0
if ( cRowHeight.type == Variable ||
( cRowHeight.type == Relative && cRowHeight.value < height.value ) )
grid[cRow].height = height;
break;
#endif
default:
break;
}
}
}
ensureRows( cRow + rSpan );
int col = cCol;
RenderTableCell *set = cell;
while ( cSpan ) {
int currentSpan;
if ( cCol >= nCols ) {
table()->appendColumn( cSpan );
currentSpan = cSpan;
} else {
if ( cSpan < columns[cCol].span )
table()->splitColumn( cCol, cSpan );
currentSpan = columns[cCol].span;
}
int r = 0;
while ( r < rSpan ) {
if ( !cellAt( cRow + r, cCol ) ) {
cellAt( cRow + r, cCol ) = set;
}
r++;
}
cCol++;
cSpan -= currentSpan;
set = (RenderTableCell *)-1;
}
if ( cell ) {
cell->setRow( cRow );
cell->setCol( table()->effColToCol( col ) );
}
}
void RenderTableSection::setCellWidths()
{
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()" << endl;
#endif
QMemArray<int> &columnPos = table()->columnPos;
int rows = grid.size();
for ( int i = 0; i < rows; i++ ) {
Row &row = *grid[i].row;
int cols = row.size();
for ( int j = 0; j < cols; j++ ) {
RenderTableCell *cell = row[j];
if ( !cell || cell == (RenderTableCell *)-1 )
continue;
int endCol = j;
int cspan = cell->colSpan();
while ( cspan && endCol < cols ) {
cspan -= table()->columns[endCol].span;
endCol++;
}
int w = columnPos[endCol] - columnPos[j] - table()->cellSpacing();
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << "setting width of cell " << cell << " " << cell->row() << "/" << cell->col() << " to " << w << " colspan=" << cell->colSpan() << " start=" << j << " end=" << endCol << endl;
#endif
int oldWidth = cell->width();
if ( w != oldWidth ) {
cell->setNeedsLayout(true);
cell->setWidth( w );
}
}
}
}
void RenderTableSection::calcRowHeight()
{
int indx;
RenderTableCell *cell;
int totalRows = grid.size();
int spacing = table()->cellSpacing();
rowPos.resize( totalRows + 1 );
rowPos[0] = spacing + borderTop();
for ( int r = 0; r < totalRows; r++ ) {
rowPos[r+1] = 0;
int baseline=0;
int bdesc = 0;
int ch = grid[r].height.minWidth( 0 );
int pos = rowPos[ r+1 ] + ch + table()->cellSpacing();
if ( pos > rowPos[r+1] )
rowPos[r+1] = pos;
Row *row = grid[r].row;
int totalCols = row->size();
int totalRows = grid.size();
for ( int c = 0; c < totalCols; c++ ) {
cell = cellAt(r, c);
if ( !cell || cell == (RenderTableCell *)-1 )
continue;
if ( r < totalRows - 1 && cellAt(r+1, c) == cell )
continue;
if ( ( indx = r - cell->rowSpan() + 1 ) < 0 )
indx = 0;
ch = cell->style()->height().width(0);
if ( cell->height() > ch)
ch = cell->height();
pos = rowPos[ indx ] + ch + table()->cellSpacing();
if ( pos > rowPos[r+1] )
rowPos[r+1] = pos;
EVerticalAlign va = cell->style()->verticalAlign();
if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP
|| va == SUPER || va == SUB)
{
int b=cell->baselinePosition();
if (b>baseline)
baseline=b;
int td = rowPos[ indx ] + ch - b;
if (td>bdesc)
bdesc = td;
}
}
if (baseline) {
int bRowPos = baseline + bdesc + table()->cellSpacing() ; if (rowPos[r+1]<bRowPos)
rowPos[r+1]=bRowPos;
grid[r].baseLine = baseline;
}
if ( rowPos[r+1] < rowPos[r] )
rowPos[r+1] = rowPos[r];
}
}
int RenderTableSection::layoutRows( int toAdd )
{
int rHeight;
int rindx;
int totalRows = grid.size();
int spacing = table()->cellSpacing();
if (toAdd && totalRows && (rowPos[totalRows] || !nextSibling())) {
int totalHeight = rowPos[totalRows] + toAdd;
int dh = toAdd;
int totalPercent = 0;
int numVariable = 0;
for ( int r = 0; r < totalRows; r++ ) {
if ( grid[r].height.type == Variable )
numVariable++;
else if ( grid[r].height.type == Percent )
totalPercent += grid[r].height.value;
}
if ( totalPercent ) {
int add = 0;
if ( totalPercent > 100 )
totalPercent = 100;
int rh = rowPos[1]-rowPos[0];
for ( int r = 0; r < totalRows; r++ ) {
if ( totalPercent > 0 && grid[r].height.type == Percent ) {
int toAdd = QMIN(dh, (totalHeight * grid[r].height.value / 100)-rh);
toAdd = QMAX(0, toAdd);
add += toAdd;
dh -= toAdd;
totalPercent -= grid[r].height.value;
}
if ( r < totalRows-1 )
rh = rowPos[r+2] - rowPos[r+1];
rowPos[r+1] += add;
}
}
if ( numVariable ) {
int add = 0;
for ( int r = 0; r < totalRows; r++ ) {
if ( numVariable > 0 && grid[r].height.type == Variable ) {
int toAdd = dh/numVariable;
add += toAdd;
dh -= toAdd;
}
rowPos[r+1] += add;
}
}
if (dh>0) {
int tot=rowPos[totalRows];
int add=0;
int prev=rowPos[0];
for ( int r = 0; r < totalRows; r++ ) {
add+=dh*(rowPos[r+1]-prev)/tot;
prev=rowPos[r+1];
rowPos[r+1]+=add;
}
}
}
int leftOffset = borderLeft() + spacing;
int nEffCols = table()->numEffCols();
for ( int r = 0; r < totalRows; r++ )
{
Row *row = grid[r].row;
int totalCols = row->size();
for ( int c = 0; c < nEffCols; c++ )
{
RenderTableCell *cell = cellAt(r, c);
if (!cell || cell == (RenderTableCell *)-1 )
continue;
if ( r < totalRows - 1 && cell == cellAt(r+1, c) )
continue;
if ( ( rindx = r-cell->rowSpan()+1 ) < 0 )
rindx = 0;
rHeight = rowPos[r+1] - rowPos[rindx] - spacing;
bool cellChildrenFlex = false;
RenderObject* o = cell->firstChild();
while (o) {
if (o->style()->height().isPercent()) {
o->setNeedsLayout(true);
cellChildrenFlex = true;
}
o = o->nextSibling();
}
if (cellChildrenFlex) {
cell->setCellPercentageHeight(rHeight);
cell->layoutIfNeeded();
cell->setCellTopExtra(0);
cell->setCellBottomExtra(0);
}
else {
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << "setting position " << r << "/" << c << ": "
<< table()->columnPos[c] << "/" << rowPos[rindx] << " height=" << rHeight<< endl;
#endif
EVerticalAlign va = cell->style()->verticalAlign();
int te=0;
switch (va)
{
case SUB:
case SUPER:
case TEXT_TOP:
case TEXT_BOTTOM:
case BASELINE:
te = getBaseline(r) - cell->baselinePosition() ;
break;
case TOP:
te = 0;
break;
case MIDDLE:
te = (rHeight - cell->height())/2;
break;
case BOTTOM:
te = rHeight - cell->height();
break;
default:
break;
}
#ifdef DEBUG_LAYOUT
#endif
cell->setCellTopExtra( te );
cell->setCellBottomExtra( rHeight - cell->height() - te);
}
if (style()->direction()==RTL) {
cell->setPos(
table()->columnPos[(int)totalCols] -
table()->columnPos[table()->colToEffCol(cell->col()+cell->colSpan())] +
leftOffset,
rowPos[rindx] );
} else {
cell->setPos( table()->columnPos[c] + leftOffset, rowPos[rindx] );
}
}
}
m_height = rowPos[totalRows];
return m_height;
}
void RenderTableSection::paint( QPainter *p, int x, int y, int w, int h,
int tx, int ty, PaintAction paintAction)
{
unsigned int totalRows = grid.size();
unsigned int totalCols = table()->columns.size();
tx += m_x;
ty += m_y;
unsigned int startrow = 0;
unsigned int endrow = totalRows;
for ( ; startrow < totalRows; startrow++ ) {
if ( ty + rowPos[startrow+1] > y )
break;
}
for ( ; endrow > 0; endrow-- ) {
if ( ty + rowPos[endrow-1] < y + h )
break;
}
unsigned int startcol = 0;
unsigned int endcol = totalCols;
if ( style()->direction() == LTR ) {
for ( ; startcol < totalCols; startcol++ ) {
if ( tx + table()->columnPos[startcol+1] > x )
break;
}
for ( ; endcol > 0; endcol-- ) {
if ( tx + table()->columnPos[endcol-1] < x + w )
break;
}
}
if ( startcol < endcol ) {
for ( unsigned int r = startrow; r < endrow; r++ ) {
unsigned int c = startcol;
while ( c && cellAt( r, c ) == (RenderTableCell *)-1 )
c--;
for ( ; c < endcol; c++ ) {
RenderTableCell *cell = cellAt(r, c);
if (!cell || cell == (RenderTableCell *)-1 )
continue;
if ( (r < endrow - 1) && (cellAt(r+1, c) == cell) )
continue;
#ifdef TABLE_PRINT
kdDebug( 6040 ) << "painting cell " << r << "/" << c << endl;
#endif
cell->paint( p, x, y, w, h, tx, ty, paintAction);
}
}
}
}
void RenderTableSection::recalcCells()
{
cCol = 0;
cRow = -1;
clearGrid();
grid.resize( 0 );
RenderObject *row = firstChild();
while ( row ) {
cRow++;
cCol = 0;
ensureRows( cRow+1 );
RenderObject *cell = row->firstChild();
while ( cell ) {
if ( cell->isTableCell() )
addCell( static_cast<RenderTableCell *>(cell) );
cell = cell->nextSibling();
}
row = row->nextSibling();
}
needCellRecalc = false;
setNeedsLayout(true);
}
void RenderTableSection::clearGrid()
{
int rows = grid.size();
while ( rows-- ) {
delete grid[rows].row;
}
}
RenderObject* RenderTableSection::removeChildNode(RenderObject* child)
{
setNeedCellRecalc();
return RenderContainer::removeChildNode( child );
}
#ifndef NDEBUG
void RenderTableSection::dump(QTextStream *stream, QString ind) const
{
*stream << endl << ind << "grid=(" << grid.size() << "," << table()->numEffCols() << ")" << endl << ind;
for ( unsigned int r = 0; r < grid.size(); r++ ) {
for ( int c = 0; c < table()->numEffCols(); c++ ) {
if ( cellAt( r, c ) && cellAt( r, c ) != (RenderTableCell *)-1 )
*stream << "(" << cellAt( r, c )->row() << "," << cellAt( r, c )->col() << ","
<< cellAt(r, c)->rowSpan() << "," << cellAt(r, c)->colSpan() << ") ";
else
*stream << cellAt( r, c ) << "null cell ";
}
*stream << endl << ind;
}
RenderContainer::dump(stream,ind);
}
#endif
RenderTableRow::RenderTableRow(DOM::NodeImpl* node)
: RenderContainer(node)
{
setInline(false); }
void RenderTableRow::detach(RenderArena* arena)
{
RenderTableSection *s = section();
if (s) {
s->setNeedCellRecalc();
}
RenderContainer::detach(arena);
}
void RenderTableRow::setStyle(RenderStyle* style)
{
style->setDisplay(TABLE_ROW);
RenderContainer::setStyle(style);
}
void RenderTableRow::addChild(RenderObject *child, RenderObject *beforeChild)
{
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(TableRow)::addChild( " << child->renderName() << " )" << ", " <<
(beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
#endif
if (child->element() && child->element()->id() == ID_FORM) {
RenderContainer::addChild(child,beforeChild);
return;
}
RenderTableCell *cell;
if ( !child->isTableCell() ) {
RenderObject *last = beforeChild;
if ( !last )
last = lastChild();
RenderTableCell *cell = 0;
if( last && last->isAnonymousBox() && last->isTableCell() )
cell = static_cast<RenderTableCell *>(last);
else {
cell = new (renderArena()) RenderTableCell(0 );
RenderStyle *newStyle = new RenderStyle();
newStyle->inheritFrom(style());
newStyle->setDisplay( TABLE_CELL );
cell->setStyle(newStyle);
cell->setIsAnonymousBox(true);
addChild(cell, beforeChild);
}
cell->addChild(child);
child->setNeedsLayoutAndMinMaxRecalc();
return;
} else
cell = static_cast<RenderTableCell *>(child);
static_cast<RenderTableSection *>(parent())->addCell( cell );
RenderContainer::addChild(cell,beforeChild);
if ( ( beforeChild || nextSibling()) && section() )
section()->setNeedCellRecalc();
}
RenderObject* RenderTableRow::removeChildNode(RenderObject* child)
{
return RenderContainer::removeChildNode( child );
}
#ifndef NDEBUG
void RenderTableRow::dump(QTextStream *stream, QString ind) const
{
RenderContainer::dump(stream,ind);
}
#endif
void RenderTableRow::layout()
{
KHTMLAssert( needsLayout() );
KHTMLAssert( minMaxKnown() );
RenderObject *child = firstChild();
while( child ) {
if ( child->isTableCell() && child->needsLayout() ) {
RenderTableCell *cell = static_cast<RenderTableCell *>(child);
cell->calcVerticalMargins();
cell->layout();
cell->setCellTopExtra(0);
cell->setCellBottomExtra(0);
}
child = child->nextSibling();
}
setNeedsLayout(false);
}
void RenderTableRow::repaint(bool immediate)
{
RenderTable* parentTable = table();
if (parentTable)
parentTable->repaint(immediate);
}
RenderTableCell::RenderTableCell(DOM::NodeImpl* _node)
: RenderBlock(_node)
{
_col = -1;
_row = -1;
updateFromElement();
setShouldPaintBackgroundOrBorder(true);
_topExtra = 0;
_bottomExtra = 0;
m_percentageHeight = 0;
}
void RenderTableCell::detach(RenderArena* arena)
{
if (parent() && section())
section()->setNeedCellRecalc();
RenderBlock::detach(arena);
}
void RenderTableCell::updateFromElement()
{
DOM::NodeImpl *node = element();
if ( node && (node->id() == ID_TD || node->id() == ID_TH) ) {
DOM::HTMLTableCellElementImpl *tc = static_cast<DOM::HTMLTableCellElementImpl *>(node);
cSpan = tc->colSpan();
rSpan = tc->rowSpan();
} else {
cSpan = rSpan = 1;
}
}
int RenderTableCell::getCellPercentageHeight() const
{
return m_percentageHeight;
}
void RenderTableCell::setCellPercentageHeight(int h)
{
m_percentageHeight = h;
}
void RenderTableCell::calcMinMaxWidth()
{
RenderBlock::calcMinMaxWidth();
if (element() && style()->whiteSpace() == NORMAL) {
DOMString nowrap = static_cast<ElementImpl*>(element())->getAttribute(ATTR_NOWRAP);
if (!nowrap.isNull() && style()->width().isFixed())
if (m_minWidth < style()->width().value)
m_minWidth = style()->width().value;
}
}
void RenderTableCell::calcWidth()
{
}
void RenderTableCell::setWidth( int width )
{
assert(width <= SHRT_MAX);
if ( width != m_width ) {
m_width = width;
m_widthChanged = true;
}
}
void RenderTableCell::layout()
{
layoutBlock(m_widthChanged);
m_widthChanged = false;
}
void RenderTableCell::close()
{
RenderBlock::close();
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(RenderTableCell)::close() total height =" << m_height << endl;
#endif
}
void RenderTableCell::repaintRectangle(int x, int y, int w, int h, bool immediate, bool f)
{
y += _topExtra;
RenderBlock::repaintRectangle(x, y, w, h, immediate, f);
}
bool RenderTableCell::absolutePosition(int &xPos, int &yPos, bool f)
{
bool ret = RenderBlock::absolutePosition(xPos, yPos, f);
if (ret)
yPos += _topExtra;
return ret;
}
short RenderTableCell::baselinePosition( bool ) const
{
RenderObject *o = firstChild();
int offset = paddingTop() + borderTop();
if ( !o ) return offset;
while ( o->firstChild() ) {
if ( !o->isInline() )
offset += o->paddingTop() + o->borderTop();
o = o->firstChild();
}
offset += o->baselinePosition( true );
return offset;
}
void RenderTableCell::setStyle( RenderStyle *style )
{
style->setDisplay(TABLE_CELL);
RenderBlock::setStyle( style );
setShouldPaintBackgroundOrBorder(true);
if (style->whiteSpace() == KHTML_NOWRAP) {
if (style->width().isFixed())
style->setWhiteSpace(NORMAL);
else
style->setWhiteSpace(NOWRAP);
}
}
#ifdef BOX_DEBUG
#include <qpainter.h>
static void outlineBox(QPainter *p, int _tx, int _ty, int w, int h)
{
p->setPen(QPen(QColor("yellow"), 3, Qt::DotLine));
p->setBrush( Qt::NoBrush );
p->drawRect(_tx, _ty, w, h );
}
#endif
void RenderTableCell::paint(QPainter *p, int _x, int _y,
int _w, int _h, int _tx, int _ty, PaintAction paintAction)
{
#ifdef TABLE_PRINT
kdDebug( 6040 ) << renderName() << "(RenderTableCell)::paint() w/h = (" << width() << "/" << height() << ")" << " _y/_h=" << _y << "/" << _h << endl;
#endif
if (needsLayout()) return;
_tx += m_x;
_ty += m_y + _topExtra;
if(!overhangingContents() && ((_ty-_topExtra > _y + _h)
|| (_ty + m_height + _bottomExtra < _y))) return;
paintObject(p, _x, _y, _w, _h, _tx, _ty, paintAction);
#ifdef BOX_DEBUG
::outlineBox( p, _tx, _ty - _topExtra, width(), height() + borderTopExtra() + borderBottomExtra());
#endif
}
void RenderTableCell::paintBoxDecorations(QPainter *p,int, int _y,
int, int _h, int _tx, int _ty)
{
int w = width();
int h = height() + borderTopExtra() + borderBottomExtra();
_ty -= borderTopExtra();
QColor c = style()->backgroundColor();
if ( !c.isValid() && parent() ) c = parent()->style()->backgroundColor();
if ( !c.isValid() && parent() && parent()->parent() ) c = parent()->parent()->style()->backgroundColor();
if ( !c.isValid() ) {
RenderTableCol *col = table()->colElement( _col );
if ( col ) {
c = col->style()->backgroundColor();
if ( !c.isValid() ) {
RenderStyle *style = col->parent()->style();
if ( style->display() == TABLE_COLUMN_GROUP )
c = style->backgroundColor();
}
}
}
CachedImage *bg = style()->backgroundImage();
if ( !bg && parent() )
bg = parent()->style()->backgroundImage();
if ( !bg && parent() && parent()->parent() )
bg = parent()->parent()->style()->backgroundImage();
if ( !bg ) {
RenderTableCol *col = table()->colElement( _col );
if ( col ) {
bg = col->style()->backgroundImage();
if ( !bg ) {
RenderStyle *style = col->parent()->style();
if ( style->display() == TABLE_COLUMN_GROUP )
bg = style->backgroundImage();
}
}
}
int my = QMAX(_ty,_y);
int end = QMIN( _y + _h, _ty + h );
int mh = end - my;
if ( bg || c.isValid() )
paintBackground(p, c, bg, my, mh, _tx, _ty, w, h);
if(style()->hasBorder())
paintBorder(p, _tx, _ty, w, h, style());
}
#ifndef NDEBUG
void RenderTableCell::dump(QTextStream *stream, QString ind) const
{
*stream << " row=" << _row;
*stream << " col=" << _col;
*stream << " rSpan=" << rSpan;
*stream << " cSpan=" << cSpan;
RenderBlock::dump(stream,ind);
}
#endif
RenderTableCol::RenderTableCol(DOM::NodeImpl* node)
: RenderContainer(node)
{
setInline(true);
_span = 1;
updateFromElement();
}
void RenderTableCol::updateFromElement()
{
DOM::NodeImpl *node = element();
if ( node && (node->id() == ID_COL || node->id() == ID_COLGROUP) ) {
DOM::HTMLTableColElementImpl *tc = static_cast<DOM::HTMLTableColElementImpl *>(node);
_span = tc->span();
} else
_span = ! ( style() && style()->display() == TABLE_COLUMN_GROUP );
}
bool RenderTableCol::canHaveChildren() const
{
return style()->display() == TABLE_COLUMN_GROUP;
}
void RenderTableCol::addChild(RenderObject *child, RenderObject *beforeChild)
{
#ifdef DEBUG_LAYOUT
#endif
KHTMLAssert(child->style()->display() == TABLE_COLUMN);
RenderContainer::addChild(child,beforeChild);
}
#ifndef NDEBUG
void RenderTableCol::dump(QTextStream *stream, QString ind) const
{
*stream << " _span=" << _span;
RenderContainer::dump(stream,ind);
}
#endif
#undef TABLE_DEBUG
#undef DEBUG_LAYOUT
#undef BOX_DEBUG