Qt 4.8
qtextdocumentlayout.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qtextdocumentlayout_p.h"
43 #include "qtextdocument_p.h"
44 #include "qtextimagehandler_p.h"
45 #include "qtexttable.h"
46 #include "qtextlist.h"
47 #include "qtextengine_p.h"
48 #include "private/qcssutil_p.h"
49 
51 #include "qcssparser_p.h"
52 
53 #include <qpainter.h>
54 #include <qmath.h>
55 #include <qrect.h>
56 #include <qpalette.h>
57 #include <qdebug.h>
58 #include <qvarlengtharray.h>
59 #include <limits.h>
60 #include <qstyle.h>
61 #include <qbasictimer.h>
62 #include "private/qfunctions_p.h"
63 
64 // #define LAYOUT_DEBUG
65 
66 #ifdef LAYOUT_DEBUG
67 #define LDEBUG qDebug()
68 #define INC_INDENT debug_indent += " "
69 #define DEC_INDENT debug_indent = debug_indent.left(debug_indent.length()-2)
70 #else
71 #define LDEBUG if(0) qDebug()
72 #define INC_INDENT do {} while(0)
73 #define DEC_INDENT do {} while(0)
74 #endif
75 
77 
78 // ################ should probably add frameFormatChange notification!
79 
80 struct QTextLayoutStruct;
81 
83 {
84 public:
86 
87  // relative to parent frame
90 
91  // contents starts at (margin+border/margin+border)
98  // contents width includes padding (as we need to treat this on a per cell basis for tables)
102 
103  // accumulated margins
106 
109 
111 
112  bool sizeDirty;
114 
116 };
117 
120  currentLayoutStruct(0), sizeDirty(true), layoutDirty(true)
121 {
122 }
123 
125  QTextLayoutStruct() : maximumWidth(QFIXED_MAX), fullLayout(false)
126  {}
130  QFixed frameY; // absolute y position of the current frame
131  QFixed y; // always relative to the current frame
143 
144  inline void addUpdateRectForFloat(const QRectF &rect) {
145  if (updateRectForFloats.isValid())
146  updateRectForFloats |= rect;
147  else
148  updateRectForFloats = rect;
149  }
150 
151  inline QFixed absoluteY() const
152  { return frameY + y; }
153 
154  inline int currentPage() const
155  { return pageHeight == 0 ? 0 : (absoluteY() / pageHeight).truncate(); }
156 
157  inline void newPage()
158  { if (pageHeight == QFIXED_MAX) return; pageBottom += pageHeight; y = pageBottom - pageHeight + pageBottomMargin + pageTopMargin - frameY; }
159 };
160 
162 {
163 public:
164  QFixed cellSpacing, cellPadding;
172 
174 
176 
177  // maps from cell index (row + col * rowCount) to child frames belonging to
178  // the specific cell
180 
181  inline QFixed cellWidth(int column, int colspan) const
182  { return columnPositions.at(column + colspan - 1) + widths.at(column + colspan - 1)
183  - columnPositions.at(column); }
184 
185  inline void calcRowPosition(int row)
186  {
187  if (row > 0)
188  rowPositions[row] = rowPositions.at(row - 1) + heights.at(row - 1) + border + cellSpacing + border;
189  }
190 
191  QRectF cellRect(const QTextTableCell &cell) const;
192 
194  {
195  QVariant v = format.property(property);
196  if (v.isNull()) {
197  return cellPadding;
198  } else {
200  return QFixed::fromReal(v.toReal() * deviceScale);
201  }
202  }
203 
204  inline QFixed topPadding(const QTextFormat &format) const
205  {
206  return paddingProperty(format, QTextFormat::TableCellTopPadding);
207  }
208 
209  inline QFixed bottomPadding(const QTextFormat &format) const
210  {
211  return paddingProperty(format, QTextFormat::TableCellBottomPadding);
212  }
213 
214  inline QFixed leftPadding(const QTextFormat &format) const
215  {
216  return paddingProperty(format, QTextFormat::TableCellLeftPadding);
217  }
218 
219  inline QFixed rightPadding(const QTextFormat &format) const
220  {
221  return paddingProperty(format, QTextFormat::TableCellRightPadding);
222  }
223 
224  inline QFixedPoint cellPosition(const QTextTableCell &cell) const
225  {
226  const QTextFormat fmt = cell.format();
227  return cellPosition(cell.row(), cell.column()) + QFixedPoint(leftPadding(fmt), topPadding(fmt));
228  }
229 
230  void updateTableSize();
231 
232 private:
233  inline QFixedPoint cellPosition(int row, int col) const
234  { return QFixedPoint(columnPositions.at(col), rowPositions.at(row) + cellVerticalOffsets.at(col + row * widths.size())); }
235 };
236 
238 {
240  if (qobject_cast<QTextTable *>(f))
241  data = new QTextTableData;
242  else
243  data = new QTextFrameData;
244  f->setLayoutData(data);
245  return data;
246 }
247 
248 static inline QTextFrameData *data(QTextFrame *f)
249 {
250  QTextFrameData *data = static_cast<QTextFrameData *>(f->layoutData());
251  if (!data)
252  data = createData(f);
253  return data;
254 }
255 
257 {
258  return f->firstPosition() > f->lastPosition();
259 }
260 
262 {
265  const QFixed effectiveLeftMargin = this->leftMargin + border + padding;
266  const QFixed effectiveRightMargin = this->rightMargin + border + padding;
267  size.height = contentsHeight == -1
268  ? rowPositions.last() + heights.last() + padding + border + cellSpacing + effectiveBottomMargin
269  : effectiveTopMargin + contentsHeight + effectiveBottomMargin;
270  size.width = effectiveLeftMargin + contentsWidth + effectiveRightMargin;
271 }
272 
274 {
275  const int row = cell.row();
276  const int rowSpan = cell.rowSpan();
277  const int column = cell.column();
278  const int colSpan = cell.columnSpan();
279 
280  return QRectF(columnPositions.at(column).toReal(),
281  rowPositions.at(row).toReal(),
282  (columnPositions.at(column + colSpan - 1) + widths.at(column + colSpan - 1) - columnPositions.at(column)).toReal(),
283  (rowPositions.at(row + rowSpan - 1) + heights.at(row + rowSpan - 1) - rowPositions.at(row)).toReal());
284 }
285 
286 static inline bool isEmptyBlockBeforeTable(const QTextBlock &block, const QTextBlockFormat &format, const QTextFrame::Iterator &nextIt)
287 {
288  return !nextIt.atEnd()
289  && qobject_cast<QTextTable *>(nextIt.currentFrame())
290  && block.isValid()
291  && block.length() == 1
294  && nextIt.currentFrame()->firstPosition() == block.position() + 1
295  ;
296 }
297 
299 {
300  QTextFrame::Iterator next = it; ++next;
301  if (it.currentFrame())
302  return false;
303  QTextBlock block = it.currentBlock();
304  return isEmptyBlockBeforeTable(block, block.blockFormat(), next);
305 }
306 
307 static inline bool isEmptyBlockAfterTable(const QTextBlock &block, const QTextFrame *previousFrame)
308 {
309  return qobject_cast<const QTextTable *>(previousFrame)
310  && block.isValid()
311  && block.length() == 1
312  && previousFrame->lastPosition() == block.position() - 1
313  ;
314 }
315 
316 static inline bool isLineSeparatorBlockAfterTable(const QTextBlock &block, const QTextFrame *previousFrame)
317 {
318  return qobject_cast<const QTextTable *>(previousFrame)
319  && block.isValid()
320  && block.length() > 1
321  && block.text().at(0) == QChar::LineSeparator
322  && previousFrame->lastPosition() == block.position() - 1
323  ;
324 }
325 
326 /*
327 
328 Optimization strategies:
329 
330 HTML layout:
331 
332 * Distinguish between normal and special flow. For normal flow the condition:
333  y1 > y2 holds for all blocks with b1.key() > b2.key().
334 * Special flow is: floats, table cells
335 
336 * Normal flow within table cells. Tables (not cells) are part of the normal flow.
337 
338 
339 * If blocks grows/shrinks in height and extends over whole page width at the end, move following blocks.
340 * If height doesn't change, no need to do anything
341 
342 Table cells:
343 
344 * If minWidth of cell changes, recalculate table width, relayout if needed.
345 * What about maxWidth when doing auto layout?
346 
347 Floats:
348 * need fixed or proportional width, otherwise don't float!
349 * On width/height change relayout surrounding paragraphs.
350 
351 Document width change:
352 * full relayout needed
353 
354 
355 Float handling:
356 
357 * Floats are specified by a special format object.
358 * currently only floating images are implemented.
359 
360 */
361 
362 /*
363 
364  On the table layouting:
365 
366  +---[ table border ]-------------------------
367  | [ cell spacing ]
368  | +------[ cell border ]-----+ +--------
369  | | | |
370  | |
371  | |
372  | |
373  |
374 
375  rowPositions[i] and columnPositions[i] point at the cell content
376  position. So for example the left border is drawn at
377  x = columnPositions[i] - fd->border and similar for y.
378 
379 */
380 
382 {
384  QFixed frameY; // absolute y position of the current frame
389 };
391 
393 {
394  return checkPoint.y < y;
395 }
396 
397 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QCheckPoint &checkPoint, int pos)
398 {
399  return checkPoint.positionInFrame < pos;
400 }
401 
402 static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, const QPointF &origin, QRectF gradientRect = QRectF())
403 {
404  p->save();
405  if (brush.style() >= Qt::LinearGradientPattern && brush.style() <= Qt::ConicalGradientPattern) {
406  if (!gradientRect.isNull()) {
407  QTransform m;
408  m.translate(gradientRect.left(), gradientRect.top());
409  m.scale(gradientRect.width(), gradientRect.height());
410  brush.setTransform(m);
411  const_cast<QGradient *>(brush.gradient())->setCoordinateMode(QGradient::LogicalMode);
412  }
413  } else {
414  p->setBrushOrigin(origin);
415  }
416  p->fillRect(rect, brush);
417  p->restore();
418 }
419 
421 {
423 public:
425 
427 #ifdef LAYOUT_DEBUG
428  mutable QString debug_indent;
429 #endif
430 
433 
437 
439  mutable int lazyLayoutStepSize;
444 
448 
449  QFixed blockIndent(const QTextBlockFormat &blockFormat) const;
450 
451  void drawFrame(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
452  QTextFrame *f) const;
453  void drawFlow(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
454  QTextFrame::Iterator it, const QList<QTextFrame *> &floats, QTextBlock *cursorBlockNeedingRepaint) const;
455  void drawBlock(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
456  QTextBlock bl, bool inRootFrame) const;
457  void drawListItem(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
458  QTextBlock bl, const QTextCharFormat *selectionFormat) const;
459  void drawTableCell(const QRectF &cellRect, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &cell_context,
460  QTextTable *table, QTextTableData *td, int r, int c,
461  QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset) const;
462  void drawBorder(QPainter *painter, const QRectF &rect, qreal topMargin, qreal bottomMargin, qreal border,
463  const QBrush &brush, QTextFrameFormat::BorderStyle style) const;
464  void drawFrameDecoration(QPainter *painter, QTextFrame *frame, QTextFrameData *fd, const QRectF &clip, const QRectF &rect) const;
465 
466  enum HitPoint {
470  PointExact
471  };
472  HitPoint hitTest(QTextFrame *frame, const QFixedPoint &point, int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const;
473  HitPoint hitTest(QTextFrame::Iterator it, HitPoint hit, const QFixedPoint &p,
474  int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const;
475  HitPoint hitTest(QTextTable *table, const QFixedPoint &point, int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const;
476  HitPoint hitTest(QTextBlock bl, const QFixedPoint &point, int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const;
477 
478  QTextLayoutStruct layoutCell(QTextTable *t, const QTextTableCell &cell, QFixed width,
479  int layoutFrom, int layoutTo, QTextTableData *tableData, QFixed absoluteTableY,
480  bool withPageBreaks);
481  void setCellPosition(QTextTable *t, const QTextTableCell &cell, const QPointF &pos);
482  QRectF layoutTable(QTextTable *t, int layoutFrom, int layoutTo, QFixed parentY);
483 
484  void positionFloat(QTextFrame *frame, QTextLine *currentLine = 0);
485 
486  // calls the next one
487  QRectF layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed parentY = 0);
488  QRectF layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed frameWidth, QFixed frameHeight, QFixed parentY = 0);
489 
490  void layoutBlock(const QTextBlock &bl, int blockPosition, const QTextBlockFormat &blockFormat,
491  QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat);
492  void layoutFlow(QTextFrame::Iterator it, QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, QFixed width = 0);
493  void pageBreakInsideTable(QTextTable *table, QTextLayoutStruct *layoutStruct);
494 
495 
496  void floatMargins(const QFixed &y, const QTextLayoutStruct *layoutStruct, QFixed *left, QFixed *right) const;
497  QFixed findY(QFixed yFrom, const QTextLayoutStruct *layoutStruct, QFixed requiredWidth) const;
498 
500 
501  QTextFrame::Iterator frameIteratorForYPosition(QFixed y) const;
502  QTextFrame::Iterator frameIteratorForTextPosition(int position) const;
503 
504  void ensureLayouted(QFixed y) const;
505  void ensureLayoutedByPosition(int position) const;
506  inline void ensureLayoutFinished() const
507  { ensureLayoutedByPosition(INT_MAX); }
508  void layoutStep() const;
509 
510  QRectF frameBoundingRectInternal(QTextFrame *frame) const;
511 
512  qreal scaleToDevice(qreal value) const;
513  QFixed scaleToDevice(QFixed value) const;
514 };
515 
517  : fixedColumnWidth(-1),
518  cursorWidth(1),
519  currentLazyLayoutPosition(-1),
520  lazyLayoutStepSize(1000),
521  lastPageCount(-1)
522 {
523  showLayoutProgress = true;
524  insideDocumentChange = false;
525  idealWidth = 0;
526  contentHasAlignment = false;
527 }
528 
530 {
531  QTextFrame *rootFrame = document->rootFrame();
532 
533  if (checkPoints.isEmpty()
534  || y < 0 || y > data(rootFrame)->size.height)
535  return rootFrame->begin();
536 
538  if (checkPoint == checkPoints.end())
539  return rootFrame->begin();
540 
541  if (checkPoint != checkPoints.begin())
542  --checkPoint;
543 
544  const int position = rootFrame->firstPosition() + checkPoint->positionInFrame;
545  return frameIteratorForTextPosition(position);
546 }
547 
549 {
550  QTextFrame *rootFrame = docPrivate->rootFrame();
551 
553  const int begin = map.findNode(rootFrame->firstPosition());
554  const int end = map.findNode(rootFrame->lastPosition()+1);
555 
556  const int block = map.findNode(position);
557  const int blockPos = map.position(block);
558 
559  QTextFrame::iterator it(rootFrame, block, begin, end);
560 
561  QTextFrame *containingFrame = docPrivate->frameAt(blockPos);
562  if (containingFrame != rootFrame) {
563  while (containingFrame->parentFrame() != rootFrame) {
564  containingFrame = containingFrame->parentFrame();
565  Q_ASSERT(containingFrame);
566  }
567 
568  it.cf = containingFrame;
569  it.cb = 0;
570  }
571 
572  return it;
573 }
574 
577 {
578  QTextFrameData *fd = data(frame);
579  // #########
580  if (fd->layoutDirty)
581  return PointAfter;
582  Q_ASSERT(!fd->layoutDirty);
583  Q_ASSERT(!fd->sizeDirty);
584  const QFixedPoint relativePoint(point.x - fd->position.x, point.y - fd->position.y);
585 
586  QTextFrame *rootFrame = docPrivate->rootFrame();
587 
588 // LDEBUG << "checking frame" << frame->firstPosition() << "point=" << point
589 // << "position" << fd->position << "size" << fd->size;
590  if (frame != rootFrame) {
591  if (relativePoint.y < 0 || relativePoint.x < 0) {
592  *position = frame->firstPosition() - 1;
593 // LDEBUG << "before pos=" << *position;
594  return PointBefore;
595  } else if (relativePoint.y > fd->size.height || relativePoint.x > fd->size.width) {
596  *position = frame->lastPosition() + 1;
597 // LDEBUG << "after pos=" << *position;
598  return PointAfter;
599  }
600  }
601 
602  if (isFrameFromInlineObject(frame)) {
603  *position = frame->firstPosition() - 1;
604  return PointExact;
605  }
606 
607  if (QTextTable *table = qobject_cast<QTextTable *>(frame)) {
608  const int rows = table->rows();
609  const int columns = table->columns();
610  QTextTableData *td = static_cast<QTextTableData *>(data(table));
611 
612  if (!td->childFrameMap.isEmpty()) {
613  for (int r = 0; r < rows; ++r) {
614  for (int c = 0; c < columns; ++c) {
615  QTextTableCell cell = table->cellAt(r, c);
616  if (cell.row() != r || cell.column() != c)
617  continue;
618 
619  QRectF cellRect = td->cellRect(cell);
620  const QFixedPoint cellPos = QFixedPoint::fromPointF(cellRect.topLeft());
621  const QFixedPoint pointInCell = relativePoint - cellPos;
622 
623  const QList<QTextFrame *> childFrames = td->childFrameMap.values(r + c * rows);
624  for (int i = 0; i < childFrames.size(); ++i) {
625  QTextFrame *child = childFrames.at(i);
626  if (isFrameFromInlineObject(child)
628  && hitTest(child, pointInCell, position, l, accuracy) == PointExact)
629  {
630  return PointExact;
631  }
632  }
633  }
634  }
635  }
636 
637  return hitTest(table, relativePoint, position, l, accuracy);
638  }
639 
640  const QList<QTextFrame *> childFrames = frame->childFrames();
641  for (int i = 0; i < childFrames.size(); ++i) {
642  QTextFrame *child = childFrames.at(i);
643  if (isFrameFromInlineObject(child)
645  && hitTest(child, relativePoint, position, l, accuracy) == PointExact)
646  {
647  return PointExact;
648  }
649  }
650 
651  QTextFrame::Iterator it = frame->begin();
652 
653  if (frame == rootFrame) {
654  it = frameIteratorForYPosition(relativePoint.y);
655 
656  Q_ASSERT(it.parentFrame() == frame);
657  }
658 
659  if (it.currentFrame())
660  *position = it.currentFrame()->firstPosition();
661  else
662  *position = it.currentBlock().position();
663 
664  return hitTest(it, PointBefore, relativePoint, position, l, accuracy);
665 }
666 
669  int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const
670 {
671  INC_INDENT;
672 
673  for (; !it.atEnd(); ++it) {
674  QTextFrame *c = it.currentFrame();
675  HitPoint hp;
676  int pos = -1;
677  if (c) {
678  hp = hitTest(c, p, &pos, l, accuracy);
679  } else {
680  hp = hitTest(it.currentBlock(), p, &pos, l, accuracy);
681  }
682  if (hp >= PointInside) {
683  if (isEmptyBlockBeforeTable(it))
684  continue;
685  hit = hp;
686  *position = pos;
687  break;
688  }
689  if (hp == PointBefore && pos < *position) {
690  *position = pos;
691  hit = hp;
692  } else if (hp == PointAfter && pos > *position) {
693  *position = pos;
694  hit = hp;
695  }
696  }
697 
698  DEC_INDENT;
699 // LDEBUG << "inside=" << hit << " pos=" << *position;
700  return hit;
701 }
702 
705  int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const
706 {
707  QTextTableData *td = static_cast<QTextTableData *>(data(table));
708 
710  if (rowIt == td->rowPositions.constEnd()) {
711  rowIt = td->rowPositions.constEnd() - 1;
712  } else if (rowIt != td->rowPositions.constBegin()) {
713  --rowIt;
714  }
715 
717  if (colIt == td->columnPositions.constEnd()) {
718  colIt = td->columnPositions.constEnd() - 1;
719  } else if (colIt != td->columnPositions.constBegin()) {
720  --colIt;
721  }
722 
723  QTextTableCell cell = table->cellAt(rowIt - td->rowPositions.constBegin(),
724  colIt - td->columnPositions.constBegin());
725  if (!cell.isValid())
726  return PointBefore;
727 
728  *position = cell.firstPosition();
729 
730  HitPoint hp = hitTest(cell.begin(), PointInside, point - td->cellPosition(cell), position, l, accuracy);
731 
732  if (hp == PointExact)
733  return hp;
734  if (hp == PointAfter)
735  *position = cell.lastPosition();
736  return PointInside;
737 }
738 
741  Qt::HitTestAccuracy accuracy) const
742 {
743  QTextLayout *tl = bl.layout();
744  QRectF textrect = tl->boundingRect();
745  textrect.translate(tl->position());
746 // LDEBUG << " checking block" << bl.position() << "point=" << point
747 // << " tlrect" << textrect;
748  *position = bl.position();
749  if (point.y.toReal() < textrect.top()) {
750 // LDEBUG << " before pos=" << *position;
751  return PointBefore;
752  } else if (point.y.toReal() > textrect.bottom()) {
753  *position += bl.length();
754 // LDEBUG << " after pos=" << *position;
755  return PointAfter;
756  }
757 
758  QPointF pos = point.toPointF() - tl->position();
759 
760  // ### rtl?
761 
762  HitPoint hit = PointInside;
763  *l = tl;
764  int off = 0;
765  for (int i = 0; i < tl->lineCount(); ++i) {
766  QTextLine line = tl->lineAt(i);
767  const QRectF lr = line.naturalTextRect();
768  if (lr.top() > pos.y()) {
769  off = qMin(off, line.textStart());
770  } else if (lr.bottom() <= pos.y()) {
771  off = qMax(off, line.textStart() + line.textLength());
772  } else {
773  if (lr.left() <= pos.x() && lr.right() >= pos.x())
774  hit = PointExact;
775  // when trying to hit an anchor we want it to hit not only in the left
776  // half
777  if (accuracy == Qt::ExactHit)
778  off = line.xToCursor(pos.x(), QTextLine::CursorOnCharacter);
779  else
781  break;
782  }
783  }
784  *position += off;
785 
786 // LDEBUG << " inside=" << hit << " pos=" << *position;
787  return hit;
788 }
789 
790 // ### could be moved to QTextBlock
792 {
793  qreal indent = blockFormat.indent();
794 
795  QTextObject *object = document->objectForFormat(blockFormat);
796  if (object)
797  indent += object->format().toListFormat().indent();
798 
799  if (qIsNull(indent))
800  return 0;
801 
802  qreal scale = 1;
803  if (paintDevice) {
805  }
806 
807  return QFixed::fromReal(indent * scale * document->indentWidth());
808 }
809 
810 void QTextDocumentLayoutPrivate::drawBorder(QPainter *painter, const QRectF &rect, qreal topMargin, qreal bottomMargin,
811  qreal border, const QBrush &brush, QTextFrameFormat::BorderStyle style) const
812 {
813  const qreal pageHeight = document->pageSize().height();
814  const int topPage = pageHeight > 0 ? static_cast<int>(rect.top() / pageHeight) : 0;
815  const int bottomPage = pageHeight > 0 ? static_cast<int>((rect.bottom() + border) / pageHeight) : 0;
816 
817 #ifndef QT_NO_CSSPARSER
818  QCss::BorderStyle cssStyle = static_cast<QCss::BorderStyle>(style + 1);
819 #endif //QT_NO_CSSPARSER
820 
821  bool turn_off_antialiasing = !(painter->renderHints() & QPainter::Antialiasing);
823 
824  for (int i = topPage; i <= bottomPage; ++i) {
825  QRectF clipped = rect.toRect();
826 
827  if (topPage != bottomPage) {
828  clipped.setTop(qMax(clipped.top(), i * pageHeight + topMargin - border));
829  clipped.setBottom(qMin(clipped.bottom(), (i + 1) * pageHeight - bottomMargin));
830 
831  if (clipped.bottom() <= clipped.top())
832  continue;
833  }
834 #ifndef QT_NO_CSSPARSER
835  qDrawEdge(painter, clipped.left(), clipped.top(), clipped.left() + border, clipped.bottom() + border, 0, 0, QCss::LeftEdge, cssStyle, brush);
836  qDrawEdge(painter, clipped.left() + border, clipped.top(), clipped.right() + border, clipped.top() + border, 0, 0, QCss::TopEdge, cssStyle, brush);
837  qDrawEdge(painter, clipped.right(), clipped.top() + border, clipped.right() + border, clipped.bottom(), 0, 0, QCss::RightEdge, cssStyle, brush);
838  qDrawEdge(painter, clipped.left() + border, clipped.bottom(), clipped.right() + border, clipped.bottom() + border, 0, 0, QCss::BottomEdge, cssStyle, brush);
839 #else
840  painter->save();
841  painter->setPen(Qt::NoPen);
842  painter->setBrush(brush);
843  painter->drawRect(QRectF(clipped.left(), clipped.top(), clipped.left() + border, clipped.bottom() + border));
844  painter->drawRect(QRectF(clipped.left() + border, clipped.top(), clipped.right() + border, clipped.top() + border));
845  painter->drawRect(QRectF(clipped.right(), clipped.top() + border, clipped.right() + border, clipped.bottom()));
846  painter->drawRect(QRectF(clipped.left() + border, clipped.bottom(), clipped.right() + border, clipped.bottom() + border));
847  painter->restore();
848 #endif //QT_NO_CSSPARSER
849  }
850  if (turn_off_antialiasing)
851  painter->setRenderHint(QPainter::Antialiasing, false);
852 }
853 
854 void QTextDocumentLayoutPrivate::drawFrameDecoration(QPainter *painter, QTextFrame *frame, QTextFrameData *fd, const QRectF &clip, const QRectF &rect) const
855 {
856 
857  const QBrush bg = frame->frameFormat().background();
858  if (bg != Qt::NoBrush) {
859  QRectF bgRect = rect;
860  bgRect.adjust((fd->leftMargin + fd->border).toReal(),
861  (fd->topMargin + fd->border).toReal(),
862  - (fd->rightMargin + fd->border).toReal(),
863  - (fd->bottomMargin + fd->border).toReal());
864 
865  QRectF gradientRect; // invalid makes it default to bgRect
866  QPointF origin = bgRect.topLeft();
867  if (!frame->parentFrame()) {
868  bgRect = clip;
869  gradientRect.setWidth(painter->device()->width());
870  gradientRect.setHeight(painter->device()->height());
871  }
872  fillBackground(painter, bgRect, bg, origin, gradientRect);
873  }
874  if (fd->border != 0) {
875  painter->save();
876  painter->setBrush(Qt::lightGray);
877  painter->setPen(Qt::NoPen);
878 
879  const qreal leftEdge = rect.left() + fd->leftMargin.toReal();
880  const qreal border = fd->border.toReal();
881  const qreal topMargin = fd->topMargin.toReal();
882  const qreal leftMargin = fd->leftMargin.toReal();
883  const qreal bottomMargin = fd->bottomMargin.toReal();
884  const qreal rightMargin = fd->rightMargin.toReal();
885  const qreal w = rect.width() - 2 * border - leftMargin - rightMargin;
886  const qreal h = rect.height() - 2 * border - topMargin - bottomMargin;
887 
888  drawBorder(painter, QRectF(leftEdge, rect.top() + topMargin, w + border, h + border),
890  border, frame->frameFormat().borderBrush(), frame->frameFormat().borderStyle());
891 
892  painter->restore();
893  }
894 }
895 
897  const QTextTableCell &cell,
898  int r, int c,
899  const int *selectedTableCells)
900 {
901  for (int i = 0; i < cell_context.selections.size(); ++i) {
902  int row_start = selectedTableCells[i * 4];
903  int col_start = selectedTableCells[i * 4 + 1];
904  int num_rows = selectedTableCells[i * 4 + 2];
905  int num_cols = selectedTableCells[i * 4 + 3];
906 
907  if (row_start != -1) {
908  if (r >= row_start && r < row_start + num_rows
909  && c >= col_start && c < col_start + num_cols)
910  {
911  int firstPosition = cell.firstPosition();
912  int lastPosition = cell.lastPosition();
913 
914  // make sure empty cells are still selected
915  if (firstPosition == lastPosition)
916  ++lastPosition;
917 
918  cell_context.selections[i].cursor.setPosition(firstPosition);
919  cell_context.selections[i].cursor.setPosition(lastPosition, QTextCursor::KeepAnchor);
920  } else {
921  cell_context.selections[i].cursor.clearSelection();
922  }
923  }
924 
925  // FullWidthSelection is not useful for tables
926  cell_context.selections[i].format.clearProperty(QTextFormat::FullWidthSelection);
927  }
928 }
929 
932  QTextFrame *frame) const
933 {
934  QTextFrameData *fd = data(frame);
935  // #######
936  if (fd->layoutDirty)
937  return;
938  Q_ASSERT(!fd->sizeDirty);
939  Q_ASSERT(!fd->layoutDirty);
940 
941  const QPointF off = offset + fd->position.toPointF();
942  if (context.clip.isValid()
943  && (off.y() > context.clip.bottom() || off.y() + fd->size.height.toReal() < context.clip.top()
944  || off.x() > context.clip.right() || off.x() + fd->size.width.toReal() < context.clip.left()))
945  return;
946 
947 // LDEBUG << debug_indent << "drawFrame" << frame->firstPosition() << "--" << frame->lastPosition() << "at" << offset;
948 // INC_INDENT;
949 
950  // if the cursor is /on/ a table border we may need to repaint it
951  // afterwards, as we usually draw the decoration first
952  QTextBlock cursorBlockNeedingRepaint;
953  QPointF offsetOfRepaintedCursorBlock = off;
954 
955  QTextTable *table = qobject_cast<QTextTable *>(frame);
956  const QRectF frameRect(off, fd->size.toSizeF());
957 
958  if (table) {
959  const int rows = table->rows();
960  const int columns = table->columns();
961  QTextTableData *td = static_cast<QTextTableData *>(data(table));
962 
963  QVarLengthArray<int> selectedTableCells(context.selections.size() * 4);
964  for (int i = 0; i < context.selections.size(); ++i) {
965  const QAbstractTextDocumentLayout::Selection &s = context.selections.at(i);
966  int row_start = -1, col_start = -1, num_rows = -1, num_cols = -1;
967 
968  if (s.cursor.currentTable() == table)
969  s.cursor.selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
970 
971  selectedTableCells[i * 4] = row_start;
972  selectedTableCells[i * 4 + 1] = col_start;
973  selectedTableCells[i * 4 + 2] = num_rows;
974  selectedTableCells[i * 4 + 3] = num_cols;
975  }
976 
977  QFixed pageHeight = QFixed::fromReal(document->pageSize().height());
978  if (pageHeight <= 0)
979  pageHeight = QFIXED_MAX;
980 
981  const int tableStartPage = (td->position.y / pageHeight).truncate();
982  const int tableEndPage = ((td->position.y + td->size.height) / pageHeight).truncate();
983 
984  qreal border = td->border.toReal();
985  drawFrameDecoration(painter, frame, fd, context.clip, frameRect);
986 
987  // draw the table headers
988  const int headerRowCount = qMin(table->format().headerRowCount(), rows - 1);
989  int page = tableStartPage + 1;
990  while (page <= tableEndPage) {
991  const QFixed pageTop = page * pageHeight + td->effectiveTopMargin + td->cellSpacing + td->border;
992  const qreal headerOffset = (pageTop - td->rowPositions.at(0)).toReal();
993  for (int r = 0; r < headerRowCount; ++r) {
994  for (int c = 0; c < columns; ++c) {
995  QTextTableCell cell = table->cellAt(r, c);
996  QAbstractTextDocumentLayout::PaintContext cell_context = context;
997  adjustContextSelectionsForCell(cell_context, cell, r, c, selectedTableCells.data());
998  QRectF cellRect = td->cellRect(cell);
999 
1000  cellRect.translate(off.x(), headerOffset);
1001  // we need to account for the cell border in the clipping test
1002  int leftAdjust = qMin(qreal(0), 1 - border);
1003  if (cell_context.clip.isValid() && !cellRect.adjusted(leftAdjust, leftAdjust, border, border).intersects(cell_context.clip))
1004  continue;
1005 
1006  drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
1007  &offsetOfRepaintedCursorBlock);
1008  }
1009  }
1010  ++page;
1011  }
1012 
1013  int firstRow = 0;
1014  int lastRow = rows;
1015 
1016  if (context.clip.isValid()) {
1018  if (rowIt != td->rowPositions.constEnd() && rowIt != td->rowPositions.constBegin()) {
1019  --rowIt;
1020  firstRow = rowIt - td->rowPositions.constBegin();
1021  }
1022 
1023  rowIt = qUpperBound(td->rowPositions.constBegin(), td->rowPositions.constEnd(), QFixed::fromReal(context.clip.bottom() - off.y()));
1024  if (rowIt != td->rowPositions.constEnd()) {
1025  ++rowIt;
1026  lastRow = rowIt - td->rowPositions.constBegin();
1027  }
1028  }
1029 
1030  for (int c = 0; c < columns; ++c) {
1031  QTextTableCell cell = table->cellAt(firstRow, c);
1032  firstRow = qMin(firstRow, cell.row());
1033  }
1034 
1035  for (int r = firstRow; r < lastRow; ++r) {
1036  for (int c = 0; c < columns; ++c) {
1037  QTextTableCell cell = table->cellAt(r, c);
1038  QAbstractTextDocumentLayout::PaintContext cell_context = context;
1039  adjustContextSelectionsForCell(cell_context, cell, r, c, selectedTableCells.data());
1040  QRectF cellRect = td->cellRect(cell);
1041 
1042  cellRect.translate(off);
1043  // we need to account for the cell border in the clipping test
1044  int leftAdjust = qMin(qreal(0), 1 - border);
1045  if (cell_context.clip.isValid() && !cellRect.adjusted(leftAdjust, leftAdjust, border, border).intersects(cell_context.clip))
1046  continue;
1047 
1048  drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
1049  &offsetOfRepaintedCursorBlock);
1050  }
1051  }
1052 
1053  } else {
1054  drawFrameDecoration(painter, frame, fd, context.clip, frameRect);
1055 
1056  QTextFrame::Iterator it = frame->begin();
1057 
1058  if (frame == docPrivate->rootFrame())
1060 
1061  QList<QTextFrame *> floats;
1062  for (int i = 0; i < fd->floats.count(); ++i)
1063  floats.append(fd->floats.at(i));
1064 
1065  drawFlow(off, painter, context, it, floats, &cursorBlockNeedingRepaint);
1066  }
1067 
1068  if (cursorBlockNeedingRepaint.isValid()) {
1069  const QPen oldPen = painter->pen();
1070  painter->setPen(context.palette.color(QPalette::Text));
1071  const int cursorPos = context.cursorPosition - cursorBlockNeedingRepaint.position();
1072  cursorBlockNeedingRepaint.layout()->drawCursor(painter, offsetOfRepaintedCursorBlock,
1073  cursorPos, cursorWidth);
1074  painter->setPen(oldPen);
1075  }
1076 
1077 // DEC_INDENT;
1078 
1079  return;
1080 }
1081 
1083  QTextTable *table, QTextTableData *td, int r, int c,
1084  QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset) const
1085 {
1086  QTextTableCell cell = table->cellAt(r, c);
1087  int rspan = cell.rowSpan();
1088  int cspan = cell.columnSpan();
1089  if (rspan != 1) {
1090  int cr = cell.row();
1091  if (cr != r)
1092  return;
1093  }
1094  if (cspan != 1) {
1095  int cc = cell.column();
1096  if (cc != c)
1097  return;
1098  }
1099 
1100  QTextFormat fmt = cell.format();
1101  const QFixed leftPadding = td->leftPadding(fmt);
1102  const QFixed topPadding = td->topPadding(fmt);
1103 
1104  if (td->border != 0) {
1105  const QBrush oldBrush = painter->brush();
1106  const QPen oldPen = painter->pen();
1107 
1108  const qreal border = td->border.toReal();
1109 
1110  QRectF borderRect(cellRect.left() - border, cellRect.top() - border, cellRect.width() + border, cellRect.height() + border);
1111 
1112  // invert the border style for cells
1113  QTextFrameFormat::BorderStyle cellBorder = table->format().borderStyle();
1114  switch (cellBorder) {
1117  break;
1120  break;
1123  break;
1126  break;
1127  default:
1128  break;
1129  }
1130 
1131  qreal topMargin = (td->effectiveTopMargin + td->cellSpacing + td->border).toReal();
1132  qreal bottomMargin = (td->effectiveBottomMargin + td->cellSpacing + td->border).toReal();
1133 
1134  const int headerRowCount = qMin(table->format().headerRowCount(), table->rows() - 1);
1135  if (r >= headerRowCount)
1136  topMargin += td->headerHeight.toReal();
1137 
1138  drawBorder(painter, borderRect, topMargin, bottomMargin,
1139  border, table->format().borderBrush(), cellBorder);
1140 
1141  painter->setBrush(oldBrush);
1142  painter->setPen(oldPen);
1143  }
1144 
1145  const QBrush bg = cell.format().background();
1146  const QPointF brushOrigin = painter->brushOrigin();
1147  if (bg.style() != Qt::NoBrush) {
1148  fillBackground(painter, cellRect, bg, cellRect.topLeft());
1149 
1150  if (bg.style() > Qt::SolidPattern)
1151  painter->setBrushOrigin(cellRect.topLeft());
1152  }
1153 
1154  const QFixed verticalOffset = td->cellVerticalOffsets.at(c + r * table->columns());
1155 
1156  const QPointF cellPos = QPointF(cellRect.left() + leftPadding.toReal(),
1157  cellRect.top() + (topPadding + verticalOffset).toReal());
1158 
1159  QTextBlock repaintBlock;
1160  drawFlow(cellPos, painter, cell_context, cell.begin(),
1161  td->childFrameMap.values(r + c * table->rows()),
1162  &repaintBlock);
1163  if (repaintBlock.isValid()) {
1164  *cursorBlockNeedingRepaint = repaintBlock;
1165  *cursorBlockOffset = cellPos;
1166  }
1167 
1168  if (bg.style() > Qt::SolidPattern)
1169  painter->setBrushOrigin(brushOrigin);
1170 }
1171 
1173  QTextFrame::Iterator it, const QList<QTextFrame *> &floats, QTextBlock *cursorBlockNeedingRepaint) const
1174 {
1175  Q_Q(const QTextDocumentLayout);
1176  const bool inRootFrame = (!it.atEnd() && it.parentFrame() && it.parentFrame()->parentFrame() == 0);
1177 
1178  QVector<QCheckPoint>::ConstIterator lastVisibleCheckPoint = checkPoints.end();
1179  if (inRootFrame && context.clip.isValid()) {
1180  lastVisibleCheckPoint = qLowerBound(checkPoints.begin(), checkPoints.end(), QFixed::fromReal(context.clip.bottom()));
1181  }
1182 
1183  QTextBlock previousBlock;
1184  QTextFrame *previousFrame = 0;
1185 
1186  for (; !it.atEnd(); ++it) {
1187  QTextFrame *c = it.currentFrame();
1188 
1189  if (inRootFrame && !checkPoints.isEmpty()) {
1190  int currentPosInDoc;
1191  if (c)
1192  currentPosInDoc = c->firstPosition();
1193  else
1194  currentPosInDoc = it.currentBlock().position();
1195 
1196  // if we're past what is already laid out then we're better off
1197  // not trying to draw things that may not be positioned correctly yet
1198  if (currentPosInDoc >= checkPoints.last().positionInFrame)
1199  break;
1200 
1201  if (lastVisibleCheckPoint != checkPoints.end()
1202  && context.clip.isValid()
1203  && currentPosInDoc >= lastVisibleCheckPoint->positionInFrame
1204  )
1205  break;
1206  }
1207 
1208  if (c)
1209  drawFrame(offset, painter, context, c);
1210  else {
1212  if (isEmptyBlockAfterTable(it.currentBlock(), previousFrame))
1213  pc.selections.clear();
1214  drawBlock(offset, painter, pc, it.currentBlock(), inRootFrame);
1215  }
1216 
1217  // when entering a table and the previous block is empty
1218  // then layoutFlow 'hides' the block that just causes a
1219  // new line by positioning it /on/ the table border. as we
1220  // draw that block before the table itself the decoration
1221  // 'overpaints' the cursor and we need to paint it afterwards
1222  // again
1223  if (isEmptyBlockBeforeTable(previousBlock, previousBlock.blockFormat(), it)
1224  && previousBlock.contains(context.cursorPosition)
1225  ) {
1226  *cursorBlockNeedingRepaint = previousBlock;
1227  }
1228 
1229  previousBlock = it.currentBlock();
1230  previousFrame = c;
1231  }
1232 
1233  for (int i = 0; i < floats.count(); ++i) {
1234  QTextFrame *frame = floats.at(i);
1235  if (!isFrameFromInlineObject(frame)
1237  continue;
1238 
1239  const int pos = frame->firstPosition() - 1;
1240  QTextCharFormat format = const_cast<QTextDocumentLayout *>(q)->format(pos);
1241  QTextObjectInterface *handler = q->handlerForObject(format.objectType());
1242  if (handler) {
1243  QRectF rect = frameBoundingRectInternal(frame);
1244  handler->drawObject(painter, rect, document, pos, format);
1245  }
1246  }
1247 }
1248 
1251  QTextBlock bl, bool inRootFrame) const
1252 {
1253  const QTextLayout *tl = bl.layout();
1254  QRectF r = tl->boundingRect();
1255  r.translate(offset + tl->position());
1256  if (context.clip.isValid() && (r.bottom() < context.clip.y() || r.top() > context.clip.bottom()))
1257  return;
1258 // LDEBUG << debug_indent << "drawBlock" << bl.position() << "at" << offset << "br" << tl->boundingRect();
1259 
1260  QTextBlockFormat blockFormat = bl.blockFormat();
1261 
1262  QBrush bg = blockFormat.background();
1263  if (bg != Qt::NoBrush) {
1264  QRectF rect = r;
1265 
1266  // extend the background rectangle if we're in the root frame with NoWrap,
1267  // as the rect of the text block will then be only the width of the text
1268  // instead of the full page width
1269  if (inRootFrame && document->pageSize().width() <= 0) {
1270  const QTextFrameData *fd = data(document->rootFrame());
1271  rect.setRight((fd->size.width - fd->rightMargin).toReal());
1272  }
1273 
1274  fillBackground(painter, rect, bg, r.topLeft());
1275  }
1276 
1278  int blpos = bl.position();
1279  int bllen = bl.length();
1280  const QTextCharFormat *selFormat = 0;
1281  for (int i = 0; i < context.selections.size(); ++i) {
1282  const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i);
1283  const int selStart = range.cursor.selectionStart() - blpos;
1284  const int selEnd = range.cursor.selectionEnd() - blpos;
1285  if (selStart < bllen && selEnd > 0
1286  && selEnd > selStart) {
1288  o.start = selStart;
1289  o.length = selEnd - selStart;
1290  o.format = range.format;
1291  selections.append(o);
1293  && bl.contains(range.cursor.position())) {
1294  // for full width selections we don't require an actual selection, just
1295  // a position to specify the line. that's more convenience in usage.
1297  QTextLine l = tl->lineForTextPosition(range.cursor.position() - blpos);
1298  o.start = l.textStart();
1299  o.length = l.textLength();
1300  if (o.start + o.length == bllen - 1)
1301  ++o.length; // include newline
1302  o.format = range.format;
1303  selections.append(o);
1304  }
1305  if (selStart < 0 && selEnd >= 1)
1306  selFormat = &range.format;
1307  }
1308 
1310  if (object && object->format().toListFormat().style() != QTextListFormat::ListStyleUndefined)
1311  drawListItem(offset, painter, context, bl, selFormat);
1312 
1313  QPen oldPen = painter->pen();
1314  painter->setPen(context.palette.color(QPalette::Text));
1315 
1316  tl->draw(painter, offset, selections, context.clip.isValid() ? (context.clip & clipRect) : clipRect);
1317 
1318  if ((context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen)
1319  || (context.cursorPosition < -1 && !tl->preeditAreaText().isEmpty())) {
1320  int cpos = context.cursorPosition;
1321  if (cpos < -1)
1322  cpos = tl->preeditAreaPosition() - (cpos + 2);
1323  else
1324  cpos -= blpos;
1325  tl->drawCursor(painter, offset, cpos, cursorWidth);
1326  }
1327 
1330  painter->setPen(context.palette.color(QPalette::Dark));
1331  qreal y = r.bottom();
1332  if (bl.length() == 1)
1333  y = r.top() + r.height() / 2;
1334 
1335  const qreal middleX = r.left() + r.width() / 2;
1336  painter->drawLine(QLineF(middleX - width / 2, y, middleX + width / 2, y));
1337  }
1338 
1339  painter->setPen(oldPen);
1340 }
1341 
1342 
1345  QTextBlock bl, const QTextCharFormat *selectionFormat) const
1346 {
1347  Q_Q(const QTextDocumentLayout);
1348  const QTextBlockFormat blockFormat = bl.blockFormat();
1349  const QTextCharFormat charFormat = QTextCursor(bl).charFormat();
1350  QFont font(charFormat.font());
1351  if (q->paintDevice())
1352  font = QFont(font, q->paintDevice());
1353 
1354  const QFontMetrics fontMetrics(font);
1355  QTextObject * const object = document->objectForFormat(blockFormat);
1356  const QTextListFormat lf = object->format().toListFormat();
1357  int style = lf.style();
1358  QString itemText;
1359  QSizeF size;
1360 
1361  if (blockFormat.hasProperty(QTextFormat::ListStyle))
1363 
1364  QTextLayout *layout = bl.layout();
1365  if (layout->lineCount() == 0)
1366  return;
1367  QTextLine firstLine = layout->lineAt(0);
1368  Q_ASSERT(firstLine.isValid());
1369  QPointF pos = (offset + layout->position()).toPoint();
1371  {
1372  QRectF textRect = firstLine.naturalTextRect();
1373  pos += textRect.topLeft().toPoint();
1374  if (dir == Qt::RightToLeft)
1375  pos.rx() += textRect.width();
1376  }
1377 
1378  switch (style) {
1384  itemText = static_cast<QTextList *>(object)->itemText(bl);
1385  size.setWidth(fontMetrics.width(itemText));
1386  size.setHeight(fontMetrics.height());
1387  break;
1388 
1392  size.setWidth(fontMetrics.lineSpacing() / 3);
1393  size.setHeight(size.width());
1394  break;
1395 
1397  return;
1398  default: return;
1399  }
1400 
1401  QRectF r(pos, size);
1402 
1403  qreal xoff = fontMetrics.width(QLatin1Char(' '));
1404  if (dir == Qt::LeftToRight)
1405  xoff = -xoff - size.width();
1406  r.translate( xoff, (fontMetrics.height() / 2) - (size.height() / 2));
1407 
1408  // Prevent clipping the left side of the list decorator (on left to
1409  // right layouts) and clipping the right side of the list
1410  // decorator (on right to left layouts).
1411  if ((r.left() < 0) && (dir == Qt::LeftToRight)) {
1412  int horizontalOffset = -r.left();
1413  r.translate(horizontalOffset, 0);
1414  layout->setPosition(layout->position() + QPointF(horizontalOffset, 0));
1415  } else if ((r.right() > document->pageSize().width()) && (dir == Qt::RightToLeft)) {
1416  int horizontalOffset = r.right() - document->pageSize().width();
1417  r.translate(-horizontalOffset, 0);
1418  layout->setPosition(layout->position() - QPointF(horizontalOffset, 0));
1419  }
1420 
1421  painter->save();
1422 
1424 
1425  if (selectionFormat) {
1426  painter->setPen(QPen(selectionFormat->foreground(), 0));
1427  painter->fillRect(r, selectionFormat->background());
1428  } else {
1429  QBrush fg = charFormat.foreground();
1430  if (fg == Qt::NoBrush)
1431  fg = context.palette.text();
1432  painter->setPen(QPen(fg, 0));
1433  }
1434 
1435  QBrush brush = context.palette.brush(QPalette::Text);
1436 
1437  switch (style) {
1443  QTextLayout layout(itemText, font, q->paintDevice());
1444  layout.setCacheEnabled(true);
1446  option.setTextDirection(dir);
1447  layout.setTextOption(option);
1448  layout.beginLayout();
1449  QTextLine line = layout.createLine();
1450  if (line.isValid())
1451  line.setLeadingIncluded(true);
1452  layout.endLayout();
1453  layout.draw(painter, QPointF(r.left(), pos.y()));
1454  break;
1455  }
1457  painter->fillRect(r, brush);
1458  break;
1460  painter->setPen(QPen(brush, 0));
1461  painter->drawEllipse(r.translated(0.5, 0.5)); // pixel align for sharper rendering
1462  break;
1464  painter->setBrush(brush);
1465  painter->setPen(Qt::NoPen);
1466  painter->drawEllipse(r);
1467  break;
1469  break;
1470  default:
1471  break;
1472  }
1473 
1474  painter->restore();
1475 }
1476 
1478 {
1479  if (it.atEnd())
1480  return 0;
1481 
1482  if (it.currentFrame()) {
1483  return data(it.currentFrame())->position.y;
1484  } else {
1485  QTextBlock block = it.currentBlock();
1486  QTextLayout *layout = block.layout();
1487  if (layout->lineCount() == 0)
1488  return QFixed::fromReal(layout->position().y());
1489  else
1490  return QFixed::fromReal(layout->position().y() + layout->lineAt(0).y());
1491  }
1492 }
1493 
1495 {
1496  return flowPosition(f->begin());
1497 }
1498 
1500  int layoutFrom, int layoutTo, QTextTableData *td,
1501  QFixed absoluteTableY, bool withPageBreaks)
1502 {
1503  LDEBUG << "layoutCell";
1504  QTextLayoutStruct layoutStruct;
1505  layoutStruct.frame = t;
1506  layoutStruct.minimumWidth = 0;
1507  layoutStruct.maximumWidth = QFIXED_MAX;
1508  layoutStruct.y = 0;
1509 
1510  const QTextFormat fmt = cell.format();
1511  const QFixed topPadding = td->topPadding(fmt);
1512  if (withPageBreaks) {
1513  layoutStruct.frameY = absoluteTableY + td->rowPositions.at(cell.row()) + topPadding;
1514  }
1515  layoutStruct.x_left = 0;
1516  layoutStruct.x_right = width;
1517  // we get called with different widths all the time (for example for figuring
1518  // out the min/max widths), so we always have to do the full layout ;(
1519  // also when for example in a table layoutFrom/layoutTo affect only one cell,
1520  // making that one cell grow the available width of the other cells may change
1521  // (shrink) and therefore when layoutCell gets called for them they have to
1522  // be re-laid out, even if layoutFrom/layoutTo is not in their range. Hence
1523  // this line:
1524 
1525  layoutStruct.pageHeight = QFixed::fromReal(document->pageSize().height());
1526  if (layoutStruct.pageHeight < 0 || !withPageBreaks)
1527  layoutStruct.pageHeight = QFIXED_MAX;
1528  const int currentPage = layoutStruct.currentPage();
1529  layoutStruct.pageTopMargin = td->effectiveTopMargin + td->cellSpacing + td->border + topPadding;
1530  layoutStruct.pageBottomMargin = td->effectiveBottomMargin + td->cellSpacing + td->border + td->bottomPadding(fmt);
1531  layoutStruct.pageBottom = (currentPage + 1) * layoutStruct.pageHeight - layoutStruct.pageBottomMargin;
1532 
1533  layoutStruct.fullLayout = true;
1534 
1535  QFixed pageTop = currentPage * layoutStruct.pageHeight + layoutStruct.pageTopMargin - layoutStruct.frameY;
1536  layoutStruct.y = qMax(layoutStruct.y, pageTop);
1537 
1538  const QList<QTextFrame *> childFrames = td->childFrameMap.values(cell.row() + cell.column() * t->rows());
1539  for (int i = 0; i < childFrames.size(); ++i) {
1540  QTextFrame *frame = childFrames.at(i);
1541  QTextFrameData *cd = data(frame);
1542  cd->sizeDirty = true;
1543  }
1544 
1545  layoutFlow(cell.begin(), &layoutStruct, layoutFrom, layoutTo, width);
1546 
1547  QFixed floatMinWidth;
1548 
1549  // floats that are located inside the text (like inline images) aren't taken into account by
1550  // layoutFlow with regards to the cell height (layoutStruct->y), so for a safety measure we
1551  // do that here. For example with <td><img align="right" src="..." />blah</td>
1552  // when the image happens to be higher than the text
1553  for (int i = 0; i < childFrames.size(); ++i) {
1554  QTextFrame *frame = childFrames.at(i);
1555  QTextFrameData *cd = data(frame);
1556 
1557  if (frame->frameFormat().position() != QTextFrameFormat::InFlow)
1558  layoutStruct.y = qMax(layoutStruct.y, cd->position.y + cd->size.height);
1559 
1560  floatMinWidth = qMax(floatMinWidth, cd->minimumWidth);
1561  }
1562 
1563  // constraint the maximumWidth by the minimum width of the fixed size floats, to
1564  // keep them visible
1565  layoutStruct.maximumWidth = qMax(layoutStruct.maximumWidth, floatMinWidth);
1566 
1567  // as floats in cells get added to the table's float list but must not affect
1568  // floats in other cells we must clear the list here.
1569  data(t)->floats.clear();
1570 
1571 // qDebug() << "layoutCell done";
1572 
1573  return layoutStruct;
1574 }
1575 
1576 QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom, int layoutTo, QFixed parentY)
1577 {
1578  LDEBUG << "layoutTable";
1579  QTextTableData *td = static_cast<QTextTableData *>(data(table));
1580  Q_ASSERT(td->sizeDirty);
1581  const int rows = table->rows();
1582  const int columns = table->columns();
1583 
1584  const QTextTableFormat fmt = table->format();
1585 
1586  td->childFrameMap.clear();
1587  {
1588  const QList<QTextFrame *> children = table->childFrames();
1589  for (int i = 0; i < children.count(); ++i) {
1590  QTextFrame *frame = children.at(i);
1591  QTextTableCell cell = table->cellAt(frame->firstPosition());
1592  td->childFrameMap.insertMulti(cell.row() + cell.column() * rows, frame);
1593  }
1594  }
1595 
1596  QVector<QTextLength> columnWidthConstraints = fmt.columnWidthConstraints();
1597  if (columnWidthConstraints.size() != columns)
1598  columnWidthConstraints.resize(columns);
1599  Q_ASSERT(columnWidthConstraints.count() == columns);
1600 
1601  const QFixed cellSpacing = td->cellSpacing = QFixed::fromReal(scaleToDevice(fmt.cellSpacing()));
1602  td->deviceScale = scaleToDevice(qreal(1));
1604  const QFixed leftMargin = td->leftMargin + td->border + td->padding;
1605  const QFixed rightMargin = td->rightMargin + td->border + td->padding;
1606  const QFixed topMargin = td->topMargin + td->border + td->padding;
1607 
1608  const QFixed absoluteTableY = parentY + td->position.y;
1609 
1610  const QTextOption::WrapMode oldDefaultWrapMode = docPrivate->defaultTextOption.wrapMode();
1611 
1612 recalc_minmax_widths:
1613 
1614  QFixed remainingWidth = td->contentsWidth;
1615  // two (vertical) borders per cell per column
1616  remainingWidth -= columns * 2 * td->border;
1617  // inter-cell spacing
1618  remainingWidth -= (columns - 1) * cellSpacing;
1619  // cell spacing at the left and right hand side
1620  remainingWidth -= 2 * cellSpacing;
1621  // remember the width used to distribute to percentaged columns
1622  const QFixed initialTotalWidth = remainingWidth;
1623 
1624  td->widths.resize(columns);
1625  td->widths.fill(0);
1626 
1627  td->minWidths.resize(columns);
1628  // start with a minimum width of 0. totally empty
1629  // cells of default created tables are invisible otherwise
1630  // and therefore hardly editable
1631  td->minWidths.fill(1);
1632 
1633  td->maxWidths.resize(columns);
1634  td->maxWidths.fill(QFIXED_MAX);
1635 
1636  // calculate minimum and maximum sizes of the columns
1637  for (int i = 0; i < columns; ++i) {
1638  for (int row = 0; row < rows; ++row) {
1639  const QTextTableCell cell = table->cellAt(row, i);
1640  const int cspan = cell.columnSpan();
1641 
1642  if (cspan > 1 && i != cell.column())
1643  continue;
1644 
1645  const QTextFormat fmt = cell.format();
1646  const QFixed leftPadding = td->leftPadding(fmt);
1647  const QFixed rightPadding = td->rightPadding(fmt);
1648  const QFixed widthPadding = leftPadding + rightPadding;
1649 
1650  // to figure out the min and the max width lay out the cell at
1651  // maximum width. otherwise the maxwidth calculation sometimes
1652  // returns wrong values
1653  QTextLayoutStruct layoutStruct = layoutCell(table, cell, QFIXED_MAX, layoutFrom,
1654  layoutTo, td, absoluteTableY,
1655  /*withPageBreaks =*/false);
1656 
1657  // distribute the minimum width over all columns the cell spans
1658  QFixed widthToDistribute = layoutStruct.minimumWidth + widthPadding;
1659  for (int n = 0; n < cspan; ++n) {
1660  const int col = i + n;
1661  QFixed w = widthToDistribute / (cspan - n);
1662  td->minWidths[col] = qMax(td->minWidths.at(col), w);
1663  widthToDistribute -= td->minWidths.at(col);
1664  if (widthToDistribute <= 0)
1665  break;
1666  }
1667 
1668  QFixed maxW = td->maxWidths.at(i);
1669  if (layoutStruct.maximumWidth != QFIXED_MAX) {
1670  if (maxW == QFIXED_MAX)
1671  maxW = layoutStruct.maximumWidth + widthPadding;
1672  else
1673  maxW = qMax(maxW, layoutStruct.maximumWidth + widthPadding);
1674  }
1675  if (maxW == QFIXED_MAX)
1676  continue;
1677 
1678  widthToDistribute = maxW;
1679  for (int n = 0; n < cspan; ++n) {
1680  const int col = i + n;
1681  QFixed w = widthToDistribute / (cspan - n);
1682  td->maxWidths[col] = qMax(td->minWidths.at(col), w);
1683  widthToDistribute -= td->maxWidths.at(col);
1684  if (widthToDistribute <= 0)
1685  break;
1686  }
1687  }
1688  }
1689 
1690  // set fixed values, figure out total percentages used and number of
1691  // variable length cells. Also assign the minimum width for variable columns.
1692  QFixed totalPercentage;
1693  int variableCols = 0;
1694  QFixed totalMinWidth = 0;
1695  for (int i = 0; i < columns; ++i) {
1696  const QTextLength &length = columnWidthConstraints.at(i);
1697  if (length.type() == QTextLength::FixedLength) {
1698  td->minWidths[i] = td->widths[i] = qMax(scaleToDevice(QFixed::fromReal(length.rawValue())), td->minWidths.at(i));
1699  remainingWidth -= td->widths.at(i);
1700  } else if (length.type() == QTextLength::PercentageLength) {
1701  totalPercentage += QFixed::fromReal(length.rawValue());
1702  } else if (length.type() == QTextLength::VariableLength) {
1703  variableCols++;
1704 
1705  td->widths[i] = td->minWidths.at(i);
1706  remainingWidth -= td->minWidths.at(i);
1707  }
1708  totalMinWidth += td->minWidths.at(i);
1709  }
1710 
1711  // set percentage values
1712  {
1713  const QFixed totalPercentagedWidth = initialTotalWidth * totalPercentage / 100;
1714  QFixed remainingMinWidths = totalMinWidth;
1715  for (int i = 0; i < columns; ++i) {
1716  remainingMinWidths -= td->minWidths.at(i);
1717  if (columnWidthConstraints.at(i).type() == QTextLength::PercentageLength) {
1718  const QFixed allottedPercentage = QFixed::fromReal(columnWidthConstraints.at(i).rawValue());
1719 
1720  const QFixed percentWidth = totalPercentagedWidth * allottedPercentage / totalPercentage;
1721  if (percentWidth >= td->minWidths.at(i)) {
1722  td->widths[i] = qBound(td->minWidths.at(i), percentWidth, remainingWidth - remainingMinWidths);
1723  } else {
1724  td->widths[i] = td->minWidths.at(i);
1725  }
1726  remainingWidth -= td->widths.at(i);
1727  }
1728  }
1729  }
1730 
1731  // for variable columns distribute the remaining space
1732  if (variableCols > 0 && remainingWidth > 0) {
1733  QVarLengthArray<int> columnsWithProperMaxSize;
1734  for (int i = 0; i < columns; ++i)
1735  if (columnWidthConstraints.at(i).type() == QTextLength::VariableLength
1736  && td->maxWidths.at(i) != QFIXED_MAX)
1737  columnsWithProperMaxSize.append(i);
1738 
1739  QFixed lastRemainingWidth = remainingWidth;
1740  while (remainingWidth > 0) {
1741  for (int k = 0; k < columnsWithProperMaxSize.count(); ++k) {
1742  const int col = columnsWithProperMaxSize[k];
1743  const int colsLeft = columnsWithProperMaxSize.count() - k;
1744  const QFixed w = qMin(td->maxWidths.at(col) - td->widths.at(col), remainingWidth / colsLeft);
1745  td->widths[col] += w;
1746  remainingWidth -= w;
1747  }
1748  if (remainingWidth == lastRemainingWidth)
1749  break;
1750  lastRemainingWidth = remainingWidth;
1751  }
1752 
1753  if (remainingWidth > 0
1754  // don't unnecessarily grow variable length sized tables
1755  && fmt.width().type() != QTextLength::VariableLength) {
1756  const QFixed widthPerAnySizedCol = remainingWidth / variableCols;
1757  for (int col = 0; col < columns; ++col) {
1758  if (columnWidthConstraints.at(col).type() == QTextLength::VariableLength)
1759  td->widths[col] += widthPerAnySizedCol;
1760  }
1761  }
1762  }
1763 
1764  td->columnPositions.resize(columns);
1765  td->columnPositions[0] = leftMargin /*includes table border*/ + cellSpacing + td->border;
1766 
1767  for (int i = 1; i < columns; ++i)
1768  td->columnPositions[i] = td->columnPositions.at(i-1) + td->widths.at(i-1) + 2 * td->border + cellSpacing;
1769 
1770  // - margin to compensate the + margin in columnPositions[0]
1771  const QFixed contentsWidth = td->columnPositions.last() + td->widths.last() + td->padding + td->border + cellSpacing - leftMargin;
1772 
1773  // if the table is too big and causes an overflow re-do the layout with WrapAnywhere as wrap
1774  // mode
1776  && contentsWidth > td->contentsWidth) {
1778  // go back to the top of the function
1779  goto recalc_minmax_widths;
1780  }
1781 
1782  td->contentsWidth = contentsWidth;
1783 
1784  docPrivate->defaultTextOption.setWrapMode(oldDefaultWrapMode);
1785 
1786  td->heights.resize(rows);
1787  td->heights.fill(0);
1788 
1789  td->rowPositions.resize(rows);
1790  td->rowPositions[0] = topMargin /*includes table border*/ + cellSpacing + td->border;
1791 
1792  bool haveRowSpannedCells = false;
1793 
1794  // need to keep track of cell heights for vertical alignment
1795  QVector<QFixed> cellHeights;
1796  cellHeights.reserve(rows * columns);
1797 
1798  QFixed pageHeight = QFixed::fromReal(document->pageSize().height());
1799  if (pageHeight <= 0)
1800  pageHeight = QFIXED_MAX;
1801 
1802  QVector<QFixed> heightToDistribute;
1803  heightToDistribute.resize(columns);
1804 
1805  td->headerHeight = 0;
1806  const int headerRowCount = qMin(table->format().headerRowCount(), rows - 1);
1807  const QFixed originalTopMargin = td->effectiveTopMargin;
1808  bool hasDroppedTable = false;
1809 
1810  // now that we have the column widths we can lay out all cells with the right width.
1811  // spanning cells are only allowed to grow the last row spanned by the cell.
1812  //
1813  // ### this could be made faster by iterating over the cells array of QTextTable
1814  for (int r = 0; r < rows; ++r) {
1815  td->calcRowPosition(r);
1816 
1817  const int tableStartPage = (absoluteTableY / pageHeight).truncate();
1818  const int currentPage = ((td->rowPositions[r] + absoluteTableY) / pageHeight).truncate();
1819  const QFixed pageBottom = (currentPage + 1) * pageHeight - td->effectiveBottomMargin - absoluteTableY - cellSpacing - td->border;
1820  const QFixed pageTop = currentPage * pageHeight + td->effectiveTopMargin - absoluteTableY + cellSpacing + td->border;
1821  const QFixed nextPageTop = pageTop + pageHeight;
1822 
1823  if (td->rowPositions[r] > pageBottom)
1824  td->rowPositions[r] = nextPageTop;
1825  else if (td->rowPositions[r] < pageTop)
1826  td->rowPositions[r] = pageTop;
1827 
1828  bool dropRowToNextPage = true;
1829  int cellCountBeforeRow = cellHeights.size();
1830 
1831  // if we drop the row to the next page we need to subtract the drop
1832  // distance from any row spanning cells
1833  QFixed dropDistance = 0;
1834 
1835 relayout:
1836  const int rowStartPage = ((td->rowPositions[r] + absoluteTableY) / pageHeight).truncate();
1837  // if any of the header rows or the first non-header row start on the next page
1838  // then the entire header should be dropped
1839  if (r <= headerRowCount && rowStartPage > tableStartPage && !hasDroppedTable) {
1840  td->rowPositions[0] = nextPageTop;
1841  cellHeights.clear();
1842  td->effectiveTopMargin = originalTopMargin;
1843  hasDroppedTable = true;
1844  r = -1;
1845  continue;
1846  }
1847 
1848  int rowCellCount = 0;
1849  for (int c = 0; c < columns; ++c) {
1850  QTextTableCell cell = table->cellAt(r, c);
1851  const int rspan = cell.rowSpan();
1852  const int cspan = cell.columnSpan();
1853 
1854  if (cspan > 1 && cell.column() != c)
1855  continue;
1856 
1857  if (rspan > 1) {
1858  haveRowSpannedCells = true;
1859 
1860  const int cellRow = cell.row();
1861  if (cellRow != r) {
1862  // the last row gets all the remaining space
1863  if (cellRow + rspan - 1 == r)
1864  td->heights[r] = qMax(td->heights.at(r), heightToDistribute.at(c) - dropDistance);
1865  continue;
1866  }
1867  }
1868 
1869  const QTextFormat fmt = cell.format();
1870 
1871  const QFixed topPadding = td->topPadding(fmt);
1872  const QFixed bottomPadding = td->bottomPadding(fmt);
1873  const QFixed leftPadding = td->leftPadding(fmt);
1874  const QFixed rightPadding = td->rightPadding(fmt);
1875  const QFixed widthPadding = leftPadding + rightPadding;
1876 
1877  ++rowCellCount;
1878 
1879  const QFixed width = td->cellWidth(c, cspan) - widthPadding;
1880  QTextLayoutStruct layoutStruct = layoutCell(table, cell, width,
1881  layoutFrom, layoutTo,
1882  td, absoluteTableY,
1883  /*withPageBreaks =*/true);
1884 
1885  const QFixed height = layoutStruct.y + bottomPadding + topPadding;
1886 
1887  if (rspan > 1)
1888  heightToDistribute[c] = height + dropDistance;
1889  else
1890  td->heights[r] = qMax(td->heights.at(r), height);
1891 
1892  cellHeights.append(layoutStruct.y);
1893 
1894  QFixed childPos = td->rowPositions.at(r) + topPadding + flowPosition(cell.begin());
1895  if (childPos < pageBottom)
1896  dropRowToNextPage = false;
1897  }
1898 
1899  if (rowCellCount > 0 && dropRowToNextPage) {
1900  dropDistance = nextPageTop - td->rowPositions[r];
1901  td->rowPositions[r] = nextPageTop;
1902  td->heights[r] = 0;
1903  dropRowToNextPage = false;
1904  cellHeights.resize(cellCountBeforeRow);
1905  if (r > headerRowCount)
1906  td->heights[r-1] = pageBottom - td->rowPositions[r-1];
1907  goto relayout;
1908  }
1909 
1910  if (haveRowSpannedCells) {
1911  const QFixed effectiveHeight = td->heights.at(r) + td->border + cellSpacing + td->border;
1912  for (int c = 0; c < columns; ++c)
1913  heightToDistribute[c] = qMax(heightToDistribute.at(c) - effectiveHeight - dropDistance, QFixed(0));
1914  }
1915 
1916  if (r == headerRowCount - 1) {
1917  td->headerHeight = td->rowPositions[r] + td->heights[r] - td->rowPositions[0] + td->cellSpacing + 2 * td->border;
1918  td->headerHeight -= td->headerHeight * (td->headerHeight / pageHeight).truncate();
1919  td->effectiveTopMargin += td->headerHeight;
1920  }
1921  }
1922 
1923  td->effectiveTopMargin = originalTopMargin;
1924 
1925  // now that all cells have been properly laid out, we can compute the
1926  // vertical offsets for vertical alignment
1927  td->cellVerticalOffsets.resize(rows * columns);
1928  int cellIndex = 0;
1929  for (int r = 0; r < rows; ++r) {
1930  for (int c = 0; c < columns; ++c) {
1931  QTextTableCell cell = table->cellAt(r, c);
1932  if (cell.row() != r || cell.column() != c)
1933  continue;
1934 
1935  const int rowSpan = cell.rowSpan();
1936  const QFixed availableHeight = td->rowPositions.at(r + rowSpan - 1) + td->heights.at(r + rowSpan - 1) - td->rowPositions.at(r);
1937 
1938  const QTextCharFormat cellFormat = cell.format();
1939  const QFixed cellHeight = cellHeights.at(cellIndex++) + td->topPadding(cellFormat) + td->bottomPadding(cellFormat);
1940 
1941  QFixed offset = 0;
1942  switch (cellFormat.verticalAlignment()) {
1944  offset = (availableHeight - cellHeight) / 2;
1945  break;
1947  offset = availableHeight - cellHeight;
1948  break;
1949  default:
1950  break;
1951  };
1952 
1953  for (int rd = 0; rd < cell.rowSpan(); ++rd) {
1954  for (int cd = 0; cd < cell.columnSpan(); ++cd) {
1955  const int index = (c + cd) + (r + rd) * columns;
1956  td->cellVerticalOffsets[index] = offset;
1957  }
1958  }
1959  }
1960  }
1961 
1962  td->minimumWidth = td->columnPositions.at(0);
1963  for (int i = 0; i < columns; ++i) {
1964  td->minimumWidth += td->minWidths.at(i) + 2 * td->border + cellSpacing;
1965  }
1966  td->minimumWidth += rightMargin - td->border;
1967 
1968  td->maximumWidth = td->columnPositions.at(0);
1969  for (int i = 0; i < columns; ++i)
1970  if (td->maxWidths.at(i) != QFIXED_MAX)
1971  td->maximumWidth += td->maxWidths.at(i) + 2 * td->border + cellSpacing;
1972  td->maximumWidth += rightMargin - td->border;
1973 
1974  td->updateTableSize();
1975  td->sizeDirty = false;
1976  return QRectF(); // invalid rect -> update everything
1977 }
1978 
1980 {
1981  QTextFrameData *fd = data(frame);
1982 
1983  QTextFrame *parent = frame->parentFrame();
1984  Q_ASSERT(parent);
1985  QTextFrameData *pd = data(parent);
1986  Q_ASSERT(pd && pd->currentLayoutStruct);
1987 
1988  QTextLayoutStruct *layoutStruct = pd->currentLayoutStruct;
1989 
1990  if (!pd->floats.contains(frame))
1991  pd->floats.append(frame);
1992  fd->layoutDirty = true;
1993  Q_ASSERT(!fd->sizeDirty);
1994 
1995 // qDebug() << "positionFloat:" << frame << "width=" << fd->size.width;
1996  QFixed y = layoutStruct->y;
1997  if (currentLine) {
1998  QFixed left, right;
1999  floatMargins(y, layoutStruct, &left, &right);
2000 // qDebug() << "have line: right=" << right << "left=" << left << "textWidth=" << currentLine->width();
2001  if (right - left < QFixed::fromReal(currentLine->naturalTextWidth()) + fd->size.width) {
2002  layoutStruct->pendingFloats.append(frame);
2003 // qDebug() << " adding to pending list";
2004  return;
2005  }
2006  }
2007 
2008  bool frameSpansIntoNextPage = (y + layoutStruct->frameY + fd->size.height > layoutStruct->pageBottom);
2009  if (frameSpansIntoNextPage && fd->size.height <= layoutStruct->pageHeight) {
2010  layoutStruct->newPage();
2011  y = layoutStruct->y;
2012 
2013  frameSpansIntoNextPage = false;
2014  }
2015 
2016  y = findY(y, layoutStruct, fd->size.width);
2017 
2018  QFixed left, right;
2019  floatMargins(y, layoutStruct, &left, &right);
2020 
2021  if (frame->frameFormat().position() == QTextFrameFormat::FloatLeft) {
2022  fd->position.x = left;
2023  fd->position.y = y;
2024  } else {
2025  fd->position.x = right - fd->size.width;
2026  fd->position.y = y;
2027  }
2028 
2029  layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, fd->minimumWidth);
2030  layoutStruct->maximumWidth = qMin(layoutStruct->maximumWidth, fd->maximumWidth);
2031 
2032 // qDebug()<< "float positioned at " << fd->position.x << fd->position.y;
2033  fd->layoutDirty = false;
2034 
2035  // If the frame is a table, then positioning it will affect the size if it covers more than
2036  // one page, because of page breaks and repeating the header.
2037  if (qobject_cast<QTextTable *>(frame) != 0)
2038  fd->sizeDirty = frameSpansIntoNextPage;
2039 }
2040 
2041 QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed parentY)
2042 {
2043  LDEBUG << "layoutFrame (pre)";
2044  Q_ASSERT(data(f)->sizeDirty);
2045 // qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame());
2046 
2047  QTextFrameFormat fformat = f->frameFormat();
2048 
2049  QTextFrame *parent = f->parentFrame();
2050  const QTextFrameData *pd = parent ? data(parent) : 0;
2051 
2052  const qreal maximumWidth = qMax(qreal(0), pd ? pd->contentsWidth.toReal() : document->pageSize().width());
2053  QFixed width = QFixed::fromReal(fformat.width().value(maximumWidth));
2054  if (fformat.width().type() == QTextLength::FixedLength)
2055  width = scaleToDevice(width);
2056 
2057  const QFixed maximumHeight = pd ? pd->contentsHeight : -1;
2058  const QFixed height = (maximumHeight != -1 || fformat.height().type() != QTextLength::PercentageLength)
2059  ? QFixed::fromReal(fformat.height().value(maximumHeight.toReal()))
2060  : -1;
2061 
2062  return layoutFrame(f, layoutFrom, layoutTo, width, height, parentY);
2063 }
2064 
2065 QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed frameWidth, QFixed frameHeight, QFixed parentY)
2066 {
2067  LDEBUG << "layoutFrame from=" << layoutFrom << "to=" << layoutTo;
2068  Q_ASSERT(data(f)->sizeDirty);
2069 // qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame());
2070 
2071  QTextFrameData *fd = data(f);
2072  QFixed newContentsWidth;
2073 
2074  {
2075  QTextFrameFormat fformat = f->frameFormat();
2076  // set sizes of this frame from the format
2077  fd->topMargin = QFixed::fromReal(fformat.topMargin());
2078  fd->bottomMargin = QFixed::fromReal(fformat.bottomMargin());
2079  fd->leftMargin = QFixed::fromReal(fformat.leftMargin());
2080  fd->rightMargin = QFixed::fromReal(fformat.rightMargin());
2081  fd->border = QFixed::fromReal(fformat.border());
2082  fd->padding = QFixed::fromReal(fformat.padding());
2083 
2084  QTextFrame *parent = f->parentFrame();
2085  const QTextFrameData *pd = parent ? data(parent) : 0;
2086 
2087  // accumulate top and bottom margins
2088  if (parent) {
2089  fd->effectiveTopMargin = pd->effectiveTopMargin + fd->topMargin + fd->border + fd->padding;
2091 
2092  if (qobject_cast<QTextTable *>(parent)) {
2093  const QTextTableData *td = static_cast<const QTextTableData *>(pd);
2094  fd->effectiveTopMargin += td->cellSpacing + td->border + td->cellPadding;
2095  fd->effectiveBottomMargin += td->cellSpacing + td->border + td->cellPadding;
2096  }
2097  } else {
2098  fd->effectiveTopMargin = fd->topMargin + fd->border + fd->padding;
2099  fd->effectiveBottomMargin = fd->bottomMargin + fd->border + fd->padding;
2100  }
2101 
2102  newContentsWidth = frameWidth - 2*(fd->border + fd->padding)
2103  - fd->leftMargin - fd->rightMargin;
2104 
2105  if (frameHeight != -1) {
2106  fd->contentsHeight = frameHeight - 2*(fd->border + fd->padding)
2107  - fd->topMargin - fd->bottomMargin;
2108  } else {
2109  fd->contentsHeight = frameHeight;
2110  }
2111  }
2112 
2113  if (isFrameFromInlineObject(f)) {
2114  // never reached, handled in resizeInlineObject/positionFloat instead
2115  return QRectF();
2116  }
2117 
2118  if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
2119  fd->contentsWidth = newContentsWidth;
2120  return layoutTable(table, layoutFrom, layoutTo, parentY);
2121  }
2122 
2123  // set fd->contentsWidth temporarily, so that layoutFrame for the children
2124  // picks the right width. We'll initialize it properly at the end of this
2125  // function.
2126  fd->contentsWidth = newContentsWidth;
2127 
2128  QTextLayoutStruct layoutStruct;
2129  layoutStruct.frame = f;
2130  layoutStruct.x_left = fd->leftMargin + fd->border + fd->padding;
2131  layoutStruct.x_right = layoutStruct.x_left + newContentsWidth;
2132  layoutStruct.y = fd->topMargin + fd->border + fd->padding;
2133  layoutStruct.frameY = parentY + fd->position.y;
2134  layoutStruct.contentsWidth = 0;
2135  layoutStruct.minimumWidth = 0;
2136  layoutStruct.maximumWidth = QFIXED_MAX;
2137  layoutStruct.fullLayout = fd->oldContentsWidth != newContentsWidth;
2138  layoutStruct.updateRect = QRectF(QPointF(0, 0), QSizeF(qreal(INT_MAX), qreal(INT_MAX)));
2139  LDEBUG << "layoutStruct: x_left" << layoutStruct.x_left << "x_right" << layoutStruct.x_right
2140  << "fullLayout" << layoutStruct.fullLayout;
2141  fd->oldContentsWidth = newContentsWidth;
2142 
2143  layoutStruct.pageHeight = QFixed::fromReal(document->pageSize().height());
2144  if (layoutStruct.pageHeight < 0)
2145  layoutStruct.pageHeight = QFIXED_MAX;
2146 
2147  const int currentPage = layoutStruct.pageHeight == 0 ? 0 : (layoutStruct.frameY / layoutStruct.pageHeight).truncate();
2148  layoutStruct.pageTopMargin = fd->effectiveTopMargin;
2149  layoutStruct.pageBottomMargin = fd->effectiveBottomMargin;
2150  layoutStruct.pageBottom = (currentPage + 1) * layoutStruct.pageHeight - layoutStruct.pageBottomMargin;
2151 
2152  if (!f->parentFrame())
2153  idealWidth = 0; // reset
2154 
2155  QTextFrame::Iterator it = f->begin();
2156  layoutFlow(it, &layoutStruct, layoutFrom, layoutTo);
2157 
2158  QFixed maxChildFrameWidth = 0;
2160  for (int i = 0; i < children.size(); ++i) {
2161  QTextFrame *c = children.at(i);
2162  QTextFrameData *cd = data(c);
2163  maxChildFrameWidth = qMax(maxChildFrameWidth, cd->size.width);
2164  }
2165 
2166  const QFixed marginWidth = 2*(fd->border + fd->padding) + fd->leftMargin + fd->rightMargin;
2167  if (!f->parentFrame()) {
2168  idealWidth = qMax(maxChildFrameWidth, layoutStruct.contentsWidth).toReal();
2169  idealWidth += marginWidth.toReal();
2170  }
2171 
2172  QFixed actualWidth = qMax(newContentsWidth, qMax(maxChildFrameWidth, layoutStruct.contentsWidth));
2173  fd->contentsWidth = actualWidth;
2174  if (newContentsWidth <= 0) { // nowrap layout?
2175  fd->contentsWidth = newContentsWidth;
2176  }
2177 
2178  fd->minimumWidth = layoutStruct.minimumWidth;
2179  fd->maximumWidth = layoutStruct.maximumWidth;
2180 
2181  fd->size.height = fd->contentsHeight == -1
2182  ? layoutStruct.y + fd->border + fd->padding + fd->bottomMargin
2183  : fd->contentsHeight + 2*(fd->border + fd->padding) + fd->topMargin + fd->bottomMargin;
2184  fd->size.width = actualWidth + marginWidth;
2185  fd->sizeDirty = false;
2186  if (layoutStruct.updateRectForFloats.isValid())
2187  layoutStruct.updateRect |= layoutStruct.updateRectForFloats;
2188  return layoutStruct.updateRect;
2189 }
2190 
2192  int layoutFrom, int layoutTo, QFixed width)
2193 {
2194  LDEBUG << "layoutFlow from=" << layoutFrom << "to=" << layoutTo;
2195  QTextFrameData *fd = data(layoutStruct->frame);
2196 
2197  fd->currentLayoutStruct = layoutStruct;
2198 
2199  QTextFrame::Iterator previousIt;
2200 
2201  const bool inRootFrame = (it.parentFrame() == document->rootFrame());
2202  if (inRootFrame) {
2203  bool redoCheckPoints = layoutStruct->fullLayout || checkPoints.isEmpty();
2204 
2205  if (!redoCheckPoints) {
2207  if (checkPoint != checkPoints.end()) {
2208  if (checkPoint != checkPoints.begin())
2209  --checkPoint;
2210 
2211  layoutStruct->y = checkPoint->y;
2212  layoutStruct->frameY = checkPoint->frameY;
2213  layoutStruct->minimumWidth = checkPoint->minimumWidth;
2214  layoutStruct->maximumWidth = checkPoint->maximumWidth;
2215  layoutStruct->contentsWidth = checkPoint->contentsWidth;
2216 
2217  if (layoutStruct->pageHeight > 0) {
2218  int page = layoutStruct->currentPage();
2219  layoutStruct->pageBottom = (page + 1) * layoutStruct->pageHeight - layoutStruct->pageBottomMargin;
2220  }
2221 
2222  it = frameIteratorForTextPosition(checkPoint->positionInFrame);
2223  checkPoints.resize(checkPoint - checkPoints.begin() + 1);
2224 
2225  if (checkPoint != checkPoints.begin()) {
2226  previousIt = it;
2227  --previousIt;
2228  }
2229  } else {
2230  redoCheckPoints = true;
2231  }
2232  }
2233 
2234  if (redoCheckPoints) {
2235  checkPoints.clear();
2236  QCheckPoint cp;
2237  cp.y = layoutStruct->y;
2238  cp.frameY = layoutStruct->frameY;
2239  cp.positionInFrame = 0;
2240  cp.minimumWidth = layoutStruct->minimumWidth;
2241  cp.maximumWidth = layoutStruct->maximumWidth;
2242  cp.contentsWidth = layoutStruct->contentsWidth;
2243  checkPoints.append(cp);
2244  }
2245  }
2246 
2247  QTextBlockFormat previousBlockFormat = previousIt.currentBlock().blockFormat();
2248 
2249  QFixed maximumBlockWidth = 0;
2250  while (!it.atEnd()) {
2251  QTextFrame *c = it.currentFrame();
2252 
2253  int docPos;
2254  if (it.currentFrame())
2255  docPos = it.currentFrame()->firstPosition();
2256  else
2257  docPos = it.currentBlock().position();
2258 
2259  if (inRootFrame) {
2260  if (qAbs(layoutStruct->y - checkPoints.last().y) > 2000) {
2261  QFixed left, right;
2262  floatMargins(layoutStruct->y, layoutStruct, &left, &right);
2263  if (left == layoutStruct->x_left && right == layoutStruct->x_right) {
2264  QCheckPoint p;
2265  p.y = layoutStruct->y;
2266  p.frameY = layoutStruct->frameY;
2267  p.positionInFrame = docPos;
2268  p.minimumWidth = layoutStruct->minimumWidth;
2269  p.maximumWidth = layoutStruct->maximumWidth;
2270  p.contentsWidth = layoutStruct->contentsWidth;
2271  checkPoints.append(p);
2272 
2273  if (currentLazyLayoutPosition != -1
2275  break;
2276 
2277  }
2278  }
2279  }
2280 
2281  if (c) {
2282  // position child frame
2283  QTextFrameData *cd = data(c);
2284 
2285  QTextFrameFormat fformat = c->frameFormat();
2286 
2287  if (fformat.position() == QTextFrameFormat::InFlow) {
2289  layoutStruct->newPage();
2290 
2291  QFixed left, right;
2292  floatMargins(layoutStruct->y, layoutStruct, &left, &right);
2293  left = qMax(left, layoutStruct->x_left);
2294  right = qMin(right, layoutStruct->x_right);
2295 
2296  if (right - left < cd->size.width) {
2297  layoutStruct->y = findY(layoutStruct->y, layoutStruct, cd->size.width);
2298  floatMargins(layoutStruct->y, layoutStruct, &left, &right);
2299  }
2300 
2301  QFixedPoint pos(left, layoutStruct->y);
2302 
2303  Qt::Alignment align = Qt::AlignLeft;
2304 
2305  QTextTable *table = qobject_cast<QTextTable *>(c);
2306 
2307  if (table)
2308  align = table->format().alignment() & Qt::AlignHorizontal_Mask;
2309 
2310  // detect whether we have any alignment in the document that disallows optimizations,
2311  // such as not laying out the document again in a textedit with wrapping disabled.
2312  if (inRootFrame && !(align & Qt::AlignLeft))
2313  contentHasAlignment = true;
2314 
2315  cd->position = pos;
2316 
2317  if (document->pageSize().height() > 0.0f)
2318  cd->sizeDirty = true;
2319 
2320  if (cd->sizeDirty) {
2321  if (width != 0)
2322  layoutFrame(c, layoutFrom, layoutTo, width, -1, layoutStruct->frameY);
2323  else
2324  layoutFrame(c, layoutFrom, layoutTo, layoutStruct->frameY);
2325 
2326  QFixed absoluteChildPos = table ? pos.y + static_cast<QTextTableData *>(data(table))->rowPositions.at(0) : pos.y + firstChildPos(c);
2327  absoluteChildPos += layoutStruct->frameY;
2328 
2329  // drop entire frame to next page if first child of frame is on next page
2330  if (absoluteChildPos > layoutStruct->pageBottom) {
2331  layoutStruct->newPage();
2332  pos.y = layoutStruct->y;
2333 
2334  cd->position = pos;
2335  cd->sizeDirty = true;
2336 
2337  if (width != 0)
2338  layoutFrame(c, layoutFrom, layoutTo, width, -1, layoutStruct->frameY);
2339  else
2340  layoutFrame(c, layoutFrom, layoutTo, layoutStruct->frameY);
2341  }
2342  }
2343 
2344  // align only if there is space for alignment
2345  if (right - left > cd->size.width) {
2346  if (align & Qt::AlignRight)
2347  pos.x += layoutStruct->x_right - cd->size.width;
2348  else if (align & Qt::AlignHCenter)
2349  pos.x += (layoutStruct->x_right - cd->size.width) / 2;
2350  }
2351 
2352  cd->position = pos;
2353 
2354  layoutStruct->y += cd->size.height;
2355  const int page = layoutStruct->currentPage();
2356  layoutStruct->pageBottom = (page + 1) * layoutStruct->pageHeight - layoutStruct->pageBottomMargin;
2357 
2358  cd->layoutDirty = false;
2359 
2361  layoutStruct->newPage();
2362  } else {
2363  QRectF oldFrameRect(cd->position.toPointF(), cd->size.toSizeF());
2364  QRectF updateRect;
2365 
2366  if (cd->sizeDirty)
2367  updateRect = layoutFrame(c, layoutFrom, layoutTo);
2368 
2369  positionFloat(c);
2370 
2371  // If the size was made dirty when the position was set, layout again
2372  if (cd->sizeDirty)
2373  updateRect = layoutFrame(c, layoutFrom, layoutTo);
2374 
2375  QRectF frameRect(cd->position.toPointF(), cd->size.toSizeF());
2376 
2377  if (frameRect == oldFrameRect && updateRect.isValid())
2378  updateRect.translate(cd->position.toPointF());
2379  else
2380  updateRect = frameRect;
2381 
2382  layoutStruct->addUpdateRectForFloat(updateRect);
2383  if (oldFrameRect.isValid())
2384  layoutStruct->addUpdateRectForFloat(oldFrameRect);
2385  }
2386 
2387  layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, cd->minimumWidth);
2388  layoutStruct->maximumWidth = qMin(layoutStruct->maximumWidth, cd->maximumWidth);
2389 
2390  previousIt = it;
2391  ++it;
2392  } else {
2393  QTextFrame::Iterator lastIt;
2394  if (!previousIt.atEnd())
2395  lastIt = previousIt;
2396  previousIt = it;
2397  QTextBlock block = it.currentBlock();
2398  ++it;
2399 
2400  const QTextBlockFormat blockFormat = block.blockFormat();
2401 
2403  layoutStruct->newPage();
2404 
2405  const QFixed origY = layoutStruct->y;
2406  const QFixed origPageBottom = layoutStruct->pageBottom;
2407  const QFixed origMaximumWidth = layoutStruct->maximumWidth;
2408  layoutStruct->maximumWidth = 0;
2409 
2410  const QTextBlockFormat *previousBlockFormatPtr = 0;
2411  if (lastIt.currentBlock().isValid())
2412  previousBlockFormatPtr = &previousBlockFormat;
2413 
2414  // layout and position child block
2415  layoutBlock(block, docPos, blockFormat, layoutStruct, layoutFrom, layoutTo, previousBlockFormatPtr);
2416 
2417  // detect whether we have any alignment in the document that disallows optimizations,
2418  // such as not laying out the document again in a textedit with wrapping disabled.
2419  if (inRootFrame && !(block.layout()->textOption().alignment() & Qt::AlignLeft))
2420  contentHasAlignment = true;
2421 
2422  // if the block right before a table is empty 'hide' it by
2423  // positioning it into the table border
2424  if (isEmptyBlockBeforeTable(block, blockFormat, it)) {
2425  const QTextBlock lastBlock = lastIt.currentBlock();
2426  const qreal lastBlockBottomMargin = lastBlock.isValid() ? lastBlock.blockFormat().bottomMargin() : 0.0f;
2427  layoutStruct->y = origY + QFixed::fromReal(qMax(lastBlockBottomMargin, block.blockFormat().topMargin()));
2428  layoutStruct->pageBottom = origPageBottom;
2429  } else {
2430  // if the block right after a table is empty then 'hide' it, too
2431  if (isEmptyBlockAfterTable(block, lastIt.currentFrame())) {
2432  QTextTableData *td = static_cast<QTextTableData *>(data(lastIt.currentFrame()));
2433  QTextLayout *layout = block.layout();
2434 
2435  QPointF pos((td->position.x + td->size.width).toReal(),
2436  (td->position.y + td->size.height).toReal() - layout->boundingRect().height());
2437 
2438  layout->setPosition(pos);
2439  layoutStruct->y = origY;
2440  layoutStruct->pageBottom = origPageBottom;
2441  }
2442 
2443  // if the block right after a table starts with a line separator, shift it up by one line
2444  if (isLineSeparatorBlockAfterTable(block, lastIt.currentFrame())) {
2445  QTextTableData *td = static_cast<QTextTableData *>(data(lastIt.currentFrame()));
2446  QTextLayout *layout = block.layout();
2447 
2448  QFixed height = QFixed::fromReal(layout->lineAt(0).height());
2449 
2450  if (layoutStruct->pageBottom == origPageBottom) {
2451  layoutStruct->y -= height;
2452  layout->setPosition(layout->position() - QPointF(0, height.toReal()));
2453  } else {
2454  // relayout block to correctly handle page breaks
2455  layoutStruct->y = origY - height;
2456  layoutStruct->pageBottom = origPageBottom;
2457  layoutBlock(block, docPos, blockFormat, layoutStruct, layoutFrom, layoutTo, previousBlockFormatPtr);
2458  }
2459 
2460  QPointF linePos((td->position.x + td->size.width).toReal(),
2461  (td->position.y + td->size.height - height).toReal());
2462 
2463  layout->lineAt(0).setPosition(linePos - layout->position());
2464  }
2465 
2467  layoutStruct->newPage();
2468  }
2469 
2470  maximumBlockWidth = qMax(maximumBlockWidth, layoutStruct->maximumWidth);
2471  layoutStruct->maximumWidth = origMaximumWidth;
2472  previousBlockFormat = blockFormat;
2473  }
2474  }
2475  if (layoutStruct->maximumWidth == QFIXED_MAX && maximumBlockWidth > 0)
2476  layoutStruct->maximumWidth = maximumBlockWidth;
2477  else
2478  layoutStruct->maximumWidth = qMax(layoutStruct->maximumWidth, maximumBlockWidth);
2479 
2480  // a float at the bottom of a frame may make it taller, hence the qMax() for layoutStruct->y.
2481  // we don't need to do it for tables though because floats in tables are per table
2482  // and not per cell and layoutCell already takes care of doing the same as we do here
2483  if (!qobject_cast<QTextTable *>(layoutStruct->frame)) {
2484  QList<QTextFrame *> children = layoutStruct->frame->childFrames();
2485  for (int i = 0; i < children.count(); ++i) {
2486  QTextFrameData *fd = data(children.at(i));
2487  if (!fd->layoutDirty && children.at(i)->frameFormat().position() != QTextFrameFormat::InFlow)
2488  layoutStruct->y = qMax(layoutStruct->y, fd->position.y + fd->size.height);
2489  }
2490  }
2491 
2492  if (inRootFrame) {
2493  // we assume that any float is aligned in a way that disallows the optimizations that rely
2494  // on unaligned content.
2495  if (!fd->floats.isEmpty())
2496  contentHasAlignment = true;
2497 
2498  if (it.atEnd()) {
2499  //qDebug() << "layout done!";
2501  QCheckPoint cp;
2502  cp.y = layoutStruct->y;
2504  cp.minimumWidth = layoutStruct->minimumWidth;
2505  cp.maximumWidth = layoutStruct->maximumWidth;
2506  cp.contentsWidth = layoutStruct->contentsWidth;
2507  checkPoints.append(cp);
2509  } else {
2511  // #######
2512  //checkPoints.last().positionInFrame = q->document()->docHandle()->length();
2513  }
2514  }
2515 
2516 
2517  fd->currentLayoutStruct = 0;
2518 }
2519 
2520 static inline void getLineHeightParams(const QTextBlockFormat &blockFormat, const QTextLine &line, qreal scaling,
2521  QFixed *lineAdjustment, QFixed *lineBreakHeight, QFixed *lineHeight)
2522 {
2523  *lineHeight = QFixed::fromReal(blockFormat.lineHeight(line.height(), scaling));
2524 
2526  *lineBreakHeight = *lineHeight;
2527  if (blockFormat.lineHeightType() == QTextBlockFormat::FixedHeight)
2528  *lineAdjustment = QFixed::fromReal(line.ascent() + qMax(line.leading(), qreal(0.0))) - ((*lineHeight * 4) / 5);
2529  else
2530  *lineAdjustment = QFixed::fromReal(line.height()) - *lineHeight;
2531  }
2532  else {
2533  *lineBreakHeight = QFixed::fromReal(line.height());
2534  *lineAdjustment = 0;
2535  }
2536 }
2537 
2538 void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosition, const QTextBlockFormat &blockFormat,
2539  QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat)
2540 {
2542 
2543  QTextLayout *tl = bl.layout();
2544  const int blockLength = bl.length();
2545 
2546  LDEBUG << "layoutBlock from=" << layoutFrom << "to=" << layoutTo;
2547 
2548 // qDebug() << "layoutBlock; width" << layoutStruct->x_right - layoutStruct->x_left << "(maxWidth is btw" << tl->maximumWidth() << ')';
2549 
2550  if (previousBlockFormat) {
2551  qreal margin = qMax(blockFormat.topMargin(), previousBlockFormat->bottomMargin());
2552  if (margin > 0 && q->paintDevice()) {
2553  margin *= qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi());
2554  }
2555  layoutStruct->y += QFixed::fromReal(margin);
2556  }
2557 
2558  //QTextFrameData *fd = data(layoutStruct->frame);
2559 
2561 
2562  QFixed extraMargin;
2564  QFontMetricsF fm(bl.charFormat().font());
2565  extraMargin = QFixed::fromReal(fm.width(QChar(QChar(0x21B5))));
2566  }
2567 
2568  const QFixed indent = this->blockIndent(blockFormat);
2569  const QFixed totalLeftMargin = QFixed::fromReal(blockFormat.leftMargin()) + (dir == Qt::RightToLeft ? extraMargin : indent);
2570  const QFixed totalRightMargin = QFixed::fromReal(blockFormat.rightMargin()) + (dir == Qt::RightToLeft ? indent : extraMargin);
2571 
2572  const QPointF oldPosition = tl->position();
2573  tl->setPosition(QPointF(layoutStruct->x_left.toReal(), layoutStruct->y.toReal()));
2574 
2575  if (layoutStruct->fullLayout
2576  || (blockPosition + blockLength > layoutFrom && blockPosition <= layoutTo)
2577  // force relayout if we cross a page boundary
2578  || (layoutStruct->pageHeight != QFIXED_MAX && layoutStruct->absoluteY() + QFixed::fromReal(tl->boundingRect().height()) > layoutStruct->pageBottom)) {
2579 
2580  LDEBUG << " do layout";
2582  option.setTextDirection(dir);
2583  option.setTabs( blockFormat.tabPositions() );
2584 
2585  Qt::Alignment align = docPrivate->defaultTextOption.alignment();
2586  if (blockFormat.hasProperty(QTextFormat::BlockAlignment))
2587  align = blockFormat.alignment();
2588  option.setAlignment(QStyle::visualAlignment(dir, align)); // for paragraph that are RTL, alignment is auto-reversed;
2589 
2590  if (blockFormat.nonBreakableLines() || document->pageSize().width() < 0) {
2592  }
2593 
2594  tl->setTextOption(option);
2595 
2596  const bool haveWordOrAnyWrapMode = (option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere);
2597 
2598 // qDebug() << " layouting block at" << bl.position();
2599  const QFixed cy = layoutStruct->y;
2600  const QFixed l = layoutStruct->x_left + totalLeftMargin;
2601  const QFixed r = layoutStruct->x_right - totalRightMargin;
2602 
2603  tl->beginLayout();
2604  bool firstLine = true;
2605  while (1) {
2606  QTextLine line = tl->createLine();
2607  if (!line.isValid())
2608  break;
2609  line.setLeadingIncluded(true);
2610 
2611  QFixed left, right;
2612  floatMargins(layoutStruct->y, layoutStruct, &left, &right);
2613  left = qMax(left, l);
2614  right = qMin(right, r);
2615  QFixed text_indent;
2616  if (firstLine) {
2617  text_indent = QFixed::fromReal(blockFormat.textIndent());
2618  if (dir == Qt::LeftToRight)
2619  left += text_indent;
2620  else
2621  right -= text_indent;
2622  firstLine = false;
2623  }
2624 // qDebug() << "layout line y=" << currentYPos << "left=" << left << "right=" <<right;
2625 
2626  if (fixedColumnWidth != -1)
2627  line.setNumColumns(fixedColumnWidth, (right - left).toReal());
2628  else
2629  line.setLineWidth((right - left).toReal());
2630 
2631 // qDebug() << "layoutBlock; layouting line with width" << right - left << "->textWidth" << line.textWidth();
2632  floatMargins(layoutStruct->y, layoutStruct, &left, &right);
2633  left = qMax(left, l);
2634  right = qMin(right, r);
2635  if (dir == Qt::LeftToRight)
2636  left += text_indent;
2637  else
2638  right -= text_indent;
2639 
2640  if (fixedColumnWidth == -1 && QFixed::fromReal(line.naturalTextWidth()) > right-left) {
2641  // float has been added in the meantime, redo
2642  layoutStruct->pendingFloats.clear();
2643 
2644  line.setLineWidth((right-left).toReal());
2645  if (QFixed::fromReal(line.naturalTextWidth()) > right-left) {
2646  if (haveWordOrAnyWrapMode) {
2648  tl->setTextOption(option);
2649  }
2650 
2651  layoutStruct->pendingFloats.clear();
2652  // lines min width more than what we have
2653  layoutStruct->y = findY(layoutStruct->y, layoutStruct, QFixed::fromReal(line.naturalTextWidth()));
2654  floatMargins(layoutStruct->y, layoutStruct, &left, &right);
2655  left = qMax(left, l);
2656  right = qMin(right, r);
2657  if (dir == Qt::LeftToRight)
2658  left += text_indent;
2659  else
2660  right -= text_indent;
2661  line.setLineWidth(qMax<qreal>(line.naturalTextWidth(), (right-left).toReal()));
2662 
2663  if (haveWordOrAnyWrapMode) {
2665  tl->setTextOption(option);
2666  }
2667  }
2668 
2669  }
2670 
2671  QFixed lineBreakHeight, lineHeight, lineAdjustment;
2672  qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ?
2673  qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1;
2674  getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight);
2675 
2676  if (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom) {
2677  layoutStruct->newPage();
2678 
2679  floatMargins(layoutStruct->y, layoutStruct, &left, &right);
2680  left = qMax(left, l);
2681  right = qMin(right, r);
2682  if (dir == Qt::LeftToRight)
2683  left += text_indent;
2684  else
2685  right -= text_indent;
2686  }
2687 
2688  line.setPosition(QPointF((left - layoutStruct->x_left).toReal(), (layoutStruct->y - cy - lineAdjustment).toReal()));
2689  layoutStruct->y += lineHeight;
2690  layoutStruct->contentsWidth
2691  = qMax<QFixed>(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + line.naturalTextWidth()) + totalRightMargin);
2692 
2693  // position floats
2694  for (int i = 0; i < layoutStruct->pendingFloats.size(); ++i) {
2695  QTextFrame *f = layoutStruct->pendingFloats.at(i);
2696  positionFloat(f);
2697  }
2698  layoutStruct->pendingFloats.clear();
2699  }
2700  tl->endLayout();
2701  } else {
2702  const int cnt = tl->lineCount();
2703  for (int i = 0; i < cnt; ++i) {
2704  LDEBUG << "going to move text line" << i;
2705  QTextLine line = tl->lineAt(i);
2706  layoutStruct->contentsWidth
2707  = qMax(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + tl->lineAt(i).naturalTextWidth()) + totalRightMargin);
2708 
2709  QFixed lineBreakHeight, lineHeight, lineAdjustment;
2710  qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ?
2711  qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1;
2712  getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight);
2713 
2714  if (layoutStruct->pageHeight != QFIXED_MAX) {
2715  if (layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom)
2716  layoutStruct->newPage();
2717  line.setPosition(QPointF(line.position().x(), (layoutStruct->y - lineAdjustment).toReal() - tl->position().y()));
2718  }
2719  layoutStruct->y += lineHeight;
2720  }
2721  if (layoutStruct->updateRect.isValid()
2722  && blockLength > 1) {
2723  if (layoutFrom >= blockPosition + blockLength) {
2724  // if our height didn't change and the change in the document is
2725  // in one of the later paragraphs, then we don't need to repaint
2726  // this one
2727  layoutStruct->updateRect.setTop(qMax(layoutStruct->updateRect.top(), layoutStruct->y.toReal()));
2728  } else if (layoutTo < blockPosition) {
2729  if (oldPosition == tl->position())
2730  // if the change in the document happened earlier in the document
2731  // and our position did /not/ change because none of the earlier paragraphs
2732  // or frames changed their height, then we don't need to repaint
2733  // this one
2734  layoutStruct->updateRect.setBottom(qMin(layoutStruct->updateRect.bottom(), tl->position().y()));
2735  else
2736  layoutStruct->updateRect.setBottom(qreal(INT_MAX)); // reset
2737  }
2738  }
2739  }
2740 
2741  // ### doesn't take floats into account. would need to do it per line. but how to retrieve then? (Simon)
2742  const QFixed margins = totalLeftMargin + totalRightMargin;
2743  layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, QFixed::fromReal(tl->minimumWidth()) + margins);
2744 
2745  const QFixed maxW = QFixed::fromReal(tl->maximumWidth()) + margins;
2746 
2747  if (maxW > 0) {
2748  if (layoutStruct->maximumWidth == QFIXED_MAX)
2749  layoutStruct->maximumWidth = maxW;
2750  else
2751  layoutStruct->maximumWidth = qMax(layoutStruct->maximumWidth, maxW);
2752  }
2753 }
2754 
2756  QFixed *left, QFixed *right) const
2757 {
2758 // qDebug() << "floatMargins y=" << y;
2759  *left = layoutStruct->x_left;
2760  *right = layoutStruct->x_right;
2761  QTextFrameData *lfd = data(layoutStruct->frame);
2762  for (int i = 0; i < lfd->floats.size(); ++i) {
2763  QTextFrameData *fd = data(lfd->floats.at(i));
2764  if (!fd->layoutDirty) {
2765  if (fd->position.y <= y && fd->position.y + fd->size.height > y) {
2766 // qDebug() << "adjusting with float" << f << fd->position.x()<< fd->size.width();
2768  *left = qMax(*left, fd->position.x + fd->size.width);
2769  else
2770  *right = qMin(*right, fd->position.x);
2771  }
2772  }
2773  }
2774 // qDebug() << "floatMargins: left="<<*left<<"right="<<*right<<"y="<<y;
2775 }
2776 
2777 QFixed QTextDocumentLayoutPrivate::findY(QFixed yFrom, const QTextLayoutStruct *layoutStruct, QFixed requiredWidth) const
2778 {
2779  QFixed right, left;
2780  requiredWidth = qMin(requiredWidth, layoutStruct->x_right - layoutStruct->x_left);
2781 
2782 // qDebug() << "findY:" << yFrom;
2783  while (1) {
2784  floatMargins(yFrom, layoutStruct, &left, &right);
2785 // qDebug() << " yFrom=" << yFrom<<"right=" << right << "left=" << left << "requiredWidth=" << requiredWidth;
2786  if (right-left >= requiredWidth)
2787  break;
2788 
2789  // move float down until we find enough space
2790  QFixed newY = QFIXED_MAX;
2791  QTextFrameData *lfd = data(layoutStruct->frame);
2792  for (int i = 0; i < lfd->floats.size(); ++i) {
2793  QTextFrameData *fd = data(lfd->floats.at(i));
2794  if (!fd->layoutDirty) {
2795  if (fd->position.y <= yFrom && fd->position.y + fd->size.height > yFrom)
2796  newY = qMin(newY, fd->position.y + fd->size.height);
2797  }
2798  }
2799  if (newY == QFIXED_MAX)
2800  break;
2801  yFrom = newY;
2802  }
2803  return yFrom;
2804 }
2805 
2808 {
2810 }
2811 
2812 
2813 void QTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
2814 {
2816  QTextFrame *frame = d->document->rootFrame();
2817  QTextFrameData *fd = data(frame);
2818 
2819  if(fd->sizeDirty)
2820  return;
2821 
2822  if (context.clip.isValid()) {
2823  d->ensureLayouted(QFixed::fromReal(context.clip.bottom()));
2824  } else {
2825  d->ensureLayoutFinished();
2826  }
2827 
2828  QFixed width = fd->size.width;
2829  if (d->document->pageSize().width() == 0 && d->viewportRect.isValid()) {
2830  // we're in NoWrap mode, meaning the frame should expand to the viewport
2831  // so that backgrounds are drawn correctly
2832  fd->size.width = qMax(width, QFixed::fromReal(d->viewportRect.right()));
2833  }
2834 
2835  // Make sure we conform to the root frames bounds when drawing.
2836  d->clipRect = QRectF(fd->position.toPointF(), fd->size.toSizeF()).adjusted(fd->leftMargin.toReal(), 0, -fd->rightMargin.toReal(), 0);
2837  d->drawFrame(QPointF(), painter, context, frame);
2838  fd->size.width = width;
2839 }
2840 
2842 {
2844  d->viewportRect = viewport;
2845 }
2846 
2847 static void markFrames(QTextFrame *current, int from, int oldLength, int length)
2848 {
2849  int end = qMax(oldLength, length) + from;
2850 
2851  if (current->firstPosition() >= end || current->lastPosition() < from)
2852  return;
2853 
2854  QTextFrameData *fd = data(current);
2855  for (int i = 0; i < fd->floats.size(); ++i) {
2856  QTextFrame *f = fd->floats[i];
2857  if (!f) {
2858  // float got removed in editing operation
2859  fd->floats.removeAt(i);
2860  --i;
2861  }
2862  }
2863 
2864  fd->layoutDirty = true;
2865  fd->sizeDirty = true;
2866 
2867 // qDebug(" marking frame (%d--%d) as dirty", current->firstPosition(), current->lastPosition());
2869  for (int i = 0; i < children.size(); ++i)
2870  markFrames(children.at(i), from, oldLength, length);
2871 }
2872 
2873 void QTextDocumentLayout::documentChanged(int from, int oldLength, int length)
2874 {
2876 
2877  QTextBlock blockIt = document()->findBlock(from);
2878  QTextBlock endIt = document()->findBlock(qMax(0, from + length - 1));
2879  if (endIt.isValid())
2880  endIt = endIt.next();
2881  for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
2882  blockIt.clearLayout();
2883 
2884  if (d->docPrivate->pageSize.isNull())
2885  return;
2886 
2887  QRectF updateRect;
2888 
2889  d->lazyLayoutStepSize = 1000;
2890  d->sizeChangedTimer.stop();
2891  d->insideDocumentChange = true;
2892 
2893  const int documentLength = d->docPrivate->length();
2894  const bool fullLayout = (oldLength == 0 && length == documentLength);
2895  const bool smallChange = documentLength > 0
2896  && (qMax(length, oldLength) * 100 / documentLength) < 5;
2897 
2898  // don't show incremental layout progress (avoid scroll bar flicker)
2899  // if we see only a small change in the document and we're either starting
2900  // a layout run or we're already in progress for that and we haven't seen
2901  // any bigger change previously (showLayoutProgress already false)
2902  if (smallChange
2903  && (d->currentLazyLayoutPosition == -1 || d->showLayoutProgress == false))
2904  d->showLayoutProgress = false;
2905  else
2906  d->showLayoutProgress = true;
2907 
2908  if (fullLayout) {
2909  d->contentHasAlignment = false;
2910  d->currentLazyLayoutPosition = 0;
2911  d->checkPoints.clear();
2912  d->layoutStep();
2913  } else {
2914  d->ensureLayoutedByPosition(from);
2915  updateRect = doLayout(from, oldLength, length);
2916  }
2917 
2918  if (!d->layoutTimer.isActive() && d->currentLazyLayoutPosition != -1)
2919  d->layoutTimer.start(10, this);
2920 
2921  d->insideDocumentChange = false;
2922 
2923  if (d->showLayoutProgress) {
2924  const QSizeF newSize = dynamicDocumentSize();
2925  if (newSize != d->lastReportedSize) {
2926  d->lastReportedSize = newSize;
2927  emit documentSizeChanged(newSize);
2928  }
2929  }
2930 
2931  if (!updateRect.isValid()) {
2932  // don't use the frame size, it might have shrunken
2933  updateRect = QRectF(QPointF(0, 0), QSizeF(qreal(INT_MAX), qreal(INT_MAX)));
2934  }
2935 
2936  emit update(updateRect);
2937 }
2938 
2939 QRectF QTextDocumentLayout::doLayout(int from, int oldLength, int length)
2940 {
2942 
2943 // qDebug("documentChange: from=%d, oldLength=%d, length=%d", from, oldLength, length);
2944 
2945  // mark all frames between f_start and f_end as dirty
2946  markFrames(d->docPrivate->rootFrame(), from, oldLength, length);
2947 
2948  QRectF updateRect;
2949 
2950  QTextFrame *root = d->docPrivate->rootFrame();
2951  if(data(root)->sizeDirty)
2952  updateRect = d->layoutFrame(root, from, from + length);
2953  data(root)->layoutDirty = false;
2954 
2955  if (d->currentLazyLayoutPosition == -1)
2956  layoutFinished();
2957  else if (d->showLayoutProgress)
2958  d->sizeChangedTimer.start(0, this);
2959 
2960  return updateRect;
2961 }
2962 
2964 {
2965  Q_D(const QTextDocumentLayout);
2966  d->ensureLayouted(QFixed::fromReal(point.y()));
2967  QTextFrame *f = d->docPrivate->rootFrame();
2968  int position = 0;
2969  QTextLayout *l = 0;
2970  QFixedPoint pointf;
2971  pointf.x = QFixed::fromReal(point.x());
2972  pointf.y = QFixed::fromReal(point.y());
2973  QTextDocumentLayoutPrivate::HitPoint p = d->hitTest(f, pointf, &position, &l, accuracy);
2975  return -1;
2976 
2977  // ensure we stay within document bounds
2978  int lastPos = f->lastPosition();
2979  if (l && !l->preeditAreaText().isEmpty())
2980  lastPos += l->preeditAreaText().length();
2981  if (position > lastPos)
2982  position = lastPos;
2983  else if (position < 0)
2984  position = 0;
2985 
2986  return position;
2987 }
2988 
2990 {
2992  QTextCharFormat f = format.toCharFormat();
2993  Q_ASSERT(f.isValid());
2994  QTextObjectHandler handler = d->handlers.value(f.objectType());
2995  if (!handler.component)
2996  return;
2997 
2998  QSizeF intrinsic = handler.iface->intrinsicSize(d->document, posInDocument, format);
2999 
3001  QTextFrame *frame = qobject_cast<QTextFrame *>(d->document->objectForFormat(f));
3002  if (frame) {
3003  pos = frame->frameFormat().position();
3004  QTextFrameData *fd = data(frame);
3005  fd->sizeDirty = false;
3006  fd->size = QFixedSize::fromSizeF(intrinsic);
3007  fd->minimumWidth = fd->maximumWidth = fd->size.width;
3008  }
3009 
3010  QSizeF inlineSize = (pos == QTextFrameFormat::InFlow ? intrinsic : QSizeF(0, 0));
3011  item.setWidth(inlineSize.width());
3012 
3013  QFontMetrics m(f.font());
3014  switch (f.verticalAlignment())
3015  {
3017  item.setDescent(inlineSize.height() / 2);
3018  item.setAscent(inlineSize.height() / 2 - 1);
3019  break;
3021  item.setDescent(m.descent());
3022  item.setAscent(inlineSize.height() - m.descent() - 1);
3023  break;
3024  default:
3025  item.setDescent(0);
3026  item.setAscent(inlineSize.height() - 1);
3027  }
3028 }
3029 
3031 {
3033  Q_UNUSED(posInDocument);
3034  if (item.width() != 0)
3035  // inline
3036  return;
3037 
3038  QTextCharFormat f = format.toCharFormat();
3039  Q_ASSERT(f.isValid());
3040  QTextObjectHandler handler = d->handlers.value(f.objectType());
3041  if (!handler.component)
3042  return;
3043 
3044  QTextFrame *frame = qobject_cast<QTextFrame *>(d->document->objectForFormat(f));
3045  if (!frame)
3046  return;
3047 
3048  QTextBlock b = d->document->findBlock(frame->firstPosition());
3049  QTextLine line;
3050  if (b.position() <= frame->firstPosition() && b.position() + b.length() > frame->lastPosition())
3051  line = b.layout()->lineAt(b.layout()->lineCount()-1);
3052 // qDebug() << "layoutObject: line.isValid" << line.isValid() << b.position() << b.length() <<
3053 // frame->firstPosition() << frame->lastPosition();
3054  d->positionFloat(frame, line.isValid() ? &line : 0);
3055 }
3056 
3058  int posInDocument, const QTextFormat &format)
3059 {
3061  QTextCharFormat f = format.toCharFormat();
3062  Q_ASSERT(f.isValid());
3063  QTextFrame *frame = qobject_cast<QTextFrame *>(d->document->objectForFormat(f));
3064  if (frame && frame->frameFormat().position() != QTextFrameFormat::InFlow)
3065  return; // don't draw floating frames from inline objects here but in drawFlow instead
3066 
3067 // qDebug() << "drawObject at" << r;
3068  QAbstractTextDocumentLayout::drawInlineObject(p, rect, item, posInDocument, format);
3069 }
3070 
3072 {
3073  Q_D(const QTextDocumentLayout);
3074  const QSizeF pgSize = d->document->pageSize();
3075  if (pgSize.height() < 0)
3076  return 1;
3077  return qCeil(dynamicDocumentSize().height() / pgSize.height());
3078 }
3079 
3081 {
3082  Q_D(const QTextDocumentLayout);
3083  return data(d->docPrivate->rootFrame())->size.toSizeF();
3084 }
3085 
3087 {
3088  Q_D(const QTextDocumentLayout);
3089  d->ensureLayoutFinished();
3090  return dynamicPageCount();
3091 }
3092 
3094 {
3095  Q_D(const QTextDocumentLayout);
3096  d->ensureLayoutFinished();
3097  return dynamicDocumentSize();
3098 }
3099 
3101 {
3102  Q_Q(const QTextDocumentLayout);
3103  if (currentLazyLayoutPosition == -1)
3104  return;
3105  const QSizeF oldSize = q->dynamicDocumentSize();
3106  Q_UNUSED(oldSize);
3107 
3108  if (checkPoints.isEmpty())
3109  layoutStep();
3110 
3111  while (currentLazyLayoutPosition != -1
3112  && checkPoints.last().y < y)
3113  layoutStep();
3114 }
3115 
3117 {
3118  if (currentLazyLayoutPosition == -1)
3119  return;
3120  if (position < currentLazyLayoutPosition)
3121  return;
3122  while (currentLazyLayoutPosition != -1
3123  && currentLazyLayoutPosition < position) {
3124  const_cast<QTextDocumentLayout *>(q_func())->doLayout(currentLazyLayoutPosition, 0, INT_MAX - currentLazyLayoutPosition);
3125  }
3126 }
3127 
3129 {
3130  ensureLayoutedByPosition(currentLazyLayoutPosition + lazyLayoutStepSize);
3131  lazyLayoutStepSize = qMin(200000, lazyLayoutStepSize * 2);
3132 }
3133 
3135 {
3137  d->cursorWidth = width;
3138 }
3139 
3141 {
3142  Q_D(const QTextDocumentLayout);
3143  return d->cursorWidth;
3144 }
3145 
3147 {
3149  d->fixedColumnWidth = width;
3150 }
3151 
3153 {
3154  Q_D(const QTextDocumentLayout);
3155  if (d->docPrivate->pageSize.isNull())
3156  return QRectF();
3157  d->ensureLayoutFinished();
3158  return d->frameBoundingRectInternal(frame);
3159 }
3160 
3162 {
3163  QPointF pos;
3164  const int framePos = frame->firstPosition();
3165  QTextFrame *f = frame;
3166  while (f) {
3167  QTextFrameData *fd = data(f);
3168  pos += fd->position.toPointF();
3169 
3170  if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
3171  QTextTableCell cell = table->cellAt(framePos);
3172  if (cell.isValid())
3173  pos += static_cast<QTextTableData *>(fd)->cellPosition(cell).toPointF();
3174  }
3175 
3176  f = f->parentFrame();
3177  }
3178  return QRectF(pos, data(frame)->size.toSizeF());
3179 }
3180 
3182 {
3183  Q_D(const QTextDocumentLayout);
3184  if (d->docPrivate->pageSize.isNull() || !block.isValid())
3185  return QRectF();
3186  d->ensureLayoutedByPosition(block.position() + block.length());
3187  QTextFrame *frame = d->document->frameAt(block.position());
3188  QPointF offset;
3189  const int blockPos = block.position();
3190 
3191  while (frame) {
3192  QTextFrameData *fd = data(frame);
3193  offset += fd->position.toPointF();
3194 
3195  if (QTextTable *table = qobject_cast<QTextTable *>(frame)) {
3196  QTextTableCell cell = table->cellAt(blockPos);
3197  if (cell.isValid())
3198  offset += static_cast<QTextTableData *>(fd)->cellPosition(cell).toPointF();
3199  }
3200 
3201  frame = frame->parentFrame();
3202  }
3203 
3204  const QTextLayout *layout = block.layout();
3205  QRectF rect = layout->boundingRect();
3206  rect.moveTopLeft(layout->position() + offset);
3207  return rect;
3208 }
3209 
3211 {
3212  Q_D(const QTextDocumentLayout);
3213  int pos = d->currentLazyLayoutPosition;
3214  if (pos == -1)
3215  return 100;
3216  return pos * 100 / d->document->docHandle()->length();
3217 }
3218 
3220 {
3222  if (e->timerId() == d->layoutTimer.timerId()) {
3223  if (d->currentLazyLayoutPosition != -1)
3224  d->layoutStep();
3225  } else if (e->timerId() == d->sizeChangedTimer.timerId()) {
3226  d->lastReportedSize = dynamicDocumentSize();
3227  emit documentSizeChanged(d->lastReportedSize);
3228  d->sizeChangedTimer.stop();
3229 
3230  if (d->currentLazyLayoutPosition == -1) {
3231  const int newCount = dynamicPageCount();
3232  if (newCount != d->lastPageCount) {
3233  d->lastPageCount = newCount;
3234  emit pageCountChanged(newCount);
3235  }
3236  }
3237  } else {
3239  }
3240 }
3241 
3243 {
3245  d->layoutTimer.stop();
3246  if (!d->insideDocumentChange)
3247  d->sizeChangedTimer.start(0, this);
3248  // reset
3249  d->showLayoutProgress = true;
3250 }
3251 
3253 {
3254  d_func()->ensureLayouted(QFixed::fromReal(y));
3255 }
3256 
3258 {
3259  Q_D(const QTextDocumentLayout);
3260  d->ensureLayoutFinished();
3261  return d->idealWidth;
3262 }
3263 
3265 {
3266  Q_D(const QTextDocumentLayout);
3267  return d->contentHasAlignment;
3268 }
3269 
3271 {
3272  if (!paintDevice)
3273  return value;
3274  return value * paintDevice->logicalDpiY() / qreal(qt_defaultDpi());
3275 }
3276 
3278 {
3279  if (!paintDevice)
3280  return value;
3281  return value * QFixed(paintDevice->logicalDpiY()) / QFixed(qt_defaultDpi());
3282 }
3283 
3285 
3286 #include "moc_qtextdocumentlayout_p.cpp"
The QVariant class acts like a union for the most common Qt data types.
Definition: qvariant.h:92
T qobject_cast(QObject *object)
Definition: qobject.h:375
The QMultiHash class is a convenience QHash subclass that provides multi-valued hashes.
Definition: qcontainerfwd.h:58
The QPainter class performs low-level painting on widgets and other paint devices.
Definition: qpainter.h:86
QPaintDevice * device() const
Returns the paint device on which this painter is currently painting, or 0 if the painter is not acti...
Definition: qpainter.cpp:1530
int columns() const
Returns the number of columns in the table.
double d
Definition: qnumeric_p.h:62
QSizeF pageSize
the page size that should be used for laying out the document
qreal cellSpacing() const
Returns the table&#39;s cell spacing.
Definition: qtextformat.h:862
QFont font() const
Returns the font for this character format.
QTextOption textOption() const
Returns the current text option used to control the layout process.
The QTextLayout::FormatRange structure is used to apply extra formatting information for a specified ...
Definition: qtextlayout.h:128
qreal y() const
Returns the y-coordinate of the rectangle&#39;s top edge.
Definition: qrect.h:667
int width(const QString &, int len=-1) const
Returns the width in pixels of the first len characters of text.
Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
Definition: qalgorithms.h:262
qreal right() const
Returns the x-coordinate of the rectangle&#39;s right edge.
Definition: qrect.h:527
QString text() const
Returns the block&#39;s contents as plain text.
void registerHandler(int objectType, QObject *component)
Registers the given component as a handler for items of the given objectType.
QPointF position() const
The global position of the layout.
QMultiHash< int, QTextFrame * > childFrameMap
The QTextCharFormat class provides formatting information for characters in a QTextDocument.
Definition: qtextformat.h:372
double qreal
Definition: qglobal.h:1193
void setFixedColumnWidth(int width)
The QTextListFormat class provides formatting information for lists in a QTextDocument.
Definition: qtextformat.h:642
static QFixed firstChildPos(const QTextFrame *f)
unsigned char c[8]
Definition: qnumeric_p.h:62
QTextCharFormat charFormat() const
Returns the format of the character immediately before the cursor position().
void setHeight(qreal h)
Sets the height of the rectangle to the given height.
Definition: qrect.h:787
void layoutBlock(const QTextBlock &bl, int blockPosition, const QTextBlockFormat &blockFormat, QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat)
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
The QFontMetrics class provides font metrics information.
Definition: qfontmetrics.h:65
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
qreal y() const
Returns the line&#39;s y position.
void setPosition(const QPointF &pos)
Moves the line to position pos.
#define QFIXED_MAX
Definition: qfixed_p.h:158
QFixed blockIndent(const QTextBlockFormat &blockFormat) const
void clear()
Removes all items from the hash.
Definition: qhash.h:574
bool isValid() const
Returns true if the rectangle is valid, otherwise returns false.
Definition: qrect.h:661
qreal maximumWidth() const
The maximum width the layout could expand to; this is essentially the width of the entire text...
const QChar at(int i) const
Returns the character at the given index position in the string.
Definition: qstring.h:698
void calcRowPosition(int row)
void positionFloat(QTextFrame *frame, QTextLine *currentLine=0)
void floatMargins(const QFixed &y, const QTextLayoutStruct *layoutStruct, QFixed *left, QFixed *right) const
QRectF cellRect(const QTextTableCell &cell) const
QTextCharFormat format
the format of the selection
int qCeil(qreal v)
Definition: qmath.h:63
QVector< QFixed > heights
int headerRowCount() const
Returns the number of rows in the table that define the header.
Definition: qtextformat.h:877
const QGradient * gradient() const
Returns the gradient describing this brush.
Definition: qbrush.cpp:871
bool isNull() const
Returns true if this is a NULL variant, false otherwise.
Definition: qvariant.cpp:3102
QVector< QCheckPoint > checkPoints
QVector< T > & fill(const T &t, int size=-1)
Assigns value to all items in the vector.
Definition: qvector.h:665
#define it(className, varName)
int logicalDpiY() const
Definition: qpaintdevice.h:96
int count(const T &t) const
Returns the number of occurrences of value in the vector.
Definition: qvector.h:742
#define DEC_INDENT
void setTabs(QList< Tab > tabStops)
Sets the tab positions for the text layout to those specified by tabStops.
The QTextFrame class represents a frame in a QTextDocument.
Definition: qtextobject.h:122
static bool isLineSeparatorBlockAfterTable(const QTextBlock &block, const QTextFrame *previousFrame)
void setTransform(const QTransform &)
Sets matrix as an explicit transformation matrix on the current brush.
Definition: qbrush.cpp:968
Type type() const
Returns the type of this length object.
Definition: qtextformat.h:93
Position
This enum describes how a frame is located relative to the surrounding text.
Definition: qtextformat.h:734
QTextFrame::iterator begin() const
Returns a frame iterator pointing to the beginning of the table&#39;s cell.
Definition: qtexttable.cpp:306
qreal width() const
Returns the width.
Definition: qsize.h:284
virtual void drawInlineObject(QPainter *painter, const QRectF &rect, QTextInlineObject object, int posInDocument, const QTextFormat &format)
This function is called to draw the inline object, object, with the given painter within the rectangl...
int selectionEnd() const
Returns the end of the selection or position() if the cursor doesn&#39;t have a selection.
QVector< QFixed > rowPositions
int height() const
Definition: qpaintdevice.h:92
QVector< QFixed > columnPositions
PageBreakFlags pageBreakPolicy() const
Returns the currently set page break policy for the paragraph.
Definition: qtextformat.h:608
static void adjustContextSelectionsForCell(QAbstractTextDocumentLayout::PaintContext &cell_context, const QTextTableCell &cell, int r, int c, const int *selectedTableCells)
QTextFrame * currentFrame() const
Returns the current frame pointed to by the iterator, or 0 if the iterator currently points to a bloc...
int length() const
Returns the number of characters in this string.
Definition: qstring.h:696
HitTestAccuracy
Definition: qtextdocument.h:78
void drawTableCell(const QRectF &cellRect, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &cell_context, QTextTable *table, QTextTableData *td, int r, int c, QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset) const
The QPointF class defines a point in the plane using floating point precision.
Definition: qpoint.h:214
qreal height() const
Returns the height.
Definition: qsize.h:287
qreal left() const
Returns the x-coordinate of the rectangle&#39;s left edge.
Definition: qrect.h:525
The QTextLine class represents a line of text inside a QTextLayout.
Definition: qtextlayout.h:197
virtual void timerEvent(QTimerEvent *)
This event handler can be reimplemented in a subclass to receive timer events for the object...
Definition: qobject.cpp:1294
static qreal position(QGraphicsObject *item, QDeclarativeAnchorLine::AnchorLine anchorLine)
void restore()
Restores the current painter state (pops a saved state off the stack).
Definition: qpainter.cpp:1620
const_iterator constEnd() const
Returns a const STL-style iterator pointing to the imaginary item after the last item in the vector...
Definition: qvector.h:252
WrapMode
This enum describes how text is wrapped in a document.
Definition: qtextoption.h:102
int textLength() const
Returns the length of the text in the line.
QRectF layoutTable(QTextTable *t, int layoutFrom, int layoutTo, QFixed parentY)
void drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QList< QTextFrame * > pendingFloats
QPointF topLeft() const
Returns the position of the rectangle&#39;s top-left corner.
Definition: qrect.h:539
Flags flags() const
Returns the flags associated with the option.
Definition: qtextoption.h:121
QTextDocument * document() const
Returns the text document that this layout is operating on.
qreal leading() const
Returns the line&#39;s leading.
void setTop(qreal pos)
Sets the top edge of the rectangle to the given y coordinate.
Definition: qrect.h:674
QRectF boundingRect() const
The smallest rectangle that contains all the lines in the layout.
void positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
Lays out the inline object item using the given text format.
HitPoint hitTest(QTextFrame *frame, const QFixedPoint &point, int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const
QTextFrame::Iterator frameIteratorForYPosition(QFixed y) const
void setBottom(qreal pos)
Sets the bottom edge of the rectangle to the given y coordinate.
Definition: qrect.h:676
QTextCursor cursor
the selection&#39;s cursor
int count(const T &t) const
Returns the number of occurrences of value in the list.
Definition: qlist.h:891
QFixed bottomPadding(const QTextFormat &format) const
void setLineWidth(qreal width)
Lays out the line with the given width.
void drawBlock(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context, QTextBlock bl, bool inRootFrame) const
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
Definition: qtransform.cpp:417
void drawLine(const QLineF &line)
Draws a line defined by line.
Definition: qpainter.h:573
int start
Specifies the beginning of the format range within the text layout&#39;s text.
Definition: qtextlayout.h:129
qreal leftMargin() const
Returns the width of the frame&#39;s left margin in pixels.
The QString class provides a Unicode character string.
Definition: qstring.h:83
T * qobject_cast(QObject *object)
Definition: qobject.h:375
static bool qIsNull(double d)
Definition: qglobal.h:2061
static void getLineHeightParams(const QTextBlockFormat &blockFormat, const QTextLine &line, qreal scaling, QFixed *lineAdjustment, QFixed *lineBreakHeight, QFixed *lineHeight)
bool isValid() const
Returns true if this character format is valid; otherwise returns false.
Definition: qtextformat.h:397
qreal topMargin() const
Returns the paragraph&#39;s top margin.
Definition: qtextformat.h:566
void setWidth(qreal w)
Sets the width to the given width.
Definition: qsize.h:290
static QFixed fromReal(qreal r)
Definition: qfixed_p.h:70
QPalette palette
the default color that is used for the text, when no color is specified.
Q_DECL_CONSTEXPR T qAbs(const T &t)
Definition: qglobal.h:1201
void clearLayout()
Clears the QTextLayout that is used to lay out and display the block&#39;s contents.
int indent() const
Returns the paragraph&#39;s indent.
Definition: qtextformat.h:590
qreal rightMargin() const
Returns the paragraph&#39;s right margin.
Definition: qtextformat.h:581
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
void documentSizeChanged(const QSizeF &newSize)
This signal is emitted when the size of the document layout changes to newSize.
int firstPosition() const
Returns the first document position inside the frame.
Qt::Alignment alignment() const
Returns the paragraph&#39;s alignment.
Definition: qtextformat.h:561
#define Q_D(Class)
Definition: qglobal.h:2482
static QFixedPoint fromPointF(const QPointF &p)
Definition: qfixed_p.h:195
Q_STATIC_GLOBAL_OPERATOR bool operator<(const QCheckPoint &checkPoint, QFixed y)
const QColor & color(ColorGroup cg, ColorRole cr) const
Returns the color in the specified color group, used for the given color role.
Definition: qpalette.h:107
QTextObject * objectForFormat(const QTextFormat &) const
Returns the text object associated with the format f.
int cursorPosition
the position within the document, where the cursor line should be drawn.
The QPen class defines how a QPainter should draw lines and outlines of shapes.
Definition: qpen.h:64
void setBrushOrigin(int x, int y)
Sets the brush&#39;s origin to point (x, y).
Definition: qpainter.h:825
bool isValid() const
Returns true if this text line is valid; otherwise returns false.
Definition: qtextlayout.h:201
qreal ascent() const
Returns the line&#39;s ascent.
QList< QPointer< QTextFrame > > floats
The QSizeF class defines the size of a two-dimensional object using floating point precision...
Definition: qsize.h:202
QObjectList children
Definition: qobject.h:93
void append(const T &t)
void update(const QRectF &=QRectF(0., 0., 1000000000., 1000000000.))
This signal is emitted when the rectangle rect has been updated.
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:72
void drawBorder(QPainter *painter, const QRectF &rect, qreal topMargin, qreal bottomMargin, qreal border, const QBrush &brush, QTextFrameFormat::BorderStyle style) const
Q_CORE_EXPORT QTextStream & right(QTextStream &s)
void setRight(qreal pos)
Sets the right edge of the rectangle to the given x coordinate.
Definition: qrect.h:672
QTextOption defaultTextOption
void save()
Saves the current painter state (pushes the state onto a stack).
Definition: qpainter.cpp:1590
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
QVector< QFixed > cellVerticalOffsets
void drawFlow(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context, QTextFrame::Iterator it, const QList< QTextFrame *> &floats, QTextBlock *cursorBlockNeedingRepaint) const
static QTextFrameData * createData(QTextFrame *f)
bool isEmpty() const
Returns true if the list contains no items; otherwise returns false.
Definition: qlist.h:152
qreal x() const
Returns the x-coordinate of this point.
Definition: qpoint.h:282
void resize(int size)
Sets the size of the vector to size.
Definition: qvector.h:342
iterator end()
Returns an STL-style iterator pointing to the imaginary item after the last item in the vector...
Definition: qvector.h:250
QTextBlock next() const
Returns the text block in the document after this block, or an empty text block if this is the last o...
#define Q_Q(Class)
Definition: qglobal.h:2483
QTextLine lineForTextPosition(int pos) const
Returns the line that contains the cursor position specified by pos.
void setDescent(qreal d)
Sets the inline object&#39;s decent to d.
int rowSpan() const
Returns the number of rows this cell spans.
Definition: qtexttable.cpp:218
QTextFrame * rootFrame() const
Returns the document&#39;s root frame.
The QLineF class provides a two-dimensional vector using floating point precision.
Definition: qline.h:212
int position() const
Returns the index of the block&#39;s first character within the document.
void qDrawEdge(QPainter *p, qreal x1, qreal y1, qreal x2, qreal y2, qreal dw1, qreal dw2, QCss::Edge edge, QCss::BorderStyle style, QBrush c)
Definition: qcssutil.cpp:187
Position position() const
Returns the positioning policy for frames with this frame format.
Definition: qtextformat.h:758
qreal padding() const
Returns the width of the frame&#39;s internal padding in pixels.
Definition: qtextformat.h:792
The QAbstractTextDocumentLayout::Selection class is a convenience class defining the parameters of a ...
QSizeF documentSize() const
Returns the total size of the document&#39;s layout.
void ensureLayouted(QFixed y) const
const_iterator constBegin() const
Returns a const STL-style iterator pointing to the first item in the vector.
Definition: qvector.h:249
void selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const
If the selection spans over table cells, firstRow is populated with the number of the first row in th...
bool hasProperty(int propertyId) const
Returns true if the text format has a property with the given propertyId; otherwise returns false...
virtual QRectF blockBoundingRect(const QTextBlock &block) const
Returns the bounding rectangle of block.
QTextTableCell cellAt(int row, int col) const
Returns the table cell at the given row and column in the table.
Definition: qtexttable.cpp:630
void setNumColumns(int columns)
Lays out the line.
Qt::Alignment alignment() const
Returns the table&#39;s alignment.
Definition: qtextformat.h:872
void setAlignment(Qt::Alignment alignment)
Sets the option&#39;s text alignment to the specified alignment.
Definition: qtextoption.h:148
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
void setRenderHint(RenderHint hint, bool on=true)
Sets the given render hint on the painter if on is true; otherwise clears the render hint...
Definition: qpainter.cpp:7620
const QPen & pen() const
Returns the painter&#39;s current pen.
Definition: qpainter.cpp:4152
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
qreal scaleToDevice(qreal value) const
QSizeF dynamicDocumentSize() const
void drawEllipse(const QRectF &r)
Draws the ellipse defined by the given rectangle.
Definition: qpainter.cpp:4464
int width() const
Definition: qpaintdevice.h:91
Style style() const
Returns the list format&#39;s style.
Definition: qtextformat.h:662
The QRectF class defines a rectangle in the plane using floating point precision. ...
Definition: qrect.h:511
int objectType() const
Returns the text format&#39;s object type.
Definition: qtextformat.h:315
QFixed y
Definition: qfixed_p.h:191
qreal topMargin() const
Returns the width of the frame&#39;s top margin in pixels.
QBool contains(const T &t) const
Returns true if the list contains an occurrence of value; otherwise returns false.
Definition: qlist.h:880
QTextCharFormat format() const
Returns the cell&#39;s character format.
Definition: qtexttable.cpp:153
QTextTableFormat format() const
Returns the table&#39;s format.
Definition: qtexttable.h:133
int column() const
Returns the number of the column in the table that contains this cell.
Definition: qtexttable.cpp:201
QPoint brushOrigin() const
Returns the currently set brush origin.
Definition: qpainter.cpp:2168
QTextFrame * frameAt(int pos) const
QTextFrameLayoutData * layoutData() const
qreal bottomMargin() const
Returns the width of the frame&#39;s bottom margin in pixels.
int lastPosition() const
Returns the last document position inside the frame.
bool hasSelection() const
Returns true if the cursor contains a selection; otherwise returns false.
bool contentHasAlignment() const
QTextTable * currentTable() const
Returns a pointer to the current table if the cursor position() is inside a block that is part of a t...
void clear()
Removes all the elements from the vector and releases the memory used by the vector.
Definition: qvector.h:347
Property
This enum describes the different properties a format can have.
Definition: qtextformat.h:145
Style
This enum describes the symbols used to decorate list items:
Definition: qtextformat.h:649
The QTextFormat class provides formatting information for a QTextDocument.
Definition: qtextformat.h:129
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition: qstring.h:704
QTextFrame * parentFrame() const
Returns the frame&#39;s parent frame.
QVector< QFixed > minWidths
The QTextCursor class offers an API to access and modify QTextDocuments.
Definition: qtextcursor.h:70
QTextFrame * rootFrame() const
void setCursorWidth(int width)
qreal height() const
Returns the height of the rectangle.
Definition: qrect.h:710
void layoutFlow(QTextFrame::Iterator it, QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, QFixed width=0)
void drawFrameDecoration(QPainter *painter, QTextFrame *frame, QTextFrameData *fd, const QRectF &clip, const QRectF &rect) const
#define Q_STATIC_GLOBAL_OPERATOR
Definition: qfunctions_p.h:71
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
#define emit
Definition: qobjectdefs.h:76
QList< QTextOption::Tab > tabPositions() const
Returns a list of tab positions defined for the text block.
const char * layout
void drawListItem(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context, QTextBlock bl, const QTextCharFormat *selectionFormat) const
LayoutDirection
Definition: qnamespace.h:1580
QFixed rightPadding(const QTextFormat &format) const
bool isEmpty() const
Returns true if the hash contains no items; otherwise returns false.
Definition: qhash.h:297
void append(const T &t)
Inserts value at the end of the vector.
Definition: qvector.h:573
bool contains(int position) const
Returns true if the given position is located within the text block; otherwise returns false...
void moveTopLeft(const QPointF &p)
Moves the rectangle, leaving the top-left corner at the given position.
Definition: qrect.h:697
QRectF doLayout(int from, int oldLength, int length)
int & rx()
Returns a reference to the x coordinate of this point.
Definition: qpoint.h:140
QRectF frameBoundingRectInternal(QTextFrame *frame) const
int timerId() const
Returns the unique timer identifier, which is the same identifier as returned from QObject::startTime...
Definition: qcoreevent.h:346
The QTextBlock class provides a container for text fragments in a QTextDocument.
Definition: qtextobject.h:199
unsigned int uint
Definition: qglobal.h:996
qreal x() const
Returns the line&#39;s x position.
int pageCount() const
Returns the number of pages contained in the layout.
uint position(uint node, uint field=0) const
QSizeF toSizeF() const
Definition: qfixed_p.h:209
qreal width() const
Returns the width of the rectangle.
Definition: qrect.h:707
QTextListFormat toListFormat() const
Returns this format as a list format.
QFixed paddingProperty(const QTextFormat &format, QTextFormat::Property property) const
int row() const
Returns the number of the row in the table that contains this cell.
Definition: qtexttable.cpp:184
void setTextDirection(Qt::LayoutDirection aDirection)
Sets the direction of the text layout defined by the option to the given direction.
Definition: qtextoption.h:99
QRectF layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed parentY=0)
void drawFrame(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context, QTextFrame *f) const
void clear()
Removes all items from the list.
Definition: qlist.h:764
QRectF clip
a hint to the layout specifying the area around paragraphs, frames or text require painting...
void draw(QPainter *p, const QPointF &pos, const QVector< FormatRange > &selections=QVector< FormatRange >(), const QRectF &clip=QRectF()) const
Draws the whole layout on the painter p at the position specified by pos.
static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, const QPointF &origin, QRectF gradientRect=QRectF())
int position() const
Returns the absolute position of the cursor within the document.
int length() const
Returns the length of the block in characters.
QBrush background() const
Returns the brush used to paint the document&#39;s background.
Definition: qtextformat.h:345
Qt::BrushStyle style() const
Returns the brush style.
Definition: qbrush.h:182
The QTextTable class represents a table in a QTextDocument.
Definition: qtexttable.h:103
const BlockMap & blockMap() const
QTextLayoutStruct * currentLayoutStruct
int count() const
void translate(qreal dx, qreal dy)
Moves the rectangle dx along the x-axis and dy along the y-axis, relative to the current position...
Definition: qrect.h:716
QTextLine lineAt(int i) const
Returns the {i}-th line of text in this text layout.
qreal idealWidth() const
qreal textIndent() const
Returns the paragraph&#39;s text indent.
Definition: qtextformat.h:586
QRect toRect() const
Returns a QRect based on the values of this rectangle.
Definition: qrect.h:845
void documentChanged(int from, int oldLength, int length)
This function is called whenever the contents of the document change.
The QTextObjectInterface class allows drawing of custom text objects in QTextDocument.
QTextCharFormat charFormat() const
Returns the QTextCharFormat that describes the block&#39;s character format.
int preeditAreaPosition() const
Returns the position of the area in the text layout that will be processed before editing occurs...
const QBrush & brush(ColorGroup cg, ColorRole cr) const
Returns the brush in the specified color group, used for the given color role.
Definition: qpalette.cpp:874
const T & at(int i) const
Returns the item at index position i in the vector.
Definition: qvector.h:350
static bool isFrameFromInlineObject(QTextFrame *f)
#define LDEBUG
void setLeadingIncluded(bool included)
Includes positive leading into the line&#39;s height if included is true; otherwise does not include lead...
The iterator class provides an iterator for reading the contents of a QTextFrame. ...
Definition: qtextobject.h:144
QTextLength height() const
Returns the height of the frame&#39;s border rectangle.
Definition: qtextformat.h:803
const QBrush & brush() const
Returns the painter&#39;s current brush.
Definition: qpainter.cpp:4232
QTextFormat format() const
Returns the text object&#39;s format.
QPointF position() const
Returns the line&#39;s position relative to the text layout&#39;s position.
QList< QTextFrame * > childFrames() const
Returns a (possibly empty) list of the frame&#39;s child frames.
QTextLayoutStruct layoutCell(QTextTable *t, const QTextTableCell &cell, QFixed width, int layoutFrom, int layoutTo, QTextTableData *tableData, QFixed absoluteTableY, bool withPageBreaks)
The QBrush class defines the fill pattern of shapes drawn by QPainter.
Definition: qbrush.h:76
QFixed x
Definition: qfixed_p.h:190
Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
Definition: qalgorithms.h:227
qreal minimumWidth() const
The minimum width the layout needs.
VerticalAlignment verticalAlignment() const
Returns the vertical alignment used for characters with this format.
Definition: qtextformat.h:486
QString preeditAreaText() const
Returns the text that is inserted in the layout before editing occurs.
QFixed leftPadding(const QTextFormat &format) const
void setWidth(qreal w)
Sets the inline object&#39;s width to w.
QTextLength width() const
Returns the width of the frame&#39;s border rectangle.
Definition: qtextformat.h:798
The QTextLayout class is used to lay out and render text.
Definition: qtextlayout.h:105
static Qt::Alignment visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment)
Transforms an alignment of Qt::AlignLeft or Qt::AlignRight without Qt::AlignAbsolute into Qt::AlignLe...
Definition: qstyle.cpp:2149
bool nonBreakableLines() const
Returns true if the lines in the paragraph are non-breakable; otherwise returns false.
Definition: qtextformat.h:603
int userType() const
Returns the storage type of the value stored in the variant.
Definition: qvariant.cpp:1913
QTextDocumentLayout(QTextDocument *doc)
#define Q_DECLARE_PUBLIC(Class)
Definition: qglobal.h:2477
void setPosition(const QPointF &p)
Moves the text layout to point p.
int truncate() const
Definition: qfixed_p.h:79
QPointF toPointF() const
Definition: qfixed_p.h:194
void draw(QPainter *painter, const PaintContext &context)
Draws the layout with the given painter using the given context.
The QTimerEvent class contains parameters that describe a timer event.
Definition: qcoreevent.h:341
iterator begin()
Returns an STL-style iterator pointing to the first item in the vector.
Definition: qvector.h:247
static QTextFrameData * data(QTextFrame *f)
int firstPosition() const
Returns the first valid position in the document occupied by this cell.
Definition: qtexttable.cpp:273
The QTextInlineObject class represents an inline object in a QTextLayout.
Definition: qtextlayout.h:69
The QFont class specifies a font used for drawing text.
Definition: qfont.h:64
QTextBlock findBlock(int pos) const
Returns the text block that contains the {pos}-th character.
The QGradient class is used in combination with QBrush to specify gradient fills. ...
Definition: qbrush.h:201
void ensureLayoutedByPosition(int position) const
static bool isEmptyBlockAfterTable(const QTextBlock &block, const QTextFrame *previousFrame)
QPoint toPoint() const
Rounds the coordinates of this point to the nearest integer, and returns a QPoint object with the rou...
Definition: qpoint.h:376
void setAscent(qreal a)
Sets the inline object&#39;s ascent to a.
QTextLine createLine()
Returns a new text line to be laid out if there is text to be inserted into the layout; otherwise ret...
int cursorWidth() const
qreal naturalTextWidth() const
Returns the width of the line that is occupied by text.
Q_GUI_EXPORT int qt_defaultDpi()
Definition: qfont.cpp:240
void adjust(qreal x1, qreal y1, qreal x2, qreal y2)
Adds dx1, dy1, dx2 and dy2 respectively to the existing coordinates of the rectangle.
Definition: qrect.h:778
uint findNode(int k, uint field=0) const
QFixedPoint cellPosition(int row, int col) const
QFixed width
Definition: qfixed_p.h:207
void drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item, int posInDocument, const QTextFormat &format)
This function is called to draw the inline object, object, with the given painter within the rectangl...
void setTextOption(const QTextOption &option)
Sets the text option structure that controls the layout process to the given option.
qreal bottomMargin() const
Returns the paragraph&#39;s bottom margin.
Definition: qtextformat.h:571
QList< T > values() const
Returns a list containing all the values in the hash, in an arbitrary order.
Definition: qhash.h:693
virtual void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format)=0
Draws this text object using the specified painter.
void addUpdateRectForFloat(const QRectF &rect)
QVariant property(int propertyId) const
Returns the property specified by the given propertyId.
int rows() const
Returns the number of rows in the table.
T & last()
Returns a reference to the last item in the vector.
Definition: qvector.h:262
Q_DECL_CONSTEXPR const T & qBound(const T &min, const T &val, const T &max)
Definition: qglobal.h:1219
void setBrush(const QBrush &brush)
Sets the painter&#39;s brush to the given brush.
Definition: qpainter.cpp:4171
qreal rightMargin() const
Returns the width of the frame&#39;s right margin in pixels.
The QTextFrameFormat class provides formatting information for frames in a QTextDocument.
Definition: qtextformat.h:727
The QTextTableCell class represents the properties of a cell in a QTextTable.
Definition: qtexttable.h:59
QBrush borderBrush() const
Returns the brush used for the frame&#39;s border.
Definition: qtextformat.h:767
The QAbstractTextDocumentLayout::PaintContext class is a convenience class defining the parameters us...
int size() const
Returns the number of items in the list.
Definition: qlist.h:137
The QTextLength class encapsulates the different types of length used in a QTextDocument.
Definition: qtextformat.h:84
The QTextBlockFormat class provides formatting information for blocks of text in a QTextDocument...
Definition: qtextformat.h:545
void setPen(const QColor &color)
Sets the painter&#39;s pen to have style Qt::SolidLine, width 0 and the specified color.
Definition: qpainter.cpp:4047
QBrush foreground() const
Returns the brush used to render foreground details, such as text, frame outlines, and table borders.
Definition: qtextformat.h:352
QPaintDevice * paintDevice() const
Returns the paint device used to render the document&#39;s layout.
const char * property
Definition: qwizard.cpp:138
if(void) toggleToolbarShown
qreal lineHeight(qreal scriptLineHeight, qreal scaling) const
Returns the height of the lines in the paragraph based on the height of the script line given by scri...
Definition: qtextformat.h:625
qreal value(qreal maximumLength) const
Returns the effective length, constrained by the type of the length object and the specified maximumL...
Definition: qtextformat.h:94
static QFixedSize fromSizeF(const QSizeF &s)
Definition: qfixed_p.h:210
WrapMode wrapMode() const
Returns the text wrap mode defined by the option.
Definition: qtextoption.h:110
QFactoryLoader * l
QFixed findY(QFixed yFrom, const QTextLayoutStruct *layoutStruct, QFixed requiredWidth) const
int lastPosition() const
Returns the last valid position in the document occupied by this cell.
Definition: qtexttable.cpp:287
qreal toReal() const
Definition: qfixed_p.h:77
The QTextOption class provides a description of general rich text properties.
Definition: qtextoption.h:59
const QObjectList & children() const
Returns a list of child objects.
Definition: qobject.h:197
Qt::LayoutDirection textDirection() const
Returns the resolved text direction.
Q_DECLARE_TYPEINFO(QCheckPoint, Q_PRIMITIVE_TYPE)
QVector< QFixed > widths
#define INC_INDENT
void drawRect(const QRectF &rect)
Draws the current rectangle with the current pen and brush.
Definition: qpainter.h:650
QFixedPoint cellPosition(const QTextTableCell &cell) const
bool isValid() const
Returns true if this is a valid table cell; otherwise returns false.
Definition: qtexttable.h:77
int lineSpacing() const
Returns the distance from one base line to the next.
qreal y() const
Returns the y-coordinate of this point.
Definition: qpoint.h:287
quint16 index
QObject * parent
Definition: qobject.h:92
The QTextDocument class holds formatted text that can be viewed and edited using a QTextEdit...
bool isValid() const
Returns true if this text block is valid; otherwise returns false.
Definition: qtextobject.h:208
qreal top() const
Returns the y-coordinate of the rectangle&#39;s top edge.
Definition: qrect.h:526
static QFixed flowPosition(const QTextFrame::iterator it)
void setHeight(qreal h)
Sets the height to the given height.
Definition: qsize.h:293
Qt::Alignment alignment() const
Returns the text alignment defined by the option.
Definition: qtextoption.h:97
int length
Specifies the numer of characters the format range spans.
Definition: qtextlayout.h:130
The QTextTableFormat class provides formatting information for tables in a QTextDocument.
Definition: qtextformat.h:842
QTextCharFormat format
Specifies the format to apply.
Definition: qtextlayout.h:131
int lineHeightType() const
This returns the LineHeightType property of the paragraph.
Definition: qtextformat.h:598
The QTextList class provides a decorated list of items in a QTextDocument.
Definition: qtextlist.h:57
void setWrapMode(WrapMode wrap)
Sets the option&#39;s text wrap mode to the given mode.
Definition: qtextoption.h:109
The QBasicTimer class provides timer events for objects.
Definition: qbasictimer.h:55
QTextCharFormat toCharFormat() const
Returns this format as a character format.
The QFontMetricsF class provides font metrics information.
Definition: qfontmetrics.h:145
void setWidth(qreal w)
Sets the width of the rectangle to the given width.
Definition: qrect.h:784
QTextFrame * parentFrame() const
Returns the parent frame of the current frame.
Definition: qtextobject.h:160
QTransform & scale(qreal sx, qreal sy)
Scales the coordinate system by sx horizontally and sy vertically, and returns a reference to the mat...
Definition: qtransform.cpp:485
QTextFrame::Iterator frameIteratorForTextPosition(int position) const
bool isEmpty() const
Returns true if the vector has size 0; otherwise returns false.
Definition: qvector.h:139
PageBreakFlags pageBreakPolicy() const
Returns the currently set page break policy for the frame/table.
Definition: qtextformat.h:808
RenderHints renderHints() const
Returns a flag that specifies the rendering hints that are set for this painter.
Definition: qpainter.cpp:7675
bool atEnd() const
Returns true if the current item is the last item in the text frame.
Definition: qtextobject.h:165
void setCacheEnabled(bool enable)
Enables caching of the complete layout information if enable is true; otherwise disables layout cachi...
void reserve(int size)
Attempts to allocate memory for at least size elements.
Definition: qvector.h:339
qreal rawValue() const
Returns the constraint value that is specific for the type of the length.
Definition: qtextformat.h:104
qreal width() const
Returns the inline object&#39;s width.
QVector< QFixed > maxWidths
void endLayout()
Ends the layout process.
qreal bottom() const
Returns the y-coordinate of the rectangle&#39;s bottom edge.
Definition: qrect.h:528
iterator insertMulti(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition: qhash.h:772
int intProperty(int propertyId) const
Returns the value of the property specified by propertyId.
int columnSpan() const
Returns the number of columns this cell spans.
Definition: qtexttable.cpp:228
void pageCountChanged(int newPages)
This signal is emitted when the number of pages in the layout changes; newPages is the updated page c...
void setViewport(const QRectF &viewport)
QVector< Selection > selections
the collection of selections that will be rendered when passing this paint context to QAbstractTextDo...
QRectF translated(qreal dx, qreal dy) const
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis...
Definition: qrect.h:740
qreal border() const
Returns the width of the border in pixels.
Definition: qtextformat.h:762
int height() const
Returns the height of the font.
virtual QRectF frameBoundingRect(QTextFrame *frame) const
Returns the bounding rectangle of frame.
void setLayoutData(QTextFrameLayoutData *data)
QImageIOHandler * handler
qreal cellPadding() const
Returns the table&#39;s cell padding.
Definition: qtextformat.h:867
The QTextObject class is a base class for different kinds of objects that can group parts of a QTextD...
Definition: qtextobject.h:64
static const KeyPair *const end
qreal indentWidth
Returns the width used for text list and text block indenting.
int xToCursor(qreal x, CursorPosition=CursorBetweenCharacters) const
Converts the x-coordinate x, to the nearest matching cursor position, depending on the cursor positio...
QFixed topPadding(const QTextFormat &format) const
virtual void timerEvent(QTimerEvent *e)
This event handler can be reimplemented in a subclass to receive timer events for the object...
Q_CORE_EXPORT QTextStream & left(QTextStream &s)
QTextBlock currentBlock() const
Returns the current block the iterator points to.
BorderStyle
This enum describes different border styles for the text frame.
Definition: qtextformat.h:742
static void markFrames(QTextFrame *current, int from, int oldLength, int length)
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
int size() const
Returns the number of items in the vector.
Definition: qvector.h:137
QTextFrameFormat frameFormat() const
Returns the frame&#39;s format.
Definition: qtextobject.h:131
int indent() const
Returns the list format&#39;s indentation.
Definition: qtextformat.h:666
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:55
QFixed cellWidth(int column, int colspan) const
qreal leftMargin() const
Returns the paragraph&#39;s left margin.
Definition: qtextformat.h:576
#define INT_MAX
QTextBlockFormat blockFormat() const
Returns the QTextBlockFormat that describes block-specific properties.
qreal height() const
Returns the line&#39;s height.
int hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
Returns the cursor postion for the given point with the specified accuracy.
void resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
Sets the size of the inline object item corresponding to the text format.
QVector< QTextLength > columnWidthConstraints() const
Returns a list of constraints used by this table format to control the appearance of columns in a tab...
Definition: qtextformat.h:856
int selectionStart() const
Returns the start of the selection or position() if the cursor doesn&#39;t have a selection.
QTextOption::WrapMode wordWrapMode
void beginLayout()
Begins the layout process.
int textStart() const
Returns the start of the line from the beginning of the string passed to the QTextLayout.
qreal toReal(bool *ok=0) const
Returns the variant as a qreal if the variant has type() Double , QMetaType::Float ...
Definition: qvariant.cpp:2740
void fillRect(const QRectF &, const QBrush &)
Fills the given rectangle with the brush specified.
Definition: qpainter.cpp:7420
static bool isEmptyBlockBeforeTable(const QTextBlock &block, const QTextBlockFormat &format, const QTextFrame::Iterator &nextIt)
const QBrush & text() const
Returns the text foreground brush of the current color group.
Definition: qpalette.h:129
int lineCount() const
Returns the number of lines in this text layout.
static qreal toReal(Register *reg, int type, bool *ok=0)
iterator begin() const
Returns an iterator pointing to the first document element inside the frame.
The QTransform class specifies 2D transformations of a coordinate system.
Definition: qtransform.h:65
The QList class is a template class that provides lists.
Definition: qdatastream.h:62
QRectF naturalTextRect() const
Returns the rectangle covered by the line.
BorderStyle borderStyle() const
Returns the style of the frame&#39;s border.
Definition: qtextformat.h:772
The QAbstractTextDocumentLayout class is an abstract base class used to implement custom layouts for ...
QTextLength lengthProperty(int propertyId) const
Returns the value of the property given by propertyId.
QFixed height
Definition: qfixed_p.h:208
void removeAt(int i)
Removes the item at index position i.
Definition: qlist.h:480
QTextLayout * layout() const
Returns the QTextLayout that is used to lay out and display the block&#39;s contents. ...