00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
00054
00055 struct QLayoutStruct;
00056
00057 class QTextFrameData : public QTextFrameLayoutData
00058 {
00059 public:
00060 QTextFrameData();
00061
00062
00063 QPointF position;
00064 QSizeF size;
00065
00066
00067 qreal margin;
00068 qreal border;
00069 qreal padding;
00070
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
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
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
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
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),
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
00476
00477 if (frame != rootFrame) {
00478 if (relativePoint.y() < 0 || relativePoint.x() < 0) {
00479 *position = frame->firstPosition() - 1;
00480
00481 return PointBefore;
00482 } else if (relativePoint.y() > fd->size.height() || relativePoint.x() > fd->size.width()) {
00483 *position = frame->lastPosition() + 1;
00484
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
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
00587
00588 *position = bl.position();
00589 if (point.y() < textrect.top()) {
00590
00591 return PointBefore;
00592 } else if (point.y() > textrect.bottom()) {
00593 *position += bl.length();
00594
00595 return PointAfter;
00596 }
00597
00598 QPointF pos = point - textrect.topLeft();
00599
00600
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
00622 return hit;
00623 }
00624
00625
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
00655 painter->drawRect(QRectF(rect.left() + fd->margin, rect.top() + fd->margin, fd->border, h + 2 * fd->border));
00656
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
00661 painter->drawRect(QRectF(rect.left() + fd->margin + fd->border + w, rect.top() + fd->margin + fd->border, fd->border, h));
00662
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;
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
00703
00704
00705
00706
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
00739 + td->margin + td->border + td->padding;
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
00791
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
00847
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
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
00907 painter->drawRect(QRectF(cellRect.left(), cellRect.top() - td->border,
00908 cellRect.width() + td->border, td->border));
00909
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
00916 painter->drawRect(QRectF(cellRect.left(), cellRect.top() + cellRect.height(),
00917 cellRect.width() + td->border, td->border));
00918
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
00965
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
00982
00983
00984
00985
00986
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
01008
01009 QTextBlockFormat blockFormat = bl.blockFormat();
01010
01011 QBrush bg = blockFormat.background();
01012 if (bg != Qt::NoBrush) {
01013
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
01037
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
01217
01218
01219
01220
01221
01222
01223 layoutStruct.fullLayout = true;
01224
01225 QList<QTextFrame *> floats;
01226
01227
01228
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
01248
01249
01250
01251 for (int i = 0; i < floats.size(); ++i)
01252 layoutStruct.y = qMax(layoutStruct.y, data(floats.at(i))->size.height());
01253
01254
01255
01256 layoutStruct.maximumWidth = qMax(layoutStruct.maximumWidth, floatMinWidth);
01257
01258
01259
01260 data(t)->floats.clear();
01261
01262
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
01288 totalWidth -= columns * 2 * td->border;
01289
01290 totalWidth -= (columns - 1) * cellSpacing;
01291
01292 totalWidth -= 2 * cellSpacing;
01293
01294 qreal initialTotalWidth = totalWidth;
01295
01296 td->widths.resize(columns);
01297 td->widths.fill(0);
01298
01299 td->minWidths.resize(columns);
01300
01301
01302
01303 td->minWidths.fill(1);
01304
01305 td->maxWidths.resize(columns);
01306 td->maxWidths.fill(INT_MAX);
01307
01308 td->rowsAfterPageBreak.clear();
01309
01310
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
01320
01321
01322 QLayoutStruct layoutStruct = layoutCell(table, cell, INT_MAX, layoutFrom, layoutTo);
01323
01324
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
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
01348
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
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
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
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 + 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 + cellSpacing + td->border;
01422
01423 bool haveRowSpannedCells = false;
01424
01425
01426
01427
01428
01429
01430
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
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
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
01494
01495
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();
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
01532 qreal y = pd->currentLayoutStruct->y;
01533 if (currentLine) {
01534 qreal left, right;
01535 floatMargins(y, pd->currentLayoutStruct, &left, &right);
01536
01537 if (right - left < currentLine->naturalTextWidth() + fd->size.width()) {
01538 pd->currentLayoutStruct->pendingFloats.append(frame);
01539
01540 return;
01541 }
01542 }
01543
01544 if (!parent->parentFrame()
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
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
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
01590
01591 QTextFrameData *fd = data(f);
01592 const qreal oldContentsWidth = fd->contentsWidth;
01593 qreal newContentsWidth;
01594
01595 {
01596 QTextFrameFormat fformat = f->frameFormat();
01597
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
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
01632
01633
01634 fd->contentsWidth = newContentsWidth;
01635 qreal maxChildFrameWidth = 0;
01636
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;
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) {
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
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
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
01823
01824
01825
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
01867 layoutBlock(block, layoutStruct, layoutFrom, layoutTo, lastIt.currentBlock());
01868
01869
01870
01871 if (isEmptyBlockBeforeTable(block, it)) {
01872 layoutStruct->y = origY;
01873 continue;
01874 }
01875
01876
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
01894
01895
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
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
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
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
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
01985 || (layoutStruct->pageHeight > 0.0 && layoutStruct->y + tl->boundingRect().height() > layoutStruct->pageBottom)) {
01986
01987
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
02013
02014 if (fixedColumnWidth != -1)
02015 line.setNumColumns(fixedColumnWidth);
02016 else
02017 line.setLineWidth(right - left);
02018
02019
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
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
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
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
02100
02101
02102 layoutStruct->updateRect.setTop(qMax(layoutStruct->updateRect.top(), layoutStruct->y));
02103 } else if (layoutTo < bl.position()
02104 && oldPosition == tl->position()) {
02105
02106
02107
02108
02109 layoutStruct->updateRect.setBottom(qMin(layoutStruct->updateRect.bottom(), tl->position().y()));
02110 }
02111 }
02112 }
02113
02114
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
02133 qreal pageBottom = layoutStruct->pageBottom - td->position.y();
02134
02135 const qreal extraTableHeight = td->padding + td->border + td->cellSpacing
02136 + td->margin + td->border + td->padding;
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
02147
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;
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
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
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
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
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
02210 while (1) {
02211 floatMargins(yFrom, layoutStruct, &left, &right);
02212
02213 if (right-left >= requiredWidth)
02214 break;
02215
02216
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
02266 fd->floats.removeAt(i);
02267 --i;
02268 }
02269 }
02270
02271 fd->layoutDirty = true;
02272 fd->sizeDirty = true;
02273
02274
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
02302
02303
02304
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
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
02344
02345
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
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
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
02434
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
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
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"