📄 render_table.cpp
字号:
/**
* This file is part of the DOM implementation for KDE.
*
* Copyright (C) 1997 Martin Jones (mjones@kde.org)
* (C) 1997 Torben Weis (weis@kde.org)
* (C) 1998 Waldo Bastian (bastian@kde.org)
* (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* Copyright (C) 2003 Apple Computer, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
//#define TABLE_DEBUG
//#define TABLE_PRINT
//#define DEBUG_LAYOUT
//#define BOX_DEBUG
#include "rendering/render_table.h"
#include "rendering/table_layout.h"
#include "html/html_tableimpl.h"
#include "misc/htmltags.h"
#include "misc/htmlattrs.h"
#include "xml/dom_docimpl.h"
#include <kglobal.h>
#include <qapplication.h>
#include <qstyle.h>
#include <kdebug.h>
#include <assert.h>
#include "khtmlview.h"
using namespace khtml;
using namespace DOM;
RenderTable::RenderTable(DOM::NodeImpl* node)
: RenderBlock(node)
{
tCaption = 0;
head = foot = firstBody = 0;
tableLayout = 0;
m_currentBorder = 0;
rules = None;
frame = Void;
has_col_elems = false;
hspacing = 0;
vspacing = 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;
RenderBlock::setStyle(_style);
// In the collapsed border model, there is no cell spacing.
hspacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing();
vspacing = collapseBorders() ? 0 : style()->verticalBorderSpacing();
columnPos[0] = hspacing;
if ( !tableLayout || style()->tableLayout() != oldTableLayout ) {
delete tableLayout;
// According to the CSS2 spec, you only use fixed table layout if an
// explicit width is specified on the table. Auto width implies auto table layout.
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;
}
// fall through
case TABLE_ROW_GROUP:
if(!firstBody)
firstBody = static_cast<RenderTableSection *>(child);
break;
default:
if ( !beforeChild && lastChild() &&
lastChild()->isTableSection() && lastChild()->isAnonymous() ) {
o = lastChild();
} else {
RenderObject *lastBox = beforeChild;
while ( lastBox && lastBox->parent()->isAnonymous() &&
!lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION )
lastBox = lastBox->parent();
if ( lastBox && lastBox->isAnonymous() ) {
lastBox->addChild( child, beforeChild );
return;
} else {
if ( beforeChild && !beforeChild->isTableSection() )
beforeChild = 0;
//kdDebug( 6040 ) << this <<" creating anonymous table section beforeChild="<< beforeChild << endl;
o = new (renderArena()) RenderTableSection(document() /* anonymous */);
RenderStyle *newStyle = new (renderArena()) RenderStyle();
newStyle->inheritFrom(style());
newStyle->setDisplay(TABLE_ROW_GROUP);
o->setStyle(newStyle);
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) {
// Percent or fixed table
m_width = style()->width().minWidth( availableWidth );
if(m_minWidth > m_width) m_width = m_minWidth;
}
else {
// An auto width table should shrink to fit within the line width if necessary in order to
// avoid overlapping floats.
availableWidth = cb->lineWidth( m_y );
// Subtract out any fixed margins from our available width for auto width tables.
int marginTotal = 0;
if (style()->marginLeft().type != Variable)
marginTotal += style()->marginLeft().width(availableWidth);
if (style()->marginRight().type != Variable)
marginTotal += style()->marginRight().width(availableWidth);
// Subtract out our margins to get the available content width.
int availContentWidth = kMax(0, availableWidth - marginTotal);
// Ensure we aren't bigger than our max width or smaller than our min width.
m_width = kMin(availContentWidth, m_maxWidth);
}
m_width = kMax(m_width, m_minWidth);
// Finally, with our true width determined, compute our margins for real.
m_marginRight = 0;
m_marginLeft = 0;
calcHorizontalMargins(style()->marginLeft(),style()->marginRight(),availableWidth);
}
void RenderTable::layout()
{
KHTMLAssert( needsLayout() );
KHTMLAssert( minMaxKnown() );
KHTMLAssert( !needSectionRecalc );
if (posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
// All we have to is lay out our positioned objects.
layoutPositionedObjects(true);
setNeedsLayout(false);
return;
}
QRect oldBounds, oldFullBounds;
bool checkForRepaint = checkForRepaintDuringLayout();
if (checkForRepaint)
getAbsoluteRepaintRectIncludingFloats(oldBounds, oldFullBounds);
//kdDebug( 6040 ) << renderName() << "(Table)"<< this << " ::layout0() width=" << width() << ", needsLayout=" << needsLayout() << endl;
m_height = m_overflowHeight = 0;
initMaxMarginValues();
//int oldWidth = m_width;
calcWidth();
m_overflowWidth = m_width;
// the optimisation below doesn't work since the internal table
// layout could have changed. we need to add a flag to the table
// layout that tells us if something has changed in the min max
// calculations to do it correctly.
// if ( oldWidth != m_width || columns.size() + 1 != columnPos.size() )
tableLayout->layout();
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(Table)::layout1() width=" << width() << ", marginLeft=" << marginLeft() << " marginRight=" << marginRight() << endl;
#endif
setCellWidths();
// layout child objects
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();
}
// ### collapse caption margin
if(tCaption && tCaption->style()->captionSide() != CAPBOTTOM) {
tCaption->setPos(tCaption->marginLeft(), m_height);
m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
}
int bpTop = borderTop() + (collapseBorders() ? 0 : paddingTop());
int bpBottom = borderBottom() + (collapseBorders() ? 0 : paddingBottom());
m_height += bpTop;
int oldHeight = m_height;
calcHeight();
int newHeight = m_height;
m_height = oldHeight;
Length h = style()->height();
int th = 0;
if (isPositioned())
th = newHeight; // FIXME: Leave this alone for now but investigate later.
else if (h.isFixed())
th = h.value - (bpTop + bpBottom); // Tables size as though CSS height includes border/padding.
else if (h.isPercent())
th = calcPercentageHeight(h);
th = kMax(0, th);
// layout rows
if ( th > calculatedHeight ) {
// we have to redistribute that height to get the constraint correctly
// just force the first body to the height needed
// ### FIXME This should take height constraints on all table sections into account and distribute
// accordingly. For now this should be good enough
if (firstBody) {
firstBody->calcRowHeight();
firstBody->layoutRows( th - calculatedHeight );
}
}
int bl = borderLeft();
if (!collapseBorders())
bl += paddingLeft();
// position the table sections
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 += bpBottom;
if(tCaption && tCaption->style()->captionSide()==CAPBOTTOM) {
tCaption->setPos(tCaption->marginLeft(), m_height);
m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
}
//kdDebug(0) << "table height: " << m_height << endl;
// table can be containing block of positioned elements.
// ### only pass true if width or height changed.
layoutPositionedObjects( true );
// Repaint with our new bounds if they are different from our old bounds.
if (checkForRepaint)
repaintAfterLayoutIfNeeded(oldBounds, oldFullBounds);
m_overflowHeight = kMax(m_overflowHeight, m_height);
m_overflowWidth = kMax(m_overflowWidth, m_width);
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(PaintInfo& i, int _tx, int _ty)
{
_tx += xPos();
_ty += yPos();
PaintAction paintAction = i.phase;
#ifdef TABLE_PRINT
kdDebug( 6040 ) << "RenderTable::paint() w/h = (" << width() << "/" << height() << ")" << endl;
#endif
int os = 2*maximalOutlineSize(paintAction);
if ((_ty >= i.r.y() + i.r.height() + os) || (_ty + height() <= i.r.y() - os)) return;
if ((_tx >= i.r.x() + i.r.width() + os) || (_tx + width() <= i.r.x() - os)) return;
#ifdef TABLE_PRINT
kdDebug( 6040 ) << "RenderTable::paint(2) " << _tx << "/" << _ty << " (" << _y << "/" << _h << ")" << endl;
#endif
if ((paintAction == PaintActionBlockBackground || paintAction == PaintActionChildBlockBackground)
&& shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE)
paintBoxDecorations(i, _tx, _ty);
// We're done. We don't bother painting any children.
if (paintAction == PaintActionBlockBackground)
return;
// We don't paint our own background, but we do let the kids paint their backgrounds.
if (paintAction == PaintActionChildBlockBackgrounds)
paintAction = PaintActionChildBlockBackground;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -