fix logic
[personal-kdelibs.git] / khtml / rendering / render_inline.cpp
blobbc7c2215ec36c145baeec27381c08cdd56e180d9
1 /*
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>
34 #include <kglobal.h>
36 using DOM::Position;
37 using namespace khtml;
39 void RenderInline::setStyle(RenderStyle* _style)
41 RenderFlow::setStyle(_style);
42 setInline(true);
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();
51 while (currCont) {
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();
61 if (attached()) {
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()
72 RenderFlow::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);
101 return;
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());
114 return o;
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;
128 while (o) {
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();
159 while (o) {
160 RenderObject* tmp = o;
161 o = tmp->nextSibling();
162 clone->appendChildNode(curr->removeChildNode(tmp));
163 tmp->setNeedsLayoutAndMinMaxRecalc();
166 // Keep walking up the chain.
167 currChild = curr;
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();
177 while (o) {
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.
196 pre = block;
197 block = block->containingBlock();
199 else {
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;
216 while (o)
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.
239 pre->close();
240 pre->setPos(0, -500000);
241 pre->setNeedsLayoutAndMinMaxRecalc();
242 newBlockBox->close();
243 newBlockBox->setPos(0, -500000);
244 newBlockBox->setNeedsLayout(true);
245 post->close();
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);
272 return true;
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:
281 * 2 0 1
282 * x------x<-----x
283 * (0 is stack-top in point-array)
285 * This will be reduced to
286 * 1 0
287 * x------x
289 * Preconditions:
290 * - No other spikes exist in the whole point-array except at most
291 * one at the end
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
294 * true
295 * - No such spike exists where 2 is situated between 0 and 1.
297 * Postcondition:
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();
307 QPoint p0 = *--it;
308 QPoint p1 = *--it;
309 QPoint p2 = *--it;
311 bool elide = false;
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();
330 if (!elide)
331 pointArray.push_back(p0);
332 return true;
334 return false;
338 * Reduces segment separators.
340 * A segment separator separates a segment into two segments, thus causing
341 * two adjacent segment with the same orientation.
343 * 2 1 0
344 * x-------x---->x
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:
351 * 1 0
352 * x------------>x
354 * Preconditions:
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
359 * true
360 * - No such spike exists where 2 is situated between 0 and 1.
362 * Postcondition:
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
366 * left unchanged.
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();
373 QPoint p0 = *--it;
374 QPoint p1 = *--it;
375 QPoint p2 = *--it;
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);
388 return true;
390 return false;
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
407 * the given array.
408 * @param box inline box
409 * @param pointArray array collecting coordinates
410 * @param bottom \c true, collect bottom coordinates, \c false, collect top
411 * coordinates.
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))
424 y = limit;
425 int x = box->xPos() + bottom*box->width() + offset;
426 QPoint newPnt(x, y);
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();
434 insPnt.ry() = y;
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
449 // x-coordinate
450 int l2;
451 if (b->xPos() != box->xPos() && b->xPos() + b->width() != box->xPos() + box->width())
452 l2 = y;
453 else
454 l2 = limit;
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
478 * succeeding
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------------------+
537 * ^ | ^ |
538 * | | ==> | |
539 * +..... ...+ +..... ...+
541 * +----->X--------+ +----------------->X
542 * | | ==> | |
543 * +..... ...+ +..... ...+
545 * ^X
546 * ||
547 * +-----++--------+ +----------------->X
548 * | | ==> | |
549 * +..... ...+ +..... ...+
551 * @param pointArray point-array
552 * @return actual begin of point array
554 static QPoint *linkEndToBegin(QVector<QPoint> &pointArray)
556 uint index = 0;
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();
566 linkup = true;
569 const QPoint *it = pointArray.begin() + index;
570 QPoint pfirst = *it;
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()) {
578 ++index;
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) {
583 ++index;
584 appendPoint(pointArray, *++it);
586 } else if (linkup)
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)
608 return;
609 int offset = style()->outlineOffset();
611 // We may have to draw more than one outline path as they may be
612 // disjoint.
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));
637 // paint the outline
638 paintOutlinePath(p, _tx, _ty, begin, path.end(),
639 bs, direction, endingBS);
643 template<class T> inline void kSwap(T &a1, T &a2)
645 T tmp = a2;
646 a2 = a1;
647 a1 = tmp;
650 enum BSOrientation { BSHorizontal, BSVertical };
653 * Returns the orientation of the given border side.
655 inline BSOrientation bsOrientation(RenderObject::BorderSide bs)
657 switch (bs) {
658 case RenderObject::BSTop:
659 case RenderObject::BSBottom:
660 return BSHorizontal;
661 case RenderObject::BSLeft:
662 case RenderObject::BSRight:
663 return BSVertical;
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
698 * @param p painter
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();
721 if (x1 > x2) {
722 kSwap(x1, x2);
723 if (bsOrientation(curBS) == BSHorizontal) kSwap(prevBS, nextBS);
725 if (y1 > y2) {
726 kSwap(y1, y2);
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);*/
733 switch (curBS) {
734 case RenderObject::BSLeft:
735 case RenderObject::BSRight:
736 /* p->setPen(QColor("#ffe4dd"));
737 p->drawLine(
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)
742 );*/
743 o->drawBorder(p,
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,
753 true);
754 break;
755 case RenderObject::BSBottom:
756 case RenderObject::BSTop:
757 // kDebug(6040) << "BSTop/BSBottom: prevBS " << prevBS << " curBS " << curBS << " nextBS " << nextBS;
758 o->drawBorder(p,
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,
768 true);
769 break;
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.
777 return;
779 QPoint last = *begin;
780 BorderSide lastBS = bs;
781 Q_ASSERT(begin != end);
782 ++begin;
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) {
790 QPoint cur = *it;
791 // kDebug(6040) << "cur: " << cur;
792 BorderSide nextBS;
793 if (it + 1 != end) {
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;
798 } else
799 nextBS = endingBS;
801 Q_ASSERT(bsOrientation(bs) != bsOrientation(nextBS));
802 paintOutlineSegment(this, p, tx, ty, last, cur,
803 lastBS, bs, nextBS);
804 lastBS = bs;
805 last = cur;
806 bs = nextBS;
811 void RenderInline::calcMinMaxWidth()
813 KHTMLAssert( !minMaxKnown() );
815 #ifdef DEBUG_LAYOUT
816 kDebug( 6040 ) << renderName() << "(RenderInline)::calcMinMaxWidth() this=" << this;
817 #endif
819 // Irrelevant, since some enclosing block will actually measure us and our children.
820 m_minWidth = 0;
821 m_maxWidth = 0;
823 setMinMaxKnown();
826 short RenderInline::width() const
828 // Return the width of the minimal left side and the maximal right side.
829 short leftSide = 0;
830 short rightSide = 0;
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
843 int h = 0;
844 if (firstLineBox())
845 h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
846 return h;
849 int RenderInline::offsetLeft() const
851 int x = RenderFlow::offsetLeft();
852 if (firstLineBox())
853 x += firstLineBox()->xPos();
854 return x;
857 int RenderInline::offsetTop() const
859 int y = RenderFlow::offsetTop();
860 if (firstLineBox())
861 y += firstLineBox()->yPos();
862 return y;
865 const char *RenderInline::renderName() const
867 if (isRelPositioned())
868 return "RenderInline (relative positioned)";
869 if (isAnonymous())
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))
880 inside = true;
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());
907 return inside;
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
922 _x = -1;
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
932 // the same.
933 InlineFlowBox *line = !outsideEnd ^ rtl ? firstLineBox() : lastLineBox();
935 if (!line) { // umpf, handle "gracefully"
936 RenderFlow::caretPos(offset, flags, _x, _y, width, height);
937 return;
940 _x = line->xPos();
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()
948 : 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();
955 } else {
956 _x += line->width() + line->marginBorderPaddingRight();
959 } else {
960 const QFontMetrics &fm = style()->fontMetrics();
961 _y = line->yPos() + line->baseline() - fm.ascent();
962 height = fm.height();
965 int absx, absy;
966 if (cb && cb->absolutePosition(absx,absy)) {
967 //kDebug(6040) << "absx=" << absx << " absy=" << absy;
968 _x += absx;
969 _y += absy;
970 } else {
971 // we don't know our absolute position, and there is no point returning
972 // just a relative one
973 _x = _y = -1;
977 inline int minXPos(const RenderInline *o)
979 int retval=6666666;
980 if (!o->firstLineBox()) return 0;
981 for (InlineRunBox* curr = o->firstLineBox(); curr; curr = curr->nextLineBox())
982 retval = qMin( retval, int( curr->m_x ));
983 return retval;
986 int RenderInline::inlineXPos() const
988 return minXPos(this);
991 int RenderInline::inlineYPos() const
993 return firstLineBox() ? firstLineBox()->yPos() : 0;