You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdelibs/khtml/rendering/render_inline.cpp

936 lines
33 KiB

/*
* 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;
}