~gabriel1984sibiu/minitube/qt5.6

« back to all changes in this revision

Viewing changes to src/gui/text/qtextdocumentlayout.cpp

  • Committer: Grevutiu Gabriel
  • Date: 2017-06-13 08:43:17 UTC
  • Revision ID: gabriel1984sibiu@gmail.com-20170613084317-ek0zqe0u9g3ocvi8
OriginalĀ upstreamĀ code

Show diffs side-by-side

added added

removed removed

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