File indexing completed on 2024-04-28 11:38:48
0001 /** 0002 * This file is part of the html renderer for KDE. 0003 * 0004 * Copyright (C) 2003-2007 Apple Computer, Inc. 0005 * (C) 2006-2007 Germain Garand (germain@ebooksfrance.org) 0006 * 0007 * This library is free software; you can redistribute it and/or 0008 * modify it under the terms of the GNU Library General Public 0009 * License as published by the Free Software Foundation; either 0010 * version 2 of the License, or (at your option) any later version. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Library General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Library General Public License 0018 * along with this library; see the file COPYING.LIB. If not, write to 0019 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 * Boston, MA 02110-1301, USA. 0021 */ 0022 // ------------------------------------------------------------------------- 0023 0024 #include "render_line.h" 0025 0026 #include "khtml_debug.h" 0027 #include <assert.h> 0028 #include <QPainter> 0029 0030 #include "render_flow.h" 0031 #include "render_text.h" 0032 #include "render_table.h" 0033 #include "render_inline.h" 0034 #include "render_block.h" 0035 #include "render_arena.h" 0036 #include <xml/dom_nodeimpl.h> 0037 #include <xml/dom_docimpl.h> 0038 #include <html/html_formimpl.h> 0039 #include <khtmlview.h> 0040 0041 using namespace DOM; 0042 using namespace khtml; 0043 0044 #ifndef NDEBUG 0045 static bool inInlineBoxDetach; 0046 #endif 0047 0048 class khtml::EllipsisBox : public InlineBox 0049 { 0050 public: 0051 EllipsisBox(RenderObject *obj, const DOM::DOMString &ellipsisStr, InlineFlowBox *p, 0052 int w, int y, int h, int b, bool firstLine, InlineBox *markupBox) 0053 : InlineBox(obj), m_str(ellipsisStr) 0054 { 0055 m_parent = p; 0056 m_width = w; 0057 m_y = y; 0058 m_height = h; 0059 m_baseline = b; 0060 m_firstLine = firstLine; 0061 m_constructed = true; 0062 m_markupBox = markupBox; 0063 } 0064 0065 void paint(RenderObject::PaintInfo &i, int _tx, int _ty) override; 0066 bool nodeAtPoint(RenderObject::NodeInfo &info, int _x, int _y, int _tx, int _ty) override; 0067 0068 private: 0069 DOM::DOMString m_str; 0070 InlineBox *m_markupBox; 0071 }; 0072 0073 void InlineBox::remove() 0074 { 0075 if (m_parent) { 0076 m_parent->removeFromLine(this); 0077 } 0078 } 0079 0080 void InlineBox::detach(RenderArena *renderArena, bool noRemove) 0081 { 0082 if (!noRemove) { 0083 remove(); 0084 } 0085 0086 #ifndef NDEBUG 0087 inInlineBoxDetach = true; 0088 #endif 0089 delete this; 0090 #ifndef NDEBUG 0091 inInlineBoxDetach = false; 0092 #endif 0093 0094 // Recover the size left there for us by operator delete and free the memory. 0095 renderArena->free(*(size_t *)this, this); 0096 } 0097 0098 void *InlineBox::operator new(size_t sz, RenderArena *renderArena) throw() 0099 { 0100 return renderArena->allocate(sz); 0101 } 0102 0103 void InlineBox::operator delete(void *ptr, size_t sz) 0104 { 0105 assert(inInlineBoxDetach); 0106 #ifdef KHTML_USE_ARENA_ALLOCATOR 0107 // Stash size where detach can find it. 0108 *(size_t *)ptr = sz; 0109 #endif 0110 } 0111 0112 static bool needsOutlinePhaseRepaint(RenderObject *o, RenderObject::PaintInfo &i, int tx, int ty) 0113 { 0114 if (o->style()->outlineWidth() <= 0) { 0115 return false; 0116 } 0117 QRect r(tx + o->xPos(), ty + o->yPos(), o->width(), o->height()); 0118 if (r.intersects(i.r)) { 0119 return false; 0120 } 0121 r.adjust(-o->style()->outlineSize(), 0122 -o->style()->outlineSize(), 0123 o->style()->outlineSize(), 0124 o->style()->outlineSize()); 0125 if (!r.intersects(i.r)) { 0126 return false; 0127 } 0128 return true; 0129 } 0130 0131 void InlineBox::paint(RenderObject::PaintInfo &i, int tx, int ty) 0132 { 0133 if (i.phase == PaintActionOutline && !needsOutlinePhaseRepaint(object(), i, tx, ty)) { 0134 return; 0135 } 0136 0137 // Paint all phases of replaced elements atomically, as though the replaced element established its 0138 // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 0139 // specification.) 0140 bool paintSelectionOnly = i.phase == PaintActionSelection; 0141 RenderObject::PaintInfo info(i.p, i.r, paintSelectionOnly ? i.phase : PaintActionElementBackground); 0142 object()->paint(info, tx, ty); 0143 if (!paintSelectionOnly) { 0144 info.phase = PaintActionChildBackgrounds; 0145 object()->paint(info, tx, ty); 0146 info.phase = PaintActionFloat; 0147 object()->paint(info, tx, ty); 0148 info.phase = PaintActionForeground; 0149 object()->paint(info, tx, ty); 0150 info.phase = PaintActionOutline; 0151 object()->paint(info, tx, ty); 0152 } 0153 } 0154 0155 bool InlineBox::nodeAtPoint(RenderObject::NodeInfo &i, int x, int y, int tx, int ty) 0156 { 0157 // Hit test all phases of replaced elements atomically, as though the replaced element established its 0158 // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 0159 // specification.) 0160 bool inside = false; 0161 return object()->nodeAtPoint(i, x, y, tx, ty, HitTestAll, inside); // ### port hitTest 0162 } 0163 0164 long InlineBox::caretMinOffset() const 0165 { 0166 return 0; 0167 } 0168 0169 long InlineBox::caretMaxOffset() const 0170 { 0171 return 1; 0172 } 0173 0174 unsigned long InlineBox::caretMaxRenderedOffset() const 0175 { 0176 return 1; 0177 } 0178 0179 RootInlineBox *InlineBox::root() 0180 { 0181 if (m_parent) { 0182 return m_parent->root(); 0183 } 0184 assert(isRootInlineBox()); 0185 return static_cast<RootInlineBox *>(this); 0186 } 0187 0188 InlineFlowBox::~InlineFlowBox() 0189 { 0190 } 0191 0192 void InlineFlowBox::extractLine() 0193 { 0194 if (!m_extracted) { 0195 static_cast<RenderFlow *>(m_object)->extractLineBox(this); 0196 } 0197 for (InlineBox *child = firstChild(); child; child = child->nextOnLine()) { 0198 child->extractLine(); 0199 } 0200 } 0201 0202 void InlineFlowBox::attachLine() 0203 { 0204 if (m_extracted) { 0205 static_cast<RenderFlow *>(m_object)->attachLineBox(this); 0206 } 0207 for (InlineBox *child = firstChild(); child; child = child->nextOnLine()) { 0208 child->attachLine(); 0209 } 0210 } 0211 0212 void InlineFlowBox::deleteLine(RenderArena *arena) 0213 { 0214 InlineBox *child = firstChild(); 0215 InlineBox *next = nullptr; 0216 while (child) { 0217 assert(this == child->parent()); 0218 next = child->nextOnLine(); 0219 #ifndef NDEBUG 0220 child->setParent(nullptr); 0221 #endif 0222 child->deleteLine(arena); 0223 child = next; 0224 } 0225 #ifndef NDEBUG 0226 m_firstChild = nullptr; 0227 m_lastChild = nullptr; 0228 #endif 0229 0230 m_object->removeInlineBox(this); 0231 detach(arena, true /*no remove*/); 0232 } 0233 0234 void InlineFlowBox::removeFromLine(InlineBox *child) 0235 { 0236 if (!m_dirty) { 0237 dirtyInlineBoxes(); 0238 } 0239 0240 root()->childRemoved(child); 0241 0242 if (child == m_firstChild) { 0243 m_firstChild = child->nextOnLine(); 0244 } 0245 if (child == m_lastChild) { 0246 m_lastChild = child->prevOnLine(); 0247 } 0248 if (child->nextOnLine()) { 0249 child->nextOnLine()->m_prev = child->prevOnLine(); 0250 } 0251 if (child->prevOnLine()) { 0252 child->prevOnLine()->m_next = child->nextOnLine(); 0253 } 0254 0255 child->setParent(nullptr); 0256 } 0257 0258 void InlineBox::dirtyInlineBoxes() 0259 { 0260 markDirty(); 0261 for (InlineFlowBox *curr = parent(); curr && !curr->isDirty(); curr = curr->parent()) { 0262 curr->markDirty(); 0263 } 0264 } 0265 0266 void InlineBox::deleteLine(RenderArena *arena) 0267 { 0268 if (!m_extracted && m_object->isBox()) { 0269 static_cast<RenderBox *>(m_object)->setPlaceHolderBox(nullptr); 0270 } 0271 detach(arena, true /*no remove*/); 0272 } 0273 0274 void InlineBox::extractLine() 0275 { 0276 m_extracted = true; 0277 if (m_object->isBox()) { 0278 static_cast<RenderBox *>(m_object)->setPlaceHolderBox(nullptr); 0279 } 0280 } 0281 0282 void InlineBox::attachLine() 0283 { 0284 m_extracted = false; 0285 if (m_object->isBox()) { 0286 static_cast<RenderBox *>(m_object)->setPlaceHolderBox(this); 0287 } 0288 } 0289 0290 void InlineBox::adjustPosition(int dx, int dy) 0291 { 0292 m_x += dx; 0293 m_y += dy; 0294 if (m_object->isReplaced() || m_object->isBR()) { 0295 m_object->setPos(m_object->xPos() + dx, m_object->yPos() + dy); 0296 } 0297 } 0298 0299 bool InlineBox::canAccommodateEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth) 0300 { 0301 // Non-replaced elements can always accommodate an ellipsis. 0302 if (!m_object || !m_object->isReplaced()) { 0303 return true; 0304 } 0305 0306 QRect boxRect(m_x, 0, m_width, 10); 0307 QRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10); 0308 return !(boxRect.intersects(ellipsisRect)); 0309 } 0310 0311 int InlineBox::placeEllipsisBox(bool /*ltr*/, int /*blockEdge*/, int /*ellipsisWidth*/, bool &) 0312 { 0313 // Use -1 to mean "we didn't set the position." 0314 return -1; 0315 } 0316 0317 bool InlineBox::nextOnLineExists() const 0318 { 0319 if (!parent()) { 0320 return false; 0321 } 0322 0323 if (nextOnLine()) { 0324 return true; 0325 } 0326 0327 return parent()->nextOnLineExists(); 0328 } 0329 0330 bool InlineBox::prevOnLineExists() const 0331 { 0332 if (!parent()) { 0333 return false; 0334 } 0335 0336 if (prevOnLine()) { 0337 return true; 0338 } 0339 0340 return parent()->prevOnLineExists(); 0341 } 0342 0343 InlineBox *InlineBox::firstLeafChild() 0344 { 0345 return this; 0346 } 0347 0348 InlineBox *InlineBox::lastLeafChild() 0349 { 0350 return this; 0351 } 0352 0353 InlineBox *InlineBox::closestLeafChildForXPos(int _x, int _tx) 0354 { 0355 if (!isInlineFlowBox()) { 0356 return this; 0357 } 0358 0359 InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(this); 0360 if (!flowBox->firstChild()) { 0361 return this; 0362 } 0363 0364 InlineBox *box = flowBox->closestChildForXPos(_x, _tx); 0365 if (!box) { 0366 return this; 0367 } 0368 0369 return box->closestLeafChildForXPos(_x, _tx); 0370 } 0371 0372 #ifdef ENABLE_DUMP 0373 static QString getInlineBoxType(const InlineBox *box) 0374 { 0375 if (box->isInlineTextBox()) { 0376 return "Text"; 0377 } 0378 if (box->isRootInlineBox()) { 0379 return "RootBox"; 0380 } 0381 if (box->isInlineFlowBox()) { 0382 return "FlowBox"; 0383 } 0384 if (box->isPlaceHolderBox()) { 0385 return "PlaceHolderBox"; 0386 } 0387 return "InlineBox"; 0388 } 0389 0390 QString InlineBox::information() const 0391 { 0392 QString result; 0393 QTextStream out(&result, QIODevice::WriteOnly); 0394 out << getInlineBoxType(this) << "(" << (void *)this << ") " 0395 << "Pos" << "(" << xPos() << "," << yPos() << ") " 0396 << "Size" << "(" << width() << "," << height() << ") " 0397 << "Overflow" << "(" << topOverflow() << "," << bottomOverflow() << ") " 0398 << (object() ? object()->renderName() : "NoRenderObject") << "(" << (void *)object() << ") "; 0399 if (isInlineTextBox()) { 0400 const InlineTextBox *textBox = static_cast<const InlineTextBox *>(this); 0401 out << "Text[" << textBox->renderText()->data().substring(textBox->start(), textBox->len()).string() << "]"; 0402 } 0403 return result; 0404 } 0405 0406 void InlineBox::printTree(int indent) const 0407 { 0408 QString temp; 0409 temp.fill(' ', indent); 0410 0411 // qCDebug(KHTML_LOG) << (temp + information()); 0412 if (isRootInlineBox()) { 0413 // const RootInlineBox* root = static_cast<const RootInlineBox*>(this); 0414 } 0415 if (isInlineTextBox()) { 0416 // 0417 } 0418 if (isInlineFlowBox()) { 0419 const InlineFlowBox *flowBox = static_cast<const InlineFlowBox *>(this); 0420 for (InlineBox *box = flowBox->firstChild(); box; box = box->nextOnLine()) { 0421 box->printTree(indent + 2); 0422 } 0423 } 0424 } 0425 #endif 0426 0427 int InlineFlowBox::marginLeft() const 0428 { 0429 if (!includeLeftEdge()) { 0430 return 0; 0431 } 0432 0433 RenderStyle *cstyle = object()->style(); 0434 Length margin = cstyle->marginLeft(); 0435 if (!margin.isAuto()) { 0436 return (margin.isFixed() ? margin.value() : object()->marginLeft()); 0437 } 0438 return 0; 0439 } 0440 0441 int InlineFlowBox::marginRight() const 0442 { 0443 if (!includeRightEdge()) { 0444 return 0; 0445 } 0446 0447 RenderStyle *cstyle = object()->style(); 0448 Length margin = cstyle->marginRight(); 0449 if (!margin.isAuto()) { 0450 return (margin.isFixed() ? margin.value() : object()->marginRight()); 0451 } 0452 return 0; 0453 } 0454 0455 int InlineFlowBox::marginBorderPaddingLeft() const 0456 { 0457 return marginLeft() + borderLeft() + paddingLeft(); 0458 } 0459 0460 int InlineFlowBox::marginBorderPaddingRight() const 0461 { 0462 return marginRight() + borderRight() + paddingRight(); 0463 } 0464 0465 int InlineFlowBox::getFlowSpacingWidth() const 0466 { 0467 int totWidth = marginBorderPaddingLeft() + marginBorderPaddingRight(); 0468 for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) { 0469 if (curr->isInlineFlowBox()) { 0470 totWidth += static_cast<InlineFlowBox *>(curr)->getFlowSpacingWidth(); 0471 } 0472 } 0473 return totWidth; 0474 } 0475 0476 bool InlineFlowBox::nextOnLineExists() 0477 { 0478 if (!parent()) { 0479 return false; 0480 } 0481 0482 if (nextOnLine()) { 0483 return true; 0484 } 0485 0486 return parent()->nextOnLineExists(); 0487 } 0488 0489 bool InlineFlowBox::prevOnLineExists() 0490 { 0491 if (!parent()) { 0492 return false; 0493 } 0494 0495 if (prevOnLine()) { 0496 return true; 0497 } 0498 0499 return parent()->prevOnLineExists(); 0500 } 0501 0502 bool InlineFlowBox::onEndChain(RenderObject *endObject) 0503 { 0504 if (!endObject) { 0505 return false; 0506 } 0507 0508 if (endObject == object()) { 0509 return true; 0510 } 0511 0512 RenderObject *curr = endObject; 0513 RenderObject *parent = curr->parent(); 0514 while (parent && !parent->isRenderBlock()) { 0515 if (parent->lastChild() != curr || parent == object()) { 0516 return false; 0517 } 0518 0519 curr = parent; 0520 parent = curr->parent(); 0521 } 0522 0523 return true; 0524 } 0525 0526 void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject *endObject) 0527 { 0528 // All boxes start off open. They will not apply any margins/border/padding on 0529 // any side. 0530 bool includeLeftEdge = false; 0531 bool includeRightEdge = false; 0532 0533 RenderFlow *flow = static_cast<RenderFlow *>(object()); 0534 0535 // The root inline box never has borders/margins/padding. 0536 if (parent()) { 0537 bool ltr = flow->style()->direction() == LTR; 0538 0539 // Check to see if all initial lines are unconstructed. If so, then 0540 // we know the inline began on this line. 0541 if (!flow->firstLineBox()->isConstructed() && !object()->isInlineContinuation()) { 0542 if (ltr && flow->firstLineBox() == this) { 0543 includeLeftEdge = true; 0544 } else if (!ltr && flow->lastLineBox() == this) { 0545 includeRightEdge = true; 0546 } 0547 } 0548 0549 // In order to determine if the inline ends on this line, we check three things: 0550 // (1) If we are the last line and we don't have a continuation(), then we can 0551 // close up. 0552 // (2) If the last line box for the flow has an object following it on the line (ltr, 0553 // reverse for rtl), then the inline has closed. 0554 // (3) The line may end on the inline. If we are the last child (climbing up 0555 // the end object's chain), then we just closed as well. 0556 if (!flow->lastLineBox()->isConstructed()) { 0557 if (ltr) { 0558 if (!nextLineBox() && 0559 ((lastLine && !object()->continuation()) || nextOnLineExists() 0560 || onEndChain(endObject))) { 0561 includeRightEdge = true; 0562 } 0563 } else { 0564 if ((!prevLineBox() || prevLineBox()->isConstructed()) && 0565 ((lastLine && !object()->continuation()) || 0566 prevOnLineExists() || onEndChain(endObject))) { 0567 includeLeftEdge = true; 0568 } 0569 } 0570 0571 } 0572 } 0573 0574 setEdges(includeLeftEdge, includeRightEdge); 0575 0576 // Recur into our children. 0577 for (InlineBox *currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) { 0578 if (currChild->isInlineFlowBox()) { 0579 InlineFlowBox *currFlow = static_cast<InlineFlowBox *>(currChild); 0580 currFlow->determineSpacingForFlowBoxes(lastLine, endObject); 0581 } 0582 } 0583 } 0584 0585 int InlineFlowBox::placeBoxesHorizontally(int x) 0586 { 0587 // Set our x position. 0588 setXPos(x); 0589 0590 int startX = x; 0591 x += borderLeft() + paddingLeft(); 0592 0593 for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) { 0594 if (curr->object()->isText()) { 0595 InlineTextBox *text = static_cast<InlineTextBox *>(curr); 0596 text->setXPos(x); 0597 x += curr->width(); 0598 } else { 0599 if (curr->object()->isPositioned()) { 0600 if (curr->object()->parent()->style()->direction() == LTR) { 0601 curr->setXPos(x); 0602 } else { 0603 // Our offset that we cache needs to be from the edge of the right border box and 0604 // not the left border box. We have to subtract |x| from the width of the block 0605 // (which can be obtained by walking up to the root line box). 0606 InlineBox *root = this; 0607 while (!root->isRootInlineBox()) { 0608 root = root->parent(); 0609 } 0610 curr->setXPos(root->object()->width() - x); 0611 } 0612 continue; // The positioned object has no effect on the width. 0613 } 0614 if (curr->object()->isInlineFlow()) { 0615 InlineFlowBox *flow = static_cast<InlineFlowBox *>(curr); 0616 x += flow->marginLeft(); 0617 x = flow->placeBoxesHorizontally(x); 0618 x += flow->marginRight(); 0619 } else { 0620 x += curr->object()->marginLeft(); 0621 curr->setXPos(x); 0622 x += curr->width() + curr->object()->marginRight(); 0623 } 0624 } 0625 } 0626 0627 x += borderRight() + paddingRight(); 0628 setWidth(x - startX); 0629 return x; 0630 } 0631 0632 void InlineFlowBox::verticallyAlignBoxes(int &heightOfBlock) 0633 { 0634 int maxPositionTop = 0; 0635 int maxPositionBottom = 0; 0636 int maxAscent = 0; 0637 int maxDescent = 0; 0638 0639 // Figure out if we're in strict mode. 0640 RenderObject *curr = object(); 0641 while (curr && !curr->element()) { 0642 curr = curr->container(); 0643 } 0644 bool strictMode = (curr && curr->document()->inStrictMode()); 0645 0646 computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode); 0647 0648 if (maxAscent + maxDescent < qMax(maxPositionTop, maxPositionBottom)) { 0649 adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); 0650 } 0651 0652 int maxHeight = maxAscent + maxDescent; 0653 int topPosition = heightOfBlock; 0654 int bottomPosition = heightOfBlock; 0655 placeBoxesVertically(heightOfBlock, maxHeight, maxAscent, strictMode, topPosition, bottomPosition); 0656 0657 setOverflowPositions(topPosition, bottomPosition); 0658 0659 // Shrink boxes with no text children in quirks and almost strict mode. 0660 if (!strictMode) { 0661 shrinkBoxesWithNoTextChildren(topPosition, bottomPosition); 0662 } 0663 0664 heightOfBlock += maxHeight; 0665 } 0666 0667 void InlineFlowBox::adjustMaxAscentAndDescent(int &maxAscent, int &maxDescent, 0668 int maxPositionTop, int maxPositionBottom) 0669 { 0670 for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) { 0671 // The computed lineheight needs to be extended for the 0672 // positioned elements 0673 // see khtmltests/rendering/html_align.html 0674 0675 if (curr->object()->isPositioned()) { 0676 continue; // Positioned placeholders don't affect calculations. 0677 } 0678 if (curr->yPos() == PositionTop || curr->yPos() == PositionBottom) { 0679 if (curr->yPos() == PositionTop) { 0680 if (maxAscent + maxDescent < curr->height()) { 0681 maxDescent = curr->height() - maxAscent; 0682 } 0683 } else { 0684 if (maxAscent + maxDescent < curr->height()) { 0685 maxAscent = curr->height() - maxDescent; 0686 } 0687 } 0688 0689 if (maxAscent + maxDescent >= qMax(maxPositionTop, maxPositionBottom)) { 0690 break; 0691 } 0692 } 0693 0694 if (curr->isInlineFlowBox()) { 0695 static_cast<InlineFlowBox *>(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); 0696 } 0697 } 0698 } 0699 0700 void InlineFlowBox::computeLogicalBoxHeights(int &maxPositionTop, int &maxPositionBottom, 0701 int &maxAscent, int &maxDescent, bool strictMode) 0702 { 0703 if (isRootInlineBox()) { 0704 // Examine our root box. 0705 setHeight(object()->lineHeight(m_firstLine)); 0706 bool isTableCell = object()->isTableCell(); 0707 if (isTableCell) { 0708 RenderTableCell *tableCell = static_cast<RenderTableCell *>(object()); 0709 setBaseline(tableCell->RenderBlock::baselinePosition(m_firstLine)); 0710 } else { 0711 setBaseline(object()->baselinePosition(m_firstLine)); 0712 } 0713 if (hasTextChildren() || strictMode) { 0714 int ascent = baseline(); 0715 int descent = height() - ascent; 0716 if (maxAscent < ascent) { 0717 maxAscent = ascent; 0718 } 0719 if (maxDescent < descent) { 0720 maxDescent = descent; 0721 } 0722 } 0723 } 0724 0725 for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) { 0726 if (curr->object()->isPositioned()) { 0727 continue; // Positioned placeholders don't affect calculations. 0728 } 0729 0730 curr->setHeight(curr->object()->lineHeight(m_firstLine)); 0731 curr->setBaseline(curr->object()->baselinePosition(m_firstLine)); 0732 curr->setYPos(curr->object()->verticalPositionHint(m_firstLine)); 0733 if (curr->yPos() == PositionTop) { 0734 if (maxPositionTop < curr->height()) { 0735 maxPositionTop = curr->height(); 0736 } 0737 } else if (curr->yPos() == PositionBottom) { 0738 if (maxPositionBottom < curr->height()) { 0739 maxPositionBottom = curr->height(); 0740 } 0741 } else if (curr->hasTextChildren() || strictMode) { 0742 int ascent = curr->baseline() - curr->yPos(); 0743 int descent = curr->height() - ascent; 0744 if (maxAscent < ascent) { 0745 maxAscent = ascent; 0746 } 0747 if (maxDescent < descent) { 0748 maxDescent = descent; 0749 } 0750 } 0751 0752 if (curr->isInlineFlowBox()) { 0753 static_cast<InlineFlowBox *>(curr)->computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode); 0754 } 0755 } 0756 } 0757 0758 void InlineFlowBox::placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode, 0759 int &topPosition, int &bottomPosition) 0760 { 0761 if (isRootInlineBox()) { 0762 setYPos(y + maxAscent - baseline());// Place our root box. 0763 // CSS2: 10.8.1 - line-height on the block level element specifies the *minimum* 0764 // height of the generated line box 0765 if (hasTextChildren() && maxHeight < object()->lineHeight(m_firstLine)) { 0766 maxHeight = object()->lineHeight(m_firstLine); 0767 } 0768 } 0769 0770 for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) { 0771 if (curr->object()->isPositioned()) { 0772 continue; // Positioned placeholders don't affect calculations. 0773 } 0774 0775 // Adjust boxes to use their real box y/height and not the logical height (as dictated by 0776 // line-height). 0777 if (curr->isInlineFlowBox()) 0778 static_cast<InlineFlowBox *>(curr)->placeBoxesVertically(y, maxHeight, maxAscent, strictMode, 0779 topPosition, bottomPosition); 0780 0781 bool childAffectsTopBottomPos = true; 0782 0783 if (curr->yPos() == PositionTop) { 0784 curr->setYPos(y); 0785 } else if (curr->yPos() == PositionBottom) { 0786 curr->setYPos(y + maxHeight - curr->height()); 0787 } else { 0788 if (!strictMode && !curr->hasTextDescendant()) { 0789 childAffectsTopBottomPos = false; 0790 } 0791 curr->setYPos(curr->yPos() + y + maxAscent - curr->baseline()); 0792 } 0793 int newY = curr->yPos(); 0794 int newHeight = curr->height(); 0795 int newBaseline = curr->baseline(); 0796 int overflowTop = 0; 0797 int overflowBottom = 0; 0798 if (curr->isInlineTextBox() || curr->isInlineFlowBox()) { 0799 const QFontMetrics &fm = curr->object()->fontMetrics(m_firstLine); 0800 #ifdef APPLE_CHANGES 0801 newBaseline = fm.ascent(); 0802 newY += curr->baseline() - newBaseline; 0803 newHeight = newBaseline + fm.descent(); 0804 #else 0805 // only adjust if the leading delta is superior to the font's natural leading 0806 if (qAbs(fm.ascent() - curr->baseline()) > fm.leading() / 2) { 0807 int ascent = fm.ascent() + fm.leading() / 2; 0808 newBaseline = ascent; 0809 newY += curr->baseline() - newBaseline; 0810 newHeight = fm.lineSpacing(); 0811 } 0812 #endif 0813 for (ShadowData *shadow = curr->object()->style()->textShadow(); shadow; shadow = shadow->next) { 0814 overflowTop = qMin(overflowTop, shadow->y - shadow->blur); 0815 overflowBottom = qMax(overflowBottom, shadow->y + shadow->blur); 0816 } 0817 if (curr->isInlineFlowBox()) { 0818 newHeight += curr->object()->borderTop() + curr->object()->paddingTop() + 0819 curr->object()->borderBottom() + curr->object()->paddingBottom(); 0820 newY -= curr->object()->borderTop() + curr->object()->paddingTop(); 0821 newBaseline += curr->object()->borderTop() + curr->object()->paddingTop(); 0822 } 0823 } else { 0824 newY += curr->object()->marginTop(); 0825 newHeight = curr->height() - (curr->object()->marginTop() + curr->object()->marginBottom()); 0826 overflowTop = curr->object()->overflowTop(); 0827 overflowBottom = curr->object()->overflowHeight() - newHeight; 0828 } 0829 curr->setYPos(newY); 0830 curr->setHeight(newHeight); 0831 curr->setBaseline(newBaseline); 0832 0833 if (childAffectsTopBottomPos) { 0834 topPosition = qMin(topPosition, newY + overflowTop); 0835 bottomPosition = qMax(bottomPosition, newY + newHeight + overflowBottom); 0836 } 0837 } 0838 0839 if (isRootInlineBox()) { 0840 const QFontMetrics &fm = object()->fontMetrics(m_firstLine); 0841 #ifdef APPLE_CHANGES 0842 setHeight(fm.ascent() + fm.descent()); 0843 setYPos(yPos() + baseline() - fm.ascent()); 0844 setBaseline(fm.ascent()); 0845 #else 0846 if (qAbs(fm.ascent() - baseline()) > fm.leading() / 2) { 0847 int ascent = fm.ascent() + fm.leading() / 2; 0848 setHeight(fm.lineSpacing()); 0849 setYPos(yPos() + baseline() - ascent); 0850 setBaseline(ascent); 0851 } 0852 #endif 0853 if (hasTextDescendant() || strictMode) { 0854 if (yPos() < topPosition) { 0855 topPosition = yPos(); 0856 } 0857 if (yPos() + height() > bottomPosition) { 0858 bottomPosition = yPos() + height(); 0859 } 0860 } 0861 } 0862 } 0863 0864 void InlineFlowBox::shrinkBoxesWithNoTextChildren(int topPos, int bottomPos) 0865 { 0866 // First shrink our kids. 0867 for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) { 0868 if (curr->object()->isPositioned()) { 0869 continue; // Positioned placeholders don't affect calculations. 0870 } 0871 0872 if (curr->isInlineFlowBox()) { 0873 static_cast<InlineFlowBox *>(curr)->shrinkBoxesWithNoTextChildren(topPos, bottomPos); 0874 } 0875 } 0876 0877 // See if we have text children. If not, then we need to shrink ourselves to fit on the line. 0878 if (!hasTextDescendant()) { 0879 if (yPos() < topPos) { 0880 setYPos(topPos); 0881 } 0882 if (yPos() + height() > bottomPos) { 0883 setHeight(bottomPos - yPos()); 0884 } 0885 if (baseline() > height()) { 0886 setBaseline(height()); 0887 } 0888 } 0889 } 0890 0891 bool InlineFlowBox::nodeAtPoint(RenderObject::NodeInfo &i, int x, int y, int tx, int ty) 0892 { 0893 // Check children first. 0894 for (InlineBox *curr = lastChild(); curr; curr = curr->prevOnLine()) { 0895 if (!curr->object()->layer() && curr->nodeAtPoint(i, x, y, tx, ty)) { 0896 object()->setInnerNode(i); 0897 return true; 0898 } 0899 } 0900 0901 // Now check ourselves. 0902 QRect rect(tx + m_x, ty + m_y, m_width, m_height); 0903 if (object()->style()->visibility() == VISIBLE && rect.contains(x, y)) { 0904 object()->setInnerNode(i); 0905 return true; 0906 } 0907 0908 return false; 0909 } 0910 0911 void InlineFlowBox::paint(RenderObject::PaintInfo &i, int tx, int ty) 0912 { 0913 bool intersectsDamageRect = true; 0914 int xPos = tx + m_x - object()->maximalOutlineSize(i.phase); 0915 int w = width() + 2 * object()->maximalOutlineSize(i.phase); 0916 if ((xPos >= i.r.x() + i.r.width()) || (xPos + w <= i.r.x())) { 0917 intersectsDamageRect = false; 0918 } 0919 0920 if (intersectsDamageRect) { 0921 if (i.phase == PaintActionOutline) { 0922 // Add ourselves to the paint info struct's list of inlines that need to paint their 0923 // outlines. 0924 if (object()->style()->visibility() == VISIBLE && object()->style()->outlineWidth() > 0 && 0925 !object()->isInlineContinuation() && !isRootInlineBox()) { 0926 if (!i.outlineObjects) { 0927 i.outlineObjects = new QList<RenderFlow *>; 0928 } 0929 i.outlineObjects->append(static_cast<RenderFlow *>(object())); 0930 } 0931 } else { 0932 // 1. Paint our background and border. 0933 paintBackgroundAndBorder(i, tx, ty); 0934 0935 // 2. Paint our underline and overline. 0936 paintDecorations(i, tx, ty, false); 0937 } 0938 } 0939 0940 // 3. Paint our children. 0941 for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) { 0942 if (!curr->object()->layer()) { 0943 curr->paint(i, tx, ty); 0944 } 0945 } 0946 0947 // 4. Paint our strike-through 0948 if (intersectsDamageRect && i.phase != PaintActionOutline) { 0949 paintDecorations(i, tx, ty, true); 0950 } 0951 } 0952 0953 void InlineFlowBox::paintAllBackgrounds(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, 0954 QRect clipr, int _tx, int _ty, int w, int h) 0955 { 0956 if (!bgLayer) { 0957 return; 0958 } 0959 paintAllBackgrounds(p, c, bgLayer->next(), clipr, _tx, _ty, w, h); 0960 paintOneBackground(p, c, bgLayer, clipr, _tx, _ty, w, h); 0961 } 0962 0963 void InlineFlowBox::paintOneBackground(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, 0964 QRect clipr, int _tx, int _ty, int w, int h) 0965 { 0966 CachedImage *bg = bgLayer->backgroundImage(); 0967 bool hasBackgroundImage = bg && bg->isComplete() && 0968 !bg->isTransparent() && !bg->isErrorImage(); 0969 if (!hasBackgroundImage || (!prevLineBox() && !nextLineBox()) || !parent()) 0970 object()->paintBackgroundExtended(p, c, bgLayer, clipr, _tx, _ty, w, h, borderLeft(), borderRight(), paddingLeft(), paddingRight(), 0971 object()->borderTop(), object()->borderBottom(), object()->paddingTop(), object()->paddingBottom()); 0972 else { 0973 // We have a background image that spans multiple lines. 0974 // We need to adjust _tx and _ty by the width of all previous lines. 0975 // Think of background painting on inlines as though you had one long line, a single continuous 0976 // strip. Even though that strip has been broken up across multiple lines, you still paint it 0977 // as though you had one single line. This means each line has to pick up the background where 0978 // the previous line left off. 0979 // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, 0980 // but it isn't even clear how this should work at all. 0981 int xOffsetOnLine = 0; 0982 for (InlineRunBox *curr = prevLineBox(); curr; curr = curr->prevLineBox()) { 0983 xOffsetOnLine += curr->width(); 0984 } 0985 int startX = _tx - xOffsetOnLine; 0986 int totalWidth = xOffsetOnLine; 0987 for (InlineRunBox *curr = this; curr; curr = curr->nextLineBox()) { 0988 totalWidth += curr->width(); 0989 } 0990 p->save(); 0991 p->setClipRect(QRect(_tx, _ty, width(), height())); 0992 object()->paintBackgroundExtended(p, c, bgLayer, clipr, startX, _ty, 0993 totalWidth, h, borderLeft(), borderRight(), paddingLeft(), paddingRight(), 0994 object()->borderTop(), object()->borderBottom(), object()->paddingTop(), object()->paddingBottom()); 0995 p->restore(); 0996 } 0997 } 0998 0999 void InlineFlowBox::paintBackgroundAndBorder(RenderObject::PaintInfo &pI, int _tx, int _ty) 1000 { 1001 if (object()->style()->visibility() != VISIBLE || pI.phase != PaintActionForeground) { 1002 return; 1003 } 1004 1005 // Move x/y to our coordinates. 1006 _tx += m_x; 1007 _ty += m_y; 1008 1009 int w = width(); 1010 int h = height(); 1011 1012 QRect cr; 1013 cr.setX(qMax(_tx, pI.r.x())); 1014 cr.setY(qMax(_ty, pI.r.y())); 1015 cr.setWidth(_tx < pI.r.x() ? qMax(0, w - (pI.r.x() - _tx)) : qMin(pI.r.width(), w)); 1016 cr.setHeight(_ty < pI.r.y() ? qMax(0, h - (pI.r.y() - _ty)) : qMin(pI.r.height(), h)); 1017 1018 // You can use p::first-line to specify a background. If so, the root line boxes for 1019 // a line may actually have to paint a background. 1020 RenderStyle *styleToUse = object()->style(m_firstLine); 1021 if ((!parent() && m_firstLine && styleToUse != object()->style()) || 1022 (parent() && object()->shouldPaintBackgroundOrBorder())) { 1023 QColor c = styleToUse->backgroundColor(); 1024 paintAllBackgrounds(pI.p, c, styleToUse->backgroundLayers(), cr, _tx, _ty, w, h); 1025 1026 // ::first-line cannot be used to put borders on a line. Always paint borders with our 1027 // non-first-line style. 1028 if (parent() && object()->style()->hasBorder()) { 1029 object()->paintBorder(pI.p, _tx, _ty, w, h, object()->style(), includeLeftEdge(), includeRightEdge()); 1030 } 1031 } 1032 } 1033 1034 static bool shouldDrawDecoration(RenderObject *obj) 1035 { 1036 bool shouldDraw = false; 1037 for (RenderObject *curr = obj->firstChild(); 1038 curr; curr = curr->nextSibling()) { 1039 if (curr->isInlineFlow()) { 1040 shouldDraw = true; 1041 break; 1042 } else if (curr->isText() && !curr->isBR() && (curr->style()->preserveWS() || 1043 !curr->element() || !curr->element()->containsOnlyWhitespace())) { 1044 shouldDraw = true; 1045 break; 1046 } 1047 } 1048 return shouldDraw; 1049 } 1050 1051 void InlineFlowBox::paintDecorations(RenderObject::PaintInfo &pI, int _tx, int _ty, bool paintedChildren) 1052 { 1053 // Now paint our text decorations. We only do this if we aren't in quirks mode (i.e., in 1054 // almost-strict mode or strict mode). 1055 if (object()->style()->htmlHacks() || object()->style()->visibility() != VISIBLE) { 1056 return; 1057 } 1058 1059 _tx += m_x; 1060 _ty += m_y; 1061 RenderStyle *styleToUse = object()->style(m_firstLine); 1062 int deco = parent() ? styleToUse->textDecoration() : styleToUse->textDecorationsInEffect(); 1063 if (deco != TDNONE && 1064 ((!paintedChildren && ((deco & UNDERLINE) || (deco & OVERLINE))) || (paintedChildren && (deco & LINE_THROUGH))) && 1065 shouldDrawDecoration(object())) { 1066 // We must have child boxes and have decorations defined. 1067 _tx += borderLeft() + paddingLeft(); 1068 int w = m_width - (borderLeft() + paddingLeft() + borderRight() + paddingRight()); 1069 if (!w) { 1070 return; 1071 } 1072 const QFontMetrics &fm = object()->fontMetrics(m_firstLine); 1073 // thick lines on small fonts look ugly 1074 int thickness = fm.height() > 20 ? fm.lineWidth() : 1; 1075 QColor underline, overline, linethrough; 1076 underline = overline = linethrough = styleToUse->color(); 1077 if (!parent()) { 1078 object()->getTextDecorationColors(deco, underline, overline, linethrough); 1079 } 1080 1081 if (styleToUse->font() != pI.p->font()) { 1082 pI.p->setFont(styleToUse->font()); 1083 } 1084 1085 if (deco & UNDERLINE && !paintedChildren) { 1086 int underlineOffset = (fm.height() + m_baseline) / 2; 1087 if (underlineOffset <= m_baseline) { 1088 underlineOffset = m_baseline + 1; 1089 } 1090 1091 pI.p->fillRect(_tx, _ty + underlineOffset, w, thickness, underline); 1092 } 1093 if (deco & OVERLINE && !paintedChildren) { 1094 pI.p->fillRect(_tx, _ty, w, thickness, overline); 1095 } 1096 if (deco & LINE_THROUGH && paintedChildren) { 1097 pI.p->fillRect(_tx, _ty + 2 * m_baseline / 3, w, thickness, linethrough); 1098 } 1099 } 1100 } 1101 1102 bool InlineFlowBox::canAccommodateEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth) 1103 { 1104 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) { 1105 if (!box->canAccommodateEllipsisBox(ltr, blockEdge, ellipsisWidth)) { 1106 return false; 1107 } 1108 } 1109 return true; 1110 } 1111 1112 int InlineFlowBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool &foundBox) 1113 { 1114 int result = -1; 1115 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) { 1116 int currResult = box->placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox); 1117 if (currResult != -1 && result == -1) { 1118 result = currResult; 1119 } 1120 } 1121 return result; 1122 } 1123 1124 void InlineFlowBox::clearTruncation() 1125 { 1126 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) { 1127 box->clearTruncation(); 1128 } 1129 } 1130 1131 void EllipsisBox::paint(RenderObject::PaintInfo &i, int _tx, int _ty) 1132 { 1133 QPainter *p = i.p; 1134 RenderStyle *_style = m_firstLine ? m_object->style(true) : m_object->style(); 1135 if (_style->font() != p->font()) { 1136 p->setFont(_style->font()); 1137 } 1138 1139 const Font *font = &_style->htmlFont(); 1140 QColor textColor = _style->color(); 1141 if (textColor != p->pen().color()) { 1142 p->setPen(textColor); 1143 } 1144 /* 1145 bool setShadow = false; 1146 if (_style->textShadow()) { 1147 p->setShadow(_style->textShadow()->x, _style->textShadow()->y, 1148 _style->textShadow()->blur, _style->textShadow()->color); 1149 setShadow = true; 1150 }*/ 1151 1152 const DOMString &str = m_str.string(); 1153 font->drawText(p, m_x + _tx, 1154 m_y + _ty + m_baseline, 1155 (str.implementation())->s, 1156 str.length(), 0, str.length(), 1157 0, 1158 Qt::LeftToRight, _style->visuallyOrdered()); 1159 1160 /* 1161 if (setShadow) 1162 p->clearShadow(); 1163 */ 1164 1165 if (m_markupBox) { 1166 // Paint the markup box 1167 _tx += m_x + m_width - m_markupBox->xPos(); 1168 _ty += m_y + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline()); 1169 m_markupBox->object()->paint(i, _tx, _ty); 1170 } 1171 } 1172 1173 bool EllipsisBox::nodeAtPoint(RenderObject::NodeInfo &info, int _x, int _y, int _tx, int _ty) 1174 { 1175 // Hit test the markup box. 1176 if (m_markupBox) { 1177 _tx += m_x + m_width - m_markupBox->xPos(); 1178 _ty += m_y + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline()); 1179 if (m_markupBox->nodeAtPoint(info, _x, _y, _tx, _ty)) { 1180 object()->setInnerNode(info); 1181 return true; 1182 } 1183 } 1184 1185 QRect rect(_tx + m_x, _ty + m_y, m_width, m_height); 1186 if (object()->style()->visibility() == VISIBLE && rect.contains(_x, _y)) { 1187 object()->setInnerNode(info); 1188 return true; 1189 } 1190 return false; 1191 } 1192 1193 void RootInlineBox::detach(RenderArena *arena, bool noRemove) 1194 { 1195 if (m_lineBreakContext) { 1196 m_lineBreakContext->deref(); 1197 } 1198 m_lineBreakContext = nullptr; 1199 detachEllipsisBox(arena); 1200 InlineFlowBox::detach(arena, noRemove); 1201 1202 } 1203 1204 void RootInlineBox::detachEllipsisBox(RenderArena *arena) 1205 { 1206 if (m_ellipsisBox) { 1207 m_ellipsisBox->detach(arena); 1208 m_ellipsisBox = nullptr; 1209 } 1210 } 1211 1212 void RootInlineBox::clearTruncation() 1213 { 1214 if (m_ellipsisBox) { 1215 detachEllipsisBox(m_object->renderArena()); 1216 InlineFlowBox::clearTruncation(); 1217 } 1218 } 1219 1220 bool RootInlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth) 1221 { 1222 // First sanity-check the unoverflowed width of the whole line to see if there is sufficient room. 1223 int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge; 1224 if (width() - delta < ellipsisWidth) { 1225 return false; 1226 } 1227 1228 // Next iterate over all the line boxes on the line. If we find a replaced element that intersects 1229 // then we refuse to accommodate the ellipsis. Otherwise we're ok. 1230 return InlineFlowBox::canAccommodateEllipsisBox(ltr, blockEdge, ellipsisWidth); 1231 } 1232 1233 void RootInlineBox::placeEllipsis(const DOMString &ellipsisStr, bool ltr, int blockEdge, int ellipsisWidth, InlineBox *markupBox) 1234 { 1235 // Create an ellipsis box. 1236 m_ellipsisBox = new(m_object->renderArena()) EllipsisBox(m_object, ellipsisStr, this, 1237 ellipsisWidth - (markupBox ? markupBox->width() : 0), 1238 yPos(), height(), baseline(), !prevRootBox(), 1239 markupBox); 1240 1241 if (ltr && (xPos() + width() + ellipsisWidth) <= blockEdge) { 1242 m_ellipsisBox->m_x = xPos() + width(); 1243 return; 1244 } 1245 1246 // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL) 1247 // of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being 1248 // truncated). 1249 bool foundBox = false; 1250 m_ellipsisBox->m_x = placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox); 1251 } 1252 1253 int RootInlineBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool &foundBox) 1254 { 1255 int result = InlineFlowBox::placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox); 1256 if (result == -1) { 1257 result = ltr ? blockEdge - ellipsisWidth : blockEdge; 1258 } 1259 return result; 1260 } 1261 1262 void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo &i, int _tx, int _ty) const 1263 { 1264 if (m_ellipsisBox) { 1265 m_ellipsisBox->paint(i, _tx, _ty); 1266 } 1267 } 1268 1269 void RootInlineBox::paint(RenderObject::PaintInfo &i, int tx, int ty) 1270 { 1271 InlineFlowBox::paint(i, tx, ty); 1272 paintEllipsisBox(i, tx, ty); 1273 } 1274 1275 bool RootInlineBox::nodeAtPoint(RenderObject::NodeInfo &i, int x, int y, int tx, int ty) 1276 { 1277 if (m_ellipsisBox && object()->style()->visibility() == VISIBLE) { 1278 if (m_ellipsisBox->nodeAtPoint(i, x, y, tx, ty)) { 1279 object()->setInnerNode(i); 1280 return true; 1281 } 1282 } 1283 return InlineFlowBox::nodeAtPoint(i, x, y, tx, ty); 1284 } 1285 1286 BidiStatus RootInlineBox::lineBreakBidiStatus() const 1287 { 1288 BidiStatus st; 1289 st.eor = KDE_CAST_BF_ENUM(QChar::Direction, m_lineBreakBidiStatusEor); 1290 st.last = KDE_CAST_BF_ENUM(QChar::Direction, m_lineBreakBidiStatusLast); 1291 st.lastStrong = KDE_CAST_BF_ENUM(QChar::Direction, m_lineBreakBidiStatusLastStrong); 1292 return st; 1293 } 1294 1295 void RootInlineBox::childRemoved(InlineBox *box) 1296 { 1297 if (box->object() == m_lineBreakObj) { 1298 setLineBreakInfo(nullptr, 0, BidiStatus(), nullptr); 1299 } 1300 1301 for (RootInlineBox *prev = prevRootBox(); prev && prev->lineBreakObj() == box->object(); prev = prev->prevRootBox()) { 1302 prev->setLineBreakInfo(nullptr, 0, BidiStatus(), nullptr); 1303 prev->markDirty(); 1304 } 1305 } 1306 1307 void RootInlineBox::setLineBreakInfo(RenderObject *obj, unsigned breakPos, const BidiStatus &status, BidiContext *context) 1308 { 1309 m_lineBreakObj = obj; 1310 m_lineBreakPos = breakPos; 1311 m_lineBreakBidiStatusEor = status.eor; 1312 m_lineBreakBidiStatusLastStrong = status.lastStrong; 1313 m_lineBreakBidiStatusLast = status.last; 1314 if (m_lineBreakContext) { 1315 m_lineBreakContext->deref(); 1316 } 1317 m_lineBreakContext = context; 1318 if (m_lineBreakContext) { 1319 m_lineBreakContext->ref(); 1320 } 1321 } 1322 1323 InlineBox *InlineFlowBox::firstLeafChild() 1324 { 1325 InlineBox *box = firstChild(); 1326 while (box) { 1327 InlineBox *next = nullptr; 1328 if (!box->isInlineFlowBox()) { 1329 break; 1330 } 1331 next = static_cast<InlineFlowBox *>(box)->firstChild(); 1332 if (!next) { 1333 break; 1334 } 1335 box = next; 1336 } 1337 return box; 1338 } 1339 1340 InlineBox *InlineFlowBox::lastLeafChild() 1341 { 1342 InlineBox *box = lastChild(); 1343 while (box) { 1344 InlineBox *next = nullptr; 1345 if (!box->isInlineFlowBox()) { 1346 break; 1347 } 1348 next = static_cast<InlineFlowBox *>(box)->lastChild(); 1349 if (!next) { 1350 break; 1351 } 1352 box = next; 1353 } 1354 return box; 1355 } 1356 1357 InlineBox *InlineFlowBox::closestChildForXPos(int _x, int _tx) 1358 { 1359 if (_x < _tx + firstChild()->m_x) 1360 // if the x coordinate is to the left of the first child 1361 { 1362 return firstChild(); 1363 } else if (_x >= _tx + lastChild()->m_x + lastChild()->m_width) 1364 // if the x coordinate is to the right of the last child 1365 { 1366 return lastChild(); 1367 } else 1368 // look for the closest child; 1369 // check only the right edges, since the left edge of the first 1370 // box has already been checked 1371 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) 1372 if (_x < _tx + box->m_x + box->m_width) { 1373 return box; 1374 } 1375 1376 return nullptr; 1377 } 1378