source: trunk/src/gui/widgets/qplaintextedit.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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