/* * 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 #include "rendering/render_arena.h" #include "rendering/render_inline.h" #include "rendering/render_block.h" #include "xml/dom_docimpl.h" #include 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., foo

goo

moo
. The 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(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(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 &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 &pointArray) { if (pointArray.size() < 3) return false; QValueVector::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 &pointArray) { if (pointArray.size() < 3) return false; QValueVector::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 &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 &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(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 &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()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 &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 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 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; }