📄 rendertable.cpp
字号:
/* * 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, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) * * 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */#include "config.h"#include "RenderTable.h"#include "AutoTableLayout.h"#include "DeleteButtonController.h"#include "Document.h"#include "FixedTableLayout.h"#include "FrameView.h"#include "HTMLNames.h"#include "RenderLayer.h"#include "RenderTableCell.h"#include "RenderTableCol.h"#include "RenderTableSection.h"#include "RenderView.h"using namespace std;namespace WebCore {using namespace HTMLNames;RenderTable::RenderTable(Node* node) : RenderBlock(node) , m_caption(0) , m_head(0) , m_foot(0) , m_firstBody(0) , m_tableLayout(0) , m_currentBorder(0) , m_hasColElements(false) , m_needsSectionRecalc(0) , m_hSpacing(0) , m_vSpacing(0) , m_borderLeft(0) , m_borderRight(0){ m_columnPos.fill(0, 2); m_columns.fill(ColumnStruct(), 1);}RenderTable::~RenderTable(){ delete m_tableLayout;}void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle){ RenderBlock::styleDidChange(diff, oldStyle); ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO; // In the collapsed border model, there is no cell spacing. m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing(); m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing(); m_columnPos[0] = m_hSpacing; if (!m_tableLayout || style()->tableLayout() != oldTableLayout) { delete m_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().isAuto()) m_tableLayout = new FixedTableLayout(this); else m_tableLayout = new AutoTableLayout(this); }}static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before){ if (!before || !ptr) return; RenderObject* o = before->previousSibling(); while (o && o != ptr) o = o->previousSibling(); if (!o) ptr = 0;}void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild){ // Make sure we don't append things after :after-generated content if we have it. if (!beforeChild && isAfterContent(lastChild())) beforeChild = lastChild(); bool wrapInAnonymousSection = !child->isPositioned(); bool isTableElement = node() && node()->hasTagName(tableTag); if (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION) { // First caption wins. if (beforeChild && m_caption) { RenderObject* o = beforeChild->previousSibling(); while (o && o != m_caption) o = o->previousSibling(); if (!o) m_caption = 0; } if (!m_caption) m_caption = toRenderBlock(child); wrapInAnonymousSection = false; } else if (child->isTableCol()) { m_hasColElements = true; wrapInAnonymousSection = false; } else if (child->isTableSection()) { switch (child->style()->display()) { case TABLE_HEADER_GROUP: if (child->isTableSection()) { resetSectionPointerIfNotBefore(m_head, beforeChild); if (!m_head) { m_head = static_cast<RenderTableSection*>(child); } else { resetSectionPointerIfNotBefore(m_firstBody, beforeChild); if (!m_firstBody) m_firstBody = static_cast<RenderTableSection*>(child); } } wrapInAnonymousSection = false; break; case TABLE_FOOTER_GROUP: if (child->isTableSection()) { resetSectionPointerIfNotBefore(m_foot, beforeChild); if (!m_foot) { m_foot = static_cast<RenderTableSection*>(child); wrapInAnonymousSection = false; break; } } // Fall through. case TABLE_ROW_GROUP: if (child->isTableSection()) { resetSectionPointerIfNotBefore(m_firstBody, beforeChild); if (!m_firstBody) m_firstBody = static_cast<RenderTableSection*>(child); } wrapInAnonymousSection = false; break; default: ASSERT_NOT_REACHED(); } } else if (child->isTableCell() || child->isTableRow()) { wrapInAnonymousSection = true; } else // Allow a form to just sit at the top level. wrapInAnonymousSection = !isTableElement || !child->node() || !(child->node()->hasTagName(formTag) && document()->isHTMLDocument()); if (!wrapInAnonymousSection) { // If the next renderer is actually wrapped in an anonymous table section, we need to go up and find that. while (beforeChild && !beforeChild->isTableSection() && !beforeChild->isTableCol() && beforeChild->style()->display() != TABLE_CAPTION) beforeChild = beforeChild->parent(); RenderBox::addChild(child, beforeChild); return; } if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) { lastChild()->addChild(child); return; } RenderObject* lastBox = beforeChild; while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP) lastBox = lastBox->parent(); if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) { lastBox->addChild(child, beforeChild); return; } if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP) beforeChild = 0; RenderTableSection* section = new (renderArena()) RenderTableSection(document() /* anonymous */); RefPtr<RenderStyle> newStyle = RenderStyle::create(); newStyle->inheritFrom(style()); newStyle->setDisplay(TABLE_ROW_GROUP); section->setStyle(newStyle.release()); addChild(section, beforeChild); section->addChild(child);}void RenderTable::removeChild(RenderObject* oldChild){ RenderBox::removeChild(oldChild); setNeedsSectionRecalc();}void RenderTable::calcWidth(){ if (isPositioned()) calcAbsoluteHorizontal(); RenderBlock* cb = containingBlock(); int availableWidth = cb->availableWidth(); LengthType widthType = style()->width().type(); if (widthType > Relative && style()->width().isPositive()) { // Percent or fixed table setWidth(style()->width().calcMinValue(availableWidth)); setWidth(max(minPrefWidth(), width())); } else { // An auto width table should shrink to fit within the line width if necessary in order to // avoid overlapping floats. availableWidth = cb->lineWidth(y(), false); // Subtract out any fixed margins from our available width for auto width tables. int marginTotal = 0; if (!style()->marginLeft().isAuto()) marginTotal += style()->marginLeft().calcValue(availableWidth); if (!style()->marginRight().isAuto()) marginTotal += style()->marginRight().calcValue(availableWidth); // Subtract out our margins to get the available content width. int availContentWidth = max(0, availableWidth - marginTotal); // Ensure we aren't bigger than our max width or smaller than our min width. setWidth(min(availContentWidth, maxPrefWidth())); } setWidth(max(width(), minPrefWidth())); // 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(){ ASSERT(needsLayout()); if (layoutOnlyPositionedObjects()) return; recalcSectionsIfNeeded(); LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y())); setHeight(0); m_overflowHeight = 0; m_overflowTop = 0; initMaxMarginValues(); int oldWidth = width(); calcWidth(); if (m_caption && width() != oldWidth) m_caption->setNeedsLayout(true, false); // FIXME: 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 != width() || columns.size() + 1 != columnPos.size() ) m_tableLayout->layout(); setCellWidths(); // layout child objects int calculatedHeight = 0; int oldTableTop = m_caption ? m_caption->height() + m_caption->marginTop() + m_caption->marginBottom() : 0; bool collapsing = collapseBorders(); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (child->isTableSection()) { child->layoutIfNeeded(); RenderTableSection* section = static_cast<RenderTableSection*>(child); calculatedHeight += section->calcRowHeight(); if (collapsing) section->recalcOuterBorder(); ASSERT(!section->needsLayout()); } } // Only lay out one caption, since it's the only one we're going to end up painting. if (m_caption) m_caption->layoutIfNeeded(); m_overflowWidth = width() + (collapsing ? outerBorderRight() - borderRight() : 0); m_overflowLeft = collapsing ? borderLeft() - outerBorderLeft() : 0; // If any table section moved vertically, we will just repaint everything from that // section down (it is quite unlikely that any of the following sections // did not shift). bool sectionMoved = false; int movedSectionTop = 0; // FIXME: Collapse caption margin. if (m_caption && m_caption->style()->captionSide() != CAPBOTTOM) { IntRect captionRect(m_caption->x(), m_caption->y(), m_caption->width(), m_caption->height()); m_caption->setLocation(m_caption->marginLeft(), height()); if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout()) m_caption->repaintDuringLayoutIfMoved(captionRect); setHeight(height() + m_caption->height() + m_caption->marginTop() + m_caption->marginBottom()); m_overflowLeft = min(m_overflowLeft, m_caption->x() + m_caption->overflowLeft(false)); m_overflowWidth = max(m_overflowWidth, m_caption->x() + m_caption->overflowWidth(false)); m_overflowTop = min(m_overflowTop, m_caption->y() + m_caption->overflowTop(false)); m_overflowHeight = max(m_overflowHeight, m_caption->y() + m_caption->overflowHeight(false)); if (height() != oldTableTop) { sectionMoved = true; movedSectionTop = min(height(), oldTableTop); } } int bpTop = borderTop() + (collapsing ? 0 : paddingTop()); int bpBottom = borderBottom() + (collapsing ? 0 : paddingBottom()); setHeight(height() + bpTop); if (!isPositioned()) calcHeight(); Length h = style()->height(); int th = 0; if (h.isFixed()) // Tables size as though CSS height includes border/padding. th = h.value() - (bpTop + bpBottom); else if (h.isPercent()) th = calcPercentageHeight(h); th = max(0, th); for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { if (child->isTableSection()) // FIXME: Distribute extra height between all table body sections instead of giving it all to the first one. static_cast<RenderTableSection*>(child)->layoutRows(child == m_firstBody ? max(0, th - calculatedHeight) : 0); } if (!m_firstBody && th > calculatedHeight && !style()->htmlHacks()) { // Completely empty tables (with no sections or anything) should at least honor specified height // in strict mode. setHeight(height() + th); } int bl = borderLeft(); if (!collapsing) bl += paddingLeft(); // position the table sections RenderTableSection* section = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); while (section) { if (!sectionMoved && section->y() != height()) { sectionMoved = true; movedSectionTop = min(height(), section->y()) + section->overflowTop(false); } section->setLocation(bl, height()); setHeight(height() + section->height()); m_overflowLeft = min(m_overflowLeft, section->x() + section->overflowLeft(false)); m_overflowWidth = max(m_overflowWidth, section->x() + section->overflowWidth(false)); m_overflowTop = min(m_overflowTop, section->y() + section->overflowTop(false)); m_overflowHeight = max(m_overflowHeight, section->y() + section->overflowHeight(false)); section = sectionBelow(section); } setHeight(height() + bpBottom); if (m_caption && m_caption->style()->captionSide() == CAPBOTTOM) { IntRect captionRect(m_caption->x(), m_caption->y(), m_caption->width(), m_caption->height()); m_caption->setLocation(m_caption->marginLeft(), height()); if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout()) m_caption->repaintDuringLayoutIfMoved(captionRect); setHeight(height() + m_caption->height() + m_caption->marginTop() + m_caption->marginBottom()); m_overflowLeft = min(m_overflowLeft, m_caption->x() + m_caption->overflowLeft(false));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -