/**
* This file is part of the html renderer for KDE.
*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* Copyright (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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
// -------------------------------------------------------------------------
#include
#include
#include
#include
#include "rendering/render_flow.h"
#include "rendering/render_text.h"
#include "rendering/render_table.h"
#include "rendering/render_canvas.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_docimpl.h"
#include "html/html_formimpl.h"
#include "render_inline.h"
#include "render_block.h"
#include "render_arena.h"
#include "render_line.h"
#include "khtmlview.h"
#include "htmltags.h"
using namespace DOM;
using namespace khtml;
RenderFlow* RenderFlow::createAnonymousFlow(DOM::DocumentImpl* doc, RenderStyle* style)
{
RenderFlow* result;
if (style->display() == INLINE)
result = new (doc->renderArena()) RenderInline(doc);
else
result = new (doc->renderArena()) RenderBlock(doc);
result->setStyle(style);
return result;
}
RenderFlow* RenderFlow::continuationBefore(RenderObject* beforeChild)
{
if (beforeChild && beforeChild->parent() == this)
return this;
RenderFlow* curr = continuation();
RenderFlow* nextToLast = this;
RenderFlow* last = this;
while (curr) {
if (beforeChild && beforeChild->parent() == curr) {
if (curr->firstChild() == beforeChild)
return last;
return curr;
}
nextToLast = last;
last = curr;
curr = curr->continuation();
}
if (!beforeChild && !last->firstChild())
return nextToLast;
return last;
}
void RenderFlow::addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild)
{
RenderFlow* flow = continuationBefore(beforeChild);
KHTMLAssert(!beforeChild || beforeChild->parent()->isRenderBlock() ||
beforeChild->parent()->isRenderInline());
RenderFlow* beforeChildParent = beforeChild ? static_cast(beforeChild->parent()) :
(flow->continuation() ? flow->continuation() : flow);
if (newChild->isFloatingOrPositioned())
return beforeChildParent->addChildToFlow(newChild, beforeChild);
// A continuation always consists of two potential candidates: an inline or an anonymous
// block box holding block children.
bool childInline = newChild->isInline();
bool bcpInline = beforeChildParent->isInline();
bool flowInline = flow->isInline();
if (flow == beforeChildParent)
return flow->addChildToFlow(newChild, beforeChild);
else {
// The goal here is to match up if we can, so that we can coalesce and create the
// minimal # of continuations needed for the inline.
if (childInline == bcpInline)
return beforeChildParent->addChildToFlow(newChild, beforeChild);
else if (flowInline == childInline)
return flow->addChildToFlow(newChild, 0); // Just treat like an append.
else
return beforeChildParent->addChildToFlow(newChild, beforeChild);
}
}
void RenderFlow::addChild(RenderObject *newChild, RenderObject *beforeChild)
{
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << renderName() << "(RenderFlow)::addChild( " << newChild->renderName() <<
", " << (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
kdDebug( 6040 ) << "current height = " << m_height << endl;
#endif
if (continuation())
return addChildWithContinuation(newChild, beforeChild);
return addChildToFlow(newChild, beforeChild);
}
void RenderFlow::extractLineBox(InlineFlowBox* box)
{
m_lastLineBox = box->prevFlowBox();
if (box == m_firstLineBox)
m_firstLineBox = 0;
if (box->prevLineBox())
box->prevLineBox()->setNextLineBox(0);
box->setPreviousLineBox(0);
for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
curr->setExtracted();
}
void RenderFlow::attachLineBox(InlineFlowBox* box)
{
if (m_lastLineBox) {
m_lastLineBox->setNextLineBox(box);
box->setPreviousLineBox(m_lastLineBox);
}
else
m_firstLineBox = box;
InlineFlowBox* last = box;
for (InlineFlowBox* curr = box; curr; curr = curr->nextFlowBox()) {
curr->setExtracted(false);
last = curr;
}
m_lastLineBox = last;
}
void RenderFlow::removeLineBox(InlineFlowBox* box)
{
if (box == m_firstLineBox)
m_firstLineBox = box->nextFlowBox();
if (box == m_lastLineBox)
m_lastLineBox = box->prevFlowBox();
if (box->nextLineBox())
box->nextLineBox()->setPreviousLineBox(box->prevLineBox());
if (box->prevLineBox())
box->prevLineBox()->setNextLineBox(box->nextLineBox());
}
void RenderFlow::deleteLineBoxes()
{
if (m_firstLineBox) {
RenderArena* arena = renderArena();
InlineRunBox *curr=m_firstLineBox, *next=0;
while (curr) {
next = curr->nextLineBox();
curr->detach(arena);
curr = next;
}
m_firstLineBox = 0;
m_lastLineBox = 0;
}
}
void RenderFlow::detach()
{
if (!documentBeingDestroyed()) {
if (m_firstLineBox) {
// We can't wait for RenderContainer::detach to clear the selection,
// because by then we will have nuked the line boxes.
if (isSelectionBorder())
canvas()->clearSelection();
// If line boxes are contained inside a root, that means we're an inline.
// In that case, we need to remove all the line boxes so that the parent
// lines aren't pointing to deleted children. If the first line box does
// not have a parent that means they are either already disconnected or
// root lines that can just be destroyed without disconnecting.
if (m_firstLineBox->parent()) {
for (InlineRunBox* box = m_firstLineBox; box; box = box->nextLineBox())
box->remove();
}
// If we are an anonymous block, then our line boxes might have children
// that will outlast this block. In the non-anonymous block case those
// children will be destroyed by the time we return from this function.
if (isAnonymousBlock()) {
for (InlineFlowBox* box = m_firstLineBox; box; box = box->nextFlowBox()) {
while (InlineBox *childBox = box->firstChild()) {
childBox->remove();
}
}
}
}
else if (isInline() && parent())
parent()->dirtyLinesFromChangedChild(this, false);
}
deleteLineBoxes();
RenderContainer::detach();
}
void RenderFlow::dirtyLinesFromChangedChild(RenderObject* child, bool adding)
{
if (!parent() || selfNeedsLayout() || isTable())
return;
// For an empty inline, go ahead and propagate the check up to our parent.
if (isInline() && !firstLineBox())
return parent()->dirtyLinesFromChangedChild(this);
// Try to figure out which line box we belong in. First try to find a previous
// line box by examining our siblings. If we didn't find a line box, then use our
// parent's first line box.
RootInlineBox* box = 0;
RenderObject* curr = 0;
for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
if (curr->isFloatingOrPositioned())
continue;
if (curr->isReplaced()) {
InlineBox* wrapper = curr->inlineBoxWrapper();
if (wrapper)
box = wrapper->root();
}
else if (curr->isText()) {
InlineTextBox* textBox = static_cast(curr)->lastTextBox();
if (textBox)
box = textBox->root();
}
else if (curr->isInlineFlow()) {
InlineRunBox* runBox = static_cast(curr)->lastLineBox();
if (runBox)
box = runBox->root();
}
if (box)
break;
}
if (!box && firstLineBox())
box = firstLineBox()->root();
// If we found a line box, then dirty it.
if (box) {
RootInlineBox* adjacentBox;
box->markDirty();
// dirty the adjacent lines that might be affected
// NOTE: we dirty the previous line because RootInlineBox objects cache
// the address of the first object on the next line after a BR, which we may be
// invalidating here. For more info, see how RenderBlock::layoutInlineChildren
// calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak,
// despite the name, actually returns the first RenderObject after the BR.
// "Typing after pasting line does not appear until after window resize."
adjacentBox = box->prevRootBox();
if (adjacentBox)
adjacentBox->markDirty();
if (child->isBR() || (curr && curr->isBR())) {
adjacentBox = box->nextRootBox();
if (adjacentBox)
adjacentBox->markDirty();
}
}
}
short RenderFlow::lineHeight(bool firstLine, bool isRootLineBox) const
{
if (firstLine) {
RenderStyle* s = style(firstLine);
Length lh = s->lineHeight();
if (lh.value < 0) {
if (s == style()) {
if (m_lineHeight == -1)
m_lineHeight = RenderObject::lineHeight(false);
return m_lineHeight;
}
return s->fontMetrics().lineSpacing();
}
if (lh.isPercent())
return lh.minWidth(s->font().pixelSize());
return lh.value;
}
if (m_lineHeight == -1)
m_lineHeight = RenderObject::lineHeight(false);
return m_lineHeight;
}
void RenderFlow::dirtyLineBoxes(bool fullLayout, bool isRootLineBox)
{
if (!isRootLineBox && isReplaced())
return RenderContainer::dirtyLineBoxes(fullLayout, isRootLineBox);
if (fullLayout)
deleteLineBoxes();
else {
for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
curr->dirtyLineBoxes();
}
}
InlineBox* RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun)
{
if (!isRootLineBox &&
(isReplaced() || makePlaceHolderBox)) // Inline tables and inline blocks
return RenderContainer::createInlineBox(false, isRootLineBox); // (or positioned element placeholders).
InlineFlowBox* flowBox = 0;
if (isInlineFlow())
flowBox = new (renderArena()) InlineFlowBox(this);
else
flowBox = new (renderArena()) RootInlineBox(this);
if (!m_firstLineBox)
m_firstLineBox = m_lastLineBox = flowBox;
else {
m_lastLineBox->setNextLineBox(flowBox);
flowBox->setPreviousLineBox(m_lastLineBox);
m_lastLineBox = flowBox;
}
return flowBox;
}
void RenderFlow::paintLines(PaintInfo& i, int _tx, int _ty)
{
// Only paint during the foreground/selection phases.
if (i.phase != PaintActionForeground && i.phase != PaintActionSelection && i.phase != PaintActionOutline)
return;
bool inlineFlow = isInlineFlow();
if (inlineFlow)
KHTMLAssert(m_layer); // The only way a compact/run-in/inline could paint like this is if it has a layer.
// If we have no lines then we have no work to do.
if (!firstLineBox())
return;
// We can check the first box and last box and avoid painting if we don't
// intersect. This is a quick short-circuit that we can take to avoid walking any lines.
// FIXME: This check is flawed in two extremely obscure ways.
// (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
// (2) The overflow from an inline block on a line is not reported to the line.
int yPos = firstLineBox()->root()->selectionTop() - maximalOutlineSize(i.phase);
int h = maximalOutlineSize(i.phase) + lastLineBox()->root()->selectionTop() + lastLineBox()->root()->selectionHeight() - yPos;
yPos += _ty;
if ((yPos >= i.r.y() + i.r.height()) || (yPos + h <= i.r.y()))
return;
// See if our root lines intersect with the dirty rect. If so, then we paint
// them. Note that boxes can easily overlap, so we can't make any assumptions
// based off positions of our first line box or our last line box.
bool isPrinting = (i.p->device()->devType() == QInternal::Printer);
for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) {
if (isPrinting) {
// FIXME: This is a feeble effort to avoid splitting a line across two pages.
// It is utterly inadequate, and this should not be done at paint time at all.
// The whole way objects break across pages needs to be redone.
RenderCanvas* c = canvas();
// Try to avoid splitting a line vertically, but only if it's less than the height
// of the entire page.
if (curr->root()->bottomOverflow() - curr->root()->topOverflow() <= c->printRect().height()) {
if (_ty + curr->root()->bottomOverflow() > c->printRect().y() + c->printRect().height()) {
if (_ty + curr->root()->topOverflow() < c->truncatedAt())
c->setBestTruncatedAt(_ty + curr->root()->topOverflow(), this);
// If we were able to truncate, don't paint.
if (_ty + curr->root()->topOverflow() >= c->truncatedAt())
break;
}
}
}
int top = kMin(curr->root()->topOverflow(), curr->root()->selectionTop()) - maximalOutlineSize(i.phase);
int bottom = kMax(curr->root()->selectionTop() + curr->root()->selectionHeight(), curr->root()->bottomOverflow()) + maximalOutlineSize(i.phase);
h = bottom - top;
yPos = _ty + top;
if ((yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y()))
curr->paint(i, _tx, _ty);
}
if (i.phase == PaintActionOutline && i.outlineObjects) {
// FIXME: Will the order in which we added objects to the dictionary be preserved? Probably not.
// This means the paint order of outlines will be wrong, although this is a minor issue.
QPtrDictIterator objects(*i.outlineObjects);
for (objects.toFirst(); objects.current(); ++objects) {
#ifdef APPLE_CHANGES
if (objects.current()->style()->outlineStyleIsAuto())
objects.current()->paintFocusRing(i.p, _tx, _ty);
else
#endif
objects.current()->paintOutlines(i.p, _tx, _ty);
}
i.outlineObjects->clear();
}
}
bool RenderFlow::hitTestLines(NodeInfo& i, int x, int y, int tx, int ty, HitTestAction hitTestAction)
{
if (hitTestAction != HitTestForeground)
return false;
bool inlineFlow = isInlineFlow();
if (inlineFlow)
KHTMLAssert(m_layer); // The only way a compact/run-in/inline could paint like this is if it has a layer.
// If we have no lines then we have no work to do.
if (!firstLineBox())
return false;
// We can check the first box and last box and avoid hit testing if we don't
// contain the point. This is a quick short-circuit that we can take to avoid walking any lines.
// FIXME: This check is flawed in two extremely obscure ways.
// (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
// (2) The overflow from an inline block on a line is not reported to the line.
if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow()))
return false;
// See if our root lines contain the point. If so, then we hit test
// them further. Note that boxes can easily overlap, so we can't make any assumptions
// based off positions of our first line box or our last line box.
for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevFlowBox()) {
if (y >= ty + curr->root()->topOverflow() && y < ty + curr->root()->bottomOverflow()) {
bool inside = curr->nodeAtPoint(i, x, y, tx, ty);
if (inside) {
setInnerNode(i);
return true;
}
}
}
return false;
}
QRect RenderFlow::getAbsoluteRepaintRect()
{
if (isInlineFlow()) {
// Find our leftmost position.
int left = 0;
int top = firstLineBox() ? firstLineBox()->yPos() : 0;
for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox())
if (curr == firstLineBox() || curr->xPos() < left)
left = curr->xPos();
// Now invalidate a rectangle.
int ow = style() ? style()->outlineSize() : 0;
if (isCompact())
left -= m_x;
// We need to add in the relative position offsets of any inlines (including us) up to our
// containing block.
RenderBlock* cb = containingBlock();
for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isInlineFlow() && inlineFlow != cb;
inlineFlow = inlineFlow->parent()) {
if (inlineFlow->style()->position() == RELATIVE && inlineFlow->layer())
inlineFlow->layer()->relativePositionOffset(left, top);
}
QRect r(-ow+left, -ow+top, width()+ow*2, height()+ow*2);
if (cb->hasOverflowClip()) {
// cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the
// layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
// anyway if its size does change.
int x = r.left();
int y = r.top();
QRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height());
cb->layer()->subtractScrollOffset(x,y); // For overflow:auto/scroll/hidden.
QRect repaintRect(x, y, r.width(), r.height());
r = repaintRect.intersect(boxRect);
}
cb->computeAbsoluteRepaintRect(r);
if (ow) {
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (!curr->isText()) {
QRect childRect = curr->getAbsoluteRepaintRectWithOutline(ow);
r = r.unite(childRect);
}
}
if (continuation() && !continuation()->isInline()) {
QRect contRect = continuation()->getAbsoluteRepaintRectWithOutline(ow);
r = r.unite(contRect);
}
}
return r;
}
else {
if (firstLineBox() && firstLineBox()->topOverflow() < 0) {
int ow = style() ? style()->outlineSize() : 0;
QRect r(-ow, -ow+firstLineBox()->topOverflow(),
overflowWidth(false)+ow*2,
overflowHeight(false)+ow*2-firstLineBox()->topOverflow());
computeAbsoluteRepaintRect(r);
return r;
}
}
return RenderContainer::getAbsoluteRepaintRect();
}
int
RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
{
int bottom = RenderContainer::lowestPosition(includeOverflowInterior, includeSelf);
if (!includeOverflowInterior && hasOverflowClip())
return bottom;
// FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
// For now, we have to descend into all the children, since we may have a huge abs div inside
// a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
// the abs div.
for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
if (!c->isFloatingOrPositioned() && !c->isText()) {
int lp = c->yPos() + c->lowestPosition(false);
bottom = kMax(bottom, lp);
}
}
return bottom;
}
int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
{
int right = RenderContainer::rightmostPosition(includeOverflowInterior, includeSelf);
if (!includeOverflowInterior && hasOverflowClip())
return right;
// FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
// For now, we have to descend into all the children, since we may have a huge abs div inside
// a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
// the abs div.
for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
if (!c->isFloatingOrPositioned() && !c->isText()) {
int rp = c->xPos() + c->rightmostPosition(false);
right = kMax(right, rp);
}
}
return right;
}
int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
{
int left = RenderContainer::leftmostPosition(includeOverflowInterior, includeSelf);
if (!includeOverflowInterior && hasOverflowClip())
return left;
// FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
// For now, we have to descend into all the children, since we may have a huge abs div inside
// a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
// the abs div.
for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
if (!c->isFloatingOrPositioned() && !c->isText()) {
int lp = c->xPos() + c->leftmostPosition(false);
left = kMin(left, lp);
}
}
return left;
}
QRect RenderFlow::caretRect(int offset, EAffinity affinity, int *extraWidthToEndOfLine)
{
if (firstChild() || style()->display() == INLINE) {
// Do the normal calculation
return RenderContainer::caretRect(offset, affinity, extraWidthToEndOfLine);
}
// This is a special case:
// The element is not an inline element, and it's empty. So we have to
// calculate a fake position to indicate where objects are to be inserted.
int _x, _y, width, height;
// EDIT FIXME: this does neither take into regard :first-line nor :first-letter
// However, as soon as some content is entered, the line boxes will be
// constructed properly and this kludge is not called any more. So only
// the caret size of an empty :first-line'd block is wrong, but I think we
// can live with that.
RenderStyle *currentStyle = style(true);
//height = currentStyle->fontMetrics().height();
height = lineHeight(true);
width = 1;
// EDIT FIXME: This needs to account for text direction
int w = this->width();
switch (currentStyle->textAlign()) {
case LEFT:
case KHTML_LEFT:
case TAAUTO:
case JUSTIFY:
default:
_x = 0;
break;
case CENTER:
case KHTML_CENTER:
_x = w / 2;
break;
case RIGHT:
case KHTML_RIGHT:
_x = w;
break;
}
_y = 0;
if (extraWidthToEndOfLine) {
if (isRenderBlock()) {
*extraWidthToEndOfLine = this->width() - (_x + width);
} else {
int myRight = _x + width;
int ignore;
absolutePosition(myRight, ignore);
int containerRight = containingBlock()->xPos() + containingBlockWidth();
absolutePosition(containerRight, ignore);
*extraWidthToEndOfLine = containerRight - myRight;
}
}
int absx, absy;
absolutePosition(absx, absy, false);
_x += absx + paddingLeft() + borderLeft();
_y += absy + paddingTop() + borderTop();
return QRect(_x, _y, width, height);
}
#if APPLE_CHANGES
void RenderFlow::addFocusRingRects(QPainter *p, int _tx, int _ty)
{
// Only paint focus ring around outermost contenteditable element.
// But skip the body element if it is outermost.
if (element() && element()->isContentEditable()) {
if (element()->parentNode() && !element()->parentNode()->isContentEditable() && element()->id() != ID_BODY)
p->addFocusRingRect(_tx, _ty, width(), height());
return;
}
for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
p->addFocusRingRect(_tx + curr->xPos(),
_ty + curr->yPos(),
curr->width(),
curr->height());
}
for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (!curr->isText())
curr->addFocusRingRects(p, _tx + curr->xPos(), _ty + curr->yPos());
}
if (continuation())
continuation()->addFocusRingRects(p,
_tx - containingBlock()->xPos() + continuation()->xPos(),
_ty - containingBlock()->yPos() + continuation()->yPos());
}
void RenderFlow::paintFocusRing(QPainter *p, int tx, int ty)
{
int ow = style()->outlineWidth();
QColor oc = style()->outlineColor();
if (!oc.isValid())
oc = style()->color();
p->initFocusRing(ow, style()->outlineOffset(), oc);
addFocusRingRects(p, tx, ty);
p->drawFocusRing();
p->clearFocusRing();
}
#endif
void RenderFlow::paintOutlines(QPainter *p, int _tx, int _ty)
{
if (style()->outlineStyle() <= BHIDDEN)
return;
QPtrList rects;
rects.setAutoDelete(true);
rects.append(new QRect(0,0,0,0));
for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
rects.append(new QRect(curr->xPos(), curr->yPos(), curr->width(), curr->height()));
}
rects.append(new QRect(0,0,0,0));
for (unsigned int i = 1; i < rects.count() - 1; i++)
paintOutlineForLine(p, _tx, _ty, *rects.at(i-1), *rects.at(i), *rects.at(i+1));
}
void RenderFlow::paintOutlineForLine(QPainter *p, int tx, int ty, const QRect &lastline, const QRect &thisline, const QRect &nextline)
{
int ow = style()->outlineWidth();
EBorderStyle os = style()->outlineStyle();
QColor oc = style()->outlineColor();
if (!oc.isValid())
oc = style()->color();
int offset = style()->outlineOffset();
int t = ty + thisline.top() - offset;
int l = tx + thisline.left() - offset;
int b = ty + thisline.bottom() + offset + 1;
int r = tx + thisline.right() + offset + 1;
// left edge
drawBorder(p,
l - ow,
t - (lastline.isEmpty() || thisline.left() < lastline.left() || lastline.right() <= thisline.left() ? ow : 0),
l,
b + (nextline.isEmpty() || thisline.left() <= nextline.left() || nextline.right() <= thisline.left() ? ow : 0),
BSLeft,
oc, style()->color(), os,
(lastline.isEmpty() || thisline.left() < lastline.left() || lastline.right() <= thisline.left() ? ow : -ow),
(nextline.isEmpty() || thisline.left() <= nextline.left() || nextline.right() <= thisline.left() ? ow : -ow),
true);
// right edge
drawBorder(p,
r,
t - (lastline.isEmpty() || lastline.right() < thisline.right() || thisline.right() <= lastline.left() ? ow : 0),
r + ow,
b + (nextline.isEmpty() || nextline.right() <= thisline.right() || thisline.right() <= nextline.left() ? ow : 0),
BSRight,
oc, style()->color(), os,
(lastline.isEmpty() || lastline.right() < thisline.right() || thisline.right() <= lastline.left() ? ow : -ow),
(nextline.isEmpty() || nextline.right() <= thisline.right() || thisline.right() <= nextline.left() ? ow : -ow),
true);
// upper edge
if ( thisline.left() < lastline.left())
drawBorder(p,
l - ow,
t - ow,
QMIN(r+ow, (lastline.isValid()? tx+lastline.left() : 1000000)),
t ,
BSTop, oc, style()->color(), os,
ow,
(lastline.isValid() && tx+lastline.left()+1color(), os,
(lastline.isValid() && l-ow < tx+lastline.right()+1 ? -ow : ow),
ow,
true);
// lower edge
if ( thisline.left() < nextline.left())
drawBorder(p,
l - ow,
b,
QMIN(r+ow, nextline.isValid()? tx+nextline.left()+1 : 1000000),
b + ow,
BSBottom, oc, style()->color(), os,
ow,
(nextline.isValid() && tx+nextline.left()+1color(), os,
(nextline.isValid() && l-ow < tx+nextline.right()+1? -ow : ow),
ow,
true);
}