File indexing completed on 2024-04-28 11:38:45
0001 /* 0002 * This file is part of the render object implementation for KHTML. 0003 * 0004 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) 0005 * (C) 1999-2003 Antti Koivisto (koivisto@kde.org) 0006 * (C) 2002-2003 Dirk Mueller (mueller@kde.org) 0007 * (C) 2003 Apple Computer, Inc. 0008 * 0009 * This library is free software; you can redistribute it and/or 0010 * modify it under the terms of the GNU Library General Public 0011 * License as published by the Free Software Foundation; either 0012 * version 2 of the License, or (at your option) any later version. 0013 * 0014 * This library is distributed in the hope that it will be useful, 0015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 * Library General Public License for more details. 0018 * 0019 * You should have received a copy of the GNU Library General Public License 0020 * along with this library; see the file COPYING.LIB. If not, write to 0021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0022 * Boston, MA 02110-1301, USA. 0023 * 0024 */ 0025 0026 #include "render_inline.h" 0027 0028 #include "render_arena.h" 0029 #include "render_block.h" 0030 #include "rendering/render_position.h" 0031 0032 #include <xml/dom_docimpl.h> 0033 0034 using namespace khtml; 0035 0036 void RenderInline::setStyle(RenderStyle *_style) 0037 { 0038 RenderFlow::setStyle(_style); 0039 setInline(true); 0040 0041 // Ensure that all of the split inlines pick up the new style. We 0042 // only do this if we're an inline, since we don't want to propagate 0043 // a block's style to the other inlines. 0044 // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before 0045 // and after the block share the same style, but the block doesn't 0046 // need to pass its style on to anyone else. 0047 RenderFlow *currCont = continuation(); 0048 while (currCont) { 0049 if (currCont->isInline()) { 0050 RenderFlow *nextCont = currCont->continuation(); 0051 currCont->setContinuation(nullptr); 0052 currCont->setStyle(style()); 0053 currCont->setContinuation(nextCont); 0054 } 0055 currCont = currCont->continuation(); 0056 } 0057 0058 if (attached()) { 0059 // Update replaced content 0060 updateReplacedContent(); 0061 // Update pseudos for ::before and ::after 0062 updatePseudoChildren(); 0063 } 0064 } 0065 0066 // Attach handles initial setStyle that requires parent nodes 0067 void RenderInline::attach() 0068 { 0069 RenderFlow::attach(); 0070 0071 updateReplacedContent(); 0072 updatePseudoChildren(); 0073 } 0074 0075 bool RenderInline::isInlineContinuation() const 0076 { 0077 return m_isContinuation; 0078 } 0079 0080 void RenderInline::addChildToFlow(RenderObject *newChild, RenderObject *beforeChild) 0081 { 0082 // Make sure we don't append things after :after-generated content if we have it. 0083 if (!beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER) { 0084 beforeChild = lastChild(); 0085 } 0086 0087 if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) { 0088 // We are placing a block inside an inline. We have to perform a split of this 0089 // inline into continuations. This involves creating an anonymous block box to hold 0090 // |newChild|. We then make that block box a continuation of this inline. We take all of 0091 // the children after |beforeChild| and put them in a clone of this object. 0092 0093 RenderBlock *newBox = createAnonymousBlock(); 0094 RenderFlow *oldContinuation = continuation(); 0095 setContinuation(newBox); 0096 0097 splitFlow(beforeChild, newBox, newChild, oldContinuation); 0098 return; 0099 } 0100 0101 RenderBox::addChild(newChild, beforeChild); 0102 0103 newChild->setNeedsLayoutAndMinMaxRecalc(); 0104 } 0105 0106 RenderInline *RenderInline::cloneInline(RenderFlow *src) 0107 { 0108 RenderInline *o = new(src->renderArena()) RenderInline(src->element()); 0109 o->m_isContinuation = true; 0110 o->setStyle(src->style()); 0111 return o; 0112 } 0113 0114 void RenderInline::splitInlines(RenderBlock *fromBlock, RenderBlock *toBlock, 0115 RenderBlock *middleBlock, 0116 RenderObject *beforeChild, RenderFlow *oldCont) 0117 { 0118 // Create a clone of this inline. 0119 RenderInline *clone = cloneInline(this); 0120 clone->setContinuation(oldCont); 0121 0122 // Now take all of the children from beforeChild to the end and remove 0123 // then from |this| and place them in the clone. 0124 RenderObject *o = beforeChild; 0125 while (o) { 0126 RenderObject *tmp = o; 0127 o = tmp->nextSibling(); 0128 clone->addChildToFlow(removeChildNode(tmp), nullptr); 0129 tmp->setNeedsLayoutAndMinMaxRecalc(); 0130 } 0131 0132 // Hook |clone| up as the continuation of the middle block. 0133 middleBlock->setContinuation(clone); 0134 0135 // We have been reparented and are now under the fromBlock. We need 0136 // to walk up our inline parent chain until we hit the containing block. 0137 // Once we hit the containing block we're done. 0138 RenderFlow *curr = static_cast<RenderFlow *>(parent()); 0139 RenderFlow *currChild = this; 0140 while (curr && curr != fromBlock) { 0141 // Create a new clone. 0142 RenderInline *cloneChild = clone; 0143 clone = cloneInline(curr); 0144 0145 // Insert our child clone as the first child. 0146 clone->addChildToFlow(cloneChild, nullptr); 0147 0148 // Hook the clone up as a continuation of |curr|. 0149 RenderFlow *oldCont = curr->continuation(); 0150 curr->setContinuation(clone); 0151 clone->setContinuation(oldCont); 0152 0153 // Now we need to take all of the children starting from the first child 0154 // *after* currChild and append them all to the clone. 0155 o = currChild->nextSibling(); 0156 while (o) { 0157 RenderObject *tmp = o; 0158 o = tmp->nextSibling(); 0159 clone->appendChildNode(curr->removeChildNode(tmp)); 0160 tmp->setNeedsLayoutAndMinMaxRecalc(); 0161 } 0162 0163 // Keep walking up the chain. 0164 currChild = curr; 0165 curr = static_cast<RenderFlow *>(curr->parent()); 0166 } 0167 0168 // Now we are at the block level. We need to put the clone into the toBlock. 0169 toBlock->appendChildNode(clone); 0170 0171 // Now take all the children after currChild and remove them from the fromBlock 0172 // and put them in the toBlock. 0173 o = currChild->nextSibling(); 0174 while (o) { 0175 RenderObject *tmp = o; 0176 o = tmp->nextSibling(); 0177 toBlock->appendChildNode(fromBlock->removeChildNode(tmp)); 0178 } 0179 } 0180 0181 void RenderInline::splitFlow(RenderObject *beforeChild, RenderBlock *newBlockBox, 0182 RenderObject *newChild, RenderFlow *oldCont) 0183 { 0184 RenderBlock *pre = nullptr; 0185 RenderBlock *block = containingBlock(); 0186 bool madeNewBeforeBlock = false; 0187 0188 // Delete our line boxes before we do the inline split into continuations. 0189 block->deleteLineBoxTree(); 0190 0191 if (block->isAnonymousBlock()) { 0192 // We can reuse this block and make it the preBlock of the next continuation. 0193 pre = block; 0194 block = block->containingBlock(); 0195 } else { 0196 // No anonymous block available for use. Make one. 0197 pre = block->createAnonymousBlock(); 0198 madeNewBeforeBlock = true; 0199 } 0200 0201 RenderBlock *post = block->createAnonymousBlock(); 0202 0203 RenderObject *boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); 0204 if (madeNewBeforeBlock) { 0205 block->insertChildNode(pre, boxFirst); 0206 } 0207 block->insertChildNode(newBlockBox, boxFirst); 0208 block->insertChildNode(post, boxFirst); 0209 block->setChildrenInline(false); 0210 0211 if (madeNewBeforeBlock) { 0212 RenderObject *o = boxFirst; 0213 while (o) { 0214 RenderObject *no = o; 0215 o = no->nextSibling(); 0216 pre->appendChildNode(block->removeChildNode(no)); 0217 no->setNeedsLayoutAndMinMaxRecalc(); 0218 } 0219 } 0220 0221 splitInlines(pre, post, newBlockBox, beforeChild, oldCont); 0222 0223 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting 0224 // time in makeChildrenNonInline by just setting this explicitly up front. 0225 newBlockBox->setChildrenInline(false); 0226 0227 // We don't just call addChild, since it would pass things off to the 0228 // continuation, so we call addChildToFlow explicitly instead. We delayed 0229 // adding the newChild until now so that the |newBlockBox| would be fully 0230 // connected, thus allowing newChild access to a renderArena should it need 0231 // to wrap itself in additional boxes (e.g., table construction). 0232 newBlockBox->addChildToFlow(newChild, nullptr); 0233 0234 // XXXdwh is any of this even necessary? I don't think it is. 0235 pre->close(); 0236 pre->setPos(0, -500000); 0237 pre->setNeedsLayoutAndMinMaxRecalc(); 0238 newBlockBox->close(); 0239 newBlockBox->setPos(0, -500000); 0240 newBlockBox->setNeedsLayout(true); 0241 post->close(); 0242 post->setPos(0, -500000); 0243 post->setNeedsLayoutAndMinMaxRecalc(); 0244 0245 updatePseudoChildren(); 0246 0247 block->setNeedsLayoutAndMinMaxRecalc(); 0248 } 0249 0250 void RenderInline::paint(PaintInfo &i, int _tx, int _ty) 0251 { 0252 paintLines(i, _tx, _ty); 0253 } 0254 0255 /** 0256 * Appends the given coordinate-pair to the point-array if it is not 0257 * equal to the last element. 0258 * @param pointArray point-array 0259 * @param pnt point to append 0260 * @return \c true if \c pnt has actually been appended 0261 */ 0262 inline static bool appendIfNew(QVector<QPoint> &pointArray, const QPoint &pnt) 0263 { 0264 // if (!pointArray.isEmpty()) qCDebug(KHTML_LOG) << "appifnew: " << pointArray.back() << " == " << pnt << ": " << (pointArray.back() == pnt); 0265 // else qCDebug(KHTML_LOG) << "appifnew: " << pnt << " (unconditional)"; 0266 if (!pointArray.isEmpty() && pointArray.back() == pnt) { 0267 return false; 0268 } 0269 pointArray.append(pnt); 0270 return true; 0271 } 0272 0273 /** 0274 * Does spike-reduction on the given point-array's stack-top. 0275 * 0276 * Spikes are path segments of which one goes forward, and the sucessor 0277 * goes backward on the predecessor's segment: 0278 * 0279 * 2 0 1 0280 * x------x<-----x 0281 * (0 is stack-top in point-array) 0282 * 0283 * This will be reduced to 0284 * 1 0 0285 * x------x 0286 * 0287 * Preconditions: 0288 * - No other spikes exist in the whole point-array except at most 0289 * one at the end 0290 * - No two succeeding points are ever equal 0291 * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds 0292 * true 0293 * - No such spike exists where 2 is situated between 0 and 1. 0294 * 0295 * Postcondition: 0296 * - No spikes exist in the whole point-array 0297 * 0298 * If no spike is found, the point-array is left unchanged. 0299 * @return \c true if an actual reduction was done 0300 */ 0301 inline static bool reduceSpike(QVector<QPoint> &pointArray) 0302 { 0303 if (pointArray.size() < 3) { 0304 return false; 0305 } 0306 QVector<QPoint>::Iterator it = pointArray.end(); 0307 QPoint p0 = *--it; 0308 QPoint p1 = *--it; 0309 QPoint p2 = *--it; 0310 0311 bool elide = false; 0312 0313 if ((p0.x() == p1.x() && p1.x() == p2.x() 0314 && ((p1.y() < p0.y() && p0.y() < p2.y()) 0315 || (p2.y() < p0.y() && p0.y() < p1.y()) 0316 || (p1.y() < p2.y() && p2.y() < p0.y()) 0317 || (p0.y() < p2.y() && p2.y() < p1.y()) 0318 || (elide = p2.y() == p0.y() && p0.y() < p1.y()) 0319 || (elide = p1.y() < p0.y() && p0.y() == p2.y()))) 0320 || (p0.y() == p1.y() && p1.y() == p2.y() 0321 && ((p1.x() < p0.x() && p0.x() < p2.x()) 0322 || (p2.x() < p0.x() && p0.x() < p1.x()) 0323 || (p1.x() < p2.x() && p2.x() < p0.x()) 0324 || (p0.x() < p2.x() && p2.x() < p1.x()) 0325 || (elide = p2.x() == p0.x() && p0.x() < p1.x()) 0326 || (elide = p1.x() < p0.x() && p0.x() == p2.x())))) { 0327 // qCDebug(KHTML_LOG) << "spikered p2" << (elide ? " (elide)" : "") << ": " << p2 << " p1: " << p1 << " p0: " << p0; 0328 pointArray.pop_back(); pointArray.pop_back(); 0329 if (!elide) { 0330 pointArray.push_back(p0); 0331 } 0332 return true; 0333 } 0334 return false; 0335 } 0336 0337 /** 0338 * Reduces segment separators. 0339 * 0340 * A segment separator separates a segment into two segments, thus causing 0341 * two adjacent segment with the same orientation. 0342 * 0343 * 2 1 0 0344 * x-------x---->x 0345 * (0 means stack-top) 0346 * 0347 * Here, 1 is a segment separator. As segment separators not only make 0348 * the line drawing algorithm inefficient, but also make the spike-reduction 0349 * fail, they must be eliminated: 0350 * 0351 * 1 0 0352 * x------------>x 0353 * 0354 * Preconditions: 0355 * - No other segment separators exist in the whole point-array except 0356 * at most one at the end 0357 * - No two succeeding points are ever equal 0358 * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds 0359 * true 0360 * - No such spike exists where 2 is situated between 0 and 1. 0361 * 0362 * Postcondition: 0363 * - No segment separators exist in the whole point-array 0364 * 0365 * If no segment separator is found at the end of the point-array, it is 0366 * left unchanged. 0367 * @return \c true if a segment separator was actually reduced. 0368 */ 0369 inline static bool reduceSegmentSeparator(QVector<QPoint> &pointArray) 0370 { 0371 if (pointArray.size() < 3) { 0372 return false; 0373 } 0374 QVector<QPoint>::Iterator it = pointArray.end(); 0375 QPoint p0 = *--it; 0376 QPoint p1 = *--it; 0377 QPoint p2 = *--it; 0378 // qCDebug(KHTML_LOG) << "checking p2: " << p2 << " p1: " << p1 << " p0: " << p0; 0379 0380 if ((p0.x() == p1.x() && p1.x() == p2.x() 0381 && ((p2.y() < p1.y() && p1.y() < p0.y()) 0382 || (p0.y() < p1.y() && p1.y() < p2.y()))) 0383 || (p0.y() == p1.y() && p1.y() == p2.y() 0384 && ((p2.x() < p1.x() && p1.x() < p0.x()) 0385 || (p0.x() < p1.x() && p1.x() < p2.x())))) { 0386 // qCDebug(KHTML_LOG) << "segred p2: " << p2 << " p1: " << p1 << " p0: " << p0; 0387 pointArray.pop_back(); pointArray.pop_back(); 0388 pointArray.push_back(p0); 0389 return true; 0390 } 0391 return false; 0392 } 0393 0394 /** 0395 * Appends the given point to the point-array, doing necessary reductions to 0396 * produce a path without spikes and segment separators. 0397 */ 0398 static void appendPoint(QVector<QPoint> &pointArray, const QPoint &pnt) 0399 { 0400 if (!appendIfNew(pointArray, pnt)) { 0401 return; 0402 } 0403 // qCDebug(KHTML_LOG) << "appendPoint: appended " << pnt; 0404 reduceSegmentSeparator(pointArray) 0405 || reduceSpike(pointArray); 0406 } 0407 0408 /** 0409 * Traverses the horizontal inline boxes and appends the point coordinates to 0410 * the given array. 0411 * @param box inline box 0412 * @param pointArray array collecting coordinates 0413 * @param bottom \c true, collect bottom coordinates, \c false, collect top 0414 * coordinates. 0415 * @param limit lower limit that an y-coordinate must at least reach. Note 0416 * that limit designates the highest y-coordinate for \c bottom, and 0417 * the lowest for !\c bottom. 0418 */ 0419 static void collectHorizontalBoxCoordinates(InlineBox *box, 0420 QVector<QPoint> &pointArray, 0421 bool bottom, int offset, int limit = -500000) 0422 { 0423 // qCDebug(KHTML_LOG) << "collectHorizontalBoxCoordinates: "; 0424 offset = bottom ? offset : -offset; 0425 int y = box->yPos() + bottom * box->height() + offset; 0426 if (limit != -500000 && (bottom ? y < limit : y > limit)) { 0427 y = limit; 0428 } 0429 int x = box->xPos() + bottom * box->width() + offset; 0430 QPoint newPnt(x, y); 0431 // Add intersection point if point-array not empty. 0432 if (!pointArray.isEmpty()) { 0433 QPoint lastPnt = pointArray.back(); 0434 QPoint insPnt(newPnt.x(), lastPnt.y()); 0435 0436 if (offset && ((bottom && lastPnt.y() > y) || (!bottom && lastPnt.y() < y))) { 0437 insPnt.rx() = lastPnt.x(); 0438 insPnt.ry() = y; 0439 } 0440 // qCDebug(KHTML_LOG) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt); 0441 appendPoint(pointArray, insPnt); 0442 } 0443 // Insert starting point of box 0444 appendPoint(pointArray, newPnt); 0445 0446 newPnt.rx() += (bottom ? -box->width() : box->width()) - 2 * offset; 0447 0448 if (box->isInlineFlowBox()) { 0449 InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box); 0450 for (InlineBox *b = bottom ? flowBox->lastChild() : flowBox->firstChild(); b; b = bottom ? b->prevOnLine() : b->nextOnLine()) { 0451 // Don't let boxes smaller than this flow box' height influence 0452 // the vertical position of the outline if they have a different 0453 // x-coordinate 0454 int l2; 0455 if (b->xPos() != box->xPos() && b->xPos() + b->width() != box->xPos() + box->width()) { 0456 l2 = y; 0457 } else { 0458 l2 = limit; 0459 } 0460 collectHorizontalBoxCoordinates(b, pointArray, bottom, qAbs(offset), l2); 0461 } 0462 0463 // Add intersection point if flow box contained any children 0464 if (flowBox->firstChild()) { 0465 QPoint lastPnt = pointArray.back(); 0466 QPoint insPnt(lastPnt.x(), newPnt.y()); 0467 // qCDebug(KHTML_LOG) << "right: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt); 0468 appendPoint(pointArray, insPnt); 0469 } 0470 } 0471 0472 // Insert ending point of box 0473 appendPoint(pointArray, newPnt); 0474 0475 // qCDebug(KHTML_LOG) << "collectHorizontalBoxCoordinates: " << "ende"; 0476 } 0477 0478 /** 0479 * Checks whether the given line box' extents and the following line box' 0480 * extents are disjount (i. e. do not share the same x-coordinate range). 0481 * @param line line box 0482 * @param toBegin \c true, compare with preceding line box, \c false, with 0483 * succeeding 0484 * @return \c true if this and the next box are disjoint 0485 */ 0486 inline static bool lineBoxesDisjoint(InlineRunBox *line, int offset, bool toBegin) 0487 { 0488 InlineRunBox *next = toBegin ? line->prevLineBox() : line->nextLineBox(); 0489 return !next || next->xPos() + next->width() + 2 * offset < line->xPos() 0490 || next->xPos() > line->xPos() + line->width() + 2 * offset; 0491 } 0492 0493 /** 0494 * Traverses the vertical outer borders of the given render flow's line 0495 * boxes and appends the point coordinates to the given point array. 0496 * @param line line box to begin traversal 0497 * @param pointArray point array 0498 * @param left \c true, traverse the left vertical coordinates, 0499 * \c false, traverse the right vertical coordinates. 0500 * @param lastline if not 0, returns the pointer to the last line box traversed 0501 */ 0502 static void collectVerticalBoxCoordinates(InlineRunBox *line, 0503 QVector<QPoint> &pointArray, 0504 bool left, int offset, InlineRunBox **lastline = nullptr) 0505 { 0506 InlineRunBox *last = nullptr; 0507 offset = left ? -offset : offset; 0508 for (InlineRunBox *curr = line; curr && !last; curr = left ? curr->prevLineBox() : curr->nextLineBox()) { 0509 InlineBox *root = curr; 0510 0511 bool isLast = lineBoxesDisjoint(curr, qAbs(offset), left); 0512 if (isLast) { 0513 last = curr; 0514 } 0515 0516 if (root != line && !isLast) 0517 while (root->parent()) { 0518 root = root->parent(); 0519 } 0520 QPoint newPnt(curr->xPos() + !left * curr->width() + offset, 0521 (left ? root->topOverflow() : root->bottomOverflow()) + offset); 0522 if (!pointArray.isEmpty()) { 0523 QPoint lastPnt = pointArray.back(); 0524 if (newPnt.x() > lastPnt.x() && !left) { 0525 pointArray.back().setY(qMin(lastPnt.y(), root->topOverflow() - offset)); 0526 } else if (newPnt.x() < lastPnt.x() && left) { 0527 pointArray.back().setY(qMax(lastPnt.y(), root->bottomOverflow() + offset)); 0528 } 0529 QPoint insPnt(newPnt.x(), pointArray.back().y()); 0530 // qCDebug(KHTML_LOG) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt); 0531 appendPoint(pointArray, insPnt); 0532 } 0533 appendPoint(pointArray, newPnt); 0534 } 0535 if (lastline) { 0536 *lastline = last; 0537 } 0538 } 0539 0540 /** 0541 * Links up the end of the given point-array such that the starting point 0542 * is not a segment separator. 0543 * 0544 * To achieve this, improper points are removed from the beginning of 0545 * the point-array (by changing the array's starting iterator), and 0546 * proper ones appended to the point-array's back. 0547 * 0548 * X---------------+ X------------------+ 0549 * ^ | ^ | 0550 * | | ==> | | 0551 * +..... ...+ +..... ...+ 0552 * 0553 * +----->X--------+ +----------------->X 0554 * | | ==> | | 0555 * +..... ...+ +..... ...+ 0556 * 0557 * ^X 0558 * || 0559 * +-----++--------+ +----------------->X 0560 * | | ==> | | 0561 * +..... ...+ +..... ...+ 0562 * 0563 * @param pointArray point-array 0564 * @return actual begin of point array 0565 */ 0566 static QPoint *linkEndToBegin(QVector<QPoint> &pointArray) 0567 { 0568 uint index = 0; 0569 // ### BUG: outlines with zero width aren't treated correctly 0570 // this is not the right fix 0571 if (pointArray.size() < 3) { 0572 return pointArray.data(); 0573 } 0574 0575 // if first and last points match, ignore the last one. 0576 bool linkup = false; QPoint linkupPnt; 0577 if (pointArray.front() == pointArray.back()) { 0578 linkupPnt = pointArray.back(); 0579 pointArray.pop_back(); 0580 linkup = true; 0581 } 0582 0583 const QPoint *it = pointArray.data() + index; 0584 QPoint pfirst = *it; 0585 QPoint pnext = *++it; 0586 QPoint plast = pointArray.back(); 0587 // qCDebug(KHTML_LOG) << "linkcheck plast: " << plast << " pfirst: " << pfirst << " pnext: " << pnext; 0588 0589 if ((plast.x() == pfirst.x() && pfirst.x() == pnext.x()) 0590 || (plast.y() == pfirst.y() && pfirst.y() == pnext.y())) { 0591 0592 ++index; 0593 appendPoint(pointArray, pfirst); // ### do we really need this point? 0594 appendPoint(pointArray, pnext); 0595 // ended up at a segment separator? move one point forward 0596 if (plast == pnext) { 0597 ++index; 0598 appendPoint(pointArray, *++it); 0599 } 0600 } else if (linkup) { 0601 pointArray.push_back(linkupPnt); 0602 } 0603 return pointArray.data() + index; 0604 } 0605 0606 // assumes clock-wise orientation 0607 static RenderObject::BorderSide borderSide(const QPoint &first, 0608 const QPoint &second) 0609 { 0610 if (second.x() > first.x()) { 0611 return RenderObject::BSTop; 0612 } else if (second.x() < first.x()) { 0613 return RenderObject::BSBottom; 0614 } else if (second.y() > first.y()) { 0615 return RenderObject::BSRight; 0616 } else { // second.y() < first.y() 0617 return RenderObject::BSLeft; 0618 } 0619 } 0620 0621 void RenderInline::paintOutlines(QPainter *p, int _tx, int _ty) 0622 { 0623 if (style()->outlineWidth() == 0 || style()->outlineStyle() <= BHIDDEN) { 0624 return; 0625 } 0626 int offset = style()->outlineOffset(); 0627 0628 // We may have to draw more than one outline path as they may be 0629 // disjoint. 0630 for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) { 0631 QVector<QPoint> path; 0632 0633 // collect topmost outline 0634 collectHorizontalBoxCoordinates(curr, path, false, offset); 0635 // collect right outline 0636 collectVerticalBoxCoordinates(curr, path, false, offset, &curr); 0637 // collect bottommost outline 0638 collectHorizontalBoxCoordinates(curr, path, true, offset); 0639 // collect left outline 0640 collectVerticalBoxCoordinates(curr, path, true, offset); 0641 0642 if (path.size() < 3) { 0643 continue; 0644 } 0645 0646 const QPoint *begin = linkEndToBegin(path); 0647 0648 // initial borderside and direction values 0649 QPoint pstart = *begin; 0650 QPoint pprev = *(path.end() - 2); 0651 RenderObject::BorderSide bs = borderSide(pprev, pstart); 0652 QPoint diff = pstart - pprev; 0653 int direction = diff.x() + diff.y(); 0654 RenderObject::BorderSide endingBS = borderSide(*begin, *(begin + 1)); 0655 0656 // paint the outline 0657 paintOutlinePath(p, _tx, _ty, begin, path.data() + path.size(), 0658 bs, direction, endingBS); 0659 } 0660 } 0661 0662 template<class T> inline void kSwap(T &a1, T &a2) 0663 { 0664 T tmp = a2; 0665 a2 = a1; 0666 a1 = tmp; 0667 } 0668 0669 enum BSOrientation { BSHorizontal, BSVertical }; 0670 0671 /** 0672 * Returns the orientation of the given border side. 0673 */ 0674 inline BSOrientation bsOrientation(RenderObject::BorderSide bs) 0675 { 0676 switch (bs) { 0677 case RenderObject::BSTop: 0678 case RenderObject::BSBottom: 0679 return BSHorizontal; 0680 case RenderObject::BSLeft: 0681 case RenderObject::BSRight: 0682 return BSVertical; 0683 } 0684 return BSHorizontal; // make gcc happy (sigh) 0685 } 0686 0687 /** 0688 * Determines the new border side by evaluating the new direction as determined 0689 * by the given coordinates, the old border side, and the relative direction. 0690 * 0691 * The relative direction specifies whether the old border side meets with the 0692 * straight given by the coordinates from below/right (negative), or 0693 * above/left (positive). 0694 */ 0695 inline RenderObject::BorderSide newBorderSide(RenderObject::BorderSide oldBS, int direction, const QPoint &last, const QPoint &cur) 0696 { 0697 bool below = direction < 0; 0698 if (last.x() == cur.x()) { // new segment is vertical 0699 bool t = oldBS == RenderObject::BSTop; 0700 bool b = oldBS == RenderObject::BSBottom; 0701 if ((t || b) && last.y() != cur.y()) 0702 return (cur.y() < last.y()) ^ ((t && below) || (b && !below)) 0703 ? RenderObject::BSLeft : RenderObject::BSRight; 0704 } else { /*if (last.y() == cur.y())*/ // new segment is horizontal 0705 bool l = oldBS == RenderObject::BSLeft; 0706 bool r = oldBS == RenderObject::BSRight; 0707 if ((l || r) && last.x() != cur.x()) 0708 return (cur.x() < last.x()) ^ ((l && below) || (r && !below)) 0709 ? RenderObject::BSTop : RenderObject::BSBottom; 0710 } 0711 return oldBS; // same direction 0712 } 0713 0714 /** 0715 * Draws an outline segment between the given two points. 0716 * @param o render object 0717 * @param p painter 0718 * @param tx absolute x-coordinate of containing block 0719 * @param ty absolute y-coordinate of containing block 0720 * @param p1 starting point 0721 * @param p2 end point 0722 * @param prevBS border side of previous segment 0723 * @param curBS border side of this segment 0724 * @param nextBS border side of next segment 0725 */ 0726 static void paintOutlineSegment(RenderObject *o, QPainter *p, int tx, int ty, 0727 const QPoint &p1, const QPoint &p2, 0728 RenderObject::BorderSide prevBS, 0729 RenderObject::BorderSide curBS, 0730 RenderObject::BorderSide nextBS) 0731 { 0732 int ow = o->style()->outlineWidth(); 0733 EBorderStyle os = o->style()->outlineStyle(); 0734 QColor oc = o->style()->outlineColor(); 0735 0736 int x1 = tx + p1.x(); 0737 int y1 = ty + p1.y(); 0738 int x2 = tx + p2.x(); 0739 int y2 = ty + p2.y(); 0740 if (x1 > x2) { 0741 kSwap(x1, x2); 0742 if (bsOrientation(curBS) == BSHorizontal) { 0743 kSwap(prevBS, nextBS); 0744 } 0745 } 0746 if (y1 > y2) { 0747 kSwap(y1, y2); 0748 if (bsOrientation(curBS) == BSVertical) { 0749 kSwap(prevBS, nextBS); 0750 } 0751 } 0752 0753 // qCDebug(KHTML_LOG) << "segment(" << x1 << "," << y1 << ") - (" << x2 << "," << y2 << ")"; 0754 /* p->setPen(Qt::gray); 0755 p->drawLine(x1,y1,x2,y2);*/ 0756 switch (curBS) { 0757 case RenderObject::BSLeft: 0758 case RenderObject::BSRight: 0759 /* p->setPen(QColor("#ffe4dd")); 0760 p->drawLine( 0761 x1 - (curBS == RenderObject::BSLeft ? ow : 0), 0762 y1 - (prevBS == RenderObject::BSTop ? ow : 0), 0763 x2 + (curBS == RenderObject::BSRight ? ow : 0), 0764 y2 + (nextBS == RenderObject::BSBottom ? ow : 0) 0765 );*/ 0766 o->drawBorder(p, 0767 x1 - (curBS == RenderObject::BSLeft ? ow : 0), 0768 y1 - (prevBS == RenderObject::BSTop ? ow : 0), 0769 x2 + (curBS == RenderObject::BSRight ? ow : 0), 0770 y2 + (nextBS == RenderObject::BSBottom ? ow : 0), 0771 curBS, oc, o->style()->color(), os, 0772 prevBS == RenderObject::BSTop ? ow 0773 : prevBS == RenderObject::BSBottom ? -ow : 0, 0774 nextBS == RenderObject::BSTop ? -ow 0775 : nextBS == RenderObject::BSBottom ? ow : 0, 0776 true); 0777 break; 0778 case RenderObject::BSBottom: 0779 case RenderObject::BSTop: 0780 // qCDebug(KHTML_LOG) << "BSTop/BSBottom: prevBS " << prevBS << " curBS " << curBS << " nextBS " << nextBS; 0781 o->drawBorder(p, 0782 x1 - (prevBS == RenderObject::BSLeft ? ow : 0), 0783 y1 - (curBS == RenderObject::BSTop ? ow : 0), 0784 x2 + (nextBS == RenderObject::BSRight ? ow : 0), 0785 y2 + (curBS == RenderObject::BSBottom ? ow : 0), 0786 curBS, oc, o->style()->color(), os, 0787 prevBS == RenderObject::BSLeft ? ow 0788 : prevBS == RenderObject::BSRight ? -ow : 0, 0789 nextBS == RenderObject::BSLeft ? -ow 0790 : nextBS == RenderObject::BSRight ? ow : 0, 0791 true); 0792 break; 0793 } 0794 } 0795 0796 void RenderInline::paintOutlinePath(QPainter *p, int tx, int ty, const QPoint *begin, const QPoint *end, BorderSide bs, int direction, BorderSide endingBS) 0797 { 0798 int ow = style()->outlineWidth(); 0799 if (ow == 0 || m_isContinuation) { // Continuations get painted by the original inline. 0800 return; 0801 } 0802 0803 QPoint last = *begin; 0804 BorderSide lastBS = bs; 0805 Q_ASSERT(begin != end); 0806 ++begin; 0807 0808 // qCDebug(KHTML_LOG) << "last: " << last; 0809 0810 bs = newBorderSide(bs, direction, last, *begin); 0811 // qCDebug(KHTML_LOG) << "newBorderSide: " << lastBS << " " << direction << "d " << last << " - " << *begin << " => " << bs; 0812 0813 for (const QPoint *it = begin; it != end; ++it) { 0814 QPoint cur = *it; 0815 // qCDebug(KHTML_LOG) << "cur: " << cur; 0816 BorderSide nextBS; 0817 if (it + 1 != end) { 0818 QPoint diff = cur - last; 0819 direction = diff.x() + diff.y(); 0820 nextBS = newBorderSide(bs, direction, cur, *(it + 1)); 0821 // qCDebug(KHTML_LOG) << "newBorderSide*: " << bs << " " << direction << "d " << cur << " - " << *(it + 1) << " => " << nextBS; 0822 } else { 0823 nextBS = endingBS; 0824 } 0825 0826 Q_ASSERT(bsOrientation(bs) != bsOrientation(nextBS)); 0827 paintOutlineSegment(this, p, tx, ty, last, cur, 0828 lastBS, bs, nextBS); 0829 lastBS = bs; 0830 last = cur; 0831 bs = nextBS; 0832 } 0833 0834 } 0835 0836 void RenderInline::calcMinMaxWidth() 0837 { 0838 KHTMLAssert(!minMaxKnown()); 0839 0840 #ifdef DEBUG_LAYOUT 0841 // qCDebug(KHTML_LOG) << renderName() << "(RenderInline)::calcMinMaxWidth() this=" << this; 0842 #endif 0843 0844 // Irrelevant, since some enclosing block will actually measure us and our children. 0845 m_minWidth = 0; 0846 m_maxWidth = 0; 0847 0848 setMinMaxKnown(); 0849 } 0850 0851 short RenderInline::width() const 0852 { 0853 // Return the width of the minimal left side and the maximal right side. 0854 short leftSide = 0; 0855 short rightSide = 0; 0856 for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) { 0857 if (curr == firstLineBox() || curr->xPos() < leftSide) { 0858 leftSide = curr->xPos(); 0859 } 0860 if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide) { 0861 rightSide = curr->xPos() + curr->width(); 0862 } 0863 } 0864 0865 return rightSide - leftSide; 0866 } 0867 0868 int RenderInline::height() const 0869 { 0870 int h = 0; 0871 if (firstLineBox()) { 0872 h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos(); 0873 } 0874 return h; 0875 } 0876 0877 int RenderInline::offsetLeft() const 0878 { 0879 int x = RenderFlow::offsetLeft(); 0880 if (firstLineBox()) { 0881 x += firstLineBox()->xPos(); 0882 } 0883 return x; 0884 } 0885 0886 int RenderInline::offsetTop() const 0887 { 0888 int y = RenderFlow::offsetTop(); 0889 if (firstLineBox()) { 0890 y += firstLineBox()->yPos(); 0891 } 0892 return y; 0893 } 0894 0895 const char *RenderInline::renderName() const 0896 { 0897 if (isRelPositioned()) { 0898 return "RenderInline (relative positioned)"; 0899 } 0900 if (isAnonymous()) { 0901 return "RenderInline (anonymous)"; 0902 } 0903 return "RenderInline"; 0904 } 0905 0906 bool RenderInline::nodeAtPoint(NodeInfo &info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside) 0907 { 0908 0909 // Check our line boxes if we're still not inside. 0910 if (!inside) { 0911 // See if we're inside one of our line boxes. 0912 inside = hitTestLines(info, _x, _y, _tx, _ty, hitTestAction); 0913 } 0914 0915 if (inside && element() && style()->visibility() != HIDDEN) { 0916 if (info.innerNode() && info.innerNode()->renderer() && 0917 !info.innerNode()->renderer()->isInline()) { 0918 // Within the same layer, inlines are ALWAYS fully above blocks. Change inner node. 0919 info.setInnerNode(element()); 0920 0921 // Clear everything else. 0922 info.setInnerNonSharedNode(nullptr); 0923 info.setURLElement(nullptr); 0924 } 0925 0926 if (!info.innerNode()) { 0927 info.setInnerNode(element()); 0928 } 0929 0930 if (!info.innerNonSharedNode()) { 0931 info.setInnerNonSharedNode(element()); 0932 } 0933 } 0934 0935 return inside; 0936 } 0937 0938 RenderPosition RenderInline::positionForCoordinates(int x, int y) 0939 { 0940 for (RenderObject *c = continuation(); c; c = c->continuation()) { 0941 if (c->isInline() || c->firstChild()) { 0942 return c->positionForCoordinates(x, y); 0943 } 0944 } 0945 0946 return RenderFlow::positionForCoordinates(x, y); 0947 } 0948 0949 void RenderInline::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height) const 0950 { 0951 _x = -1; 0952 0953 RenderBlock *cb = containingBlock(); 0954 bool rtl = cb->style()->direction() == RTL; 0955 bool outsideEnd = flags & CFOutsideEnd; 0956 // I need to explain that: outsideEnd contains a meaningful value if 0957 // and only if flags & CFOutside is set. If it is not, then randomly 0958 // either the first or the last line box is returned. 0959 // This doesn't matter because the only case this can happen is on an 0960 // empty inline element, whose first and last line boxes are actually 0961 // the same. 0962 InlineFlowBox *line = !outsideEnd ^ rtl ? firstLineBox() : lastLineBox(); 0963 0964 if (!line) { // umpf, handle "gracefully" 0965 RenderFlow::caretPos(offset, flags, _x, _y, width, height); 0966 return; 0967 } 0968 0969 _x = line->xPos(); 0970 width = 1; // ### regard CFOverride 0971 0972 // Place caret outside the border 0973 if (flags & CFOutside) { 0974 RenderStyle *s = element() && element()->parent() 0975 && element()->parent()->renderer() 0976 ? element()->parent()->renderer()->style() 0977 : style(); 0978 const QFontMetrics &fm = s->fontMetrics(); 0979 _y = line->yPos() + line->baseline() - fm.ascent(); 0980 height = fm.height(); 0981 0982 if (!outsideEnd ^ rtl) { 0983 _x -= line->marginBorderPaddingLeft(); 0984 } else { 0985 _x += line->width() + line->marginBorderPaddingRight(); 0986 } 0987 0988 } else { 0989 const QFontMetrics &fm = style()->fontMetrics(); 0990 _y = line->yPos() + line->baseline() - fm.ascent(); 0991 height = fm.height(); 0992 } 0993 0994 int absx, absy; 0995 if (cb && cb->absolutePosition(absx, absy)) { 0996 //qCDebug(KHTML_LOG) << "absx=" << absx << " absy=" << absy; 0997 _x += absx; 0998 _y += absy; 0999 } else { 1000 // we don't know our absolute position, and there is no point returning 1001 // just a relative one 1002 _x = _y = -1; 1003 } 1004 } 1005 1006 inline int minXPos(const RenderInline *o) 1007 { 1008 int retval = 6666666; 1009 if (!o->firstLineBox()) { 1010 return 0; 1011 } 1012 for (InlineRunBox *curr = o->firstLineBox(); curr; curr = curr->nextLineBox()) { 1013 retval = qMin(retval, int(curr->m_x)); 1014 } 1015 return retval; 1016 } 1017 1018 int RenderInline::inlineXPos() const 1019 { 1020 return minXPos(this); 1021 } 1022 1023 int RenderInline::inlineYPos() const 1024 { 1025 return firstLineBox() ? firstLineBox()->yPos() : 0; 1026 } 1027