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