src/gui/text/qtextdocumentlayout.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 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         layout.createLine();
       
  1441         layout.endLayout();
       
  1442         layout.draw(painter, QPointF(r.left(), pos.y()));
       
  1443         break;
       
  1444     }
       
  1445     case QTextListFormat::ListSquare:
       
  1446         painter->fillRect(r, brush);
       
  1447         break;
       
  1448     case QTextListFormat::ListCircle:
       
  1449         painter->drawEllipse(r);
       
  1450         break;
       
  1451     case QTextListFormat::ListDisc:
       
  1452         painter->setBrush(brush);
       
  1453         painter->setPen(Qt::NoPen);
       
  1454         painter->drawEllipse(r);
       
  1455         painter->setBrush(Qt::NoBrush);
       
  1456         break;
       
  1457     case QTextListFormat::ListStyleUndefined:
       
  1458         break;
       
  1459     default:
       
  1460         break;
       
  1461     }
       
  1462 
       
  1463     painter->restore();
       
  1464 }
       
  1465 
       
  1466 static QFixed flowPosition(const QTextFrame::iterator it)
       
  1467 {
       
  1468     if (it.atEnd())
       
  1469         return 0;
       
  1470 
       
  1471     if (it.currentFrame()) {
       
  1472         return data(it.currentFrame())->position.y;
       
  1473     } else {
       
  1474         QTextBlock block = it.currentBlock();
       
  1475         QTextLayout *layout = block.layout();
       
  1476         if (layout->lineCount() == 0)
       
  1477             return QFixed::fromReal(layout->position().y());
       
  1478         else
       
  1479             return QFixed::fromReal(layout->position().y() + layout->lineAt(0).y());
       
  1480     }
       
  1481 }
       
  1482 
       
  1483 static QFixed firstChildPos(const QTextFrame *f)
       
  1484 {
       
  1485     return flowPosition(f->begin());
       
  1486 }
       
  1487 
       
  1488 QLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QTextTableCell &cell, QFixed width,
       
  1489                                                     int layoutFrom, int layoutTo, QTextTableData *td,
       
  1490                                                     QFixed absoluteTableY, bool withPageBreaks)
       
  1491 {
       
  1492     LDEBUG << "layoutCell";
       
  1493     QLayoutStruct layoutStruct;
       
  1494     layoutStruct.frame = t;
       
  1495     layoutStruct.minimumWidth = 0;
       
  1496     layoutStruct.maximumWidth = QFIXED_MAX;
       
  1497     layoutStruct.y = 0;
       
  1498 
       
  1499     const QTextFormat fmt = cell.format();
       
  1500     const QFixed topPadding = td->topPadding(fmt);
       
  1501     if (withPageBreaks) {
       
  1502         layoutStruct.frameY = absoluteTableY + td->rowPositions.at(cell.row()) + topPadding;
       
  1503     }
       
  1504     layoutStruct.x_left = 0;
       
  1505     layoutStruct.x_right = width;
       
  1506     // we get called with different widths all the time (for example for figuring
       
  1507     // out the min/max widths), so we always have to do the full layout ;(
       
  1508     // also when for example in a table layoutFrom/layoutTo affect only one cell,
       
  1509     // making that one cell grow the available width of the other cells may change
       
  1510     // (shrink) and therefore when layoutCell gets called for them they have to
       
  1511     // be re-laid out, even if layoutFrom/layoutTo is not in their range. Hence
       
  1512     // this line:
       
  1513 
       
  1514     layoutStruct.pageHeight = QFixed::fromReal(document->pageSize().height());
       
  1515     if (layoutStruct.pageHeight < 0 || !withPageBreaks)
       
  1516         layoutStruct.pageHeight = QFIXED_MAX;
       
  1517     const int currentPage = layoutStruct.currentPage();
       
  1518     layoutStruct.pageTopMargin = td->effectiveTopMargin + td->cellSpacing + td->border + topPadding;
       
  1519     layoutStruct.pageBottomMargin = td->effectiveBottomMargin + td->cellSpacing + td->border + td->bottomPadding(fmt);
       
  1520     layoutStruct.pageBottom = (currentPage + 1) * layoutStruct.pageHeight - layoutStruct.pageBottomMargin;
       
  1521 
       
  1522     layoutStruct.fullLayout = true;
       
  1523 
       
  1524     QFixed pageTop = currentPage * layoutStruct.pageHeight + layoutStruct.pageTopMargin - layoutStruct.frameY;
       
  1525     layoutStruct.y = qMax(layoutStruct.y, pageTop);
       
  1526 
       
  1527     const QList<QTextFrame *> childFrames = td->childFrameMap.values(cell.row() + cell.column() * t->rows());
       
  1528     for (int i = 0; i < childFrames.size(); ++i) {
       
  1529         QTextFrame *frame = childFrames.at(i);
       
  1530         QTextFrameData *cd = data(frame);
       
  1531         cd->sizeDirty = true;
       
  1532     }
       
  1533 
       
  1534     layoutFlow(cell.begin(), &layoutStruct, layoutFrom, layoutTo, width);
       
  1535 
       
  1536     QFixed floatMinWidth;
       
  1537 
       
  1538     // floats that are located inside the text (like inline images) aren't taken into account by
       
  1539     // layoutFlow with regards to the cell height (layoutStruct->y), so for a safety measure we
       
  1540     // do that here. For example with <td><img align="right" src="..." />blah</td>
       
  1541     // when the image happens to be higher than the text
       
  1542     for (int i = 0; i < childFrames.size(); ++i) {
       
  1543         QTextFrame *frame = childFrames.at(i);
       
  1544         QTextFrameData *cd = data(frame);
       
  1545 
       
  1546         if (frame->frameFormat().position() != QTextFrameFormat::InFlow)
       
  1547             layoutStruct.y = qMax(layoutStruct.y, cd->position.y + cd->size.height);
       
  1548 
       
  1549         floatMinWidth = qMax(floatMinWidth, cd->minimumWidth);
       
  1550     }
       
  1551 
       
  1552     // constraint the maximumWidth by the minimum width of the fixed size floats, to
       
  1553     // keep them visible
       
  1554     layoutStruct.maximumWidth = qMax(layoutStruct.maximumWidth, floatMinWidth);
       
  1555 
       
  1556     // as floats in cells get added to the table's float list but must not affect
       
  1557     // floats in other cells we must clear the list here.
       
  1558     data(t)->floats.clear();
       
  1559 
       
  1560 //    qDebug() << "layoutCell done";
       
  1561 
       
  1562     return layoutStruct;
       
  1563 }
       
  1564 
       
  1565 QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom, int layoutTo, QFixed parentY)
       
  1566 {
       
  1567     LDEBUG << "layoutTable";
       
  1568     QTextTableData *td = static_cast<QTextTableData *>(data(table));
       
  1569     Q_ASSERT(td->sizeDirty);
       
  1570     const int rows = table->rows();
       
  1571     const int columns = table->columns();
       
  1572 
       
  1573     const QTextTableFormat fmt = table->format();
       
  1574 
       
  1575     td->childFrameMap.clear();
       
  1576     {
       
  1577         const QList<QTextFrame *> children = table->childFrames();
       
  1578         for (int i = 0; i < children.count(); ++i) {
       
  1579             QTextFrame *frame = children.at(i);
       
  1580             QTextTableCell cell = table->cellAt(frame->firstPosition());
       
  1581             td->childFrameMap.insertMulti(cell.row() + cell.column() * rows, frame);
       
  1582         }
       
  1583     }
       
  1584 
       
  1585     QVector<QTextLength> columnWidthConstraints = fmt.columnWidthConstraints();
       
  1586     if (columnWidthConstraints.size() != columns)
       
  1587         columnWidthConstraints.resize(columns);
       
  1588     Q_ASSERT(columnWidthConstraints.count() == columns);
       
  1589 
       
  1590     const QFixed cellSpacing = td->cellSpacing = QFixed::fromReal(scaleToDevice(fmt.cellSpacing()));
       
  1591     td->deviceScale = scaleToDevice(qreal(1));
       
  1592     td->cellPadding = QFixed::fromReal(scaleToDevice(fmt.cellPadding()));
       
  1593     const QFixed leftMargin = td->leftMargin + td->border + td->padding;
       
  1594     const QFixed rightMargin = td->rightMargin + td->border + td->padding;
       
  1595     const QFixed topMargin = td->topMargin + td->border + td->padding;
       
  1596 
       
  1597     const QFixed absoluteTableY = parentY + td->position.y;
       
  1598 
       
  1599     const QTextOption::WrapMode oldDefaultWrapMode = docPrivate->defaultTextOption.wrapMode();
       
  1600 
       
  1601 recalc_minmax_widths:
       
  1602 
       
  1603     QFixed remainingWidth = td->contentsWidth;
       
  1604     // two (vertical) borders per cell per column
       
  1605     remainingWidth -= columns * 2 * td->border;
       
  1606     // inter-cell spacing
       
  1607     remainingWidth -= (columns - 1) * cellSpacing;
       
  1608     // cell spacing at the left and right hand side
       
  1609     remainingWidth -= 2 * cellSpacing;
       
  1610     // remember the width used to distribute to percentaged columns
       
  1611     const QFixed initialTotalWidth = remainingWidth;
       
  1612 
       
  1613     td->widths.resize(columns);
       
  1614     td->widths.fill(0);
       
  1615 
       
  1616     td->minWidths.resize(columns);
       
  1617     // start with a minimum width of 0. totally empty
       
  1618     // cells of default created tables are invisible otherwise
       
  1619     // and therefore hardly editable
       
  1620     td->minWidths.fill(1);
       
  1621 
       
  1622     td->maxWidths.resize(columns);
       
  1623     td->maxWidths.fill(QFIXED_MAX);
       
  1624 
       
  1625     // calculate minimum and maximum sizes of the columns
       
  1626     for (int i = 0; i < columns; ++i) {
       
  1627         for (int row = 0; row < rows; ++row) {
       
  1628             const QTextTableCell cell = table->cellAt(row, i);
       
  1629             const int cspan = cell.columnSpan();
       
  1630 
       
  1631             if (cspan > 1 && i != cell.column())
       
  1632                 continue;
       
  1633 
       
  1634             const QTextFormat fmt = cell.format();
       
  1635             const QFixed leftPadding = td->leftPadding(fmt);
       
  1636             const QFixed rightPadding = td->rightPadding(fmt);
       
  1637             const QFixed widthPadding = leftPadding + rightPadding;
       
  1638 
       
  1639             // to figure out the min and the max width lay out the cell at
       
  1640             // maximum width. otherwise the maxwidth calculation sometimes
       
  1641             // returns wrong values
       
  1642             QLayoutStruct layoutStruct = layoutCell(table, cell, QFIXED_MAX, layoutFrom,
       
  1643                                                     layoutTo, td, absoluteTableY,
       
  1644                                                     /*withPageBreaks =*/false);
       
  1645 
       
  1646             // distribute the minimum width over all columns the cell spans
       
  1647             QFixed widthToDistribute = layoutStruct.minimumWidth + widthPadding;
       
  1648             for (int n = 0; n < cspan; ++n) {
       
  1649                 const int col = i + n;
       
  1650                 QFixed w = widthToDistribute / (cspan - n);
       
  1651                 td->minWidths[col] = qMax(td->minWidths.at(col), w);
       
  1652                 widthToDistribute -= td->minWidths.at(col);
       
  1653                 if (widthToDistribute <= 0)
       
  1654                     break;
       
  1655             }
       
  1656 
       
  1657             QFixed maxW = td->maxWidths.at(i);
       
  1658             if (layoutStruct.maximumWidth != QFIXED_MAX) {
       
  1659                 if (maxW == QFIXED_MAX)
       
  1660                     maxW = layoutStruct.maximumWidth + widthPadding;
       
  1661                 else
       
  1662                     maxW = qMax(maxW, layoutStruct.maximumWidth + widthPadding);
       
  1663             }
       
  1664             if (maxW == QFIXED_MAX)
       
  1665                 continue;
       
  1666 
       
  1667             widthToDistribute = maxW;
       
  1668             for (int n = 0; n < cspan; ++n) {
       
  1669                 const int col = i + n;
       
  1670                 QFixed w = widthToDistribute / (cspan - n);
       
  1671                 td->maxWidths[col] = qMax(td->minWidths.at(col), w);
       
  1672                 widthToDistribute -= td->maxWidths.at(col);
       
  1673                 if (widthToDistribute <= 0)
       
  1674                     break;
       
  1675             }
       
  1676         }
       
  1677     }
       
  1678 
       
  1679     // set fixed values, figure out total percentages used and number of
       
  1680     // variable length cells. Also assign the minimum width for variable columns.
       
  1681     QFixed totalPercentage;
       
  1682     int variableCols = 0;
       
  1683     QFixed totalMinWidth = 0;
       
  1684     for (int i = 0; i < columns; ++i) {
       
  1685         const QTextLength &length = columnWidthConstraints.at(i);
       
  1686         if (length.type() == QTextLength::FixedLength) {
       
  1687             td->minWidths[i] = td->widths[i] = qMax(scaleToDevice(QFixed::fromReal(length.rawValue())), td->minWidths.at(i));
       
  1688             remainingWidth -= td->widths.at(i);
       
  1689         } else if (length.type() == QTextLength::PercentageLength) {
       
  1690             totalPercentage += QFixed::fromReal(length.rawValue());
       
  1691         } else if (length.type() == QTextLength::VariableLength) {
       
  1692             variableCols++;
       
  1693 
       
  1694             td->widths[i] = td->minWidths.at(i);
       
  1695             remainingWidth -= td->minWidths.at(i);
       
  1696         }
       
  1697         totalMinWidth += td->minWidths.at(i);
       
  1698     }
       
  1699 
       
  1700     // set percentage values
       
  1701     {
       
  1702         const QFixed totalPercentagedWidth = initialTotalWidth * totalPercentage / 100;
       
  1703         QFixed remainingMinWidths = totalMinWidth;
       
  1704         for (int i = 0; i < columns; ++i) {
       
  1705             remainingMinWidths -= td->minWidths.at(i);
       
  1706             if (columnWidthConstraints.at(i).type() == QTextLength::PercentageLength) {
       
  1707                 const QFixed allottedPercentage = QFixed::fromReal(columnWidthConstraints.at(i).rawValue());
       
  1708 
       
  1709                 const QFixed percentWidth = totalPercentagedWidth * allottedPercentage / totalPercentage;
       
  1710                 if (percentWidth >= td->minWidths.at(i)) {
       
  1711                     td->widths[i] = qBound(td->minWidths.at(i), percentWidth, remainingWidth - remainingMinWidths);
       
  1712                 } else {
       
  1713                     td->widths[i] = td->minWidths.at(i);
       
  1714                 }
       
  1715                 remainingWidth -= td->widths.at(i);
       
  1716             }
       
  1717         }
       
  1718     }
       
  1719 
       
  1720     // for variable columns distribute the remaining space
       
  1721     if (variableCols > 0 && remainingWidth > 0) {
       
  1722         QVarLengthArray<int> columnsWithProperMaxSize;
       
  1723         for (int i = 0; i < columns; ++i)
       
  1724             if (columnWidthConstraints.at(i).type() == QTextLength::VariableLength
       
  1725                 && td->maxWidths.at(i) != QFIXED_MAX)
       
  1726                 columnsWithProperMaxSize.append(i);
       
  1727 
       
  1728         QFixed lastRemainingWidth = remainingWidth;
       
  1729         while (remainingWidth > 0) {
       
  1730             for (int k = 0; k < columnsWithProperMaxSize.count(); ++k) {
       
  1731                 const int col = columnsWithProperMaxSize[k];
       
  1732                 const int colsLeft = columnsWithProperMaxSize.count() - k;
       
  1733                 const QFixed w = qMin(td->maxWidths.at(col) - td->widths.at(col), remainingWidth / colsLeft);
       
  1734                 td->widths[col] += w;
       
  1735                 remainingWidth -= w;
       
  1736             }
       
  1737             if (remainingWidth == lastRemainingWidth)
       
  1738                 break;
       
  1739             lastRemainingWidth = remainingWidth;
       
  1740         }
       
  1741 
       
  1742         if (remainingWidth > 0
       
  1743             // don't unnecessarily grow variable length sized tables
       
  1744             && fmt.width().type() != QTextLength::VariableLength) {
       
  1745             const QFixed widthPerAnySizedCol = remainingWidth / variableCols;
       
  1746             for (int col = 0; col < columns; ++col) {
       
  1747                 if (columnWidthConstraints.at(col).type() == QTextLength::VariableLength)
       
  1748                     td->widths[col] += widthPerAnySizedCol;
       
  1749             }
       
  1750         }
       
  1751     }
       
  1752 
       
  1753     td->columnPositions.resize(columns);
       
  1754     td->columnPositions[0] = leftMargin /*includes table border*/ + cellSpacing + td->border;
       
  1755 
       
  1756     for (int i = 1; i < columns; ++i)
       
  1757         td->columnPositions[i] = td->columnPositions.at(i-1) + td->widths.at(i-1) + 2 * td->border + cellSpacing;
       
  1758 
       
  1759     // - margin to compensate the + margin in columnPositions[0]
       
  1760     const QFixed contentsWidth = td->columnPositions.last() + td->widths.last() + td->padding + td->border + cellSpacing - leftMargin;
       
  1761 
       
  1762     // if the table is too big and causes an overflow re-do the layout with WrapAnywhere as wrap
       
  1763     // mode
       
  1764     if (docPrivate->defaultTextOption.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere
       
  1765         && contentsWidth > td->contentsWidth) {
       
  1766         docPrivate->defaultTextOption.setWrapMode(QTextOption::WrapAnywhere);
       
  1767         // go back to the top of the function
       
  1768         goto recalc_minmax_widths;
       
  1769     }
       
  1770 
       
  1771     td->contentsWidth = contentsWidth;
       
  1772 
       
  1773     docPrivate->defaultTextOption.setWrapMode(oldDefaultWrapMode);
       
  1774 
       
  1775     td->heights.resize(rows);
       
  1776     td->heights.fill(0);
       
  1777 
       
  1778     td->rowPositions.resize(rows);
       
  1779     td->rowPositions[0] = topMargin /*includes table border*/ + cellSpacing + td->border;
       
  1780 
       
  1781     bool haveRowSpannedCells = false;
       
  1782 
       
  1783     // need to keep track of cell heights for vertical alignment
       
  1784     QVector<QFixed> cellHeights;
       
  1785     cellHeights.reserve(rows * columns);
       
  1786 
       
  1787     QFixed pageHeight = QFixed::fromReal(document->pageSize().height());
       
  1788     if (pageHeight <= 0)
       
  1789         pageHeight = QFIXED_MAX;
       
  1790 
       
  1791     QVector<QFixed> heightToDistribute;
       
  1792     heightToDistribute.resize(columns);
       
  1793 
       
  1794     td->headerHeight = 0;
       
  1795     const int headerRowCount = qMin(table->format().headerRowCount(), rows - 1);
       
  1796     const QFixed originalTopMargin = td->effectiveTopMargin;
       
  1797     bool hasDroppedTable = false;
       
  1798 
       
  1799     // now that we have the column widths we can lay out all cells with the right width.
       
  1800     // spanning cells are only allowed to grow the last row spanned by the cell.
       
  1801     //
       
  1802     // ### this could be made faster by iterating over the cells array of QTextTable
       
  1803     for (int r = 0; r < rows; ++r) {
       
  1804         td->calcRowPosition(r);
       
  1805 
       
  1806         const int tableStartPage = (absoluteTableY / pageHeight).truncate();
       
  1807         const int currentPage = ((td->rowPositions[r] + absoluteTableY) / pageHeight).truncate();
       
  1808         const QFixed pageBottom = (currentPage + 1) * pageHeight - td->effectiveBottomMargin - absoluteTableY - cellSpacing - td->border;
       
  1809         const QFixed pageTop = currentPage * pageHeight + td->effectiveTopMargin - absoluteTableY + cellSpacing + td->border;
       
  1810         const QFixed nextPageTop = pageTop + pageHeight;
       
  1811 
       
  1812         if (td->rowPositions[r] > pageBottom)
       
  1813             td->rowPositions[r] = nextPageTop;
       
  1814         else if (td->rowPositions[r] < pageTop)
       
  1815             td->rowPositions[r] = pageTop;
       
  1816 
       
  1817         bool dropRowToNextPage = true;
       
  1818         int cellCountBeforeRow = cellHeights.size();
       
  1819 
       
  1820         // if we drop the row to the next page we need to subtract the drop
       
  1821         // distance from any row spanning cells
       
  1822         QFixed dropDistance = 0;
       
  1823 
       
  1824 relayout:
       
  1825         const int rowStartPage = ((td->rowPositions[r] + absoluteTableY) / pageHeight).truncate();
       
  1826         // if any of the header rows or the first non-header row start on the next page
       
  1827         // then the entire header should be dropped
       
  1828         if (r <= headerRowCount && rowStartPage > tableStartPage && !hasDroppedTable) {
       
  1829             td->rowPositions[0] = nextPageTop;
       
  1830             cellHeights.clear();
       
  1831             td->effectiveTopMargin = originalTopMargin;
       
  1832             hasDroppedTable = true;
       
  1833             r = -1;
       
  1834             continue;
       
  1835         }
       
  1836 
       
  1837         int rowCellCount = 0;
       
  1838         for (int c = 0; c < columns; ++c) {
       
  1839             QTextTableCell cell = table->cellAt(r, c);
       
  1840             const int rspan = cell.rowSpan();
       
  1841             const int cspan = cell.columnSpan();
       
  1842 
       
  1843             if (cspan > 1 && cell.column() != c)
       
  1844                 continue;
       
  1845 
       
  1846             if (rspan > 1) {
       
  1847                 haveRowSpannedCells = true;
       
  1848 
       
  1849                 const int cellRow = cell.row();
       
  1850                 if (cellRow != r) {
       
  1851                     // the last row gets all the remaining space
       
  1852                     if (cellRow + rspan - 1 == r)
       
  1853                         td->heights[r] = qMax(td->heights.at(r), heightToDistribute.at(c) - dropDistance);
       
  1854                     continue;
       
  1855                 }
       
  1856             }
       
  1857 
       
  1858             const QTextFormat fmt = cell.format();
       
  1859 
       
  1860             const QFixed topPadding = td->topPadding(fmt);
       
  1861             const QFixed bottomPadding = td->bottomPadding(fmt);
       
  1862             const QFixed leftPadding = td->leftPadding(fmt);
       
  1863             const QFixed rightPadding = td->rightPadding(fmt);
       
  1864             const QFixed widthPadding = leftPadding + rightPadding;
       
  1865 
       
  1866             ++rowCellCount;
       
  1867 
       
  1868             const QFixed width = td->cellWidth(c, cspan) - widthPadding;
       
  1869             QLayoutStruct layoutStruct = layoutCell(table, cell, width,
       
  1870                                                     layoutFrom, layoutTo,
       
  1871                                                     td, absoluteTableY,
       
  1872                                                     /*withPageBreaks =*/true);
       
  1873 
       
  1874             const QFixed height = layoutStruct.y + bottomPadding + topPadding;
       
  1875 
       
  1876             if (rspan > 1)
       
  1877                 heightToDistribute[c] = height + dropDistance;
       
  1878             else
       
  1879                 td->heights[r] = qMax(td->heights.at(r), height);
       
  1880 
       
  1881             cellHeights.append(layoutStruct.y);
       
  1882 
       
  1883             QFixed childPos = td->rowPositions.at(r) + topPadding + flowPosition(cell.begin());
       
  1884             if (childPos < pageBottom)
       
  1885                 dropRowToNextPage = false;
       
  1886         }
       
  1887 
       
  1888         if (rowCellCount > 0 && dropRowToNextPage) {
       
  1889             dropDistance = nextPageTop - td->rowPositions[r];
       
  1890             td->rowPositions[r] = nextPageTop;
       
  1891             td->heights[r] = 0;
       
  1892             dropRowToNextPage = false;
       
  1893             cellHeights.resize(cellCountBeforeRow);
       
  1894             if (r > headerRowCount)
       
  1895                 td->heights[r-1] = pageBottom - td->rowPositions[r-1];
       
  1896             goto relayout;
       
  1897         }
       
  1898 
       
  1899         if (haveRowSpannedCells) {
       
  1900             const QFixed effectiveHeight = td->heights.at(r) + td->border + cellSpacing + td->border;
       
  1901             for (int c = 0; c < columns; ++c)
       
  1902                 heightToDistribute[c] = qMax(heightToDistribute.at(c) - effectiveHeight - dropDistance, QFixed(0));
       
  1903         }
       
  1904 
       
  1905         if (r == headerRowCount - 1) {
       
  1906             td->headerHeight = td->rowPositions[r] + td->heights[r] - td->rowPositions[0] + td->cellSpacing + 2 * td->border;
       
  1907             td->headerHeight -= td->headerHeight * (td->headerHeight / pageHeight).truncate();
       
  1908             td->effectiveTopMargin += td->headerHeight;
       
  1909         }
       
  1910     }
       
  1911 
       
  1912     td->effectiveTopMargin = originalTopMargin;
       
  1913 
       
  1914     // now that all cells have been properly laid out, we can compute the
       
  1915     // vertical offsets for vertical alignment
       
  1916     td->cellVerticalOffsets.resize(rows * columns);
       
  1917     int cellIndex = 0;
       
  1918     for (int r = 0; r < rows; ++r) {
       
  1919         for (int c = 0; c < columns; ++c) {
       
  1920             QTextTableCell cell = table->cellAt(r, c);
       
  1921             if (cell.row() != r || cell.column() != c)
       
  1922                 continue;
       
  1923 
       
  1924             const int rowSpan = cell.rowSpan();
       
  1925             const QFixed availableHeight = td->rowPositions.at(r + rowSpan - 1) + td->heights.at(r + rowSpan - 1) - td->rowPositions.at(r);
       
  1926 
       
  1927             const QTextCharFormat cellFormat = cell.format();
       
  1928             const QFixed cellHeight = cellHeights.at(cellIndex++) + td->topPadding(cellFormat) + td->bottomPadding(cellFormat);
       
  1929 
       
  1930             QFixed offset = 0;
       
  1931             switch (cellFormat.verticalAlignment()) {
       
  1932             case QTextCharFormat::AlignMiddle:
       
  1933                 offset = (availableHeight - cellHeight) / 2;
       
  1934                 break;
       
  1935             case QTextCharFormat::AlignBottom:
       
  1936                 offset = availableHeight - cellHeight;
       
  1937                 break;
       
  1938             default:
       
  1939                 break;
       
  1940             };
       
  1941 
       
  1942             for (int rd = 0; rd < cell.rowSpan(); ++rd) {
       
  1943                 for (int cd = 0; cd < cell.columnSpan(); ++cd) {
       
  1944                     const int index = (c + cd) + (r + rd) * columns;
       
  1945                     td->cellVerticalOffsets[index] = offset;
       
  1946                 }
       
  1947             }
       
  1948         }
       
  1949     }
       
  1950 
       
  1951     td->minimumWidth = td->columnPositions.at(0);
       
  1952     for (int i = 0; i < columns; ++i) {
       
  1953         td->minimumWidth += td->minWidths.at(i) + 2 * td->border + cellSpacing;
       
  1954     }
       
  1955     td->minimumWidth += rightMargin - td->border;
       
  1956 
       
  1957     td->maximumWidth = td->columnPositions.at(0);
       
  1958     for (int i = 0; i < columns; ++i)
       
  1959         if (td->maxWidths.at(i) != QFIXED_MAX)
       
  1960             td->maximumWidth += td->maxWidths.at(i) + 2 * td->border + cellSpacing;
       
  1961     td->maximumWidth += rightMargin - td->border;
       
  1962 
       
  1963     td->updateTableSize();
       
  1964     td->sizeDirty = false;
       
  1965     return QRectF(); // invalid rect -> update everything
       
  1966 }
       
  1967 
       
  1968 void QTextDocumentLayoutPrivate::positionFloat(QTextFrame *frame, QTextLine *currentLine)
       
  1969 {
       
  1970     QTextFrameData *fd = data(frame);
       
  1971 
       
  1972     QTextFrame *parent = frame->parentFrame();
       
  1973     Q_ASSERT(parent);
       
  1974     QTextFrameData *pd = data(parent);
       
  1975     Q_ASSERT(pd && pd->currentLayoutStruct);
       
  1976 
       
  1977     QLayoutStruct *layoutStruct = pd->currentLayoutStruct;
       
  1978 
       
  1979     if (!pd->floats.contains(frame))
       
  1980         pd->floats.append(frame);
       
  1981     fd->layoutDirty = true;
       
  1982     Q_ASSERT(!fd->sizeDirty);
       
  1983 
       
  1984 //     qDebug() << "positionFloat:" << frame << "width=" << fd->size.width;
       
  1985     QFixed y = layoutStruct->y;
       
  1986     if (currentLine) {
       
  1987         QFixed left, right;
       
  1988         floatMargins(y, layoutStruct, &left, &right);
       
  1989 //         qDebug() << "have line: right=" << right << "left=" << left << "textWidth=" << currentLine->width();
       
  1990         if (right - left < QFixed::fromReal(currentLine->naturalTextWidth()) + fd->size.width) {
       
  1991             layoutStruct->pendingFloats.append(frame);
       
  1992 //             qDebug() << "    adding to pending list";
       
  1993             return;
       
  1994         }
       
  1995     }
       
  1996 
       
  1997     bool frameSpansIntoNextPage = (y + layoutStruct->frameY + fd->size.height > layoutStruct->pageBottom);
       
  1998     if (frameSpansIntoNextPage && fd->size.height <= layoutStruct->pageHeight) {
       
  1999         layoutStruct->newPage();
       
  2000         y = layoutStruct->y;
       
  2001 
       
  2002         frameSpansIntoNextPage = false;
       
  2003     }
       
  2004 
       
  2005     y = findY(y, layoutStruct, fd->size.width);
       
  2006 
       
  2007     QFixed left, right;
       
  2008     floatMargins(y, layoutStruct, &left, &right);
       
  2009 
       
  2010     if (frame->frameFormat().position() == QTextFrameFormat::FloatLeft) {
       
  2011         fd->position.x = left;
       
  2012         fd->position.y = y;
       
  2013     } else {
       
  2014         fd->position.x = right - fd->size.width;
       
  2015         fd->position.y = y;
       
  2016     }
       
  2017 
       
  2018     layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, fd->minimumWidth);
       
  2019     layoutStruct->maximumWidth = qMin(layoutStruct->maximumWidth, fd->maximumWidth);
       
  2020 
       
  2021 //     qDebug()<< "float positioned at " << fd->position.x << fd->position.y;
       
  2022     fd->layoutDirty = false;
       
  2023 
       
  2024     // If the frame is a table, then positioning it will affect the size if it covers more than
       
  2025     // one page, because of page breaks and repeating the header.
       
  2026     if (qobject_cast<QTextTable *>(frame) != 0)
       
  2027         fd->sizeDirty = frameSpansIntoNextPage;
       
  2028 }
       
  2029 
       
  2030 QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed parentY)
       
  2031 {
       
  2032     LDEBUG << "layoutFrame (pre)";
       
  2033     Q_ASSERT(data(f)->sizeDirty);
       
  2034 //     qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame());
       
  2035 
       
  2036     QTextFrameFormat fformat = f->frameFormat();
       
  2037 
       
  2038     QTextFrame *parent = f->parentFrame();
       
  2039     const QTextFrameData *pd = parent ? data(parent) : 0;
       
  2040 
       
  2041     const qreal maximumWidth = qMax(qreal(0), pd ? pd->contentsWidth.toReal() : document->pageSize().width());
       
  2042     QFixed width = QFixed::fromReal(fformat.width().value(maximumWidth));
       
  2043     if (fformat.width().type() == QTextLength::FixedLength)
       
  2044         width = scaleToDevice(width);
       
  2045 
       
  2046     const QFixed maximumHeight = pd ? pd->contentsHeight : -1;
       
  2047     const QFixed height = (maximumHeight != -1 || fformat.height().type() != QTextLength::PercentageLength)
       
  2048                             ? QFixed::fromReal(fformat.height().value(maximumHeight.toReal()))
       
  2049                             : -1;
       
  2050 
       
  2051     return layoutFrame(f, layoutFrom, layoutTo, width, height, parentY);
       
  2052 }
       
  2053 
       
  2054 QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed frameWidth, QFixed frameHeight, QFixed parentY)
       
  2055 {
       
  2056     LDEBUG << "layoutFrame from=" << layoutFrom << "to=" << layoutTo;
       
  2057     Q_ASSERT(data(f)->sizeDirty);
       
  2058 //     qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame());
       
  2059 
       
  2060     QTextFrameData *fd = data(f);
       
  2061     QFixed newContentsWidth;
       
  2062 
       
  2063     {
       
  2064         QTextFrameFormat fformat = f->frameFormat();
       
  2065         // set sizes of this frame from the format
       
  2066         fd->topMargin = QFixed::fromReal(fformat.topMargin());
       
  2067         fd->bottomMargin = QFixed::fromReal(fformat.bottomMargin());
       
  2068         fd->leftMargin = QFixed::fromReal(fformat.leftMargin());
       
  2069         fd->rightMargin = QFixed::fromReal(fformat.rightMargin());
       
  2070         fd->border = QFixed::fromReal(fformat.border());
       
  2071         fd->padding = QFixed::fromReal(fformat.padding());
       
  2072 
       
  2073         QTextFrame *parent = f->parentFrame();
       
  2074         const QTextFrameData *pd = parent ? data(parent) : 0;
       
  2075 
       
  2076         // accumulate top and bottom margins
       
  2077         if (parent) {
       
  2078             fd->effectiveTopMargin = pd->effectiveTopMargin + fd->topMargin + fd->border + fd->padding;
       
  2079             fd->effectiveBottomMargin = pd->effectiveBottomMargin + fd->topMargin + fd->border + fd->padding;
       
  2080 
       
  2081             if (qobject_cast<QTextTable *>(parent)) {
       
  2082                 const QTextTableData *td = static_cast<const QTextTableData *>(pd);
       
  2083                 fd->effectiveTopMargin += td->cellSpacing + td->border + td->cellPadding;
       
  2084                 fd->effectiveBottomMargin += td->cellSpacing + td->border + td->cellPadding;
       
  2085             }
       
  2086         } else {
       
  2087             fd->effectiveTopMargin = fd->topMargin + fd->border + fd->padding;
       
  2088             fd->effectiveBottomMargin = fd->bottomMargin + fd->border + fd->padding;
       
  2089         }
       
  2090 
       
  2091         newContentsWidth = frameWidth - 2*(fd->border + fd->padding)
       
  2092                            - fd->leftMargin - fd->rightMargin;
       
  2093 
       
  2094         if (frameHeight != -1) {
       
  2095             fd->contentsHeight = frameHeight - 2*(fd->border + fd->padding)
       
  2096                                  - fd->topMargin - fd->bottomMargin;
       
  2097         } else {
       
  2098             fd->contentsHeight = frameHeight;
       
  2099         }
       
  2100     }
       
  2101 
       
  2102     if (isFrameFromInlineObject(f)) {
       
  2103         // never reached, handled in resizeInlineObject/positionFloat instead
       
  2104         return QRectF();
       
  2105     }
       
  2106 
       
  2107     if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
       
  2108         fd->contentsWidth = newContentsWidth;
       
  2109         return layoutTable(table, layoutFrom, layoutTo, parentY);
       
  2110     }
       
  2111 
       
  2112     // set fd->contentsWidth temporarily, so that layoutFrame for the children
       
  2113     // picks the right width. We'll initialize it properly at the end of this
       
  2114     // function.
       
  2115     fd->contentsWidth = newContentsWidth;
       
  2116 
       
  2117     QLayoutStruct layoutStruct;
       
  2118     layoutStruct.frame = f;
       
  2119     layoutStruct.x_left = fd->leftMargin + fd->border + fd->padding;
       
  2120     layoutStruct.x_right = layoutStruct.x_left + newContentsWidth;
       
  2121     layoutStruct.y = fd->topMargin + fd->border + fd->padding;
       
  2122     layoutStruct.frameY = parentY + fd->position.y;
       
  2123     layoutStruct.contentsWidth = 0;
       
  2124     layoutStruct.minimumWidth = 0;
       
  2125     layoutStruct.maximumWidth = QFIXED_MAX;
       
  2126     layoutStruct.fullLayout = fd->oldContentsWidth != newContentsWidth;
       
  2127     layoutStruct.updateRect = QRectF(QPointF(0, 0), QSizeF(qreal(INT_MAX), qreal(INT_MAX)));
       
  2128     LDEBUG << "layoutStruct: x_left" << layoutStruct.x_left << "x_right" << layoutStruct.x_right
       
  2129            << "fullLayout" << layoutStruct.fullLayout;
       
  2130     fd->oldContentsWidth = newContentsWidth;
       
  2131 
       
  2132     layoutStruct.pageHeight = QFixed::fromReal(document->pageSize().height());
       
  2133     if (layoutStruct.pageHeight < 0)
       
  2134         layoutStruct.pageHeight = QFIXED_MAX;
       
  2135 
       
  2136     const int currentPage = layoutStruct.pageHeight == 0 ? 0 : (layoutStruct.frameY / layoutStruct.pageHeight).truncate();
       
  2137     layoutStruct.pageTopMargin = fd->effectiveTopMargin;
       
  2138     layoutStruct.pageBottomMargin = fd->effectiveBottomMargin;
       
  2139     layoutStruct.pageBottom = (currentPage + 1) * layoutStruct.pageHeight - layoutStruct.pageBottomMargin;
       
  2140 
       
  2141     if (!f->parentFrame())
       
  2142         idealWidth = 0; // reset
       
  2143 
       
  2144     QTextFrame::Iterator it = f->begin();
       
  2145     layoutFlow(it, &layoutStruct, layoutFrom, layoutTo);
       
  2146 
       
  2147     QFixed maxChildFrameWidth = 0;
       
  2148     QList<QTextFrame *> children = f->childFrames();
       
  2149     for (int i = 0; i < children.size(); ++i) {
       
  2150         QTextFrame *c = children.at(i);
       
  2151         QTextFrameData *cd = data(c);
       
  2152         maxChildFrameWidth = qMax(maxChildFrameWidth, cd->size.width);
       
  2153     }
       
  2154 
       
  2155     const QFixed marginWidth = 2*(fd->border + fd->padding) + fd->leftMargin + fd->rightMargin;
       
  2156     if (!f->parentFrame()) {
       
  2157         idealWidth = qMax(maxChildFrameWidth, layoutStruct.contentsWidth).toReal();
       
  2158         idealWidth += marginWidth.toReal();
       
  2159     }
       
  2160 
       
  2161     QFixed actualWidth = qMax(newContentsWidth, qMax(maxChildFrameWidth, layoutStruct.contentsWidth));
       
  2162     fd->contentsWidth = actualWidth;
       
  2163     if (newContentsWidth <= 0) { // nowrap layout?
       
  2164         fd->contentsWidth = newContentsWidth;
       
  2165     }
       
  2166 
       
  2167     fd->minimumWidth = layoutStruct.minimumWidth;
       
  2168     fd->maximumWidth = layoutStruct.maximumWidth;
       
  2169 
       
  2170     fd->size.height = fd->contentsHeight == -1
       
  2171                  ? layoutStruct.y + fd->border + fd->padding + fd->bottomMargin
       
  2172                  : fd->contentsHeight + 2*(fd->border + fd->padding) + fd->topMargin + fd->bottomMargin;
       
  2173     fd->size.width = actualWidth + marginWidth;
       
  2174     fd->sizeDirty = false;
       
  2175     if (layoutStruct.updateRectForFloats.isValid())
       
  2176         layoutStruct.updateRect |= layoutStruct.updateRectForFloats;
       
  2177     return layoutStruct.updateRect;
       
  2178 }
       
  2179 
       
  2180 void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QLayoutStruct *layoutStruct,
       
  2181                                             int layoutFrom, int layoutTo, QFixed width)
       
  2182 {
       
  2183     LDEBUG << "layoutFlow from=" << layoutFrom << "to=" << layoutTo;
       
  2184     QTextFrameData *fd = data(layoutStruct->frame);
       
  2185 
       
  2186     fd->currentLayoutStruct = layoutStruct;
       
  2187 
       
  2188     QTextFrame::Iterator previousIt;
       
  2189 
       
  2190     const bool inRootFrame = (it.parentFrame() == document->rootFrame());
       
  2191     if (inRootFrame) {
       
  2192         bool redoCheckPoints = layoutStruct->fullLayout || checkPoints.isEmpty();
       
  2193 
       
  2194         if (!redoCheckPoints) {
       
  2195             QVector<QCheckPoint>::Iterator checkPoint = qLowerBound(checkPoints.begin(), checkPoints.end(), layoutFrom);
       
  2196             if (checkPoint != checkPoints.end()) {
       
  2197                 if (checkPoint != checkPoints.begin())
       
  2198                     --checkPoint;
       
  2199 
       
  2200                 layoutStruct->y = checkPoint->y;
       
  2201                 layoutStruct->frameY = checkPoint->frameY;
       
  2202                 layoutStruct->minimumWidth = checkPoint->minimumWidth;
       
  2203                 layoutStruct->maximumWidth = checkPoint->maximumWidth;
       
  2204                 layoutStruct->contentsWidth = checkPoint->contentsWidth;
       
  2205 
       
  2206                 if (layoutStruct->pageHeight > 0) {
       
  2207                     int page = layoutStruct->currentPage();
       
  2208                     layoutStruct->pageBottom = (page + 1) * layoutStruct->pageHeight - layoutStruct->pageBottomMargin;
       
  2209                 }
       
  2210 
       
  2211                 it = frameIteratorForTextPosition(checkPoint->positionInFrame);
       
  2212                 checkPoints.resize(checkPoint - checkPoints.begin() + 1);
       
  2213 
       
  2214                 if (checkPoint != checkPoints.begin()) {
       
  2215                     previousIt = it;
       
  2216                     --previousIt;
       
  2217                 }
       
  2218             } else {
       
  2219                 redoCheckPoints = true;
       
  2220             }
       
  2221         }
       
  2222 
       
  2223         if (redoCheckPoints) {
       
  2224             checkPoints.clear();
       
  2225             QCheckPoint cp;
       
  2226             cp.y = layoutStruct->y;
       
  2227             cp.frameY = layoutStruct->frameY;
       
  2228             cp.positionInFrame = 0;
       
  2229             cp.minimumWidth = layoutStruct->minimumWidth;
       
  2230             cp.maximumWidth = layoutStruct->maximumWidth;
       
  2231             cp.contentsWidth = layoutStruct->contentsWidth;
       
  2232             checkPoints.append(cp);
       
  2233         }
       
  2234     }
       
  2235 
       
  2236     QTextBlockFormat previousBlockFormat = previousIt.currentBlock().blockFormat();
       
  2237 
       
  2238     QFixed maximumBlockWidth = 0;
       
  2239     while (!it.atEnd()) {
       
  2240         QTextFrame *c = it.currentFrame();
       
  2241 
       
  2242         int docPos;
       
  2243         if (it.currentFrame())
       
  2244             docPos = it.currentFrame()->firstPosition();
       
  2245         else
       
  2246             docPos = it.currentBlock().position();
       
  2247 
       
  2248         if (inRootFrame) {
       
  2249             if (qAbs(layoutStruct->y - checkPoints.last().y) > 2000) {
       
  2250                 QFixed left, right;
       
  2251                 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
       
  2252                 if (left == layoutStruct->x_left && right == layoutStruct->x_right) {
       
  2253                     QCheckPoint p;
       
  2254                     p.y = layoutStruct->y;
       
  2255                     p.frameY = layoutStruct->frameY;
       
  2256                     p.positionInFrame = docPos;
       
  2257                     p.minimumWidth = layoutStruct->minimumWidth;
       
  2258                     p.maximumWidth = layoutStruct->maximumWidth;
       
  2259                     p.contentsWidth = layoutStruct->contentsWidth;
       
  2260                     checkPoints.append(p);
       
  2261 
       
  2262                     if (currentLazyLayoutPosition != -1
       
  2263                         && docPos > currentLazyLayoutPosition + lazyLayoutStepSize)
       
  2264                         break;
       
  2265 
       
  2266                 }
       
  2267             }
       
  2268         }
       
  2269 
       
  2270         if (c) {
       
  2271             // position child frame
       
  2272             QTextFrameData *cd = data(c);
       
  2273 
       
  2274             QTextFrameFormat fformat = c->frameFormat();
       
  2275 
       
  2276             if (fformat.position() == QTextFrameFormat::InFlow) {
       
  2277                 if (fformat.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
       
  2278                     layoutStruct->newPage();
       
  2279 
       
  2280                 QFixed left, right;
       
  2281                 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
       
  2282                 left = qMax(left, layoutStruct->x_left);
       
  2283                 right = qMin(right, layoutStruct->x_right);
       
  2284 
       
  2285                 if (right - left < cd->size.width) {
       
  2286                     layoutStruct->y = findY(layoutStruct->y, layoutStruct, cd->size.width);
       
  2287                     floatMargins(layoutStruct->y, layoutStruct, &left, &right);
       
  2288                 }
       
  2289 
       
  2290                 QFixedPoint pos(left, layoutStruct->y);
       
  2291 
       
  2292                 Qt::Alignment align = Qt::AlignLeft;
       
  2293 
       
  2294                 QTextTable *table = qobject_cast<QTextTable *>(c);
       
  2295 
       
  2296                 if (table)
       
  2297                     align = table->format().alignment() & Qt::AlignHorizontal_Mask;
       
  2298 
       
  2299                 // detect whether we have any alignment in the document that disallows optimizations,
       
  2300                 // such as not laying out the document again in a textedit with wrapping disabled.
       
  2301                 if (inRootFrame && !(align & Qt::AlignLeft))
       
  2302                     contentHasAlignment = true;
       
  2303 
       
  2304                 cd->position = pos;
       
  2305 
       
  2306                 if (document->pageSize().height() > 0.0f)
       
  2307                     cd->sizeDirty = true;
       
  2308 
       
  2309                 if (cd->sizeDirty) {
       
  2310                     if (width != 0)
       
  2311                         layoutFrame(c, layoutFrom, layoutTo, width, -1, layoutStruct->frameY);
       
  2312                     else
       
  2313                         layoutFrame(c, layoutFrom, layoutTo, layoutStruct->frameY);
       
  2314 
       
  2315                     QFixed absoluteChildPos = table ? pos.y + static_cast<QTextTableData *>(data(table))->rowPositions.at(0) : pos.y + firstChildPos(c);
       
  2316                     absoluteChildPos += layoutStruct->frameY;
       
  2317 
       
  2318                     // drop entire frame to next page if first child of frame is on next page
       
  2319                     if (absoluteChildPos > layoutStruct->pageBottom) {
       
  2320                         layoutStruct->newPage();
       
  2321                         pos.y = layoutStruct->y;
       
  2322 
       
  2323                         cd->position = pos;
       
  2324                         cd->sizeDirty = true;
       
  2325 
       
  2326                         if (width != 0)
       
  2327                             layoutFrame(c, layoutFrom, layoutTo, width, -1, layoutStruct->frameY);
       
  2328                         else
       
  2329                             layoutFrame(c, layoutFrom, layoutTo, layoutStruct->frameY);
       
  2330                     }
       
  2331                 }
       
  2332 
       
  2333                 // align only if there is space for alignment
       
  2334                 if (right - left > cd->size.width) {
       
  2335                     if (align & Qt::AlignRight)
       
  2336                         pos.x += layoutStruct->x_right - cd->size.width;
       
  2337                     else if (align & Qt::AlignHCenter)
       
  2338                         pos.x += (layoutStruct->x_right - cd->size.width) / 2;
       
  2339                 }
       
  2340 
       
  2341                 cd->position = pos;
       
  2342 
       
  2343                 layoutStruct->y += cd->size.height;
       
  2344                 const int page = layoutStruct->currentPage();
       
  2345                 layoutStruct->pageBottom = (page + 1) * layoutStruct->pageHeight - layoutStruct->pageBottomMargin;
       
  2346 
       
  2347                 cd->layoutDirty = false;
       
  2348 
       
  2349                 if (c->frameFormat().pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
       
  2350                     layoutStruct->newPage();
       
  2351             } else {
       
  2352                 QRectF oldFrameRect(cd->position.toPointF(), cd->size.toSizeF());
       
  2353                 QRectF updateRect;
       
  2354 
       
  2355                 if (cd->sizeDirty)
       
  2356                     updateRect = layoutFrame(c, layoutFrom, layoutTo);
       
  2357 
       
  2358                 positionFloat(c);
       
  2359 
       
  2360                 // If the size was made dirty when the position was set, layout again
       
  2361                 if (cd->sizeDirty)
       
  2362                     updateRect = layoutFrame(c, layoutFrom, layoutTo);
       
  2363 
       
  2364                 QRectF frameRect(cd->position.toPointF(), cd->size.toSizeF());
       
  2365 
       
  2366                 if (frameRect == oldFrameRect && updateRect.isValid())
       
  2367                     updateRect.translate(cd->position.toPointF());
       
  2368                 else
       
  2369                     updateRect = frameRect;
       
  2370 
       
  2371                 layoutStruct->addUpdateRectForFloat(updateRect);
       
  2372                 if (oldFrameRect.isValid())
       
  2373                     layoutStruct->addUpdateRectForFloat(oldFrameRect);
       
  2374             }
       
  2375 
       
  2376             layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, cd->minimumWidth);
       
  2377             layoutStruct->maximumWidth = qMin(layoutStruct->maximumWidth, cd->maximumWidth);
       
  2378 
       
  2379             previousIt = it;
       
  2380             ++it;
       
  2381         } else {
       
  2382             QTextFrame::Iterator lastIt;
       
  2383             if (!previousIt.atEnd())
       
  2384                 lastIt = previousIt;
       
  2385             previousIt = it;
       
  2386             QTextBlock block = it.currentBlock();
       
  2387             ++it;
       
  2388 
       
  2389             const QTextBlockFormat blockFormat = block.blockFormat();
       
  2390 
       
  2391             if (blockFormat.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
       
  2392                 layoutStruct->newPage();
       
  2393 
       
  2394             const QFixed origY = layoutStruct->y;
       
  2395             const QFixed origPageBottom = layoutStruct->pageBottom;
       
  2396             const QFixed origMaximumWidth = layoutStruct->maximumWidth;
       
  2397             layoutStruct->maximumWidth = 0;
       
  2398 
       
  2399             const QTextBlockFormat *previousBlockFormatPtr = 0;
       
  2400             if (lastIt.currentBlock().isValid())
       
  2401                 previousBlockFormatPtr = &previousBlockFormat;
       
  2402 
       
  2403             // layout and position child block
       
  2404             layoutBlock(block, docPos, blockFormat, layoutStruct, layoutFrom, layoutTo, previousBlockFormatPtr);
       
  2405 
       
  2406             // detect whether we have any alignment in the document that disallows optimizations,
       
  2407             // such as not laying out the document again in a textedit with wrapping disabled.
       
  2408             if (inRootFrame && !(block.layout()->textOption().alignment() & Qt::AlignLeft))
       
  2409                 contentHasAlignment = true;
       
  2410 
       
  2411             // if the block right before a table is empty 'hide' it by
       
  2412             // positioning it into the table border
       
  2413             if (isEmptyBlockBeforeTable(block, blockFormat, it)) {
       
  2414                 const QTextBlock lastBlock = lastIt.currentBlock();
       
  2415                 const qreal lastBlockBottomMargin = lastBlock.isValid() ? lastBlock.blockFormat().bottomMargin() : 0.0f;
       
  2416                 layoutStruct->y = origY + QFixed::fromReal(qMax(lastBlockBottomMargin, block.blockFormat().topMargin()));
       
  2417                 layoutStruct->pageBottom = origPageBottom;
       
  2418             } else {
       
  2419                 // if the block right after a table is empty then 'hide' it, too
       
  2420                 if (isEmptyBlockAfterTable(block, lastIt.currentFrame())) {
       
  2421                     QTextTableData *td = static_cast<QTextTableData *>(data(lastIt.currentFrame()));
       
  2422                     QTextLayout *layout = block.layout();
       
  2423 
       
  2424                     QPointF pos((td->position.x + td->size.width).toReal(),
       
  2425                                 (td->position.y + td->size.height).toReal() - layout->boundingRect().height());
       
  2426 
       
  2427                     layout->setPosition(pos);
       
  2428                     layoutStruct->y = origY;
       
  2429                     layoutStruct->pageBottom = origPageBottom;
       
  2430                 }
       
  2431 
       
  2432                 // if the block right after a table starts with a line separator, shift it up by one line
       
  2433                 if (isLineSeparatorBlockAfterTable(block, lastIt.currentFrame())) {
       
  2434                     QTextTableData *td = static_cast<QTextTableData *>(data(lastIt.currentFrame()));
       
  2435                     QTextLayout *layout = block.layout();
       
  2436 
       
  2437                     QFixed height = QFixed::fromReal(layout->lineAt(0).height());
       
  2438 
       
  2439                     if (layoutStruct->pageBottom == origPageBottom) {
       
  2440                         layoutStruct->y -= height;
       
  2441                         layout->setPosition(layout->position() - QPointF(0, height.toReal()));
       
  2442                     } else {
       
  2443                         // relayout block to correctly handle page breaks
       
  2444                         layoutStruct->y = origY - height;
       
  2445                         layoutStruct->pageBottom = origPageBottom;
       
  2446                         layoutBlock(block, docPos, blockFormat, layoutStruct, layoutFrom, layoutTo, previousBlockFormatPtr);
       
  2447                     }
       
  2448 
       
  2449                     QPointF linePos((td->position.x + td->size.width).toReal(),
       
  2450                                     (td->position.y + td->size.height - height).toReal());
       
  2451 
       
  2452                     layout->lineAt(0).setPosition(linePos - layout->position());
       
  2453                 }
       
  2454 
       
  2455                 if (blockFormat.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
       
  2456                     layoutStruct->newPage();
       
  2457             }
       
  2458 
       
  2459             maximumBlockWidth = qMax(maximumBlockWidth, layoutStruct->maximumWidth);
       
  2460             layoutStruct->maximumWidth = origMaximumWidth;
       
  2461             previousBlockFormat = blockFormat;
       
  2462         }
       
  2463     }
       
  2464     if (layoutStruct->maximumWidth == QFIXED_MAX && maximumBlockWidth > 0)
       
  2465         layoutStruct->maximumWidth = maximumBlockWidth;
       
  2466     else
       
  2467         layoutStruct->maximumWidth = qMax(layoutStruct->maximumWidth, maximumBlockWidth);
       
  2468 
       
  2469     // a float at the bottom of a frame may make it taller, hence the qMax() for layoutStruct->y.
       
  2470     // we don't need to do it for tables though because floats in tables are per table
       
  2471     // and not per cell and layoutCell already takes care of doing the same as we do here
       
  2472     if (!qobject_cast<QTextTable *>(layoutStruct->frame)) {
       
  2473         QList<QTextFrame *> children = layoutStruct->frame->childFrames();
       
  2474         for (int i = 0; i < children.count(); ++i) {
       
  2475             QTextFrameData *fd = data(children.at(i));
       
  2476             if (!fd->layoutDirty && children.at(i)->frameFormat().position() != QTextFrameFormat::InFlow)
       
  2477                 layoutStruct->y = qMax(layoutStruct->y, fd->position.y + fd->size.height);
       
  2478         }
       
  2479     }
       
  2480 
       
  2481     if (inRootFrame) {
       
  2482         // we assume that any float is aligned in a way that disallows the optimizations that rely
       
  2483         // on unaligned content.
       
  2484         if (!fd->floats.isEmpty())
       
  2485             contentHasAlignment = true;
       
  2486 
       
  2487         if (it.atEnd()) {
       
  2488             //qDebug() << "layout done!";
       
  2489             currentLazyLayoutPosition = -1;
       
  2490             QCheckPoint cp;
       
  2491             cp.y = layoutStruct->y;
       
  2492             cp.positionInFrame = docPrivate->length();
       
  2493             cp.minimumWidth = layoutStruct->minimumWidth;
       
  2494             cp.maximumWidth = layoutStruct->maximumWidth;
       
  2495             cp.contentsWidth = layoutStruct->contentsWidth;
       
  2496             checkPoints.append(cp);
       
  2497             checkPoints.reserve(checkPoints.size());
       
  2498         } else {
       
  2499             currentLazyLayoutPosition = checkPoints.last().positionInFrame;
       
  2500             // #######
       
  2501             //checkPoints.last().positionInFrame = q->document()->docHandle()->length();
       
  2502         }
       
  2503     }
       
  2504 
       
  2505 
       
  2506     fd->currentLayoutStruct = 0;
       
  2507 }
       
  2508 
       
  2509 void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosition, const QTextBlockFormat &blockFormat,
       
  2510                                              QLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat)
       
  2511 {
       
  2512     Q_Q(QTextDocumentLayout);
       
  2513 
       
  2514     QTextLayout *tl = bl.layout();
       
  2515     const int blockLength = bl.length();
       
  2516 
       
  2517     LDEBUG << "layoutBlock from=" << layoutFrom << "to=" << layoutTo;
       
  2518 
       
  2519 //    qDebug() << "layoutBlock; width" << layoutStruct->x_right - layoutStruct->x_left << "(maxWidth is btw" << tl->maximumWidth() << ')';
       
  2520 
       
  2521     if (previousBlockFormat) {
       
  2522         qreal margin = qMax(blockFormat.topMargin(), previousBlockFormat->bottomMargin());
       
  2523         if (margin > 0 && q->paintDevice()) {
       
  2524             margin *= qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi());
       
  2525         }
       
  2526         layoutStruct->y += QFixed::fromReal(margin);
       
  2527     }
       
  2528 
       
  2529     //QTextFrameData *fd = data(layoutStruct->frame);
       
  2530 
       
  2531     Qt::LayoutDirection dir = docPrivate->defaultTextOption.textDirection();
       
  2532     if (blockFormat.hasProperty(QTextFormat::LayoutDirection))
       
  2533         dir = blockFormat.layoutDirection();
       
  2534 
       
  2535     QFixed extraMargin;
       
  2536     if (docPrivate->defaultTextOption.flags() & QTextOption::AddSpaceForLineAndParagraphSeparators) {
       
  2537         QFontMetricsF fm(bl.charFormat().font());
       
  2538         extraMargin = QFixed::fromReal(fm.width(QChar(QChar(0x21B5))));
       
  2539     }
       
  2540 
       
  2541     const QFixed indent = this->blockIndent(blockFormat);
       
  2542     const QFixed totalLeftMargin = QFixed::fromReal(blockFormat.leftMargin()) + (dir == Qt::RightToLeft ? extraMargin : indent);
       
  2543     const QFixed totalRightMargin = QFixed::fromReal(blockFormat.rightMargin()) + (dir == Qt::RightToLeft ? indent : extraMargin);
       
  2544 
       
  2545     const QPointF oldPosition = tl->position();
       
  2546     tl->setPosition(QPointF(layoutStruct->x_left.toReal(), layoutStruct->y.toReal()));
       
  2547 
       
  2548     if (layoutStruct->fullLayout
       
  2549         || (blockPosition + blockLength > layoutFrom && blockPosition <= layoutTo)
       
  2550         // force relayout if we cross a page boundary
       
  2551         || (layoutStruct->pageHeight != QFIXED_MAX && layoutStruct->absoluteY() + QFixed::fromReal(tl->boundingRect().height()) > layoutStruct->pageBottom)) {
       
  2552 
       
  2553         LDEBUG << " do layout";
       
  2554         QTextOption option = docPrivate->defaultTextOption;
       
  2555         option.setTextDirection(dir);
       
  2556         option.setTabs( blockFormat.tabPositions() );
       
  2557 
       
  2558         Qt::Alignment align = docPrivate->defaultTextOption.alignment();
       
  2559         if (blockFormat.hasProperty(QTextFormat::BlockAlignment))
       
  2560             align = blockFormat.alignment();
       
  2561         option.setAlignment(QStyle::visualAlignment(dir, align)); // for paragraph that are RTL, alignment is auto-reversed;
       
  2562 
       
  2563         if (blockFormat.nonBreakableLines() || document->pageSize().width() < 0) {
       
  2564             option.setWrapMode(QTextOption::ManualWrap);
       
  2565         }
       
  2566 
       
  2567         tl->setTextOption(option);
       
  2568 
       
  2569         const bool haveWordOrAnyWrapMode = (option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere);
       
  2570 
       
  2571 //         qDebug() << "    layouting block at" << bl.position();
       
  2572         const QFixed cy = layoutStruct->y;
       
  2573         const QFixed l = layoutStruct->x_left  + totalLeftMargin;
       
  2574         const QFixed r = layoutStruct->x_right - totalRightMargin;
       
  2575 
       
  2576         tl->beginLayout();
       
  2577         bool firstLine = true;
       
  2578         while (1) {
       
  2579             QTextLine line = tl->createLine();
       
  2580             if (!line.isValid())
       
  2581                 break;
       
  2582 
       
  2583             QFixed left, right;
       
  2584             floatMargins(layoutStruct->y, layoutStruct, &left, &right);
       
  2585             left = qMax(left, l);
       
  2586             right = qMin(right, r);
       
  2587             QFixed text_indent;
       
  2588             if (firstLine) {
       
  2589                 text_indent = QFixed::fromReal(blockFormat.textIndent());
       
  2590                 if (dir == Qt::LeftToRight)
       
  2591                     left += text_indent;
       
  2592                 else
       
  2593                     right -= text_indent;
       
  2594                 firstLine = false;
       
  2595             }
       
  2596 //         qDebug() << "layout line y=" << currentYPos << "left=" << left << "right=" <<right;
       
  2597 
       
  2598             if (fixedColumnWidth != -1)
       
  2599                 line.setNumColumns(fixedColumnWidth, (right - left).toReal());
       
  2600             else
       
  2601                 line.setLineWidth((right - left).toReal());
       
  2602 
       
  2603 //        qDebug() << "layoutBlock; layouting line with width" << right - left << "->textWidth" << line.textWidth();
       
  2604             floatMargins(layoutStruct->y, layoutStruct, &left, &right);
       
  2605             left = qMax(left, l);
       
  2606             right = qMin(right, r);
       
  2607             if (dir == Qt::LeftToRight)
       
  2608                 left += text_indent;
       
  2609             else
       
  2610                 right -= text_indent;
       
  2611 
       
  2612             if (fixedColumnWidth == -1 && QFixed::fromReal(line.naturalTextWidth()) > right-left) {
       
  2613                 // float has been added in the meantime, redo
       
  2614                 layoutStruct->pendingFloats.clear();
       
  2615 
       
  2616                 line.setLineWidth((right-left).toReal());
       
  2617                 if (QFixed::fromReal(line.naturalTextWidth()) > right-left) {
       
  2618                     if (haveWordOrAnyWrapMode) {
       
  2619                         option.setWrapMode(QTextOption::WrapAnywhere);
       
  2620                         tl->setTextOption(option);
       
  2621                     }
       
  2622 
       
  2623                     layoutStruct->pendingFloats.clear();
       
  2624                     // lines min width more than what we have
       
  2625                     layoutStruct->y = findY(layoutStruct->y, layoutStruct, QFixed::fromReal(line.naturalTextWidth()));
       
  2626                     floatMargins(layoutStruct->y, layoutStruct, &left, &right);
       
  2627                     left = qMax(left, l);
       
  2628                     right = qMin(right, r);
       
  2629                     if (dir == Qt::LeftToRight)
       
  2630                         left += text_indent;
       
  2631                     else
       
  2632                         right -= text_indent;
       
  2633                     line.setLineWidth(qMax<qreal>(line.naturalTextWidth(), (right-left).toReal()));
       
  2634 
       
  2635                     if (haveWordOrAnyWrapMode) {
       
  2636                         option.setWrapMode(QTextOption::WordWrap);
       
  2637                         tl->setTextOption(option);
       
  2638                     }
       
  2639                 }
       
  2640 
       
  2641             }
       
  2642 
       
  2643             QFixed lineHeight = QFixed::fromReal(line.height());
       
  2644             if (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineHeight > layoutStruct->pageBottom) {
       
  2645                 layoutStruct->newPage();
       
  2646 
       
  2647                 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
       
  2648                 left = qMax(left, l);
       
  2649                 right = qMin(right, r);
       
  2650                 if (dir == Qt::LeftToRight)
       
  2651                     left += text_indent;
       
  2652                 else
       
  2653                     right -= text_indent;
       
  2654             }
       
  2655 
       
  2656             line.setPosition(QPointF((left - layoutStruct->x_left).toReal(), (layoutStruct->y - cy).toReal()));
       
  2657             layoutStruct->y += lineHeight;
       
  2658             layoutStruct->contentsWidth
       
  2659                 = qMax<QFixed>(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + line.naturalTextWidth()) + totalRightMargin);
       
  2660 
       
  2661             // position floats
       
  2662             for (int i = 0; i < layoutStruct->pendingFloats.size(); ++i) {
       
  2663                 QTextFrame *f = layoutStruct->pendingFloats.at(i);
       
  2664                 positionFloat(f);
       
  2665             }
       
  2666             layoutStruct->pendingFloats.clear();
       
  2667         }
       
  2668         tl->endLayout();
       
  2669     } else {
       
  2670         const int cnt = tl->lineCount();
       
  2671         for (int i = 0; i < cnt; ++i) {
       
  2672             LDEBUG << "going to move text line" << i;
       
  2673             QTextLine line = tl->lineAt(i);
       
  2674             layoutStruct->contentsWidth
       
  2675                 = qMax(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + tl->lineAt(i).naturalTextWidth()) + totalRightMargin);
       
  2676             const QFixed lineHeight = QFixed::fromReal(line.height());
       
  2677             if (layoutStruct->pageHeight != QFIXED_MAX) {
       
  2678                 if (layoutStruct->absoluteY() + lineHeight > layoutStruct->pageBottom)
       
  2679                     layoutStruct->newPage();
       
  2680                 line.setPosition(QPointF(line.position().x(), layoutStruct->y.toReal() - tl->position().y()));
       
  2681             }
       
  2682             layoutStruct->y += lineHeight;
       
  2683         }
       
  2684         if (layoutStruct->updateRect.isValid()
       
  2685             && blockLength > 1) {
       
  2686             if (layoutFrom >= blockPosition + blockLength) {
       
  2687                 // if our height didn't change and the change in the document is
       
  2688                 // in one of the later paragraphs, then we don't need to repaint
       
  2689                 // this one
       
  2690                 layoutStruct->updateRect.setTop(qMax(layoutStruct->updateRect.top(), layoutStruct->y.toReal()));
       
  2691             } else if (layoutTo < blockPosition) {
       
  2692                 if (oldPosition == tl->position())
       
  2693                     // if the change in the document happened earlier in the document
       
  2694                     // and our position did /not/ change because none of the earlier paragraphs
       
  2695                     // or frames changed their height, then we don't need to repaint
       
  2696                     // this one
       
  2697                     layoutStruct->updateRect.setBottom(qMin(layoutStruct->updateRect.bottom(), tl->position().y()));
       
  2698                 else
       
  2699                     layoutStruct->updateRect.setBottom(qreal(INT_MAX)); // reset
       
  2700             }
       
  2701         }
       
  2702     }
       
  2703 
       
  2704     // ### doesn't take floats into account. would need to do it per line. but how to retrieve then? (Simon)
       
  2705     const QFixed margins = totalLeftMargin + totalRightMargin;
       
  2706     layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, QFixed::fromReal(tl->minimumWidth()) + margins);
       
  2707 
       
  2708     const QFixed maxW = QFixed::fromReal(tl->maximumWidth()) + margins;
       
  2709 
       
  2710     if (maxW > 0) {
       
  2711         if (layoutStruct->maximumWidth == QFIXED_MAX)
       
  2712             layoutStruct->maximumWidth = maxW;
       
  2713         else
       
  2714             layoutStruct->maximumWidth = qMax(layoutStruct->maximumWidth, maxW);
       
  2715     }
       
  2716 }
       
  2717 
       
  2718 void QTextDocumentLayoutPrivate::floatMargins(const QFixed &y, const QLayoutStruct *layoutStruct,
       
  2719                                               QFixed *left, QFixed *right) const
       
  2720 {
       
  2721 //     qDebug() << "floatMargins y=" << y;
       
  2722     *left = layoutStruct->x_left;
       
  2723     *right = layoutStruct->x_right;
       
  2724     QTextFrameData *lfd = data(layoutStruct->frame);
       
  2725     for (int i = 0; i < lfd->floats.size(); ++i) {
       
  2726         QTextFrameData *fd = data(lfd->floats.at(i));
       
  2727         if (!fd->layoutDirty) {
       
  2728             if (fd->position.y <= y && fd->position.y + fd->size.height > y) {
       
  2729 //                 qDebug() << "adjusting with float" << f << fd->position.x()<< fd->size.width();
       
  2730                 if (lfd->floats.at(i)->frameFormat().position() == QTextFrameFormat::FloatLeft)
       
  2731                     *left = qMax(*left, fd->position.x + fd->size.width);
       
  2732                 else
       
  2733                     *right = qMin(*right, fd->position.x);
       
  2734             }
       
  2735         }
       
  2736     }
       
  2737 //     qDebug() << "floatMargins: left="<<*left<<"right="<<*right<<"y="<<y;
       
  2738 }
       
  2739 
       
  2740 QFixed QTextDocumentLayoutPrivate::findY(QFixed yFrom, const QLayoutStruct *layoutStruct, QFixed requiredWidth) const
       
  2741 {
       
  2742     QFixed right, left;
       
  2743     requiredWidth = qMin(requiredWidth, layoutStruct->x_right - layoutStruct->x_left);
       
  2744 
       
  2745 //     qDebug() << "findY:" << yFrom;
       
  2746     while (1) {
       
  2747         floatMargins(yFrom, layoutStruct, &left, &right);
       
  2748 //         qDebug() << "    yFrom=" << yFrom<<"right=" << right << "left=" << left << "requiredWidth=" << requiredWidth;
       
  2749         if (right-left >= requiredWidth)
       
  2750             break;
       
  2751 
       
  2752         // move float down until we find enough space
       
  2753         QFixed newY = QFIXED_MAX;
       
  2754         QTextFrameData *lfd = data(layoutStruct->frame);
       
  2755         for (int i = 0; i < lfd->floats.size(); ++i) {
       
  2756             QTextFrameData *fd = data(lfd->floats.at(i));
       
  2757             if (!fd->layoutDirty) {
       
  2758                 if (fd->position.y <= yFrom && fd->position.y + fd->size.height > yFrom)
       
  2759                     newY = qMin(newY, fd->position.y + fd->size.height);
       
  2760             }
       
  2761         }
       
  2762         if (newY == QFIXED_MAX)
       
  2763             break;
       
  2764         yFrom = newY;
       
  2765     }
       
  2766     return yFrom;
       
  2767 }
       
  2768 
       
  2769 QTextDocumentLayout::QTextDocumentLayout(QTextDocument *doc)
       
  2770     : QAbstractTextDocumentLayout(*new QTextDocumentLayoutPrivate, doc)
       
  2771 {
       
  2772     registerHandler(QTextFormat::ImageObject, new QTextImageHandler(this));
       
  2773 }
       
  2774 
       
  2775 
       
  2776 void QTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
       
  2777 {
       
  2778     Q_D(QTextDocumentLayout);
       
  2779     QTextFrame *frame = d->document->rootFrame();
       
  2780     QTextFrameData *fd = data(frame);
       
  2781 
       
  2782     if(fd->sizeDirty)
       
  2783         return;
       
  2784 
       
  2785     if (context.clip.isValid()) {
       
  2786         d->ensureLayouted(QFixed::fromReal(context.clip.bottom()));
       
  2787     } else {
       
  2788         d->ensureLayoutFinished();
       
  2789     }
       
  2790 
       
  2791     QFixed width = fd->size.width;
       
  2792     if (d->document->pageSize().width() == 0 && d->viewportRect.isValid()) {
       
  2793         // we're in NoWrap mode, meaning the frame should expand to the viewport
       
  2794         // so that backgrounds are drawn correctly
       
  2795         fd->size.width = qMax(width, QFixed::fromReal(d->viewportRect.right()));
       
  2796     }
       
  2797 
       
  2798     // Make sure we conform to the root frames bounds when drawing.
       
  2799     d->clipRect = QRectF(fd->position.toPointF(), fd->size.toSizeF()).adjusted(fd->leftMargin.toReal(), 0, -fd->rightMargin.toReal(), 0);
       
  2800     d->drawFrame(QPointF(), painter, context, frame);
       
  2801     fd->size.width = width;
       
  2802 }
       
  2803 
       
  2804 void QTextDocumentLayout::setViewport(const QRectF &viewport)
       
  2805 {
       
  2806     Q_D(QTextDocumentLayout);
       
  2807     d->viewportRect = viewport;
       
  2808 }
       
  2809 
       
  2810 static void markFrames(QTextFrame *current, int from, int oldLength, int length)
       
  2811 {
       
  2812     int end = qMax(oldLength, length) + from;
       
  2813 
       
  2814     if (current->firstPosition() >= end || current->lastPosition() < from)
       
  2815         return;
       
  2816 
       
  2817     QTextFrameData *fd = data(current);
       
  2818     for (int i = 0; i < fd->floats.size(); ++i) {
       
  2819         QTextFrame *f = fd->floats[i];
       
  2820         if (!f) {
       
  2821             // float got removed in editing operation
       
  2822             fd->floats.removeAt(i);
       
  2823             --i;
       
  2824         }
       
  2825     }
       
  2826 
       
  2827     fd->layoutDirty = true;
       
  2828     fd->sizeDirty = true;
       
  2829 
       
  2830 //     qDebug("    marking frame (%d--%d) as dirty", current->firstPosition(), current->lastPosition());
       
  2831     QList<QTextFrame *> children = current->childFrames();
       
  2832     for (int i = 0; i < children.size(); ++i)
       
  2833         markFrames(children.at(i), from, oldLength, length);
       
  2834 }
       
  2835 
       
  2836 void QTextDocumentLayout::documentChanged(int from, int oldLength, int length)
       
  2837 {
       
  2838     Q_D(QTextDocumentLayout);
       
  2839 
       
  2840     QTextBlock blockIt = document()->findBlock(from);
       
  2841     QTextBlock endIt = document()->findBlock(qMax(0, from + length - 1));
       
  2842     if (endIt.isValid())
       
  2843         endIt = endIt.next();
       
  2844      for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
       
  2845          blockIt.clearLayout();
       
  2846 
       
  2847     if (d->docPrivate->pageSize.isNull())
       
  2848         return;
       
  2849 
       
  2850     QRectF updateRect;
       
  2851 
       
  2852     d->lazyLayoutStepSize = 1000;
       
  2853     d->sizeChangedTimer.stop();
       
  2854     d->insideDocumentChange = true;
       
  2855 
       
  2856     const int documentLength = d->docPrivate->length();
       
  2857     const bool fullLayout = (oldLength == 0 && length == documentLength);
       
  2858     const bool smallChange = documentLength > 0
       
  2859                              && (qMax(length, oldLength) * 100 / documentLength) < 5;
       
  2860 
       
  2861     // don't show incremental layout progress (avoid scroll bar flicker)
       
  2862     // if we see only a small change in the document and we're either starting
       
  2863     // a layout run or we're already in progress for that and we haven't seen
       
  2864     // any bigger change previously (showLayoutProgress already false)
       
  2865     if (smallChange
       
  2866         && (d->currentLazyLayoutPosition == -1 || d->showLayoutProgress == false))
       
  2867         d->showLayoutProgress = false;
       
  2868     else
       
  2869         d->showLayoutProgress = true;
       
  2870 
       
  2871     if (fullLayout) {
       
  2872         d->contentHasAlignment = false;
       
  2873         d->currentLazyLayoutPosition = 0;
       
  2874         d->checkPoints.clear();
       
  2875         d->layoutStep();
       
  2876     } else {
       
  2877         d->ensureLayoutedByPosition(from);
       
  2878         updateRect = doLayout(from, oldLength, length);
       
  2879     }
       
  2880 
       
  2881     if (!d->layoutTimer.isActive() && d->currentLazyLayoutPosition != -1)
       
  2882         d->layoutTimer.start(10, this);
       
  2883 
       
  2884     d->insideDocumentChange = false;
       
  2885 
       
  2886     if (d->showLayoutProgress) {
       
  2887         const QSizeF newSize = dynamicDocumentSize();
       
  2888         if (newSize != d->lastReportedSize) {
       
  2889             d->lastReportedSize = newSize;
       
  2890             emit documentSizeChanged(newSize);
       
  2891         }
       
  2892     }
       
  2893 
       
  2894     if (!updateRect.isValid()) {
       
  2895         // don't use the frame size, it might have shrunken
       
  2896         updateRect = QRectF(QPointF(0, 0), QSizeF(qreal(INT_MAX), qreal(INT_MAX)));
       
  2897     }
       
  2898 
       
  2899     emit update(updateRect);
       
  2900 }
       
  2901 
       
  2902 QRectF QTextDocumentLayout::doLayout(int from, int oldLength, int length)
       
  2903 {
       
  2904     Q_D(QTextDocumentLayout);
       
  2905 
       
  2906 //     qDebug("documentChange: from=%d, oldLength=%d, length=%d", from, oldLength, length);
       
  2907 
       
  2908     // mark all frames between f_start and f_end as dirty
       
  2909     markFrames(d->docPrivate->rootFrame(), from, oldLength, length);
       
  2910 
       
  2911     QRectF updateRect;
       
  2912 
       
  2913     QTextFrame *root = d->docPrivate->rootFrame();
       
  2914     if(data(root)->sizeDirty)
       
  2915         updateRect = d->layoutFrame(root, from, from + length);
       
  2916     data(root)->layoutDirty = false;
       
  2917 
       
  2918     if (d->currentLazyLayoutPosition == -1)
       
  2919         layoutFinished();
       
  2920     else if (d->showLayoutProgress)
       
  2921         d->sizeChangedTimer.start(0, this);
       
  2922 
       
  2923     return updateRect;
       
  2924 }
       
  2925 
       
  2926 int QTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
       
  2927 {
       
  2928     Q_D(const QTextDocumentLayout);
       
  2929     d->ensureLayouted(QFixed::fromReal(point.y()));
       
  2930     QTextFrame *f = d->docPrivate->rootFrame();
       
  2931     int position = 0;
       
  2932     QTextLayout *l = 0;
       
  2933     QFixedPoint pointf;
       
  2934     pointf.x = QFixed::fromReal(point.x());
       
  2935     pointf.y = QFixed::fromReal(point.y());
       
  2936     QTextDocumentLayoutPrivate::HitPoint p = d->hitTest(f, pointf, &position, &l, accuracy);
       
  2937     if (accuracy == Qt::ExactHit && p < QTextDocumentLayoutPrivate::PointExact)
       
  2938         return -1;
       
  2939 
       
  2940     // ensure we stay within document bounds
       
  2941     int lastPos = f->lastPosition();
       
  2942     if (l && !l->preeditAreaText().isEmpty())
       
  2943         lastPos += l->preeditAreaText().length();
       
  2944     if (position > lastPos)
       
  2945         position = lastPos;
       
  2946     else if (position < 0)
       
  2947         position = 0;
       
  2948 
       
  2949     return position;
       
  2950 }
       
  2951 
       
  2952 void QTextDocumentLayout::resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
       
  2953 {
       
  2954     Q_D(QTextDocumentLayout);
       
  2955     QTextCharFormat f = format.toCharFormat();
       
  2956     Q_ASSERT(f.isValid());
       
  2957     QTextObjectHandler handler = d->handlers.value(f.objectType());
       
  2958     if (!handler.component)
       
  2959         return;
       
  2960 
       
  2961     QSizeF intrinsic = handler.iface->intrinsicSize(d->document, posInDocument, format);
       
  2962 
       
  2963     QTextFrameFormat::Position pos = QTextFrameFormat::InFlow;
       
  2964     QTextFrame *frame = qobject_cast<QTextFrame *>(d->document->objectForFormat(f));
       
  2965     if (frame) {
       
  2966         pos = frame->frameFormat().position();
       
  2967         QTextFrameData *fd = data(frame);
       
  2968         fd->sizeDirty = false;
       
  2969         fd->size = QFixedSize::fromSizeF(intrinsic);
       
  2970         fd->minimumWidth = fd->maximumWidth = fd->size.width;
       
  2971     }
       
  2972 
       
  2973     QSizeF inlineSize = (pos == QTextFrameFormat::InFlow ? intrinsic : QSizeF(0, 0));
       
  2974     item.setWidth(inlineSize.width());
       
  2975     if (f.verticalAlignment() == QTextCharFormat::AlignMiddle) {
       
  2976         item.setDescent(inlineSize.height() / 2);
       
  2977         item.setAscent(inlineSize.height() / 2 - 1);
       
  2978     } else {
       
  2979         item.setDescent(0);
       
  2980         item.setAscent(inlineSize.height() - 1);
       
  2981     }
       
  2982 }
       
  2983 
       
  2984 void QTextDocumentLayout::positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
       
  2985 {
       
  2986     Q_D(QTextDocumentLayout);
       
  2987     Q_UNUSED(posInDocument);
       
  2988     if (item.width() != 0)
       
  2989         // inline
       
  2990         return;
       
  2991 
       
  2992     QTextCharFormat f = format.toCharFormat();
       
  2993     Q_ASSERT(f.isValid());
       
  2994     QTextObjectHandler handler = d->handlers.value(f.objectType());
       
  2995     if (!handler.component)
       
  2996         return;
       
  2997 
       
  2998     QTextFrame *frame = qobject_cast<QTextFrame *>(d->document->objectForFormat(f));
       
  2999     if (!frame)
       
  3000         return;
       
  3001 
       
  3002     QTextBlock b = d->document->findBlock(frame->firstPosition());
       
  3003     QTextLine line;
       
  3004     if (b.position() <= frame->firstPosition() && b.position() + b.length() > frame->lastPosition())
       
  3005         line = b.layout()->lineAt(b.layout()->lineCount()-1);
       
  3006 //     qDebug() << "layoutObject: line.isValid" << line.isValid() << b.position() << b.length() <<
       
  3007 //         frame->firstPosition() << frame->lastPosition();
       
  3008     d->positionFloat(frame, line.isValid() ? &line : 0);
       
  3009 }
       
  3010 
       
  3011 void QTextDocumentLayout::drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item,
       
  3012                                            int posInDocument, const QTextFormat &format)
       
  3013 {
       
  3014     Q_D(QTextDocumentLayout);
       
  3015     QTextCharFormat f = format.toCharFormat();
       
  3016     Q_ASSERT(f.isValid());
       
  3017     QTextFrame *frame = qobject_cast<QTextFrame *>(d->document->objectForFormat(f));
       
  3018     if (frame && frame->frameFormat().position() != QTextFrameFormat::InFlow)
       
  3019         return; // don't draw floating frames from inline objects here but in drawFlow instead
       
  3020 
       
  3021 //    qDebug() << "drawObject at" << r;
       
  3022     QAbstractTextDocumentLayout::drawInlineObject(p, rect, item, posInDocument, format);
       
  3023 }
       
  3024 
       
  3025 int QTextDocumentLayout::dynamicPageCount() const
       
  3026 {
       
  3027     Q_D(const QTextDocumentLayout);
       
  3028     const QSizeF pgSize = d->document->pageSize();
       
  3029     if (pgSize.height() < 0)
       
  3030         return 1;
       
  3031     return qCeil(dynamicDocumentSize().height() / pgSize.height());
       
  3032 }
       
  3033 
       
  3034 QSizeF QTextDocumentLayout::dynamicDocumentSize() const
       
  3035 {
       
  3036     Q_D(const QTextDocumentLayout);
       
  3037     return data(d->docPrivate->rootFrame())->size.toSizeF();
       
  3038 }
       
  3039 
       
  3040 int QTextDocumentLayout::pageCount() const
       
  3041 {
       
  3042     Q_D(const QTextDocumentLayout);
       
  3043     d->ensureLayoutFinished();
       
  3044     return dynamicPageCount();
       
  3045 }
       
  3046 
       
  3047 QSizeF QTextDocumentLayout::documentSize() const
       
  3048 {
       
  3049     Q_D(const QTextDocumentLayout);
       
  3050     d->ensureLayoutFinished();
       
  3051     return dynamicDocumentSize();
       
  3052 }
       
  3053 
       
  3054 void QTextDocumentLayoutPrivate::ensureLayouted(QFixed y) const
       
  3055 {
       
  3056     Q_Q(const QTextDocumentLayout);
       
  3057     if (currentLazyLayoutPosition == -1)
       
  3058         return;
       
  3059     const QSizeF oldSize = q->dynamicDocumentSize();
       
  3060 
       
  3061     if (checkPoints.isEmpty())
       
  3062         layoutStep();
       
  3063 
       
  3064     while (currentLazyLayoutPosition != -1
       
  3065            && checkPoints.last().y < y)
       
  3066         layoutStep();
       
  3067 }
       
  3068 
       
  3069 void QTextDocumentLayoutPrivate::ensureLayoutedByPosition(int position) const
       
  3070 {
       
  3071     if (currentLazyLayoutPosition == -1)
       
  3072         return;
       
  3073     if (position < currentLazyLayoutPosition)
       
  3074         return;
       
  3075     while (currentLazyLayoutPosition != -1
       
  3076            && currentLazyLayoutPosition < position) {
       
  3077         const_cast<QTextDocumentLayout *>(q_func())->doLayout(currentLazyLayoutPosition, 0, INT_MAX - currentLazyLayoutPosition);
       
  3078     }
       
  3079 }
       
  3080 
       
  3081 void QTextDocumentLayoutPrivate::layoutStep() const
       
  3082 {
       
  3083     ensureLayoutedByPosition(currentLazyLayoutPosition + lazyLayoutStepSize);
       
  3084     lazyLayoutStepSize = qMin(200000, lazyLayoutStepSize * 2);
       
  3085 }
       
  3086 
       
  3087 void QTextDocumentLayout::setCursorWidth(int width)
       
  3088 {
       
  3089     Q_D(QTextDocumentLayout);
       
  3090     d->cursorWidth = width;
       
  3091 }
       
  3092 
       
  3093 int QTextDocumentLayout::cursorWidth() const
       
  3094 {
       
  3095     Q_D(const QTextDocumentLayout);
       
  3096     return d->cursorWidth;
       
  3097 }
       
  3098 
       
  3099 void QTextDocumentLayout::setFixedColumnWidth(int width)
       
  3100 {
       
  3101     Q_D(QTextDocumentLayout);
       
  3102     d->fixedColumnWidth = width;
       
  3103 }
       
  3104 
       
  3105 QRectF QTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
       
  3106 {
       
  3107     Q_D(const QTextDocumentLayout);
       
  3108     if (d->docPrivate->pageSize.isNull())
       
  3109         return QRectF();
       
  3110     d->ensureLayoutFinished();
       
  3111     return d->frameBoundingRectInternal(frame);
       
  3112 }
       
  3113 
       
  3114 QRectF QTextDocumentLayoutPrivate::frameBoundingRectInternal(QTextFrame *frame) const
       
  3115 {
       
  3116     QPointF pos;
       
  3117     const int framePos = frame->firstPosition();
       
  3118     QTextFrame *f = frame;
       
  3119     while (f) {
       
  3120         QTextFrameData *fd = data(f);
       
  3121         pos += fd->position.toPointF();
       
  3122 
       
  3123         if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
       
  3124             QTextTableCell cell = table->cellAt(framePos);
       
  3125             if (cell.isValid())
       
  3126                 pos += static_cast<QTextTableData *>(fd)->cellPosition(cell).toPointF();
       
  3127         }
       
  3128 
       
  3129         f = f->parentFrame();
       
  3130     }
       
  3131     return QRectF(pos, data(frame)->size.toSizeF());
       
  3132 }
       
  3133 
       
  3134 QRectF QTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
       
  3135 {
       
  3136     Q_D(const QTextDocumentLayout);
       
  3137     if (d->docPrivate->pageSize.isNull())
       
  3138         return QRectF();
       
  3139     d->ensureLayoutedByPosition(block.position() + block.length());
       
  3140     QTextFrame *frame = d->document->frameAt(block.position());
       
  3141     QPointF offset;
       
  3142     const int blockPos = block.position();
       
  3143 
       
  3144     while (frame) {
       
  3145         QTextFrameData *fd = data(frame);
       
  3146         offset += fd->position.toPointF();
       
  3147 
       
  3148         if (QTextTable *table = qobject_cast<QTextTable *>(frame)) {
       
  3149             QTextTableCell cell = table->cellAt(blockPos);
       
  3150             if (cell.isValid())
       
  3151                 offset += static_cast<QTextTableData *>(fd)->cellPosition(cell).toPointF();
       
  3152         }
       
  3153 
       
  3154         frame = frame->parentFrame();
       
  3155     }
       
  3156 
       
  3157     const QTextLayout *layout = block.layout();
       
  3158     QRectF rect = layout->boundingRect();
       
  3159     rect.moveTopLeft(layout->position() + offset);
       
  3160     return rect;
       
  3161 }
       
  3162 
       
  3163 int QTextDocumentLayout::layoutStatus() const
       
  3164 {
       
  3165     Q_D(const QTextDocumentLayout);
       
  3166     int pos = d->currentLazyLayoutPosition;
       
  3167     if (pos == -1)
       
  3168         return 100;
       
  3169     return pos * 100 / d->document->docHandle()->length();
       
  3170 }
       
  3171 
       
  3172 void QTextDocumentLayout::timerEvent(QTimerEvent *e)
       
  3173 {
       
  3174     Q_D(QTextDocumentLayout);
       
  3175     if (e->timerId() == d->layoutTimer.timerId()) {
       
  3176         if (d->currentLazyLayoutPosition != -1)
       
  3177             d->layoutStep();
       
  3178     } else if (e->timerId() == d->sizeChangedTimer.timerId()) {
       
  3179         d->lastReportedSize = dynamicDocumentSize();
       
  3180         emit documentSizeChanged(d->lastReportedSize);
       
  3181         d->sizeChangedTimer.stop();
       
  3182 
       
  3183         if (d->currentLazyLayoutPosition == -1) {
       
  3184             const int newCount = dynamicPageCount();
       
  3185             if (newCount != d->lastPageCount) {
       
  3186                 d->lastPageCount = newCount;
       
  3187                 emit pageCountChanged(newCount);
       
  3188             }
       
  3189         }
       
  3190     } else {
       
  3191         QAbstractTextDocumentLayout::timerEvent(e);
       
  3192     }
       
  3193 }
       
  3194 
       
  3195 void QTextDocumentLayout::layoutFinished()
       
  3196 {
       
  3197     Q_D(QTextDocumentLayout);
       
  3198     d->layoutTimer.stop();
       
  3199     if (!d->insideDocumentChange)
       
  3200         d->sizeChangedTimer.start(0, this);
       
  3201     // reset
       
  3202     d->showLayoutProgress = true;
       
  3203 }
       
  3204 
       
  3205 void QTextDocumentLayout::ensureLayouted(qreal y)
       
  3206 {
       
  3207     d_func()->ensureLayouted(QFixed::fromReal(y));
       
  3208 }
       
  3209 
       
  3210 qreal QTextDocumentLayout::idealWidth() const
       
  3211 {
       
  3212     Q_D(const QTextDocumentLayout);
       
  3213     d->ensureLayoutFinished();
       
  3214     return d->idealWidth;
       
  3215 }
       
  3216 
       
  3217 bool QTextDocumentLayout::contentHasAlignment() const
       
  3218 {
       
  3219     Q_D(const QTextDocumentLayout);
       
  3220     return d->contentHasAlignment;
       
  3221 }
       
  3222 
       
  3223 qreal QTextDocumentLayoutPrivate::scaleToDevice(qreal value) const
       
  3224 {
       
  3225     if (!paintDevice)
       
  3226         return value;
       
  3227     return value * paintDevice->logicalDpiY() / qreal(qt_defaultDpi());
       
  3228 }
       
  3229 
       
  3230 QFixed QTextDocumentLayoutPrivate::scaleToDevice(QFixed value) const
       
  3231 {
       
  3232     if (!paintDevice)
       
  3233         return value;
       
  3234     return value * QFixed(paintDevice->logicalDpiY()) / QFixed(qt_defaultDpi());
       
  3235 }
       
  3236 
       
  3237 QT_END_NAMESPACE
       
  3238 
       
  3239 #include "moc_qtextdocumentlayout_p.cpp"