1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qplaintextedit_p.h"
41 
42 
43 #include <qfont.h>
44 #include <qpainter.h>
45 #include <qevent.h>
46 #include <qdebug.h>
47 #if QT_CONFIG(draganddrop)
48 #include <qdrag.h>
49 #endif
50 #include <qclipboard.h>
51 #include <qmath.h>
52 #if QT_CONFIG(menu)
53 #include <qmenu.h>
54 #endif
55 #include <qstyle.h>
56 #include <qtimer.h>
57 #include "private/qapplication_p.h"
58 #include "private/qtextdocumentlayout_p.h"
59 #include "private/qabstracttextdocumentlayout_p.h"
60 #include "qtextdocument.h"
61 #include "private/qtextdocument_p.h"
62 #include "qtextlist.h"
63 #include "qaccessible.h"
64 
65 #include <qtextformat.h>
66 #include <qdatetime.h>
67 #include <qapplication.h>
68 #include <limits.h>
69 #include <qtexttable.h>
70 #include <qvariant.h>
71 
72 QT_BEGIN_NAMESPACE
73 
shouldEnableInputMethod(QPlainTextEdit * plaintextedit)74 static inline bool shouldEnableInputMethod(QPlainTextEdit *plaintextedit)
75 {
76     return !plaintextedit->isReadOnly();
77 }
78 
79 class QPlainTextDocumentLayoutPrivate : public QAbstractTextDocumentLayoutPrivate
80 {
81     Q_DECLARE_PUBLIC(QPlainTextDocumentLayout)
82 public:
QPlainTextDocumentLayoutPrivate()83     QPlainTextDocumentLayoutPrivate() {
84         mainViewPrivate = nullptr;
85         width = 0;
86         maximumWidth = 0;
87         maximumWidthBlockNumber = 0;
88         blockCount = 1;
89         blockUpdate = blockDocumentSizeChanged = false;
90         cursorWidth = 1;
91         textLayoutFlags = 0;
92     }
93 
94     qreal width;
95     qreal maximumWidth;
96     int maximumWidthBlockNumber;
97     int blockCount;
98     QPlainTextEditPrivate *mainViewPrivate;
99     bool blockUpdate;
100     bool blockDocumentSizeChanged;
101     int cursorWidth;
102     int textLayoutFlags;
103 
104     void layoutBlock(const QTextBlock &block);
105     qreal blockWidth(const QTextBlock &block);
106 
107     void relayout();
108 };
109 
110 
111 
112 /*! \class QPlainTextDocumentLayout
113     \since 4.4
114     \brief The QPlainTextDocumentLayout class implements a plain text layout for QTextDocument.
115 
116     \ingroup richtext-processing
117     \inmodule QtWidgets
118 
119    A QPlainTextDocumentLayout is required for text documents that can
120    be display or edited in a QPlainTextEdit. See
121    QTextDocument::setDocumentLayout().
122 
123    QPlainTextDocumentLayout uses the QAbstractTextDocumentLayout API
124    that QTextDocument requires, but redefines it partially in order to
125    support plain text better. For instances, it does not operate on
126    vertical pixels, but on paragraphs (called blocks) instead. The
127    height of a document is identical to the number of paragraphs it
128    contains. The layout also doesn't support tables or nested frames,
129    or any sort of advanced text layout that goes beyond a list of
130    paragraphs with syntax highlighting.
131 
132 */
133 
134 
135 
136 /*!
137   Constructs a plain text document layout for the text \a document.
138  */
QPlainTextDocumentLayout(QTextDocument * document)139 QPlainTextDocumentLayout::QPlainTextDocumentLayout(QTextDocument *document)
140     :QAbstractTextDocumentLayout(* new QPlainTextDocumentLayoutPrivate, document) {
141 }
142 /*!
143   Destructs a plain text document layout.
144  */
~QPlainTextDocumentLayout()145 QPlainTextDocumentLayout::~QPlainTextDocumentLayout() {}
146 
147 
148 /*!
149   \reimp
150  */
draw(QPainter *,const PaintContext &)151 void QPlainTextDocumentLayout::draw(QPainter *, const PaintContext &)
152 {
153 }
154 
155 /*!
156   \reimp
157  */
hitTest(const QPointF &,Qt::HitTestAccuracy) const158 int QPlainTextDocumentLayout::hitTest(const QPointF &, Qt::HitTestAccuracy ) const
159 {
160 //     this function is used from
161 //     QAbstractTextDocumentLayout::anchorAt(), but is not
162 //     implementable in a plain text document layout, because the
163 //     layout depends on the top block and top line which depends on
164 //     the view
165     return -1;
166 }
167 
168 /*!
169   \reimp
170  */
pageCount() const171 int QPlainTextDocumentLayout::pageCount() const
172 { return 1; }
173 
174 /*!
175   \reimp
176  */
documentSize() const177 QSizeF QPlainTextDocumentLayout::documentSize() const
178 {
179     Q_D(const QPlainTextDocumentLayout);
180     return QSizeF(d->maximumWidth, document()->lineCount());
181 }
182 
183 /*!
184   \reimp
185  */
frameBoundingRect(QTextFrame *) const186 QRectF QPlainTextDocumentLayout::frameBoundingRect(QTextFrame *) const
187 {
188     Q_D(const QPlainTextDocumentLayout);
189     return QRectF(0, 0, qMax(d->width, d->maximumWidth), qreal(INT_MAX));
190 }
191 
192 /*!
193   \reimp
194  */
blockBoundingRect(const QTextBlock & block) const195 QRectF QPlainTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
196 {
197     if (!block.isValid()) { return QRectF(); }
198     QTextLayout *tl = block.layout();
199     if (!tl->lineCount())
200         const_cast<QPlainTextDocumentLayout*>(this)->layoutBlock(block);
201     QRectF br;
202     if (block.isVisible()) {
203         br = QRectF(QPointF(0, 0), tl->boundingRect().bottomRight());
204         if (tl->lineCount() == 1)
205             br.setWidth(qMax(br.width(), tl->lineAt(0).naturalTextWidth()));
206         qreal margin = document()->documentMargin();
207         br.adjust(0, 0, margin, 0);
208         if (!block.next().isValid())
209             br.adjust(0, 0, 0, margin);
210     }
211     return br;
212 
213 }
214 
215 /*!
216   Ensures that \a block has a valid layout
217  */
ensureBlockLayout(const QTextBlock & block) const218 void QPlainTextDocumentLayout::ensureBlockLayout(const QTextBlock &block) const
219 {
220     if (!block.isValid())
221         return;
222     QTextLayout *tl = block.layout();
223     if (!tl->lineCount())
224         const_cast<QPlainTextDocumentLayout*>(this)->layoutBlock(block);
225 }
226 
227 
228 /*! \property QPlainTextDocumentLayout::cursorWidth
229 
230     This property specifies the width of the cursor in pixels. The default value is 1.
231 */
setCursorWidth(int width)232 void QPlainTextDocumentLayout::setCursorWidth(int width)
233 {
234     Q_D(QPlainTextDocumentLayout);
235     d->cursorWidth = width;
236 }
237 
cursorWidth() const238 int QPlainTextDocumentLayout::cursorWidth() const
239 {
240     Q_D(const QPlainTextDocumentLayout);
241     return d->cursorWidth;
242 }
243 
priv() const244 QPlainTextDocumentLayoutPrivate *QPlainTextDocumentLayout::priv() const
245 {
246     Q_D(const QPlainTextDocumentLayout);
247     return const_cast<QPlainTextDocumentLayoutPrivate*>(d);
248 }
249 
250 
251 /*!
252 
253    Requests a complete update on all views.
254  */
requestUpdate()255 void QPlainTextDocumentLayout::requestUpdate()
256 {
257     emit update(QRectF(0., -document()->documentMargin(), 1000000000., 1000000000.));
258 }
259 
260 
setTextWidth(qreal newWidth)261 void QPlainTextDocumentLayout::setTextWidth(qreal newWidth)
262 {
263     Q_D(QPlainTextDocumentLayout);
264     d->width = d->maximumWidth = newWidth;
265     d->relayout();
266 }
267 
textWidth() const268 qreal QPlainTextDocumentLayout::textWidth() const
269 {
270     Q_D(const QPlainTextDocumentLayout);
271     return d->width;
272 }
273 
relayout()274 void QPlainTextDocumentLayoutPrivate::relayout()
275 {
276     Q_Q(QPlainTextDocumentLayout);
277     QTextBlock block = q->document()->firstBlock();
278     while (block.isValid()) {
279         block.layout()->clearLayout();
280         block.setLineCount(block.isVisible() ? 1 : 0);
281         block = block.next();
282     }
283     emit q->update();
284 }
285 
286 
287 /*! \reimp
288  */
documentChanged(int from,int charsRemoved,int charsAdded)289 void QPlainTextDocumentLayout::documentChanged(int from, int charsRemoved, int charsAdded)
290 {
291     Q_D(QPlainTextDocumentLayout);
292     QTextDocument *doc = document();
293     int newBlockCount = doc->blockCount();
294     int charsChanged = charsRemoved + charsAdded;
295 
296     QTextBlock changeStartBlock = doc->findBlock(from);
297     QTextBlock changeEndBlock = doc->findBlock(qMax(0, from + charsChanged - 1));
298     bool blockVisibilityChanged = false;
299 
300     if (changeStartBlock == changeEndBlock && newBlockCount == d->blockCount) {
301         QTextBlock block = changeStartBlock;
302         if (block.isValid() && block.length()) {
303             QRectF oldBr = blockBoundingRect(block);
304             layoutBlock(block);
305             QRectF newBr = blockBoundingRect(block);
306             if (newBr.height() == oldBr.height()) {
307                 if (!d->blockUpdate)
308                     emit updateBlock(block);
309                 return;
310             }
311         }
312     } else {
313         QTextBlock block = changeStartBlock;
314         do {
315             block.clearLayout();
316             if (block.isVisible()
317                     ? (block.lineCount() == 0)
318                     : (block.lineCount() > 0)) {
319                 blockVisibilityChanged = true;
320                 block.setLineCount(block.isVisible() ? 1 : 0);
321             }
322             if (block == changeEndBlock)
323                 break;
324             block = block.next();
325         } while(block.isValid());
326     }
327 
328     if (newBlockCount != d->blockCount || blockVisibilityChanged) {
329         int changeEnd = changeEndBlock.blockNumber();
330         int blockDiff = newBlockCount - d->blockCount;
331         int oldChangeEnd = changeEnd - blockDiff;
332 
333         if (d->maximumWidthBlockNumber > oldChangeEnd)
334             d->maximumWidthBlockNumber += blockDiff;
335 
336         d->blockCount = newBlockCount;
337         if (d->blockCount == 1)
338             d->maximumWidth = blockWidth(doc->firstBlock());
339 
340         if (!d->blockDocumentSizeChanged)
341             emit documentSizeChanged(documentSize());
342 
343         if (blockDiff == 1 && changeEnd == newBlockCount -1 ) {
344             if (!d->blockUpdate) {
345                 QTextBlock b = changeStartBlock;
346                 for(;;) {
347                     emit updateBlock(b);
348                     if (b == changeEndBlock)
349                         break;
350                     b = b.next();
351                 }
352             }
353             return;
354         }
355     }
356 
357     if (!d->blockUpdate)
358         emit update(QRectF(0., -doc->documentMargin(), 1000000000., 1000000000.)); // optimization potential
359 }
360 
361 
layoutBlock(const QTextBlock & block)362 void QPlainTextDocumentLayout::layoutBlock(const QTextBlock &block)
363 {
364     Q_D(QPlainTextDocumentLayout);
365     QTextDocument *doc = document();
366     qreal margin = doc->documentMargin();
367     qreal blockMaximumWidth = 0;
368 
369     qreal height = 0;
370     QTextLayout *tl = block.layout();
371     QTextOption option = doc->defaultTextOption();
372     tl->setTextOption(option);
373 
374     int extraMargin = 0;
375     if (option.flags() & QTextOption::AddSpaceForLineAndParagraphSeparators) {
376         QFontMetrics fm(block.charFormat().font());
377         extraMargin += fm.horizontalAdvance(QChar(0x21B5));
378     }
379     tl->beginLayout();
380     qreal availableWidth = d->width;
381     if (availableWidth <= 0) {
382         availableWidth = qreal(INT_MAX); // similar to text edit with pageSize.width == 0
383     }
384     availableWidth -= 2*margin + extraMargin;
385     while (1) {
386         QTextLine line = tl->createLine();
387         if (!line.isValid())
388             break;
389         line.setLeadingIncluded(true);
390         line.setLineWidth(availableWidth);
391         line.setPosition(QPointF(margin, height));
392         height += line.height();
393         if (line.leading() < 0)
394             height += qCeil(line.leading());
395         blockMaximumWidth = qMax(blockMaximumWidth, line.naturalTextWidth() + 2*margin);
396     }
397     tl->endLayout();
398 
399     int previousLineCount = doc->lineCount();
400     const_cast<QTextBlock&>(block).setLineCount(block.isVisible() ? tl->lineCount() : 0);
401     int lineCount = doc->lineCount();
402 
403     bool emitDocumentSizeChanged = previousLineCount != lineCount;
404     if (blockMaximumWidth > d->maximumWidth) {
405         // new longest line
406         d->maximumWidth = blockMaximumWidth;
407         d->maximumWidthBlockNumber = block.blockNumber();
408         emitDocumentSizeChanged = true;
409     } else if (block.blockNumber() == d->maximumWidthBlockNumber && blockMaximumWidth < d->maximumWidth) {
410         // longest line shrinking
411         QTextBlock b = doc->firstBlock();
412         d->maximumWidth = 0;
413         QTextBlock maximumBlock;
414         while (b.isValid()) {
415             qreal blockMaximumWidth = blockWidth(b);
416             if (blockMaximumWidth > d->maximumWidth) {
417                 d->maximumWidth = blockMaximumWidth;
418                 maximumBlock = b;
419             }
420             b = b.next();
421         }
422         if (maximumBlock.isValid()) {
423             d->maximumWidthBlockNumber = maximumBlock.blockNumber();
424             emitDocumentSizeChanged = true;
425         }
426     }
427     if (emitDocumentSizeChanged && !d->blockDocumentSizeChanged)
428         emit documentSizeChanged(documentSize());
429 }
430 
blockWidth(const QTextBlock & block)431 qreal QPlainTextDocumentLayout::blockWidth(const QTextBlock &block)
432 {
433     QTextLayout *layout = block.layout();
434     if (!layout->lineCount())
435         return 0; // only for layouted blocks
436     qreal blockWidth = 0;
437     for (int i = 0; i < layout->lineCount(); ++i) {
438         QTextLine line = layout->lineAt(i);
439         blockWidth = qMax(line.naturalTextWidth() + 8, blockWidth);
440     }
441     return blockWidth;
442 }
443 
444 
QPlainTextEditControl(QPlainTextEdit * parent)445 QPlainTextEditControl::QPlainTextEditControl(QPlainTextEdit *parent)
446     : QWidgetTextControl(parent), textEdit(parent),
447       topBlock(0)
448 {
449     setAcceptRichText(false);
450 }
451 
_q_cursorPositionChanged()452 void QPlainTextEditPrivate::_q_cursorPositionChanged()
453 {
454     pageUpDownLastCursorYIsValid = false;
455     Q_Q(QPlainTextEdit);
456 #ifndef QT_NO_ACCESSIBILITY
457     QAccessibleTextCursorEvent ev(q, q->textCursor().position());
458     QAccessible::updateAccessibility(&ev);
459 #endif
460     emit q->cursorPositionChanged();
461 }
462 
_q_verticalScrollbarActionTriggered(int action)463 void QPlainTextEditPrivate::_q_verticalScrollbarActionTriggered(int action) {
464     if (action == QAbstractSlider::SliderPageStepAdd) {
465         pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor, false);
466     } else if (action == QAbstractSlider::SliderPageStepSub) {
467         pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor, false);
468     }
469 }
470 
createMimeDataFromSelection() const471 QMimeData *QPlainTextEditControl::createMimeDataFromSelection() const {
472         QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
473         if (!ed)
474             return QWidgetTextControl::createMimeDataFromSelection();
475         return ed->createMimeDataFromSelection();
476     }
canInsertFromMimeData(const QMimeData * source) const477 bool QPlainTextEditControl::canInsertFromMimeData(const QMimeData *source) const {
478     QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
479     if (!ed)
480         return QWidgetTextControl::canInsertFromMimeData(source);
481     return ed->canInsertFromMimeData(source);
482 }
insertFromMimeData(const QMimeData * source)483 void QPlainTextEditControl::insertFromMimeData(const QMimeData *source) {
484     QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
485     if (!ed)
486         QWidgetTextControl::insertFromMimeData(source);
487     else
488         ed->insertFromMimeData(source);
489 }
490 
verticalOffset(int topBlock,int topLine) const491 qreal QPlainTextEditPrivate::verticalOffset(int topBlock, int topLine) const
492 {
493     qreal offset = 0;
494     QTextDocument *doc = control->document();
495 
496     if (topLine) {
497         QTextBlock currentBlock = doc->findBlockByNumber(topBlock);
498         QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
499         Q_ASSERT(documentLayout);
500         QRectF r = documentLayout->blockBoundingRect(currentBlock);
501         Q_UNUSED(r);
502         QTextLayout *layout = currentBlock.layout();
503         if (layout && topLine <= layout->lineCount()) {
504             QTextLine line = layout->lineAt(topLine - 1);
505             const QRectF lr = line.naturalTextRect();
506             offset = lr.bottom();
507         }
508     }
509     if (topBlock == 0 && topLine == 0)
510         offset -= doc->documentMargin(); // top margin
511     return offset;
512 }
513 
514 
verticalOffset() const515 qreal QPlainTextEditPrivate::verticalOffset() const {
516     return verticalOffset(control->topBlock, topLine) + topLineFracture;
517 }
518 
519 
firstVisibleBlock() const520 QTextBlock QPlainTextEditControl::firstVisibleBlock() const
521 {
522     return document()->findBlockByNumber(topBlock);
523 }
524 
525 
526 
hitTest(const QPointF & point,Qt::HitTestAccuracy) const527 int QPlainTextEditControl::hitTest(const QPointF &point, Qt::HitTestAccuracy ) const {
528     int currentBlockNumber = topBlock;
529     QTextBlock currentBlock = document()->findBlockByNumber(currentBlockNumber);
530     if (!currentBlock.isValid())
531         return -1;
532 
533     QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout());
534     Q_ASSERT(documentLayout);
535 
536     QPointF offset;
537     QRectF r = documentLayout->blockBoundingRect(currentBlock);
538     while (currentBlock.next().isValid() && r.bottom() + offset.y() <= point.y()) {
539         offset.ry() += r.height();
540         currentBlock = currentBlock.next();
541         ++currentBlockNumber;
542         r = documentLayout->blockBoundingRect(currentBlock);
543     }
544     while (currentBlock.previous().isValid() && r.top() + offset.y() > point.y()) {
545         offset.ry() -= r.height();
546         currentBlock = currentBlock.previous();
547         --currentBlockNumber;
548         r = documentLayout->blockBoundingRect(currentBlock);
549     }
550 
551 
552     if (!currentBlock.isValid())
553         return -1;
554     QTextLayout *layout = currentBlock.layout();
555     int off = 0;
556     QPointF pos = point - offset;
557     for (int i = 0; i < layout->lineCount(); ++i) {
558         QTextLine line = layout->lineAt(i);
559         const QRectF lr = line.naturalTextRect();
560         if (lr.top() > pos.y()) {
561             off = qMin(off, line.textStart());
562         } else if (lr.bottom() <= pos.y()) {
563             off = qMax(off, line.textStart() + line.textLength());
564         } else {
565             off = line.xToCursor(pos.x(), overwriteMode() ?
566                                  QTextLine::CursorOnCharacter : QTextLine::CursorBetweenCharacters);
567             break;
568         }
569     }
570 
571     return currentBlock.position() + off;
572 }
573 
blockBoundingRect(const QTextBlock & block) const574 QRectF QPlainTextEditControl::blockBoundingRect(const QTextBlock &block) const {
575     int currentBlockNumber = topBlock;
576     int blockNumber = block.blockNumber();
577     QTextBlock currentBlock = document()->findBlockByNumber(currentBlockNumber);
578     if (!currentBlock.isValid())
579         return QRectF();
580     Q_ASSERT(currentBlock.blockNumber() == currentBlockNumber);
581     QTextDocument *doc = document();
582     QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
583     Q_ASSERT(documentLayout);
584 
585     QPointF offset;
586     if (!block.isValid())
587         return QRectF();
588     QRectF r = documentLayout->blockBoundingRect(currentBlock);
589     int maxVerticalOffset = r.height();
590     while (currentBlockNumber < blockNumber && offset.y() - maxVerticalOffset <= 2* textEdit->viewport()->height()) {
591         offset.ry() += r.height();
592         currentBlock = currentBlock.next();
593         ++currentBlockNumber;
594         if (!currentBlock.isVisible()) {
595             currentBlock = doc->findBlockByLineNumber(currentBlock.firstLineNumber());
596             currentBlockNumber = currentBlock.blockNumber();
597         }
598         r = documentLayout->blockBoundingRect(currentBlock);
599     }
600     while (currentBlockNumber > blockNumber && offset.y() + maxVerticalOffset >= -textEdit->viewport()->height()) {
601         currentBlock = currentBlock.previous();
602         --currentBlockNumber;
603         while (!currentBlock.isVisible()) {
604             currentBlock = currentBlock.previous();
605             --currentBlockNumber;
606         }
607         if (!currentBlock.isValid())
608             break;
609 
610         r = documentLayout->blockBoundingRect(currentBlock);
611         offset.ry() -= r.height();
612     }
613 
614     if (currentBlockNumber != blockNumber) {
615         // fallback for blocks out of reach. Give it some geometry at
616         // least, and ensure the layout is up to date.
617         r = documentLayout->blockBoundingRect(block);
618         if (currentBlockNumber > blockNumber)
619             offset.ry() -= r.height();
620     }
621     r.translate(offset);
622     return r;
623 }
624 
anchorAt(const QPointF & pos) const625 QString QPlainTextEditControl::anchorAt(const QPointF &pos) const
626 {
627     return textEdit->anchorAt(pos.toPoint());
628 }
629 
setTopLine(int visualTopLine,int dx)630 void QPlainTextEditPrivate::setTopLine(int visualTopLine, int dx)
631 {
632     QTextDocument *doc = control->document();
633     QTextBlock block = doc->findBlockByLineNumber(visualTopLine);
634     int blockNumber = block.blockNumber();
635     int lineNumber = visualTopLine - block.firstLineNumber();
636     setTopBlock(blockNumber, lineNumber, dx);
637 }
638 
setTopBlock(int blockNumber,int lineNumber,int dx)639 void QPlainTextEditPrivate::setTopBlock(int blockNumber, int lineNumber, int dx)
640 {
641     Q_Q(QPlainTextEdit);
642     blockNumber = qMax(0, blockNumber);
643     lineNumber = qMax(0, lineNumber);
644     QTextDocument *doc = control->document();
645     QTextBlock block = doc->findBlockByNumber(blockNumber);
646 
647     int newTopLine = block.firstLineNumber() + lineNumber;
648     int maxTopLine = vbar->maximum();
649 
650     if (newTopLine > maxTopLine) {
651         block = doc->findBlockByLineNumber(maxTopLine);
652         blockNumber = block.blockNumber();
653         lineNumber = maxTopLine - block.firstLineNumber();
654     }
655 
656     {
657         const QSignalBlocker blocker(vbar);
658         vbar->setValue(newTopLine);
659     }
660 
661     if (!dx && blockNumber == control->topBlock && lineNumber == topLine)
662         return;
663 
664     if (viewport->updatesEnabled() && viewport->isVisible()) {
665         int dy = 0;
666         if (doc->findBlockByNumber(control->topBlock).isValid()) {
667             qreal realdy = -q->blockBoundingGeometry(block).y()
668                     + verticalOffset() - verticalOffset(blockNumber, lineNumber);
669             dy = (int)realdy;
670             topLineFracture = realdy - dy;
671         }
672         control->topBlock = blockNumber;
673         topLine = lineNumber;
674 
675         {
676             const QSignalBlocker blocker(vbar);
677             vbar->setValue(block.firstLineNumber() + lineNumber);
678         }
679 
680         if (dx || dy) {
681             viewport->scroll(q->isRightToLeft() ? -dx : dx, dy);
682             QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle | Qt::ImAnchorRectangle);
683         } else {
684             viewport->update();
685             topLineFracture = 0;
686         }
687         emit q->updateRequest(viewport->rect(), dy);
688     } else {
689         control->topBlock = blockNumber;
690         topLine = lineNumber;
691         topLineFracture = 0;
692     }
693 
694 }
695 
696 
697 
ensureVisible(int position,bool center,bool forceCenter)698 void QPlainTextEditPrivate::ensureVisible(int position, bool center, bool forceCenter) {
699     Q_Q(QPlainTextEdit);
700     QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset());
701     QTextBlock block = control->document()->findBlock(position);
702     if (!block.isValid())
703         return;
704     QRectF br = control->blockBoundingRect(block);
705     if (!br.isValid())
706         return;
707     QTextLine line = block.layout()->lineForTextPosition(position - block.position());
708     Q_ASSERT(line.isValid());
709     QRectF lr = line.naturalTextRect().translated(br.topLeft());
710 
711     if (lr.bottom() >= visible.bottom() || (center && lr.top() < visible.top()) || forceCenter){
712 
713         qreal height = visible.height();
714         if (center)
715             height /= 2;
716 
717         qreal h = center ? line.naturalTextRect().center().y() : line.naturalTextRect().bottom();
718 
719         QTextBlock previousVisibleBlock = block;
720         while (h < height && block.previous().isValid()) {
721             previousVisibleBlock = block;
722             do {
723                 block = block.previous();
724             } while (!block.isVisible() && block.previous().isValid());
725             h += q->blockBoundingRect(block).height();
726         }
727 
728         int l = 0;
729         int lineCount = block.layout()->lineCount();
730         qreal voffset = verticalOffset(block.blockNumber(), 0);
731         while (l < lineCount) {
732             QRectF lineRect = block.layout()->lineAt(l).naturalTextRect();
733             if (h - voffset - lineRect.top() <= height)
734                 break;
735             ++l;
736         }
737 
738         if (l >= lineCount) {
739             block = previousVisibleBlock;
740             l = 0;
741         }
742         setTopBlock(block.blockNumber(), l);
743     } else if (lr.top() < visible.top()) {
744         setTopBlock(block.blockNumber(), line.lineNumber());
745     }
746 
747 }
748 
749 
updateViewport()750 void QPlainTextEditPrivate::updateViewport()
751 {
752     Q_Q(QPlainTextEdit);
753     viewport->update();
754     emit q->updateRequest(viewport->rect(), 0);
755 }
756 
QPlainTextEditPrivate()757 QPlainTextEditPrivate::QPlainTextEditPrivate()
758     : control(nullptr),
759       tabChangesFocus(false),
760       lineWrap(QPlainTextEdit::WidgetWidth),
761       wordWrap(QTextOption::WrapAtWordBoundaryOrAnywhere),
762       clickCausedFocus(0), placeholderVisible(1),
763       topLine(0), topLineFracture(0),
764       pageUpDownLastCursorYIsValid(false)
765 {
766     showCursorOnInitialShow = true;
767     backgroundVisible = false;
768     centerOnScroll = false;
769     inDrag = false;
770 }
771 
772 
init(const QString & txt)773 void QPlainTextEditPrivate::init(const QString &txt)
774 {
775     Q_Q(QPlainTextEdit);
776     control = new QPlainTextEditControl(q);
777 
778     QTextDocument *doc = new QTextDocument(control);
779     QAbstractTextDocumentLayout *layout = new QPlainTextDocumentLayout(doc);
780     doc->setDocumentLayout(layout);
781     control->setDocument(doc);
782 
783     control->setPalette(q->palette());
784 
785     QObject::connect(vbar, SIGNAL(actionTriggered(int)), q, SLOT(_q_verticalScrollbarActionTriggered(int)));
786 
787     QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(updateMicroFocus()));
788     QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), q, SLOT(_q_adjustScrollbars()));
789     QObject::connect(control, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int)));
790     QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(_q_repaintContents(QRectF)));
791     QObject::connect(control, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool)));
792 
793     QObject::connect(control, SIGNAL(textChanged()), q, SIGNAL(textChanged()));
794     QObject::connect(control, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
795     QObject::connect(control, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
796     QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool)));
797     QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
798     QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(_q_cursorPositionChanged()));
799 
800     QObject::connect(control, SIGNAL(textChanged()), q, SLOT(_q_textChanged()));
801     QObject::connect(control, SIGNAL(textChanged()), q, SLOT(updateMicroFocus()));
802 
803     // set a null page size initially to avoid any relayouting until the textedit
804     // is shown. relayoutDocument() will take care of setting the page size to the
805     // viewport dimensions later.
806     doc->setTextWidth(-1);
807     doc->documentLayout()->setPaintDevice(viewport);
808     doc->setDefaultFont(q->font());
809 
810 
811     if (!txt.isEmpty())
812         control->setPlainText(txt);
813 
814     hbar->setSingleStep(20);
815     vbar->setSingleStep(1);
816 
817     viewport->setBackgroundRole(QPalette::Base);
818     q->setAcceptDrops(true);
819     q->setFocusPolicy(Qt::StrongFocus);
820     q->setAttribute(Qt::WA_KeyCompression);
821     q->setAttribute(Qt::WA_InputMethodEnabled);
822     q->setInputMethodHints(Qt::ImhMultiLine);
823 
824 #ifndef QT_NO_CURSOR
825     viewport->setCursor(Qt::IBeamCursor);
826 #endif
827     originalOffsetY = 0;
828 }
829 
_q_textChanged()830 void QPlainTextEditPrivate::_q_textChanged()
831 {
832     Q_Q(QPlainTextEdit);
833 
834     // We normally only repaint the part of view that contains text in the
835     // document that has changed (in _q_repaintContents). But the placeholder
836     // text is not a part of the document, but is drawn on separately. So whenever
837     // we either show or hide the placeholder text, we issue a full update.
838     bool placeholderCurrentyVisible = placeholderVisible;
839 
840     placeholderVisible = !placeholderText.isEmpty()
841             && q->document()->isEmpty()
842             && (!q->firstVisibleBlock().isValid() ||
843                  q->firstVisibleBlock().layout()->preeditAreaText().isEmpty());
844 
845     if (placeholderCurrentyVisible != placeholderVisible)
846         viewport->update();
847 }
848 
_q_repaintContents(const QRectF & contentsRect)849 void QPlainTextEditPrivate::_q_repaintContents(const QRectF &contentsRect)
850 {
851     Q_Q(QPlainTextEdit);
852     if (!contentsRect.isValid()) {
853         updateViewport();
854         return;
855     }
856     const int xOffset = horizontalOffset();
857     const int yOffset = (int)verticalOffset();
858     const QRect visibleRect(xOffset, yOffset, viewport->width(), viewport->height());
859 
860     QRect r = contentsRect.adjusted(-1, -1, 1, 1).intersected(visibleRect).toAlignedRect();
861     if (r.isEmpty())
862         return;
863 
864     r.translate(-xOffset, -yOffset);
865     viewport->update(r);
866     emit q->updateRequest(r, 0);
867 }
868 
pageUpDown(QTextCursor::MoveOperation op,QTextCursor::MoveMode moveMode,bool moveCursor)869 void QPlainTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode, bool moveCursor)
870 {
871 
872     Q_Q(QPlainTextEdit);
873 
874     QTextCursor cursor = control->textCursor();
875     if (moveCursor) {
876         ensureCursorVisible();
877         if (!pageUpDownLastCursorYIsValid)
878             pageUpDownLastCursorY = control->cursorRect(cursor).top() - verticalOffset();
879     }
880 
881     qreal lastY = pageUpDownLastCursorY;
882 
883 
884     if (op == QTextCursor::Down) {
885         QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset());
886         QTextBlock firstVisibleBlock = q->firstVisibleBlock();
887         QTextBlock block = firstVisibleBlock;
888         QRectF br = q->blockBoundingRect(block);
889         qreal h = 0;
890         int atEnd = false;
891         while (h + br.height() <= visible.bottom()) {
892             if (!block.next().isValid()) {
893                 atEnd = true;
894                 lastY = visible.bottom(); // set cursor to last line
895                 break;
896             }
897             h += br.height();
898             block = block.next();
899             br = q->blockBoundingRect(block);
900         }
901 
902         if (!atEnd) {
903             int line = 0;
904             qreal diff = visible.bottom() - h;
905             int lineCount = block.layout()->lineCount();
906             while (line < lineCount - 1) {
907                 if (block.layout()->lineAt(line).naturalTextRect().bottom() > diff) {
908                     // the first line that did not completely fit the screen
909                     break;
910                 }
911                 ++line;
912             }
913             setTopBlock(block.blockNumber(), line);
914         }
915 
916         if (moveCursor) {
917             // move using movePosition to keep the cursor's x
918             lastY += verticalOffset();
919             bool moved = false;
920             do {
921                 moved = cursor.movePosition(op, moveMode);
922             } while (moved && control->cursorRect(cursor).top() < lastY);
923         }
924 
925     } else if (op == QTextCursor::Up) {
926 
927         QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset());
928         visible.translate(0, -visible.height()); // previous page
929         QTextBlock block = q->firstVisibleBlock();
930         qreal h = 0;
931         while (h >= visible.top()) {
932             if (!block.previous().isValid()) {
933                 if (control->topBlock == 0 && topLine == 0) {
934                     lastY = 0; // set cursor to first line
935                 }
936                 break;
937             }
938             block = block.previous();
939             QRectF br = q->blockBoundingRect(block);
940             h -= br.height();
941         }
942 
943         int line = 0;
944         if (block.isValid()) {
945             qreal diff = visible.top() - h;
946             int lineCount = block.layout()->lineCount();
947             while (line < lineCount) {
948                 if (block.layout()->lineAt(line).naturalTextRect().top() >= diff)
949                     break;
950                 ++line;
951             }
952             if (line == lineCount) {
953                 if (block.next().isValid() && block.next() != q->firstVisibleBlock()) {
954                     block = block.next();
955                     line = 0;
956                 } else {
957                     --line;
958                 }
959             }
960         }
961         setTopBlock(block.blockNumber(), line);
962 
963         if (moveCursor) {
964             cursor.setVisualNavigation(true);
965             // move using movePosition to keep the cursor's x
966             lastY += verticalOffset();
967             bool moved = false;
968             do {
969                 moved = cursor.movePosition(op, moveMode);
970             } while (moved && control->cursorRect(cursor).top() > lastY);
971         }
972     }
973 
974     if (moveCursor) {
975         control->setTextCursor(cursor, moveMode == QTextCursor::KeepAnchor);
976         pageUpDownLastCursorYIsValid = true;
977     }
978 }
979 
980 #if QT_CONFIG(scrollbar)
981 
_q_adjustScrollbars()982 void QPlainTextEditPrivate::_q_adjustScrollbars()
983 {
984     Q_Q(QPlainTextEdit);
985     QTextDocument *doc = control->document();
986     QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
987     Q_ASSERT(documentLayout);
988     bool documentSizeChangedBlocked = documentLayout->priv()->blockDocumentSizeChanged;
989     documentLayout->priv()->blockDocumentSizeChanged = true;
990     qreal margin = doc->documentMargin();
991 
992     int vmax = 0;
993 
994     int vSliderLength = 0;
995     if (!centerOnScroll && q->isVisible()) {
996         QTextBlock block = doc->lastBlock();
997         const qreal visible = viewport->rect().height() - margin - 1;
998         qreal y = 0;
999         int visibleFromBottom = 0;
1000 
1001         while (block.isValid()) {
1002             if (!block.isVisible()) {
1003                 block = block.previous();
1004                 continue;
1005             }
1006             y += documentLayout->blockBoundingRect(block).height();
1007 
1008             QTextLayout *layout = block.layout();
1009             int layoutLineCount = layout->lineCount();
1010             if (y > visible) {
1011                 int lineNumber = 0;
1012                 while (lineNumber < layoutLineCount) {
1013                     QTextLine line = layout->lineAt(lineNumber);
1014                     const QRectF lr = line.naturalTextRect();
1015                     if (lr.top() >= y - visible)
1016                         break;
1017                     ++lineNumber;
1018                 }
1019                 if (lineNumber < layoutLineCount)
1020                     visibleFromBottom += (layoutLineCount - lineNumber);
1021                 break;
1022 
1023             }
1024             visibleFromBottom += layoutLineCount;
1025             block = block.previous();
1026         }
1027         vmax = qMax(0, doc->lineCount() - visibleFromBottom);
1028         vSliderLength = visibleFromBottom;
1029 
1030     } else {
1031         vmax = qMax(0, doc->lineCount() - 1);
1032         int lineSpacing = q->fontMetrics().lineSpacing();
1033         vSliderLength = lineSpacing != 0 ? viewport->height() / lineSpacing : 0;
1034     }
1035 
1036 
1037 
1038     QSizeF documentSize = documentLayout->documentSize();
1039     vbar->setRange(0, qMax(0, vmax));
1040     vbar->setPageStep(vSliderLength);
1041     int visualTopLine = vmax;
1042     QTextBlock firstVisibleBlock = q->firstVisibleBlock();
1043     if (firstVisibleBlock.isValid())
1044         visualTopLine = firstVisibleBlock.firstLineNumber() + topLine;
1045 
1046     {
1047         const QSignalBlocker blocker(vbar);
1048         vbar->setValue(visualTopLine);
1049     }
1050 
1051     hbar->setRange(0, (int)documentSize.width() - viewport->width());
1052     hbar->setPageStep(viewport->width());
1053     documentLayout->priv()->blockDocumentSizeChanged = documentSizeChangedBlocked;
1054     setTopLine(vbar->value());
1055 }
1056 
1057 #endif
1058 
1059 
ensureViewportLayouted()1060 void QPlainTextEditPrivate::ensureViewportLayouted()
1061 {
1062 }
1063 
1064 /*!
1065     \class QPlainTextEdit
1066     \since 4.4
1067     \brief The QPlainTextEdit class provides a widget that is used to edit and display
1068     plain text.
1069 
1070     \ingroup richtext-processing
1071     \inmodule QtWidgets
1072 
1073     \tableofcontents
1074 
1075     \section1 Introduction and Concepts
1076 
1077     QPlainTextEdit is an advanced viewer/editor supporting plain
1078     text. It is optimized to handle large documents and to respond
1079     quickly to user input.
1080 
1081     QPlainText uses very much the same technology and concepts as
1082     QTextEdit, but is optimized for plain text handling.
1083 
1084     QPlainTextEdit works on paragraphs and characters. A paragraph is
1085     a formatted string which is word-wrapped to fit into the width of
1086     the widget. By default when reading plain text, one newline
1087     signifies a paragraph. A document consists of zero or more
1088     paragraphs. Paragraphs are separated by hard line breaks. Each
1089     character within a paragraph has its own attributes, for example,
1090     font and color.
1091 
1092     The shape of the mouse cursor on a QPlainTextEdit is
1093     Qt::IBeamCursor by default.  It can be changed through the
1094     viewport()'s cursor property.
1095 
1096     \section1 Using QPlainTextEdit as a Display Widget
1097 
1098     The text is set or replaced using setPlainText() which deletes the
1099     existing text and replaces it with the text passed to setPlainText().
1100 
1101     Text can be inserted using the QTextCursor class or using the
1102     convenience functions insertPlainText(), appendPlainText() or
1103     paste().
1104 
1105     By default, the text edit wraps words at whitespace to fit within
1106     the text edit widget. The setLineWrapMode() function is used to
1107     specify the kind of line wrap you want, \l WidgetWidth or \l
1108     NoWrap if you don't want any wrapping.  If you use word wrap to
1109     the widget's width \l WidgetWidth, you can specify whether to
1110     break on whitespace or anywhere with setWordWrapMode().
1111 
1112     The find() function can be used to find and select a given string
1113     within the text.
1114 
1115     If you want to limit the total number of paragraphs in a
1116     QPlainTextEdit, as it is for example useful in a log viewer, then
1117     you can use the maximumBlockCount property. The combination of
1118     setMaximumBlockCount() and appendPlainText() turns QPlainTextEdit
1119     into an efficient viewer for log text. The scrolling can be
1120     reduced with the centerOnScroll() property, making the log viewer
1121     even faster. Text can be formatted in a limited way, either using
1122     a syntax highlighter (see below), or by appending html-formatted
1123     text with appendHtml(). While QPlainTextEdit does not support
1124     complex rich text rendering with tables and floats, it does
1125     support limited paragraph-based formatting that you may need in a
1126     log viewer.
1127 
1128     \section2 Read-only Key Bindings
1129 
1130     When QPlainTextEdit is used read-only the key bindings are limited to
1131     navigation, and text may only be selected with the mouse:
1132     \table
1133     \header \li Keypresses \li Action
1134     \row \li Qt::UpArrow        \li Moves one line up.
1135     \row \li Qt::DownArrow        \li Moves one line down.
1136     \row \li Qt::LeftArrow        \li Moves one character to the left.
1137     \row \li Qt::RightArrow        \li Moves one character to the right.
1138     \row \li PageUp        \li Moves one (viewport) page up.
1139     \row \li PageDown        \li Moves one (viewport) page down.
1140     \row \li Home        \li Moves to the beginning of the text.
1141     \row \li End                \li Moves to the end of the text.
1142     \row \li Alt+Wheel
1143          \li Scrolls the page horizontally (the Wheel is the mouse wheel).
1144     \row \li Ctrl+Wheel        \li Zooms the text.
1145     \row \li Ctrl+A            \li Selects all text.
1146     \endtable
1147 
1148 
1149     \section1 Using QPlainTextEdit as an Editor
1150 
1151     All the information about using QPlainTextEdit as a display widget also
1152     applies here.
1153 
1154     Selection of text is handled by the QTextCursor class, which provides
1155     functionality for creating selections, retrieving the text contents or
1156     deleting selections. You can retrieve the object that corresponds with
1157     the user-visible cursor using the textCursor() method. If you want to set
1158     a selection in QPlainTextEdit just create one on a QTextCursor object and
1159     then make that cursor the visible cursor using setCursor(). The selection
1160     can be copied to the clipboard with copy(), or cut to the clipboard with
1161     cut(). The entire text can be selected using selectAll().
1162 
1163     QPlainTextEdit holds a QTextDocument object which can be retrieved using the
1164     document() method. You can also set your own document object using setDocument().
1165     QTextDocument emits a textChanged() signal if the text changes and it also
1166     provides a isModified() function which will return true if the text has been
1167     modified since it was either loaded or since the last call to setModified
1168     with false as argument. In addition it provides methods for undo and redo.
1169 
1170     \section2 Syntax Highlighting
1171 
1172     Just like QTextEdit, QPlainTextEdit works together with
1173     QSyntaxHighlighter.
1174 
1175     \section2 Editing Key Bindings
1176 
1177     The list of key bindings which are implemented for editing:
1178     \table
1179     \header \li Keypresses \li Action
1180     \row \li Backspace \li Deletes the character to the left of the cursor.
1181     \row \li Delete \li Deletes the character to the right of the cursor.
1182     \row \li Ctrl+C \li Copy the selected text to the clipboard.
1183     \row \li Ctrl+Insert \li Copy the selected text to the clipboard.
1184     \row \li Ctrl+K \li Deletes to the end of the line.
1185     \row \li Ctrl+V \li Pastes the clipboard text into text edit.
1186     \row \li Shift+Insert \li Pastes the clipboard text into text edit.
1187     \row \li Ctrl+X \li Deletes the selected text and copies it to the clipboard.
1188     \row \li Shift+Delete \li Deletes the selected text and copies it to the clipboard.
1189     \row \li Ctrl+Z \li Undoes the last operation.
1190     \row \li Ctrl+Y \li Redoes the last operation.
1191     \row \li LeftArrow \li Moves the cursor one character to the left.
1192     \row \li Ctrl+LeftArrow \li Moves the cursor one word to the left.
1193     \row \li RightArrow \li Moves the cursor one character to the right.
1194     \row \li Ctrl+RightArrow \li Moves the cursor one word to the right.
1195     \row \li UpArrow \li Moves the cursor one line up.
1196     \row \li Ctrl+UpArrow \li Moves the cursor one word up.
1197     \row \li DownArrow \li Moves the cursor one line down.
1198     \row \li Ctrl+Down Arrow \li Moves the cursor one word down.
1199     \row \li PageUp \li Moves the cursor one page up.
1200     \row \li PageDown \li Moves the cursor one page down.
1201     \row \li Home \li Moves the cursor to the beginning of the line.
1202     \row \li Ctrl+Home \li Moves the cursor to the beginning of the text.
1203     \row \li End \li Moves the cursor to the end of the line.
1204     \row \li Ctrl+End \li Moves the cursor to the end of the text.
1205     \row \li Alt+Wheel \li Scrolls the page horizontally (the Wheel is the mouse wheel).
1206     \row \li Ctrl+Wheel \li Zooms the text.
1207     \endtable
1208 
1209     To select (mark) text hold down the Shift key whilst pressing one
1210     of the movement keystrokes, for example, \e{Shift+Right Arrow}
1211     will select the character to the right, and \e{Shift+Ctrl+Right
1212     Arrow} will select the word to the right, etc.
1213 
1214    \section1 Differences to QTextEdit
1215 
1216    QPlainTextEdit is a thin class, implemented by using most of the
1217    technology that is behind QTextEdit and QTextDocument. Its
1218    performance benefits over QTextEdit stem mostly from using a
1219    different and simplified text layout called
1220    QPlainTextDocumentLayout on the text document (see
1221    QTextDocument::setDocumentLayout()). The plain text document layout
1222    does not support tables nor embedded frames, and \e{replaces a
1223    pixel-exact height calculation with a line-by-line respectively
1224    paragraph-by-paragraph scrolling approach}. This makes it possible
1225    to handle significantly larger documents, and still resize the
1226    editor with line wrap enabled in real time. It also makes for a
1227    fast log viewer (see setMaximumBlockCount()).
1228 
1229 
1230     \sa QTextDocument, QTextCursor, {Application Example},
1231         {Code Editor Example}, {Syntax Highlighter Example},
1232         {Rich Text Processing}
1233 
1234 */
1235 
1236 /*!
1237     \property QPlainTextEdit::plainText
1238 
1239     This property gets and sets the plain text editor's contents. The previous
1240     contents are removed and undo/redo history is reset when this property is set.
1241     currentCharFormat() is also reset, unless textCursor() is already at the
1242     beginning of the document.
1243 
1244     By default, for an editor with no contents, this property contains an empty string.
1245 */
1246 
1247 /*!
1248     \property QPlainTextEdit::undoRedoEnabled
1249     \brief whether undo and redo are enabled
1250 
1251     Users are only able to undo or redo actions if this property is
1252     true, and if there is an action that can be undone (or redone).
1253 
1254     By default, this property is \c true.
1255 */
1256 
1257 /*!
1258     \enum QPlainTextEdit::LineWrapMode
1259 
1260     \value NoWrap
1261     \value WidgetWidth
1262 */
1263 
1264 
1265 /*!
1266     Constructs an empty QPlainTextEdit with parent \a
1267     parent.
1268 */
QPlainTextEdit(QWidget * parent)1269 QPlainTextEdit::QPlainTextEdit(QWidget *parent)
1270     : QAbstractScrollArea(*new QPlainTextEditPrivate, parent)
1271 {
1272     Q_D(QPlainTextEdit);
1273     d->init();
1274 }
1275 
1276 /*!
1277     \internal
1278 */
QPlainTextEdit(QPlainTextEditPrivate & dd,QWidget * parent)1279 QPlainTextEdit::QPlainTextEdit(QPlainTextEditPrivate &dd, QWidget *parent)
1280     : QAbstractScrollArea(dd, parent)
1281 {
1282     Q_D(QPlainTextEdit);
1283     d->init();
1284 }
1285 
1286 /*!
1287     Constructs a QPlainTextEdit with parent \a parent. The text edit will display
1288     the plain text \a text.
1289 */
QPlainTextEdit(const QString & text,QWidget * parent)1290 QPlainTextEdit::QPlainTextEdit(const QString &text, QWidget *parent)
1291     : QAbstractScrollArea(*new QPlainTextEditPrivate, parent)
1292 {
1293     Q_D(QPlainTextEdit);
1294     d->init(text);
1295 }
1296 
1297 
1298 /*!
1299     Destructor.
1300 */
~QPlainTextEdit()1301 QPlainTextEdit::~QPlainTextEdit()
1302 {
1303     Q_D(QPlainTextEdit);
1304     if (d->documentLayoutPtr) {
1305         if (d->documentLayoutPtr->priv()->mainViewPrivate == d)
1306             d->documentLayoutPtr->priv()->mainViewPrivate = nullptr;
1307     }
1308 }
1309 
1310 /*!
1311     Makes \a document the new document of the text editor.
1312 
1313     The parent QObject of the provided document remains the owner
1314     of the object. If the current document is a child of the text
1315     editor, then it is deleted.
1316 
1317     The document must have a document layout that inherits
1318     QPlainTextDocumentLayout (see QTextDocument::setDocumentLayout()).
1319 
1320     \sa document()
1321 */
setDocument(QTextDocument * document)1322 void QPlainTextEdit::setDocument(QTextDocument *document)
1323 {
1324     Q_D(QPlainTextEdit);
1325     QPlainTextDocumentLayout *documentLayout = nullptr;
1326 
1327     if (!document) {
1328         document = new QTextDocument(d->control);
1329         documentLayout = new QPlainTextDocumentLayout(document);
1330         document->setDocumentLayout(documentLayout);
1331     } else {
1332         documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document->documentLayout());
1333         if (Q_UNLIKELY(!documentLayout)) {
1334             qWarning("QPlainTextEdit::setDocument: Document set does not support QPlainTextDocumentLayout");
1335             return;
1336         }
1337     }
1338     d->control->setDocument(document);
1339     if (!documentLayout->priv()->mainViewPrivate)
1340         documentLayout->priv()->mainViewPrivate = d;
1341     d->documentLayoutPtr = documentLayout;
1342     d->updateDefaultTextOption();
1343     d->relayoutDocument();
1344     d->_q_adjustScrollbars();
1345 }
1346 
1347 /*!
1348     Returns a pointer to the underlying document.
1349 
1350     \sa setDocument()
1351 */
document() const1352 QTextDocument *QPlainTextEdit::document() const
1353 {
1354     Q_D(const QPlainTextEdit);
1355     return d->control->document();
1356 }
1357 
1358 /*!
1359     \since 5.3
1360 
1361     \property QPlainTextEdit::placeholderText
1362     \brief the editor placeholder text
1363 
1364     Setting this property makes the editor display a grayed-out
1365     placeholder text as long as the document() is empty.
1366 
1367     By default, this property contains an empty string.
1368 
1369     \sa document()
1370 */
setPlaceholderText(const QString & placeholderText)1371 void QPlainTextEdit::setPlaceholderText(const QString &placeholderText)
1372 {
1373     Q_D(QPlainTextEdit);
1374     if (d->placeholderText != placeholderText) {
1375         d->placeholderText = placeholderText;
1376         if (d->control->document()->isEmpty())
1377             d->viewport->update();
1378     }
1379 }
1380 
placeholderText() const1381 QString QPlainTextEdit::placeholderText() const
1382 {
1383     Q_D(const QPlainTextEdit);
1384     return d->placeholderText;
1385 }
1386 
1387 /*!
1388     Sets the visible \a cursor.
1389 */
setTextCursor(const QTextCursor & cursor)1390 void QPlainTextEdit::setTextCursor(const QTextCursor &cursor)
1391 {
1392     doSetTextCursor(cursor);
1393 }
1394 
1395 /*!
1396     \internal
1397 
1398      This provides a hook for subclasses to intercept cursor changes.
1399 */
1400 
doSetTextCursor(const QTextCursor & cursor)1401 void QPlainTextEdit::doSetTextCursor(const QTextCursor &cursor)
1402 {
1403     Q_D(QPlainTextEdit);
1404     d->control->setTextCursor(cursor);
1405 }
1406 
1407 /*!
1408     Returns a copy of the QTextCursor that represents the currently visible cursor.
1409     Note that changes on the returned cursor do not affect QPlainTextEdit's cursor; use
1410     setTextCursor() to update the visible cursor.
1411  */
textCursor() const1412 QTextCursor QPlainTextEdit::textCursor() const
1413 {
1414     Q_D(const QPlainTextEdit);
1415     return d->control->textCursor();
1416 }
1417 
1418 /*!
1419     Returns the reference of the anchor at position \a pos, or an
1420     empty string if no anchor exists at that point.
1421 
1422     \since 4.7
1423  */
anchorAt(const QPoint & pos) const1424 QString QPlainTextEdit::anchorAt(const QPoint &pos) const
1425 {
1426     Q_D(const QPlainTextEdit);
1427     int cursorPos = d->control->hitTest(pos + QPointF(d->horizontalOffset(),
1428                                                      d->verticalOffset()),
1429                                         Qt::ExactHit);
1430     if (cursorPos < 0)
1431         return QString();
1432 
1433     QTextDocumentPrivate *pieceTable = document()->docHandle();
1434     QTextDocumentPrivate::FragmentIterator it = pieceTable->find(cursorPos);
1435     QTextCharFormat fmt = pieceTable->formatCollection()->charFormat(it->format);
1436     return fmt.anchorHref();
1437 }
1438 
1439 /*!
1440     Undoes the last operation.
1441 
1442     If there is no operation to undo, i.e. there is no undo step in
1443     the undo/redo history, nothing happens.
1444 
1445     \sa redo()
1446 */
undo()1447 void QPlainTextEdit::undo()
1448 {
1449     Q_D(QPlainTextEdit);
1450     d->control->undo();
1451 }
1452 
redo()1453 void QPlainTextEdit::redo()
1454 {
1455     Q_D(QPlainTextEdit);
1456     d->control->redo();
1457 }
1458 
1459 /*!
1460     \fn void QPlainTextEdit::redo()
1461 
1462     Redoes the last operation.
1463 
1464     If there is no operation to redo, i.e. there is no redo step in
1465     the undo/redo history, nothing happens.
1466 
1467     \sa undo()
1468 */
1469 
1470 #ifndef QT_NO_CLIPBOARD
1471 /*!
1472     Copies the selected text to the clipboard and deletes it from
1473     the text edit.
1474 
1475     If there is no selected text nothing happens.
1476 
1477     \sa copy(), paste()
1478 */
1479 
cut()1480 void QPlainTextEdit::cut()
1481 {
1482     Q_D(QPlainTextEdit);
1483     d->control->cut();
1484 }
1485 
1486 /*!
1487     Copies any selected text to the clipboard.
1488 
1489     \sa copyAvailable()
1490 */
1491 
copy()1492 void QPlainTextEdit::copy()
1493 {
1494     Q_D(QPlainTextEdit);
1495     d->control->copy();
1496 }
1497 
1498 /*!
1499     Pastes the text from the clipboard into the text edit at the
1500     current cursor position.
1501 
1502     If there is no text in the clipboard nothing happens.
1503 
1504     To change the behavior of this function, i.e. to modify what
1505     QPlainTextEdit can paste and how it is being pasted, reimplement the
1506     virtual canInsertFromMimeData() and insertFromMimeData()
1507     functions.
1508 
1509     \sa cut(), copy()
1510 */
1511 
paste()1512 void QPlainTextEdit::paste()
1513 {
1514     Q_D(QPlainTextEdit);
1515     d->control->paste();
1516 }
1517 #endif
1518 
1519 /*!
1520     Deletes all the text in the text edit.
1521 
1522     Notes:
1523     \list
1524     \li The undo/redo history is also cleared.
1525     \li currentCharFormat() is reset, unless textCursor()
1526     is already at the beginning of the document.
1527     \endlist
1528 
1529     \sa cut(), setPlainText()
1530 */
clear()1531 void QPlainTextEdit::clear()
1532 {
1533     Q_D(QPlainTextEdit);
1534     // clears and sets empty content
1535     d->control->topBlock = d->topLine = d->topLineFracture = 0;
1536     d->control->clear();
1537 }
1538 
1539 
1540 /*!
1541     Selects all text.
1542 
1543     \sa copy(), cut(), textCursor()
1544  */
selectAll()1545 void QPlainTextEdit::selectAll()
1546 {
1547     Q_D(QPlainTextEdit);
1548     d->control->selectAll();
1549 }
1550 
1551 /*! \internal
1552 */
event(QEvent * e)1553 bool QPlainTextEdit::event(QEvent *e)
1554 {
1555     Q_D(QPlainTextEdit);
1556 
1557 #ifndef QT_NO_CONTEXTMENU
1558     if (e->type() == QEvent::ContextMenu
1559         && static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) {
1560         ensureCursorVisible();
1561         const QPoint cursorPos = cursorRect().center();
1562         QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos));
1563         ce.setAccepted(e->isAccepted());
1564         const bool result = QAbstractScrollArea::event(&ce);
1565         e->setAccepted(ce.isAccepted());
1566         return result;
1567     }
1568 #endif // QT_NO_CONTEXTMENU
1569     if (e->type() == QEvent::ShortcutOverride
1570                || e->type() == QEvent::ToolTip) {
1571         d->sendControlEvent(e);
1572     }
1573 #ifdef QT_KEYPAD_NAVIGATION
1574     else if (e->type() == QEvent::EnterEditFocus || e->type() == QEvent::LeaveEditFocus) {
1575         if (QApplicationPrivate::keypadNavigationEnabled())
1576             d->sendControlEvent(e);
1577     }
1578 #endif
1579 #ifndef QT_NO_GESTURES
1580     else if (e->type() == QEvent::Gesture) {
1581         QGestureEvent *ge = static_cast<QGestureEvent *>(e);
1582         QPanGesture *g = static_cast<QPanGesture *>(ge->gesture(Qt::PanGesture));
1583         if (g) {
1584             QScrollBar *hBar = horizontalScrollBar();
1585             QScrollBar *vBar = verticalScrollBar();
1586             if (g->state() == Qt::GestureStarted)
1587                 d->originalOffsetY = vBar->value();
1588             QPointF offset = g->offset();
1589             if (!offset.isNull()) {
1590                 if (QGuiApplication::isRightToLeft())
1591                     offset.rx() *= -1;
1592                 // QPlainTextEdit scrolls by lines only in vertical direction
1593                 QFontMetrics fm(document()->defaultFont());
1594                 int lineHeight = fm.height();
1595                 int newX = hBar->value() - g->delta().x();
1596                 int newY = d->originalOffsetY - offset.y()/lineHeight;
1597                 hBar->setValue(newX);
1598                 vBar->setValue(newY);
1599             }
1600         }
1601         return true;
1602     }
1603 #endif // QT_NO_GESTURES
1604     return QAbstractScrollArea::event(e);
1605 }
1606 
1607 /*! \internal
1608 */
1609 
timerEvent(QTimerEvent * e)1610 void QPlainTextEdit::timerEvent(QTimerEvent *e)
1611 {
1612     Q_D(QPlainTextEdit);
1613     if (e->timerId() == d->autoScrollTimer.timerId()) {
1614         QRect visible = d->viewport->rect();
1615         QPoint pos;
1616         if (d->inDrag) {
1617             pos = d->autoScrollDragPos;
1618             visible.adjust(qMin(visible.width()/3,20), qMin(visible.height()/3,20),
1619                            -qMin(visible.width()/3,20), -qMin(visible.height()/3,20));
1620         } else {
1621             const QPoint globalPos = QCursor::pos();
1622             pos = d->viewport->mapFromGlobal(globalPos);
1623             QMouseEvent ev(QEvent::MouseMove, pos, d->viewport->mapTo(d->viewport->topLevelWidget(), pos), globalPos,
1624                            Qt::LeftButton, Qt::LeftButton, QGuiApplication::keyboardModifiers());
1625             mouseMoveEvent(&ev);
1626         }
1627         int deltaY = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height();
1628         int deltaX = qMax(pos.x() - visible.left(), visible.right() - pos.x()) - visible.width();
1629         int delta = qMax(deltaX, deltaY);
1630         if (delta >= 0) {
1631             if (delta < 7)
1632                 delta = 7;
1633             int timeout = 4900 / (delta * delta);
1634             d->autoScrollTimer.start(timeout, this);
1635 
1636             if (deltaY > 0)
1637                 d->vbar->triggerAction(pos.y() < visible.center().y() ?
1638                                        QAbstractSlider::SliderSingleStepSub
1639                                        : QAbstractSlider::SliderSingleStepAdd);
1640             if (deltaX > 0)
1641                 d->hbar->triggerAction(pos.x() < visible.center().x() ?
1642                                        QAbstractSlider::SliderSingleStepSub
1643                                        : QAbstractSlider::SliderSingleStepAdd);
1644         }
1645     }
1646 #ifdef QT_KEYPAD_NAVIGATION
1647     else if (e->timerId() == d->deleteAllTimer.timerId()) {
1648         d->deleteAllTimer.stop();
1649         clear();
1650     }
1651 #endif
1652 }
1653 
1654 /*!
1655     Changes the text of the text edit to the string \a text.
1656     Any previous text is removed.
1657 
1658     \a text is interpreted as plain text.
1659 
1660     Notes:
1661     \list
1662     \li The undo/redo history is also cleared.
1663     \li currentCharFormat() is reset, unless textCursor()
1664     is already at the beginning of the document.
1665     \endlist
1666 
1667     \sa toPlainText()
1668 */
1669 
setPlainText(const QString & text)1670 void QPlainTextEdit::setPlainText(const QString &text)
1671 {
1672     Q_D(QPlainTextEdit);
1673     d->control->setPlainText(text);
1674 }
1675 
1676 /*!
1677     \fn QString QPlainTextEdit::toPlainText() const
1678 
1679     Returns the text of the text edit as plain text.
1680 
1681     \sa QPlainTextEdit::setPlainText()
1682  */
1683 
1684 /*! \reimp
1685 */
keyPressEvent(QKeyEvent * e)1686 void QPlainTextEdit::keyPressEvent(QKeyEvent *e)
1687 {
1688     Q_D(QPlainTextEdit);
1689 
1690 #ifdef QT_KEYPAD_NAVIGATION
1691     switch (e->key()) {
1692         case Qt::Key_Select:
1693             if (QApplicationPrivate::keypadNavigationEnabled()) {
1694                 if (!(d->control->textInteractionFlags() & Qt::LinksAccessibleByKeyboard))
1695                     setEditFocus(!hasEditFocus());
1696                 else {
1697                     if (!hasEditFocus())
1698                         setEditFocus(true);
1699                     else {
1700                         QTextCursor cursor = d->control->textCursor();
1701                         QTextCharFormat charFmt = cursor.charFormat();
1702                         if (!cursor.hasSelection() || charFmt.anchorHref().isEmpty()) {
1703                             setEditFocus(false);
1704                         }
1705                     }
1706                 }
1707             }
1708             break;
1709         case Qt::Key_Back:
1710         case Qt::Key_No:
1711             if (!QApplicationPrivate::keypadNavigationEnabled()
1712                     || (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus())) {
1713                 e->ignore();
1714                 return;
1715             }
1716             break;
1717         default:
1718             if (QApplicationPrivate::keypadNavigationEnabled()) {
1719                 if (!hasEditFocus() && !(e->modifiers() & Qt::ControlModifier)) {
1720                     if (e->text()[0].isPrint()) {
1721                         setEditFocus(true);
1722                         clear();
1723                     } else {
1724                         e->ignore();
1725                         return;
1726                     }
1727                 }
1728             }
1729             break;
1730     }
1731 #endif
1732 
1733 #ifndef QT_NO_SHORTCUT
1734 
1735     Qt::TextInteractionFlags tif = d->control->textInteractionFlags();
1736 
1737     if (tif & Qt::TextSelectableByKeyboard){
1738         if (e == QKeySequence::SelectPreviousPage) {
1739             e->accept();
1740             d->pageUpDown(QTextCursor::Up, QTextCursor::KeepAnchor);
1741             return;
1742         } else if (e ==QKeySequence::SelectNextPage) {
1743             e->accept();
1744             d->pageUpDown(QTextCursor::Down, QTextCursor::KeepAnchor);
1745             return;
1746         }
1747     }
1748     if (tif & (Qt::TextSelectableByKeyboard | Qt::TextEditable)) {
1749         if (e == QKeySequence::MoveToPreviousPage) {
1750             e->accept();
1751             d->pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor);
1752             return;
1753         } else if (e == QKeySequence::MoveToNextPage) {
1754             e->accept();
1755             d->pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor);
1756             return;
1757         }
1758     }
1759 
1760     if (!(tif & Qt::TextEditable)) {
1761         switch (e->key()) {
1762             case Qt::Key_Space:
1763                 e->accept();
1764                 if (e->modifiers() & Qt::ShiftModifier)
1765                     d->vbar->triggerAction(QAbstractSlider::SliderPageStepSub);
1766                 else
1767                     d->vbar->triggerAction(QAbstractSlider::SliderPageStepAdd);
1768                 break;
1769             default:
1770                 d->sendControlEvent(e);
1771                 if (!e->isAccepted() && e->modifiers() == Qt::NoModifier) {
1772                     if (e->key() == Qt::Key_Home) {
1773                         d->vbar->triggerAction(QAbstractSlider::SliderToMinimum);
1774                         e->accept();
1775                     } else if (e->key() == Qt::Key_End) {
1776                         d->vbar->triggerAction(QAbstractSlider::SliderToMaximum);
1777                         e->accept();
1778                     }
1779                 }
1780                 if (!e->isAccepted()) {
1781                     QAbstractScrollArea::keyPressEvent(e);
1782                 }
1783         }
1784         return;
1785     }
1786 #endif // QT_NO_SHORTCUT
1787 
1788     d->sendControlEvent(e);
1789 #ifdef QT_KEYPAD_NAVIGATION
1790     if (!e->isAccepted()) {
1791         switch (e->key()) {
1792             case Qt::Key_Up:
1793             case Qt::Key_Down:
1794                 if (QApplicationPrivate::keypadNavigationEnabled()) {
1795                     // Cursor position didn't change, so we want to leave
1796                     // these keys to change focus.
1797                     e->ignore();
1798                     return;
1799                 }
1800                 break;
1801             case Qt::Key_Left:
1802             case Qt::Key_Right:
1803                 if (QApplicationPrivate::keypadNavigationEnabled()
1804                         && QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) {
1805                     // Same as for Key_Up and Key_Down.
1806                     e->ignore();
1807                     return;
1808                 }
1809                 break;
1810             case Qt::Key_Back:
1811                 if (!e->isAutoRepeat()) {
1812                     if (QApplicationPrivate::keypadNavigationEnabled()) {
1813                         if (document()->isEmpty()) {
1814                             setEditFocus(false);
1815                             e->accept();
1816                         } else if (!d->deleteAllTimer.isActive()) {
1817                             e->accept();
1818                             d->deleteAllTimer.start(750, this);
1819                         }
1820                     } else {
1821                         e->ignore();
1822                         return;
1823                     }
1824                 }
1825                 break;
1826             default: break;
1827         }
1828     }
1829 #endif
1830 }
1831 
1832 /*! \reimp
1833 */
keyReleaseEvent(QKeyEvent * e)1834 void QPlainTextEdit::keyReleaseEvent(QKeyEvent *e)
1835 {
1836 #ifdef QT_KEYPAD_NAVIGATION
1837     Q_D(QPlainTextEdit);
1838     if (QApplicationPrivate::keypadNavigationEnabled()) {
1839         if (!e->isAutoRepeat() && e->key() == Qt::Key_Back
1840             && d->deleteAllTimer.isActive()) {
1841             d->deleteAllTimer.stop();
1842             QTextCursor cursor = d->control->textCursor();
1843             QTextBlockFormat blockFmt = cursor.blockFormat();
1844 
1845             QTextList *list = cursor.currentList();
1846             if (list && cursor.atBlockStart()) {
1847                 list->remove(cursor.block());
1848             } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
1849                 blockFmt.setIndent(blockFmt.indent() - 1);
1850                 cursor.setBlockFormat(blockFmt);
1851             } else {
1852                 cursor.deletePreviousChar();
1853             }
1854             setTextCursor(cursor);
1855         }
1856     }
1857 #else
1858     QWidget::keyReleaseEvent(e);
1859 #endif
1860 }
1861 
1862 /*!
1863     Loads the resource specified by the given \a type and \a name.
1864 
1865     This function is an extension of QTextDocument::loadResource().
1866 
1867     \sa QTextDocument::loadResource()
1868 */
loadResource(int type,const QUrl & name)1869 QVariant QPlainTextEdit::loadResource(int type, const QUrl &name)
1870 {
1871     Q_UNUSED(type);
1872     Q_UNUSED(name);
1873     return QVariant();
1874 }
1875 
1876 /*! \reimp
1877 */
resizeEvent(QResizeEvent * e)1878 void QPlainTextEdit::resizeEvent(QResizeEvent *e)
1879 {
1880     Q_D(QPlainTextEdit);
1881     if (e->oldSize().width() != e->size().width())
1882         d->relayoutDocument();
1883     d->_q_adjustScrollbars();
1884 }
1885 
relayoutDocument()1886 void QPlainTextEditPrivate::relayoutDocument()
1887 {
1888     QTextDocument *doc = control->document();
1889     QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
1890     Q_ASSERT(documentLayout);
1891     documentLayoutPtr = documentLayout;
1892 
1893     int width = viewport->width();
1894 
1895     if (documentLayout->priv()->mainViewPrivate == nullptr
1896         || documentLayout->priv()->mainViewPrivate == this
1897         || width > documentLayout->textWidth()) {
1898         documentLayout->priv()->mainViewPrivate = this;
1899         documentLayout->setTextWidth(width);
1900     }
1901 }
1902 
fillBackground(QPainter * p,const QRectF & rect,QBrush brush,const QRectF & gradientRect=QRectF ())1903 static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, const QRectF &gradientRect = QRectF())
1904 {
1905     p->save();
1906     if (brush.style() >= Qt::LinearGradientPattern && brush.style() <= Qt::ConicalGradientPattern) {
1907         if (!gradientRect.isNull()) {
1908             QTransform m = QTransform::fromTranslate(gradientRect.left(), gradientRect.top());
1909             m.scale(gradientRect.width(), gradientRect.height());
1910             brush.setTransform(m);
1911             const_cast<QGradient *>(brush.gradient())->setCoordinateMode(QGradient::LogicalMode);
1912         }
1913     } else {
1914         p->setBrushOrigin(rect.topLeft());
1915     }
1916     p->fillRect(rect, brush);
1917     p->restore();
1918 }
1919 
1920 
1921 
1922 /*! \reimp
1923 */
paintEvent(QPaintEvent * e)1924 void QPlainTextEdit::paintEvent(QPaintEvent *e)
1925 {
1926     Q_D(QPlainTextEdit);
1927     QPainter painter(viewport());
1928     Q_ASSERT(qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout()));
1929 
1930     QPointF offset(contentOffset());
1931 
1932     QRect er = e->rect();
1933     QRect viewportRect = viewport()->rect();
1934 
1935     bool editable = !isReadOnly();
1936 
1937     QTextBlock block = firstVisibleBlock();
1938     qreal maximumWidth = document()->documentLayout()->documentSize().width();
1939 
1940     // Set a brush origin so that the WaveUnderline knows where the wave started
1941     painter.setBrushOrigin(offset);
1942 
1943     // keep right margin clean from full-width selection
1944     int maxX = offset.x() + qMax((qreal)viewportRect.width(), maximumWidth)
1945                - document()->documentMargin();
1946     er.setRight(qMin(er.right(), maxX));
1947     painter.setClipRect(er);
1948 
1949     if (d->placeholderVisible) {
1950         const QColor col = d->control->palette().placeholderText().color();
1951         painter.setPen(col);
1952         painter.setClipRect(e->rect());
1953         const int margin = int(document()->documentMargin());
1954         QRectF textRect = viewportRect.adjusted(margin, margin, 0, 0);
1955         painter.drawText(textRect, Qt::AlignTop | Qt::TextWordWrap, placeholderText());
1956     }
1957 
1958     QAbstractTextDocumentLayout::PaintContext context = getPaintContext();
1959     painter.setPen(context.palette.text().color());
1960 
1961     while (block.isValid()) {
1962 
1963         QRectF r = blockBoundingRect(block).translated(offset);
1964         QTextLayout *layout = block.layout();
1965 
1966         if (!block.isVisible()) {
1967             offset.ry() += r.height();
1968             block = block.next();
1969             continue;
1970         }
1971 
1972         if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
1973 
1974             QTextBlockFormat blockFormat = block.blockFormat();
1975 
1976             QBrush bg = blockFormat.background();
1977             if (bg != Qt::NoBrush) {
1978                 QRectF contentsRect = r;
1979                 contentsRect.setWidth(qMax(r.width(), maximumWidth));
1980                 fillBackground(&painter, contentsRect, bg);
1981             }
1982 
1983 
1984             QVector<QTextLayout::FormatRange> selections;
1985             int blpos = block.position();
1986             int bllen = block.length();
1987             for (int i = 0; i < context.selections.size(); ++i) {
1988                 const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i);
1989                 const int selStart = range.cursor.selectionStart() - blpos;
1990                 const int selEnd = range.cursor.selectionEnd() - blpos;
1991                 if (selStart < bllen && selEnd > 0
1992                     && selEnd > selStart) {
1993                     QTextLayout::FormatRange o;
1994                     o.start = selStart;
1995                     o.length = selEnd - selStart;
1996                     o.format = range.format;
1997                     selections.append(o);
1998                 } else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection)
1999                            && block.contains(range.cursor.position())) {
2000                     // for full width selections we don't require an actual selection, just
2001                     // a position to specify the line. that's more convenience in usage.
2002                     QTextLayout::FormatRange o;
2003                     QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos);
2004                     o.start = l.textStart();
2005                     o.length = l.textLength();
2006                     if (o.start + o.length == bllen - 1)
2007                         ++o.length; // include newline
2008                     o.format = range.format;
2009                     selections.append(o);
2010                 }
2011             }
2012 
2013             bool drawCursor = ((editable || (textInteractionFlags() & Qt::TextSelectableByKeyboard))
2014                                && context.cursorPosition >= blpos
2015                                && context.cursorPosition < blpos + bllen);
2016 
2017             bool drawCursorAsBlock = drawCursor && overwriteMode() ;
2018 
2019             if (drawCursorAsBlock) {
2020                 if (context.cursorPosition == blpos + bllen - 1) {
2021                     drawCursorAsBlock = false;
2022                 } else {
2023                     QTextLayout::FormatRange o;
2024                     o.start = context.cursorPosition - blpos;
2025                     o.length = 1;
2026                     o.format.setForeground(palette().base());
2027                     o.format.setBackground(palette().text());
2028                     selections.append(o);
2029                 }
2030             }
2031 
2032             layout->draw(&painter, offset, selections, er);
2033 
2034             if ((drawCursor && !drawCursorAsBlock)
2035                 || (editable && context.cursorPosition < -1
2036                     && !layout->preeditAreaText().isEmpty())) {
2037                 int cpos = context.cursorPosition;
2038                 if (cpos < -1)
2039                     cpos = layout->preeditAreaPosition() - (cpos + 2);
2040                 else
2041                     cpos -= blpos;
2042                 layout->drawCursor(&painter, offset, cpos, cursorWidth());
2043             }
2044         }
2045 
2046         offset.ry() += r.height();
2047         if (offset.y() > viewportRect.height())
2048             break;
2049         block = block.next();
2050     }
2051 
2052     if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom()
2053         && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) {
2054         painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().window());
2055     }
2056 }
2057 
2058 
updateDefaultTextOption()2059 void QPlainTextEditPrivate::updateDefaultTextOption()
2060 {
2061     QTextDocument *doc = control->document();
2062 
2063     QTextOption opt = doc->defaultTextOption();
2064     QTextOption::WrapMode oldWrapMode = opt.wrapMode();
2065 
2066     if (lineWrap == QPlainTextEdit::NoWrap)
2067         opt.setWrapMode(QTextOption::NoWrap);
2068     else
2069         opt.setWrapMode(wordWrap);
2070 
2071     if (opt.wrapMode() != oldWrapMode)
2072         doc->setDefaultTextOption(opt);
2073 }
2074 
2075 
2076 /*! \reimp
2077 */
mousePressEvent(QMouseEvent * e)2078 void QPlainTextEdit::mousePressEvent(QMouseEvent *e)
2079 {
2080     Q_D(QPlainTextEdit);
2081 #ifdef QT_KEYPAD_NAVIGATION
2082     if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus())
2083         setEditFocus(true);
2084 #endif
2085     d->sendControlEvent(e);
2086 }
2087 
2088 /*! \reimp
2089 */
mouseMoveEvent(QMouseEvent * e)2090 void QPlainTextEdit::mouseMoveEvent(QMouseEvent *e)
2091 {
2092     Q_D(QPlainTextEdit);
2093     d->inDrag = false; // paranoia
2094     const QPoint pos = e->pos();
2095     d->sendControlEvent(e);
2096     if (!(e->buttons() & Qt::LeftButton))
2097         return;
2098     if (e->source() == Qt::MouseEventNotSynthesized) {
2099         const QRect visible = d->viewport->rect();
2100         if (visible.contains(pos))
2101             d->autoScrollTimer.stop();
2102         else if (!d->autoScrollTimer.isActive())
2103             d->autoScrollTimer.start(100, this);
2104     }
2105 }
2106 
2107 /*! \reimp
2108 */
mouseReleaseEvent(QMouseEvent * e)2109 void QPlainTextEdit::mouseReleaseEvent(QMouseEvent *e)
2110 {
2111     Q_D(QPlainTextEdit);
2112     d->sendControlEvent(e);
2113     if (e->source() == Qt::MouseEventNotSynthesized && d->autoScrollTimer.isActive()) {
2114         d->autoScrollTimer.stop();
2115         d->ensureCursorVisible();
2116     }
2117 
2118     if (!isReadOnly() && rect().contains(e->pos()))
2119         d->handleSoftwareInputPanel(e->button(), d->clickCausedFocus);
2120     d->clickCausedFocus = 0;
2121 }
2122 
2123 /*! \reimp
2124 */
mouseDoubleClickEvent(QMouseEvent * e)2125 void QPlainTextEdit::mouseDoubleClickEvent(QMouseEvent *e)
2126 {
2127     Q_D(QPlainTextEdit);
2128     d->sendControlEvent(e);
2129 }
2130 
2131 /*! \reimp
2132 */
focusNextPrevChild(bool next)2133 bool QPlainTextEdit::focusNextPrevChild(bool next)
2134 {
2135     Q_D(const QPlainTextEdit);
2136     if (!d->tabChangesFocus && d->control->textInteractionFlags() & Qt::TextEditable)
2137         return false;
2138     return QAbstractScrollArea::focusNextPrevChild(next);
2139 }
2140 
2141 #ifndef QT_NO_CONTEXTMENU
2142 /*!
2143   \fn void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
2144 
2145   Shows the standard context menu created with createStandardContextMenu().
2146 
2147   If you do not want the text edit to have a context menu, you can set
2148   its \l contextMenuPolicy to Qt::NoContextMenu. If you want to
2149   customize the context menu, reimplement this function. If you want
2150   to extend the standard context menu, reimplement this function, call
2151   createStandardContextMenu() and extend the menu returned.
2152 
2153   Information about the event is passed in the \a event object.
2154 
2155   \snippet code/src_gui_widgets_qplaintextedit.cpp 0
2156 */
contextMenuEvent(QContextMenuEvent * e)2157 void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *e)
2158 {
2159     Q_D(QPlainTextEdit);
2160     d->sendControlEvent(e);
2161 }
2162 #endif // QT_NO_CONTEXTMENU
2163 
2164 #if QT_CONFIG(draganddrop)
2165 /*! \reimp
2166 */
dragEnterEvent(QDragEnterEvent * e)2167 void QPlainTextEdit::dragEnterEvent(QDragEnterEvent *e)
2168 {
2169     Q_D(QPlainTextEdit);
2170     d->inDrag = true;
2171     d->sendControlEvent(e);
2172 }
2173 
2174 /*! \reimp
2175 */
dragLeaveEvent(QDragLeaveEvent * e)2176 void QPlainTextEdit::dragLeaveEvent(QDragLeaveEvent *e)
2177 {
2178     Q_D(QPlainTextEdit);
2179     d->inDrag = false;
2180     d->autoScrollTimer.stop();
2181     d->sendControlEvent(e);
2182 }
2183 
2184 /*! \reimp
2185 */
dragMoveEvent(QDragMoveEvent * e)2186 void QPlainTextEdit::dragMoveEvent(QDragMoveEvent *e)
2187 {
2188     Q_D(QPlainTextEdit);
2189     d->autoScrollDragPos = e->pos();
2190     if (!d->autoScrollTimer.isActive())
2191         d->autoScrollTimer.start(100, this);
2192     d->sendControlEvent(e);
2193 }
2194 
2195 /*! \reimp
2196 */
dropEvent(QDropEvent * e)2197 void QPlainTextEdit::dropEvent(QDropEvent *e)
2198 {
2199     Q_D(QPlainTextEdit);
2200     d->inDrag = false;
2201     d->autoScrollTimer.stop();
2202     d->sendControlEvent(e);
2203 }
2204 
2205 #endif // QT_CONFIG(draganddrop)
2206 
2207 /*! \reimp
2208  */
inputMethodEvent(QInputMethodEvent * e)2209 void QPlainTextEdit::inputMethodEvent(QInputMethodEvent *e)
2210 {
2211     Q_D(QPlainTextEdit);
2212 #ifdef QT_KEYPAD_NAVIGATION
2213     if (d->control->textInteractionFlags() & Qt::TextEditable
2214         && QApplicationPrivate::keypadNavigationEnabled()
2215         && !hasEditFocus()) {
2216         setEditFocus(true);
2217         selectAll();    // so text is replaced rather than appended to
2218     }
2219 #endif
2220     d->sendControlEvent(e);
2221     ensureCursorVisible();
2222 }
2223 
2224 /*!\reimp
2225 */
scrollContentsBy(int dx,int)2226 void QPlainTextEdit::scrollContentsBy(int dx, int /*dy*/)
2227 {
2228     Q_D(QPlainTextEdit);
2229     d->setTopLine(d->vbar->value(), dx);
2230 }
2231 
2232 /*!\reimp
2233 */
inputMethodQuery(Qt::InputMethodQuery property) const2234 QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
2235 {
2236     return inputMethodQuery(property, QVariant());
2237 }
2238 
2239 /*!\internal
2240  */
inputMethodQuery(Qt::InputMethodQuery query,QVariant argument) const2241 QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery query, QVariant argument) const
2242 {
2243     Q_D(const QPlainTextEdit);
2244     switch (query) {
2245         case Qt::ImHints:
2246         case Qt::ImInputItemClipRectangle:
2247         return QWidget::inputMethodQuery(query);
2248     default:
2249         break;
2250     }
2251 
2252     const QPointF offset = contentOffset();
2253     switch (argument.userType()) {
2254     case QMetaType::QRectF:
2255         argument = argument.toRectF().translated(-offset);
2256         break;
2257     case QMetaType::QPointF:
2258         argument = argument.toPointF() - offset;
2259         break;
2260     case QMetaType::QRect:
2261         argument = argument.toRect().translated(-offset.toPoint());
2262         break;
2263     case QMetaType::QPoint:
2264         argument = argument.toPoint() - offset;
2265         break;
2266     default:
2267         break;
2268     }
2269 
2270     const QVariant v = d->control->inputMethodQuery(query, argument);
2271     switch (v.userType()) {
2272     case QMetaType::QRectF:
2273         return v.toRectF().translated(offset);
2274     case QMetaType::QPointF:
2275         return v.toPointF() + offset;
2276     case QMetaType::QRect:
2277         return v.toRect().translated(offset.toPoint());
2278     case QMetaType::QPoint:
2279         return v.toPoint() + offset.toPoint();
2280     default:
2281         break;
2282     }
2283     return v;
2284 }
2285 
2286 /*! \reimp
2287 */
focusInEvent(QFocusEvent * e)2288 void QPlainTextEdit::focusInEvent(QFocusEvent *e)
2289 {
2290     Q_D(QPlainTextEdit);
2291     if (e->reason() == Qt::MouseFocusReason) {
2292         d->clickCausedFocus = 1;
2293     }
2294     QAbstractScrollArea::focusInEvent(e);
2295     d->sendControlEvent(e);
2296 }
2297 
2298 /*! \reimp
2299 */
focusOutEvent(QFocusEvent * e)2300 void QPlainTextEdit::focusOutEvent(QFocusEvent *e)
2301 {
2302     Q_D(QPlainTextEdit);
2303     QAbstractScrollArea::focusOutEvent(e);
2304     d->sendControlEvent(e);
2305 }
2306 
2307 /*! \reimp
2308 */
showEvent(QShowEvent *)2309 void QPlainTextEdit::showEvent(QShowEvent *)
2310 {
2311     Q_D(QPlainTextEdit);
2312     if (d->showCursorOnInitialShow) {
2313         d->showCursorOnInitialShow = false;
2314         ensureCursorVisible();
2315     }
2316 }
2317 
2318 /*! \reimp
2319 */
changeEvent(QEvent * e)2320 void QPlainTextEdit::changeEvent(QEvent *e)
2321 {
2322     Q_D(QPlainTextEdit);
2323     QAbstractScrollArea::changeEvent(e);
2324     if (e->type() == QEvent::ApplicationFontChange
2325         || e->type() == QEvent::FontChange) {
2326         d->control->document()->setDefaultFont(font());
2327     }  else if(e->type() == QEvent::ActivationChange) {
2328         if (!isActiveWindow())
2329             d->autoScrollTimer.stop();
2330     } else if (e->type() == QEvent::EnabledChange) {
2331         e->setAccepted(isEnabled());
2332         d->control->setPalette(palette());
2333         d->sendControlEvent(e);
2334     } else if (e->type() == QEvent::PaletteChange) {
2335         d->control->setPalette(palette());
2336     } else if (e->type() == QEvent::LayoutDirectionChange) {
2337         d->sendControlEvent(e);
2338     }
2339 }
2340 
2341 /*! \reimp
2342 */
2343 #if QT_CONFIG(wheelevent)
wheelEvent(QWheelEvent * e)2344 void QPlainTextEdit::wheelEvent(QWheelEvent *e)
2345 {
2346     Q_D(QPlainTextEdit);
2347     if (!(d->control->textInteractionFlags() & Qt::TextEditable)) {
2348         if (e->modifiers() & Qt::ControlModifier) {
2349             float delta = e->angleDelta().y() / 120.f;
2350             zoomInF(delta);
2351             return;
2352         }
2353     }
2354     QAbstractScrollArea::wheelEvent(e);
2355     updateMicroFocus();
2356 }
2357 #endif
2358 
2359 /*!
2360     Zooms in on the text by making the base font size \a range
2361     points larger and recalculating all font sizes to be the new size.
2362     This does not change the size of any images.
2363 
2364     \sa zoomOut()
2365 */
zoomIn(int range)2366 void QPlainTextEdit::zoomIn(int range)
2367 {
2368     zoomInF(range);
2369 }
2370 
2371 /*!
2372     Zooms out on the text by making the base font size \a range points
2373     smaller and recalculating all font sizes to be the new size. This
2374     does not change the size of any images.
2375 
2376     \sa zoomIn()
2377 */
zoomOut(int range)2378 void QPlainTextEdit::zoomOut(int range)
2379 {
2380     zoomInF(-range);
2381 }
2382 
2383 /*!
2384     \internal
2385 */
zoomInF(float range)2386 void QPlainTextEdit::zoomInF(float range)
2387 {
2388     if (range == 0.f)
2389         return;
2390     QFont f = font();
2391     const float newSize = f.pointSizeF() + range;
2392     if (newSize <= 0)
2393         return;
2394     f.setPointSizeF(newSize);
2395     setFont(f);
2396 }
2397 
2398 #ifndef QT_NO_CONTEXTMENU
2399 /*!  This function creates the standard context menu which is shown
2400   when the user clicks on the text edit with the right mouse
2401   button. It is called from the default contextMenuEvent() handler.
2402   The popup menu's ownership is transferred to the caller.
2403 
2404   We recommend that you use the createStandardContextMenu(QPoint) version instead
2405   which will enable the actions that are sensitive to where the user clicked.
2406 */
2407 
createStandardContextMenu()2408 QMenu *QPlainTextEdit::createStandardContextMenu()
2409 {
2410     Q_D(QPlainTextEdit);
2411     return d->control->createStandardContextMenu(QPointF(), this);
2412 }
2413 
2414 /*!
2415   \since 5.5
2416   This function creates the standard context menu which is shown
2417   when the user clicks on the text edit with the right mouse
2418   button. It is called from the default contextMenuEvent() handler
2419   and it takes the \a position in document coordinates where the mouse click was.
2420   This can enable actions that are sensitive to the position where the user clicked.
2421   The popup menu's ownership is transferred to the caller.
2422 */
2423 
createStandardContextMenu(const QPoint & position)2424 QMenu *QPlainTextEdit::createStandardContextMenu(const QPoint &position)
2425 {
2426     Q_D(QPlainTextEdit);
2427     return d->control->createStandardContextMenu(position, this);
2428 }
2429 #endif // QT_NO_CONTEXTMENU
2430 
2431 /*!
2432   returns a QTextCursor at position \a pos (in viewport coordinates).
2433 */
cursorForPosition(const QPoint & pos) const2434 QTextCursor QPlainTextEdit::cursorForPosition(const QPoint &pos) const
2435 {
2436     Q_D(const QPlainTextEdit);
2437     return d->control->cursorForPosition(d->mapToContents(pos));
2438 }
2439 
2440 /*!
2441   returns a rectangle (in viewport coordinates) that includes the
2442   \a cursor.
2443  */
cursorRect(const QTextCursor & cursor) const2444 QRect QPlainTextEdit::cursorRect(const QTextCursor &cursor) const
2445 {
2446     Q_D(const QPlainTextEdit);
2447     if (cursor.isNull())
2448         return QRect();
2449 
2450     QRect r = d->control->cursorRect(cursor).toRect();
2451     r.translate(-d->horizontalOffset(),-(int)d->verticalOffset());
2452     return r;
2453 }
2454 
2455 /*!
2456   returns a rectangle (in viewport coordinates) that includes the
2457   cursor of the text edit.
2458  */
cursorRect() const2459 QRect QPlainTextEdit::cursorRect() const
2460 {
2461     Q_D(const QPlainTextEdit);
2462     QRect r = d->control->cursorRect().toRect();
2463     r.translate(-d->horizontalOffset(),-(int)d->verticalOffset());
2464     return r;
2465 }
2466 
2467 
2468 /*!
2469    \property QPlainTextEdit::overwriteMode
2470    \brief whether text entered by the user will overwrite existing text
2471 
2472    As with many text editors, the plain text editor widget can be configured
2473    to insert or overwrite existing text with new text entered by the user.
2474 
2475    If this property is \c true, existing text is overwritten, character-for-character
2476    by new text; otherwise, text is inserted at the cursor position, displacing
2477    existing text.
2478 
2479    By default, this property is \c false (new text does not overwrite existing text).
2480 */
2481 
overwriteMode() const2482 bool QPlainTextEdit::overwriteMode() const
2483 {
2484     Q_D(const QPlainTextEdit);
2485     return d->control->overwriteMode();
2486 }
2487 
setOverwriteMode(bool overwrite)2488 void QPlainTextEdit::setOverwriteMode(bool overwrite)
2489 {
2490     Q_D(QPlainTextEdit);
2491     d->control->setOverwriteMode(overwrite);
2492 }
2493 
2494 #if QT_DEPRECATED_SINCE(5, 10)
2495 /*!
2496     \property QPlainTextEdit::tabStopWidth
2497     \brief the tab stop width in pixels
2498     \deprecated in Qt 5.10. Use tabStopDistance instead.
2499 
2500     By default, this property contains a value of 80.
2501 */
2502 
tabStopWidth() const2503 int QPlainTextEdit::tabStopWidth() const
2504 {
2505     return qRound(tabStopDistance());
2506 }
2507 
setTabStopWidth(int width)2508 void QPlainTextEdit::setTabStopWidth(int width)
2509 {
2510     setTabStopDistance(width);
2511 }
2512 #endif
2513 
2514 /*!
2515     \property QPlainTextEdit::tabStopDistance
2516     \brief the tab stop distance in pixels
2517     \since 5.10
2518 
2519     By default, this property contains a value of 80.
2520 */
2521 
tabStopDistance() const2522 qreal QPlainTextEdit::tabStopDistance() const
2523 {
2524     Q_D(const QPlainTextEdit);
2525     return d->control->document()->defaultTextOption().tabStopDistance();
2526 }
2527 
setTabStopDistance(qreal distance)2528 void QPlainTextEdit::setTabStopDistance(qreal distance)
2529 {
2530     Q_D(QPlainTextEdit);
2531     QTextOption opt = d->control->document()->defaultTextOption();
2532     if (opt.tabStopDistance() == distance || distance < 0)
2533         return;
2534     opt.setTabStopDistance(distance);
2535     d->control->document()->setDefaultTextOption(opt);
2536 }
2537 
2538 
2539 /*!
2540     \property QPlainTextEdit::cursorWidth
2541 
2542     This property specifies the width of the cursor in pixels. The default value is 1.
2543 */
cursorWidth() const2544 int QPlainTextEdit::cursorWidth() const
2545 {
2546     Q_D(const QPlainTextEdit);
2547     return d->control->cursorWidth();
2548 }
2549 
setCursorWidth(int width)2550 void QPlainTextEdit::setCursorWidth(int width)
2551 {
2552     Q_D(QPlainTextEdit);
2553     d->control->setCursorWidth(width);
2554 }
2555 
2556 
2557 
2558 /*!
2559     This function allows temporarily marking certain regions in the document
2560     with a given color, specified as \a selections. This can be useful for
2561     example in a programming editor to mark a whole line of text with a given
2562     background color to indicate the existence of a breakpoint.
2563 
2564     \sa QTextEdit::ExtraSelection, extraSelections()
2565 */
setExtraSelections(const QList<QTextEdit::ExtraSelection> & selections)2566 void QPlainTextEdit::setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections)
2567 {
2568     Q_D(QPlainTextEdit);
2569     d->control->setExtraSelections(selections);
2570 }
2571 
2572 /*!
2573     Returns previously set extra selections.
2574 
2575     \sa setExtraSelections()
2576 */
extraSelections() const2577 QList<QTextEdit::ExtraSelection> QPlainTextEdit::extraSelections() const
2578 {
2579     Q_D(const QPlainTextEdit);
2580     return d->control->extraSelections();
2581 }
2582 
2583 /*!
2584     This function returns a new MIME data object to represent the contents
2585     of the text edit's current selection. It is called when the selection needs
2586     to be encapsulated into a new QMimeData object; for example, when a drag
2587     and drop operation is started, or when data is copied to the clipboard.
2588 
2589     If you reimplement this function, note that the ownership of the returned
2590     QMimeData object is passed to the caller. The selection can be retrieved
2591     by using the textCursor() function.
2592 */
createMimeDataFromSelection() const2593 QMimeData *QPlainTextEdit::createMimeDataFromSelection() const
2594 {
2595     Q_D(const QPlainTextEdit);
2596     return d->control->QWidgetTextControl::createMimeDataFromSelection();
2597 }
2598 
2599 /*!
2600     This function returns \c true if the contents of the MIME data object, specified
2601     by \a source, can be decoded and inserted into the document. It is called
2602     for example when during a drag operation the mouse enters this widget and it
2603     is necessary to determine whether it is possible to accept the drag.
2604  */
canInsertFromMimeData(const QMimeData * source) const2605 bool QPlainTextEdit::canInsertFromMimeData(const QMimeData *source) const
2606 {
2607     Q_D(const QPlainTextEdit);
2608     return d->control->QWidgetTextControl::canInsertFromMimeData(source);
2609 }
2610 
2611 /*!
2612     This function inserts the contents of the MIME data object, specified
2613     by \a source, into the text edit at the current cursor position. It is
2614     called whenever text is inserted as the result of a clipboard paste
2615     operation, or when the text edit accepts data from a drag and drop
2616     operation.
2617 */
insertFromMimeData(const QMimeData * source)2618 void QPlainTextEdit::insertFromMimeData(const QMimeData *source)
2619 {
2620     Q_D(QPlainTextEdit);
2621     d->control->QWidgetTextControl::insertFromMimeData(source);
2622 }
2623 
2624 /*!
2625     \property QPlainTextEdit::readOnly
2626     \brief whether the text edit is read-only
2627 
2628     In a read-only text edit the user can only navigate through the
2629     text and select text; modifying the text is not possible.
2630 
2631     This property's default is false.
2632 */
2633 
isReadOnly() const2634 bool QPlainTextEdit::isReadOnly() const
2635 {
2636     Q_D(const QPlainTextEdit);
2637     return !(d->control->textInteractionFlags() & Qt::TextEditable);
2638 }
2639 
setReadOnly(bool ro)2640 void QPlainTextEdit::setReadOnly(bool ro)
2641 {
2642     Q_D(QPlainTextEdit);
2643     Qt::TextInteractionFlags flags = Qt::NoTextInteraction;
2644     if (ro) {
2645         flags = Qt::TextSelectableByMouse;
2646     } else {
2647         flags = Qt::TextEditorInteraction;
2648     }
2649     d->control->setTextInteractionFlags(flags);
2650     setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this));
2651     QEvent event(QEvent::ReadOnlyChange);
2652     QCoreApplication::sendEvent(this, &event);
2653 }
2654 
2655 /*!
2656     \property QPlainTextEdit::textInteractionFlags
2657 
2658     Specifies how the label should interact with user input if it displays text.
2659 
2660     If the flags contain either Qt::LinksAccessibleByKeyboard or Qt::TextSelectableByKeyboard
2661     then the focus policy is also automatically set to Qt::ClickFocus.
2662 
2663     The default value depends on whether the QPlainTextEdit is read-only
2664     or editable.
2665 */
2666 
setTextInteractionFlags(Qt::TextInteractionFlags flags)2667 void QPlainTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
2668 {
2669     Q_D(QPlainTextEdit);
2670     d->control->setTextInteractionFlags(flags);
2671 }
2672 
textInteractionFlags() const2673 Qt::TextInteractionFlags QPlainTextEdit::textInteractionFlags() const
2674 {
2675     Q_D(const QPlainTextEdit);
2676     return d->control->textInteractionFlags();
2677 }
2678 
2679 /*!
2680     Merges the properties specified in \a modifier into the current character
2681     format by calling QTextCursor::mergeCharFormat on the editor's cursor.
2682     If the editor has a selection then the properties of \a modifier are
2683     directly applied to the selection.
2684 
2685     \sa QTextCursor::mergeCharFormat()
2686  */
mergeCurrentCharFormat(const QTextCharFormat & modifier)2687 void QPlainTextEdit::mergeCurrentCharFormat(const QTextCharFormat &modifier)
2688 {
2689     Q_D(QPlainTextEdit);
2690     d->control->mergeCurrentCharFormat(modifier);
2691 }
2692 
2693 /*!
2694     Sets the char format that is be used when inserting new text to \a
2695     format by calling QTextCursor::setCharFormat() on the editor's
2696     cursor.  If the editor has a selection then the char format is
2697     directly applied to the selection.
2698  */
setCurrentCharFormat(const QTextCharFormat & format)2699 void QPlainTextEdit::setCurrentCharFormat(const QTextCharFormat &format)
2700 {
2701     Q_D(QPlainTextEdit);
2702     d->control->setCurrentCharFormat(format);
2703 }
2704 
2705 /*!
2706     Returns the char format that is used when inserting new text.
2707  */
currentCharFormat() const2708 QTextCharFormat QPlainTextEdit::currentCharFormat() const
2709 {
2710     Q_D(const QPlainTextEdit);
2711     return d->control->currentCharFormat();
2712 }
2713 
2714 
2715 
2716 /*!
2717     Convenience slot that inserts \a text at the current
2718     cursor position.
2719 
2720     It is equivalent to
2721 
2722     \snippet code/src_gui_widgets_qplaintextedit.cpp 1
2723  */
insertPlainText(const QString & text)2724 void QPlainTextEdit::insertPlainText(const QString &text)
2725 {
2726     Q_D(QPlainTextEdit);
2727     d->control->insertPlainText(text);
2728 }
2729 
2730 
2731 /*!
2732     Moves the cursor by performing the given \a operation.
2733 
2734     If \a mode is QTextCursor::KeepAnchor, the cursor selects the text it moves over.
2735     This is the same effect that the user achieves when they hold down the Shift key
2736     and move the cursor with the cursor keys.
2737 
2738     \sa QTextCursor::movePosition()
2739 */
moveCursor(QTextCursor::MoveOperation operation,QTextCursor::MoveMode mode)2740 void QPlainTextEdit::moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode)
2741 {
2742     Q_D(QPlainTextEdit);
2743     d->control->moveCursor(operation, mode);
2744 }
2745 
2746 /*!
2747     Returns whether text can be pasted from the clipboard into the textedit.
2748 */
canPaste() const2749 bool QPlainTextEdit::canPaste() const
2750 {
2751     Q_D(const QPlainTextEdit);
2752     return d->control->canPaste();
2753 }
2754 
2755 /*!
2756     Convenience function to print the text edit's document to the given \a printer. This
2757     is equivalent to calling the print method on the document directly except that this
2758     function also supports QPrinter::Selection as print range.
2759 
2760     \sa QTextDocument::print()
2761 */
2762 #ifndef QT_NO_PRINTER
print(QPagedPaintDevice * printer) const2763 void QPlainTextEdit::print(QPagedPaintDevice *printer) const
2764 {
2765     Q_D(const QPlainTextEdit);
2766     d->control->print(printer);
2767 }
2768 #endif
2769 
2770 /*! \property QPlainTextEdit::tabChangesFocus
2771   \brief whether \uicontrol Tab changes focus or is accepted as input
2772 
2773   In some occasions text edits should not allow the user to input
2774   tabulators or change indentation using the \uicontrol Tab key, as this breaks
2775   the focus chain. The default is false.
2776 
2777 */
2778 
tabChangesFocus() const2779 bool QPlainTextEdit::tabChangesFocus() const
2780 {
2781     Q_D(const QPlainTextEdit);
2782     return d->tabChangesFocus;
2783 }
2784 
setTabChangesFocus(bool b)2785 void QPlainTextEdit::setTabChangesFocus(bool b)
2786 {
2787     Q_D(QPlainTextEdit);
2788     d->tabChangesFocus = b;
2789 }
2790 
2791 /*!
2792     \property QPlainTextEdit::documentTitle
2793     \brief the title of the document parsed from the text.
2794 
2795     By default, this property contains an empty string.
2796 */
2797 
2798 /*!
2799     \property QPlainTextEdit::lineWrapMode
2800     \brief the line wrap mode
2801 
2802     The default mode is WidgetWidth which causes words to be
2803     wrapped at the right edge of the text edit. Wrapping occurs at
2804     whitespace, keeping whole words intact. If you want wrapping to
2805     occur within words use setWordWrapMode().
2806 */
2807 
lineWrapMode() const2808 QPlainTextEdit::LineWrapMode QPlainTextEdit::lineWrapMode() const
2809 {
2810     Q_D(const QPlainTextEdit);
2811     return d->lineWrap;
2812 }
2813 
setLineWrapMode(LineWrapMode wrap)2814 void QPlainTextEdit::setLineWrapMode(LineWrapMode wrap)
2815 {
2816     Q_D(QPlainTextEdit);
2817     if (d->lineWrap == wrap)
2818         return;
2819     d->lineWrap = wrap;
2820     d->updateDefaultTextOption();
2821     d->relayoutDocument();
2822     d->_q_adjustScrollbars();
2823     ensureCursorVisible();
2824 }
2825 
2826 /*!
2827     \property QPlainTextEdit::wordWrapMode
2828     \brief the mode QPlainTextEdit will use when wrapping text by words
2829 
2830     By default, this property is set to QTextOption::WrapAtWordBoundaryOrAnywhere.
2831 
2832     \sa QTextOption::WrapMode
2833 */
2834 
wordWrapMode() const2835 QTextOption::WrapMode QPlainTextEdit::wordWrapMode() const
2836 {
2837     Q_D(const QPlainTextEdit);
2838     return d->wordWrap;
2839 }
2840 
setWordWrapMode(QTextOption::WrapMode mode)2841 void QPlainTextEdit::setWordWrapMode(QTextOption::WrapMode mode)
2842 {
2843     Q_D(QPlainTextEdit);
2844     if (mode == d->wordWrap)
2845         return;
2846     d->wordWrap = mode;
2847     d->updateDefaultTextOption();
2848 }
2849 
2850 /*!
2851     \property QPlainTextEdit::backgroundVisible
2852     \brief whether the palette background is visible outside the document area
2853 
2854     If set to true, the plain text edit paints the palette background
2855     on the viewport area not covered by the text document. Otherwise,
2856     if set to false, it won't. The feature makes it possible for
2857     the user to visually distinguish between the area of the document,
2858     painted with the base color of the palette, and the empty
2859     area not covered by any document.
2860 
2861     The default is false.
2862 */
2863 
backgroundVisible() const2864 bool QPlainTextEdit::backgroundVisible() const
2865 {
2866     Q_D(const QPlainTextEdit);
2867     return d->backgroundVisible;
2868 }
2869 
setBackgroundVisible(bool visible)2870 void QPlainTextEdit::setBackgroundVisible(bool visible)
2871 {
2872     Q_D(QPlainTextEdit);
2873     if (visible == d->backgroundVisible)
2874         return;
2875     d->backgroundVisible = visible;
2876     d->updateViewport();
2877 }
2878 
2879 /*!
2880     \property QPlainTextEdit::centerOnScroll
2881     \brief whether the cursor should be centered on screen
2882 
2883     If set to true, the plain text edit scrolls the document
2884     vertically to make the cursor visible at the center of the
2885     viewport. This also allows the text edit to scroll below the end
2886     of the document. Otherwise, if set to false, the plain text edit
2887     scrolls the smallest amount possible to ensure the cursor is
2888     visible.  The same algorithm is applied to any new line appended
2889     through appendPlainText().
2890 
2891     The default is false.
2892 
2893     \sa centerCursor(), ensureCursorVisible()
2894 */
2895 
centerOnScroll() const2896 bool QPlainTextEdit::centerOnScroll() const
2897 {
2898     Q_D(const QPlainTextEdit);
2899     return d->centerOnScroll;
2900 }
2901 
setCenterOnScroll(bool enabled)2902 void QPlainTextEdit::setCenterOnScroll(bool enabled)
2903 {
2904     Q_D(QPlainTextEdit);
2905     if (enabled == d->centerOnScroll)
2906         return;
2907     d->centerOnScroll = enabled;
2908     d->_q_adjustScrollbars();
2909 }
2910 
2911 
2912 
2913 /*!
2914     Finds the next occurrence of the string, \a exp, using the given
2915     \a options. Returns \c true if \a exp was found and changes the
2916     cursor to select the match; otherwise returns \c false.
2917 */
find(const QString & exp,QTextDocument::FindFlags options)2918 bool QPlainTextEdit::find(const QString &exp, QTextDocument::FindFlags options)
2919 {
2920     Q_D(QPlainTextEdit);
2921     return d->control->find(exp, options);
2922 }
2923 
2924 /*!
2925     \fn bool QPlainTextEdit::find(const QRegExp &exp, QTextDocument::FindFlags options)
2926 
2927     \since 5.3
2928     \overload
2929 
2930     Finds the next occurrence, matching the regular expression, \a exp, using the given
2931     \a options. The QTextDocument::FindCaseSensitively option is ignored for this overload,
2932     use QRegExp::caseSensitivity instead.
2933 
2934     Returns \c true if a match was found and changes the cursor to select the match;
2935     otherwise returns \c false.
2936 */
2937 #ifndef QT_NO_REGEXP
find(const QRegExp & exp,QTextDocument::FindFlags options)2938 bool QPlainTextEdit::find(const QRegExp &exp, QTextDocument::FindFlags options)
2939 {
2940     Q_D(QPlainTextEdit);
2941     return d->control->find(exp, options);
2942 }
2943 #endif
2944 
2945 /*!
2946     \fn bool QPlainTextEdit::find(const QRegularExpression &exp, QTextDocument::FindFlags options)
2947 
2948     \since 5.13
2949     \overload
2950 
2951     Finds the next occurrence, matching the regular expression, \a exp, using the given
2952     \a options. The QTextDocument::FindCaseSensitively option is ignored for this overload,
2953     use QRegularExpression::CaseInsensitiveOption instead.
2954 
2955     Returns \c true if a match was found and changes the cursor to select the match;
2956     otherwise returns \c false.
2957 */
2958 #if QT_CONFIG(regularexpression)
find(const QRegularExpression & exp,QTextDocument::FindFlags options)2959 bool QPlainTextEdit::find(const QRegularExpression &exp, QTextDocument::FindFlags options)
2960 {
2961     Q_D(QPlainTextEdit);
2962     return d->control->find(exp, options);
2963 }
2964 #endif
2965 
2966 /*!
2967     \fn void QPlainTextEdit::copyAvailable(bool yes)
2968 
2969     This signal is emitted when text is selected or de-selected in the
2970     text edit.
2971 
2972     When text is selected this signal will be emitted with \a yes set
2973     to true. If no text has been selected or if the selected text is
2974     de-selected this signal is emitted with \a yes set to false.
2975 
2976     If \a yes is true then copy() can be used to copy the selection to
2977     the clipboard. If \a yes is false then copy() does nothing.
2978 
2979     \sa selectionChanged()
2980 */
2981 
2982 
2983 /*!
2984     \fn void QPlainTextEdit::selectionChanged()
2985 
2986     This signal is emitted whenever the selection changes.
2987 
2988     \sa copyAvailable()
2989 */
2990 
2991 /*!
2992     \fn void QPlainTextEdit::cursorPositionChanged()
2993 
2994     This signal is emitted whenever the position of the
2995     cursor changed.
2996 */
2997 
2998 
2999 
3000 /*!
3001     \fn void QPlainTextEdit::updateRequest(const QRect &rect, int dy)
3002 
3003     This signal is emitted when the text document needs an update of
3004     the specified \a rect. If the text is scrolled, \a rect will cover
3005     the entire viewport area. If the text is scrolled vertically, \a
3006     dy carries the amount of pixels the viewport was scrolled.
3007 
3008     The purpose of the signal is to support extra widgets in plain
3009     text edit subclasses that e.g. show line numbers, breakpoints, or
3010     other extra information.
3011 */
3012 
3013 /*!  \fn void QPlainTextEdit::blockCountChanged(int newBlockCount);
3014 
3015     This signal is emitted whenever the block count changes. The new
3016     block count is passed in \a newBlockCount.
3017 */
3018 
3019 /*!  \fn void QPlainTextEdit::modificationChanged(bool changed);
3020 
3021     This signal is emitted whenever the content of the document
3022     changes in a way that affects the modification state. If \a
3023     changed is true, the document has been modified; otherwise it is
3024     false.
3025 
3026     For example, calling setModified(false) on a document and then
3027     inserting text causes the signal to get emitted. If you undo that
3028     operation, causing the document to return to its original
3029     unmodified state, the signal will get emitted again.
3030 */
3031 
3032 
3033 
3034 
append(const QString & text,Qt::TextFormat format)3035 void QPlainTextEditPrivate::append(const QString &text, Qt::TextFormat format)
3036 {
3037     Q_Q(QPlainTextEdit);
3038 
3039     QTextDocument *document = control->document();
3040     QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document->documentLayout());
3041     Q_ASSERT(documentLayout);
3042 
3043     int maximumBlockCount = document->maximumBlockCount();
3044     if (maximumBlockCount)
3045         document->setMaximumBlockCount(0);
3046 
3047     const bool atBottom =  q->isVisible()
3048                            && (control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset()
3049                                <= viewport->rect().bottom());
3050 
3051     if (!q->isVisible())
3052         showCursorOnInitialShow = true;
3053 
3054     bool documentSizeChangedBlocked = documentLayout->priv()->blockDocumentSizeChanged;
3055     documentLayout->priv()->blockDocumentSizeChanged = true;
3056 
3057     if (format == Qt::RichText)
3058         control->appendHtml(text);
3059     else if (format == Qt::PlainText)
3060         control->appendPlainText(text);
3061     else
3062         control->append(text);
3063 
3064     if (maximumBlockCount > 0) {
3065         if (document->blockCount() > maximumBlockCount) {
3066             bool blockUpdate = false;
3067             if (control->topBlock) {
3068                 control->topBlock--;
3069                 blockUpdate = true;
3070                 emit q->updateRequest(viewport->rect(), 0);
3071             }
3072 
3073             bool updatesBlocked = documentLayout->priv()->blockUpdate;
3074             documentLayout->priv()->blockUpdate = blockUpdate;
3075             QTextCursor cursor(document);
3076             cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
3077             cursor.removeSelectedText();
3078             documentLayout->priv()->blockUpdate = updatesBlocked;
3079         }
3080         document->setMaximumBlockCount(maximumBlockCount);
3081     }
3082 
3083     documentLayout->priv()->blockDocumentSizeChanged = documentSizeChangedBlocked;
3084     _q_adjustScrollbars();
3085 
3086 
3087     if (atBottom) {
3088         const bool needScroll =  !centerOnScroll
3089                                  || control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset()
3090                                  > viewport->rect().bottom();
3091         if (needScroll)
3092             vbar->setValue(vbar->maximum());
3093     }
3094 }
3095 
3096 
3097 /*!
3098     Appends a new paragraph with \a text to the end of the text edit.
3099 
3100     \sa appendHtml()
3101 */
3102 
appendPlainText(const QString & text)3103 void QPlainTextEdit::appendPlainText(const QString &text)
3104 {
3105     Q_D(QPlainTextEdit);
3106     d->append(text, Qt::PlainText);
3107 }
3108 
3109 /*!
3110     Appends a new paragraph with \a html to the end of the text edit.
3111 
3112     appendPlainText()
3113 */
3114 
appendHtml(const QString & html)3115 void QPlainTextEdit::appendHtml(const QString &html)
3116 {
3117     Q_D(QPlainTextEdit);
3118     d->append(html, Qt::RichText);
3119 }
3120 
ensureCursorVisible(bool center)3121 void QPlainTextEditPrivate::ensureCursorVisible(bool center)
3122 {
3123     Q_Q(QPlainTextEdit);
3124     QRect visible = viewport->rect();
3125     QRect cr = q->cursorRect();
3126     if (cr.top() < visible.top() || cr.bottom() > visible.bottom()) {
3127         ensureVisible(control->textCursor().position(), center);
3128     }
3129 
3130     const bool rtl = q->isRightToLeft();
3131     if (cr.left() < visible.left() || cr.right() > visible.right()) {
3132         int x = cr.center().x() + horizontalOffset() - visible.width()/2;
3133         hbar->setValue(rtl ? hbar->maximum() - x : x);
3134     }
3135 }
3136 
3137 /*!
3138     Ensures that the cursor is visible by scrolling the text edit if
3139     necessary.
3140 
3141     \sa centerCursor(), centerOnScroll
3142 */
ensureCursorVisible()3143 void QPlainTextEdit::ensureCursorVisible()
3144 {
3145     Q_D(QPlainTextEdit);
3146     d->ensureCursorVisible(d->centerOnScroll);
3147 }
3148 
3149 
3150 /*!  Scrolls the document in order to center the cursor vertically.
3151 
3152 \sa ensureCursorVisible(), centerOnScroll
3153  */
centerCursor()3154 void QPlainTextEdit::centerCursor()
3155 {
3156     Q_D(QPlainTextEdit);
3157     d->ensureVisible(textCursor().position(), true, true);
3158 }
3159 
3160 /*!
3161   Returns the first visible block.
3162 
3163   \sa blockBoundingRect()
3164  */
firstVisibleBlock() const3165 QTextBlock QPlainTextEdit::firstVisibleBlock() const
3166 {
3167     Q_D(const QPlainTextEdit);
3168     return d->control->firstVisibleBlock();
3169 }
3170 
3171 /*!  Returns the content's origin in viewport coordinates.
3172 
3173      The origin of the content of a plain text edit is always the top
3174      left corner of the first visible text block. The content offset
3175      is different from (0,0) when the text has been scrolled
3176      horizontally, or when the first visible block has been scrolled
3177      partially off the screen, i.e. the visible text does not start
3178      with the first line of the first visible block, or when the first
3179      visible block is the very first block and the editor displays a
3180      margin.
3181 
3182      \sa firstVisibleBlock(), horizontalScrollBar(), verticalScrollBar()
3183  */
contentOffset() const3184 QPointF QPlainTextEdit::contentOffset() const
3185 {
3186     Q_D(const QPlainTextEdit);
3187     return QPointF(-d->horizontalOffset(), -d->verticalOffset());
3188 }
3189 
3190 
3191 /*!  Returns the bounding rectangle of the text \a block in content
3192   coordinates. Translate the rectangle with the contentOffset() to get
3193   visual coordinates on the viewport.
3194 
3195   \sa firstVisibleBlock(), blockBoundingRect()
3196  */
blockBoundingGeometry(const QTextBlock & block) const3197 QRectF QPlainTextEdit::blockBoundingGeometry(const QTextBlock &block) const
3198 {
3199     Q_D(const QPlainTextEdit);
3200     return d->control->blockBoundingRect(block);
3201 }
3202 
3203 /*!
3204   Returns the bounding rectangle of the text \a block in the block's own coordinates.
3205 
3206   \sa blockBoundingGeometry()
3207  */
blockBoundingRect(const QTextBlock & block) const3208 QRectF QPlainTextEdit::blockBoundingRect(const QTextBlock &block) const
3209 {
3210     QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout());
3211     Q_ASSERT(documentLayout);
3212     return documentLayout->blockBoundingRect(block);
3213 }
3214 
3215 /*!
3216     \property QPlainTextEdit::blockCount
3217     \brief the number of text blocks in the document.
3218 
3219     By default, in an empty document, this property contains a value of 1.
3220 */
blockCount() const3221 int QPlainTextEdit::blockCount() const
3222 {
3223     return document()->blockCount();
3224 }
3225 
3226 /*!  Returns the paint context for the viewport(), useful only when
3227   reimplementing paintEvent().
3228  */
getPaintContext() const3229 QAbstractTextDocumentLayout::PaintContext QPlainTextEdit::getPaintContext() const
3230 {
3231     Q_D(const QPlainTextEdit);
3232     return d->control->getPaintContext(d->viewport);
3233 }
3234 
3235 /*!
3236     \property QPlainTextEdit::maximumBlockCount
3237     \brief the limit for blocks in the document.
3238 
3239     Specifies the maximum number of blocks the document may have. If there are
3240     more blocks in the document that specified with this property blocks are removed
3241     from the beginning of the document.
3242 
3243     A negative or zero value specifies that the document may contain an unlimited
3244     amount of blocks.
3245 
3246     The default value is 0.
3247 
3248     Note that setting this property will apply the limit immediately to the document
3249     contents. Setting this property also disables the undo redo history.
3250 
3251 */
3252 
3253 
3254 /*!
3255     \fn void QPlainTextEdit::textChanged()
3256 
3257     This signal is emitted whenever the document's content changes; for
3258     example, when text is inserted or deleted, or when formatting is applied.
3259 */
3260 
3261 /*!
3262     \fn void QPlainTextEdit::undoAvailable(bool available)
3263 
3264     This signal is emitted whenever undo operations become available
3265     (\a available is true) or unavailable (\a available is false).
3266 */
3267 
3268 /*!
3269     \fn void QPlainTextEdit::redoAvailable(bool available)
3270 
3271     This signal is emitted whenever redo operations become available
3272     (\a available is true) or unavailable (\a available is false).
3273 */
3274 
3275 QT_END_NAMESPACE
3276 
3277 #include "moc_qplaintextedit.cpp"
3278 #include "moc_qplaintextedit_p.cpp"
3279