summaryrefslogtreecommitdiffstats
path: root/khtml/rendering/render_inline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'khtml/rendering/render_inline.cpp')
-rw-r--r--khtml/rendering/render_inline.cpp935
1 files changed, 935 insertions, 0 deletions
diff --git a/khtml/rendering/render_inline.cpp b/khtml/rendering/render_inline.cpp
new file mode 100644
index 000000000..5848db47a
--- /dev/null
+++ b/khtml/rendering/render_inline.cpp
@@ -0,0 +1,935 @@
+/*
+ * This file is part of the render object implementation for KHTML.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ * (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., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <kglobal.h>
+
+#include "rendering/render_arena.h"
+#include "rendering/render_inline.h"
+#include "rendering/render_block.h"
+#include "xml/dom_docimpl.h"
+
+#include <qvaluevector.h>
+
+using namespace khtml;
+
+void RenderInline::setStyle(RenderStyle* _style)
+{
+ RenderFlow::setStyle(_style);
+ setInline(true);
+
+ // Ensure that all of the split inlines pick up the new style. We
+ // only do this if we're an inline, since we don't want to propagate
+ // a block's style to the other inlines.
+ // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
+ // and after the block share the same style, but the block doesn't
+ // need to pass its style on to anyone else.
+ RenderFlow* currCont = continuation();
+ while (currCont) {
+ if (currCont->isInline()) {
+ RenderFlow* nextCont = currCont->continuation();
+ currCont->setContinuation(0);
+ currCont->setStyle(style());
+ currCont->setContinuation(nextCont);
+ }
+ currCont = currCont->continuation();
+ }
+
+ if (attached()) {
+ // Update replaced content
+ updateReplacedContent();
+ // Update pseudos for ::before and ::after
+ updatePseudoChildren();
+ }
+}
+
+// Attach handles initial setStyle that requires parent nodes
+void RenderInline::attach()
+{
+ RenderFlow::attach();
+
+ updateReplacedContent();
+ updatePseudoChildren();
+}
+
+bool RenderInline::isInlineContinuation() const
+{
+ return m_isContinuation;
+}
+
+void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild)
+{
+ // Make sure we don't append things after :after-generated content if we have it.
+ if (!beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER)
+ beforeChild = lastChild();
+
+ if (!newChild->isText() && newChild->style()->position() != STATIC)
+ setOverhangingContents();
+
+ if (!newChild->isInline() && !newChild->isFloatingOrPositioned() )
+ {
+ // We are placing a block inside an inline. We have to perform a split of this
+ // inline into continuations. This involves creating an anonymous block box to hold
+ // |newChild|. We then make that block box a continuation of this inline. We take all of
+ // the children after |beforeChild| and put them in a clone of this object.
+
+ RenderBlock *newBox = createAnonymousBlock();
+ RenderFlow* oldContinuation = continuation();
+ setContinuation(newBox);
+
+ splitFlow(beforeChild, newBox, newChild, oldContinuation);
+ return;
+ }
+
+ RenderBox::addChild(newChild,beforeChild);
+
+ newChild->setNeedsLayoutAndMinMaxRecalc();
+}
+
+RenderInline* RenderInline::cloneInline(RenderFlow* src)
+{
+ RenderInline *o = new (src->renderArena()) RenderInline(src->element());
+ o->m_isContinuation = true;
+ o->setStyle(src->style());
+ return o;
+}
+
+void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock,
+ RenderBlock* middleBlock,
+ RenderObject* beforeChild, RenderFlow* oldCont)
+{
+ // Create a clone of this inline.
+ RenderInline* clone = cloneInline(this);
+ clone->setContinuation(oldCont);
+
+ // Now take all of the children from beforeChild to the end and remove
+ // then from |this| and place them in the clone.
+ RenderObject* o = beforeChild;
+ while (o) {
+ RenderObject* tmp = o;
+ o = tmp->nextSibling();
+ clone->addChildToFlow(removeChildNode(tmp), 0);
+ tmp->setNeedsLayoutAndMinMaxRecalc();
+ }
+
+ // Hook |clone| up as the continuation of the middle block.
+ middleBlock->setContinuation(clone);
+
+ // We have been reparented and are now under the fromBlock. We need
+ // to walk up our inline parent chain until we hit the containing block.
+ // Once we hit the containing block we're done.
+ RenderFlow* curr = static_cast<RenderFlow*>(parent());
+ RenderFlow* currChild = this;
+ while (curr && curr != fromBlock) {
+ // Create a new clone.
+ RenderInline* cloneChild = clone;
+ clone = cloneInline(curr);
+
+ // Insert our child clone as the first child.
+ clone->addChildToFlow(cloneChild, 0);
+
+ // Hook the clone up as a continuation of |curr|.
+ RenderFlow* oldCont = curr->continuation();
+ curr->setContinuation(clone);
+ clone->setContinuation(oldCont);
+
+ // Now we need to take all of the children starting from the first child
+ // *after* currChild and append them all to the clone.
+ o = currChild->nextSibling();
+ while (o) {
+ RenderObject* tmp = o;
+ o = tmp->nextSibling();
+ clone->appendChildNode(curr->removeChildNode(tmp));
+ tmp->setNeedsLayoutAndMinMaxRecalc();
+ }
+
+ // Keep walking up the chain.
+ currChild = curr;
+ curr = static_cast<RenderFlow*>(curr->parent());
+ }
+
+ // Now we are at the block level. We need to put the clone into the toBlock.
+ toBlock->appendChildNode(clone);
+
+ // Now take all the children after currChild and remove them from the fromBlock
+ // and put them in the toBlock.
+ o = currChild->nextSibling();
+ while (o) {
+ RenderObject* tmp = o;
+ o = tmp->nextSibling();
+ toBlock->appendChildNode(fromBlock->removeChildNode(tmp));
+ }
+}
+
+void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox,
+ RenderObject* newChild, RenderFlow* oldCont)
+{
+ RenderBlock* pre = 0;
+ RenderBlock* block = containingBlock();
+ bool madeNewBeforeBlock = false;
+ if (block->isAnonymousBlock()) {
+ // We can reuse this block and make it the preBlock of the next continuation.
+ pre = block;
+ block = block->containingBlock();
+ }
+ else {
+ // No anonymous block available for use. Make one.
+ pre = block->createAnonymousBlock();
+ madeNewBeforeBlock = true;
+ }
+
+ RenderBlock* post = block->createAnonymousBlock();
+
+ RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
+ if (madeNewBeforeBlock)
+ block->insertChildNode(pre, boxFirst);
+ block->insertChildNode(newBlockBox, boxFirst);
+ block->insertChildNode(post, boxFirst);
+ block->setChildrenInline(false);
+
+ if (madeNewBeforeBlock) {
+ RenderObject* o = boxFirst;
+ while (o)
+ {
+ RenderObject* no = o;
+ o = no->nextSibling();
+ pre->appendChildNode(block->removeChildNode(no));
+ no->setNeedsLayoutAndMinMaxRecalc();
+ }
+ }
+
+ splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
+
+ // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
+ // time in makeChildrenNonInline by just setting this explicitly up front.
+ newBlockBox->setChildrenInline(false);
+
+ // We don't just call addChild, since it would pass things off to the
+ // continuation, so we call addChildToFlow explicitly instead. We delayed
+ // adding the newChild until now so that the |newBlockBox| would be fully
+ // connected, thus allowing newChild access to a renderArena should it need
+ // to wrap itself in additional boxes (e.g., table construction).
+ newBlockBox->addChildToFlow(newChild, 0);
+
+ // XXXdwh is any of this even necessary? I don't think it is.
+ pre->close();
+ pre->setPos(0, -500000);
+ pre->setNeedsLayout(true);
+ newBlockBox->close();
+ newBlockBox->setPos(0, -500000);
+ newBlockBox->setNeedsLayout(true);
+ post->close();
+ post->setPos(0, -500000);
+ post->setNeedsLayout(true);
+
+ updatePseudoChildren();
+
+ block->setNeedsLayoutAndMinMaxRecalc();
+}
+
+void RenderInline::paint(PaintInfo& i, int _tx, int _ty)
+{
+ paintLines(i, _tx, _ty);
+}
+
+/**
+ * Appends the given coordinate-pair to the point-array if it is not
+ * equal to the last element.
+ * @param pointArray point-array
+ * @param pnt point to append
+ * @return \c true if \c pnt has actually been appended
+ */
+inline static bool appendIfNew(QValueVector<QPoint> &pointArray, const QPoint &pnt)
+{
+// if (!pointArray.isEmpty()) kdDebug(6040) << "appifnew: " << pointArray.back() << " == " << pnt << ": " << (pointArray.back() == pnt) << endl;
+// else kdDebug(6040) << "appifnew: " << pnt << " (unconditional)" << endl;
+ if (!pointArray.isEmpty() && pointArray.back() == pnt) return false;
+ pointArray.append(pnt);
+ return true;
+}
+
+/**
+ * Does spike-reduction on the given point-array's stack-top.
+ *
+ * Spikes are path segments of which one goes forward, and the sucessor
+ * goes backward on the predecessor's segment:
+ *
+ * 2 0 1
+ * x------x<-----x
+ * (0 is stack-top in point-array)
+ *
+ * This will be reduced to
+ * 1 0
+ * x------x
+ *
+ * Preconditions:
+ * - No other spikes exist in the whole point-array except at most
+ * one at the end
+ * - No two succeeding points are ever equal
+ * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
+ * true
+ * - No such spike exists where 2 is situated between 0 and 1.
+ *
+ * Postcondition:
+ * - No spikes exist in the whole point-array
+ *
+ * If no spike is found, the point-array is left unchanged.
+ * @return \c true if an actual reduction was done
+ */
+inline static bool reduceSpike(QValueVector<QPoint> &pointArray)
+{
+ if (pointArray.size() < 3) return false;
+ QValueVector<QPoint>::Iterator it = pointArray.end();
+ QPoint p0 = *--it;
+ QPoint p1 = *--it;
+ QPoint p2 = *--it;
+
+ bool elide = false;
+
+ if (p0.x() == p1.x() && p1.x() == p2.x()
+ && (p1.y() < p0.y() && p0.y() < p2.y()
+ || p2.y() < p0.y() && p0.y() < p1.y()
+ || p1.y() < p2.y() && p2.y() < p0.y()
+ || p0.y() < p2.y() && p2.y() < p1.y()
+ || (elide = p2.y() == p0.y() && p0.y() < p1.y())
+ || (elide = p1.y() < p0.y() && p0.y() == p2.y()))
+ || p0.y() == p1.y() && p1.y() == p2.y()
+ && (p1.x() < p0.x() && p0.x() < p2.x()
+ || p2.x() < p0.x() && p0.x() < p1.x()
+ || p1.x() < p2.x() && p2.x() < p0.x()
+ || p0.x() < p2.x() && p2.x() < p1.x()
+ || (elide = p2.x() == p0.x() && p0.x() < p1.x())
+ || (elide = p1.x() < p0.x() && p0.x() == p2.x())))
+ {
+// kdDebug(6040) << "spikered p2" << (elide ? " (elide)" : "") << ": " << p2 << " p1: " << p1 << " p0: " << p0 << endl;
+ pointArray.pop_back(); pointArray.pop_back();
+ if (!elide)
+ pointArray.push_back(p0);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Reduces segment separators.
+ *
+ * A segment separator separates a segment into two segments, thus causing
+ * two adjacent segment with the same orientation.
+ *
+ * 2 1 0
+ * x-------x---->x
+ * (0 means stack-top)
+ *
+ * Here, 1 is a segment separator. As segment separators not only make
+ * the line drawing algorithm inefficient, but also make the spike-reduction
+ * fail, they must be eliminated:
+ *
+ * 1 0
+ * x------------>x
+ *
+ * Preconditions:
+ * - No other segment separators exist in the whole point-array except
+ * at most one at the end
+ * - No two succeeding points are ever equal
+ * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
+ * true
+ * - No such spike exists where 2 is situated between 0 and 1.
+ *
+ * Postcondition:
+ * - No segment separators exist in the whole point-array
+ *
+ * If no segment separator is found at the end of the point-array, it is
+ * left unchanged.
+ * @return \c true if a segment separator was actually reduced.
+ */
+inline static bool reduceSegmentSeparator(QValueVector<QPoint> &pointArray)
+{
+ if (pointArray.size() < 3) return false;
+ QValueVector<QPoint>::Iterator it = pointArray.end();
+ QPoint p0 = *--it;
+ QPoint p1 = *--it;
+ QPoint p2 = *--it;
+// kdDebug(6040) << "checking p2: " << p2 << " p1: " << p1 << " p0: " << p0 << endl;
+
+ if (p0.x() == p1.x() && p1.x() == p2.x()
+ && (p2.y() < p1.y() && p1.y() < p0.y()
+ || p0.y() < p1.y() && p1.y() < p2.y())
+ || p0.y() == p1.y() && p1.y() == p2.y()
+ && (p2.x() < p1.x() && p1.x() < p0.x()
+ || p0.x() < p1.x() && p1.x() < p2.x()))
+ {
+// kdDebug(6040) << "segred p2: " << p2 << " p1: " << p1 << " p0: " << p0 << endl;
+ pointArray.pop_back(); pointArray.pop_back();
+ pointArray.push_back(p0);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Appends the given point to the point-array, doing necessary reductions to
+ * produce a path without spikes and segment separators.
+ */
+static void appendPoint(QValueVector<QPoint> &pointArray, QPoint &pnt)
+{
+ if (!appendIfNew(pointArray, pnt)) return;
+// kdDebug(6040) << "appendPoint: appended " << pnt << endl;
+ reduceSegmentSeparator(pointArray)
+ || reduceSpike(pointArray);
+}
+
+/**
+ * Traverses the horizontal inline boxes and appends the point coordinates to
+ * the given array.
+ * @param box inline box
+ * @param pointArray array collecting coordinates
+ * @param bottom \c true, collect bottom coordinates, \c false, collect top
+ * coordinates.
+ * @param limit lower limit that an y-coordinate must at least reach. Note
+ * that limit designates the highest y-coordinate for \c bottom, and
+ * the lowest for !\c bottom.
+ */
+static void collectHorizontalBoxCoordinates(InlineBox *box,
+ QValueVector<QPoint> &pointArray,
+ bool bottom, int offset, int limit = -500000)
+{
+// kdDebug(6000) << "collectHorizontalBoxCoordinates: " << endl;
+ offset = bottom ? offset:-offset;
+ int y = box->yPos() + bottom*box->height() + offset;
+ if (limit != -500000 && (bottom ? y < limit : y > limit))
+ y = limit;
+ int x = box->xPos() + bottom*box->width() + offset;
+ QPoint newPnt(x, y);
+ // Add intersection point if point-array not empty.
+ if (!pointArray.isEmpty()) {
+ QPoint lastPnt = pointArray.back();
+ QPoint insPnt(newPnt.x(), lastPnt.y());
+ if (offset && ((bottom && lastPnt.y() > y) || (!bottom && lastPnt.y() < y))) {
+ insPnt.rx() = lastPnt.x();
+ insPnt.ry() = y;
+ }
+// kdDebug(6040) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt) << endl;
+ appendPoint(pointArray, insPnt);
+ }
+ // Insert starting point of box
+ appendPoint(pointArray, newPnt);
+
+ newPnt.rx() += (bottom ? -box->width() : box->width()) - 2*offset;
+
+ if (box->isInlineFlowBox()) {
+ InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box);
+ for (InlineBox *b = bottom ? flowBox->lastChild() : flowBox->firstChild(); b; b = bottom ? b->prevOnLine() : b->nextOnLine()) {
+ // Don't let boxes smaller than this flow box' height influence
+ // the vertical position of the outline if they have a different
+ // x-coordinate
+ int l2;
+ if (b->xPos() != box->xPos() && b->xPos() + b->width() != box->xPos() + box->width())
+ l2 = y;
+ else
+ l2 = limit;
+ collectHorizontalBoxCoordinates(b, pointArray, bottom, kAbs(offset), l2);
+ }
+
+ // Add intersection point if flow box contained any children
+ if (flowBox->firstChild()) {
+ QPoint lastPnt = pointArray.back();
+ QPoint insPnt(lastPnt.x(), newPnt.y());
+// kdDebug(6040) << "right: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt) << endl;
+ appendPoint(pointArray, insPnt);
+ }
+ }
+
+ // Insert ending point of box
+ appendPoint(pointArray, newPnt);
+
+// kdDebug(6000) << "collectHorizontalBoxCoordinates: " << "ende" << endl;
+}
+
+/**
+ * Checks whether the given line box' extents and the following line box'
+ * extents are disjount (i. e. do not share the same x-coordinate range).
+ * @param line line box
+ * @param toBegin \c true, compare with preceding line box, \c false, with
+ * succeeding
+ * @return \c true if this and the next box are disjoint
+ */
+inline static bool lineBoxesDisjoint(InlineRunBox *line, int offset, bool toBegin)
+{
+ InlineRunBox *next = toBegin ? line->prevLineBox() : line->nextLineBox();
+ return !next || next->xPos() + next->width() + 2*offset < line->xPos()
+ || next->xPos() > line->xPos() + line->width() + 2*offset;
+}
+
+/**
+ * Traverses the vertical outer borders of the given render flow's line
+ * boxes and appends the point coordinates to the given point array.
+ * @param line line box to begin traversal
+ * @param pointArray point array
+ * @param left \c true, traverse the left vertical coordinates,
+ * \c false, traverse the right vertical coordinates.
+ * @param lastline if not 0, returns the pointer to the last line box traversed
+ */
+static void collectVerticalBoxCoordinates(InlineRunBox *line,
+ QValueVector<QPoint> &pointArray,
+ bool left, int offset, InlineRunBox **lastline = 0)
+{
+ InlineRunBox *last = 0;
+ offset = left ? -offset:offset;
+ for (InlineRunBox* curr = line; curr && !last; curr = left ? curr->prevLineBox() : curr->nextLineBox()) {
+ InlineBox *root = curr;
+
+ bool isLast = lineBoxesDisjoint(curr, kAbs(offset), left);
+ if (isLast) last = curr;
+
+ if (root != line && !isLast)
+ while (root->parent()) root = root->parent();
+ QPoint newPnt(curr->xPos() + !left*curr->width() + offset,
+ (left ? root->topOverflow() : root->bottomOverflow()) + offset);
+ if (!pointArray.isEmpty()) {
+ QPoint lastPnt = pointArray.back();
+ if (newPnt.x()>lastPnt.x() && !left)
+ pointArray.back().setY( kMin(lastPnt.y(), root->topOverflow()-offset) );
+ else if (newPnt.x()<lastPnt.x() && left)
+ pointArray.back().setY( kMax(lastPnt.y(), root->bottomOverflow()+offset) );
+ QPoint insPnt(newPnt.x(), pointArray.back().y());
+// kdDebug(6040) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt) << endl;
+ appendPoint(pointArray, insPnt);
+ }
+ appendPoint(pointArray, newPnt);
+ }
+ if (lastline) *lastline = last;
+}
+
+/**
+ * Links up the end of the given point-array such that the starting point
+ * is not a segment separator.
+ *
+ * To achieve this, improper points are removed from the beginning of
+ * the point-array (by changing the array's starting iterator), and
+ * proper ones appended to the point-array's back.
+ *
+ * @param pointArray point-array
+ * @return actual begin of point array
+ */
+static QPoint *linkEndToBegin(QValueVector<QPoint> &pointArray)
+{
+ uint index = 0;
+ assert(pointArray.size() >= 3);
+
+ // if first and last points match, ignore the last one.
+ bool linkup = false; QPoint linkupPnt;
+ if (pointArray.front() == pointArray.back()) {
+ linkupPnt = pointArray.back();
+ pointArray.pop_back();
+ linkup = true;
+ }
+
+ const QPoint *it = pointArray.begin() + index;
+ QPoint pfirst = *it;
+ QPoint pnext = *++it;
+ QPoint plast = pointArray.back();
+// kdDebug(6040) << "linkcheck plast: " << plast << " pfirst: " << pfirst << " pnext: " << pnext << endl;
+
+ if (plast.x() == pfirst.x() && pfirst.x() == pnext.x()
+ || plast.y() == pfirst.y() && pfirst.y() == pnext.y()) {
+
+ ++index;
+ appendPoint(pointArray, pfirst);
+ appendPoint(pointArray, pnext);
+ } else if (linkup)
+ pointArray.push_back(linkupPnt);
+ return pointArray.begin() + index;
+}
+
+void RenderInline::paintOutlines(QPainter *p, int _tx, int _ty)
+{
+ if (style()->outlineWidth() == 0 || style()->outlineStyle() <= BHIDDEN)
+ return;
+ int offset = style()->outlineOffset();
+
+ // We may have to draw more than one outline path as they may be
+ // disjoint.
+ for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
+ QValueVector<QPoint> path;
+
+ // collect topmost outline
+ collectHorizontalBoxCoordinates(curr, path, false, offset);
+ // collect right outline
+ collectVerticalBoxCoordinates(curr, path, false, offset, &curr);
+ // collect bottommost outline
+ collectHorizontalBoxCoordinates(curr, path, true, offset);
+ // collect left outline
+ collectVerticalBoxCoordinates(curr, path, true, offset);
+
+ if (path.size() < 3) continue;
+
+ const QPoint *begin = linkEndToBegin(path);
+
+ // paint the outline
+ paintOutlinePath(p, _tx, _ty, begin, path.end(), BSLeft, -1, BSTop);
+ }
+}
+
+template<class T> inline void kSwap(T &a1, T &a2)
+{
+ T tmp = a2;
+ a2 = a1;
+ a1 = tmp;
+}
+
+enum BSOrientation { BSHorizontal, BSVertical };
+
+/**
+ * Returns the orientation of the given border side.
+ */
+inline BSOrientation bsOrientation(RenderObject::BorderSide bs)
+{
+ switch (bs) {
+ case RenderObject::BSTop:
+ case RenderObject::BSBottom:
+ return BSHorizontal;
+ case RenderObject::BSLeft:
+ case RenderObject::BSRight:
+ return BSVertical;
+ }
+ return BSHorizontal; // make gcc happy (sigh)
+}
+
+/**
+ * Determines the new border side by evaluating the new direction as determined
+ * by the given coordinates, the old border side, and the relative direction.
+ *
+ * The relative direction specifies whether the old border side meets with the
+ * straight given by the coordinates from below (negative), or above (positive).
+ */
+inline RenderObject::BorderSide newBorderSide(RenderObject::BorderSide oldBS, int direction, const QPoint &last, const QPoint &cur)
+{
+ bool below = direction < 0;
+ if (last.x() == cur.x()) { // new segment is vertical
+ bool t = oldBS == RenderObject::BSTop;
+ bool b = oldBS == RenderObject::BSBottom;
+ if ((t || b) && last.y() != cur.y())
+ return (cur.y() < last.y()) ^ (t && below || b && !below)
+ ? RenderObject::BSLeft : RenderObject::BSRight;
+ } else /*if (last.y() == cur.y())*/ { // new segment is horizontal
+ bool l = oldBS == RenderObject::BSLeft;
+ bool r = oldBS == RenderObject::BSRight;
+ if ((l || r) && last.x() != cur.x())
+ return (cur.x() < last.x()) ^ (l && below || r && !below)
+ ? RenderObject::BSTop : RenderObject::BSBottom;
+ }
+ return oldBS; // same direction
+}
+
+/**
+ * Draws an outline segment between the given two points.
+ * @param o render object
+ * @param p painter
+ * @param tx absolute x-coordinate of containing block
+ * @param ty absolute y-coordinate of containing block
+ * @param p1 starting point
+ * @param p2 end point
+ * @param prevBS border side of previous segment
+ * @param curBS border side of this segment
+ * @param nextBS border side of next segment
+ */
+static void paintOutlineSegment(RenderObject *o, QPainter *p, int tx, int ty,
+ const QPoint &p1, const QPoint &p2,
+ RenderObject::BorderSide prevBS,
+ RenderObject::BorderSide curBS,
+ RenderObject::BorderSide nextBS)
+{
+ int ow = o->style()->outlineWidth();
+ EBorderStyle os = o->style()->outlineStyle();
+ QColor oc = o->style()->outlineColor();
+
+ int x1 = tx + p1.x();
+ int y1 = ty + p1.y();
+ int x2 = tx + p2.x();
+ int y2 = ty + p2.y();
+ if (x1 > x2) {
+ kSwap(x1, x2);
+ if (bsOrientation(curBS) == BSHorizontal) kSwap(prevBS, nextBS);
+ }
+ if (y1 > y2) {
+ kSwap(y1, y2);
+ if (bsOrientation(curBS) == BSVertical) kSwap(prevBS, nextBS);
+ }
+
+// kdDebug(6040) << "segment(" << x1 << "," << y1 << ") - (" << x2 << "," << y2 << ")" << endl;
+/* p->setPen(Qt::gray);
+ p->drawLine(x1,y1,x2,y2);*/
+ switch (curBS) {
+ case RenderObject::BSLeft:
+ case RenderObject::BSRight:
+/* p->setPen(QColor("#ffe4dd"));
+ p->drawLine(
+ x1 - (curBS == RenderObject::BSLeft ? ow : 0),
+ y1 - (prevBS == RenderObject::BSTop ? ow : 0),
+ x2 + (curBS == RenderObject::BSRight ? ow : 0),
+ y2 + (nextBS == RenderObject::BSBottom ? ow : 0)
+ );*/
+ o->drawBorder(p,
+ x1 - (curBS == RenderObject::BSLeft ? ow : 0),
+ y1 - (prevBS == RenderObject::BSTop ? ow : 0),
+ x2 + (curBS == RenderObject::BSRight ? ow : 0),
+ y2 + (nextBS == RenderObject::BSBottom ? ow : 0),
+ curBS, oc, o->style()->color(), os,
+ prevBS == RenderObject::BSTop ? ow
+ : prevBS == RenderObject::BSBottom ? -ow : 0,
+ nextBS == RenderObject::BSTop ? -ow
+ : nextBS == RenderObject::BSBottom ? ow : 0,
+ true);
+ break;
+ case RenderObject::BSBottom:
+ case RenderObject::BSTop:
+// kdDebug(6040) << "BSTop/BSBottom: prevBS " << prevBS << " curBS " << curBS << " nextBS " << nextBS << endl;
+ o->drawBorder(p,
+ x1 - (prevBS == RenderObject::BSLeft ? ow : 0),
+ y1 - (curBS == RenderObject::BSTop ? ow : 0),
+ x2 + (nextBS == RenderObject::BSRight ? ow : 0),
+ y2 + (curBS == RenderObject::BSBottom ? ow : 0),
+ curBS, oc, o->style()->color(), os,
+ prevBS == RenderObject::BSLeft ? ow
+ : prevBS == RenderObject::BSRight ? -ow : 0,
+ nextBS == RenderObject::BSLeft ? -ow
+ : nextBS == RenderObject::BSRight ? ow : 0,
+ true);
+ break;
+ }
+}
+
+void RenderInline::paintOutlinePath(QPainter *p, int tx, int ty, const QPoint *begin, const QPoint *end, BorderSide bs, int direction, BorderSide endingBS)
+{
+ int ow = style()->outlineWidth();
+ if (ow == 0 || m_isContinuation) // Continuations get painted by the original inline.
+ return;
+
+ QPoint last = *begin;
+ BorderSide lastBS = bs;
+ Q_ASSERT(begin != end);
+ ++begin;
+
+// kdDebug(6040) << "last: " << last << endl;
+
+ bs = newBorderSide(bs, direction, last, *begin);
+// kdDebug(6040) << "newBorderSide: " << lastBS << " " << direction << "d " << last << " - " << *begin << " => " << bs << endl;
+
+ for (const QPoint *it = begin; it != end; ++it) {
+ QPoint cur = *it;
+// kdDebug(6040) << "cur: " << cur << endl;
+ BorderSide nextBS;
+ if (it + 1 != end) {
+ QPoint diff = cur - last;
+ direction = diff.x() + diff.y();
+ nextBS = newBorderSide(bs, direction, cur, *(it + 1));
+// kdDebug(6040) << "newBorderSide*: " << bs << " " << direction << "d " << cur << " - " << *(it + 1) << " => " << nextBS << endl;
+ } else
+ nextBS = endingBS;
+
+ Q_ASSERT(bsOrientation(bs) != bsOrientation(nextBS));
+ paintOutlineSegment(this, p, tx, ty, last, cur,
+ lastBS, bs, nextBS);
+ lastBS = bs;
+ last = cur;
+ bs = nextBS;
+ }
+
+}
+
+void RenderInline::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+#ifdef DEBUG_LAYOUT
+ kdDebug( 6040 ) << renderName() << "(RenderInline)::calcMinMaxWidth() this=" << this << endl;
+#endif
+
+ // Irrelevant, since some enclosing block will actually measure us and our children.
+ m_minWidth = 0;
+ m_maxWidth = 0;
+
+ setMinMaxKnown();
+}
+
+short RenderInline::width() const
+{
+ // Return the width of the minimal left side and the maximal right side.
+ short leftSide = 0;
+ short rightSide = 0;
+ for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
+ if (curr == firstLineBox() || curr->xPos() < leftSide)
+ leftSide = curr->xPos();
+ if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide)
+ rightSide = curr->xPos() + curr->width();
+ }
+
+ return rightSide - leftSide;
+}
+
+int RenderInline::height() const
+{
+ int h = 0;
+ if (firstLineBox())
+ h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
+ return h;
+}
+
+int RenderInline::offsetLeft() const
+{
+ int x = RenderFlow::offsetLeft();
+ if (firstLineBox())
+ x += firstLineBox()->xPos();
+ return x;
+}
+
+int RenderInline::offsetTop() const
+{
+ int y = RenderFlow::offsetTop();
+ if (firstLineBox())
+ y += firstLineBox()->yPos();
+ return y;
+}
+
+const char *RenderInline::renderName() const
+{
+ if (isRelPositioned())
+ return "RenderInline (relative positioned)";
+ if (isAnonymous())
+ return "RenderInline (anonymous)";
+ return "RenderInline";
+}
+
+bool RenderInline::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside)
+{
+/*
+ if ( hitTestAction != HitTestSelfOnly ) {
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling())
+ if (!child->layer() && !child->isFloating() && child->nodeAtPoint(info, _x, _y, _tx, _ty, HitTestAll))
+ inside = true;
+ }
+*/
+ // Check our line boxes if we're still not inside.
+ if (/*hitTestAction != HitTestChildrenOnly &&*/ !inside && style()->visibility() != HIDDEN) {
+ // See if we're inside one of our line boxes.
+ inside = hitTestLines(info, _x, _y, _tx, _ty, hitTestAction);
+ }
+
+ if (inside && element()) {
+ if (info.innerNode() && info.innerNode()->renderer() &&
+ !info.innerNode()->renderer()->isInline()) {
+ // Within the same layer, inlines are ALWAYS fully above blocks. Change inner node.
+ info.setInnerNode(element());
+
+ // Clear everything else.
+ info.setInnerNonSharedNode(0);
+ info.setURLElement(0);
+ }
+
+ if (!info.innerNode())
+ info.setInnerNode(element());
+
+ if(!info.innerNonSharedNode())
+ info.setInnerNonSharedNode(element());
+ }
+
+ return inside;
+}
+
+void RenderInline::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height)
+{
+ _x = -1;
+
+ RenderBlock *cb = containingBlock();
+ bool rtl = cb->style()->direction() == RTL;
+ bool outsideEnd = flags & CFOutsideEnd;
+ // I need to explain that: outsideEnd contains a meaningful value if
+ // and only if flags & CFOutside is set. If it is not, then randomly
+ // either the first or the last line box is returned.
+ // This doesn't matter because the only case this can happen is on an
+ // empty inline element, whose first and last line boxes are actually
+ // the same.
+ InlineFlowBox *line = !outsideEnd ^ rtl ? firstLineBox() : lastLineBox();
+
+ if (!line) { // umpf, handle "gracefully"
+ RenderFlow::caretPos(offset, flags, _x, _y, width, height);
+ return;
+ }
+
+ _x = line->xPos();
+ width = 1; // ### regard CFOverride
+
+ // Place caret outside the border
+ if (flags & CFOutside) {
+ RenderStyle *s = element() && element()->parent()
+ && element()->parent()->renderer()
+ ? element()->parent()->renderer()->style()
+ : style();
+ const QFontMetrics &fm = s->fontMetrics();
+ _y = line->yPos() + line->baseline() - fm.ascent();
+ height = fm.height();
+
+ if (!outsideEnd ^ rtl) {
+ _x -= line->marginBorderPaddingLeft();
+ } else {
+ _x += line->width() + line->marginBorderPaddingRight();
+ }
+
+ } else {
+ const QFontMetrics &fm = style()->fontMetrics();
+ _y = line->yPos() + line->baseline() - fm.ascent();
+ height = fm.height();
+ }
+
+ int absx, absy;
+ if (cb && cb->absolutePosition(absx,absy)) {
+ //kdDebug(6040) << "absx=" << absx << " absy=" << absy << endl;
+ _x += absx;
+ _y += absy;
+ } else {
+ // we don't know our absolute position, and there is no point returning
+ // just a relative one
+ _x = _y = -1;
+ }
+}
+
+inline int minXPos(const RenderInline *o)
+{
+ int retval=6666666;
+ if (!o->firstLineBox()) return 0;
+ for (InlineRunBox* curr = o->firstLineBox(); curr; curr = curr->nextLineBox())
+ retval = kMin( retval, int( curr->m_x ));
+ return retval;
+}
+
+int RenderInline::inlineXPos() const
+{
+ return minXPos(this);
+}
+
+int RenderInline::inlineYPos() const
+{
+ return firstLineBox() ? firstLineBox()->yPos() : 0;
+}
+