2 * This file is part of the render object implementation for KHTML.
4 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5 * (C) 1999-2003 Antti Koivisto (koivisto@kde.org)
6 * (C) 2002-2003 Dirk Mueller (mueller@kde.org)
7 * (C) 2003 Apple Computer, Inc.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
26 #include "render_inline.h"
28 #include "render_arena.h"
29 #include "render_block.h"
31 #include <xml/dom_docimpl.h>
32 #include <xml/dom_position.h>
37 using namespace khtml
;
39 void RenderInline::setStyle(RenderStyle
* _style
)
41 RenderFlow::setStyle(_style
);
44 // Ensure that all of the split inlines pick up the new style. We
45 // only do this if we're an inline, since we don't want to propagate
46 // a block's style to the other inlines.
47 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
48 // and after the block share the same style, but the block doesn't
49 // need to pass its style on to anyone else.
50 RenderFlow
* currCont
= continuation();
52 if (currCont
->isInline()) {
53 RenderFlow
* nextCont
= currCont
->continuation();
54 currCont
->setContinuation(0);
55 currCont
->setStyle(style());
56 currCont
->setContinuation(nextCont
);
58 currCont
= currCont
->continuation();
62 // Update replaced content
63 updateReplacedContent();
64 // Update pseudos for ::before and ::after
65 updatePseudoChildren();
69 // Attach handles initial setStyle that requires parent nodes
70 void RenderInline::attach()
74 updateReplacedContent();
75 updatePseudoChildren();
78 bool RenderInline::isInlineContinuation() const
80 return m_isContinuation
;
83 void RenderInline::addChildToFlow(RenderObject
* newChild
, RenderObject
* beforeChild
)
85 // Make sure we don't append things after :after-generated content if we have it.
86 if (!beforeChild
&& lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER
)
87 beforeChild
= lastChild();
89 if (!newChild
->isInline() && !newChild
->isFloatingOrPositioned() )
91 // We are placing a block inside an inline. We have to perform a split of this
92 // inline into continuations. This involves creating an anonymous block box to hold
93 // |newChild|. We then make that block box a continuation of this inline. We take all of
94 // the children after |beforeChild| and put them in a clone of this object.
96 RenderBlock
*newBox
= createAnonymousBlock();
97 RenderFlow
* oldContinuation
= continuation();
98 setContinuation(newBox
);
100 splitFlow(beforeChild
, newBox
, newChild
, oldContinuation
);
104 RenderBox::addChild(newChild
,beforeChild
);
106 newChild
->setNeedsLayoutAndMinMaxRecalc();
109 RenderInline
* RenderInline::cloneInline(RenderFlow
* src
)
111 RenderInline
*o
= new (src
->renderArena()) RenderInline(src
->element());
112 o
->m_isContinuation
= true;
113 o
->setStyle(src
->style());
117 void RenderInline::splitInlines(RenderBlock
* fromBlock
, RenderBlock
* toBlock
,
118 RenderBlock
* middleBlock
,
119 RenderObject
* beforeChild
, RenderFlow
* oldCont
)
121 // Create a clone of this inline.
122 RenderInline
* clone
= cloneInline(this);
123 clone
->setContinuation(oldCont
);
125 // Now take all of the children from beforeChild to the end and remove
126 // then from |this| and place them in the clone.
127 RenderObject
* o
= beforeChild
;
129 RenderObject
* tmp
= o
;
130 o
= tmp
->nextSibling();
131 clone
->addChildToFlow(removeChildNode(tmp
), 0);
132 tmp
->setNeedsLayoutAndMinMaxRecalc();
135 // Hook |clone| up as the continuation of the middle block.
136 middleBlock
->setContinuation(clone
);
138 // We have been reparented and are now under the fromBlock. We need
139 // to walk up our inline parent chain until we hit the containing block.
140 // Once we hit the containing block we're done.
141 RenderFlow
* curr
= static_cast<RenderFlow
*>(parent());
142 RenderFlow
* currChild
= this;
143 while (curr
&& curr
!= fromBlock
) {
144 // Create a new clone.
145 RenderInline
* cloneChild
= clone
;
146 clone
= cloneInline(curr
);
148 // Insert our child clone as the first child.
149 clone
->addChildToFlow(cloneChild
, 0);
151 // Hook the clone up as a continuation of |curr|.
152 RenderFlow
* oldCont
= curr
->continuation();
153 curr
->setContinuation(clone
);
154 clone
->setContinuation(oldCont
);
156 // Now we need to take all of the children starting from the first child
157 // *after* currChild and append them all to the clone.
158 o
= currChild
->nextSibling();
160 RenderObject
* tmp
= o
;
161 o
= tmp
->nextSibling();
162 clone
->appendChildNode(curr
->removeChildNode(tmp
));
163 tmp
->setNeedsLayoutAndMinMaxRecalc();
166 // Keep walking up the chain.
168 curr
= static_cast<RenderFlow
*>(curr
->parent());
171 // Now we are at the block level. We need to put the clone into the toBlock.
172 toBlock
->appendChildNode(clone
);
174 // Now take all the children after currChild and remove them from the fromBlock
175 // and put them in the toBlock.
176 o
= currChild
->nextSibling();
178 RenderObject
* tmp
= o
;
179 o
= tmp
->nextSibling();
180 toBlock
->appendChildNode(fromBlock
->removeChildNode(tmp
));
184 void RenderInline::splitFlow(RenderObject
* beforeChild
, RenderBlock
* newBlockBox
,
185 RenderObject
* newChild
, RenderFlow
* oldCont
)
187 RenderBlock
* pre
= 0;
188 RenderBlock
* block
= containingBlock();
189 bool madeNewBeforeBlock
= false;
191 // Delete our line boxes before we do the inline split into continuations.
192 block
->deleteLineBoxTree();
194 if (block
->isAnonymousBlock()) {
195 // We can reuse this block and make it the preBlock of the next continuation.
197 block
= block
->containingBlock();
200 // No anonymous block available for use. Make one.
201 pre
= block
->createAnonymousBlock();
202 madeNewBeforeBlock
= true;
205 RenderBlock
* post
= block
->createAnonymousBlock();
207 RenderObject
* boxFirst
= madeNewBeforeBlock
? block
->firstChild() : pre
->nextSibling();
208 if (madeNewBeforeBlock
)
209 block
->insertChildNode(pre
, boxFirst
);
210 block
->insertChildNode(newBlockBox
, boxFirst
);
211 block
->insertChildNode(post
, boxFirst
);
212 block
->setChildrenInline(false);
214 if (madeNewBeforeBlock
) {
215 RenderObject
* o
= boxFirst
;
218 RenderObject
* no
= o
;
219 o
= no
->nextSibling();
220 pre
->appendChildNode(block
->removeChildNode(no
));
221 no
->setNeedsLayoutAndMinMaxRecalc();
225 splitInlines(pre
, post
, newBlockBox
, beforeChild
, oldCont
);
227 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
228 // time in makeChildrenNonInline by just setting this explicitly up front.
229 newBlockBox
->setChildrenInline(false);
231 // We don't just call addChild, since it would pass things off to the
232 // continuation, so we call addChildToFlow explicitly instead. We delayed
233 // adding the newChild until now so that the |newBlockBox| would be fully
234 // connected, thus allowing newChild access to a renderArena should it need
235 // to wrap itself in additional boxes (e.g., table construction).
236 newBlockBox
->addChildToFlow(newChild
, 0);
238 // XXXdwh is any of this even necessary? I don't think it is.
240 pre
->setPos(0, -500000);
241 pre
->setNeedsLayoutAndMinMaxRecalc();
242 newBlockBox
->close();
243 newBlockBox
->setPos(0, -500000);
244 newBlockBox
->setNeedsLayout(true);
246 post
->setPos(0, -500000);
247 post
->setNeedsLayoutAndMinMaxRecalc();
249 updatePseudoChildren();
251 block
->setNeedsLayoutAndMinMaxRecalc();
254 void RenderInline::paint(PaintInfo
& i
, int _tx
, int _ty
)
256 paintLines(i
, _tx
, _ty
);
260 * Appends the given coordinate-pair to the point-array if it is not
261 * equal to the last element.
262 * @param pointArray point-array
263 * @param pnt point to append
264 * @return \c true if \c pnt has actually been appended
266 inline static bool appendIfNew(QVector
<QPoint
> &pointArray
, const QPoint
&pnt
)
268 // if (!pointArray.isEmpty()) kDebug(6040) << "appifnew: " << pointArray.back() << " == " << pnt << ": " << (pointArray.back() == pnt);
269 // else kDebug(6040) << "appifnew: " << pnt << " (unconditional)";
270 if (!pointArray
.isEmpty() && pointArray
.back() == pnt
) return false;
271 pointArray
.append(pnt
);
276 * Does spike-reduction on the given point-array's stack-top.
278 * Spikes are path segments of which one goes forward, and the sucessor
279 * goes backward on the predecessor's segment:
283 * (0 is stack-top in point-array)
285 * This will be reduced to
290 * - No other spikes exist in the whole point-array except at most
292 * - No two succeeding points are ever equal
293 * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
295 * - No such spike exists where 2 is situated between 0 and 1.
298 * - No spikes exist in the whole point-array
300 * If no spike is found, the point-array is left unchanged.
301 * @return \c true if an actual reduction was done
303 inline static bool reduceSpike(QVector
<QPoint
> &pointArray
)
305 if (pointArray
.size() < 3) return false;
306 QVector
<QPoint
>::Iterator it
= pointArray
.end();
313 if (p0
.x() == p1
.x() && p1
.x() == p2
.x()
314 && (p1
.y() < p0
.y() && p0
.y() < p2
.y()
315 || p2
.y() < p0
.y() && p0
.y() < p1
.y()
316 || p1
.y() < p2
.y() && p2
.y() < p0
.y()
317 || p0
.y() < p2
.y() && p2
.y() < p1
.y()
318 || (elide
= p2
.y() == p0
.y() && p0
.y() < p1
.y())
319 || (elide
= p1
.y() < p0
.y() && p0
.y() == p2
.y()))
320 || p0
.y() == p1
.y() && p1
.y() == p2
.y()
321 && (p1
.x() < p0
.x() && p0
.x() < p2
.x()
322 || p2
.x() < p0
.x() && p0
.x() < p1
.x()
323 || p1
.x() < p2
.x() && p2
.x() < p0
.x()
324 || p0
.x() < p2
.x() && p2
.x() < p1
.x()
325 || (elide
= p2
.x() == p0
.x() && p0
.x() < p1
.x())
326 || (elide
= p1
.x() < p0
.x() && p0
.x() == p2
.x())))
328 // kDebug(6040) << "spikered p2" << (elide ? " (elide)" : "") << ": " << p2 << " p1: " << p1 << " p0: " << p0;
329 pointArray
.pop_back(); pointArray
.pop_back();
331 pointArray
.push_back(p0
);
338 * Reduces segment separators.
340 * A segment separator separates a segment into two segments, thus causing
341 * two adjacent segment with the same orientation.
345 * (0 means stack-top)
347 * Here, 1 is a segment separator. As segment separators not only make
348 * the line drawing algorithm inefficient, but also make the spike-reduction
349 * fail, they must be eliminated:
355 * - No other segment separators exist in the whole point-array except
356 * at most one at the end
357 * - No two succeeding points are ever equal
358 * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
360 * - No such spike exists where 2 is situated between 0 and 1.
363 * - No segment separators exist in the whole point-array
365 * If no segment separator is found at the end of the point-array, it is
367 * @return \c true if a segment separator was actually reduced.
369 inline static bool reduceSegmentSeparator(QVector
<QPoint
> &pointArray
)
371 if (pointArray
.size() < 3) return false;
372 QVector
<QPoint
>::Iterator it
= pointArray
.end();
376 // kDebug(6040) << "checking p2: " << p2 << " p1: " << p1 << " p0: " << p0;
378 if (p0
.x() == p1
.x() && p1
.x() == p2
.x()
379 && (p2
.y() < p1
.y() && p1
.y() < p0
.y()
380 || p0
.y() < p1
.y() && p1
.y() < p2
.y())
381 || p0
.y() == p1
.y() && p1
.y() == p2
.y()
382 && (p2
.x() < p1
.x() && p1
.x() < p0
.x()
383 || p0
.x() < p1
.x() && p1
.x() < p2
.x()))
385 // kDebug(6040) << "segred p2: " << p2 << " p1: " << p1 << " p0: " << p0;
386 pointArray
.pop_back(); pointArray
.pop_back();
387 pointArray
.push_back(p0
);
394 * Appends the given point to the point-array, doing necessary reductions to
395 * produce a path without spikes and segment separators.
397 static void appendPoint(QVector
<QPoint
> &pointArray
, const QPoint
&pnt
)
399 if (!appendIfNew(pointArray
, pnt
)) return;
400 // kDebug(6040) << "appendPoint: appended " << pnt;
401 reduceSegmentSeparator(pointArray
)
402 || reduceSpike(pointArray
);
406 * Traverses the horizontal inline boxes and appends the point coordinates to
408 * @param box inline box
409 * @param pointArray array collecting coordinates
410 * @param bottom \c true, collect bottom coordinates, \c false, collect top
412 * @param limit lower limit that an y-coordinate must at least reach. Note
413 * that limit designates the highest y-coordinate for \c bottom, and
414 * the lowest for !\c bottom.
416 static void collectHorizontalBoxCoordinates(InlineBox
*box
,
417 QVector
<QPoint
> &pointArray
,
418 bool bottom
, int offset
, int limit
= -500000)
420 // kDebug(6000) << "collectHorizontalBoxCoordinates: ";
421 offset
= bottom
? offset
:-offset
;
422 int y
= box
->yPos() + bottom
*box
->height() + offset
;
423 if (limit
!= -500000 && (bottom
? y
< limit
: y
> limit
))
425 int x
= box
->xPos() + bottom
*box
->width() + offset
;
427 // Add intersection point if point-array not empty.
428 if (!pointArray
.isEmpty()) {
429 QPoint lastPnt
= pointArray
.back();
430 QPoint
insPnt(newPnt
.x(), lastPnt
.y());
432 if (offset
&& ((bottom
&& lastPnt
.y() > y
) || (!bottom
&& lastPnt
.y() < y
))) {
433 insPnt
.rx() = lastPnt
.x();
436 // kDebug(6040) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt);
437 appendPoint(pointArray
, insPnt
);
439 // Insert starting point of box
440 appendPoint(pointArray
, newPnt
);
442 newPnt
.rx() += (bottom
? -box
->width() : box
->width()) - 2*offset
;
444 if (box
->isInlineFlowBox()) {
445 InlineFlowBox
*flowBox
= static_cast<InlineFlowBox
*>(box
);
446 for (InlineBox
*b
= bottom
? flowBox
->lastChild() : flowBox
->firstChild(); b
; b
= bottom
? b
->prevOnLine() : b
->nextOnLine()) {
447 // Don't let boxes smaller than this flow box' height influence
448 // the vertical position of the outline if they have a different
451 if (b
->xPos() != box
->xPos() && b
->xPos() + b
->width() != box
->xPos() + box
->width())
455 collectHorizontalBoxCoordinates(b
, pointArray
, bottom
, qAbs(offset
), l2
);
458 // Add intersection point if flow box contained any children
459 if (flowBox
->firstChild()) {
460 QPoint lastPnt
= pointArray
.back();
461 QPoint
insPnt(lastPnt
.x(), newPnt
.y());
462 // kDebug(6040) << "right: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt);
463 appendPoint(pointArray
, insPnt
);
467 // Insert ending point of box
468 appendPoint(pointArray
, newPnt
);
470 // kDebug(6000) << "collectHorizontalBoxCoordinates: " << "ende";
474 * Checks whether the given line box' extents and the following line box'
475 * extents are disjount (i. e. do not share the same x-coordinate range).
476 * @param line line box
477 * @param toBegin \c true, compare with preceding line box, \c false, with
479 * @return \c true if this and the next box are disjoint
481 inline static bool lineBoxesDisjoint(InlineRunBox
*line
, int offset
, bool toBegin
)
483 InlineRunBox
*next
= toBegin
? line
->prevLineBox() : line
->nextLineBox();
484 return !next
|| next
->xPos() + next
->width() + 2*offset
< line
->xPos()
485 || next
->xPos() > line
->xPos() + line
->width() + 2*offset
;
489 * Traverses the vertical outer borders of the given render flow's line
490 * boxes and appends the point coordinates to the given point array.
491 * @param line line box to begin traversal
492 * @param pointArray point array
493 * @param left \c true, traverse the left vertical coordinates,
494 * \c false, traverse the right vertical coordinates.
495 * @param lastline if not 0, returns the pointer to the last line box traversed
497 static void collectVerticalBoxCoordinates(InlineRunBox
*line
,
498 QVector
<QPoint
> &pointArray
,
499 bool left
, int offset
, InlineRunBox
**lastline
= 0)
501 InlineRunBox
*last
= 0;
502 offset
= left
? -offset
:offset
;
503 for (InlineRunBox
* curr
= line
; curr
&& !last
; curr
= left
? curr
->prevLineBox() : curr
->nextLineBox()) {
504 InlineBox
*root
= curr
;
506 bool isLast
= lineBoxesDisjoint(curr
, qAbs(offset
), left
);
507 if (isLast
) last
= curr
;
509 if (root
!= line
&& !isLast
)
510 while (root
->parent()) root
= root
->parent();
511 QPoint
newPnt(curr
->xPos() + !left
*curr
->width() + offset
,
512 (left
? root
->topOverflow() : root
->bottomOverflow()) + offset
);
513 if (!pointArray
.isEmpty()) {
514 QPoint lastPnt
= pointArray
.back();
515 if (newPnt
.x()>lastPnt
.x() && !left
)
516 pointArray
.back().setY( qMin(lastPnt
.y(), root
->topOverflow()-offset
) );
517 else if (newPnt
.x()<lastPnt
.x() && left
)
518 pointArray
.back().setY( qMax(lastPnt
.y(), root
->bottomOverflow()+offset
) );
519 QPoint
insPnt(newPnt
.x(), pointArray
.back().y());
520 // kDebug(6040) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt);
521 appendPoint(pointArray
, insPnt
);
523 appendPoint(pointArray
, newPnt
);
525 if (lastline
) *lastline
= last
;
529 * Links up the end of the given point-array such that the starting point
530 * is not a segment separator.
532 * To achieve this, improper points are removed from the beginning of
533 * the point-array (by changing the array's starting iterator), and
534 * proper ones appended to the point-array's back.
536 * X---------------+ X------------------+
539 * +..... ...+ +..... ...+
541 * +----->X--------+ +----------------->X
543 * +..... ...+ +..... ...+
547 * +-----++--------+ +----------------->X
549 * +..... ...+ +..... ...+
551 * @param pointArray point-array
552 * @return actual begin of point array
554 static QPoint
*linkEndToBegin(QVector
<QPoint
> &pointArray
)
557 // ### BUG: outlines with zero width aren't treated correctly
558 // this is not the right fix
559 if (pointArray
.size() < 3) return pointArray
.begin();
561 // if first and last points match, ignore the last one.
562 bool linkup
= false; QPoint linkupPnt
;
563 if (pointArray
.front() == pointArray
.back()) {
564 linkupPnt
= pointArray
.back();
565 pointArray
.pop_back();
569 const QPoint
*it
= pointArray
.begin() + index
;
571 QPoint pnext
= *++it
;
572 QPoint plast
= pointArray
.back();
573 // kDebug(6040) << "linkcheck plast: " << plast << " pfirst: " << pfirst << " pnext: " << pnext;
575 if (plast
.x() == pfirst
.x() && pfirst
.x() == pnext
.x()
576 || plast
.y() == pfirst
.y() && pfirst
.y() == pnext
.y()) {
579 appendPoint(pointArray
, pfirst
); // ### do we really need this point?
580 appendPoint(pointArray
, pnext
);
581 // ended up at a segment separator? move one point forward
582 if (plast
== pnext
) {
584 appendPoint(pointArray
, *++it
);
587 pointArray
.push_back(linkupPnt
);
588 return pointArray
.begin() + index
;
591 // assumes clock-wise orientation
592 static RenderObject::BorderSide
borderSide( const QPoint
&first
,
593 const QPoint
&second
)
595 if (second
.x() > first
.x())
596 return RenderObject::BSTop
;
597 else if (second
.x() < first
.x())
598 return RenderObject::BSBottom
;
599 else if (second
.y() > first
.y())
600 return RenderObject::BSRight
;
601 else // second.y() < first.y()
602 return RenderObject::BSLeft
;
605 void RenderInline::paintOutlines(QPainter
*p
, int _tx
, int _ty
)
607 if (style()->outlineWidth() == 0 || style()->outlineStyle() <= BHIDDEN
)
609 int offset
= style()->outlineOffset();
611 // We may have to draw more than one outline path as they may be
613 for (InlineRunBox
*curr
= firstLineBox(); curr
; curr
= curr
->nextLineBox()) {
614 QVector
<QPoint
> path
;
616 // collect topmost outline
617 collectHorizontalBoxCoordinates(curr
, path
, false, offset
);
618 // collect right outline
619 collectVerticalBoxCoordinates(curr
, path
, false, offset
, &curr
);
620 // collect bottommost outline
621 collectHorizontalBoxCoordinates(curr
, path
, true, offset
);
622 // collect left outline
623 collectVerticalBoxCoordinates(curr
, path
, true, offset
);
625 if (path
.size() < 3) continue;
627 const QPoint
*begin
= linkEndToBegin(path
);
629 // initial borderside and direction values
630 QPoint pstart
= *begin
;
631 QPoint pprev
= *(path
.end() - 2);
632 RenderObject::BorderSide bs
= borderSide(pprev
, pstart
);
633 QPoint diff
= pstart
- pprev
;
634 int direction
= diff
.x() + diff
.y();
635 RenderObject::BorderSide endingBS
= borderSide(*begin
, *(begin
+ 1));
638 paintOutlinePath(p
, _tx
, _ty
, begin
, path
.end(),
639 bs
, direction
, endingBS
);
643 template<class T
> inline void kSwap(T
&a1
, T
&a2
)
650 enum BSOrientation
{ BSHorizontal
, BSVertical
};
653 * Returns the orientation of the given border side.
655 inline BSOrientation
bsOrientation(RenderObject::BorderSide bs
)
658 case RenderObject::BSTop
:
659 case RenderObject::BSBottom
:
661 case RenderObject::BSLeft
:
662 case RenderObject::BSRight
:
665 return BSHorizontal
; // make gcc happy (sigh)
669 * Determines the new border side by evaluating the new direction as determined
670 * by the given coordinates, the old border side, and the relative direction.
672 * The relative direction specifies whether the old border side meets with the
673 * straight given by the coordinates from below/right (negative), or
674 * above/left (positive).
676 inline RenderObject::BorderSide
newBorderSide(RenderObject::BorderSide oldBS
, int direction
, const QPoint
&last
, const QPoint
&cur
)
678 bool below
= direction
< 0;
679 if (last
.x() == cur
.x()) { // new segment is vertical
680 bool t
= oldBS
== RenderObject::BSTop
;
681 bool b
= oldBS
== RenderObject::BSBottom
;
682 if ((t
|| b
) && last
.y() != cur
.y())
683 return (cur
.y() < last
.y()) ^ (t
&& below
|| b
&& !below
)
684 ? RenderObject::BSLeft
: RenderObject::BSRight
;
685 } else /*if (last.y() == cur.y())*/ { // new segment is horizontal
686 bool l
= oldBS
== RenderObject::BSLeft
;
687 bool r
= oldBS
== RenderObject::BSRight
;
688 if ((l
|| r
) && last
.x() != cur
.x())
689 return (cur
.x() < last
.x()) ^ (l
&& below
|| r
&& !below
)
690 ? RenderObject::BSTop
: RenderObject::BSBottom
;
692 return oldBS
; // same direction
696 * Draws an outline segment between the given two points.
697 * @param o render object
699 * @param tx absolute x-coordinate of containing block
700 * @param ty absolute y-coordinate of containing block
701 * @param p1 starting point
702 * @param p2 end point
703 * @param prevBS border side of previous segment
704 * @param curBS border side of this segment
705 * @param nextBS border side of next segment
707 static void paintOutlineSegment(RenderObject
*o
, QPainter
*p
, int tx
, int ty
,
708 const QPoint
&p1
, const QPoint
&p2
,
709 RenderObject::BorderSide prevBS
,
710 RenderObject::BorderSide curBS
,
711 RenderObject::BorderSide nextBS
)
713 int ow
= o
->style()->outlineWidth();
714 EBorderStyle os
= o
->style()->outlineStyle();
715 QColor oc
= o
->style()->outlineColor();
717 int x1
= tx
+ p1
.x();
718 int y1
= ty
+ p1
.y();
719 int x2
= tx
+ p2
.x();
720 int y2
= ty
+ p2
.y();
723 if (bsOrientation(curBS
) == BSHorizontal
) kSwap(prevBS
, nextBS
);
727 if (bsOrientation(curBS
) == BSVertical
) kSwap(prevBS
, nextBS
);
730 // kDebug(6040) << "segment(" << x1 << "," << y1 << ") - (" << x2 << "," << y2 << ")";
731 /* p->setPen(Qt::gray);
732 p->drawLine(x1,y1,x2,y2);*/
734 case RenderObject::BSLeft
:
735 case RenderObject::BSRight
:
736 /* p->setPen(QColor("#ffe4dd"));
738 x1 - (curBS == RenderObject::BSLeft ? ow : 0),
739 y1 - (prevBS == RenderObject::BSTop ? ow : 0),
740 x2 + (curBS == RenderObject::BSRight ? ow : 0),
741 y2 + (nextBS == RenderObject::BSBottom ? ow : 0)
744 x1
- (curBS
== RenderObject::BSLeft
? ow
: 0),
745 y1
- (prevBS
== RenderObject::BSTop
? ow
: 0),
746 x2
+ (curBS
== RenderObject::BSRight
? ow
: 0),
747 y2
+ (nextBS
== RenderObject::BSBottom
? ow
: 0),
748 curBS
, oc
, o
->style()->color(), os
,
749 prevBS
== RenderObject::BSTop
? ow
750 : prevBS
== RenderObject::BSBottom
? -ow
: 0,
751 nextBS
== RenderObject::BSTop
? -ow
752 : nextBS
== RenderObject::BSBottom
? ow
: 0,
755 case RenderObject::BSBottom
:
756 case RenderObject::BSTop
:
757 // kDebug(6040) << "BSTop/BSBottom: prevBS " << prevBS << " curBS " << curBS << " nextBS " << nextBS;
759 x1
- (prevBS
== RenderObject::BSLeft
? ow
: 0),
760 y1
- (curBS
== RenderObject::BSTop
? ow
: 0),
761 x2
+ (nextBS
== RenderObject::BSRight
? ow
: 0),
762 y2
+ (curBS
== RenderObject::BSBottom
? ow
: 0),
763 curBS
, oc
, o
->style()->color(), os
,
764 prevBS
== RenderObject::BSLeft
? ow
765 : prevBS
== RenderObject::BSRight
? -ow
: 0,
766 nextBS
== RenderObject::BSLeft
? -ow
767 : nextBS
== RenderObject::BSRight
? ow
: 0,
773 void RenderInline::paintOutlinePath(QPainter
*p
, int tx
, int ty
, const QPoint
*begin
, const QPoint
*end
, BorderSide bs
, int direction
, BorderSide endingBS
)
775 int ow
= style()->outlineWidth();
776 if (ow
== 0 || m_isContinuation
) // Continuations get painted by the original inline.
779 QPoint last
= *begin
;
780 BorderSide lastBS
= bs
;
781 Q_ASSERT(begin
!= end
);
784 // kDebug(6040) << "last: " << last;
786 bs
= newBorderSide(bs
, direction
, last
, *begin
);
787 // kDebug(6040) << "newBorderSide: " << lastBS << " " << direction << "d " << last << " - " << *begin << " => " << bs;
789 for (const QPoint
*it
= begin
; it
!= end
; ++it
) {
791 // kDebug(6040) << "cur: " << cur;
794 QPoint diff
= cur
- last
;
795 direction
= diff
.x() + diff
.y();
796 nextBS
= newBorderSide(bs
, direction
, cur
, *(it
+ 1));
797 // kDebug(6040) << "newBorderSide*: " << bs << " " << direction << "d " << cur << " - " << *(it + 1) << " => " << nextBS;
801 Q_ASSERT(bsOrientation(bs
) != bsOrientation(nextBS
));
802 paintOutlineSegment(this, p
, tx
, ty
, last
, cur
,
811 void RenderInline::calcMinMaxWidth()
813 KHTMLAssert( !minMaxKnown() );
816 kDebug( 6040 ) << renderName() << "(RenderInline)::calcMinMaxWidth() this=" << this;
819 // Irrelevant, since some enclosing block will actually measure us and our children.
826 short RenderInline::width() const
828 // Return the width of the minimal left side and the maximal right side.
831 for (InlineRunBox
* curr
= firstLineBox(); curr
; curr
= curr
->nextLineBox()) {
832 if (curr
== firstLineBox() || curr
->xPos() < leftSide
)
833 leftSide
= curr
->xPos();
834 if (curr
== firstLineBox() || curr
->xPos() + curr
->width() > rightSide
)
835 rightSide
= curr
->xPos() + curr
->width();
838 return rightSide
- leftSide
;
841 int RenderInline::height() const
845 h
= lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
849 int RenderInline::offsetLeft() const
851 int x
= RenderFlow::offsetLeft();
853 x
+= firstLineBox()->xPos();
857 int RenderInline::offsetTop() const
859 int y
= RenderFlow::offsetTop();
861 y
+= firstLineBox()->yPos();
865 const char *RenderInline::renderName() const
867 if (isRelPositioned())
868 return "RenderInline (relative positioned)";
870 return "RenderInline (anonymous)";
871 return "RenderInline";
874 bool RenderInline::nodeAtPoint(NodeInfo
& info
, int _x
, int _y
, int _tx
, int _ty
, HitTestAction hitTestAction
, bool inside
)
877 if ( hitTestAction != HitTestSelfOnly ) {
878 for (RenderObject* child = lastChild(); child; child = child->previousSibling())
879 if (!child->layer() && !child->isFloating() && child->nodeAtPoint(info, _x, _y, _tx, _ty, HitTestAll))
883 // Check our line boxes if we're still not inside.
884 if (/*hitTestAction != HitTestChildrenOnly &&*/ !inside
&& style()->visibility() != HIDDEN
) {
885 // See if we're inside one of our line boxes.
886 inside
= hitTestLines(info
, _x
, _y
, _tx
, _ty
, hitTestAction
);
889 if (inside
&& element()) {
890 if (info
.innerNode() && info
.innerNode()->renderer() &&
891 !info
.innerNode()->renderer()->isInline()) {
892 // Within the same layer, inlines are ALWAYS fully above blocks. Change inner node.
893 info
.setInnerNode(element());
895 // Clear everything else.
896 info
.setInnerNonSharedNode(0);
897 info
.setURLElement(0);
900 if (!info
.innerNode())
901 info
.setInnerNode(element());
903 if(!info
.innerNonSharedNode())
904 info
.setInnerNonSharedNode(element());
910 Position
RenderInline::positionForCoordinates(int x
, int y
)
912 for (RenderObject
*c
= continuation(); c
; c
= c
->continuation()) {
913 if (c
->isInline() || c
->firstChild())
914 return c
->positionForCoordinates(x
, y
);
917 return RenderFlow::positionForCoordinates(x
, y
);
920 void RenderInline::caretPos(int offset
, int flags
, int &_x
, int &_y
, int &width
, int &height
) const
924 RenderBlock
*cb
= containingBlock();
925 bool rtl
= cb
->style()->direction() == RTL
;
926 bool outsideEnd
= flags
& CFOutsideEnd
;
927 // I need to explain that: outsideEnd contains a meaningful value if
928 // and only if flags & CFOutside is set. If it is not, then randomly
929 // either the first or the last line box is returned.
930 // This doesn't matter because the only case this can happen is on an
931 // empty inline element, whose first and last line boxes are actually
933 InlineFlowBox
*line
= !outsideEnd
^ rtl
? firstLineBox() : lastLineBox();
935 if (!line
) { // umpf, handle "gracefully"
936 RenderFlow::caretPos(offset
, flags
, _x
, _y
, width
, height
);
941 width
= 1; // ### regard CFOverride
943 // Place caret outside the border
944 if (flags
& CFOutside
) {
945 RenderStyle
*s
= element() && element()->parent()
946 && element()->parent()->renderer()
947 ? element()->parent()->renderer()->style()
949 const QFontMetrics
&fm
= s
->fontMetrics();
950 _y
= line
->yPos() + line
->baseline() - fm
.ascent();
951 height
= fm
.height();
953 if (!outsideEnd
^ rtl
) {
954 _x
-= line
->marginBorderPaddingLeft();
956 _x
+= line
->width() + line
->marginBorderPaddingRight();
960 const QFontMetrics
&fm
= style()->fontMetrics();
961 _y
= line
->yPos() + line
->baseline() - fm
.ascent();
962 height
= fm
.height();
966 if (cb
&& cb
->absolutePosition(absx
,absy
)) {
967 //kDebug(6040) << "absx=" << absx << " absy=" << absy;
971 // we don't know our absolute position, and there is no point returning
972 // just a relative one
977 inline int minXPos(const RenderInline
*o
)
980 if (!o
->firstLineBox()) return 0;
981 for (InlineRunBox
* curr
= o
->firstLineBox(); curr
; curr
= curr
->nextLineBox())
982 retval
= qMin( retval
, int( curr
->m_x
));
986 int RenderInline::inlineXPos() const
988 return minXPos(this);
991 int RenderInline::inlineYPos() const
993 return firstLineBox() ? firstLineBox()->yPos() : 0;