KHtml

render_inline.cpp
1 /*
2  * This file is part of the render object implementation for KHTML.
3  *
4  * Copyright (C) 1999-2003 Lars Knoll ([email protected])
5  * (C) 1999-2003 Antti Koivisto ([email protected])
6  * (C) 2002-2003 Dirk Mueller ([email protected])
7  * (C) 2003 Apple Computer, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB. If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25 
26 #include "render_inline.h"
27 
28 #include "render_arena.h"
29 #include "render_block.h"
30 #include "rendering/render_position.h"
31 
32 #include <xml/dom_docimpl.h>
33 
34 using namespace khtml;
35 
36 void RenderInline::setStyle(RenderStyle *_style)
37 {
38  RenderFlow::setStyle(_style);
39  setInline(true);
40 
41  // Ensure that all of the split inlines pick up the new style. We
42  // only do this if we're an inline, since we don't want to propagate
43  // a block's style to the other inlines.
44  // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before
45  // and after the block share the same style, but the block doesn't
46  // need to pass its style on to anyone else.
47  RenderFlow *currCont = continuation();
48  while (currCont) {
49  if (currCont->isInline()) {
50  RenderFlow *nextCont = currCont->continuation();
51  currCont->setContinuation(nullptr);
52  currCont->setStyle(style());
53  currCont->setContinuation(nextCont);
54  }
55  currCont = currCont->continuation();
56  }
57 
58  if (attached()) {
59  // Update replaced content
60  updateReplacedContent();
61  // Update pseudos for ::before and ::after
62  updatePseudoChildren();
63  }
64 }
65 
66 // Attach handles initial setStyle that requires parent nodes
67 void RenderInline::attach()
68 {
69  RenderFlow::attach();
70 
71  updateReplacedContent();
72  updatePseudoChildren();
73 }
74 
75 bool RenderInline::isInlineContinuation() const
76 {
77  return m_isContinuation;
78 }
79 
80 void RenderInline::addChildToFlow(RenderObject *newChild, RenderObject *beforeChild)
81 {
82  // Make sure we don't append things after :after-generated content if we have it.
83  if (!beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER) {
84  beforeChild = lastChild();
85  }
86 
87  if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) {
88  // We are placing a block inside an inline. We have to perform a split of this
89  // inline into continuations. This involves creating an anonymous block box to hold
90  // |newChild|. We then make that block box a continuation of this inline. We take all of
91  // the children after |beforeChild| and put them in a clone of this object.
92 
93  RenderBlock *newBox = createAnonymousBlock();
94  RenderFlow *oldContinuation = continuation();
95  setContinuation(newBox);
96 
97  splitFlow(beforeChild, newBox, newChild, oldContinuation);
98  return;
99  }
100 
101  RenderBox::addChild(newChild, beforeChild);
102 
103  newChild->setNeedsLayoutAndMinMaxRecalc();
104 }
105 
106 RenderInline *RenderInline::cloneInline(RenderFlow *src)
107 {
108  RenderInline *o = new(src->renderArena()) RenderInline(src->element());
109  o->m_isContinuation = true;
110  o->setStyle(src->style());
111  return o;
112 }
113 
114 void RenderInline::splitInlines(RenderBlock *fromBlock, RenderBlock *toBlock,
115  RenderBlock *middleBlock,
116  RenderObject *beforeChild, RenderFlow *oldCont)
117 {
118  // Create a clone of this inline.
119  RenderInline *clone = cloneInline(this);
120  clone->setContinuation(oldCont);
121 
122  // Now take all of the children from beforeChild to the end and remove
123  // then from |this| and place them in the clone.
124  RenderObject *o = beforeChild;
125  while (o) {
126  RenderObject *tmp = o;
127  o = tmp->nextSibling();
128  clone->addChildToFlow(removeChildNode(tmp), nullptr);
129  tmp->setNeedsLayoutAndMinMaxRecalc();
130  }
131 
132  // Hook |clone| up as the continuation of the middle block.
133  middleBlock->setContinuation(clone);
134 
135  // We have been reparented and are now under the fromBlock. We need
136  // to walk up our inline parent chain until we hit the containing block.
137  // Once we hit the containing block we're done.
138  RenderFlow *curr = static_cast<RenderFlow *>(parent());
139  RenderFlow *currChild = this;
140  while (curr && curr != fromBlock) {
141  // Create a new clone.
142  RenderInline *cloneChild = clone;
143  clone = cloneInline(curr);
144 
145  // Insert our child clone as the first child.
146  clone->addChildToFlow(cloneChild, nullptr);
147 
148  // Hook the clone up as a continuation of |curr|.
149  RenderFlow *oldCont = curr->continuation();
150  curr->setContinuation(clone);
151  clone->setContinuation(oldCont);
152 
153  // Now we need to take all of the children starting from the first child
154  // *after* currChild and append them all to the clone.
155  o = currChild->nextSibling();
156  while (o) {
157  RenderObject *tmp = o;
158  o = tmp->nextSibling();
159  clone->appendChildNode(curr->removeChildNode(tmp));
160  tmp->setNeedsLayoutAndMinMaxRecalc();
161  }
162 
163  // Keep walking up the chain.
164  currChild = curr;
165  curr = static_cast<RenderFlow *>(curr->parent());
166  }
167 
168  // Now we are at the block level. We need to put the clone into the toBlock.
169  toBlock->appendChildNode(clone);
170 
171  // Now take all the children after currChild and remove them from the fromBlock
172  // and put them in the toBlock.
173  o = currChild->nextSibling();
174  while (o) {
175  RenderObject *tmp = o;
176  o = tmp->nextSibling();
177  toBlock->appendChildNode(fromBlock->removeChildNode(tmp));
178  }
179 }
180 
181 void RenderInline::splitFlow(RenderObject *beforeChild, RenderBlock *newBlockBox,
182  RenderObject *newChild, RenderFlow *oldCont)
183 {
184  RenderBlock *pre = nullptr;
185  RenderBlock *block = containingBlock();
186  bool madeNewBeforeBlock = false;
187 
188  // Delete our line boxes before we do the inline split into continuations.
189  block->deleteLineBoxTree();
190 
191  if (block->isAnonymousBlock()) {
192  // We can reuse this block and make it the preBlock of the next continuation.
193  pre = block;
194  block = block->containingBlock();
195  } else {
196  // No anonymous block available for use. Make one.
197  pre = block->createAnonymousBlock();
198  madeNewBeforeBlock = true;
199  }
200 
201  RenderBlock *post = block->createAnonymousBlock();
202 
203  RenderObject *boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling();
204  if (madeNewBeforeBlock) {
205  block->insertChildNode(pre, boxFirst);
206  }
207  block->insertChildNode(newBlockBox, boxFirst);
208  block->insertChildNode(post, boxFirst);
209  block->setChildrenInline(false);
210 
211  if (madeNewBeforeBlock) {
212  RenderObject *o = boxFirst;
213  while (o) {
214  RenderObject *no = o;
215  o = no->nextSibling();
216  pre->appendChildNode(block->removeChildNode(no));
217  no->setNeedsLayoutAndMinMaxRecalc();
218  }
219  }
220 
221  splitInlines(pre, post, newBlockBox, beforeChild, oldCont);
222 
223  // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting
224  // time in makeChildrenNonInline by just setting this explicitly up front.
225  newBlockBox->setChildrenInline(false);
226 
227  // We don't just call addChild, since it would pass things off to the
228  // continuation, so we call addChildToFlow explicitly instead. We delayed
229  // adding the newChild until now so that the |newBlockBox| would be fully
230  // connected, thus allowing newChild access to a renderArena should it need
231  // to wrap itself in additional boxes (e.g., table construction).
232  newBlockBox->addChildToFlow(newChild, nullptr);
233 
234  // XXXdwh is any of this even necessary? I don't think it is.
235  pre->close();
236  pre->setPos(0, -500000);
237  pre->setNeedsLayoutAndMinMaxRecalc();
238  newBlockBox->close();
239  newBlockBox->setPos(0, -500000);
240  newBlockBox->setNeedsLayout(true);
241  post->close();
242  post->setPos(0, -500000);
243  post->setNeedsLayoutAndMinMaxRecalc();
244 
245  updatePseudoChildren();
246 
247  block->setNeedsLayoutAndMinMaxRecalc();
248 }
249 
250 void RenderInline::paint(PaintInfo &i, int _tx, int _ty)
251 {
252  paintLines(i, _tx, _ty);
253 }
254 
255 /**
256  * Appends the given coordinate-pair to the point-array if it is not
257  * equal to the last element.
258  * @param pointArray point-array
259  * @param pnt point to append
260  * @return \c true if \c pnt has actually been appended
261  */
262 inline static bool appendIfNew(QVector<QPoint> &pointArray, const QPoint &pnt)
263 {
264 // if (!pointArray.isEmpty()) qCDebug(KHTML_LOG) << "appifnew: " << pointArray.back() << " == " << pnt << ": " << (pointArray.back() == pnt);
265 // else qCDebug(KHTML_LOG) << "appifnew: " << pnt << " (unconditional)";
266  if (!pointArray.isEmpty() && pointArray.back() == pnt) {
267  return false;
268  }
269  pointArray.append(pnt);
270  return true;
271 }
272 
273 /**
274  * Does spike-reduction on the given point-array's stack-top.
275  *
276  * Spikes are path segments of which one goes forward, and the sucessor
277  * goes backward on the predecessor's segment:
278  *
279  * 2 0 1
280  * x------x<-----x
281  * (0 is stack-top in point-array)
282  *
283  * This will be reduced to
284  * 1 0
285  * x------x
286  *
287  * Preconditions:
288  * - No other spikes exist in the whole point-array except at most
289  * one at the end
290  * - No two succeeding points are ever equal
291  * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
292  * true
293  * - No such spike exists where 2 is situated between 0 and 1.
294  *
295  * Postcondition:
296  * - No spikes exist in the whole point-array
297  *
298  * If no spike is found, the point-array is left unchanged.
299  * @return \c true if an actual reduction was done
300  */
301 inline static bool reduceSpike(QVector<QPoint> &pointArray)
302 {
303  if (pointArray.size() < 3) {
304  return false;
305  }
306  QVector<QPoint>::Iterator it = pointArray.end();
307  QPoint p0 = *--it;
308  QPoint p1 = *--it;
309  QPoint p2 = *--it;
310 
311  bool elide = false;
312 
313  if ((p0.x() == p1.x() && p1.x() == p2.x()
314  && ((p1.y() < p0.y() && p0.y() < p2.y())
315  || (p2.y() < p0.y() && p0.y() < p1.y())
316  || (p1.y() < p2.y() && p2.y() < p0.y())
317  || (p0.y() < p2.y() && p2.y() < p1.y())
318  || (elide = p2.y() == p0.y() && p0.y() < p1.y())
319  || (elide = p1.y() < p0.y() && p0.y() == p2.y())))
320  || (p0.y() == p1.y() && p1.y() == p2.y()
321  && ((p1.x() < p0.x() && p0.x() < p2.x())
322  || (p2.x() < p0.x() && p0.x() < p1.x())
323  || (p1.x() < p2.x() && p2.x() < p0.x())
324  || (p0.x() < p2.x() && p2.x() < p1.x())
325  || (elide = p2.x() == p0.x() && p0.x() < p1.x())
326  || (elide = p1.x() < p0.x() && p0.x() == p2.x())))) {
327 // qCDebug(KHTML_LOG) << "spikered p2" << (elide ? " (elide)" : "") << ": " << p2 << " p1: " << p1 << " p0: " << p0;
328  pointArray.pop_back(); pointArray.pop_back();
329  if (!elide) {
330  pointArray.push_back(p0);
331  }
332  return true;
333  }
334  return false;
335 }
336 
337 /**
338  * Reduces segment separators.
339  *
340  * A segment separator separates a segment into two segments, thus causing
341  * two adjacent segment with the same orientation.
342  *
343  * 2 1 0
344  * x-------x---->x
345  * (0 means stack-top)
346  *
347  * Here, 1 is a segment separator. As segment separators not only make
348  * the line drawing algorithm inefficient, but also make the spike-reduction
349  * fail, they must be eliminated:
350  *
351  * 1 0
352  * x------------>x
353  *
354  * Preconditions:
355  * - No other segment separators exist in the whole point-array except
356  * at most one at the end
357  * - No two succeeding points are ever equal
358  * - For each two succeeding points either p1.x == p2.x or p1.y == p2.y holds
359  * true
360  * - No such spike exists where 2 is situated between 0 and 1.
361  *
362  * Postcondition:
363  * - No segment separators exist in the whole point-array
364  *
365  * If no segment separator is found at the end of the point-array, it is
366  * left unchanged.
367  * @return \c true if a segment separator was actually reduced.
368  */
369 inline static bool reduceSegmentSeparator(QVector<QPoint> &pointArray)
370 {
371  if (pointArray.size() < 3) {
372  return false;
373  }
374  QVector<QPoint>::Iterator it = pointArray.end();
375  QPoint p0 = *--it;
376  QPoint p1 = *--it;
377  QPoint p2 = *--it;
378 // qCDebug(KHTML_LOG) << "checking p2: " << p2 << " p1: " << p1 << " p0: " << p0;
379 
380  if ((p0.x() == p1.x() && p1.x() == p2.x()
381  && ((p2.y() < p1.y() && p1.y() < p0.y())
382  || (p0.y() < p1.y() && p1.y() < p2.y())))
383  || (p0.y() == p1.y() && p1.y() == p2.y()
384  && ((p2.x() < p1.x() && p1.x() < p0.x())
385  || (p0.x() < p1.x() && p1.x() < p2.x())))) {
386 // qCDebug(KHTML_LOG) << "segred p2: " << p2 << " p1: " << p1 << " p0: " << p0;
387  pointArray.pop_back(); pointArray.pop_back();
388  pointArray.push_back(p0);
389  return true;
390  }
391  return false;
392 }
393 
394 /**
395  * Appends the given point to the point-array, doing necessary reductions to
396  * produce a path without spikes and segment separators.
397  */
398 static void appendPoint(QVector<QPoint> &pointArray, const QPoint &pnt)
399 {
400  if (!appendIfNew(pointArray, pnt)) {
401  return;
402  }
403 // qCDebug(KHTML_LOG) << "appendPoint: appended " << pnt;
404  reduceSegmentSeparator(pointArray)
405  || reduceSpike(pointArray);
406 }
407 
408 /**
409  * Traverses the horizontal inline boxes and appends the point coordinates to
410  * the given array.
411  * @param box inline box
412  * @param pointArray array collecting coordinates
413  * @param bottom \c true, collect bottom coordinates, \c false, collect top
414  * coordinates.
415  * @param limit lower limit that an y-coordinate must at least reach. Note
416  * that limit designates the highest y-coordinate for \c bottom, and
417  * the lowest for !\c bottom.
418  */
419 static void collectHorizontalBoxCoordinates(InlineBox *box,
420  QVector<QPoint> &pointArray,
421  bool bottom, int offset, int limit = -500000)
422 {
423 // qCDebug(KHTML_LOG) << "collectHorizontalBoxCoordinates: ";
424  offset = bottom ? offset : -offset;
425  int y = box->yPos() + bottom * box->height() + offset;
426  if (limit != -500000 && (bottom ? y < limit : y > limit)) {
427  y = limit;
428  }
429  int x = box->xPos() + bottom * box->width() + offset;
430  QPoint newPnt(x, y);
431  // Add intersection point if point-array not empty.
432  if (!pointArray.isEmpty()) {
433  QPoint lastPnt = pointArray.back();
434  QPoint insPnt(newPnt.x(), lastPnt.y());
435 
436  if (offset && ((bottom && lastPnt.y() > y) || (!bottom && lastPnt.y() < y))) {
437  insPnt.rx() = lastPnt.x();
438  insPnt.ry() = y;
439  }
440 // qCDebug(KHTML_LOG) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt);
441  appendPoint(pointArray, insPnt);
442  }
443  // Insert starting point of box
444  appendPoint(pointArray, newPnt);
445 
446  newPnt.rx() += (bottom ? -box->width() : box->width()) - 2 * offset;
447 
448  if (box->isInlineFlowBox()) {
449  InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box);
450  for (InlineBox *b = bottom ? flowBox->lastChild() : flowBox->firstChild(); b; b = bottom ? b->prevOnLine() : b->nextOnLine()) {
451  // Don't let boxes smaller than this flow box' height influence
452  // the vertical position of the outline if they have a different
453  // x-coordinate
454  int l2;
455  if (b->xPos() != box->xPos() && b->xPos() + b->width() != box->xPos() + box->width()) {
456  l2 = y;
457  } else {
458  l2 = limit;
459  }
460  collectHorizontalBoxCoordinates(b, pointArray, bottom, qAbs(offset), l2);
461  }
462 
463  // Add intersection point if flow box contained any children
464  if (flowBox->firstChild()) {
465  QPoint lastPnt = pointArray.back();
466  QPoint insPnt(lastPnt.x(), newPnt.y());
467 // qCDebug(KHTML_LOG) << "right: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt);
468  appendPoint(pointArray, insPnt);
469  }
470  }
471 
472  // Insert ending point of box
473  appendPoint(pointArray, newPnt);
474 
475 // qCDebug(KHTML_LOG) << "collectHorizontalBoxCoordinates: " << "ende";
476 }
477 
478 /**
479  * Checks whether the given line box' extents and the following line box'
480  * extents are disjount (i. e. do not share the same x-coordinate range).
481  * @param line line box
482  * @param toBegin \c true, compare with preceding line box, \c false, with
483  * succeeding
484  * @return \c true if this and the next box are disjoint
485  */
486 inline static bool lineBoxesDisjoint(InlineRunBox *line, int offset, bool toBegin)
487 {
488  InlineRunBox *next = toBegin ? line->prevLineBox() : line->nextLineBox();
489  return !next || next->xPos() + next->width() + 2 * offset < line->xPos()
490  || next->xPos() > line->xPos() + line->width() + 2 * offset;
491 }
492 
493 /**
494  * Traverses the vertical outer borders of the given render flow's line
495  * boxes and appends the point coordinates to the given point array.
496  * @param line line box to begin traversal
497  * @param pointArray point array
498  * @param left \c true, traverse the left vertical coordinates,
499  * \c false, traverse the right vertical coordinates.
500  * @param lastline if not 0, returns the pointer to the last line box traversed
501  */
502 static void collectVerticalBoxCoordinates(InlineRunBox *line,
503  QVector<QPoint> &pointArray,
504  bool left, int offset, InlineRunBox **lastline = nullptr)
505 {
506  InlineRunBox *last = nullptr;
507  offset = left ? -offset : offset;
508  for (InlineRunBox *curr = line; curr && !last; curr = left ? curr->prevLineBox() : curr->nextLineBox()) {
509  InlineBox *root = curr;
510 
511  bool isLast = lineBoxesDisjoint(curr, qAbs(offset), left);
512  if (isLast) {
513  last = curr;
514  }
515 
516  if (root != line && !isLast)
517  while (root->parent()) {
518  root = root->parent();
519  }
520  QPoint newPnt(curr->xPos() + !left * curr->width() + offset,
521  (left ? root->topOverflow() : root->bottomOverflow()) + offset);
522  if (!pointArray.isEmpty()) {
523  QPoint lastPnt = pointArray.back();
524  if (newPnt.x() > lastPnt.x() && !left) {
525  pointArray.back().setY(qMin(lastPnt.y(), root->topOverflow() - offset));
526  } else if (newPnt.x() < lastPnt.x() && left) {
527  pointArray.back().setY(qMax(lastPnt.y(), root->bottomOverflow() + offset));
528  }
529  QPoint insPnt(newPnt.x(), pointArray.back().y());
530 // qCDebug(KHTML_LOG) << "left: " << lastPnt << " == " << insPnt << ": " << (insPnt == lastPnt);
531  appendPoint(pointArray, insPnt);
532  }
533  appendPoint(pointArray, newPnt);
534  }
535  if (lastline) {
536  *lastline = last;
537  }
538 }
539 
540 /**
541  * Links up the end of the given point-array such that the starting point
542  * is not a segment separator.
543  *
544  * To achieve this, improper points are removed from the beginning of
545  * the point-array (by changing the array's starting iterator), and
546  * proper ones appended to the point-array's back.
547  *
548  * X---------------+ X------------------+
549  * ^ | ^ |
550  * | | ==> | |
551  * +..... ...+ +..... ...+
552  *
553  * +----->X--------+ +----------------->X
554  * | | ==> | |
555  * +..... ...+ +..... ...+
556  *
557  * ^X
558  * ||
559  * +-----++--------+ +----------------->X
560  * | | ==> | |
561  * +..... ...+ +..... ...+
562  *
563  * @param pointArray point-array
564  * @return actual begin of point array
565  */
566 static QPoint *linkEndToBegin(QVector<QPoint> &pointArray)
567 {
568  uint index = 0;
569  // ### BUG: outlines with zero width aren't treated correctly
570  // this is not the right fix
571  if (pointArray.size() < 3) {
572  return pointArray.data();
573  }
574 
575  // if first and last points match, ignore the last one.
576  bool linkup = false; QPoint linkupPnt;
577  if (pointArray.front() == pointArray.back()) {
578  linkupPnt = pointArray.back();
579  pointArray.pop_back();
580  linkup = true;
581  }
582 
583  const QPoint *it = pointArray.data() + index;
584  QPoint pfirst = *it;
585  QPoint pnext = *++it;
586  QPoint plast = pointArray.back();
587 // qCDebug(KHTML_LOG) << "linkcheck plast: " << plast << " pfirst: " << pfirst << " pnext: " << pnext;
588 
589  if ((plast.x() == pfirst.x() && pfirst.x() == pnext.x())
590  || (plast.y() == pfirst.y() && pfirst.y() == pnext.y())) {
591 
592  ++index;
593  appendPoint(pointArray, pfirst); // ### do we really need this point?
594  appendPoint(pointArray, pnext);
595  // ended up at a segment separator? move one point forward
596  if (plast == pnext) {
597  ++index;
598  appendPoint(pointArray, *++it);
599  }
600  } else if (linkup) {
601  pointArray.push_back(linkupPnt);
602  }
603  return pointArray.data() + index;
604 }
605 
606 // assumes clock-wise orientation
607 static RenderObject::BorderSide borderSide(const QPoint &first,
608  const QPoint &second)
609 {
610  if (second.x() > first.x()) {
611  return RenderObject::BSTop;
612  } else if (second.x() < first.x()) {
613  return RenderObject::BSBottom;
614  } else if (second.y() > first.y()) {
615  return RenderObject::BSRight;
616  } else { // second.y() < first.y()
617  return RenderObject::BSLeft;
618  }
619 }
620 
621 void RenderInline::paintOutlines(QPainter *p, int _tx, int _ty)
622 {
623  if (style()->outlineWidth() == 0 || style()->outlineStyle() <= BHIDDEN) {
624  return;
625  }
626  int offset = style()->outlineOffset();
627 
628  // We may have to draw more than one outline path as they may be
629  // disjoint.
630  for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
631  QVector<QPoint> path;
632 
633  // collect topmost outline
634  collectHorizontalBoxCoordinates(curr, path, false, offset);
635  // collect right outline
636  collectVerticalBoxCoordinates(curr, path, false, offset, &curr);
637  // collect bottommost outline
638  collectHorizontalBoxCoordinates(curr, path, true, offset);
639  // collect left outline
640  collectVerticalBoxCoordinates(curr, path, true, offset);
641 
642  if (path.size() < 3) {
643  continue;
644  }
645 
646  const QPoint *begin = linkEndToBegin(path);
647 
648  // initial borderside and direction values
649  QPoint pstart = *begin;
650  QPoint pprev = *(path.end() - 2);
651  RenderObject::BorderSide bs = borderSide(pprev, pstart);
652  QPoint diff = pstart - pprev;
653  int direction = diff.x() + diff.y();
654  RenderObject::BorderSide endingBS = borderSide(*begin, *(begin + 1));
655 
656  // paint the outline
657  paintOutlinePath(p, _tx, _ty, begin, path.data() + path.size(),
658  bs, direction, endingBS);
659  }
660 }
661 
662 template<class T> inline void kSwap(T &a1, T &a2)
663 {
664  T tmp = a2;
665  a2 = a1;
666  a1 = tmp;
667 }
668 
669 enum BSOrientation { BSHorizontal, BSVertical };
670 
671 /**
672  * Returns the orientation of the given border side.
673  */
674 inline BSOrientation bsOrientation(RenderObject::BorderSide bs)
675 {
676  switch (bs) {
677  case RenderObject::BSTop:
678  case RenderObject::BSBottom:
679  return BSHorizontal;
680  case RenderObject::BSLeft:
681  case RenderObject::BSRight:
682  return BSVertical;
683  }
684  return BSHorizontal; // make gcc happy (sigh)
685 }
686 
687 /**
688  * Determines the new border side by evaluating the new direction as determined
689  * by the given coordinates, the old border side, and the relative direction.
690  *
691  * The relative direction specifies whether the old border side meets with the
692  * straight given by the coordinates from below/right (negative), or
693  * above/left (positive).
694  */
695 inline RenderObject::BorderSide newBorderSide(RenderObject::BorderSide oldBS, int direction, const QPoint &last, const QPoint &cur)
696 {
697  bool below = direction < 0;
698  if (last.x() == cur.x()) { // new segment is vertical
699  bool t = oldBS == RenderObject::BSTop;
700  bool b = oldBS == RenderObject::BSBottom;
701  if ((t || b) && last.y() != cur.y())
702  return (cur.y() < last.y()) ^ ((t && below) || (b && !below))
703  ? RenderObject::BSLeft : RenderObject::BSRight;
704  } else { /*if (last.y() == cur.y())*/ // new segment is horizontal
705  bool l = oldBS == RenderObject::BSLeft;
706  bool r = oldBS == RenderObject::BSRight;
707  if ((l || r) && last.x() != cur.x())
708  return (cur.x() < last.x()) ^ ((l && below) || (r && !below))
709  ? RenderObject::BSTop : RenderObject::BSBottom;
710  }
711  return oldBS; // same direction
712 }
713 
714 /**
715  * Draws an outline segment between the given two points.
716  * @param o render object
717  * @param p painter
718  * @param tx absolute x-coordinate of containing block
719  * @param ty absolute y-coordinate of containing block
720  * @param p1 starting point
721  * @param p2 end point
722  * @param prevBS border side of previous segment
723  * @param curBS border side of this segment
724  * @param nextBS border side of next segment
725  */
726 static void paintOutlineSegment(RenderObject *o, QPainter *p, int tx, int ty,
727  const QPoint &p1, const QPoint &p2,
728  RenderObject::BorderSide prevBS,
729  RenderObject::BorderSide curBS,
730  RenderObject::BorderSide nextBS)
731 {
732  int ow = o->style()->outlineWidth();
733  EBorderStyle os = o->style()->outlineStyle();
734  QColor oc = o->style()->outlineColor();
735 
736  int x1 = tx + p1.x();
737  int y1 = ty + p1.y();
738  int x2 = tx + p2.x();
739  int y2 = ty + p2.y();
740  if (x1 > x2) {
741  kSwap(x1, x2);
742  if (bsOrientation(curBS) == BSHorizontal) {
743  kSwap(prevBS, nextBS);
744  }
745  }
746  if (y1 > y2) {
747  kSwap(y1, y2);
748  if (bsOrientation(curBS) == BSVertical) {
749  kSwap(prevBS, nextBS);
750  }
751  }
752 
753 // qCDebug(KHTML_LOG) << "segment(" << x1 << "," << y1 << ") - (" << x2 << "," << y2 << ")";
754  /* p->setPen(Qt::gray);
755  p->drawLine(x1,y1,x2,y2);*/
756  switch (curBS) {
757  case RenderObject::BSLeft:
758  case RenderObject::BSRight:
759  /* p->setPen(QColor("#ffe4dd"));
760  p->drawLine(
761  x1 - (curBS == RenderObject::BSLeft ? ow : 0),
762  y1 - (prevBS == RenderObject::BSTop ? ow : 0),
763  x2 + (curBS == RenderObject::BSRight ? ow : 0),
764  y2 + (nextBS == RenderObject::BSBottom ? ow : 0)
765  );*/
766  o->drawBorder(p,
767  x1 - (curBS == RenderObject::BSLeft ? ow : 0),
768  y1 - (prevBS == RenderObject::BSTop ? ow : 0),
769  x2 + (curBS == RenderObject::BSRight ? ow : 0),
770  y2 + (nextBS == RenderObject::BSBottom ? ow : 0),
771  curBS, oc, o->style()->color(), os,
772  prevBS == RenderObject::BSTop ? ow
773  : prevBS == RenderObject::BSBottom ? -ow : 0,
774  nextBS == RenderObject::BSTop ? -ow
775  : nextBS == RenderObject::BSBottom ? ow : 0,
776  true);
777  break;
778  case RenderObject::BSBottom:
779  case RenderObject::BSTop:
780 // qCDebug(KHTML_LOG) << "BSTop/BSBottom: prevBS " << prevBS << " curBS " << curBS << " nextBS " << nextBS;
781  o->drawBorder(p,
782  x1 - (prevBS == RenderObject::BSLeft ? ow : 0),
783  y1 - (curBS == RenderObject::BSTop ? ow : 0),
784  x2 + (nextBS == RenderObject::BSRight ? ow : 0),
785  y2 + (curBS == RenderObject::BSBottom ? ow : 0),
786  curBS, oc, o->style()->color(), os,
787  prevBS == RenderObject::BSLeft ? ow
788  : prevBS == RenderObject::BSRight ? -ow : 0,
789  nextBS == RenderObject::BSLeft ? -ow
790  : nextBS == RenderObject::BSRight ? ow : 0,
791  true);
792  break;
793  }
794 }
795 
796 void RenderInline::paintOutlinePath(QPainter *p, int tx, int ty, const QPoint *begin, const QPoint *end, BorderSide bs, int direction, BorderSide endingBS)
797 {
798  int ow = style()->outlineWidth();
799  if (ow == 0 || m_isContinuation) { // Continuations get painted by the original inline.
800  return;
801  }
802 
803  QPoint last = *begin;
804  BorderSide lastBS = bs;
805  Q_ASSERT(begin != end);
806  ++begin;
807 
808 // qCDebug(KHTML_LOG) << "last: " << last;
809 
810  bs = newBorderSide(bs, direction, last, *begin);
811 // qCDebug(KHTML_LOG) << "newBorderSide: " << lastBS << " " << direction << "d " << last << " - " << *begin << " => " << bs;
812 
813  for (const QPoint *it = begin; it != end; ++it) {
814  QPoint cur = *it;
815 // qCDebug(KHTML_LOG) << "cur: " << cur;
816  BorderSide nextBS;
817  if (it + 1 != end) {
818  QPoint diff = cur - last;
819  direction = diff.x() + diff.y();
820  nextBS = newBorderSide(bs, direction, cur, *(it + 1));
821 // qCDebug(KHTML_LOG) << "newBorderSide*: " << bs << " " << direction << "d " << cur << " - " << *(it + 1) << " => " << nextBS;
822  } else {
823  nextBS = endingBS;
824  }
825 
826  Q_ASSERT(bsOrientation(bs) != bsOrientation(nextBS));
827  paintOutlineSegment(this, p, tx, ty, last, cur,
828  lastBS, bs, nextBS);
829  lastBS = bs;
830  last = cur;
831  bs = nextBS;
832  }
833 
834 }
835 
836 void RenderInline::calcMinMaxWidth()
837 {
838  KHTMLAssert(!minMaxKnown());
839 
840 #ifdef DEBUG_LAYOUT
841  // qCDebug(KHTML_LOG) << renderName() << "(RenderInline)::calcMinMaxWidth() this=" << this;
842 #endif
843 
844  // Irrelevant, since some enclosing block will actually measure us and our children.
845  m_minWidth = 0;
846  m_maxWidth = 0;
847 
848  setMinMaxKnown();
849 }
850 
851 short RenderInline::width() const
852 {
853  // Return the width of the minimal left side and the maximal right side.
854  short leftSide = 0;
855  short rightSide = 0;
856  for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
857  if (curr == firstLineBox() || curr->xPos() < leftSide) {
858  leftSide = curr->xPos();
859  }
860  if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide) {
861  rightSide = curr->xPos() + curr->width();
862  }
863  }
864 
865  return rightSide - leftSide;
866 }
867 
868 int RenderInline::height() const
869 {
870  int h = 0;
871  if (firstLineBox()) {
872  h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos();
873  }
874  return h;
875 }
876 
877 int RenderInline::offsetLeft() const
878 {
879  int x = RenderFlow::offsetLeft();
880  if (firstLineBox()) {
881  x += firstLineBox()->xPos();
882  }
883  return x;
884 }
885 
886 int RenderInline::offsetTop() const
887 {
888  int y = RenderFlow::offsetTop();
889  if (firstLineBox()) {
890  y += firstLineBox()->yPos();
891  }
892  return y;
893 }
894 
895 const char *RenderInline::renderName() const
896 {
897  if (isRelPositioned()) {
898  return "RenderInline (relative positioned)";
899  }
900  if (isAnonymous()) {
901  return "RenderInline (anonymous)";
902  }
903  return "RenderInline";
904 }
905 
906 bool RenderInline::nodeAtPoint(NodeInfo &info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside)
907 {
908 
909  // Check our line boxes if we're still not inside.
910  if (!inside) {
911  // See if we're inside one of our line boxes.
912  inside = hitTestLines(info, _x, _y, _tx, _ty, hitTestAction);
913  }
914 
915  if (inside && element() && style()->visibility() != HIDDEN) {
916  if (info.innerNode() && info.innerNode()->renderer() &&
917  !info.innerNode()->renderer()->isInline()) {
918  // Within the same layer, inlines are ALWAYS fully above blocks. Change inner node.
919  info.setInnerNode(element());
920 
921  // Clear everything else.
922  info.setInnerNonSharedNode(nullptr);
923  info.setURLElement(nullptr);
924  }
925 
926  if (!info.innerNode()) {
927  info.setInnerNode(element());
928  }
929 
930  if (!info.innerNonSharedNode()) {
931  info.setInnerNonSharedNode(element());
932  }
933  }
934 
935  return inside;
936 }
937 
938 RenderPosition RenderInline::positionForCoordinates(int x, int y)
939 {
940  for (RenderObject *c = continuation(); c; c = c->continuation()) {
941  if (c->isInline() || c->firstChild()) {
942  return c->positionForCoordinates(x, y);
943  }
944  }
945 
946  return RenderFlow::positionForCoordinates(x, y);
947 }
948 
949 void RenderInline::caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height) const
950 {
951  _x = -1;
952 
953  RenderBlock *cb = containingBlock();
954  bool rtl = cb->style()->direction() == RTL;
955  bool outsideEnd = flags & CFOutsideEnd;
956  // I need to explain that: outsideEnd contains a meaningful value if
957  // and only if flags & CFOutside is set. If it is not, then randomly
958  // either the first or the last line box is returned.
959  // This doesn't matter because the only case this can happen is on an
960  // empty inline element, whose first and last line boxes are actually
961  // the same.
962  InlineFlowBox *line = !outsideEnd ^ rtl ? firstLineBox() : lastLineBox();
963 
964  if (!line) { // umpf, handle "gracefully"
965  RenderFlow::caretPos(offset, flags, _x, _y, width, height);
966  return;
967  }
968 
969  _x = line->xPos();
970  width = 1; // ### regard CFOverride
971 
972  // Place caret outside the border
973  if (flags & CFOutside) {
974  RenderStyle *s = element() && element()->parent()
975  && element()->parent()->renderer()
976  ? element()->parent()->renderer()->style()
977  : style();
978  const QFontMetrics &fm = s->fontMetrics();
979  _y = line->yPos() + line->baseline() - fm.ascent();
980  height = fm.height();
981 
982  if (!outsideEnd ^ rtl) {
983  _x -= line->marginBorderPaddingLeft();
984  } else {
985  _x += line->width() + line->marginBorderPaddingRight();
986  }
987 
988  } else {
989  const QFontMetrics &fm = style()->fontMetrics();
990  _y = line->yPos() + line->baseline() - fm.ascent();
991  height = fm.height();
992  }
993 
994  int absx, absy;
995  if (cb && cb->absolutePosition(absx, absy)) {
996  //qCDebug(KHTML_LOG) << "absx=" << absx << " absy=" << absy;
997  _x += absx;
998  _y += absy;
999  } 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 
int ascent() const const
int & rx()
void append(const T &value)
This file is part of the HTML rendering engine for KDE.
MESSAGECORE_EXPORT KMime::Content * next(KMime::Content *node, bool allowChildren=true)
const QList< QKeySequence > & begin()
QVector::reference back()
int x() const const
int y() const const
T * data()
void pop_back()
all geometry managing stuff is only in the block elements.
Definition: render_flow.h:44
bool isEmpty() const const
int height() const const
T & front()
Base Class for all rendering tree objects.
void push_back(const T &value)
int size() const const
QVector::iterator end()
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 22:48:20 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.