diff options
Diffstat (limited to 'khtml/rendering/render_inline.cpp')
-rw-r--r-- | khtml/rendering/render_inline.cpp | 935 |
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; +} + |