KHtml

render_line.cpp
1 /**
2 * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 2003-2007 Apple Computer, Inc.
5  * (C) 2006-2007 Germain Garand ([email protected])
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB. If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 // -------------------------------------------------------------------------
23 
24 #include "render_line.h"
25 
26 #include "khtml_debug.h"
27 #include <assert.h>
28 #include <QPainter>
29 
30 #include "render_flow.h"
31 #include "render_text.h"
32 #include "render_table.h"
33 #include "render_inline.h"
34 #include "render_block.h"
35 #include "render_arena.h"
36 #include <xml/dom_nodeimpl.h>
37 #include <xml/dom_docimpl.h>
38 #include <html/html_formimpl.h>
39 #include <khtmlview.h>
40 
41 using namespace DOM;
42 using namespace khtml;
43 
44 #ifndef NDEBUG
45 static bool inInlineBoxDetach;
46 #endif
47 
48 class khtml::EllipsisBox : public InlineBox
49 {
50 public:
51  EllipsisBox(RenderObject *obj, const DOM::DOMString &ellipsisStr, InlineFlowBox *p,
52  int w, int y, int h, int b, bool firstLine, InlineBox *markupBox)
53  : InlineBox(obj), m_str(ellipsisStr)
54  {
55  m_parent = p;
56  m_width = w;
57  m_y = y;
58  m_height = h;
59  m_baseline = b;
60  m_firstLine = firstLine;
61  m_constructed = true;
62  m_markupBox = markupBox;
63  }
64 
65  void paint(RenderObject::PaintInfo &i, int _tx, int _ty) override;
66  bool nodeAtPoint(RenderObject::NodeInfo &info, int _x, int _y, int _tx, int _ty) override;
67 
68 private:
69  DOM::DOMString m_str;
70  InlineBox *m_markupBox;
71 };
72 
73 void InlineBox::remove()
74 {
75  if (m_parent) {
76  m_parent->removeFromLine(this);
77  }
78 }
79 
80 void InlineBox::detach(RenderArena *renderArena, bool noRemove)
81 {
82  if (!noRemove) {
83  remove();
84  }
85 
86 #ifndef NDEBUG
87  inInlineBoxDetach = true;
88 #endif
89  delete this;
90 #ifndef NDEBUG
91  inInlineBoxDetach = false;
92 #endif
93 
94  // Recover the size left there for us by operator delete and free the memory.
95  renderArena->free(*(size_t *)this, this);
96 }
97 
98 void *InlineBox::operator new(size_t sz, RenderArena *renderArena) throw()
99 {
100  return renderArena->allocate(sz);
101 }
102 
103 void InlineBox::operator delete(void *ptr, size_t sz)
104 {
105  assert(inInlineBoxDetach);
106 #ifdef KHTML_USE_ARENA_ALLOCATOR
107  // Stash size where detach can find it.
108  *(size_t *)ptr = sz;
109 #endif
110 }
111 
112 static bool needsOutlinePhaseRepaint(RenderObject *o, RenderObject::PaintInfo &i, int tx, int ty)
113 {
114  if (o->style()->outlineWidth() <= 0) {
115  return false;
116  }
117  QRect r(tx + o->xPos(), ty + o->yPos(), o->width(), o->height());
118  if (r.intersects(i.r)) {
119  return false;
120  }
121  r.adjust(-o->style()->outlineSize(),
122  -o->style()->outlineSize(),
123  o->style()->outlineSize(),
124  o->style()->outlineSize());
125  if (!r.intersects(i.r)) {
126  return false;
127  }
128  return true;
129 }
130 
131 void InlineBox::paint(RenderObject::PaintInfo &i, int tx, int ty)
132 {
133  if (i.phase == PaintActionOutline && !needsOutlinePhaseRepaint(object(), i, tx, ty)) {
134  return;
135  }
136 
137  // Paint all phases of replaced elements atomically, as though the replaced element established its
138  // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
139  // specification.)
140  bool paintSelectionOnly = i.phase == PaintActionSelection;
141  RenderObject::PaintInfo info(i.p, i.r, paintSelectionOnly ? i.phase : PaintActionElementBackground);
142  object()->paint(info, tx, ty);
143  if (!paintSelectionOnly) {
144  info.phase = PaintActionChildBackgrounds;
145  object()->paint(info, tx, ty);
146  info.phase = PaintActionFloat;
147  object()->paint(info, tx, ty);
148  info.phase = PaintActionForeground;
149  object()->paint(info, tx, ty);
150  info.phase = PaintActionOutline;
151  object()->paint(info, tx, ty);
152  }
153 }
154 
155 bool InlineBox::nodeAtPoint(RenderObject::NodeInfo &i, int x, int y, int tx, int ty)
156 {
157  // Hit test all phases of replaced elements atomically, as though the replaced element established its
158  // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
159  // specification.)
160  bool inside = false;
161  return object()->nodeAtPoint(i, x, y, tx, ty, HitTestAll, inside); // ### port hitTest
162 }
163 
164 long InlineBox::caretMinOffset() const
165 {
166  return 0;
167 }
168 
169 long InlineBox::caretMaxOffset() const
170 {
171  return 1;
172 }
173 
174 unsigned long InlineBox::caretMaxRenderedOffset() const
175 {
176  return 1;
177 }
178 
179 RootInlineBox *InlineBox::root()
180 {
181  if (m_parent) {
182  return m_parent->root();
183  }
184  assert(isRootInlineBox());
185  return static_cast<RootInlineBox *>(this);
186 }
187 
188 InlineFlowBox::~InlineFlowBox()
189 {
190 }
191 
192 void InlineFlowBox::extractLine()
193 {
194  if (!m_extracted) {
195  static_cast<RenderFlow *>(m_object)->extractLineBox(this);
196  }
197  for (InlineBox *child = firstChild(); child; child = child->nextOnLine()) {
198  child->extractLine();
199  }
200 }
201 
202 void InlineFlowBox::attachLine()
203 {
204  if (m_extracted) {
205  static_cast<RenderFlow *>(m_object)->attachLineBox(this);
206  }
207  for (InlineBox *child = firstChild(); child; child = child->nextOnLine()) {
208  child->attachLine();
209  }
210 }
211 
212 void InlineFlowBox::deleteLine(RenderArena *arena)
213 {
214  InlineBox *child = firstChild();
215  InlineBox *next = nullptr;
216  while (child) {
217  assert(this == child->parent());
218  next = child->nextOnLine();
219 #ifndef NDEBUG
220  child->setParent(nullptr);
221 #endif
222  child->deleteLine(arena);
223  child = next;
224  }
225 #ifndef NDEBUG
226  m_firstChild = nullptr;
227  m_lastChild = nullptr;
228 #endif
229 
230  m_object->removeInlineBox(this);
231  detach(arena, true /*no remove*/);
232 }
233 
234 void InlineFlowBox::removeFromLine(InlineBox *child)
235 {
236  if (!m_dirty) {
237  dirtyInlineBoxes();
238  }
239 
240  root()->childRemoved(child);
241 
242  if (child == m_firstChild) {
243  m_firstChild = child->nextOnLine();
244  }
245  if (child == m_lastChild) {
246  m_lastChild = child->prevOnLine();
247  }
248  if (child->nextOnLine()) {
249  child->nextOnLine()->m_prev = child->prevOnLine();
250  }
251  if (child->prevOnLine()) {
252  child->prevOnLine()->m_next = child->nextOnLine();
253  }
254 
255  child->setParent(nullptr);
256 }
257 
258 void InlineBox::dirtyInlineBoxes()
259 {
260  markDirty();
261  for (InlineFlowBox *curr = parent(); curr && !curr->isDirty(); curr = curr->parent()) {
262  curr->markDirty();
263  }
264 }
265 
266 void InlineBox::deleteLine(RenderArena *arena)
267 {
268  if (!m_extracted && m_object->isBox()) {
269  static_cast<RenderBox *>(m_object)->setPlaceHolderBox(nullptr);
270  }
271  detach(arena, true /*no remove*/);
272 }
273 
274 void InlineBox::extractLine()
275 {
276  m_extracted = true;
277  if (m_object->isBox()) {
278  static_cast<RenderBox *>(m_object)->setPlaceHolderBox(nullptr);
279  }
280 }
281 
282 void InlineBox::attachLine()
283 {
284  m_extracted = false;
285  if (m_object->isBox()) {
286  static_cast<RenderBox *>(m_object)->setPlaceHolderBox(this);
287  }
288 }
289 
290 void InlineBox::adjustPosition(int dx, int dy)
291 {
292  m_x += dx;
293  m_y += dy;
294  if (m_object->isReplaced() || m_object->isBR()) {
295  m_object->setPos(m_object->xPos() + dx, m_object->yPos() + dy);
296  }
297 }
298 
299 bool InlineBox::canAccommodateEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth)
300 {
301  // Non-replaced elements can always accommodate an ellipsis.
302  if (!m_object || !m_object->isReplaced()) {
303  return true;
304  }
305 
306  QRect boxRect(m_x, 0, m_width, 10);
307  QRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10);
308  return !(boxRect.intersects(ellipsisRect));
309 }
310 
311 int InlineBox::placeEllipsisBox(bool /*ltr*/, int /*blockEdge*/, int /*ellipsisWidth*/, bool &)
312 {
313  // Use -1 to mean "we didn't set the position."
314  return -1;
315 }
316 
317 bool InlineBox::nextOnLineExists() const
318 {
319  if (!parent()) {
320  return false;
321  }
322 
323  if (nextOnLine()) {
324  return true;
325  }
326 
327  return parent()->nextOnLineExists();
328 }
329 
330 bool InlineBox::prevOnLineExists() const
331 {
332  if (!parent()) {
333  return false;
334  }
335 
336  if (prevOnLine()) {
337  return true;
338  }
339 
340  return parent()->prevOnLineExists();
341 }
342 
343 InlineBox *InlineBox::firstLeafChild()
344 {
345  return this;
346 }
347 
348 InlineBox *InlineBox::lastLeafChild()
349 {
350  return this;
351 }
352 
353 InlineBox *InlineBox::closestLeafChildForXPos(int _x, int _tx)
354 {
355  if (!isInlineFlowBox()) {
356  return this;
357  }
358 
359  InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(this);
360  if (!flowBox->firstChild()) {
361  return this;
362  }
363 
364  InlineBox *box = flowBox->closestChildForXPos(_x, _tx);
365  if (!box) {
366  return this;
367  }
368 
369  return box->closestLeafChildForXPos(_x, _tx);
370 }
371 
372 #ifdef ENABLE_DUMP
373 static QString getInlineBoxType(const InlineBox *box)
374 {
375  if (box->isInlineTextBox()) {
376  return "Text";
377  }
378  if (box->isRootInlineBox()) {
379  return "RootBox";
380  }
381  if (box->isInlineFlowBox()) {
382  return "FlowBox";
383  }
384  if (box->isPlaceHolderBox()) {
385  return "PlaceHolderBox";
386  }
387  return "InlineBox";
388 }
389 
390 QString InlineBox::information() const
391 {
392  QString result;
393  QTextStream out(&result, QIODevice::WriteOnly);
394  out << getInlineBoxType(this) << "(" << (void *)this << ") "
395  << "Pos" << "(" << xPos() << "," << yPos() << ") "
396  << "Size" << "(" << width() << "," << height() << ") "
397  << "Overflow" << "(" << topOverflow() << "," << bottomOverflow() << ") "
398  << (object() ? object()->renderName() : "NoRenderObject") << "(" << (void *)object() << ") ";
399  if (isInlineTextBox()) {
400  const InlineTextBox *textBox = static_cast<const InlineTextBox *>(this);
401  out << "Text[" << textBox->renderText()->data().substring(textBox->start(), textBox->len()).string() << "]";
402  }
403  return result;
404 }
405 
406 void InlineBox::printTree(int indent) const
407 {
408  QString temp;
409  temp.fill(' ', indent);
410 
411  // qCDebug(KHTML_LOG) << (temp + information());
412  if (isRootInlineBox()) {
413  // const RootInlineBox* root = static_cast<const RootInlineBox*>(this);
414  }
415  if (isInlineTextBox()) {
416  //
417  }
418  if (isInlineFlowBox()) {
419  const InlineFlowBox *flowBox = static_cast<const InlineFlowBox *>(this);
420  for (InlineBox *box = flowBox->firstChild(); box; box = box->nextOnLine()) {
421  box->printTree(indent + 2);
422  }
423  }
424 }
425 #endif
426 
427 int InlineFlowBox::marginLeft() const
428 {
429  if (!includeLeftEdge()) {
430  return 0;
431  }
432 
433  RenderStyle *cstyle = object()->style();
434  Length margin = cstyle->marginLeft();
435  if (!margin.isAuto()) {
436  return (margin.isFixed() ? margin.value() : object()->marginLeft());
437  }
438  return 0;
439 }
440 
441 int InlineFlowBox::marginRight() const
442 {
443  if (!includeRightEdge()) {
444  return 0;
445  }
446 
447  RenderStyle *cstyle = object()->style();
448  Length margin = cstyle->marginRight();
449  if (!margin.isAuto()) {
450  return (margin.isFixed() ? margin.value() : object()->marginRight());
451  }
452  return 0;
453 }
454 
455 int InlineFlowBox::marginBorderPaddingLeft() const
456 {
457  return marginLeft() + borderLeft() + paddingLeft();
458 }
459 
460 int InlineFlowBox::marginBorderPaddingRight() const
461 {
462  return marginRight() + borderRight() + paddingRight();
463 }
464 
465 int InlineFlowBox::getFlowSpacingWidth() const
466 {
467  int totWidth = marginBorderPaddingLeft() + marginBorderPaddingRight();
468  for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
469  if (curr->isInlineFlowBox()) {
470  totWidth += static_cast<InlineFlowBox *>(curr)->getFlowSpacingWidth();
471  }
472  }
473  return totWidth;
474 }
475 
476 bool InlineFlowBox::nextOnLineExists()
477 {
478  if (!parent()) {
479  return false;
480  }
481 
482  if (nextOnLine()) {
483  return true;
484  }
485 
486  return parent()->nextOnLineExists();
487 }
488 
489 bool InlineFlowBox::prevOnLineExists()
490 {
491  if (!parent()) {
492  return false;
493  }
494 
495  if (prevOnLine()) {
496  return true;
497  }
498 
499  return parent()->prevOnLineExists();
500 }
501 
502 bool InlineFlowBox::onEndChain(RenderObject *endObject)
503 {
504  if (!endObject) {
505  return false;
506  }
507 
508  if (endObject == object()) {
509  return true;
510  }
511 
512  RenderObject *curr = endObject;
513  RenderObject *parent = curr->parent();
514  while (parent && !parent->isRenderBlock()) {
515  if (parent->lastChild() != curr || parent == object()) {
516  return false;
517  }
518 
519  curr = parent;
520  parent = curr->parent();
521  }
522 
523  return true;
524 }
525 
526 void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject *endObject)
527 {
528  // All boxes start off open. They will not apply any margins/border/padding on
529  // any side.
530  bool includeLeftEdge = false;
531  bool includeRightEdge = false;
532 
533  RenderFlow *flow = static_cast<RenderFlow *>(object());
534 
535  // The root inline box never has borders/margins/padding.
536  if (parent()) {
537  bool ltr = flow->style()->direction() == LTR;
538 
539  // Check to see if all initial lines are unconstructed. If so, then
540  // we know the inline began on this line.
541  if (!flow->firstLineBox()->isConstructed() && !object()->isInlineContinuation()) {
542  if (ltr && flow->firstLineBox() == this) {
543  includeLeftEdge = true;
544  } else if (!ltr && flow->lastLineBox() == this) {
545  includeRightEdge = true;
546  }
547  }
548 
549  // In order to determine if the inline ends on this line, we check three things:
550  // (1) If we are the last line and we don't have a continuation(), then we can
551  // close up.
552  // (2) If the last line box for the flow has an object following it on the line (ltr,
553  // reverse for rtl), then the inline has closed.
554  // (3) The line may end on the inline. If we are the last child (climbing up
555  // the end object's chain), then we just closed as well.
556  if (!flow->lastLineBox()->isConstructed()) {
557  if (ltr) {
558  if (!nextLineBox() &&
559  ((lastLine && !object()->continuation()) || nextOnLineExists()
560  || onEndChain(endObject))) {
561  includeRightEdge = true;
562  }
563  } else {
564  if ((!prevLineBox() || prevLineBox()->isConstructed()) &&
565  ((lastLine && !object()->continuation()) ||
566  prevOnLineExists() || onEndChain(endObject))) {
567  includeLeftEdge = true;
568  }
569  }
570 
571  }
572  }
573 
574  setEdges(includeLeftEdge, includeRightEdge);
575 
576  // Recur into our children.
577  for (InlineBox *currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) {
578  if (currChild->isInlineFlowBox()) {
579  InlineFlowBox *currFlow = static_cast<InlineFlowBox *>(currChild);
580  currFlow->determineSpacingForFlowBoxes(lastLine, endObject);
581  }
582  }
583 }
584 
585 int InlineFlowBox::placeBoxesHorizontally(int x)
586 {
587  // Set our x position.
588  setXPos(x);
589 
590  int startX = x;
591  x += borderLeft() + paddingLeft();
592 
593  for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
594  if (curr->object()->isText()) {
595  InlineTextBox *text = static_cast<InlineTextBox *>(curr);
596  text->setXPos(x);
597  x += curr->width();
598  } else {
599  if (curr->object()->isPositioned()) {
600  if (curr->object()->parent()->style()->direction() == LTR) {
601  curr->setXPos(x);
602  } else {
603  // Our offset that we cache needs to be from the edge of the right border box and
604  // not the left border box. We have to subtract |x| from the width of the block
605  // (which can be obtained by walking up to the root line box).
606  InlineBox *root = this;
607  while (!root->isRootInlineBox()) {
608  root = root->parent();
609  }
610  curr->setXPos(root->object()->width() - x);
611  }
612  continue; // The positioned object has no effect on the width.
613  }
614  if (curr->object()->isInlineFlow()) {
615  InlineFlowBox *flow = static_cast<InlineFlowBox *>(curr);
616  x += flow->marginLeft();
617  x = flow->placeBoxesHorizontally(x);
618  x += flow->marginRight();
619  } else {
620  x += curr->object()->marginLeft();
621  curr->setXPos(x);
622  x += curr->width() + curr->object()->marginRight();
623  }
624  }
625  }
626 
627  x += borderRight() + paddingRight();
628  setWidth(x - startX);
629  return x;
630 }
631 
632 void InlineFlowBox::verticallyAlignBoxes(int &heightOfBlock)
633 {
634  int maxPositionTop = 0;
635  int maxPositionBottom = 0;
636  int maxAscent = 0;
637  int maxDescent = 0;
638 
639  // Figure out if we're in strict mode.
640  RenderObject *curr = object();
641  while (curr && !curr->element()) {
642  curr = curr->container();
643  }
644  bool strictMode = (curr && curr->document()->inStrictMode());
645 
646  computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode);
647 
648  if (maxAscent + maxDescent < qMax(maxPositionTop, maxPositionBottom)) {
649  adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
650  }
651 
652  int maxHeight = maxAscent + maxDescent;
653  int topPosition = heightOfBlock;
654  int bottomPosition = heightOfBlock;
655  placeBoxesVertically(heightOfBlock, maxHeight, maxAscent, strictMode, topPosition, bottomPosition);
656 
657  setOverflowPositions(topPosition, bottomPosition);
658 
659  // Shrink boxes with no text children in quirks and almost strict mode.
660  if (!strictMode) {
661  shrinkBoxesWithNoTextChildren(topPosition, bottomPosition);
662  }
663 
664  heightOfBlock += maxHeight;
665 }
666 
667 void InlineFlowBox::adjustMaxAscentAndDescent(int &maxAscent, int &maxDescent,
668  int maxPositionTop, int maxPositionBottom)
669 {
670  for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
671  // The computed lineheight needs to be extended for the
672  // positioned elements
673  // see khtmltests/rendering/html_align.html
674 
675  if (curr->object()->isPositioned()) {
676  continue; // Positioned placeholders don't affect calculations.
677  }
678  if (curr->yPos() == PositionTop || curr->yPos() == PositionBottom) {
679  if (curr->yPos() == PositionTop) {
680  if (maxAscent + maxDescent < curr->height()) {
681  maxDescent = curr->height() - maxAscent;
682  }
683  } else {
684  if (maxAscent + maxDescent < curr->height()) {
685  maxAscent = curr->height() - maxDescent;
686  }
687  }
688 
689  if (maxAscent + maxDescent >= qMax(maxPositionTop, maxPositionBottom)) {
690  break;
691  }
692  }
693 
694  if (curr->isInlineFlowBox()) {
695  static_cast<InlineFlowBox *>(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
696  }
697  }
698 }
699 
700 void InlineFlowBox::computeLogicalBoxHeights(int &maxPositionTop, int &maxPositionBottom,
701  int &maxAscent, int &maxDescent, bool strictMode)
702 {
703  if (isRootInlineBox()) {
704  // Examine our root box.
705  setHeight(object()->lineHeight(m_firstLine));
706  bool isTableCell = object()->isTableCell();
707  if (isTableCell) {
708  RenderTableCell *tableCell = static_cast<RenderTableCell *>(object());
709  setBaseline(tableCell->RenderBlock::baselinePosition(m_firstLine));
710  } else {
711  setBaseline(object()->baselinePosition(m_firstLine));
712  }
713  if (hasTextChildren() || strictMode) {
714  int ascent = baseline();
715  int descent = height() - ascent;
716  if (maxAscent < ascent) {
717  maxAscent = ascent;
718  }
719  if (maxDescent < descent) {
720  maxDescent = descent;
721  }
722  }
723  }
724 
725  for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
726  if (curr->object()->isPositioned()) {
727  continue; // Positioned placeholders don't affect calculations.
728  }
729 
730  curr->setHeight(curr->object()->lineHeight(m_firstLine));
731  curr->setBaseline(curr->object()->baselinePosition(m_firstLine));
732  curr->setYPos(curr->object()->verticalPositionHint(m_firstLine));
733  if (curr->yPos() == PositionTop) {
734  if (maxPositionTop < curr->height()) {
735  maxPositionTop = curr->height();
736  }
737  } else if (curr->yPos() == PositionBottom) {
738  if (maxPositionBottom < curr->height()) {
739  maxPositionBottom = curr->height();
740  }
741  } else if (curr->hasTextChildren() || strictMode) {
742  int ascent = curr->baseline() - curr->yPos();
743  int descent = curr->height() - ascent;
744  if (maxAscent < ascent) {
745  maxAscent = ascent;
746  }
747  if (maxDescent < descent) {
748  maxDescent = descent;
749  }
750  }
751 
752  if (curr->isInlineFlowBox()) {
753  static_cast<InlineFlowBox *>(curr)->computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode);
754  }
755  }
756 }
757 
758 void InlineFlowBox::placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode,
759  int &topPosition, int &bottomPosition)
760 {
761  if (isRootInlineBox()) {
762  setYPos(y + maxAscent - baseline());// Place our root box.
763  // CSS2: 10.8.1 - line-height on the block level element specifies the *minimum*
764  // height of the generated line box
765  if (hasTextChildren() && maxHeight < object()->lineHeight(m_firstLine)) {
766  maxHeight = object()->lineHeight(m_firstLine);
767  }
768  }
769 
770  for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
771  if (curr->object()->isPositioned()) {
772  continue; // Positioned placeholders don't affect calculations.
773  }
774 
775  // Adjust boxes to use their real box y/height and not the logical height (as dictated by
776  // line-height).
777  if (curr->isInlineFlowBox())
778  static_cast<InlineFlowBox *>(curr)->placeBoxesVertically(y, maxHeight, maxAscent, strictMode,
779  topPosition, bottomPosition);
780 
781  bool childAffectsTopBottomPos = true;
782 
783  if (curr->yPos() == PositionTop) {
784  curr->setYPos(y);
785  } else if (curr->yPos() == PositionBottom) {
786  curr->setYPos(y + maxHeight - curr->height());
787  } else {
788  if (!strictMode && !curr->hasTextDescendant()) {
789  childAffectsTopBottomPos = false;
790  }
791  curr->setYPos(curr->yPos() + y + maxAscent - curr->baseline());
792  }
793  int newY = curr->yPos();
794  int newHeight = curr->height();
795  int newBaseline = curr->baseline();
796  int overflowTop = 0;
797  int overflowBottom = 0;
798  if (curr->isInlineTextBox() || curr->isInlineFlowBox()) {
799  const QFontMetrics &fm = curr->object()->fontMetrics(m_firstLine);
800 #ifdef APPLE_CHANGES
801  newBaseline = fm.ascent();
802  newY += curr->baseline() - newBaseline;
803  newHeight = newBaseline + fm.descent();
804 #else
805  // only adjust if the leading delta is superior to the font's natural leading
806  if (qAbs(fm.ascent() - curr->baseline()) > fm.leading() / 2) {
807  int ascent = fm.ascent() + fm.leading() / 2;
808  newBaseline = ascent;
809  newY += curr->baseline() - newBaseline;
810  newHeight = fm.lineSpacing();
811  }
812 #endif
813  for (ShadowData *shadow = curr->object()->style()->textShadow(); shadow; shadow = shadow->next) {
814  overflowTop = qMin(overflowTop, shadow->y - shadow->blur);
815  overflowBottom = qMax(overflowBottom, shadow->y + shadow->blur);
816  }
817  if (curr->isInlineFlowBox()) {
818  newHeight += curr->object()->borderTop() + curr->object()->paddingTop() +
819  curr->object()->borderBottom() + curr->object()->paddingBottom();
820  newY -= curr->object()->borderTop() + curr->object()->paddingTop();
821  newBaseline += curr->object()->borderTop() + curr->object()->paddingTop();
822  }
823  } else {
824  newY += curr->object()->marginTop();
825  newHeight = curr->height() - (curr->object()->marginTop() + curr->object()->marginBottom());
826  overflowTop = curr->object()->overflowTop();
827  overflowBottom = curr->object()->overflowHeight() - newHeight;
828  }
829  curr->setYPos(newY);
830  curr->setHeight(newHeight);
831  curr->setBaseline(newBaseline);
832 
833  if (childAffectsTopBottomPos) {
834  topPosition = qMin(topPosition, newY + overflowTop);
835  bottomPosition = qMax(bottomPosition, newY + newHeight + overflowBottom);
836  }
837  }
838 
839  if (isRootInlineBox()) {
840  const QFontMetrics &fm = object()->fontMetrics(m_firstLine);
841 #ifdef APPLE_CHANGES
842  setHeight(fm.ascent() + fm.descent());
843  setYPos(yPos() + baseline() - fm.ascent());
844  setBaseline(fm.ascent());
845 #else
846  if (qAbs(fm.ascent() - baseline()) > fm.leading() / 2) {
847  int ascent = fm.ascent() + fm.leading() / 2;
848  setHeight(fm.lineSpacing());
849  setYPos(yPos() + baseline() - ascent);
850  setBaseline(ascent);
851  }
852 #endif
853  if (hasTextDescendant() || strictMode) {
854  if (yPos() < topPosition) {
855  topPosition = yPos();
856  }
857  if (yPos() + height() > bottomPosition) {
858  bottomPosition = yPos() + height();
859  }
860  }
861  }
862 }
863 
864 void InlineFlowBox::shrinkBoxesWithNoTextChildren(int topPos, int bottomPos)
865 {
866  // First shrink our kids.
867  for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
868  if (curr->object()->isPositioned()) {
869  continue; // Positioned placeholders don't affect calculations.
870  }
871 
872  if (curr->isInlineFlowBox()) {
873  static_cast<InlineFlowBox *>(curr)->shrinkBoxesWithNoTextChildren(topPos, bottomPos);
874  }
875  }
876 
877  // See if we have text children. If not, then we need to shrink ourselves to fit on the line.
878  if (!hasTextDescendant()) {
879  if (yPos() < topPos) {
880  setYPos(topPos);
881  }
882  if (yPos() + height() > bottomPos) {
883  setHeight(bottomPos - yPos());
884  }
885  if (baseline() > height()) {
886  setBaseline(height());
887  }
888  }
889 }
890 
891 bool InlineFlowBox::nodeAtPoint(RenderObject::NodeInfo &i, int x, int y, int tx, int ty)
892 {
893  // Check children first.
894  for (InlineBox *curr = lastChild(); curr; curr = curr->prevOnLine()) {
895  if (!curr->object()->layer() && curr->nodeAtPoint(i, x, y, tx, ty)) {
896  object()->setInnerNode(i);
897  return true;
898  }
899  }
900 
901  // Now check ourselves.
902  QRect rect(tx + m_x, ty + m_y, m_width, m_height);
903  if (object()->style()->visibility() == VISIBLE && rect.contains(x, y)) {
904  object()->setInnerNode(i);
905  return true;
906  }
907 
908  return false;
909 }
910 
911 void InlineFlowBox::paint(RenderObject::PaintInfo &i, int tx, int ty)
912 {
913  bool intersectsDamageRect = true;
914  int xPos = tx + m_x - object()->maximalOutlineSize(i.phase);
915  int w = width() + 2 * object()->maximalOutlineSize(i.phase);
916  if ((xPos >= i.r.x() + i.r.width()) || (xPos + w <= i.r.x())) {
917  intersectsDamageRect = false;
918  }
919 
920  if (intersectsDamageRect) {
921  if (i.phase == PaintActionOutline) {
922  // Add ourselves to the paint info struct's list of inlines that need to paint their
923  // outlines.
924  if (object()->style()->visibility() == VISIBLE && object()->style()->outlineWidth() > 0 &&
925  !object()->isInlineContinuation() && !isRootInlineBox()) {
926  if (!i.outlineObjects) {
927  i.outlineObjects = new QList<RenderFlow *>;
928  }
929  i.outlineObjects->append(static_cast<RenderFlow *>(object()));
930  }
931  } else {
932  // 1. Paint our background and border.
933  paintBackgroundAndBorder(i, tx, ty);
934 
935  // 2. Paint our underline and overline.
936  paintDecorations(i, tx, ty, false);
937  }
938  }
939 
940  // 3. Paint our children.
941  for (InlineBox *curr = firstChild(); curr; curr = curr->nextOnLine()) {
942  if (!curr->object()->layer()) {
943  curr->paint(i, tx, ty);
944  }
945  }
946 
947  // 4. Paint our strike-through
948  if (intersectsDamageRect && i.phase != PaintActionOutline) {
949  paintDecorations(i, tx, ty, true);
950  }
951 }
952 
953 void InlineFlowBox::paintAllBackgrounds(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer,
954  QRect clipr, int _tx, int _ty, int w, int h)
955 {
956  if (!bgLayer) {
957  return;
958  }
959  paintAllBackgrounds(p, c, bgLayer->next(), clipr, _tx, _ty, w, h);
960  paintOneBackground(p, c, bgLayer, clipr, _tx, _ty, w, h);
961 }
962 
963 void InlineFlowBox::paintOneBackground(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer,
964  QRect clipr, int _tx, int _ty, int w, int h)
965 {
966  CachedImage *bg = bgLayer->backgroundImage();
967  bool hasBackgroundImage = bg && bg->isComplete() &&
968  !bg->isTransparent() && !bg->isErrorImage();
969  if (!hasBackgroundImage || (!prevLineBox() && !nextLineBox()) || !parent())
970  object()->paintBackgroundExtended(p, c, bgLayer, clipr, _tx, _ty, w, h, borderLeft(), borderRight(), paddingLeft(), paddingRight(),
971  object()->borderTop(), object()->borderBottom(), object()->paddingTop(), object()->paddingBottom());
972  else {
973  // We have a background image that spans multiple lines.
974  // We need to adjust _tx and _ty by the width of all previous lines.
975  // Think of background painting on inlines as though you had one long line, a single continuous
976  // strip. Even though that strip has been broken up across multiple lines, you still paint it
977  // as though you had one single line. This means each line has to pick up the background where
978  // the previous line left off.
979  // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
980  // but it isn't even clear how this should work at all.
981  int xOffsetOnLine = 0;
982  for (InlineRunBox *curr = prevLineBox(); curr; curr = curr->prevLineBox()) {
983  xOffsetOnLine += curr->width();
984  }
985  int startX = _tx - xOffsetOnLine;
986  int totalWidth = xOffsetOnLine;
987  for (InlineRunBox *curr = this; curr; curr = curr->nextLineBox()) {
988  totalWidth += curr->width();
989  }
990  p->save();
991  p->setClipRect(QRect(_tx, _ty, width(), height()));
992  object()->paintBackgroundExtended(p, c, bgLayer, clipr, startX, _ty,
993  totalWidth, h, borderLeft(), borderRight(), paddingLeft(), paddingRight(),
994  object()->borderTop(), object()->borderBottom(), object()->paddingTop(), object()->paddingBottom());
995  p->restore();
996  }
997 }
998 
999 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 
int ascent() const const
QString & fill(QChar ch, int size)
const QFont & font() const const
This file is part of the HTML rendering engine for KDE.
void save()
MESSAGECORE_EXPORT KMime::Content * next(KMime::Content *node, bool allowChildren=true)
int descent() const const
LeftToRight
a cached image
Definition: loader.h:359
QColor color() const const
void setFont(const QFont &font)
void append(const T &value)
void setPen(const QColor &color)
RenderObject * container() const
returns the object containing this one.
void setWidth(int width)
MESSAGECORE_EXPORT KMime::Content * firstChild(const KMime::Content *node)
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
int leading() const const
all geometry managing stuff is only in the block elements.
Definition: render_flow.h:44
void restore()
This library provides a full-featured HTML parser and widget.
void setClipRect(const QRectF &rectangle, Qt::ClipOperation operation)
void setX(int x)
void setY(int y)
void setHeight(int height)
int height() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
Base Class for all rendering tree objects.
int lineWidth() const const
DOMStringImpl * implementation() const
Definition: dom_string.h:145
const QPen & pen() const const
int lineSpacing() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 16 2021 22:48:00 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.