📄 render_flow.cpp
字号:
/**
* This file is part of the html renderer for KDE.
*
* Copyright (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.
*/
// -------------------------------------------------------------------------
#include <kdebug.h>
#include <assert.h>
#include <qpainter.h>
#include <kglobal.h>
#include "rendering/render_flow.h"
#include "rendering/render_text.h"
#include "rendering/render_table.h"
#include "rendering/render_canvas.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_docimpl.h"
#include "html/html_formimpl.h"
#include "render_inline.h"
#include "render_block.h"
#include "render_arena.h"
#include "render_line.h"
#include "khtmlview.h"
#include "htmltags.h"
using namespace DOM;
using namespace khtml;
RenderFlow* RenderFlow::createAnonymousFlow(DOM::DocumentImpl* doc, RenderStyle* style)
{
RenderFlow* result;
if (style->display() == INLINE)
result = new (doc->renderArena()) RenderInline(doc);
else
result = new (doc->renderArena()) RenderBlock(doc);
result->setStyle(style);
return result;
}
RenderFlow* RenderFlow::continuationBefore(RenderObject* beforeChild)
{
if (beforeChild && beforeChild->parent() == this)
return this;
RenderFlow* curr = continuation();
RenderFlow* nextToLast = this;
RenderFlow* last = this;
while (curr) {
if (beforeChild && beforeChild->parent() == curr) {
if (curr->firstChild() == beforeChild)
return last;
return curr;
}
nextToLast = last;
last = curr;
curr = curr->continuation();
}
if (!beforeChild && !last->firstChild())
return nextToLast;
return last;
}
void RenderFlow::addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild)
{
RenderFlow* flow = continuationBefore(beforeChild);
KHTMLAssert(!beforeChild || beforeChild->parent()->isRenderBlock() ||
beforeChild->parent()->isRenderInline());
RenderFlow* beforeChildParent = beforeChild ? static_cast<RenderFlow*>(beforeChild->parent()) :
(flow->continuation() ? flow->continuation() : flow);
if (newChild->isFloatingOrPositioned())
return beforeChildParent->addChildToFlow(newChild, beforeChild);
// A continuation always consists of two potential candidates: an inline or an anonymous
// block box holding block children.
bool childInline = newChild->isInline();
bool bcpInline = beforeChildParent->isInline();
bool flowInline = flow->isInline();
if (flow == beforeChildParent)
return flow->addChildToFlow(newChild, beforeChild);
else {
// The goal here is to match up if we can, so that we can coalesce and create the
// minimal # of continuations needed for the inline.
if (childInline == bcpInline)
return beforeChildParent->addChildToFlow(newChild, beforeChild);
else if (flowInline == childInline)
return flow->addChildToFlow(newChild, 0); // Just treat like an append.
else
return beforeChildParent->addChildToFlow(newChild, beforeChild);
}
}
void RenderFlow::addChild(RenderObject *newChild, RenderObject *beforeChild)
{
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(RenderFlow)::addChild( " << newChild->renderName() <<
", " << (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
kdDebug( 6040 ) << "current height = " << m_height << endl;
#endif
if (continuation())
return addChildWithContinuation(newChild, beforeChild);
return addChildToFlow(newChild, beforeChild);
}
void RenderFlow::extractLineBox(InlineFlowBox* box)
{
m_lastLineBox = box->prevFlowBox();
if (box == m_firstLineBox)
m_firstLineBox = 0;
if (box->prevLineBox())
box->prevLineBox()->setNextLineBox(0);
box->setPreviousLineBox(0);
for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
curr->setExtracted();
}
void RenderFlow::attachLineBox(InlineFlowBox* box)
{
if (m_lastLineBox) {
m_lastLineBox->setNextLineBox(box);
box->setPreviousLineBox(m_lastLineBox);
}
else
m_firstLineBox = box;
InlineFlowBox* last = box;
for (InlineFlowBox* curr = box; curr; curr = curr->nextFlowBox()) {
curr->setExtracted(false);
last = curr;
}
m_lastLineBox = last;
}
void RenderFlow::removeLineBox(InlineFlowBox* box)
{
if (box == m_firstLineBox)
m_firstLineBox = box->nextFlowBox();
if (box == m_lastLineBox)
m_lastLineBox = box->prevFlowBox();
if (box->nextLineBox())
box->nextLineBox()->setPreviousLineBox(box->prevLineBox());
if (box->prevLineBox())
box->prevLineBox()->setNextLineBox(box->nextLineBox());
}
void RenderFlow::deleteLineBoxes()
{
if (m_firstLineBox) {
RenderArena* arena = renderArena();
InlineRunBox *curr=m_firstLineBox, *next=0;
while (curr) {
next = curr->nextLineBox();
curr->detach(arena);
curr = next;
}
m_firstLineBox = 0;
m_lastLineBox = 0;
}
}
void RenderFlow::detach()
{
if (!documentBeingDestroyed()) {
if (m_firstLineBox) {
// We can't wait for RenderContainer::detach to clear the selection,
// because by then we will have nuked the line boxes.
if (isSelectionBorder())
canvas()->clearSelection();
// If line boxes are contained inside a root, that means we're an inline.
// In that case, we need to remove all the line boxes so that the parent
// lines aren't pointing to deleted children. If the first line box does
// not have a parent that means they are either already disconnected or
// root lines that can just be destroyed without disconnecting.
if (m_firstLineBox->parent()) {
for (InlineRunBox* box = m_firstLineBox; box; box = box->nextLineBox())
box->remove();
}
// If we are an anonymous block, then our line boxes might have children
// that will outlast this block. In the non-anonymous block case those
// children will be destroyed by the time we return from this function.
if (isAnonymousBlock()) {
for (InlineFlowBox* box = m_firstLineBox; box; box = box->nextFlowBox()) {
while (InlineBox *childBox = box->firstChild()) {
childBox->remove();
}
}
}
}
else if (isInline() && parent())
parent()->dirtyLinesFromChangedChild(this, false);
}
deleteLineBoxes();
if (continuation()) {
continuation()->detach();
setContinuation(0);
}
RenderContainer::detach();
}
void RenderFlow::dirtyLinesFromChangedChild(RenderObject* child, bool adding)
{
if (!parent() || selfNeedsLayout() || isTable())
return;
// For an empty inline, go ahead and propagate the check up to our parent.
if (isInline() && !firstLineBox())
return parent()->dirtyLinesFromChangedChild(this);
// Try to figure out which line box we belong in. First try to find a previous
// line box by examining our siblings. If we didn't find a line box, then use our
// parent's first line box.
RootInlineBox* box = 0;
RenderObject* curr = 0;
for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
if (curr->isFloatingOrPositioned())
continue;
if (curr->isReplaced()) {
InlineBox* wrapper = curr->inlineBoxWrapper();
if (wrapper)
box = wrapper->root();
}
else if (curr->isText()) {
InlineTextBox* textBox = static_cast<RenderText*>(curr)->lastTextBox();
if (textBox)
box = textBox->root();
}
else if (curr->isInlineFlow()) {
InlineRunBox* runBox = static_cast<RenderFlow*>(curr)->lastLineBox();
if (runBox)
box = runBox->root();
}
if (box)
break;
}
if (!box && firstLineBox())
box = firstLineBox()->root();
// If we found a line box, then dirty it.
if (box) {
RootInlineBox* adjacentBox;
box->markDirty();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -