2 * This file is part of the html renderer for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * Copyright (C) 2003 Apple Computer, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
23 // -------------------------------------------------------------------------
30 #include "rendering/render_flow.h"
31 #include "rendering/render_text.h"
32 #include "rendering/render_table.h"
33 #include "rendering/render_canvas.h"
34 #include "xml/dom_nodeimpl.h"
35 #include "xml/dom_docimpl.h"
36 #include "html/html_formimpl.h"
37 #include "render_inline.h"
38 #include "render_block.h"
39 #include "render_arena.h"
40 #include "render_line.h"
42 #include "khtmlview.h"
46 using namespace khtml;
48 RenderFlow* RenderFlow::createAnonymousFlow(DOM::DocumentImpl* doc, RenderStyle* style)
51 if (style->display() == INLINE)
52 result = new (doc->renderArena()) RenderInline(doc);
54 result = new (doc->renderArena()) RenderBlock(doc);
55 result->setStyle(style);
59 RenderFlow* RenderFlow::continuationBefore(RenderObject* beforeChild)
61 if (beforeChild && beforeChild->parent() == this)
64 RenderFlow* curr = continuation();
65 RenderFlow* nextToLast = this;
66 RenderFlow* last = this;
68 if (beforeChild && beforeChild->parent() == curr) {
69 if (curr->firstChild() == beforeChild)
76 curr = curr->continuation();
79 if (!beforeChild && !last->firstChild())
84 void RenderFlow::addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild)
86 RenderFlow* flow = continuationBefore(beforeChild);
87 KHTMLAssert(!beforeChild || beforeChild->parent()->isRenderBlock() ||
88 beforeChild->parent()->isRenderInline());
89 RenderFlow* beforeChildParent = beforeChild ? static_cast<RenderFlow*>(beforeChild->parent()) :
90 (flow->continuation() ? flow->continuation() : flow);
92 if (newChild->isFloatingOrPositioned())
93 return beforeChildParent->addChildToFlow(newChild, beforeChild);
95 // A continuation always consists of two potential candidates: an inline or an anonymous
96 // block box holding block children.
97 bool childInline = newChild->isInline();
98 bool bcpInline = beforeChildParent->isInline();
99 bool flowInline = flow->isInline();
101 if (flow == beforeChildParent)
102 return flow->addChildToFlow(newChild, beforeChild);
104 // The goal here is to match up if we can, so that we can coalesce and create the
105 // minimal # of continuations needed for the inline.
106 if (childInline == bcpInline)
107 return beforeChildParent->addChildToFlow(newChild, beforeChild);
108 else if (flowInline == childInline)
109 return flow->addChildToFlow(newChild, 0); // Just treat like an append.
111 return beforeChildParent->addChildToFlow(newChild, beforeChild);
115 void RenderFlow::addChild(RenderObject *newChild, RenderObject *beforeChild)
118 kdDebug( 6040 ) << renderName() << "(RenderFlow)::addChild( " << newChild->renderName() <<
119 ", " << (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
120 kdDebug( 6040 ) << "current height = " << m_height << endl;
124 return addChildWithContinuation(newChild, beforeChild);
125 return addChildToFlow(newChild, beforeChild);
128 void RenderFlow::extractLineBox(InlineFlowBox* box)
130 m_lastLineBox = box->prevFlowBox();
131 if (box == m_firstLineBox)
133 if (box->prevLineBox())
134 box->prevLineBox()->setNextLineBox(0);
135 box->setPreviousLineBox(0);
136 for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
137 curr->setExtracted();
140 void RenderFlow::attachLineBox(InlineFlowBox* box)
143 m_lastLineBox->setNextLineBox(box);
144 box->setPreviousLineBox(m_lastLineBox);
147 m_firstLineBox = box;
148 InlineFlowBox* last = box;
149 for (InlineFlowBox* curr = box; curr; curr = curr->nextFlowBox()) {
150 curr->setExtracted(false);
153 m_lastLineBox = last;
156 void RenderFlow::removeLineBox(InlineFlowBox* box)
158 if (box == m_firstLineBox)
159 m_firstLineBox = box->nextFlowBox();
160 if (box == m_lastLineBox)
161 m_lastLineBox = box->prevFlowBox();
162 if (box->nextLineBox())
163 box->nextLineBox()->setPreviousLineBox(box->prevLineBox());
164 if (box->prevLineBox())
165 box->prevLineBox()->setNextLineBox(box->nextLineBox());
168 void RenderFlow::deleteLineBoxes()
170 if (m_firstLineBox) {
171 RenderArena* arena = renderArena();
172 InlineRunBox *curr=m_firstLineBox, *next=0;
174 next = curr->nextLineBox();
183 void RenderFlow::detach()
185 if (!documentBeingDestroyed()) {
186 if (m_firstLineBox) {
187 // We can't wait for RenderContainer::detach to clear the selection,
188 // because by then we will have nuked the line boxes.
189 if (isSelectionBorder())
190 canvas()->clearSelection();
192 // If line boxes are contained inside a root, that means we're an inline.
193 // In that case, we need to remove all the line boxes so that the parent
194 // lines aren't pointing to deleted children. If the first line box does
195 // not have a parent that means they are either already disconnected or
196 // root lines that can just be destroyed without disconnecting.
197 if (m_firstLineBox->parent()) {
198 for (InlineRunBox* box = m_firstLineBox; box; box = box->nextLineBox())
202 // If we are an anonymous block, then our line boxes might have children
203 // that will outlast this block. In the non-anonymous block case those
204 // children will be destroyed by the time we return from this function.
205 if (isAnonymousBlock()) {
206 for (InlineFlowBox* box = m_firstLineBox; box; box = box->nextFlowBox()) {
207 while (InlineBox *childBox = box->firstChild()) {
213 else if (isInline() && parent())
214 parent()->dirtyLinesFromChangedChild(this, false);
219 RenderContainer::detach();
222 void RenderFlow::dirtyLinesFromChangedChild(RenderObject* child, bool adding)
224 if (!parent() || selfNeedsLayout() || isTable())
227 if (adding && !isInline() && (!child->nextSibling() || !firstLineBox())) {
228 // An append onto the end of a block or we don't have any lines anyway.
229 // In this case we don't have to dirty any specific lines.
230 static_cast<RenderBlock*>(this)->setLinesAppended();
234 // For an empty inline, go ahead and propagate the check up to our parent.
235 if (isInline() && !firstLineBox())
236 return parent()->dirtyLinesFromChangedChild(this);
238 // Try to figure out which line box we belong in. First try to find a previous
239 // line box by examining our siblings. If we didn't find a line box, then use our
240 // parent's first line box.
241 RootInlineBox* box = 0;
242 for (RenderObject* curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
243 if (curr->isFloatingOrPositioned())
246 if (curr->isReplaced()) {
247 InlineBox* wrapper = curr->inlineBoxWrapper();
249 box = wrapper->root();
251 else if (curr->isText()) {
252 InlineTextBox* textBox = static_cast<RenderText*>(curr)->lastTextBox();
254 box = textBox->root();
256 else if (curr->isInlineFlow()) {
257 InlineRunBox* runBox = static_cast<RenderFlow*>(curr)->lastLineBox();
259 box = runBox->root();
265 if (!box && firstLineBox())
266 box = firstLineBox()->root();
268 // If we found a line box, then dirty it.
272 RootInlineBox* next = box->nextRootBox();
279 short RenderFlow::lineHeight(bool firstLine, bool isRootLineBox) const
282 RenderStyle* s = style(firstLine);
283 Length lh = s->lineHeight();
286 if (m_lineHeight == -1)
287 m_lineHeight = RenderObject::lineHeight(false);
290 return s->fontMetrics().lineSpacing();
293 return lh.minWidth(s->font().pixelSize());
297 if (m_lineHeight == -1)
298 m_lineHeight = RenderObject::lineHeight(false);
302 void RenderFlow::dirtyLineBoxes(bool fullLayout, bool isRootLineBox)
304 if (!isRootLineBox && isReplaced())
305 return RenderContainer::dirtyLineBoxes(isRootLineBox);
310 for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
311 curr->dirtyLineBoxes();
315 InlineBox* RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun)
317 if (!isRootLineBox &&
318 (isReplaced() || makePlaceHolderBox)) // Inline tables and inline blocks
319 return RenderContainer::createInlineBox(false, isRootLineBox); // (or positioned element placeholders).
321 InlineFlowBox* flowBox = 0;
323 flowBox = new (renderArena()) InlineFlowBox(this);
325 flowBox = new (renderArena()) RootInlineBox(this);
328 m_firstLineBox = m_lastLineBox = flowBox;
330 m_lastLineBox->setNextLineBox(flowBox);
331 flowBox->setPreviousLineBox(m_lastLineBox);
332 m_lastLineBox = flowBox;
338 void RenderFlow::paintLineBoxBackgroundBorder(PaintInfo& i, int _tx, int _ty)
340 if (!shouldPaintWithinRoot(i))
346 if (style()->visibility() == VISIBLE && i.phase == PaintActionForeground) {
347 // We can check the first box and last box and avoid painting if we don't
349 int yPos = _ty + firstLineBox()->yPos();
350 int h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
351 if( (yPos >= i.r.y() + i.r.height()) || (yPos + h <= i.r.y()))
354 // See if our boxes intersect with the dirty rect. If so, then we paint
355 // them. Note that boxes can easily overlap, so we can't make any assumptions
356 // based off positions of our first line box or our last line box.
357 int xOffsetWithinLineBoxes = 0;
358 for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
359 yPos = _ty + curr->yPos();
361 if ((yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y()))
362 curr->paintBackgroundAndBorder(i, _tx, _ty, xOffsetWithinLineBoxes);
363 xOffsetWithinLineBoxes += curr->width();
368 void RenderFlow::paintLineBoxDecorations(PaintInfo& i, int _tx, int _ty, bool paintedChildren)
370 if (!shouldPaintWithinRoot(i))
373 // We only paint line box decorations in strict or almost strict mode.
374 // Otherwise we let the InlineTextBoxes paint their own decorations.
375 if (style()->htmlHacks() || !firstLineBox())
378 if (style()->visibility() == VISIBLE && i.phase == PaintActionForeground) {
379 // We can check the first box and last box and avoid painting if we don't
381 int yPos = _ty + firstLineBox()->yPos();;
382 int h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
383 if( (yPos >= i.r.y() + i.r.height()) || (yPos + h <= i.r.y()))
386 // See if our boxes intersect with the dirty rect. If so, then we paint
387 // them. Note that boxes can easily overlap, so we can't make any assumptions
388 // based off positions of our first line box or our last line box.
389 for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
390 yPos = _ty + curr->yPos();
392 if ((yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y()))
393 curr->paintDecorations(i, _tx, _ty, paintedChildren);
398 QRect RenderFlow::getAbsoluteRepaintRect()
400 if (isInlineFlow()) {
401 // Find our leftmost position.
403 int top = firstLineBox() ? firstLineBox()->yPos() : 0;
404 for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
405 if (curr == firstLineBox() || curr->xPos() < left)
408 // Now invalidate a rectangle.
409 int ow = style() ? style()->outlineSize() : 0;
412 if (style()->position() == RELATIVE && m_layer)
413 m_layer->relativePositionOffset(left, top);
415 QRect r(-ow+left, -ow+top, width()+ow*2, height()+ow*2);
416 RenderBlock* cb = containingBlock();
417 if (cb->hasOverflowClip()) {
418 // cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the
419 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
420 // anyway if its size does change.
423 QRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height());
424 cb->layer()->subtractScrollOffset(x,y); // For overflow:auto/scroll/hidden.
425 QRect repaintRect(x, y, r.width(), r.height());
426 r = repaintRect.intersect(boxRect);
429 cb->computeAbsoluteRepaintRect(r);
432 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
433 if (!curr->isText()) {
434 QRect childRect = curr->getAbsoluteRepaintRectWithOutline(ow);
435 r = r.unite(childRect);
439 if (continuation() && !continuation()->isInline()) {
440 QRect contRect = continuation()->getAbsoluteRepaintRectWithOutline(ow);
441 r = r.unite(contRect);
448 if (firstLineBox() && firstLineBox()->topOverflow() < 0) {
449 int ow = style() ? style()->outlineSize() : 0;
450 QRect r(-ow, -ow+firstLineBox()->topOverflow(),
451 overflowWidth(false)+ow*2,
452 overflowHeight(false)+ow*2-firstLineBox()->topOverflow());
453 computeAbsoluteRepaintRect(r);
458 return RenderContainer::getAbsoluteRepaintRect();
462 RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
464 int bottom = RenderContainer::lowestPosition(includeOverflowInterior, includeSelf);
465 if (!includeOverflowInterior && hasOverflowClip())
468 // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
469 // For now, we have to descend into all the children, since we may have a huge abs div inside
470 // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
472 for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
473 if (!c->isFloatingOrPositioned() && !c->isText()) {
474 int lp = c->yPos() + c->lowestPosition(false);
475 bottom = kMax(bottom, lp);
482 int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
484 int right = RenderContainer::rightmostPosition(includeOverflowInterior, includeSelf);
485 if (!includeOverflowInterior && hasOverflowClip())
488 // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
489 // For now, we have to descend into all the children, since we may have a huge abs div inside
490 // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
492 for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
493 if (!c->isFloatingOrPositioned() && !c->isText()) {
494 int rp = c->xPos() + c->rightmostPosition(false);
495 right = kMax(right, rp);
502 int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
504 int left = RenderContainer::leftmostPosition(includeOverflowInterior, includeSelf);
505 if (!includeOverflowInterior && hasOverflowClip())
508 // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
509 // For now, we have to descend into all the children, since we may have a huge abs div inside
510 // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
512 for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
513 if (!c->isFloatingOrPositioned() && !c->isText()) {
514 int lp = c->xPos() + c->leftmostPosition(false);
515 left = kMin(left, lp);
522 QRect RenderFlow::caretRect(int offset, EAffinity affinity)
524 if (firstChild() || style()->display() == INLINE) {
525 // Do the normal calculation
526 return RenderContainer::caretRect(offset, affinity);
529 // This is a special case:
530 // The element is not an inline element, and it's empty. So we have to
531 // calculate a fake position to indicate where objects are to be inserted.
533 int _x, _y, width, height;
535 // EDIT FIXME: this does neither take into regard :first-line nor :first-letter
536 // However, as soon as some content is entered, the line boxes will be
537 // constructed properly and this kludge is not called any more. So only
538 // the caret size of an empty :first-line'd block is wrong, but I think we
539 // can live with that.
540 RenderStyle *currentStyle = style(true);
541 //height = currentStyle->fontMetrics().height();
542 height = lineHeight(true);
545 // EDIT FIXME: This needs to account for text direction
546 int w = this->width();
547 switch (currentStyle->textAlign()) {
568 absolutePosition(absx, absy, false);
569 _x += absx + paddingLeft() + borderLeft();
570 _y += absy + paddingTop() + borderTop();
572 return QRect(_x, _y, width, height);