KHtml

render_container.cpp
1 /**
2  * This file is part of the html renderer for KDE.
3  *
4  * Copyright (C) 2001-2003 Lars Knoll ([email protected])
5  * (C) 2001 Antti Koivisto ([email protected])
6  * (C) 2000-2003 Dirk Mueller ([email protected])
7  * (C) 2002-2007 Apple Computer, Inc.
8  * (C) 2007 Germain Garand ([email protected])
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB. If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  *
25  */
26 
27 //#define DEBUG_LAYOUT
28 
29 #include "rendering/render_container.h"
30 #include "rendering/render_table.h"
31 #include "rendering/render_text.h"
32 #include "rendering/render_image.h"
33 #include "rendering/render_canvas.h"
34 #include "rendering/render_generated.h"
35 #include "rendering/render_inline.h"
36 #include "rendering/render_layer.h"
37 #include "rendering/render_position.h"
38 #include "xml/dom_docimpl.h"
39 #include "xml/dom_position.h"
40 #include "css/css_valueimpl.h"
41 
42 #include "khtml_debug.h"
43 #include <assert.h>
44 #include <limits.h>
45 
46 using DOM::Position;
47 using namespace khtml;
48 
49 RenderContainer::RenderContainer(DOM::NodeImpl *node)
50  : RenderObject(node)
51 {
52  m_first = nullptr;
53  m_last = nullptr;
54 }
55 
56 void RenderContainer::addChild(RenderObject *newChild, RenderObject *beforeChild)
57 {
58 #ifdef DEBUG_LAYOUT
59  // qCDebug(KHTML_LOG) << this << ": " << renderName() << "(RenderObject)::addChild( " << newChild << ": " <<
60  // newChild->renderName() << ", " << (beforeChild ? beforeChild->renderName() : "0") << " )";
61 #endif
62  // protect ourselves from deletion
63  setDoNotDelete(true);
64 
65  bool needsTable = false;
66 
67  if (!newChild->isText() && !newChild->isReplaced()) {
68  switch (newChild->style()->display()) {
69  case INLINE:
70  case BLOCK:
71  case LIST_ITEM:
72  case RUN_IN:
73  case COMPACT:
74  case INLINE_BLOCK:
75  case TABLE:
76  case INLINE_TABLE:
77  break;
78  case TABLE_COLUMN:
79  if (isTableCol()) {
80  break;
81  }
82  // nobreak
83  case TABLE_COLUMN_GROUP:
84  case TABLE_CAPTION:
85  case TABLE_ROW_GROUP:
86  case TABLE_HEADER_GROUP:
87  case TABLE_FOOTER_GROUP:
88 
89  //qCDebug(KHTML_LOG) << "adding section";
90  if (!isTable()) {
91  needsTable = true;
92  }
93  break;
94  case TABLE_ROW:
95  //qCDebug(KHTML_LOG) << "adding row";
96  if (!isTableSection()) {
97  needsTable = true;
98  }
99  break;
100  case TABLE_CELL:
101  //qCDebug(KHTML_LOG) << "adding cell";
102  if (!isTableRow()) {
103  needsTable = true;
104  }
105  // I'm not 100% sure this is the best way to fix this, but without this
106  // change we recurse infinitely when trying to render the CSS2 test page:
107  // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html.
108  if (isTableCell() && !firstChild() && !newChild->isTableCell()) {
109  needsTable = false;
110  }
111 
112  break;
113  case NONE:
114  // RenderHtml and some others can have display:none
115  // KHTMLAssert(false);
116  break;
117  }
118  }
119 
120  if (needsTable) {
121  RenderTable *table;
122  RenderObject *last = beforeChild ? beforeChild->previousSibling() : lastChild();
123  if (last && last->isTable() && last->isAnonymous()) {
124  table = static_cast<RenderTable *>(last);
125  } else {
126  //qCDebug(KHTML_LOG) << "creating anonymous table, before=" << beforeChild;
127  table = new(renderArena()) RenderTable(document() /* is anonymous */);
128  RenderStyle *newStyle = new RenderStyle();
129  newStyle->inheritFrom(style());
130  newStyle->setDisplay(TABLE);
131  newStyle->setFlowAroundFloats(true);
132  table->setParent(this); // so it finds the arena
133  table->setStyle(newStyle);
134  table->setParent(nullptr);
135  addChild(table, beforeChild);
136  }
137  table->addChild(newChild);
138  } else {
139  // just add it...
140  insertChildNode(newChild, beforeChild);
141  }
142 
143  if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) {
144  DOM::DOMStringImpl *textToTransform = static_cast<RenderText *>(newChild)->originalString();
145  if (textToTransform) {
146  static_cast<RenderText *>(newChild)->setText(textToTransform, true);
147  }
148  }
149  newChild->attach();
150 
151  setDoNotDelete(false);
152 }
153 
154 RenderObject *RenderContainer::removeChildNode(RenderObject *oldChild)
155 {
156  KHTMLAssert(oldChild->parent() == this);
157  bool inCleanup = documentBeingDestroyed();
158 
159  if (!inCleanup) {
160  oldChild->setNeedsLayoutAndMinMaxRecalc(); // Dirty the containing block chain
161  oldChild->setNeedsLayout(false); // The child itself does not need to layout - it's going away.
162 
163  // Repaint, so that the area exposed when the child
164  // disappears gets repainted properly.
165  if (oldChild->height() && oldChild->width()) {
166  oldChild->repaint();
167  }
168  }
169 
170  // detach the place holder box
171  if (oldChild->isBox()) {
172  RenderBox *rb = static_cast<RenderBox *>(oldChild);
173  InlineBox *ph = rb->placeHolderBox();
174  if (ph) {
175  ph->detach(rb->renderArena(), inCleanup /*NoRemove*/);
176  rb->setPlaceHolderBox(nullptr);
177  }
178  }
179 
180  if (!inCleanup) {
181  // if we remove visible child from an invisible parent, we don't know the layer visibility any more
182  RenderLayer *layer = nullptr;
183  if (m_style->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->layer()) {
184  layer = enclosingLayer();
185  if (layer) {
186  layer->dirtyVisibleContentStatus();
187  }
188  }
189 
190  // Keep our layer hierarchy updated.
191  if (oldChild->firstChild() || oldChild->layer()) {
192  if (!layer) {
193  layer = enclosingLayer();
194  }
195  oldChild->removeLayers(layer);
196  }
197  // remove the child from any special layout lists
198  oldChild->removeFromObjectLists();
199 
200  // keep our fixed object lists updated.
201  if (oldChild->style()->hasFixedBackgroundImage() || oldChild->style()->position() == PFIXED) {
202  if (oldChild->style()->hasFixedBackgroundImage()) {
203  canvas()->removeStaticObject(oldChild);
204  }
205  if (oldChild->style()->position() == PFIXED) {
206  canvas()->removeStaticObject(oldChild, true);
207  }
208  }
209 
210  if (oldChild->isPosWithStaticDim() && childrenInline()) {
211  dirtyLinesFromChangedChild(oldChild);
212  }
213 
214  // We are about to take out node from the rendering tree and therefore
215  // it's possible that we're modifying the line box tree too.
216  // In order to properly recalculate it later we need
217  // to delete all the boxes from the current flow of the removed child. (vtokarev)
218  // In particular that's relevant when we're merging split inline flow. (continuations)
219  // We're taking the render objects from one block and insert into another
220  // so we have to force line box tree recalculation
221  if (oldChild->isInline()) {
222  if (oldChild->isText()) {
223  InlineTextBox *box = static_cast<RenderText *>(oldChild)->firstTextBox();
224  InlineTextBox *nextTextBox;
225  assert(!box || box->parent());
226  // delete all the text boxes
227  for (; box; box = nextTextBox) {
228  nextTextBox = box->nextTextBox();
229  box->remove();
230  box->deleteLine(renderArena());
231  }
232  } else if (oldChild->isInlineFlow()) {
233  InlineFlowBox *box = static_cast<RenderFlow *>(oldChild)->firstLineBox();
234  InlineFlowBox *nextFlowBox;
235  assert(!box || box->parent());
236  // delete all the flow
237  for (; box; box = nextFlowBox) {
238  nextFlowBox = box->nextFlowBox();
239  box->remove();
240  box->deleteLine(renderArena());
241  }
242  }
243  }
244 
245  // if oldChild is the start or end of the selection, then clear
246  // the selection to avoid problems of invalid pointers
247 
248  // ### This is not the "proper" solution... ideally the selection
249  // ### should be maintained based on DOM Nodes and a Range, which
250  // ### gets adjusted appropriately when nodes are deleted/inserted
251  // ### near etc. But this at least prevents crashes caused when
252  // ### the start or end of the selection is deleted and then
253  // ### accessed when the user next selects something.
254 
255  if (oldChild->isSelectionBorder()) {
256  canvas()->clearSelection();
257  }
258  }
259 
260  // remove the child from the render-tree
261  if (oldChild->previousSibling()) {
262  oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
263  }
264  if (oldChild->nextSibling()) {
265  oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
266  }
267 
268  if (m_first == oldChild) {
269  m_first = oldChild->nextSibling();
270  }
271  if (m_last == oldChild) {
272  m_last = oldChild->previousSibling();
273  }
274 
275  oldChild->setPreviousSibling(nullptr);
276  oldChild->setNextSibling(nullptr);
277  oldChild->setParent(nullptr);
278 
279  return oldChild;
280 }
281 
282 void RenderContainer::setStyle(RenderStyle *_style)
283 {
284  RenderObject::setStyle(_style);
285 
286  // If we are a pseudo-container we need to restyle the children
287  if (style()->isGenerated()) {
288  // ### we could save this call when the change only affected
289  // non inherited properties
290  RenderStyle *pseudoStyle = new RenderStyle();
291  pseudoStyle->inheritFrom(style());
292  pseudoStyle->ref();
293  RenderObject *child = firstChild();
294  while (child != nullptr) {
295  child->setStyle(pseudoStyle);
296  child = child->nextSibling();
297  }
298  pseudoStyle->deref();
299  }
300 }
301 
302 static bool inUpdatePseudoChildren = false;
303 
304 void RenderContainer::updatePseudoChildren()
305 {
306  if (inUpdatePseudoChildren) {
307  return;
308  }
309  inUpdatePseudoChildren = true;
310 
311  // In CSS2, before/after pseudo-content cannot nest. Check this first.
312  // Remove when CSS 3 Generated Content becomes Candidate Recommendation
313  if (style()->styleType() == RenderStyle::BEFORE
314  || style()->styleType() == RenderStyle::AFTER) {
315  return;
316  }
317 
318  updatePseudoChild(RenderStyle::BEFORE);
319  updatePseudoChild(RenderStyle::AFTER);
320  // updatePseudoChild(RenderStyle::MARKER, marker());
321 
322  inUpdatePseudoChildren = false;
323 }
324 
325 void RenderContainer::updatePseudoChild(RenderStyle::PseudoId type)
326 {
327  // The head manages generated content for its continuations
328  if (isInlineContinuation()) {
329  return;
330  }
331 
332  RenderStyle *pseudo = style()->getPseudoStyle(type);
333 
334  RenderObject *child = pseudoContainer(type);
335 
336  // Whether or not we currently have generated content attached.
337  bool oldContentPresent = child && (child->style()->styleType() == type);
338 
339  // Whether or not we now want generated content.
340  bool newContentWanted = pseudo && pseudo->display() != NONE;
341 
342  // No generated content
343  if (!oldContentPresent && !newContentWanted) {
344  return;
345  }
346 
347  bool movedContent = (type == RenderStyle::AFTER && isRenderInline() && continuation());
348 
349  // Whether or not we want the same old content.
350  bool sameOldContent = oldContentPresent && newContentWanted && !movedContent
351  && (child->style()->contentDataEquivalent(pseudo));
352 
353  // No change in content, update style
354  if (sameOldContent) {
355  child->setStyle(pseudo);
356  return;
357  }
358 
359  // If we don't want generated content any longer, or if we have generated content,
360  // but it's no longer identical to the new content data we want to build
361  // render objects for, then we nuke all of the old generated content.
362  if (oldContentPresent && (!newContentWanted || !sameOldContent)) {
363  // The child needs to be removed.
364  oldContentPresent = false;
365  child->detach();
366  child = nullptr;
367  }
368 
369  // If we have no pseudo-style or if the pseudo's display type is NONE, then we
370  // have no generated content and can now return.
371  if (!newContentWanted) {
372  return;
373  }
374 
375  // Generated content consists of a single container that houses multiple children (specified
376  // by the content property). This pseudo container gets the pseudo style set on it.
377  RenderContainer *pseudoContainer = nullptr;
378  pseudoContainer = RenderFlow::createFlow(element(), pseudo, renderArena());
379  pseudoContainer->setIsAnonymous(true);
380  pseudoContainer->createGeneratedContent();
381 
382  // Only add the container if it had content
383  if (pseudoContainer->firstChild()) {
384  addPseudoContainer(pseudoContainer);
385  pseudoContainer->close();
386  }
387 }
388 
389 void RenderContainer::createGeneratedContent()
390 {
391  RenderStyle *pseudo = style();
392  RenderStyle *style = new RenderStyle();
393  style->ref();
394  style->inheritFrom(pseudo);
395 
396  // Now walk our list of generated content and create render objects for every type
397  // we encounter.
398  for (ContentData *contentData = pseudo->contentData();
399  contentData; contentData = contentData->_nextContent) {
400  if (contentData->_contentType == CONTENT_TEXT) {
401  RenderText *t = new(renderArena()) RenderText(node(), nullptr);
402  t->setIsAnonymous(true);
403  t->setStyle(style);
404  t->setText(contentData->contentText());
405  addChild(t);
406  } else if (contentData->_contentType == CONTENT_OBJECT) {
407  RenderImage *img = new(renderArena()) RenderImage(node());
408  img->setIsAnonymous(true);
409  img->setStyle(style);
410  img->setContentObject(contentData->contentObject());
411  addChild(img);
412  } else if (contentData->_contentType == CONTENT_COUNTER) {
413  // really a counter or just a glyph?
414  EListStyleType type = (EListStyleType)contentData->contentCounter()->listStyle();
415  RenderObject *t = nullptr;
416  if (isListStyleCounted(type)) {
417  t = new(renderArena()) RenderCounter(node(), contentData->contentCounter());
418  } else {
419  t = new(renderArena()) RenderGlyph(node(), type);
420  }
421  t->setIsAnonymous(true);
422  t->setStyle(style);
423  addChild(t);
424  } else if (contentData->_contentType == CONTENT_QUOTE) {
425  RenderQuote *t = new(renderArena()) RenderQuote(node(), contentData->contentQuote());
426  t->setIsAnonymous(true);
427  t->setStyle(style);
428  addChild(t);
429  }
430  }
431  style->deref();
432 }
433 
434 RenderContainer *RenderContainer::pseudoContainer(RenderStyle::PseudoId type) const
435 {
436  RenderObject *child = nullptr;
437  switch (type) {
438  case RenderStyle::AFTER:
439  child = lastChild();
440  break;
441  case RenderStyle::BEFORE:
442  child = firstChild();
443  break;
444  case RenderStyle::REPLACED:
445  child = lastChild();
446  if (child && child->style()->styleType() == RenderStyle::AFTER) {
447  child = child->previousSibling();
448  }
449  break;
450  default:
451  child = nullptr;
452  }
453 
454  if (child && child->style()->styleType() == type) {
455  assert(child->isRenderBlock() || child->isRenderInline());
456  return static_cast<RenderContainer *>(child);
457  }
458  if (type == RenderStyle::AFTER) {
459  // check continuations
460  if (continuation()) {
461  return continuation()->pseudoContainer(type);
462  }
463  }
464  if (child && child->isAnonymousBlock()) {
465  return static_cast<RenderBlock *>(child)->pseudoContainer(type);
466  }
467  return nullptr;
468 }
469 
470 void RenderContainer::addPseudoContainer(RenderObject *child)
471 {
472  RenderStyle::PseudoId type = child->style()->styleType();
473  switch (type) {
474  case RenderStyle::AFTER: {
475  RenderObject *o = this;
476  while (o->continuation()) {
477  o = o->continuation();
478  }
479 
480  // Coalesce inlines
481  if (child->style()->display() == INLINE && o->lastChild() && o->lastChild()->isAnonymousBlock()) {
482  o->lastChild()->addChild(child, nullptr);
483  } else {
484  o->addChild(child, nullptr);
485  }
486  break;
487  }
488  case RenderStyle::BEFORE:
489  // Coalesce inlines
490  if (child->style()->display() == INLINE && firstChild() && firstChild()->isAnonymousBlock()) {
491  firstChild()->addChild(child, firstChild()->firstChild());
492  } else {
493  addChild(child, firstChild());
494  }
495  break;
496  case RenderStyle::REPLACED:
497  addChild(child, pseudoContainer(RenderStyle::AFTER));
498  break;
499  default:
500  break;
501  }
502 }
503 
504 void RenderContainer::updateReplacedContent()
505 {
506  // Only for normal elements
507  if (!style() || style()->styleType() != RenderStyle::NOPSEUDO) {
508  return;
509  }
510 
511  // delete old generated content
512  RenderContainer *container = pseudoContainer(RenderStyle::REPLACED);
513  if (container) {
514  container->detach();
515  }
516 
517  if (style()->useNormalContent()) {
518  return;
519  }
520 
521  // create generated content
522  RenderStyle *pseudo = style()->getPseudoStyle(RenderStyle::REPLACED);
523  if (!pseudo) {
524  pseudo = new RenderStyle();
525  pseudo->inheritFrom(style());
526  pseudo->setStyleType(RenderStyle::REPLACED);
527  }
528  if (pseudo->useNormalContent()) {
529  pseudo->setContentData(style()->contentData());
530  }
531 
532  container = RenderFlow::createFlow(node(), pseudo, renderArena());
533  container->setIsAnonymous(true);
534  container->createGeneratedContent();
535 
536  addChild(container, pseudoContainer(RenderStyle::AFTER));
537 }
538 
539 void RenderContainer::appendChildNode(RenderObject *newChild)
540 {
541  KHTMLAssert(newChild->parent() == nullptr);
542 
543  newChild->setParent(this);
544  RenderObject *lChild = lastChild();
545 
546  if (lChild) {
547  newChild->setPreviousSibling(lChild);
548  lChild->setNextSibling(newChild);
549  } else {
550  setFirstChild(newChild);
551  }
552 
553  setLastChild(newChild);
554 
555  // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children
556  // and don't have a layer attached to ourselves.
557  RenderLayer *layer = nullptr;
558  if (newChild->firstChild() || newChild->layer()) {
559  layer = enclosingLayer();
560  newChild->addLayers(layer, newChild);
561  // keep our fixed object lists updated.
562  if (newChild->style() && (newChild->style()->hasFixedBackgroundImage() || newChild->style()->position() == PFIXED)) {
563  if (newChild->style()->hasFixedBackgroundImage()) {
564  canvas()->addStaticObject(newChild);
565  }
566  if (newChild->style()->position() == PFIXED) {
567  canvas()->addStaticObject(newChild, true);
568  }
569  }
570  }
571 
572  // if the new child is visible but this object was not, tell the layer it has some visible content
573  // that needs to be drawn and layer visibility optimization can't be used
574  if (style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->layer()) {
575  if (!layer) {
576  layer = enclosingLayer();
577  }
578  if (layer) {
579  layer->setHasVisibleContent(true);
580  }
581  }
582 
583  if (!newChild->isFloatingOrPositioned() && childrenInline()) {
584  dirtyLinesFromChangedChild(newChild);
585  }
586 
587  newChild->setNeedsLayoutAndMinMaxRecalc(); // Goes up the containing block hierarchy.
588 
589  if (!normalChildNeedsLayout()) {
590  // We may supply the static position for an absolute positioned child.
591  if (newChild->firstChild() || newChild->isPosWithStaticDim() || !newChild->isPositioned()) {
592  setChildNeedsLayout(true);
593  } else {
594  assert(!newChild->inPosObjectList());
595  newChild->containingBlock()->insertPositionedObject(newChild);
596  }
597  }
598 }
599 
600 void RenderContainer::insertChildNode(RenderObject *child, RenderObject *beforeChild)
601 {
602  if (!beforeChild) {
603  appendChildNode(child);
604  return;
605  }
606 
607  KHTMLAssert(!child->parent());
608  while (beforeChild->parent() != this && beforeChild->parent()->isAnonymous()) {
609  beforeChild = beforeChild->parent();
610  }
611  KHTMLAssert(beforeChild->parent() == this);
612 
613  if (beforeChild == firstChild()) {
614  setFirstChild(child);
615  }
616 
617  RenderObject *prev = beforeChild->previousSibling();
618  child->setNextSibling(beforeChild);
619  beforeChild->setPreviousSibling(child);
620  if (prev) {
621  prev->setNextSibling(child);
622  }
623  child->setPreviousSibling(prev);
624  child->setParent(this);
625 
626  // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children
627  // and don't have a layer attached to ourselves.
628  RenderLayer *layer = nullptr;
629  if (child->firstChild() || child->layer()) {
630  layer = enclosingLayer();
631  child->addLayers(layer, child);
632  // keep our fixed object lists updated.
633  if (child->style() && (child->style()->hasFixedBackgroundImage() || child->style()->position() == PFIXED)) {
634  if (child->style()->hasFixedBackgroundImage()) {
635  canvas()->addStaticObject(child);
636  }
637  if (child->style()->position() == PFIXED) {
638  canvas()->addStaticObject(child, true);
639  }
640  }
641 
642  }
643 
644  // if the new child is visible but this object was not, tell the layer it has some visible content
645  // that needs to be drawn and layer visibility optimization can't be used
646  if (style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->layer()) {
647  if (!layer) {
648  layer = enclosingLayer();
649  }
650  if (layer) {
651  layer->setHasVisibleContent(true);
652  }
653  }
654 
655  if (!child->isFloating() && childrenInline()) {
656  dirtyLinesFromChangedChild(child);
657  }
658 
659  child->setNeedsLayoutAndMinMaxRecalc();
660 
661  if (!normalChildNeedsLayout()) {
662  // We may supply the static position for an absolute positioned child.
663  if (child->firstChild() || child->isPosWithStaticDim() || !child->isPositioned()) {
664  setChildNeedsLayout(true);
665  } else {
666  assert(!child->inPosObjectList());
667  child->containingBlock()->insertPositionedObject(child);
668  }
669  }
670 }
671 
672 void RenderContainer::layout()
673 {
674  KHTMLAssert(needsLayout());
675  KHTMLAssert(minMaxKnown());
676  const bool pagedMode = canvas()->pagedMode();
677  RenderObject *child = firstChild();
678  while (child) {
679  if (pagedMode) {
680  child->setNeedsLayout(true);
681  }
682  child->layoutIfNeeded();
683  if (child->containsPageBreak()) {
684  setContainsPageBreak(true);
685  }
686  if (child->needsPageClear()) {
687  setNeedsPageClear(true);
688  }
689  child = child->nextSibling();
690  }
691  setNeedsLayout(false);
692 }
693 
694 void RenderContainer::removeSuperfluousAnonymousBlockChild(RenderObject *child)
695 {
696  KHTMLAssert(child->parent() == this && child->isAnonymousBlock());
697 
698  if (child->doNotDelete() || child->continuation()) {
699  return;
700  }
701 
702  RenderObject *childSFirstChild = child->firstChild();
703  RenderObject *childSLastChild = child->lastChild();
704 
705  if (childSFirstChild) {
706  RenderObject *o = childSFirstChild;
707  while (o) {
708  o->setParent(this);
709  o = o->nextSibling();
710  }
711  childSFirstChild->setPreviousSibling(child->previousSibling());
712  childSLastChild->setNextSibling(child->nextSibling());
713  if (child->previousSibling()) {
714  child->previousSibling()->setNextSibling(childSFirstChild);
715  }
716  if (child->nextSibling()) {
717  child->nextSibling()->setPreviousSibling(childSLastChild);
718  }
719  if (child == firstChild()) {
720  m_first = childSFirstChild;
721  }
722  if (child == lastChild()) {
723  m_last = childSLastChild;
724  }
725  } else {
726  if (child->previousSibling()) {
727  child->previousSibling()->setNextSibling(child->nextSibling());
728  }
729  if (child->nextSibling()) {
730  child->nextSibling()->setPreviousSibling(child->previousSibling());
731  }
732  if (child == firstChild()) {
733  m_first = child->nextSibling();
734  }
735  if (child == lastChild()) {
736  m_last = child->previousSibling();
737  }
738  }
739  child->setParent(nullptr);
740  child->setPreviousSibling(nullptr);
741  child->setNextSibling(nullptr);
742  if (!child->isText()) {
743  RenderContainer *c = static_cast<RenderContainer *>(child);
744  c->m_first = nullptr;
745  c->m_next = nullptr;
746  }
747  child->detach();
748 }
749 
750 RenderPosition RenderContainer::positionForCoordinates(int _x, int _y)
751 {
752  // no children...return this render object's element, if there is one, and offset 0
753  if (!firstChild()) {
754  return RenderPosition(element(), 0);
755  }
756 
757  // look for the geometrically-closest child and pass off to that child
758  int min = INT_MAX;
759  RenderObject *closestRenderer = firstChild();
760  for (RenderObject *renderer = firstChild(); renderer; renderer = renderer->nextSibling()) {
761  int absx, absy;
762  renderer->absolutePosition(absx, absy);
763 
764  int top = absy + borderTop() + paddingTop();
765  int bottom = top + renderer->contentHeight();
766  int left = absx + borderLeft() + paddingLeft();
767  int right = left + renderer->contentWidth();
768 
769  int cmp;
770  cmp = abs(_y - top); if (cmp < min) {
771  closestRenderer = renderer;
772  min = cmp;
773  }
774  cmp = abs(_y - bottom); if (cmp < min) {
775  closestRenderer = renderer;
776  min = cmp;
777  }
778  cmp = abs(_x - left); if (cmp < min) {
779  closestRenderer = renderer;
780  min = cmp;
781  }
782  cmp = abs(_x - right); if (cmp < min) {
783  closestRenderer = renderer;
784  min = cmp;
785  }
786  }
787 
788  return closestRenderer->positionForCoordinates(_x, _y);
789 }
790 
791 #undef DEBUG_LAYOUT
This file is part of the HTML rendering engine for KDE.
QTextStream & right(QTextStream &stream)
Base class for rendering objects that can have children.
QTextStream & left(QTextStream &stream)
MESSAGECORE_EXPORT KMime::Content * firstChild(const KMime::Content *node)
all geometry managing stuff is only in the block elements.
Definition: render_flow.h:44
Base Class for all rendering tree objects.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Thu Oct 21 2021 22:48:11 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.