KHtml

render_flow.cpp
1 /**
2  * This file is part of the html renderer for KDE.
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-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 #include "render_flow.h"
28 
29 #include "khtml_debug.h"
30 #include <assert.h>
31 #include <QPainter>
32 
33 #include "render_text.h"
34 #include "render_table.h"
35 #include "render_canvas.h"
36 #include "render_inline.h"
37 #include "render_block.h"
38 #include "render_arena.h"
39 #include <xml/dom_nodeimpl.h>
40 #include <xml/dom_docimpl.h>
41 #include <html/html_formimpl.h>
42 
43 #include <khtmlview.h>
44 
45 using namespace DOM;
46 using namespace khtml;
47 
48 RenderFlow *RenderFlow::createFlow(DOM::NodeImpl *node, RenderStyle *style, RenderArena *arena)
49 {
50  RenderFlow *result;
51  if (style->display() == INLINE) {
52  result = new(arena) RenderInline(node);
53  } else {
54  result = new(arena) RenderBlock(node);
55  }
56  result->setStyle(style);
57  return result;
58 }
59 
60 RenderFlow *RenderFlow::continuationBefore(const RenderObject *beforeChild)
61 {
62  if (beforeChild && beforeChild->parent() == this) {
63  return this;
64  }
65 
66  RenderFlow *curr = continuation();
67  RenderFlow *nextToLast = this;
68  RenderFlow *last = this;
69  while (curr) {
70  if (beforeChild && beforeChild->parent() == curr) {
71  if (curr->firstChild() == beforeChild) {
72  return last;
73  }
74  return curr;
75  }
76 
77  nextToLast = last;
78  last = curr;
79  curr = curr->continuation();
80  }
81 
82  if (!beforeChild && !last->firstChild()) {
83  return nextToLast;
84  }
85  return last;
86 }
87 
88 void RenderFlow::addChildWithContinuation(RenderObject *newChild, RenderObject *beforeChild)
89 {
90  RenderFlow *flow = continuationBefore(beforeChild);
91 
92  RenderObject *bc = beforeChild;
93  while (bc && bc->parent() != flow && !bc->parent()->isAnonymousBlock()) {
94  // skip implicit containers around beforeChild
95  bc = bc->parent();
96  }
97 
98  RenderFlow *beforeChildParent = bc ? static_cast<RenderFlow *>(bc->parent()) :
99  (flow->continuation() ? flow->continuation() : flow);
100 
101  if (newChild->isFloatingOrPositioned()) {
102  return beforeChildParent->addChildToFlow(newChild, beforeChild);
103  }
104 
105  // A continuation always consists of two potential candidates: an inline or an anonymous
106  // block box holding block children.
107  bool childInline = newChild->isInline();
108  bool bcpInline = beforeChildParent->isInline();
109  bool flowInline = flow->isInline();
110 
111  if (flow == beforeChildParent) {
112  return flow->addChildToFlow(newChild, beforeChild);
113  } else {
114  // The goal here is to match up if we can, so that we can coalesce and create the
115  // minimal # of continuations needed for the inline.
116  if (childInline == bcpInline) {
117  return beforeChildParent->addChildToFlow(newChild, beforeChild);
118  } else if (flowInline == childInline) {
119  return flow->addChildToFlow(newChild, nullptr); // Just treat like an append.
120  } else {
121  return beforeChildParent->addChildToFlow(newChild, beforeChild);
122  }
123  }
124 }
125 
126 void RenderFlow::addChild(RenderObject *newChild, RenderObject *beforeChild)
127 {
128 #ifdef DEBUG_LAYOUT
129  // qCDebug(KHTML_LOG) << renderName() << "(RenderFlow)::addChild( " << newChild->renderName() <<
130  // ", " << (beforeChild ? beforeChild->renderName() : "0") << " )";
131  // qCDebug(KHTML_LOG) << "current height = " << m_height;
132 #endif
133 
134  if (continuation()) {
135  return addChildWithContinuation(newChild, beforeChild);
136  }
137  return addChildToFlow(newChild, beforeChild);
138 }
139 
140 void RenderFlow::extractLineBox(InlineFlowBox *box)
141 {
142  m_lastLineBox = box->prevFlowBox();
143  if (box == m_firstLineBox) {
144  m_firstLineBox = nullptr;
145  }
146  if (box->prevLineBox()) {
147  box->prevLineBox()->setNextLineBox(nullptr);
148  }
149  box->setPreviousLineBox(nullptr);
150  for (InlineRunBox *curr = box; curr; curr = curr->nextLineBox()) {
151  curr->setExtracted();
152  }
153 }
154 
155 void RenderFlow::attachLineBox(InlineFlowBox *box)
156 {
157  if (m_lastLineBox) {
158  m_lastLineBox->setNextLineBox(box);
159  box->setPreviousLineBox(m_lastLineBox);
160  } else {
161  m_firstLineBox = box;
162  }
163  InlineFlowBox *last = box;
164  for (InlineFlowBox *curr = box; curr; curr = curr->nextFlowBox()) {
165  curr->setExtracted(false);
166  last = curr;
167  }
168  m_lastLineBox = last;
169 }
170 
171 void RenderFlow::removeInlineBox(InlineBox *_box)
172 {
173  if (_box->isInlineFlowBox()) {
174  InlineFlowBox *box = static_cast<InlineFlowBox *>(_box);
175  if (box == m_firstLineBox) {
176  m_firstLineBox = box->nextFlowBox();
177  }
178  if (box == m_lastLineBox) {
179  m_lastLineBox = box->prevFlowBox();
180  }
181  if (box->nextLineBox()) {
182  box->nextLineBox()->setPreviousLineBox(box->prevLineBox());
183  }
184  if (box->prevLineBox()) {
185  box->prevLineBox()->setNextLineBox(box->nextLineBox());
186  }
187  }
188  RenderBox::removeInlineBox(_box);
189 }
190 
191 void RenderFlow::deleteInlineBoxes(RenderArena *arena)
192 {
193  if (m_firstLineBox) {
194  if (!arena) {
195  arena = renderArena();
196  }
197  InlineRunBox *curr = m_firstLineBox, *next = nullptr;
198  while (curr) {
199  next = curr->nextLineBox();
200  if (!curr->isPlaceHolderBox()) {
201  curr->detach(arena, true /*noRemove*/);
202  }
203  curr = next;
204  }
205  m_firstLineBox = nullptr;
206  m_lastLineBox = nullptr;
207  }
208 }
209 
210 void RenderFlow::dirtyInlineBoxes(bool fullLayout, bool isRootLineBox)
211 {
212  if (!isRootLineBox && (isReplaced() || isPositioned())) {
213  return RenderBox::dirtyInlineBoxes(fullLayout, isRootLineBox);
214  }
215 
216  if (fullLayout) {
217  deleteInlineBoxes();
218  } else {
219  for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
220  curr->dirtyInlineBoxes();
221  }
222  }
223 }
224 
225 void RenderFlow::deleteLastLineBox(RenderArena *arena)
226 {
227  if (m_lastLineBox) {
228  if (!arena) {
229  arena = renderArena();
230  }
231  InlineRunBox *curr = m_lastLineBox, *prev = m_lastLineBox;
232  if (m_firstLineBox == m_lastLineBox) {
233  m_firstLineBox = m_lastLineBox = nullptr;
234  } else {
235  prev = curr->prevLineBox();
236  while (!prev->isInlineFlowBox()) {
237  prev = prev->prevLineBox();
238  prev->detach(arena);
239  }
240  m_lastLineBox = static_cast<InlineFlowBox *>(prev);
241  prev->setNextLineBox(nullptr);
242  }
243  if (curr->parent()) {
244  curr->parent()->removeFromLine(curr);
245  }
246  curr->detach(arena);
247  }
248 }
249 
250 InlineBox *RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox)
251 {
252  if (!isRootLineBox &&
253  (isReplaced() || makePlaceHolderBox)) { // Inline tables and inline blocks
254  return RenderBox::createInlineBox(false, false); // (or positioned element placeholders).
255  }
256 
257  InlineFlowBox *flowBox = nullptr;
258  if (isInlineFlow()) {
259  flowBox = new(renderArena()) InlineFlowBox(this);
260  } else {
261  flowBox = new(renderArena()) RootInlineBox(this);
262  }
263 
264  if (!m_firstLineBox) {
265  m_firstLineBox = m_lastLineBox = flowBox;
266  } else {
267  m_lastLineBox->setNextLineBox(flowBox);
268  flowBox->setPreviousLineBox(m_lastLineBox);
269  m_lastLineBox = flowBox;
270  }
271 
272  return flowBox;
273 }
274 
275 void RenderFlow::dirtyLinesFromChangedChild(RenderObject *child)
276 {
277  if (!parent() || (selfNeedsLayout() && !isInlineFlow()) || isTable()) {
278  return;
279  }
280 
281  // If we have no first line box, then just bail early.
282  if (!firstLineBox()) {
283  // For an empty inline, propagate the check up to our parent, unless the parent
284  // is already dirty.
285  if (isInline() && !parent()->selfNeedsLayout() && parent()->isInlineFlow()) {
286  static_cast<RenderFlow *>(parent())->dirtyLinesFromChangedChild(this);
287  }
288  return;
289  }
290 
291  // Try to figure out which line box we belong in. First try to find a previous
292  // line box by examining our siblings. If we didn't find a line box, then use our
293  // parent's first line box.
294  RootInlineBox *box = nullptr;
295  RenderObject *curr = nullptr;
296  for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) {
297  if (curr->isFloatingOrPositioned()) {
298  continue;
299  }
300 
301  if (curr->isReplaced() && curr->isBox()) {
302  InlineBox *placeHolderBox = static_cast<RenderBox *>(curr)->placeHolderBox();
303  if (placeHolderBox) {
304  box = placeHolderBox->root();
305  }
306  } else if (curr->isText()) {
307  InlineTextBox *textBox = static_cast<RenderText *>(curr)->lastTextBox();
308  if (textBox) {
309  box = textBox->root();
310  }
311  } else if (curr->isInlineFlow()) {
312  InlineRunBox *runBox = static_cast<RenderFlow *>(curr)->lastLineBox();
313  if (runBox) {
314  box = runBox->root();
315  }
316  }
317 
318  if (box) {
319  break;
320  }
321  }
322  if (!box) {
323  box = firstLineBox()->root();
324  }
325 
326  // If we found a line box, then dirty it.
327  if (box) {
328  RootInlineBox *adjacentBox;
329  box->markDirty();
330 
331  // dirty the adjacent lines that might be affected
332  // NOTE: we dirty the previous line because RootInlineBox objects cache
333  // the address of the first object on the next line after a BR, which we may be
334  // invalidating here. For more info, see how RenderBlock::layoutInlineChildren
335  // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak,
336  // despite the name, actually returns the first RenderObject after the BR.
337 
338  adjacentBox = box->prevRootBox();
339  if (adjacentBox) {
340  adjacentBox->markDirty();
341  }
342  if (child->isBR() || (curr && curr->isBR())) {
343  adjacentBox = box->nextRootBox();
344  if (adjacentBox) {
345  adjacentBox->markDirty();
346  }
347  }
348  }
349 }
350 
351 QList< QRectF > RenderFlow::getClientRects()
352 {
353  if (isRenderInline() && isInlineFlow()) {
355 
356  InlineFlowBox *child = firstLineBox();
357  if (child) {
358  int x = 0, y = 0;
359  absolutePosition(x,y);
360  do {
361  QRectF rect(x + child->xPos(), y + child->yPos(), child->width(), child->height());
362  list.append(clientRectToViewport(rect));
363  child = child->nextFlowBox();
364  } while (child);
365  }
366 
367  // In case our flow is splitted by blocks
368  for (RenderObject *cont = continuation(); cont; cont = cont->continuation()) {
369  list.append(cont->getClientRects());
370  }
371 
372  // Empty Flow, return the Flow itself
373  if (list.isEmpty()) {
374  return RenderObject::getClientRects();
375  }
376 
377  return list;
378  } else {
379  return RenderObject::getClientRects();
380  }
381 }
382 
383 void RenderFlow::detach()
384 {
385  if (continuation()) {
386  continuation()->detach();
387  }
388  m_continuation = nullptr;
389 
390  // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will
391  // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise.
392  detachRemainingChildren();
393 
394  if (!documentBeingDestroyed()) {
395  if (m_firstLineBox) {
396  // We can't wait for RenderContainer::destroy to clear the selection,
397  // because by then we will have nuked the line boxes.
398  if (isSelectionBorder()) {
399  canvas()->clearSelection();
400  }
401 
402  // If line boxes are contained inside a root, that means we're an inline.
403  // In that case, we need to remove all the line boxes so that the parent
404  // lines aren't pointing to deleted children. If the first line box does
405  // not have a parent that means they are either already disconnected or
406  // root lines that can just be destroyed without disconnecting.
407  if (m_firstLineBox->parent()) {
408  for (InlineRunBox *box = m_firstLineBox; box; box = box->nextLineBox()) {
409  box->remove();
410  }
411  }
412 
413  // If we are an anonymous block, then our line boxes might have children
414  // that will outlast this block. In the non-anonymous block case those
415  // children will be destroyed by the time we return from this function.
416  if (isAnonymousBlock()) {
417  for (InlineFlowBox *box = m_firstLineBox; box; box = box->nextFlowBox()) {
418  while (InlineBox *childBox = box->firstChild()) {
419  childBox->remove();
420  }
421  }
422  }
423  } else if (isInline() && parent())
424  // empty inlines propagate linebox dirtying to the parent
425  {
426  parent()->dirtyLinesFromChangedChild(this);
427  }
428  }
429 
430  deleteInlineBoxes();
431 
432  RenderBox::detach();
433 }
434 
435 void RenderFlow::paintLines(PaintInfo &i, int _tx, int _ty)
436 {
437  // Only paint during the foreground/selection phases.
438  if (i.phase != PaintActionForeground && i.phase != PaintActionSelection && i.phase != PaintActionOutline) {
439  return;
440  }
441 
442  if (!firstLineBox()) {
443  return;
444  }
445 
446  // We can check the first box and last box and avoid painting if we don't
447  // intersect. This is a quick short-circuit that we can take to avoid walking any lines.
448  // FIXME: This check is flawed in two extremely obscure ways.
449  // (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
450  // (2) The overflow from an inline block on a line is not reported to the line.
451  int maxOutlineSize = maximalOutlineSize(i.phase);
452  int yPos = firstLineBox()->root()->topOverflow() - maxOutlineSize;
453  int h = maxOutlineSize + lastLineBox()->root()->bottomOverflow() - yPos;
454  yPos += _ty;
455  if ((yPos >= i.r.y() + i.r.height()) || (yPos + h <= i.r.y())) {
456  return;
457  }
458  for (InlineFlowBox *curr = firstLineBox(); curr; curr = curr->nextFlowBox()) {
459  yPos = curr->root()->topOverflow() - maxOutlineSize;
460  h = curr->root()->bottomOverflow() + maxOutlineSize - yPos;
461  yPos += _ty;
462  if ((yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y())) {
463  curr->paint(i, _tx, _ty);
464  }
465  }
466 
467  if (i.phase == PaintActionOutline && i.outlineObjects) {
468  foreach (RenderFlow *oo, *i.outlineObjects)
469  if (oo->isRenderInline()) {
470  static_cast<RenderInline *>(oo)->paintOutlines(i.p, _tx, _ty);
471  }
472  i.outlineObjects->clear();
473  }
474 }
475 
476 bool RenderFlow::hitTestLines(NodeInfo &i, int x, int y, int tx, int ty, HitTestAction hitTestAction)
477 {
478  (void) hitTestAction;
479  /*
480  if (hitTestAction != HitTestForeground) // ### port hitTest
481  return false;
482  */
483 
484  if (!firstLineBox()) {
485  return false;
486  }
487 
488  // We can check the first box and last box and avoid hit testing if we don't
489  // contain the point. This is a quick short-circuit that we can take to avoid walking any lines.
490  // FIXME: This check is flawed in two extremely obscure ways.
491  // (1) If some line in the middle has a huge overflow, it might actually extend below the last line.
492  // (2) The overflow from an inline block on a line is not reported to the line.
493  if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow())) {
494  return false;
495  }
496 
497  // See if our root lines contain the point. If so, then we hit test
498  // them further. Note that boxes can easily overlap, so we can't make any assumptions
499  // based off positions of our first line box or our last line box.
500  for (InlineFlowBox *curr = lastLineBox(); curr; curr = curr->prevFlowBox()) {
501  if (y >= ty + curr->root()->topOverflow() && y < ty + curr->root()->bottomOverflow()) {
502  bool inside = curr->nodeAtPoint(i, x, y, tx, ty);
503  if (inside) {
504  setInnerNode(i);
505  return true;
506  }
507  }
508  }
509 
510  return false;
511 }
512 
513 void RenderFlow::repaint(Priority prior)
514 {
515  if (isInlineFlow()) {
516  // Find our leftmost position.
517  int left = 0;
518  // root inline box not reliably availabe during relayout
519  int top = firstLineBox() ? (
520  needsLayout() ? firstLineBox()->xPos() : firstLineBox()->root()->topOverflow()
521  ) : 0;
522  for (InlineRunBox *curr = firstLineBox(); curr; curr = curr->nextLineBox())
523  if (curr == firstLineBox() || curr->xPos() < left) {
524  left = curr->xPos();
525  }
526 
527  // Now invalidate a rectangle.
528  int ow = style() ? style()->outlineSize() : 0;
529 
530  // We need to add in the relative position offsets of any inlines (including us) up to our
531  // containing block.
532  RenderBlock *cb = containingBlock();
533  for (RenderObject *inlineFlow = this; inlineFlow && inlineFlow->isInlineFlow() && inlineFlow != cb;
534  inlineFlow = inlineFlow->parent()) {
535  if (inlineFlow->style() && inlineFlow->style()->position() == PRELATIVE && inlineFlow->layer()) {
536  KHTMLAssert(inlineFlow->isBox());
537  static_cast<RenderBox *>(inlineFlow)->relativePositionOffset(left, top);
538  }
539  }
540 
541  RootInlineBox *lastRoot = lastLineBox() && !needsLayout() ? lastLineBox()->root() : nullptr;
542  containingBlock()->repaintRectangle(-ow + left, -ow + top,
543  width() + ow * 2,
544  (lastRoot ? lastRoot->bottomOverflow() - top : height()) + ow * 2, prior);
545  } else {
546  if (firstLineBox() && firstLineBox()->topOverflow() < 0) {
547  int ow = style() ? style()->outlineSize() : 0;
548  repaintRectangle(-ow, -ow + firstLineBox()->topOverflow(),
549  effectiveWidth() + ow * 2, effectiveHeight() + ow * 2, prior);
550  } else {
551  return RenderBox::repaint(prior);
552  }
553  }
554 }
555 
556 int
557 RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
558 {
559  int bottom = includeSelf && m_width > 0 ? m_height : 0;
560  if (!includeOverflowInterior && hasOverflowClip()) {
561  return bottom;
562  }
563 
564  // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
565  // For now, we have to descend into all the children, since we may have a huge abs div inside
566  // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
567  // the abs div.
568  for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
569  if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
570  int lp = c->yPos() + c->lowestPosition(false);
571  bottom = qMax(bottom, lp);
572  }
573  }
574 
575  if (includeSelf && isRelPositioned()) {
576  int x = 0;
577  relativePositionOffset(x, bottom);
578  }
579 
580  return bottom;
581 }
582 
583 int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
584 {
585  int right = includeSelf && m_height > 0 ? m_width : 0;
586  if (!includeOverflowInterior && hasOverflowClip()) {
587  return right;
588  }
589 
590  // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
591  // For now, we have to descend into all the children, since we may have a huge abs div inside
592  // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
593  // the abs div.
594  for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
595  if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
596  int rp = c->xPos() + c->rightmostPosition(false);
597  right = qMax(right, rp);
598  }
599  }
600 
601  if (includeSelf && isRelPositioned()) {
602  int y = 0;
603  relativePositionOffset(right, y);
604  }
605 
606  return right;
607 }
608 
609 int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
610 {
611  int left = includeSelf && m_height > 0 ? 0 : m_width;
612  if (!includeOverflowInterior && hasOverflowClip()) {
613  return left;
614  }
615 
616  // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
617  // For now, we have to descend into all the children, since we may have a huge abs div inside
618  // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
619  // the abs div.
620  for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
621  if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
622  int lp = c->xPos() + c->leftmostPosition(false);
623  left = qMin(left, lp);
624  }
625  }
626 
627  if (includeSelf && isRelPositioned()) {
628  int y = 0;
629  relativePositionOffset(left, y);
630  }
631 
632  return left;
633 }
634 
635 int RenderFlow::highestPosition(bool includeOverflowInterior, bool includeSelf) const
636 {
637  int top = RenderBox::highestPosition(includeOverflowInterior, includeSelf);
638  if (!includeOverflowInterior && hasOverflowClip()) {
639  return top;
640  }
641 
642  // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
643  // For now, we have to descend into all the children, since we may have a huge abs div inside
644  // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to
645  // the abs div.
646  for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
647  if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) {
648  int hp = c->yPos() + c->highestPosition(false);
649  top = qMin(top, hp);
650  }
651  }
652 
653  if (includeSelf && isRelPositioned()) {
654  int x = 0;
655  relativePositionOffset(x, top);
656  }
657 
658  return top;
659 }
This file is part of the HTML rendering engine for KDE.
QTextStream & right(QTextStream &stream)
MESSAGECORE_EXPORT KMime::Content * next(KMime::Content *node, bool allowChildren=true)
KGuiItem cont()
QTextStream & left(QTextStream &stream)
void append(const T &value)
bool isEmpty() const const
MESSAGECORE_EXPORT KMime::Content * firstChild(const KMime::Content *node)
all geometry managing stuff is only in the block elements.
Definition: render_flow.h:44
This library provides a full-featured HTML parser and widget.
Base Class for all rendering tree objects.
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 19 2021 22:48:05 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.