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