src/gui/text/qtextdocumentlayout.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qtextdocumentlayout_p.h"
00025 #include "qtextdocument_p.h"
00026 #include "qtextimagehandler_p.h"
00027 #include "qtexttable.h"
00028 #include "qtextlist.h"
00029 
00030 #include "qabstracttextdocumentlayout_p.h"
00031 
00032 #include <qpainter.h>
00033 #include <qrect.h>
00034 #include <qpalette.h>
00035 #include <qdebug.h>
00036 #include <qvarlengtharray.h>
00037 #include <limits.h>
00038 #include <qstyle.h>
00039 #include <qbasictimer.h>
00040 
00041 // #define LAYOUT_DEBUG
00042 
00043 #ifdef LAYOUT_DEBUG
00044 #define LDEBUG qDebug()
00045 #define INC_INDENT debug_indent += "  "
00046 #define DEC_INDENT debug_indent = debug_indent.left(debug_indent.length()-2)
00047 #else
00048 #define LDEBUG if(0) qDebug()
00049 #define INC_INDENT do {} while(0)
00050 #define DEC_INDENT do {} while(0)
00051 #endif
00052 
00053 // ################ should probably add frameFormatChange notification!
00054 
00055 struct QLayoutStruct;
00056 
00057 class QTextFrameData : public QTextFrameLayoutData
00058 {
00059 public:
00060     QTextFrameData();
00061 
00062     // relative to parent frame
00063     QPointF position;
00064     QSizeF size;
00065 
00066     // contents starts at (margin+border/margin+border)
00067     qreal margin;
00068     qreal border;
00069     qreal padding;
00070     // contents width includes padding (as we need to treat this on a per cell basis for tables)
00071     qreal contentsWidth;
00072     qreal contentsHeight;
00073 
00074     qreal minimumWidth;
00075     qreal maximumWidth;
00076 
00077     QTextFrameFormat::Position flow_position;
00078 
00079     QLayoutStruct *currentLayoutStruct;
00080 
00081     bool sizeDirty;
00082     bool layoutDirty;
00083 
00084     QList<QPointer<QTextFrame> > floats;
00085 };
00086 
00087 QTextFrameData::QTextFrameData()
00088     : margin(0), border(0), padding(0), contentsWidth(0), contentsHeight(0),
00089       minimumWidth(0), maximumWidth(INT_MAX), currentLayoutStruct(0),
00090       sizeDirty(true), layoutDirty(true)
00091 {
00092 }
00093 
00094 struct QLayoutStruct {
00095     QLayoutStruct() : contentsWidth(0), minimumWidth(0), maximumWidth(INT_MAX),
00096                       fullLayout(false), pageHeight(0.0),
00097                       pageBottom(0.0), pageMargin(0.0)
00098     {}
00099     QTextFrame *frame;
00100     qreal x_left;
00101     qreal x_right;
00102     qreal y;
00103     qreal contentsWidth;
00104     qreal minimumWidth;
00105     qreal maximumWidth;
00106     bool fullLayout;
00107     QList<QTextFrame *> pendingFloats;
00108     qreal pageHeight;
00109     qreal pageBottom;
00110     qreal pageMargin;
00111     QRectF updateRect;
00112 
00113     inline void newPage()
00114     { if (pageHeight == INT_MAX) return; pageBottom += pageHeight; y = pageBottom - pageHeight + 2 * pageMargin; }
00115 };
00116 
00117 class QTextTableData : public QTextFrameData
00118 {
00119 public:
00120     inline QTextTableData() : cellSpacing(0), cellPadding(0) {}
00121     qreal cellSpacing, cellPadding;
00122     QVector<qreal> minWidths;
00123     QVector<qreal> maxWidths;
00124     QVector<qreal> widths;
00125     QVector<qreal> heights;
00126     QVector<qreal> columnPositions;
00127     QVector<qreal> rowPositions;
00128     // rows that appear at the top of a page after a page break
00129     QVector<int> rowsAfterPageBreak;
00130     QVector<qreal> rowPositionsWithoutPageBreak;
00131 
00132     inline qreal cellWidth(int column, int colspan) const
00133     { return columnPositions.at(column + colspan - 1) + widths.at(column + colspan - 1)
00134              - columnPositions.at(column) - 2 * cellPadding; }
00135 
00136     inline void calcRowPosition(int row)
00137     {
00138         if (row > 0)
00139             rowPositions[row] = rowPositions.at(row - 1) + heights.at(row - 1) + border + cellSpacing + border;
00140     }
00141 
00142     QRectF cellRect(const QTextTableCell &cell) const;
00143 
00144     inline QPointF cellPosition(int row, int col) const
00145     { return QPointF(columnPositions.at(col) + cellPadding, rowPositions.at(row) + cellPadding); }
00146     inline QPointF cellPosition(const QTextTableCell &cell) const
00147     { return cellPosition(cell.row(), cell.column()); }
00148 
00149     void updateTableSize();
00150 };
00151 
00152 static QTextFrameData *createData(QTextFrame *f)
00153 {
00154     QTextFrameData *data;
00155     if (qobject_cast<QTextTable *>(f))
00156         data = new QTextTableData;
00157     else
00158         data = new QTextFrameData;
00159     f->setLayoutData(data);
00160     return data;
00161 }
00162 
00163 static inline QTextFrameData *data(QTextFrame *f)
00164 {
00165     QTextFrameData *data = static_cast<QTextFrameData *>(f->layoutData());
00166     if (!data)
00167         data = createData(f);
00168     return data;
00169 }
00170 
00171 void QTextTableData::updateTableSize()
00172 {
00173     const qreal effectiveMargin = this->margin + border + padding;
00174     qreal height = contentsHeight == -1
00175                    ? rowPositions.last() + heights.last() + padding + border + cellSpacing + effectiveMargin
00176                    : contentsHeight + 2*effectiveMargin;
00177     size = QSizeF(contentsWidth + 2*effectiveMargin, height);
00178 }
00179 
00180 QRectF QTextTableData::cellRect(const QTextTableCell &cell) const
00181 {
00182     const int row = cell.row();
00183     const int rowSpan = cell.rowSpan();
00184     const int column = cell.column();
00185     const int colSpan = cell.columnSpan();
00186 
00187     return QRectF(columnPositions.at(column),
00188                   rowPositions.at(row),
00189                   columnPositions.at(column + colSpan - 1) + widths.at(column + colSpan - 1) - columnPositions.at(column),
00190                   rowPositions.at(row + rowSpan - 1) + heights.at(row + rowSpan - 1) - rowPositions.at(row));
00191 }
00192 
00193 static inline bool isEmptyBlockBeforeTable(const QTextBlock &block, const QTextFrame::Iterator &nextIt)
00194 {
00195     return !nextIt.atEnd()
00196            && qobject_cast<QTextTable *>(nextIt.currentFrame())
00197            && block.isValid()
00198            && block.length() == 1
00199            && !block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)
00200            && nextIt.currentFrame()->firstPosition() == block.position() + 1
00201            ;
00202 }
00203 
00204 static inline bool isEmptyBlockBeforeTable(QTextFrame::Iterator it)
00205 {
00206     QTextFrame::Iterator next = it; ++next;
00207     return it.currentFrame() == 0
00208            && isEmptyBlockBeforeTable(it.currentBlock(), next);
00209 }
00210 
00211 static inline bool isEmptyBlockAfterTable(const QTextBlock &block, const QTextFrame *lastFrame)
00212 {
00213     return qobject_cast<const QTextTable *>(lastFrame)
00214            && block.isValid()
00215            && block.length() == 1
00216            && lastFrame->lastPosition() == block.position() - 1
00217            ;
00218 }
00219 
00220 /*
00221 
00222 Optimisation strategies:
00223 
00224 HTML layout:
00225 
00226 * Distinguish between normal and special flow. For normal flow the condition:
00227   y1 > y2 holds for all blocks with b1.key() > b2.key().
00228 * Special flow is: floats, table cells
00229 
00230 * Normal flow within table cells. Tables (not cells) are part of the normal flow.
00231 
00232 
00233 * If blocks grows/shrinks in height and extends over whole page width at the end, move following blocks.
00234 * If height doesn't change, no need to do anything
00235 
00236 Table cells:
00237 
00238 * If minWidth of cell changes, recalculate table width, relayout if needed.
00239 * What about maxWidth when doing auto layout?
00240 
00241 Floats:
00242 * need fixed or proportional width, otherwise don't float!
00243 * On width/height change relayout surrounding paragraphs.
00244 
00245 Document width change:
00246 * full relayout needed
00247 
00248 
00249 Float handling:
00250 
00251 * Floats are specified by a special format object.
00252 * currently only floating images are implemented.
00253 
00254 */
00255 
00256 /*
00257 
00258    On the table layouting:
00259 
00260    +---[ table border ]-------------------------
00261    |      [ cell spacing ]
00262    |  +------[ cell border ]-----+  +--------
00263    |  |                          |  |
00264    |  |
00265    |  |
00266    |  |
00267    |
00268 
00269    rowPositions[i] and columnPositions[i] point at the cell content
00270    position. So for example the left border is drawn at
00271    x = columnPositions[i] - fd->border and similar for y.
00272 
00273 */
00274 
00275 enum {
00276     TextIndentValue = 40
00277 };
00278 
00279 struct QCheckPoint
00280 {
00281     qreal y;
00282     int positionInFrame;
00283     qreal minimumWidth;
00284     qreal maximumWidth;
00285     qreal contentsWidth;
00286 };
00287 Q_DECLARE_TYPEINFO(QCheckPoint, Q_PRIMITIVE_TYPE);
00288 
00289 static bool operator<(const QCheckPoint &checkPoint, qreal y)
00290 {
00291     return checkPoint.y < y;
00292 }
00293 
00294 static bool operator<(const QCheckPoint &checkPoint, int pos)
00295 {
00296     return checkPoint.positionInFrame < pos;
00297 }
00298 
00299 static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, QRectF gradientRect = QRectF())
00300 {
00301     if (brush.style() >= Qt::LinearGradientPattern && brush.style() <= Qt::ConicalGradientPattern) {
00302         if (gradientRect.isNull())
00303             gradientRect = rect;
00304         QMatrix m;
00305         m.translate(gradientRect.left(), gradientRect.top());
00306         m.scale(gradientRect.width(), gradientRect.height());
00307         brush.setMatrix(m);
00308     }
00309     p->fillRect(rect, brush);
00310 }
00311 
00312 class QTextDocumentLayoutPrivate : public QAbstractTextDocumentLayoutPrivate
00313 {
00314     Q_DECLARE_PUBLIC(QTextDocumentLayout)
00315 public:
00316     QTextDocumentLayoutPrivate();
00317 
00318     int blockTextFlags;
00319     QTextOption::WrapMode wordWrapMode;
00320 #ifdef LAYOUT_DEBUG
00321     mutable QString debug_indent;
00322 #endif
00323 
00324     int fixedColumnWidth;
00325     double tabStopWidth;
00326     int cursorWidth;
00327 
00328     mutable int currentLazyLayoutPosition;
00329     mutable int lazyLayoutStepSize;
00330     QBasicTimer layoutTimer;
00331     mutable QBasicTimer sizeChangedTimer;
00332     uint showLayoutProgress : 1;
00333     uint insideDocumentChange : 1;
00334 
00335     int lastPageCount;
00336     qreal idealWidth;
00337 
00338     qreal indent(QTextBlock bl) const;
00339 
00340     void drawFrame(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
00341                    QTextFrame *f) const;
00342     void drawFlow(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
00343                   QTextFrame::Iterator it, QTextBlock *cursorBlockNeedingRepaint) const;
00344     void drawBlock(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
00345                    QTextBlock bl) const;
00346     void drawListItem(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
00347                       QTextBlock bl, const QTextCharFormat *selectionFormat) const;
00348     void drawTableCell(const QRectF &cellRect, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &cell_context,
00349                        QTextTable *table, QTextTableData *td, int r, int c,
00350                        QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset) const;
00351 
00352     enum HitPoint {
00353         PointBefore,
00354         PointAfter,
00355         PointInside,
00356         PointExact
00357     };
00358     HitPoint hitTest(QTextFrame *frame, const QPointF &point, int *position, QTextLayout **l) const;
00359     HitPoint hitTest(QTextFrame::Iterator it, HitPoint hit, const QPointF &p,
00360                      int *position, QTextLayout **l) const;
00361     HitPoint hitTest(QTextTable *table, const QPointF &point, int *position, QTextLayout **l) const;
00362     HitPoint hitTest(QTextBlock bl, const QPointF &point, int *position, QTextLayout **l) const;
00363 
00364     QLayoutStruct layoutCell(QTextTable *t, const QTextTableCell &cell, qreal width,
00365                             int layoutFrom, int layoutTo);
00366     void setCellPosition(QTextTable *t, const QTextTableCell &cell, const QPointF &pos);
00367     QRectF layoutTable(QTextTable *t, int layoutFrom, int layoutTo);
00368 
00369     void positionFloat(QTextFrame *frame, QTextLine *currentLine = 0);
00370 
00371     // calls the next one
00372     QRectF layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo);
00373     QRectF layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, qreal frameWidth, qreal frameHeight);
00374 
00375     void layoutBlock(const QTextBlock &bl, QLayoutStruct *layoutStruct, int layoutFrom, int layoutTo,
00376                      const QTextBlock &previousBlock);
00377     void layoutFlow(QTextFrame::Iterator it, QLayoutStruct *layoutStruct, int layoutFrom, int layoutTo);
00378     void pageBreakInsideTable(QTextTable *table, QLayoutStruct *layoutStruct);
00379 
00380 
00381     void floatMargins(qreal y, const QLayoutStruct *layoutStruct, qreal *left, qreal *right) const;
00382     qreal findY(qreal yFrom, const QLayoutStruct *layoutStruct, qreal requiredWidth) const;
00383 
00384     QVector<QCheckPoint> checkPoints;
00385 
00386     QTextFrame::Iterator frameIteratorForYPosition(qreal y) const;
00387     QTextFrame::Iterator frameIteratorForTextPosition(int position) const;
00388 
00389     void ensureLayouted(qreal y) const;
00390     void ensureLayoutedByPosition(int position) const;
00391     inline void ensureLayoutFinished() const
00392     { ensureLayoutedByPosition(INT_MAX); }
00393     void layoutStep() const;
00394 };
00395 
00396 QTextDocumentLayoutPrivate::QTextDocumentLayoutPrivate()
00397     : blockTextFlags(0), wordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere),
00398       fixedColumnWidth(-1),
00399       tabStopWidth(80), // same default as in qtextengine.cpp
00400       cursorWidth(1),
00401       currentLazyLayoutPosition(-1),
00402       lazyLayoutStepSize(1000),
00403       lastPageCount(-1)
00404 {
00405     showLayoutProgress = true;
00406     insideDocumentChange = false;
00407     idealWidth = 0;
00408 }
00409 
00410 QTextFrame::Iterator QTextDocumentLayoutPrivate::frameIteratorForYPosition(qreal y) const
00411 {
00412     Q_Q(const QTextDocumentLayout);
00413 
00414     const QTextDocumentPrivate *doc = q->document()->docHandle();
00415     QTextFrame *rootFrame = doc->rootFrame();
00416 
00417     if (checkPoints.isEmpty()
00418         || y < 0 || y > data(rootFrame)->size.height())
00419         return rootFrame->begin();
00420 
00421     QVector<QCheckPoint>::ConstIterator checkPoint = qLowerBound(checkPoints.begin(), checkPoints.end(), y);
00422     if (checkPoint == checkPoints.end())
00423         return rootFrame->begin();
00424 
00425     if (checkPoint != checkPoints.begin())
00426         --checkPoint;
00427 
00428     const int position = rootFrame->firstPosition() + checkPoint->positionInFrame;
00429     return frameIteratorForTextPosition(position);
00430 }
00431 
00432 QTextFrame::Iterator QTextDocumentLayoutPrivate::frameIteratorForTextPosition(int position) const
00433 {
00434     Q_Q(const QTextDocumentLayout);
00435     const QTextDocumentPrivate *doc = q->document()->docHandle();
00436     QTextFrame *rootFrame = doc->rootFrame();
00437 
00438     const QTextDocumentPrivate::BlockMap &map = doc->blockMap();
00439     const int begin = map.findNode(rootFrame->firstPosition());
00440     const int end = map.findNode(rootFrame->lastPosition()+1);
00441 
00442     const int block = map.findNode(position);
00443     const int blockPos = map.position(block);
00444 
00445     QTextFrame::iterator it(rootFrame, block, begin, end);
00446 
00447     QTextFrame *containingFrame = doc->frameAt(blockPos);
00448     if (containingFrame != rootFrame) {
00449         while (containingFrame->parentFrame() != rootFrame) {
00450             containingFrame = containingFrame->parentFrame();
00451             Q_ASSERT(containingFrame);
00452         }
00453 
00454         it.cf = containingFrame;
00455         it.cb = 0;
00456     }
00457 
00458     return it;
00459 }
00460 
00461 QTextDocumentLayoutPrivate::HitPoint
00462 QTextDocumentLayoutPrivate::hitTest(QTextFrame *frame, const QPointF &point, int *position, QTextLayout **l) const
00463 {
00464     Q_Q(const QTextDocumentLayout);
00465     QTextFrameData *fd = data(frame);
00466     // #########
00467     if (fd->layoutDirty)
00468         return PointAfter;
00469     Q_ASSERT(!fd->layoutDirty);
00470     Q_ASSERT(!fd->sizeDirty);
00471     const QPointF relativePoint = point - fd->position;
00472 
00473     QTextFrame *rootFrame = q->document()->rootFrame();
00474 
00475 //     LDEBUG << "checking frame" << frame->firstPosition() << "point=" << point
00476 //            << "position" << fd->position << "size" << fd->size;
00477     if (frame != rootFrame) {
00478         if (relativePoint.y() < 0 || relativePoint.x() < 0) {
00479             *position = frame->firstPosition() - 1;
00480 //             LDEBUG << "before pos=" << *position;
00481             return PointBefore;
00482         } else if (relativePoint.y() > fd->size.height() || relativePoint.x() > fd->size.width()) {
00483             *position = frame->lastPosition() + 1;
00484 //             LDEBUG << "after pos=" << *position;
00485             return PointAfter;
00486         }
00487     }
00488 
00489     if (QTextTable *table = qobject_cast<QTextTable *>(frame))
00490         return hitTest(table, relativePoint, position, l);
00491 
00492     QTextFrame::Iterator it = frame->begin();
00493 
00494     if (frame == rootFrame) {
00495         it = frameIteratorForYPosition(relativePoint.y());
00496 
00497         Q_ASSERT(it.parentFrame() == frame);
00498     }
00499 
00500     if (it.currentFrame())
00501         *position = it.currentFrame()->firstPosition();
00502     else
00503         *position = it.currentBlock().position();
00504 
00505     return hitTest(it, PointBefore, relativePoint, position, l);
00506 }
00507 
00508 QTextDocumentLayoutPrivate::HitPoint
00509 QTextDocumentLayoutPrivate::hitTest(QTextFrame::Iterator it, HitPoint hit, const QPointF &p,
00510                                     int *position, QTextLayout **l) const
00511 {
00512     INC_INDENT;
00513 
00514     for (; !it.atEnd(); ++it) {
00515         QTextFrame *c = it.currentFrame();
00516         HitPoint hp;
00517         int pos = -1;
00518         if (c) {
00519             hp = hitTest(c, p, &pos, l);
00520         } else {
00521             hp = hitTest(it.currentBlock(), p, &pos, l);
00522         }
00523         if (hp >= PointInside) {
00524             if (isEmptyBlockBeforeTable(it))
00525                 continue;
00526             hit = hp;
00527             *position = pos;
00528             break;
00529         }
00530         if (hp == PointBefore && pos < *position) {
00531             *position = pos;
00532             hit = hp;
00533         } else if (hp == PointAfter && pos > *position) {
00534             *position = pos;
00535             hit = hp;
00536         }
00537     }
00538 
00539     DEC_INDENT;
00540 //     LDEBUG << "inside=" << hit << " pos=" << *position;
00541     return hit;
00542 }
00543 
00544 QTextDocumentLayoutPrivate::HitPoint
00545 QTextDocumentLayoutPrivate::hitTest(QTextTable *table, const QPointF &point,
00546                                     int *position, QTextLayout **l) const
00547 {
00548     QTextTableData *td = static_cast<QTextTableData *>(data(table));
00549 
00550     QVector<qreal>::ConstIterator rowIt = qLowerBound(td->rowPositions.constBegin(), td->rowPositions.constEnd(), point.y());
00551     if (rowIt == td->rowPositions.constEnd()) {
00552         rowIt = td->rowPositions.constEnd() - 1;
00553     } else if (rowIt != td->rowPositions.constBegin()) {
00554         --rowIt;
00555     }
00556 
00557     QVector<qreal>::ConstIterator colIt = qLowerBound(td->columnPositions.constBegin(), td->columnPositions.constEnd(), point.x());
00558     if (colIt == td->columnPositions.constEnd()) {
00559         colIt = td->columnPositions.constEnd() - 1;
00560     } else if (colIt != td->columnPositions.constBegin()) {
00561         --colIt;
00562     }
00563 
00564     QTextTableCell cell = table->cellAt(rowIt - td->rowPositions.constBegin(),
00565                                         colIt - td->columnPositions.constBegin());
00566     if (!cell.isValid())
00567         return PointBefore;
00568 
00569     *position = cell.firstPosition();
00570 
00571     HitPoint hp = hitTest(cell.begin(), PointInside, point - td->cellPosition(cell), position, l);
00572 
00573     if (hp == PointExact)
00574         return hp;
00575     if (hp == PointAfter)
00576         *position = cell.lastPosition();
00577     return PointInside;
00578 }
00579 
00580 QTextDocumentLayoutPrivate::HitPoint
00581 QTextDocumentLayoutPrivate::hitTest(QTextBlock bl, const QPointF &point, int *position, QTextLayout **l) const
00582 {
00583     QTextLayout *tl = bl.layout();
00584     QRectF textrect = tl->boundingRect();
00585     textrect.translate(tl->position());
00586 //     LDEBUG << "    checking block" << bl.position() << "point=" << point
00587 //            << "    tlrect" << textrect;
00588     *position = bl.position();
00589     if (point.y() < textrect.top()) {
00590 //             LDEBUG << "    before pos=" << *position;
00591         return PointBefore;
00592     } else if (point.y() > textrect.bottom()) {
00593         *position += bl.length();
00594 //             LDEBUG << "    after pos=" << *position;
00595         return PointAfter;
00596     }
00597 
00598     QPointF pos = point - textrect.topLeft();
00599 
00600     // ### rtl?
00601 
00602     HitPoint hit = PointInside;
00603     *l = tl;
00604     int off = 0;
00605     for (int i = 0; i < tl->lineCount(); ++i) {
00606         QTextLine line = tl->lineAt(i);
00607         const QRectF lr = line.naturalTextRect();
00608         if (lr.top() > pos.y()) {
00609             off = qMin(off, line.textStart());
00610         } else if (lr.bottom() <= pos.y()) {
00611             off = qMax(off, line.textStart() + line.textLength());
00612         } else {
00613             if (lr.left() <= pos.x() && lr.right() >= pos.x())
00614                 hit = PointExact;
00615             off = line.xToCursor(pos.x());
00616             break;
00617         }
00618     }
00619     *position += off;
00620 
00621 //     LDEBUG << "    inside=" << hit << " pos=" << *position;
00622     return hit;
00623 }
00624 
00625 // ### could be moved to QTextBlock
00626 qreal QTextDocumentLayoutPrivate::indent(QTextBlock bl) const
00627 {
00628     Q_Q(const QTextDocumentLayout);
00629     QTextBlockFormat blockFormat = bl.blockFormat();
00630     qreal indent = blockFormat.indent();
00631 
00632     QTextObject *object = q->document()->objectForFormat(blockFormat);
00633     if (object)
00634         indent += object->format().toListFormat().indent();
00635 
00636     qreal scale = 1;
00637     if (q->paintDevice()) {
00638         extern int qt_defaultDpi();
00639         scale = qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi());
00640     }
00641 
00642     return indent * TextIndentValue * scale;
00643 }
00644 
00645 static void drawFrameDecoration(QPainter *painter, QTextFrame *frame, QTextFrameData *fd, const QRectF &clip, const QRectF &rect)
00646 {
00647     if (fd->border) {
00648         painter->save();
00649         painter->setBrush(Qt::lightGray);
00650         painter->setPen(Qt::NoPen);
00651         const qreal margin = fd->margin + fd->border;
00652         const qreal w = rect.width() - 2*margin;
00653         const qreal h = rect.height() - 2*margin;
00654         // left
00655         painter->drawRect(QRectF(rect.left() + fd->margin, rect.top() + fd->margin, fd->border, h + 2 * fd->border));
00656         // top
00657         painter->drawRect(QRectF(rect.left() + fd->margin + fd->border, rect.top() + fd->margin, w + fd->border, fd->border));
00658 
00659         painter->setBrush(Qt::darkGray);
00660         // right
00661         painter->drawRect(QRectF(rect.left() + fd->margin + fd->border + w, rect.top() + fd->margin + fd->border, fd->border, h));
00662         // bottom
00663         painter->drawRect(QRectF(rect.left() + fd->margin + fd->border, rect.top() + fd->margin + fd->border + h, w + fd->border, fd->border));
00664 
00665         painter->restore();
00666     }
00667 
00668     const QBrush bg = frame->frameFormat().background();
00669     if (bg != Qt::NoBrush) {
00670         QRectF bgRect = rect;
00671         const qreal margin = fd->margin + fd->border;
00672         bgRect.adjust(margin, margin, -margin, -margin);
00673 
00674         QRectF gradientRect; // invalid makes it default to bgRect
00675         if (!frame->parentFrame()) {
00676             bgRect = clip;
00677             gradientRect.setWidth(painter->device()->width());
00678             gradientRect.setHeight(painter->device()->height());
00679         }
00680         fillBackground(painter, bgRect, bg, gradientRect);
00681     }
00682 }
00683 
00684 void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *painter,
00685                                            const QAbstractTextDocumentLayout::PaintContext &context,
00686                                            QTextFrame *frame) const
00687 {
00688     Q_Q(const QTextDocumentLayout);
00689     QTextFrameData *fd = data(frame);
00690     // #######
00691     if (fd->layoutDirty)
00692         return;
00693     Q_ASSERT(!fd->sizeDirty);
00694     Q_ASSERT(!fd->layoutDirty);
00695 
00696     const QPointF off = offset + fd->position;
00697     if (context.clip.isValid()
00698         && (off.y() > context.clip.bottom() || off.y() + fd->size.height() < context.clip.top()
00699             || off.x() > context.clip.right() || off.x() + fd->size.width() < context.clip.left()))
00700         return;
00701 
00702 //     LDEBUG << debug_indent << "drawFrame" << frame->firstPosition() << "--" << frame->lastPosition() << "at" << offset;
00703 //     INC_INDENT;
00704 
00705     // if the cursor is /on/ a table border we may need to repaint it
00706     // afterwards, as we usually draw the decoration first
00707     QTextBlock cursorBlockNeedingRepaint;
00708     QPointF offsetOfRepaintedCursorBlock = off;
00709 
00710     QTextTable *table = qobject_cast<QTextTable *>(frame);
00711     const QRectF frameRect(off, fd->size);
00712 
00713     if (table) {
00714         const int rows = table->rows();
00715         const int columns = table->columns();
00716         QTextTableData *td = static_cast<QTextTableData *>(data(table));
00717 
00718         QVarLengthArray<int> selectedTableCells(context.selections.size() * 4);
00719         for (int i = 0; i < context.selections.size(); ++i) {
00720             const QAbstractTextDocumentLayout::Selection &s = context.selections.at(i);
00721             int row_start = -1, col_start = -1, num_rows = -1, num_cols = -1;
00722 
00723             if (s.cursor.currentTable() == table)
00724                 s.cursor.selectedTableCells(&row_start, &num_rows, &col_start, &num_cols);
00725 
00726             selectedTableCells[i * 4] = row_start;
00727             selectedTableCells[i * 4 + 1] = col_start;
00728             selectedTableCells[i * 4 + 2] = num_rows;
00729             selectedTableCells[i * 4 + 3] = num_cols;
00730         }
00731 
00732         if (td->rowsAfterPageBreak.isEmpty()) {
00733             drawFrameDecoration(painter, frame, fd, context.clip, frameRect);
00734         } else {
00735             Q_ASSERT(td->rowsAfterPageBreak.first() > 0);
00736             QRectF rect = frameRect;
00737 
00738             const qreal extraTableHeight = td->padding + td->border + td->cellSpacing // inter cell spacing
00739                                            + td->margin + td->border + td->padding; // effective table margin
00740 
00741             qreal tableHeaderHeight = 0;
00742             const QTextTableFormat format = table->format();
00743             const int headerRowCount = qMin(format.headerRowCount(), rows - 1);
00744             if (headerRowCount > 0)
00745                 tableHeaderHeight = td->rowPositions.at(headerRowCount) - td->rowPositions.at(0);
00746 
00747             int lastVisibleRow = td->rowsAfterPageBreak.first() - 1;
00748 
00749             rect.setHeight(td->rowPositions.at(lastVisibleRow) + td->heights.at(lastVisibleRow) + extraTableHeight);
00750             drawFrameDecoration(painter, frame, fd, context.clip, rect);
00751 
00752             for (int i = 0; i < td->rowsAfterPageBreak.count(); ++i) {
00753                 const int firstVisibleRow = td->rowsAfterPageBreak.at(i);
00754                 if (i < td->rowsAfterPageBreak.count() - 1)
00755                     lastVisibleRow = td->rowsAfterPageBreak.at(i + 1) - 1;
00756                 else
00757                     lastVisibleRow = rows - 1;
00758 
00759                 rect.setTop(off.y() + td->rowPositions.at(firstVisibleRow) - extraTableHeight - tableHeaderHeight);
00760                 rect.setBottom(off.y() + td->rowPositions.at(lastVisibleRow) + td->heights.at(lastVisibleRow) + extraTableHeight);
00761 
00762                 drawFrameDecoration(painter, frame, fd, context.clip, rect);
00763 
00764                 for (int r = 0; r < headerRowCount; ++r)
00765                     for (int c = 0; c < columns; ++c) {
00766                         QTextTableCell cell = table->cellAt(r, c);
00767                         QAbstractTextDocumentLayout::PaintContext cell_context = context;
00768                         for (int i = 0; i < context.selections.size(); ++i) {
00769                             int row_start = selectedTableCells[i * 4];
00770                             int col_start = selectedTableCells[i * 4 + 1];
00771                             int num_rows = selectedTableCells[i * 4 + 2];
00772                             int num_cols = selectedTableCells[i * 4 + 3];
00773 
00774                             if (row_start != -1) {
00775                                 if (r >= row_start && r < row_start + num_rows
00776                                         && c >= col_start && c < col_start + num_cols) {
00777                                     cell_context.selections[i].cursor.setPosition(cell.firstPosition());
00778                                     cell_context.selections[i].cursor.setPosition(cell.lastPosition(), QTextCursor::KeepAnchor);
00779                                 } else {
00780                                     cell_context.selections[i].cursor.clearSelection();
00781                                 }
00782                             }
00783                         }
00784                         QRectF cellRect = td->cellRect(cell);
00785 
00786                         cellRect.translate(off);
00787 
00788                         cellRect.translate(0, td->rowPositions.at(firstVisibleRow) - extraTableHeight - tableHeaderHeight);
00789 
00790                         // we draw the right/bottom border at left() + width(), so for the clipping test
00791                         // we have to enlarge the cell rect by one pixel in both directions
00792                         if (cell_context.clip.isValid() && !cellRect.adjusted(0, 0, 1, 1).intersects(cell_context.clip))
00793                             continue;
00794 
00795                         drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
00796                                       &offsetOfRepaintedCursorBlock);
00797                     }
00798             }
00799         }
00800 
00801         int firstRow = 0;
00802         int lastRow = rows;
00803 
00804         if (context.clip.isValid()) {
00805             QVector<qreal>::ConstIterator rowIt = qLowerBound(td->rowPositions.constBegin(), td->rowPositions.constEnd(), context.clip.top() - off.y());
00806             if (rowIt != td->rowPositions.constEnd() && rowIt != td->rowPositions.constBegin()) {
00807                 --rowIt;
00808                 firstRow = rowIt - td->rowPositions.constBegin();
00809             }
00810 
00811             rowIt = qUpperBound(td->rowPositions.constBegin(), td->rowPositions.constEnd(), context.clip.bottom() - off.y());
00812             if (rowIt != td->rowPositions.constEnd()) {
00813                 ++rowIt;
00814                 lastRow = rowIt - td->rowPositions.constBegin();
00815             }
00816         }
00817 
00818         for (int c = 0; c < columns; ++c) {
00819             QTextTableCell cell = table->cellAt(firstRow, c);
00820             firstRow = qMin(firstRow, cell.row());
00821         }
00822 
00823         for (int r = firstRow; r < lastRow; ++r) {
00824             for (int c = 0; c < columns; ++c) {
00825                 QTextTableCell cell = table->cellAt(r, c);
00826                 QAbstractTextDocumentLayout::PaintContext cell_context = context;
00827                 for (int i = 0; i < context.selections.size(); ++i) {
00828                     int row_start = selectedTableCells[i * 4];
00829                     int col_start = selectedTableCells[i * 4 + 1];
00830                     int num_rows = selectedTableCells[i * 4 + 2];
00831                     int num_cols = selectedTableCells[i * 4 + 3];
00832 
00833                     if (row_start != -1) {
00834                         if (r >= row_start && r < row_start + num_rows
00835                             && c >= col_start && c < col_start + num_cols) {
00836                             cell_context.selections[i].cursor.setPosition(cell.firstPosition());
00837                             cell_context.selections[i].cursor.setPosition(cell.lastPosition(), QTextCursor::KeepAnchor);
00838                         } else {
00839                             cell_context.selections[i].cursor.clearSelection();
00840                         }
00841                     }
00842                 }
00843                 QRectF cellRect = td->cellRect(cell);
00844 
00845                 cellRect.translate(off);
00846                 // we draw the right/bottom border at left() + width(), so for the clipping test
00847                 // we have to enlarge the cell rect by one pixel in both directions
00848                 if (cell_context.clip.isValid() && !cellRect.adjusted(0, 0, 1, 1).intersects(cell_context.clip))
00849                     continue;
00850 
00851                 drawTableCell(cellRect, painter, cell_context, table, td, r, c, &cursorBlockNeedingRepaint,
00852                               &offsetOfRepaintedCursorBlock);
00853             }
00854         }
00855 
00856     } else {
00857         drawFrameDecoration(painter, frame, fd, context.clip, frameRect);
00858 
00859         QTextFrame::Iterator it = frame->begin();
00860 
00861         if (frame == q->document()->rootFrame())
00862             it = frameIteratorForYPosition(context.clip.top());
00863 
00864         drawFlow(off, painter, context, it, &cursorBlockNeedingRepaint);
00865     }
00866 
00867     if (cursorBlockNeedingRepaint.isValid()) {
00868         const QPen oldPen = painter->pen();
00869         painter->setPen(context.palette.color(QPalette::Text));
00870         const int cursorPos = context.cursorPosition - cursorBlockNeedingRepaint.position();
00871         cursorBlockNeedingRepaint.layout()->drawCursor(painter, offsetOfRepaintedCursorBlock,
00872                                                        cursorPos, cursorWidth);
00873         painter->setPen(oldPen);
00874     }
00875 
00876 //     DEC_INDENT;
00877 
00878     return;
00879 }
00880 
00881 void QTextDocumentLayoutPrivate::drawTableCell(const QRectF &cellRect, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &cell_context,
00882                                                QTextTable *table, QTextTableData *td, int r, int c,
00883                                                QTextBlock *cursorBlockNeedingRepaint, QPointF *cursorBlockOffset) const
00884 {
00885     QTextTableCell cell = table->cellAt(r, c);
00886     int rspan = cell.rowSpan();
00887     int cspan = cell.columnSpan();
00888     if (rspan != 1) {
00889         int cr = cell.row();
00890         if (cr != r)
00891             return;
00892     }
00893     if (cspan != 1) {
00894         int cc = cell.column();
00895         if (cc != c)
00896             return;
00897     }
00898 
00899     if (td->border) {
00900         const QBrush oldBrush = painter->brush();
00901         const QPen oldPen = painter->pen();
00902 
00903         painter->setBrush(Qt::darkGray);
00904         painter->setPen(Qt::NoPen);
00905 
00906         // top border
00907         painter->drawRect(QRectF(cellRect.left(), cellRect.top() - td->border,
00908                     cellRect.width() + td->border, td->border));
00909         // left border
00910         painter->drawRect(QRectF(cellRect.left() - td->border, cellRect.top() - td->border,
00911                     td->border, cellRect.height() + 2 * td->border));
00912 
00913         painter->setBrush(Qt::lightGray);
00914 
00915         // bottom border
00916         painter->drawRect(QRectF(cellRect.left(), cellRect.top() + cellRect.height(),
00917                     cellRect.width() + td->border, td->border));
00918         // right border
00919         painter->drawRect(QRectF(cellRect.left() + cellRect.width(), cellRect.top(),
00920                     td->border, cellRect.height()));
00921 
00922         painter->setBrush(oldBrush);
00923         painter->setPen(oldPen);
00924     }
00925 
00926     {
00927         const QBrush bg = cell.format().background();
00928         if (bg != Qt::NoBrush)
00929             fillBackground(painter, cellRect, bg);
00930     }
00931 
00932     const QPointF cellPos = QPointF(cellRect.left() + td->cellPadding, cellRect.top() + td->cellPadding);
00933 
00934     QTextBlock repaintBlock;
00935     drawFlow(cellPos, painter, cell_context, cell.begin(), &repaintBlock);
00936     if (repaintBlock.isValid()) {
00937         *cursorBlockNeedingRepaint = repaintBlock;
00938         *cursorBlockOffset = cellPos;
00939     }
00940 }
00941 
00942 void QTextDocumentLayoutPrivate::drawFlow(const QPointF &offset, QPainter *painter, const QAbstractTextDocumentLayout::PaintContext &context,
00943                                           QTextFrame::Iterator it, QTextBlock *cursorBlockNeedingRepaint) const
00944 {
00945     const bool inRootFrame = (!it.atEnd() && it.parentFrame() && it.parentFrame()->parentFrame() == 0);
00946 
00947     QVector<QCheckPoint>::ConstIterator lastVisibleCheckPoint = checkPoints.end();
00948     if (inRootFrame && context.clip.isValid()) {
00949         lastVisibleCheckPoint = qLowerBound(checkPoints.begin(), checkPoints.end(), qreal(context.clip.bottom()));
00950     }
00951 
00952     QTextBlock lastBlock;
00953 
00954     for (; !it.atEnd(); ++it) {
00955         QTextFrame *c = it.currentFrame();
00956 
00957         if (inRootFrame && !checkPoints.isEmpty()) {
00958             int currentPosInDoc;
00959             if (c)
00960                 currentPosInDoc = c->firstPosition();
00961             else
00962                 currentPosInDoc = it.currentBlock().position();
00963 
00964             // if we're past what is already layouted then we're better off
00965             // not trying to draw things that may not be positioned correctly yet
00966             if (currentPosInDoc >= checkPoints.last().positionInFrame)
00967                 break;
00968 
00969             if (lastVisibleCheckPoint != checkPoints.end()
00970                 && context.clip.isValid()
00971                 && currentPosInDoc >= lastVisibleCheckPoint->positionInFrame
00972                )
00973                 break;
00974         }
00975 
00976         if (c)
00977             drawFrame(offset, painter, context, c);
00978         else
00979             drawBlock(offset, painter, context, it.currentBlock());
00980 
00981         // when entering a table and the previous block is empty
00982         // then layoutFlow 'hides' the block that just causes a
00983         // new line by positioning it /on/ the table border. as we
00984         // draw that block before the table itself the decoration
00985         // 'overpaints' the cursor and we need to paint it afterwards
00986         // again
00987         if (isEmptyBlockBeforeTable(lastBlock, it)
00988             && lastBlock.contains(context.cursorPosition)
00989            ) {
00990             *cursorBlockNeedingRepaint = lastBlock;
00991         }
00992 
00993         lastBlock = it.currentBlock();
00994     }
00995 }
00996 
00997 void QTextDocumentLayoutPrivate::drawBlock(const QPointF &offset, QPainter *painter,
00998                                            const QAbstractTextDocumentLayout::PaintContext &context,
00999                                            QTextBlock bl) const
01000 {
01001     Q_Q(const QTextDocumentLayout);
01002     const QTextLayout *tl = bl.layout();
01003     QRectF r = tl->boundingRect();
01004     r.translate(offset + tl->position());
01005     if (context.clip.isValid() && !r.intersects(context.clip))
01006         return;
01007 //      LDEBUG << debug_indent << "drawBlock" << bl.position() << "at" << offset << "br" << tl->boundingRect();
01008 
01009     QTextBlockFormat blockFormat = bl.blockFormat();
01010 
01011     QBrush bg = blockFormat.background();
01012     if (bg != Qt::NoBrush) {
01013         // don't paint into the left margin. Right margin is already excluded by the line breaking
01014         QRectF contentsRect = r;
01015         contentsRect.setLeft(contentsRect.left() + bl.blockFormat().leftMargin());
01016         fillBackground(painter, contentsRect, bg);
01017     }
01018 
01019     QVector<QTextLayout::FormatRange> selections;
01020     int blpos = bl.position();
01021     int bllen = bl.length();
01022     const QTextCharFormat *selFormat = 0;
01023     for (int i = 0; i < context.selections.size(); ++i) {
01024         const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i);
01025         const int selStart = range.cursor.selectionStart() - blpos;
01026         const int selEnd = range.cursor.selectionEnd() - blpos;
01027         if (selStart < bllen && selEnd > 0
01028              && selEnd > selStart) {
01029             QTextLayout::FormatRange o;
01030             o.start = selStart;
01031             o.length = selEnd - selStart;
01032             o.format = range.format;
01033             selections.append(o);
01034         } else if (range.format.hasProperty(QTextFormat::FullWidthSelection)
01035                    && bl.contains(range.cursor.position())) {
01036             // for full width selections we don't require an actual selection, just
01037             // a position to specify the line. that's more convenience in usage.
01038             QTextLayout::FormatRange o;
01039             QTextLine l = tl->lineForTextPosition(range.cursor.position() - blpos);
01040             o.start = l.textStart();
01041             o.length = qMax(1, l.textLength());
01042             o.format = range.format;
01043             selections.append(o);
01044        }
01045         if (selStart <= 0 && selEnd >= 1)
01046             selFormat = &range.format;
01047     }
01048 
01049     QTextObject *object = q->document()->objectForFormat(bl.blockFormat());
01050     if (object && object->format().toListFormat().style() != QTextListFormat::ListStyleUndefined)
01051         drawListItem(offset, painter, context, bl, selFormat);
01052 
01053     QPen oldPen = painter->pen();
01054     painter->setPen(context.palette.color(QPalette::Text));
01055 
01056     tl->draw(painter, offset, selections, context.clip);
01057 
01058     if ((context.cursorPosition >= blpos && context.cursorPosition < blpos + bllen)
01059         || (context.cursorPosition < -1 && !tl->preeditAreaText().isEmpty())) {
01060         int cpos = context.cursorPosition;
01061         if (cpos < -1)
01062             cpos = tl->preeditAreaPosition() - (cpos + 2);
01063         else
01064             cpos -= blpos;
01065         tl->drawCursor(painter, offset, cpos, cursorWidth);
01066     }
01067 
01068     if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) {
01069         const qreal width = blockFormat.lengthProperty(QTextFormat::BlockTrailingHorizontalRulerWidth).value(r.width());
01070         painter->setPen(context.palette.color(QPalette::Dark));
01071         qreal y = r.bottom();
01072         if (bl.length() == 1)
01073             y = r.top() + r.height() / 2;
01074 
01075         const qreal middleX = r.left() + r.width() / 2;
01076         painter->drawLine(QLineF(middleX - width / 2, y, middleX + width / 2, y));
01077     }
01078 
01079     painter->setPen(oldPen);
01080 }
01081 
01082 
01083 void QTextDocumentLayoutPrivate::drawListItem(const QPointF &offset, QPainter *painter,
01084                                               const QAbstractTextDocumentLayout::PaintContext &context,
01085                                               QTextBlock bl, const QTextCharFormat *selectionFormat) const
01086 {
01087     Q_Q(const QTextDocumentLayout);
01088     const QTextBlockFormat blockFormat = bl.blockFormat();
01089     const QTextCharFormat charFormat = bl.charFormat();
01090     QFont font(charFormat.font());
01091     if (q->paintDevice())
01092         font = QFont(font, q->paintDevice());
01093 
01094     const QFontMetrics fontMetrics(font);
01095     QTextObject * const object = q->document()->objectForFormat(blockFormat);
01096     const QTextListFormat lf = object->format().toListFormat();
01097     const int style = lf.style();
01098     QString itemText;
01099     QSizeF size;
01100 
01101     QTextLayout *layout = bl.layout();
01102     if (layout->lineCount() == 0)
01103         return;
01104     QTextLine firstLine = layout->lineAt(0);
01105     Q_ASSERT(firstLine.isValid());
01106     QPointF pos = (offset + layout->boundingRect().topLeft() + layout->position()).toPoint();
01107     Qt::LayoutDirection dir = blockFormat.layoutDirection();
01108     {
01109         QRectF textRect = firstLine.naturalTextRect();
01110         pos += textRect.topLeft().toPoint();
01111         if (dir == Qt::RightToLeft)
01112             pos.rx() += textRect.width();
01113     }
01114 
01115     switch (style) {
01116     case QTextListFormat::ListDecimal:
01117     case QTextListFormat::ListLowerAlpha:
01118     case QTextListFormat::ListUpperAlpha:
01119         itemText = static_cast<QTextList *>(object)->itemText(bl);
01120         size.setWidth(fontMetrics.width(itemText));
01121         size.setHeight(fontMetrics.height());
01122         break;
01123 
01124     case QTextListFormat::ListSquare:
01125     case QTextListFormat::ListCircle:
01126     case QTextListFormat::ListDisc:
01127         size.setWidth(fontMetrics.lineSpacing() / 3);
01128         size.setHeight(size.width());
01129         break;
01130 
01131     case QTextListFormat::ListStyleUndefined:
01132         return;
01133     default: return;
01134     }
01135 
01136     QRectF r(pos, size);
01137 
01138     qreal xoff = fontMetrics.width(QLatin1Char(' '));
01139     if (dir == Qt::LeftToRight)
01140         xoff = -xoff - size.width();
01141     r.translate( xoff, (fontMetrics.height() / 2 - size.height() / 2));
01142 
01143     painter->save();
01144 
01145     painter->setRenderHint(QPainter::Antialiasing);
01146 
01147     if (selectionFormat) {
01148         painter->setPen(QPen(selectionFormat->foreground(), 0));
01149         painter->fillRect(r, selectionFormat->background());
01150     } else {
01151         QBrush fg = charFormat.foreground();
01152         if (fg == Qt::NoBrush)
01153             fg = context.palette.text();
01154         painter->setPen(QPen(fg, 0));
01155     }
01156 
01157     QBrush brush = context.palette.brush(QPalette::Text);
01158 
01159     switch (style) {
01160     case QTextListFormat::ListDecimal:
01161     case QTextListFormat::ListLowerAlpha:
01162     case QTextListFormat::ListUpperAlpha: {
01163         QTextLayout layout(itemText, font, q->paintDevice());
01164         layout.setCacheEnabled(true);
01165         QTextOption option(Qt::AlignLeft | Qt::AlignAbsolute);
01166         option.setTextDirection(dir);
01167         layout.setTextOption(option);
01168         layout.beginLayout();
01169         layout.createLine();
01170         layout.endLayout();
01171         layout.draw(painter, QPointF(r.left(), pos.y()));
01172         break;
01173     }
01174     case QTextListFormat::ListSquare:
01175         painter->fillRect(r, brush);
01176         break;
01177     case QTextListFormat::ListCircle:
01178         painter->drawEllipse(r);
01179         break;
01180     case QTextListFormat::ListDisc:
01181         painter->setBrush(brush);
01182         painter->drawEllipse(r);
01183         painter->setBrush(Qt::NoBrush);
01184         break;
01185     case QTextListFormat::ListStyleUndefined:
01186         break;
01187     default:
01188         break;
01189     }
01190 
01191     painter->restore();
01192 }
01193 
01194 static bool isFrameInCell(const QTextTableCell &cell, QTextFrame *frame)
01195 {
01196     const int cellStart = cell.firstPosition();
01197     const int cellEnd = cell.lastPosition();
01198     const int frameStart = frame->firstPosition();
01199     const int frameEnd = frame->lastPosition();
01200 
01201     return cellStart <= frameStart && cellStart <= frameEnd
01202            && cellEnd >= frameStart && cellEnd >= frameEnd;
01203 }
01204 
01205 QLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QTextTableCell &cell, qreal width,
01206                                                     int layoutFrom, int layoutTo)
01207 {
01208     LDEBUG << "layoutCell";
01209     QLayoutStruct layoutStruct;
01210     layoutStruct.frame = t;
01211     layoutStruct.minimumWidth = 0;
01212     layoutStruct.maximumWidth = INT_MAX;
01213     layoutStruct.y = 0;
01214     layoutStruct.x_left = 0;
01215     layoutStruct.x_right = width;
01216     // we get called with different widths all the time (for example for figuring
01217     // out the min/max widths), so we always have to do the full layout ;(
01218     // also when for example in a table layoutFrom/layoutTo affect only one cell,
01219     // making that one cell grow the available width of the other cells may change
01220     // (shrink) and therefore when layoutCell gets called for them they have to
01221     // be relayouted, even if layoutFrom/layoutTo is not in their range. Hence
01222     // this line:
01223     layoutStruct.fullLayout = true;
01224 
01225     QList<QTextFrame *> floats;
01226 
01227     // ### speed up
01228     // layout out child frames in that cell first
01229     for (int i = 0; i < t->childFrames().size(); ++i){
01230         QTextFrame *frame = t->childFrames().at(i);
01231         if (isFrameInCell(cell, frame)) {
01232             QTextFrameData *cd = data(frame);
01233             cd->sizeDirty = true;
01234                 layoutFrame(frame, frame->firstPosition(), frame->lastPosition(), width, -1);
01235             layoutStruct.minimumWidth = qMax(layoutStruct.minimumWidth, cd->minimumWidth);
01236             layoutStruct.maximumWidth = qMin(layoutStruct.maximumWidth, cd->maximumWidth);
01237 
01238             if (cd->flow_position != QTextFrameFormat::InFlow)
01239                 floats.append(frame);
01240         }
01241     }
01242 
01243     qreal floatMinWidth = layoutStruct.minimumWidth;
01244 
01245     layoutFlow(cell.begin(), &layoutStruct, layoutFrom, layoutTo);
01246 
01247     // floats that are located inside the text (like inline images) aren't taken into account by
01248     // layoutFlow with regards to the cell height (layoutStruct->y), so for a safety measure we
01249     // do that here. For example with <td><img align="right" src="..." />blah</td>
01250     // when the image happens to be higher than the text
01251     for (int i = 0; i < floats.size(); ++i)
01252         layoutStruct.y = qMax(layoutStruct.y, data(floats.at(i))->size.height());
01253 
01254     // constraint the maximumWidth by the minimum width of the fixed size floats, to
01255     // keep them visible
01256     layoutStruct.maximumWidth = qMax(layoutStruct.maximumWidth, floatMinWidth);
01257 
01258     // as floats in cells get added to the table's float list but must not affect
01259     // floats in other cells we must clear the list here.
01260     data(t)->floats.clear();
01261 
01262 //    qDebug() << "layoutCell done";
01263 
01264     return layoutStruct;
01265 }
01266 
01267 QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom, int layoutTo)
01268 {
01269     LDEBUG << "layoutTable";
01270     QTextTableData *td = static_cast<QTextTableData *>(data(table));
01271     Q_ASSERT(td->sizeDirty);
01272     const int rows = table->rows();
01273     const int columns = table->columns();
01274 
01275     const QTextTableFormat fmt = table->format();
01276 
01277     QVector<QTextLength> columnWidthConstraints = fmt.columnWidthConstraints();
01278     if (columnWidthConstraints.size() != columns)
01279         columnWidthConstraints.resize(columns);
01280     Q_ASSERT(columnWidthConstraints.count() == columns);
01281 
01282     const qreal cellSpacing = td->cellSpacing = fmt.cellSpacing();
01283     td->cellPadding = fmt.cellPadding();
01284     const qreal margin = td->margin + td->border + td->padding;
01285 
01286     qreal totalWidth = fmt.width().value(td->contentsWidth);
01287     // two (vertical) borders per cell per column
01288     totalWidth -= columns * 2 * td->border;
01289     // inter-cell spacing
01290     totalWidth -= (columns - 1) * cellSpacing;
01291     // cell spacing at the left and right hand side
01292     totalWidth -= 2 * cellSpacing;
01293     // remember the width used to distribute to percentaged columns
01294     qreal initialTotalWidth = totalWidth;
01295 
01296     td->widths.resize(columns);
01297     td->widths.fill(0);
01298 
01299     td->minWidths.resize(columns);
01300     // start with a minimum width of 0. totally empty
01301     // cells of default created tables are invisible otherwise
01302     // and therefore hardly editable
01303     td->minWidths.fill(1);
01304 
01305     td->maxWidths.resize(columns);
01306     td->maxWidths.fill(INT_MAX);
01307 
01308     td->rowsAfterPageBreak.clear();
01309 
01310     // calculate minimum and maximum sizes of the columns
01311     for (int i = 0; i < columns; ++i) {
01312         for (int row = 0; row < rows; ++row) {
01313             const QTextTableCell cell = table->cellAt(row, i);
01314             const int cspan = cell.columnSpan();
01315 
01316             if (cspan > 1 && i != cell.column())
01317                 continue;
01318 
01319             // to figure out the min and the max width lay out the cell at
01320             // maximum width. otherwise the maxwidth calculation sometimes
01321             // returns wrong values
01322             QLayoutStruct layoutStruct = layoutCell(table, cell, INT_MAX, layoutFrom, layoutTo);
01323 
01324             // distribute the minimum width over all columns the cell spans
01325             qreal widthToDistribute = layoutStruct.minimumWidth + 2 * td->cellPadding;
01326             for (int n = 0; n < cspan; ++n) {
01327                 const int col = i + n;
01328                 qreal w = widthToDistribute / (cspan - n);
01329                 td->minWidths[col] = qMax(td->minWidths.at(col), w);
01330                 widthToDistribute -= td->minWidths.at(col);
01331                 if (widthToDistribute <= 0)
01332                     break;
01333             }
01334 
01335             // ### colspans
01336             qreal maxW = td->maxWidths.at(i);
01337             if (layoutStruct.maximumWidth != INT_MAX) {
01338                 if (maxW == INT_MAX)
01339                     maxW = layoutStruct.maximumWidth + 2 * td->cellPadding;
01340                 else
01341                     maxW = qMax(maxW, layoutStruct.maximumWidth + 2 * td->cellPadding);
01342             }
01343             td->maxWidths[i] = qMax(td->minWidths.at(i), maxW);
01344         }
01345     }
01346 
01347     // set fixed values, figure out total percentages used and number of
01348     // variable length cells. Also assign the minimum width for variable columns.
01349     qreal totalPercentage = 0;
01350     int variableCols = 0;
01351     for (int i = 0; i < columns; ++i) {
01352         const QTextLength &length = columnWidthConstraints.at(i);
01353         if (length.type() == QTextLength::FixedLength) {
01354             td->widths[i] = qMax(length.rawValue(), td->minWidths.at(i));
01355             totalWidth -= td->widths.at(i);
01356         } else if (length.type() == QTextLength::PercentageLength) {
01357             totalPercentage += length.rawValue();
01358         } else if (length.type() == QTextLength::VariableLength) {
01359             variableCols++;
01360 
01361             td->widths[i] = td->minWidths.at(i);
01362             totalWidth -= td->minWidths.at(i);
01363         }
01364     }
01365 
01366     // set percentage values
01367     {
01368         const qreal totalPercentagedWidth = initialTotalWidth * totalPercentage / 100;
01369         for (int i = 0; i < columns; ++i)
01370             if (columnWidthConstraints.at(i).type() == QTextLength::PercentageLength) {
01371                 const qreal percentWidth = totalPercentagedWidth * columnWidthConstraints.at(i).rawValue() / totalPercentage;
01372                 td->widths[i] = qMax(percentWidth, td->minWidths.at(i));
01373                 totalWidth -= td->widths.at(i);
01374             }
01375     }
01376 
01377     // for variable columns distribute the remaining space
01378     if (variableCols > 0 && totalWidth > 0) {
01379         QVarLengthArray<int> columnsWithProperMaxSize;
01380         for (int i = 0; i < columns; ++i)
01381             if (columnWidthConstraints.at(i).type() == QTextLength::VariableLength
01382                 && td->maxWidths.at(i) != INT_MAX)
01383                 columnsWithProperMaxSize.append(i);
01384 
01385         qreal lastTotalWidth = totalWidth;
01386         while (totalWidth > 0) {
01387             for (int k = 0; k < columnsWithProperMaxSize.count(); ++k) {
01388                 const int col = columnsWithProperMaxSize[k];
01389                 const int colsLeft = columnsWithProperMaxSize.count() - k;
01390                 const qreal w = qMin(td->maxWidths.at(col) - td->widths.at(col), totalWidth / colsLeft);
01391                 td->widths[col] += w;
01392                 totalWidth -= w;
01393             }
01394             if (totalWidth == lastTotalWidth)
01395                 break;
01396             lastTotalWidth = totalWidth;
01397         }
01398 
01399         if (totalWidth > 0
01400             // don't unnecessarily grow variable length sized tables
01401             && fmt.width().type() != QTextLength::VariableLength) {
01402             const qreal widthPerAnySizedCol = totalWidth / variableCols;
01403             for (int col = 0; col < columns; ++col) {
01404                 if (columnWidthConstraints.at(col).type() == QTextLength::VariableLength)
01405                     td->widths[col] += widthPerAnySizedCol;
01406             }
01407         }
01408     }
01409 
01410 
01411     td->columnPositions.resize(columns);
01412     td->columnPositions[0] = margin /*includes table border*/ + cellSpacing + td->border;
01413 
01414     for (int i = 1; i < columns; ++i)
01415         td->columnPositions[i] = td->columnPositions.at(i-1) + td->widths.at(i-1) + td->border + cellSpacing + td->border;
01416 
01417     td->heights.resize(rows);
01418     td->heights.fill(0);
01419 
01420     td->rowPositions.resize(rows);
01421     td->rowPositions[0] = margin /*includes table border*/ + cellSpacing + td->border;
01422 
01423     bool haveRowSpannedCells = false;
01424 
01425     // now that we have the column widths we can lay out all cells with the right
01426     // width, to calculate the row heights. we have to use two passes though, cells
01427     // which span more than one row have to be processed later to avoid them enlarging
01428     // other calls too much
01429     //
01430     // ### this could be made faster by iterating over the cells array of QTextTable
01431     for (int r = 0; r < rows; ++r) {
01432         td->calcRowPosition(r);
01433 
01434         for (int c = 0; c < columns; ++c) {
01435             QTextTableCell cell = table->cellAt(r, c);
01436             const int rspan = cell.rowSpan();
01437             const int cspan = cell.columnSpan();
01438 
01439             if (cspan > 1 && cell.column() != c)
01440                 continue;
01441 
01442             if (rspan > 1) {
01443                 haveRowSpannedCells = true;
01444                 continue;
01445             }
01446 
01447             const qreal width = td->cellWidth(c, cspan);
01448 //            qDebug() << "layoutCell for cell at row" << r << "col" << c;
01449             QLayoutStruct layoutStruct = layoutCell(table, cell, width, layoutFrom, layoutTo);
01450 
01451             td->heights[r] = qMax(td->heights.at(r), layoutStruct.y + 2 * td->cellPadding);
01452         }
01453     }
01454 
01455     if (haveRowSpannedCells) {
01456         for (int r = 0; r < rows; ++r) {
01457             td->calcRowPosition(r);
01458 
01459             for (int c = 0; c < columns; ++c) {
01460                 QTextTableCell cell = table->cellAt(r, c);
01461                 const int rspan = cell.rowSpan();
01462                 const int cspan = cell.columnSpan();
01463 
01464                 if (cspan > 1 && cell.column() != c)
01465                     continue;
01466 
01467                 if (rspan == 1)
01468                     continue;
01469 
01470                 if (cell.row() != r)
01471                     continue;
01472 
01473                 const qreal width = td->cellWidth(c, cspan);
01474                 QLayoutStruct layoutStruct = layoutCell(table, cell, width, layoutFrom, layoutTo);
01475 
01476                 // the last row gets all the remaining space
01477                 qreal heightToDistribute = layoutStruct.y + 2 * td->cellPadding;
01478                 for (int n = 0; n < rspan - 1; ++n) {
01479                     const int row = r + n;
01480                     heightToDistribute -= td->heights.at(row) + td->border + cellSpacing + td->border;
01481                     if (heightToDistribute <= 0)
01482                         break;
01483                 }
01484 
01485                 if (heightToDistribute > 0) {
01486                     const int lastRow = r + rspan - 1;
01487                     td->heights[lastRow] = qMax(td->heights.at(lastRow), heightToDistribute);
01488                 }
01489             }
01490         }
01491     }
01492 
01493     // - margin to compensate the + margin in columnPositions[0]
01494 //    td->contentsWidth = qMax(td->contentsWidth,
01495 //                             td->columnPositions.last() + td->widths.last() + td->padding + td->border + cellSpacing - margin);
01496     td->contentsWidth = td->columnPositions.last() + td->widths.last() + td->padding + td->border + cellSpacing - margin;
01497 
01498     td->minimumWidth = td->columnPositions.at(0);
01499     for (int i = 0; i < columns; ++i) {
01500         td->minimumWidth += td->minWidths.at(i) + td->border + cellSpacing + td->border;
01501     }
01502     td->minimumWidth += margin - td->border;
01503 
01504     td->maximumWidth = td->columnPositions.at(0);
01505     for (int i = 0; i < columns; ++i)
01506         if (td->maxWidths.at(i) != INT_MAX)
01507             td->maximumWidth += td->maxWidths.at(i) + td->border + cellSpacing + td->border;
01508     td->maximumWidth += margin - td->border;
01509 
01510     td->rowPositionsWithoutPageBreak = td->rowPositions;
01511 
01512     td->updateTableSize();
01513     td->sizeDirty = false;
01514     return QRectF(); // invalid rect -> update everything
01515 }
01516 
01517 void QTextDocumentLayoutPrivate::positionFloat(QTextFrame *frame, QTextLine *currentLine)
01518 {
01519     QTextFrameData *fd = data(frame);
01520 
01521     QTextFrame *parent = frame->parentFrame();
01522     Q_ASSERT(parent);
01523     QTextFrameData *pd = data(parent);
01524     Q_ASSERT(pd && pd->currentLayoutStruct);
01525 
01526     if (!pd->floats.contains(frame))
01527         pd->floats.append(frame);
01528     fd->layoutDirty = true;
01529     Q_ASSERT(!fd->sizeDirty);
01530 
01531 //     qDebug() << "positionFloat:" << frame << "width=" << fd->size.width();
01532     qreal y = pd->currentLayoutStruct->y;
01533     if (currentLine) {
01534         qreal left, right;
01535         floatMargins(y, pd->currentLayoutStruct, &left, &right);
01536 //         qDebug() << "have line: right=" << right << "left=" << left << "textWidth=" << currentLine->textWidth();
01537         if (right - left < currentLine->naturalTextWidth() + fd->size.width()) {
01538             pd->currentLayoutStruct->pendingFloats.append(frame);
01539 //             qDebug() << "    adding to pending list";
01540             return;
01541         }
01542     }
01543 
01544     if (!parent->parentFrame() /* float in root frame */
01545         && y + fd->size.height() > pd->currentLayoutStruct->pageBottom) {
01546         y = pd->currentLayoutStruct->pageBottom;
01547     }
01548 
01549     y = findY(y, pd->currentLayoutStruct, fd->size.width());
01550 
01551     qreal left, right;
01552     floatMargins(y, pd->currentLayoutStruct, &left, &right);
01553 
01554     if (fd->flow_position == QTextFrameFormat::FloatLeft)
01555         fd->position = QPointF(left, y);
01556     else
01557         fd->position = QPointF(right - fd->size.width(), y);
01558 
01559 //     qDebug()<< "float positioned at " << fd->position;
01560     fd->layoutDirty = false;
01561 }
01562 
01563 QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo)
01564 {
01565     LDEBUG << "layoutFrame (pre)";
01566     Q_ASSERT(data(f)->sizeDirty);
01567 //     qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame());
01568 
01569     QTextFrameFormat fformat = f->frameFormat();
01570 
01571     QTextFrame *parent = f->parentFrame();
01572     const QTextFrameData *pd = parent ? data(parent) : 0;
01573 
01574     const qreal maximumWidth = qMax(qreal(0), pd ? pd->contentsWidth : q_func()->document()->pageSize().width());
01575 
01576     const qreal width = fformat.width().value(maximumWidth);
01577 
01578     QTextLength height = fformat.height();
01579     qreal h = height.value(pd ? pd->contentsHeight : -1);
01580 
01581     return layoutFrame(f, layoutFrom, layoutTo, width, h);
01582 }
01583 
01584 QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, qreal frameWidth, qreal frameHeight)
01585 {
01586     LDEBUG << "layoutFrame from=" << layoutFrom << "to=" << layoutTo;
01587     Q_Q(QTextDocumentLayout);
01588     Q_ASSERT(data(f)->sizeDirty);
01589 //     qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame());
01590 
01591     QTextFrameData *fd = data(f);
01592     const qreal oldContentsWidth = fd->contentsWidth;
01593     qreal newContentsWidth;
01594 
01595     {
01596         QTextFrameFormat fformat = f->frameFormat();
01597         // set sizes of this frame from the format
01598         fd->margin = fformat.margin();
01599         fd->border = fformat.border();
01600         fd->padding = fformat.padding();
01601 
01602         newContentsWidth = frameWidth - 2*(fd->margin + fd->border + fd->padding);
01603 
01604         if (frameHeight != -1) {
01605             fd->contentsHeight = frameHeight - 2*(fd->margin + fd->border + fd->padding);
01606         } else {
01607             fd->contentsHeight = frameHeight;
01608         }
01609 
01610         fd->flow_position = fformat.position();
01611     }
01612 
01613     int startPos = f->firstPosition();
01614     int endPos = f->lastPosition();
01615     if (startPos > endPos) {
01616         fd->contentsWidth = newContentsWidth;
01617         // inline image
01618         QTextCharFormat format = q->format(startPos - 1);
01619         QTextObjectInterface *iface = q->handlerForObject(format.objectType());
01620         if (iface)
01621             fd->size = iface->intrinsicSize(q->document(), startPos - 1, format).toSize();
01622         fd->sizeDirty = false;
01623         return QRectF();
01624     }
01625 
01626     if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
01627         fd->contentsWidth = newContentsWidth;
01628         return layoutTable(table, layoutFrom, layoutTo);
01629     }
01630 
01631     // set fd->contentsWidth temporarily, so that layoutFrame for the children
01632     // picks the right width. We'll initialize it properly at the end of this
01633     // function.
01634     fd->contentsWidth = newContentsWidth;
01635     qreal maxChildFrameWidth = 0;
01636     // layout child frames
01637     QList<QTextFrame *> children = f->childFrames();
01638     for (int i = 0; i < children.size(); ++i) {
01639         QTextFrame *c = children.at(i);
01640         QTextFrameData *cd = data(c);
01641         if (cd->sizeDirty) {
01642             layoutFrame(c, layoutFrom, layoutTo);
01643         }
01644         maxChildFrameWidth = qMax(maxChildFrameWidth, cd->size.width());
01645     }
01646 
01647     qreal margin = fd->margin + fd->border + fd->padding;
01648     QLayoutStruct layoutStruct;
01649     layoutStruct.frame = f;
01650     layoutStruct.x_left = margin;
01651     layoutStruct.x_right = layoutStruct.x_left + newContentsWidth;
01652     layoutStruct.y = margin;
01653     layoutStruct.contentsWidth = 0;
01654     layoutStruct.minimumWidth = 0;
01655     layoutStruct.maximumWidth = INT_MAX;
01656     layoutStruct.fullLayout = oldContentsWidth != newContentsWidth;
01657     layoutStruct.updateRect = QRectF(QPointF(0, 0), QSizeF(INT_MAX, INT_MAX));
01658     LDEBUG << "layoutStruct: x_left" << layoutStruct.x_left << "x_right" << layoutStruct.x_right
01659            << "fullLayout" << layoutStruct.fullLayout;
01660 
01661     if (!f->parentFrame()) {
01662         layoutStruct.pageHeight = q->document()->pageSize().height();
01663         if (layoutStruct.pageHeight < 0)
01664             layoutStruct.pageHeight = INT_MAX;
01665         layoutStruct.pageBottom = layoutStruct.pageHeight - fd->margin;
01666         layoutStruct.pageMargin = fd->margin;
01667         idealWidth = 0; // reset
01668     }
01669 
01670     QTextFrame::Iterator it = f->begin();
01671     layoutFlow(it, &layoutStruct, layoutFrom, layoutTo);
01672 
01673     if (!f->parentFrame())
01674         idealWidth = qMax(maxChildFrameWidth, layoutStruct.contentsWidth);
01675 
01676     qreal actualWidth = qMax(newContentsWidth, qMax(maxChildFrameWidth, layoutStruct.contentsWidth));
01677     fd->contentsWidth = actualWidth;
01678     if (newContentsWidth <= 0) { // nowrap layout?
01679         fd->contentsWidth = newContentsWidth;
01680     }
01681 
01682     fd->minimumWidth = layoutStruct.minimumWidth;
01683     fd->maximumWidth = layoutStruct.maximumWidth;
01684 
01685     qreal height = fd->contentsHeight == -1
01686                  ? layoutStruct.y + margin
01687                  : fd->contentsHeight + 2*margin;
01688     fd->size = QSizeF(actualWidth + 2*margin, height);
01689     fd->sizeDirty = false;
01690     return layoutStruct.updateRect;
01691 }
01692 
01693 void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QLayoutStruct *layoutStruct,
01694                                             int layoutFrom, int layoutTo)
01695 {
01696     Q_Q(QTextDocumentLayout);
01697     LDEBUG << "layoutFlow from=" << layoutFrom << "to=" << layoutTo;
01698     QTextFrameData *fd = data(layoutStruct->frame);
01699 
01700     fd->currentLayoutStruct = layoutStruct;
01701 
01702     QTextFrame::Iterator previousIt;
01703 
01704     const bool inRootFrame = (it.parentFrame() == q->document()->rootFrame());
01705     if (inRootFrame) {
01706         bool redoCheckPoints = layoutStruct->fullLayout || checkPoints.isEmpty();
01707 
01708         if (!redoCheckPoints) {
01709             QVector<QCheckPoint>::Iterator checkPoint = qLowerBound(checkPoints.begin(), checkPoints.end(), layoutFrom);
01710             if (checkPoint != checkPoints.end()) {
01711                 if (checkPoint != checkPoints.begin())
01712                     --checkPoint;
01713 
01714                 layoutStruct->y = checkPoint->y;
01715                 layoutStruct->minimumWidth = checkPoint->minimumWidth;
01716                 layoutStruct->maximumWidth = checkPoint->maximumWidth;
01717                 layoutStruct->contentsWidth = checkPoint->contentsWidth;
01718 
01719                 if (layoutStruct->pageHeight > 0.0) {
01720                     int page = int(layoutStruct->y / layoutStruct->pageHeight);
01721                     layoutStruct->pageBottom = (page + 1) * layoutStruct->pageHeight - layoutStruct->pageMargin;
01722                 }
01723 
01724                 it = frameIteratorForTextPosition(checkPoint->positionInFrame);
01725                 checkPoints.resize(checkPoint - checkPoints.begin() + 1);
01726 
01727                 if (checkPoint != checkPoints.begin()) {
01728                     previousIt = it;
01729                     --previousIt;
01730                 }
01731             } else {
01732                 redoCheckPoints = true;
01733             }
01734         }
01735 
01736         if (redoCheckPoints) {
01737             checkPoints.clear();
01738             QCheckPoint cp;
01739             cp.y = layoutStruct->y;
01740             cp.positionInFrame = 0;
01741             cp.minimumWidth = layoutStruct->minimumWidth;
01742             cp.maximumWidth = layoutStruct->maximumWidth;
01743             cp.contentsWidth = layoutStruct->contentsWidth;
01744             checkPoints.append(cp);
01745         }
01746     }
01747 
01748     while (!it.atEnd()) {
01749         QTextFrame *c = it.currentFrame();
01750 
01751         if (inRootFrame) {
01752             int docPos;
01753             if (it.currentFrame())
01754                 docPos = it.currentFrame()->firstPosition();
01755             else
01756                 docPos = it.currentBlock().position();
01757 
01758             if (qAbs(layoutStruct->y - checkPoints.last().y) > 2000) {
01759                 qreal left, right;
01760                 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
01761                 if (left == layoutStruct->x_left && right == layoutStruct->x_right) {
01762                     QCheckPoint p;
01763                     p.y = layoutStruct->y;
01764                     p.positionInFrame = docPos;
01765                     p.minimumWidth = layoutStruct->minimumWidth;
01766                     p.maximumWidth = layoutStruct->maximumWidth;
01767                     p.contentsWidth = layoutStruct->contentsWidth;
01768                     checkPoints.append(p);
01769 
01770                     if (currentLazyLayoutPosition != -1
01771                         && docPos > currentLazyLayoutPosition + lazyLayoutStepSize)
01772                         break;
01773 
01774                     if (layoutTo != -1
01775                         && docPos > layoutTo)
01776                         break;
01777                 }
01778             }
01779         }
01780 
01781         if (c) {
01782             // position child frame
01783             QTextFrameData *cd = data(c);
01784             Q_ASSERT(!cd->sizeDirty);
01785             if (cd->flow_position == QTextFrameFormat::InFlow) {
01786                 if (c->frameFormat().pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
01787                     layoutStruct->newPage();
01788 
01789                 qreal left, right;
01790                 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
01791                 left = qMax(left, layoutStruct->x_left);
01792                 right = qMin(right, layoutStruct->x_right);
01793 
01794                 if (right - left < cd->size.width()) {
01795                     layoutStruct->y = findY(layoutStruct->y, layoutStruct, cd->size.width());
01796                     floatMargins(layoutStruct->y, layoutStruct, &left, &right);
01797                 }
01798 
01799                 QPointF pos(left, layoutStruct->y);
01800 
01801                 Qt::Alignment align = Qt::AlignLeft;
01802 
01803                 QTextTable *table = qobject_cast<QTextTable *>(c);
01804 
01805                 if (table)
01806                     align = table->format().alignment() & Qt::AlignHorizontal_Mask;
01807 
01808                 // align only if there is space for alignment
01809                 if (right - left > cd->size.width()) {
01810                     if (align & Qt::AlignRight)
01811                         pos.rx() += layoutStruct->x_right - cd->size.width();
01812                     else if (align & Qt::AlignHCenter)
01813                         pos.rx() += (layoutStruct->x_right - cd->size.width()) / 2;
01814                 }
01815 
01816                 cd->position = pos;
01817                 layoutStruct->y += cd->size.height();
01818                 cd->layoutDirty = false;
01819 
01820                 if (table) {
01821                     QTextTableData *td = static_cast<QTextTableData *>(data(table));
01822                     // if the table was previously broken across a page boundary
01823                     // (due to lazy layouting) then we need to reset the row positions
01824                     // and the table height (from the row positions) and call
01825                     // pageBreakInsideTable again.
01826                     if (!td->rowsAfterPageBreak.isEmpty()) {
01827                         td->rowsAfterPageBreak.clear();
01828                         td->rowPositions = td->rowPositionsWithoutPageBreak;
01829                         td->updateTableSize();
01830                     }
01831                 }
01832 
01833                 if (inRootFrame
01834                     && cd->position.y() + cd->size.height() > layoutStruct->pageBottom
01835                    ) {
01836 
01837                     if (table && cd->size.height() > layoutStruct->pageHeight / 2) {
01838                         pageBreakInsideTable(table, layoutStruct);
01839                     } else {
01840                         layoutStruct->newPage();
01841                         cd->position.setY(layoutStruct->y);
01842                         layoutStruct->y += cd->size.height();
01843                     }
01844                 }
01845 
01846                 if (c->frameFormat().pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
01847                     layoutStruct->newPage();
01848             } else {
01849                 positionFloat(c);
01850             }
01851             previousIt = it;
01852             ++it;
01853         } else {
01854             QTextFrame::Iterator lastIt;
01855             if (!previousIt.atEnd())
01856                 lastIt = previousIt;
01857             previousIt = it;
01858             QTextBlock block = it.currentBlock();
01859             ++it;
01860 
01861             if (block.blockFormat().pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore)
01862                 layoutStruct->newPage();
01863 
01864             const qreal origY = layoutStruct->y;
01865 
01866             // layout and position child block
01867             layoutBlock(block, layoutStruct, layoutFrom, layoutTo, lastIt.currentBlock());
01868 
01869             // if the block right before a table is empty 'hide' it by
01870             // positioning it into the table border
01871             if (isEmptyBlockBeforeTable(block, it)) {
01872                 layoutStruct->y = origY;
01873                 continue;
01874             }
01875 
01876             // if the block right after a table is empty then 'hide' it, too
01877             if (isEmptyBlockAfterTable(block, lastIt.currentFrame())) {
01878                 QTextTableData *td = static_cast<QTextTableData *>(data(lastIt.currentFrame()));
01879                 QTextLayout *layout = block.layout();
01880 
01881                 QPointF pos(td->position.x() + td->size.width(),
01882                             td->position.y() + td->size.height() - layout->boundingRect().height());
01883 
01884                 layout->setPosition(pos);
01885                 layoutStruct->y = origY;
01886             }
01887 
01888             if (block.blockFormat().pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter)
01889                 layoutStruct->newPage();
01890         }
01891     }
01892 
01893     // a float at the bottom of a frame may make it taller, hence the qMax() for layoutStruct->y.
01894     // we don't need to do it for tables though because floats in tables are per table
01895     // and not per cell and layoutCell already takes care of doing the same as we do here
01896     if (!qobject_cast<QTextTable *>(layoutStruct->frame)) {
01897         QList<QTextFrame *> children = layoutStruct->frame->childFrames();
01898         for (int i = 0; i < children.count(); ++i) {
01899             QTextFrameData *fd = data(children.at(i));
01900             if (!fd->layoutDirty && fd->flow_position != QTextFrameFormat::InFlow)
01901                 layoutStruct->y = qMax(layoutStruct->y, fd->position.y() + fd->size.height());
01902         }
01903     }
01904 
01905     if (inRootFrame) {
01906         if (it.atEnd()) {
01907             //qDebug() << "layout done!";
01908             currentLazyLayoutPosition = -1;
01909             QCheckPoint cp;
01910             cp.y = layoutStruct->y;
01911             cp.positionInFrame = q->document()->docHandle()->length();
01912             cp.minimumWidth = layoutStruct->minimumWidth;
01913             cp.maximumWidth = layoutStruct->maximumWidth;
01914             cp.contentsWidth = layoutStruct->contentsWidth;
01915             checkPoints.append(cp);
01916         } else {
01917             currentLazyLayoutPosition = checkPoints.last().positionInFrame;
01918             // #######
01919             //checkPoints.last().positionInFrame = q->document()->docHandle()->length();
01920         }
01921     }
01922 
01923 
01924     fd->currentLayoutStruct = 0;
01925 }
01926 
01927 void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, QLayoutStruct *layoutStruct,
01928                                              int layoutFrom, int layoutTo, const QTextBlock &previousBlock)
01929 {
01930     Q_Q(QTextDocumentLayout);
01931 
01932     const QTextDocument *doc = q->document();
01933     QTextBlockFormat blockFormat = bl.blockFormat();
01934     QTextLayout *tl = bl.layout();
01935 
01936     LDEBUG << "layoutBlock from=" << layoutFrom << "to=" << layoutTo;
01937 
01938     Qt::LayoutDirection dir = blockFormat.layoutDirection();
01939     if (blockTextFlags & ((int)QTextDocumentLayout::LTR|(int)QTextDocumentLayout::RTL)) {
01940         if (!blockFormat.hasProperty(QTextFormat::LayoutDirection))
01941             dir = blockTextFlags & QTextDocumentLayout::LTR ? Qt::LeftToRight : Qt::RightToLeft;
01942     }
01943     Qt::Alignment align = QStyle::visualAlignment(dir, blockFormat.alignment());
01944     if (blockTextFlags & Qt::AlignHorizontal_Mask) {
01945         if (!blockFormat.hasProperty(QTextFormat::BlockAlignment))
01946             align = (Qt::Alignment)(blockTextFlags & Qt::AlignHorizontal_Mask);
01947     }
01948     QTextOption option(align);
01949     option.setTextDirection(dir);
01950     if (blockTextFlags & Qt::TextSingleLine
01951         || blockFormat.nonBreakableLines()
01952         || doc->pageSize().width() < 0)
01953         option.setWrapMode(QTextOption::ManualWrap);
01954     else
01955         option.setWrapMode(wordWrapMode);
01956     option.setTabStop(tabStopWidth);
01957     option.setUseDesignMetrics(doc->useDesignMetrics());
01958     tl->setTextOption(option);
01959 
01960     const bool haveWordOrAnyWrapMode = (option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere);
01961 
01962 //    qDebug() << "layoutBlock; width" << layoutStruct->x_right - layoutStruct->x_left << "(maxWidth is btw" << tl->maximumWidth() << ")";
01963 
01964     if (previousBlock.isValid()) {
01965         qreal margin = qMax(blockFormat.topMargin(), previousBlock.blockFormat().bottomMargin());
01966         if (margin > 0 && q->paintDevice()) {
01967             extern int qt_defaultDpi();
01968             margin *= qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi());
01969         }
01970         layoutStruct->y += margin;
01971     }
01972 
01973     //QTextFrameData *fd = data(layoutStruct->frame);
01974 
01975     const qreal indent = this->indent(bl);
01976     const qreal totalLeftMargin = blockFormat.leftMargin() + (dir == Qt::RightToLeft ? 0 : indent);
01977     const qreal totalRightMargin = blockFormat.rightMargin() + (dir == Qt::RightToLeft ? indent : 0);
01978 
01979     const QPointF oldPosition = tl->position();
01980     tl->setPosition(QPointF(layoutStruct->x_left, layoutStruct->y));
01981 
01982     if (layoutStruct->fullLayout
01983         || (bl.position() + bl.length() > layoutFrom && bl.position() <= layoutTo)
01984         // force relayout if we cross a page boundary
01985         || (layoutStruct->pageHeight > 0.0 && layoutStruct->y + tl->boundingRect().height() > layoutStruct->pageBottom)) {
01986 
01987 //         qDebug() << "    layouting block at" << bl.position();
01988         const qreal cy = layoutStruct->y;
01989         const qreal l = layoutStruct->x_left  + totalLeftMargin;
01990         const qreal r = layoutStruct->x_right - totalRightMargin;
01991 
01992         tl->beginLayout();
01993         bool firstLine = true;
01994         while (1) {
01995             QTextLine line = tl->createLine();
01996             if (!line.isValid())
01997                 break;
01998 
01999             qreal left, right;
02000             floatMargins(layoutStruct->y, layoutStruct, &left, &right);
02001             left = qMax(left, l);
02002             right = qMin(right, r);
02003             qreal text_indent = 0;
02004             if (firstLine) {
02005                 text_indent = blockFormat.textIndent();
02006                 if (dir == Qt::LeftToRight)
02007                     left += text_indent;
02008                 else
02009                     right -= text_indent;
02010                 firstLine = false;
02011             }
02012 //         qDebug() << "layout line y=" << currentYPos << "left=" << left << "right=" <<right;
02013 
02014             if (fixedColumnWidth != -1)
02015                 line.setNumColumns(fixedColumnWidth);
02016             else
02017                 line.setLineWidth(right - left);
02018 
02019 //        qDebug() << "layoutBlock; layouting line with width" << right - left << "->textWidth" << line.textWidth();
02020             floatMargins(layoutStruct->y, layoutStruct, &left, &right);
02021             left = qMax(left, l);
02022             right = qMin(right, r);
02023             if (dir == Qt::LeftToRight)
02024                 left += text_indent;
02025             else
02026                 right -= text_indent;
02027 
02028             if (fixedColumnWidth == -1 && line.naturalTextWidth() > right-left) {
02029                 // float has been added in the meantime, redo
02030                 layoutStruct->pendingFloats.clear();
02031 
02032                 if (haveWordOrAnyWrapMode) {
02033                     option.setWrapMode(QTextOption::WrapAnywhere);
02034                     tl->setTextOption(option);
02035                 }
02036 
02037                 line.setLineWidth(right-left);
02038                 if (line.naturalTextWidth() > right-left) {
02039                     layoutStruct->pendingFloats.clear();
02040                     // lines min width more than what we have
02041                     layoutStruct->y = findY(layoutStruct->y, layoutStruct, line.naturalTextWidth());
02042                     floatMargins(layoutStruct->y, layoutStruct, &left, &right);
02043                     left = qMax(left, l);
02044                     right = qMin(right, r);
02045                     if (dir == Qt::LeftToRight)
02046                         left += text_indent;
02047                     else
02048                         right -= text_indent;
02049                     line.setLineWidth(qMax<qreal>(line.naturalTextWidth(), right-left));
02050                 }
02051 
02052                 if (haveWordOrAnyWrapMode) {
02053                     option.setWrapMode(QTextOption::WordWrap);
02054                     tl->setTextOption(option);
02055                 }
02056             }
02057 
02058             qreal lineHeight = line.height();
02059             if (layoutStruct->pageHeight > 0.0 && layoutStruct->y + lineHeight > layoutStruct->pageBottom) {
02060                 layoutStruct->newPage();
02061 
02062                 floatMargins(layoutStruct->y, layoutStruct, &left, &right);
02063                 left = qMax(left, l);
02064                 right = qMin(right, r);
02065                 if (dir == Qt::LeftToRight)
02066                     left += text_indent;
02067                 else
02068                     right -= text_indent;
02069             }
02070 
02071             line.setPosition(QPointF(left - layoutStruct->x_left, layoutStruct->y - cy));
02072             layoutStruct->y += lineHeight;
02073             layoutStruct->contentsWidth
02074                 = qMax<qreal>(layoutStruct->contentsWidth, line.x() + line.naturalTextWidth() + totalRightMargin);
02075 
02076             // position floats
02077             for (int i = 0; i < layoutStruct->pendingFloats.size(); ++i) {
02078                 QTextFrame *f = layoutStruct->pendingFloats.at(i);
02079                 positionFloat(f);
02080             }
02081             layoutStruct->pendingFloats.clear();
02082         }
02083         tl->endLayout();
02084     } else {
02085         const int cnt = tl->lineCount();
02086         for (int i = 0; i < cnt; ++i) {
02087             QTextLine line = tl->lineAt(i);
02088             layoutStruct->contentsWidth
02089                 = qMax(layoutStruct->contentsWidth, line.x() + tl->lineAt(i).naturalTextWidth() + totalRightMargin);
02090             const qreal lineHeight = line.height();
02091             if (layoutStruct->pageHeight > 0.0 && layoutStruct->y + lineHeight > layoutStruct->pageBottom)
02092                 layoutStruct->newPage();
02093             line.setPosition(QPointF(line.position().x(), layoutStruct->y - tl->position().y()));
02094             layoutStruct->y += lineHeight;
02095         }
02096         if (layoutStruct->updateRect.isValid()
02097             && bl.length() > 1) {
02098             if (layoutFrom >= bl.position() + bl.length()) {
02099                 // if our height didn't change and the change in the document is
02100                 // in one of the later paragraphs, then we don't need to repaint
02101                 // this one
02102                 layoutStruct->updateRect.setTop(qMax(layoutStruct->updateRect.top(), layoutStruct->y));
02103             } else if (layoutTo < bl.position()
02104                        && oldPosition == tl->position()) {
02105                 // if the change in the document happened earlier in the document
02106                 // and our position did /not/ change because none of the earlier paragraphs
02107                 // or frames changed their height, then we don't need to repaint
02108                 // this one
02109                 layoutStruct->updateRect.setBottom(qMin(layoutStruct->updateRect.bottom(), tl->position().y()));
02110             }
02111         }
02112     }
02113 
02114     // ### doesn't take floats into account. would need to do it per line. but how to retrieve then? (Simon)
02115     layoutStruct->minimumWidth = qMax(layoutStruct->minimumWidth, tl->minimumWidth() + blockFormat.leftMargin() + indent);
02116 
02117     const qreal maxW = tl->maximumWidth() + blockFormat.leftMargin() + indent;
02118     if (maxW > 0) {
02119         if (layoutStruct->maximumWidth == INT_MAX)
02120             layoutStruct->maximumWidth = maxW;
02121         else
02122             layoutStruct->maximumWidth = qMax(layoutStruct->maximumWidth, maxW);
02123     }
02124 }
02125 
02126 void QTextDocumentLayoutPrivate::pageBreakInsideTable(QTextTable *table, QLayoutStruct *layoutStruct)
02127 {
02128     QTextTableData *td = static_cast<QTextTableData *>(data(table));
02129     const int rows = table->rows();
02130     Q_ASSERT(rows > 0);
02131     qreal origY = td->position.y();
02132     // y positions relative to top of table, as rowPositions is relative, too
02133     qreal pageBottom = layoutStruct->pageBottom - td->position.y();
02134     // distance from cell content boundary (top or bottom) to end of table (top or bottom)
02135     const qreal extraTableHeight = td->padding + td->border + td->cellSpacing // inter cell spacing
02136                                    + td->margin + td->border + td->padding; // effective table margin
02137 
02138     td->rowsAfterPageBreak.clear();
02139 
02140     qreal tableHeaderHeight = 0;
02141     const QTextTableFormat format = table->format();
02142     const int headerRowCount = qMin(format.headerRowCount(), rows - 1);
02143     if (headerRowCount > 0)
02144         tableHeaderHeight = td->rowPositions.at(headerRowCount) - td->rowPositions.at(0);
02145 
02146     // if the header and the first row of data is already taller than the remaining height
02147     // move the whole table to the next page
02148     if (tableHeaderHeight + td->rowPositions.at(headerRowCount) + td->heights.at(headerRowCount) + extraTableHeight > pageBottom) {
02149         layoutStruct->newPage();
02150         origY = layoutStruct->y;
02151         td->position.setY(layoutStruct->y);
02152         pageBottom = layoutStruct->pageBottom - td->position.y();
02153     }
02154 
02155     qreal offset = 0.0;
02156     for (int r = 2; r < rows; ++r) {
02157         if (td->rowPositions[r] + offset > pageBottom) {
02158             offset += pageBottom - td->rowPositions[r - 1] + 2 * layoutStruct->pageMargin;
02159             offset += extraTableHeight; // make sure there's enough space for the table margin/border
02160             offset += tableHeaderHeight;
02161             layoutStruct->newPage();
02162             td->rowPositions[r - 1] = layoutStruct->y + extraTableHeight + tableHeaderHeight - td->position.y();
02163 
02164             td->rowsAfterPageBreak.append(r - 1);
02165             pageBottom = layoutStruct->pageBottom - td->position.y();
02166         }
02167         td->rowPositions[r] += offset;
02168     }
02169 
02170     if (rows > 1 && td->rowPositions.last() + td->heights.last() + extraTableHeight > pageBottom) {
02171         td->rowsAfterPageBreak.append(rows - 1);
02172         layoutStruct->newPage();
02173         td->rowPositions.last() = layoutStruct->y + extraTableHeight - td->position.y();
02174     }
02175 
02176     // calc new total height of table
02177     td->updateTableSize();
02178     layoutStruct->y = origY + td->size.height();
02179 }
02180 
02181 void QTextDocumentLayoutPrivate::floatMargins(qreal y, const QLayoutStruct *layoutStruct,
02182                                               qreal *left, qreal *right) const
02183 {
02184 //     qDebug() << "floatMargins y=" << y;
02185     *left = layoutStruct->x_left;
02186     *right = layoutStruct->x_right;
02187     QTextFrameData *lfd = data(layoutStruct->frame);
02188     for (int i = 0; i < lfd->floats.size(); ++i) {
02189         QTextFrameData *fd = data(lfd->floats.at(i));
02190         if (!fd->layoutDirty) {
02191             if (fd->position.y() <= y && fd->position.y() + fd->size.height() > y) {
02192 //                 qDebug() << "adjusting with float" << f << fd->position.x()<< fd->size.width();
02193                 if (fd->flow_position == QTextFrameFormat::FloatLeft)
02194                     *left = qMax(*left, fd->position.x() + fd->size.width());
02195                 else
02196                     *right = qMin(*right, fd->position.x());
02197             }
02198         }
02199     }
02200 //     qDebug() << "floatMargins: left="<<*left<<"right="<<*right<<"y="<<y;
02201 }
02202 
02203 
02204 qreal QTextDocumentLayoutPrivate::findY(qreal yFrom, const QLayoutStruct *layoutStruct, qreal requiredWidth) const
02205 {
02206     qreal right, left;
02207     requiredWidth = qMin(requiredWidth, layoutStruct->x_right - layoutStruct->x_left);
02208 
02209 //     qDebug() << "findY:" << yFrom;
02210     while (1) {
02211         floatMargins(yFrom, layoutStruct, &left, &right);
02212 //         qDebug() << "    yFrom=" << yFrom<<"right=" << right << "left=" << left << "requiredWidth=" << requiredWidth;
02213         if (right-left >= requiredWidth)
02214             break;
02215 
02216         // move float down until we find enough space
02217         qreal newY = INT_MAX;
02218         QTextFrameData *lfd = data(layoutStruct->frame);
02219         for (int i = 0; i < lfd->floats.size(); ++i) {
02220             QTextFrameData *fd = data(lfd->floats.at(i));
02221             if (!fd->layoutDirty) {
02222                 if (fd->position.y() <= yFrom && fd->position.y() + fd->size.height() > yFrom)
02223                     newY = qMin(newY, fd->position.y() + fd->size.height());
02224             }
02225         }
02226         if (newY == INT_MAX)
02227             break;
02228         yFrom = newY;
02229     }
02230     return yFrom;
02231 }
02232 
02233 QTextDocumentLayout::QTextDocumentLayout(QTextDocument *doc)
02234     : QAbstractTextDocumentLayout(*new QTextDocumentLayoutPrivate, doc)
02235 {
02236     registerHandler(QTextFormat::ImageObject, new QTextImageHandler(this));
02237 }
02238 
02239 
02240 void QTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
02241 {
02242     Q_D(QTextDocumentLayout);
02243     QTextFrame *frame = document()->rootFrame();
02244     if(data(frame)->sizeDirty)
02245         return;
02246     if (context.clip.isValid()) {
02247         d->ensureLayouted(context.clip.bottom());
02248     } else {
02249         d->ensureLayoutFinished();
02250     }
02251     d->drawFrame(QPointF(), painter, context, frame);
02252 }
02253 
02254 static void markFrames(QTextFrame *current, int from, int oldLength, int length)
02255 {
02256     int end = qMax(oldLength, length) + from;
02257 
02258     if (current->firstPosition() >= end || current->lastPosition() < from)
02259         return;
02260 
02261     QTextFrameData *fd = data(current);
02262     for (int i = 0; i < fd->floats.size(); ++i) {
02263         QTextFrame *f = fd->floats[i];
02264         if (!f) {
02265             // float got removed in editing operation
02266             fd->floats.removeAt(i);
02267             --i;
02268         }
02269     }
02270 
02271     fd->layoutDirty = true;
02272     fd->sizeDirty = true;
02273 
02274 //     qDebug("    marking frame (%d--%d) as dirty", current->firstPosition(), current->lastPosition());
02275     QList<QTextFrame *> children = current->childFrames();
02276     for (int i = 0; i < children.size(); ++i)
02277         markFrames(children.at(i), from, oldLength, length);
02278 }
02279 
02280 void QTextDocumentLayout::documentChanged(int from, int oldLength, int length)
02281 {
02282     Q_D(QTextDocumentLayout);
02283 
02284     const QSizeF pageSize = document()->pageSize();
02285     if (pageSize.isNull())
02286         return;
02287 
02288     QRectF updateRect;
02289 
02290     const QSizeF oldSize = dynamicDocumentSize();
02291 
02292     d->lazyLayoutStepSize = 1000;
02293     d->sizeChangedTimer.stop();
02294     d->insideDocumentChange = true;
02295 
02296     const int documentLength = document()->docHandle()->length();
02297     const bool fullLayout = (oldLength == 0 && length == documentLength);
02298     const bool smallChange = documentLength > 0
02299                              && (qMax(length, oldLength) * 100 / documentLength) < 5;
02300 
02301     // don't show incremental layout progress (avoid scrollbar flicker)
02302     // if we see only a small change in the document and we're either starting
02303     // a layout run or we're already in progress for that and we haven't seen
02304     // any bigger change previously (showLayoutProgress already false)
02305     if (smallChange
02306         && (d->currentLazyLayoutPosition == -1 || d->showLayoutProgress == false))
02307         d->showLayoutProgress = false;
02308     else
02309         d->showLayoutProgress = true;
02310 
02311     if (fullLayout) {
02312         d->currentLazyLayoutPosition = 0;
02313         d->checkPoints.clear();
02314         d->layoutStep();
02315     } else {
02316         d->ensureLayoutedByPosition(from);
02317         updateRect = doLayout(from, oldLength, length);
02318     }
02319 
02320     if (!d->layoutTimer.isActive() && d->currentLazyLayoutPosition != -1)
02321         d->layoutTimer.start(10, this);
02322 
02323     d->insideDocumentChange = false;
02324 
02325     if (d->showLayoutProgress) {
02326         const QSizeF newSize = dynamicDocumentSize();
02327         if (newSize != oldSize)
02328             emit documentSizeChanged(newSize);
02329     }
02330 
02331     if (!updateRect.isValid()) {
02332         // don't use the frame size, it might have shrunken
02333         updateRect = QRectF(QPointF(0, 0), QSizeF(INT_MAX, INT_MAX));
02334     }
02335 
02336     emit update(updateRect);
02337 }
02338 
02339 QRectF QTextDocumentLayout::doLayout(int from, int oldLength, int length)
02340 {
02341     Q_D(QTextDocumentLayout);
02342 
02343 //     qDebug("documentChange: from=%d, oldLength=%d, length=%d", from, oldLength, length);
02344 
02345     // mark all frames between f_start and f_end as dirty
02346     markFrames(document()->rootFrame(), from, oldLength, length);
02347 
02348     QRectF updateRect;
02349 
02350     QTextFrame *root = document()->rootFrame();
02351     if(data(root)->sizeDirty)
02352         updateRect = d->layoutFrame(root, from, from + length);
02353     data(root)->layoutDirty = false;
02354 
02355     if (d->currentLazyLayoutPosition == -1)
02356         layoutFinished();
02357     else if (d->showLayoutProgress)
02358         d->sizeChangedTimer.start(0, this);
02359 
02360     return updateRect;
02361 }
02362 
02363 int QTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
02364 {
02365     Q_D(const QTextDocumentLayout);
02366     d->ensureLayouted(point.y());
02367     QTextFrame *f = document()->rootFrame();
02368     int position = 0;
02369     QTextLayout *l = 0;
02370     QTextDocumentLayoutPrivate::HitPoint p = d->hitTest(f, point, &position, &l);
02371     if (accuracy == Qt::ExactHit && p < QTextDocumentLayoutPrivate::PointExact)
02372         return -1;
02373 
02374     // ensure we stay within document bounds
02375     int lastPos = f->lastPosition();
02376     if (l && !l->preeditAreaText().isEmpty())
02377         lastPos += l->preeditAreaText().length();
02378     if (position > lastPos)
02379         position = lastPos;
02380     else if (position < 0)
02381         position = 0;
02382 
02383     return position;
02384 }
02385 
02386 void QTextDocumentLayout::resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
02387 {
02388     Q_D(QTextDocumentLayout);
02389     QTextCharFormat f = format.toCharFormat();
02390     Q_ASSERT(f.isValid());
02391     QTextObjectHandler handler = d->handlers.value(f.objectType());
02392     if (!handler.component)
02393         return;
02394 
02395     QSizeF intrinsic = handler.iface->intrinsicSize(document(), posInDocument, format);
02396 
02397     QTextFrameFormat::Position pos = QTextFrameFormat::InFlow;
02398     QTextFrame *frame = qobject_cast<QTextFrame *>(document()->objectForFormat(f));
02399     if (frame) {
02400         pos = frame->frameFormat().position();
02401         data(frame)->sizeDirty = false;
02402         data(frame)->size = intrinsic.toSize();
02403     }
02404 
02405     item.setDescent(0);
02406     QSizeF inlineSize = (pos == QTextFrameFormat::InFlow ? intrinsic : QSizeF(0, 0));
02407     item.setWidth(inlineSize.width());
02408     item.setAscent(inlineSize.height());
02409 }
02410 
02411 void QTextDocumentLayout::positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
02412 {
02413     Q_D(QTextDocumentLayout);
02414     Q_UNUSED(posInDocument);
02415     if (item.width() != 0)
02416         // inline
02417         return;
02418 
02419     QTextCharFormat f = format.toCharFormat();
02420     Q_ASSERT(f.isValid());
02421     QTextObjectHandler handler = d->handlers.value(f.objectType());
02422     if (!handler.component)
02423         return;
02424 
02425     QTextFrame *frame = qobject_cast<QTextFrame *>(document()->objectForFormat(f));
02426     if (!frame)
02427         return;
02428 
02429     QTextBlock b = document()->findBlock(frame->firstPosition());
02430     QTextLine line;
02431     if (b.position() <= frame->firstPosition() && b.position() + b.length() > frame->lastPosition())
02432         line = b.layout()->lineAt(b.layout()->lineCount()-1);
02433 //     qDebug() << "layoutObject: line.isValid" << line.isValid() << b.position() << b.length() <<
02434 //         frame->firstPosition() << frame->lastPosition();
02435     d->positionFloat(frame, line.isValid() ? &line : 0);
02436 }
02437 
02438 void QTextDocumentLayout::drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item,
02439                                            int posInDocument, const QTextFormat &format)
02440 {
02441     QTextCharFormat f = format.toCharFormat();
02442     Q_ASSERT(f.isValid());
02443     QTextFrame *frame = qobject_cast<QTextFrame *>(document()->objectForFormat(f));
02444     QRectF r = rect;
02445     if (frame) {
02446         QTextFrameData *fd = data(frame);
02447         if (fd->flow_position != QTextFrameFormat::InFlow) {
02448             r = QRectF(fd->position, fd->size);
02449             r.translate(data(frame->parentFrame())->position);
02450         }
02451     }
02452 //    qDebug() << "drawObject at" << r;
02453     QAbstractTextDocumentLayout::drawInlineObject(p, r, item, posInDocument, format);
02454 }
02455 
02456 int QTextDocumentLayout::dynamicPageCount() const
02457 {
02458     const QSizeF pgSize = document()->pageSize();
02459     if (pgSize.height() < 0)
02460         return 1;
02461     return (int)(dynamicDocumentSize().height()
02462                  / pgSize.height()) + 1;
02463 }
02464 
02465 QSizeF QTextDocumentLayout::dynamicDocumentSize() const
02466 {
02467     return data(document()->rootFrame())->size;
02468 }
02469 
02470 int QTextDocumentLayout::pageCount() const
02471 {
02472     Q_D(const QTextDocumentLayout);
02473     d->ensureLayoutFinished();
02474     return dynamicPageCount();
02475 }
02476 
02477 QSizeF QTextDocumentLayout::documentSize() const
02478 {
02479     Q_D(const QTextDocumentLayout);
02480     d->ensureLayoutFinished();
02481     return dynamicDocumentSize();
02482 }
02483 
02484 void QTextDocumentLayoutPrivate::ensureLayouted(qreal y) const
02485 {
02486     Q_Q(const QTextDocumentLayout);
02487     if (currentLazyLayoutPosition == -1)
02488         return;
02489     const QSizeF oldSize = q->dynamicDocumentSize();
02490 
02491     if (checkPoints.isEmpty())
02492         layoutStep();
02493 
02494     while (currentLazyLayoutPosition != -1
02495            && checkPoints.last().y < y)
02496         layoutStep();
02497 }
02498 
02499 void QTextDocumentLayoutPrivate::ensureLayoutedByPosition(int position) const
02500 {
02501     if (currentLazyLayoutPosition == -1)
02502         return;
02503     if (position < currentLazyLayoutPosition)
02504         return;
02505     while (currentLazyLayoutPosition != -1
02506            && currentLazyLayoutPosition < position) {
02507         const_cast<QTextDocumentLayout *>(q_func())->doLayout(currentLazyLayoutPosition, 0, INT_MAX - currentLazyLayoutPosition);
02508     }
02509 }
02510 
02511 void QTextDocumentLayoutPrivate::layoutStep() const
02512 {
02513     ensureLayoutedByPosition(currentLazyLayoutPosition + lazyLayoutStepSize);
02514     lazyLayoutStepSize = qMin(200000, lazyLayoutStepSize * 2);
02515 }
02516 
02517 void QTextDocumentLayout::setBlockTextFlags(int flags)
02518 {
02519     Q_D(QTextDocumentLayout);
02520     d->blockTextFlags = flags;
02521 }
02522 
02523 int QTextDocumentLayout::blockTextFlags() const
02524 {
02525     Q_D(const QTextDocumentLayout);
02526     return d->blockTextFlags;
02527 }
02528 
02529 void QTextDocumentLayout::setWordWrapMode(QTextOption::WrapMode mode)
02530 {
02531     Q_D(QTextDocumentLayout);
02532     d->wordWrapMode = mode;
02533 }
02534 
02535 QTextOption::WrapMode QTextDocumentLayout::wordWrapMode() const
02536 {
02537     Q_D(const QTextDocumentLayout);
02538     return d->wordWrapMode;
02539 }
02540 
02541 void QTextDocumentLayout::setTabStopWidth(double width)
02542 {
02543     Q_D(QTextDocumentLayout);
02544     if (width < 0)
02545         return;
02546     d->tabStopWidth = width;
02547 }
02548 
02549 double QTextDocumentLayout::tabStopWidth() const
02550 {
02551     Q_D(const QTextDocumentLayout);
02552     return d->tabStopWidth;
02553 }
02554 
02555 void QTextDocumentLayout::setCursorWidth(int width)
02556 {
02557     Q_D(QTextDocumentLayout);
02558     d->cursorWidth = width;
02559 }
02560 
02561 int QTextDocumentLayout::cursorWidth() const
02562 {
02563     Q_D(const QTextDocumentLayout);
02564     return d->cursorWidth;
02565 }
02566 
02567 void QTextDocumentLayout::setFixedColumnWidth(int width)
02568 {
02569     Q_D(QTextDocumentLayout);
02570     d->fixedColumnWidth = width;
02571 }
02572 
02573 QRectF QTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
02574 {
02575     QPointF pos;
02576     d_func()->ensureLayoutFinished();
02577     const int framePos = frame->firstPosition();
02578     QTextFrame *f = frame;
02579     while (f) {
02580         QTextFrameData *fd = data(f);
02581         pos += fd->position;
02582 
02583         if (QTextTable *table = qobject_cast<QTextTable *>(f)) {
02584             QTextTableCell cell = table->cellAt(framePos);
02585             if (cell.isValid())
02586                 pos += static_cast<QTextTableData *>(fd)->cellPosition(cell.row(), cell.column());
02587         }
02588 
02589         f = f->parentFrame();
02590     }
02591     return QRectF(pos, data(frame)->size);
02592 }
02593 
02594 QRectF QTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
02595 {
02596     d_func()->ensureLayoutedByPosition(block.position() + block.length());
02597     QTextFrame *frame = document()->frameAt(block.position());
02598     QPointF offset;
02599     const int blockPos = block.position();
02600 
02601     while (frame) {
02602         QTextFrameData *fd = data(frame);
02603         offset += fd->position;
02604 
02605         if (QTextTable *table = qobject_cast<QTextTable *>(frame)) {
02606             QTextTableCell cell = table->cellAt(blockPos);
02607             if (cell.isValid())
02608                 offset += static_cast<QTextTableData *>(fd)->cellPosition(cell.row(), cell.column());
02609         }
02610 
02611         frame = frame->parentFrame();
02612     }
02613 
02614     const QTextLayout *layout = block.layout();
02615     QRectF rect = layout->boundingRect();
02616     rect.translate(layout->position() + offset);
02617     return rect;
02618 }
02619 
02620 int QTextDocumentLayout::layoutStatus() const
02621 {
02622     int pos = d_func()->currentLazyLayoutPosition;
02623     if (pos == -1)
02624         return 100;
02625     return pos * 100 / document()->docHandle()->length();
02626 }
02627 
02628 void QTextDocumentLayout::timerEvent(QTimerEvent *e)
02629 {
02630     Q_D(QTextDocumentLayout);
02631     if (e->timerId() == d->layoutTimer.timerId()) {
02632         if (d->currentLazyLayoutPosition != -1)
02633             d->layoutStep();
02634     } else if (e->timerId() == d->sizeChangedTimer.timerId()) {
02635         emit documentSizeChanged(dynamicDocumentSize());
02636         d->sizeChangedTimer.stop();
02637 
02638         if (d->currentLazyLayoutPosition == -1) {
02639             const int newCount = dynamicPageCount();
02640             if (newCount != d->lastPageCount) {
02641                 d->lastPageCount = newCount;
02642                 emit pageCountChanged(newCount);
02643             }
02644         }
02645     } else {
02646         QAbstractTextDocumentLayout::timerEvent(e);
02647     }
02648 }
02649 
02650 void QTextDocumentLayout::layoutFinished()
02651 {
02652     Q_D(QTextDocumentLayout);
02653     d->layoutTimer.stop();
02654     if (!d->insideDocumentChange)
02655         d->sizeChangedTimer.start(0, this);
02656     // reset
02657     d->showLayoutProgress = true;
02658 }
02659 
02660 void QTextDocumentLayout::ensureLayouted(qreal y)
02661 {
02662     d_func()->ensureLayouted(y);
02663 }
02664 
02665 qreal QTextDocumentLayout::idealWidth() const
02666 {
02667     Q_D(const QTextDocumentLayout);
02668     d->ensureLayoutFinished();
02669     return d->idealWidth;
02670 }
02671 
02672 #include "moc_qtextdocumentlayout_p.cpp"

Generated on Thu Mar 15 11:56:28 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1