summaryrefslogtreecommitdiffstats
path: root/src/widgets/widgets/qplaintextedit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/widgets/qplaintextedit.cpp')
-rw-r--r--src/widgets/widgets/qplaintextedit.cpp2995
1 files changed, 2995 insertions, 0 deletions
diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp
new file mode 100644
index 0000000000..7eb78acd82
--- /dev/null
+++ b/src/widgets/widgets/qplaintextedit.cpp
@@ -0,0 +1,2995 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplaintextedit_p.h"
+
+
+#include <qfont.h>
+#include <qpainter.h>
+#include <qevent.h>
+#include <qdebug.h>
+#include <qmime.h>
+#include <qdrag.h>
+#include <qclipboard.h>
+#include <qmenu.h>
+#include <qstyle.h>
+#include <qtimer.h>
+#include "private/qtextdocumentlayout_p.h"
+#include "private/qabstracttextdocumentlayout_p.h"
+#include "qtextdocument.h"
+#include "private/qtextdocument_p.h"
+#include "qtextlist.h"
+
+#include <qtextformat.h>
+#include <qdatetime.h>
+#include <qapplication.h>
+#include <limits.h>
+#include <qtexttable.h>
+#include <qvariant.h>
+#include <qinputcontext.h>
+
+#ifndef QT_NO_TEXTEDIT
+
+QT_BEGIN_NAMESPACE
+
+static inline bool shouldEnableInputMethod(QPlainTextEdit *plaintextedit)
+{
+ return !plaintextedit->isReadOnly();
+}
+
+class QPlainTextDocumentLayoutPrivate : public QAbstractTextDocumentLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QPlainTextDocumentLayout)
+public:
+ QPlainTextDocumentLayoutPrivate() {
+ mainViewPrivate = 0;
+ width = 0;
+ maximumWidth = 0;
+ maximumWidthBlockNumber = 0;
+ blockCount = 1;
+ blockUpdate = blockDocumentSizeChanged = false;
+ cursorWidth = 1;
+ textLayoutFlags = 0;
+ }
+
+ qreal width;
+ qreal maximumWidth;
+ int maximumWidthBlockNumber;
+ int blockCount;
+ QPlainTextEditPrivate *mainViewPrivate;
+ bool blockUpdate;
+ bool blockDocumentSizeChanged;
+ int cursorWidth;
+ int textLayoutFlags;
+
+ void layoutBlock(const QTextBlock &block);
+ qreal blockWidth(const QTextBlock &block);
+
+ void relayout();
+};
+
+
+
+/*! \class QPlainTextDocumentLayout
+ \since 4.4
+ \brief The QPlainTextDocumentLayout class implements a plain text layout for QTextDocument
+
+ \ingroup richtext-processing
+
+ A QPlainTextDocumentLayout is required for text documents that can
+ be display or edited in a QPlainTextEdit. See
+ QTextDocument::setDocumentLayout().
+
+ QPlainTextDocumentLayout uses the QAbstractTextDocumentLayout API
+ that QTextDocument requires, but redefines it partially in order to
+ support plain text better. For instances, it does not operate on
+ vertical pixels, but on paragraphs (called blocks) instead. The
+ height of a document is identical to the number of paragraphs it
+ contains. The layout also doesn't support tables or nested frames,
+ or any sort of advanced text layout that goes beyond a list of
+ paragraphs with syntax highlighting.
+
+*/
+
+
+
+/*!
+ Constructs a plain text document layout for the text \a document.
+ */
+QPlainTextDocumentLayout::QPlainTextDocumentLayout(QTextDocument *document)
+ :QAbstractTextDocumentLayout(* new QPlainTextDocumentLayoutPrivate, document) {
+}
+/*!
+ Destructs a plain text document layout.
+ */
+QPlainTextDocumentLayout::~QPlainTextDocumentLayout() {}
+
+
+/*!
+ \reimp
+ */
+void QPlainTextDocumentLayout::draw(QPainter *, const PaintContext &)
+{
+}
+
+/*!
+ \reimp
+ */
+int QPlainTextDocumentLayout::hitTest(const QPointF &, Qt::HitTestAccuracy ) const
+{
+// this function is used from
+// QAbstractTextDocumentLayout::anchorAt(), but is not
+// implementable in a plain text document layout, because the
+// layout depends on the top block and top line which depends on
+// the view
+ return -1;
+}
+
+/*!
+ \reimp
+ */
+int QPlainTextDocumentLayout::pageCount() const
+{ return 1; }
+
+/*!
+ \reimp
+ */
+QSizeF QPlainTextDocumentLayout::documentSize() const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return QSizeF(d->maximumWidth, document()->lineCount());
+}
+
+/*!
+ \reimp
+ */
+QRectF QPlainTextDocumentLayout::frameBoundingRect(QTextFrame *) const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return QRectF(0, 0, qMax(d->width, d->maximumWidth), qreal(INT_MAX));
+}
+
+/*!
+ \reimp
+ */
+QRectF QPlainTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
+{
+ if (!block.isValid()) { return QRectF(); }
+ QTextLayout *tl = block.layout();
+ if (!tl->lineCount())
+ const_cast<QPlainTextDocumentLayout*>(this)->layoutBlock(block);
+ QRectF br;
+ if (block.isVisible()) {
+ br = QRectF(QPointF(0, 0), tl->boundingRect().bottomRight());
+ if (tl->lineCount() == 1)
+ br.setWidth(qMax(br.width(), tl->lineAt(0).naturalTextWidth()));
+ qreal margin = document()->documentMargin();
+ br.adjust(0, 0, margin, 0);
+ if (!block.next().isValid())
+ br.adjust(0, 0, 0, margin);
+ }
+ return br;
+
+}
+
+/*!
+ Ensures that \a block has a valid layout
+ */
+void QPlainTextDocumentLayout::ensureBlockLayout(const QTextBlock &block) const
+{
+ if (!block.isValid())
+ return;
+ QTextLayout *tl = block.layout();
+ if (!tl->lineCount())
+ const_cast<QPlainTextDocumentLayout*>(this)->layoutBlock(block);
+}
+
+
+/*! \property QPlainTextDocumentLayout::cursorWidth
+
+ This property specifies the width of the cursor in pixels. The default value is 1.
+*/
+void QPlainTextDocumentLayout::setCursorWidth(int width)
+{
+ Q_D(QPlainTextDocumentLayout);
+ d->cursorWidth = width;
+}
+
+int QPlainTextDocumentLayout::cursorWidth() const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return d->cursorWidth;
+}
+
+QPlainTextDocumentLayoutPrivate *QPlainTextDocumentLayout::priv() const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return const_cast<QPlainTextDocumentLayoutPrivate*>(d);
+}
+
+
+/*!
+
+ Requests a complete update on all views.
+ */
+void QPlainTextDocumentLayout::requestUpdate()
+{
+ emit update(QRectF(0., -document()->documentMargin(), 1000000000., 1000000000.));
+}
+
+
+void QPlainTextDocumentLayout::setTextWidth(qreal newWidth)
+{
+ Q_D(QPlainTextDocumentLayout);
+ d->width = d->maximumWidth = newWidth;
+ d->relayout();
+}
+
+qreal QPlainTextDocumentLayout::textWidth() const
+{
+ Q_D(const QPlainTextDocumentLayout);
+ return d->width;
+}
+
+void QPlainTextDocumentLayoutPrivate::relayout()
+{
+ Q_Q(QPlainTextDocumentLayout);
+ QTextBlock block = q->document()->firstBlock();
+ while (block.isValid()) {
+ block.layout()->clearLayout();
+ block.setLineCount(block.isVisible() ? 1 : 0);
+ block = block.next();
+ }
+ emit q->update();
+}
+
+
+/*! \reimp
+ */
+void QPlainTextDocumentLayout::documentChanged(int from, int /*charsRemoved*/, int charsAdded)
+{
+ Q_D(QPlainTextDocumentLayout);
+ QTextDocument *doc = document();
+ int newBlockCount = doc->blockCount();
+
+ QTextBlock changeStartBlock = doc->findBlock(from);
+ QTextBlock changeEndBlock = doc->findBlock(qMax(0, from + charsAdded - 1));
+
+ if (changeStartBlock == changeEndBlock && newBlockCount == d->blockCount) {
+ QTextBlock block = changeStartBlock;
+ int blockLineCount = block.layout()->lineCount();
+ if (block.isValid() && blockLineCount) {
+ QRectF oldBr = blockBoundingRect(block);
+ layoutBlock(block);
+ QRectF newBr = blockBoundingRect(block);
+ if (newBr.height() == oldBr.height()) {
+ if (!d->blockUpdate)
+ emit updateBlock(block);
+ return;
+ }
+ }
+ } else {
+ QTextBlock block = changeStartBlock;
+ do {
+ block.clearLayout();
+ if (block == changeEndBlock)
+ break;
+ block = block.next();
+ } while(block.isValid());
+ }
+
+ if (newBlockCount != d->blockCount) {
+
+ int changeEnd = changeEndBlock.blockNumber();
+ int blockDiff = newBlockCount - d->blockCount;
+ int oldChangeEnd = changeEnd - blockDiff;
+
+ if (d->maximumWidthBlockNumber > oldChangeEnd)
+ d->maximumWidthBlockNumber += blockDiff;
+
+ d->blockCount = newBlockCount;
+ if (d->blockCount == 1)
+ d->maximumWidth = blockWidth(doc->firstBlock());
+
+ if (!d->blockDocumentSizeChanged)
+ emit documentSizeChanged(documentSize());
+
+ if (blockDiff == 1 && changeEnd == newBlockCount -1 ) {
+ if (!d->blockUpdate) {
+ QTextBlock b = changeStartBlock;
+ for(;;) {
+ emit updateBlock(b);
+ if (b == changeEndBlock)
+ break;
+ b = b.next();
+ }
+ }
+ return;
+ }
+ }
+
+ if (!d->blockUpdate)
+ emit update(QRectF(0., -doc->documentMargin(), 1000000000., 1000000000.)); // optimization potential
+}
+
+
+void QPlainTextDocumentLayout::layoutBlock(const QTextBlock &block)
+{
+ Q_D(QPlainTextDocumentLayout);
+ QTextDocument *doc = document();
+ qreal margin = doc->documentMargin();
+ qreal blockMaximumWidth = 0;
+
+ qreal height = 0;
+ QTextLayout *tl = block.layout();
+ QTextOption option = doc->defaultTextOption();
+ tl->setTextOption(option);
+
+ int extraMargin = 0;
+ if (option.flags() & QTextOption::AddSpaceForLineAndParagraphSeparators) {
+ QFontMetrics fm(block.charFormat().font());
+ extraMargin += fm.width(QChar(0x21B5));
+ }
+ tl->beginLayout();
+ qreal availableWidth = d->width;
+ if (availableWidth <= 0) {
+ availableWidth = qreal(INT_MAX); // similar to text edit with pageSize.width == 0
+ }
+ availableWidth -= 2*margin + extraMargin;
+ while (1) {
+ QTextLine line = tl->createLine();
+ if (!line.isValid())
+ break;
+ line.setLeadingIncluded(true);
+ line.setLineWidth(availableWidth);
+ line.setPosition(QPointF(margin, height));
+ height += line.height();
+ blockMaximumWidth = qMax(blockMaximumWidth, line.naturalTextWidth() + 2*margin);
+ }
+ tl->endLayout();
+
+ int previousLineCount = doc->lineCount();
+ const_cast<QTextBlock&>(block).setLineCount(block.isVisible() ? tl->lineCount() : 0);
+ int lineCount = doc->lineCount();
+
+ bool emitDocumentSizeChanged = previousLineCount != lineCount;
+ if (blockMaximumWidth > d->maximumWidth) {
+ // new longest line
+ d->maximumWidth = blockMaximumWidth;
+ d->maximumWidthBlockNumber = block.blockNumber();
+ emitDocumentSizeChanged = true;
+ } else if (block.blockNumber() == d->maximumWidthBlockNumber && blockMaximumWidth < d->maximumWidth) {
+ // longest line shrinking
+ QTextBlock b = doc->firstBlock();
+ d->maximumWidth = 0;
+ QTextBlock maximumBlock;
+ while (b.isValid()) {
+ qreal blockMaximumWidth = blockWidth(b);
+ if (blockMaximumWidth > d->maximumWidth) {
+ d->maximumWidth = blockMaximumWidth;
+ maximumBlock = b;
+ }
+ b = b.next();
+ }
+ if (maximumBlock.isValid()) {
+ d->maximumWidthBlockNumber = maximumBlock.blockNumber();
+ emitDocumentSizeChanged = true;
+ }
+ }
+ if (emitDocumentSizeChanged && !d->blockDocumentSizeChanged)
+ emit documentSizeChanged(documentSize());
+}
+
+qreal QPlainTextDocumentLayout::blockWidth(const QTextBlock &block)
+{
+ QTextLayout *layout = block.layout();
+ if (!layout->lineCount())
+ return 0; // only for layouted blocks
+ qreal blockWidth = 0;
+ for (int i = 0; i < layout->lineCount(); ++i) {
+ QTextLine line = layout->lineAt(i);
+ blockWidth = qMax(line.naturalTextWidth() + 8, blockWidth);
+ }
+ return blockWidth;
+}
+
+
+QPlainTextEditControl::QPlainTextEditControl(QPlainTextEdit *parent)
+ : QWidgetTextControl(parent), textEdit(parent),
+ topBlock(0)
+{
+ setAcceptRichText(false);
+}
+
+void QPlainTextEditPrivate::_q_cursorPositionChanged()
+{
+ pageUpDownLastCursorYIsValid = false;
+}
+
+void QPlainTextEditPrivate::_q_verticalScrollbarActionTriggered(int action) {
+ if (action == QAbstractSlider::SliderPageStepAdd) {
+ pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor, false);
+ } else if (action == QAbstractSlider::SliderPageStepSub) {
+ pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor, false);
+ }
+}
+
+QMimeData *QPlainTextEditControl::createMimeDataFromSelection() const {
+ QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
+ if (!ed)
+ return QWidgetTextControl::createMimeDataFromSelection();
+ return ed->createMimeDataFromSelection();
+ }
+bool QPlainTextEditControl::canInsertFromMimeData(const QMimeData *source) const {
+ QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
+ if (!ed)
+ return QWidgetTextControl::canInsertFromMimeData(source);
+ return ed->canInsertFromMimeData(source);
+}
+void QPlainTextEditControl::insertFromMimeData(const QMimeData *source) {
+ QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
+ if (!ed)
+ QWidgetTextControl::insertFromMimeData(source);
+ else
+ ed->insertFromMimeData(source);
+}
+
+int QPlainTextEditPrivate::verticalOffset(int topBlock, int topLine) const
+{
+ qreal offset = 0;
+ QTextDocument *doc = control->document();
+
+ if (topLine) {
+ QTextBlock currentBlock = doc->findBlockByNumber(topBlock);
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
+ Q_ASSERT(documentLayout);
+ QRectF r = documentLayout->blockBoundingRect(currentBlock);
+ Q_UNUSED(r);
+ QTextLayout *layout = currentBlock.layout();
+ if (layout && topLine <= layout->lineCount()) {
+ QTextLine line = layout->lineAt(topLine - 1);
+ const QRectF lr = line.naturalTextRect();
+ offset = lr.bottom();
+ }
+ }
+ if (topBlock == 0 && topLine == 0)
+ offset -= doc->documentMargin(); // top margin
+ return (int)offset;
+}
+
+
+int QPlainTextEditPrivate::verticalOffset() const {
+ return verticalOffset(control->topBlock, topLine);
+}
+
+
+QTextBlock QPlainTextEditControl::firstVisibleBlock() const
+{
+ return document()->findBlockByNumber(topBlock);
+}
+
+
+
+int QPlainTextEditControl::hitTest(const QPointF &point, Qt::HitTestAccuracy ) const {
+ int currentBlockNumber = topBlock;
+ QTextBlock currentBlock = document()->findBlockByNumber(currentBlockNumber);
+ if (!currentBlock.isValid())
+ return -1;
+
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout());
+ Q_ASSERT(documentLayout);
+
+ QPointF offset;
+ QRectF r = documentLayout->blockBoundingRect(currentBlock);
+ while (currentBlock.next().isValid() && r.bottom() + offset.y() <= point.y()) {
+ offset.ry() += r.height();
+ currentBlock = currentBlock.next();
+ ++currentBlockNumber;
+ r = documentLayout->blockBoundingRect(currentBlock);
+ }
+ while (currentBlock.previous().isValid() && r.top() + offset.y() > point.y()) {
+ offset.ry() -= r.height();
+ currentBlock = currentBlock.previous();
+ --currentBlockNumber;
+ r = documentLayout->blockBoundingRect(currentBlock);
+ }
+
+
+ if (!currentBlock.isValid())
+ return -1;
+ QTextLayout *layout = currentBlock.layout();
+ int off = 0;
+ QPointF pos = point - offset;
+ for (int i = 0; i < layout->lineCount(); ++i) {
+ QTextLine line = layout->lineAt(i);
+ const QRectF lr = line.naturalTextRect();
+ if (lr.top() > pos.y()) {
+ off = qMin(off, line.textStart());
+ } else if (lr.bottom() <= pos.y()) {
+ off = qMax(off, line.textStart() + line.textLength());
+ } else {
+ off = line.xToCursor(pos.x(), overwriteMode() ?
+ QTextLine::CursorOnCharacter : QTextLine::CursorBetweenCharacters);
+ break;
+ }
+ }
+
+ return currentBlock.position() + off;
+}
+
+QRectF QPlainTextEditControl::blockBoundingRect(const QTextBlock &block) const {
+ int currentBlockNumber = topBlock;
+ int blockNumber = block.blockNumber();
+ QTextBlock currentBlock = document()->findBlockByNumber(currentBlockNumber);
+ if (!currentBlock.isValid())
+ return QRectF();
+ Q_ASSERT(currentBlock.blockNumber() == currentBlockNumber);
+ QTextDocument *doc = document();
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
+ Q_ASSERT(documentLayout);
+
+ QPointF offset;
+ if (!block.isValid())
+ return QRectF();
+ QRectF r = documentLayout->blockBoundingRect(currentBlock);
+ int maxVerticalOffset = r.height();
+ while (currentBlockNumber < blockNumber && offset.y() - maxVerticalOffset <= 2* textEdit->viewport()->height()) {
+ offset.ry() += r.height();
+ currentBlock = currentBlock.next();
+ ++currentBlockNumber;
+ if (!currentBlock.isVisible()) {
+ currentBlock = doc->findBlockByLineNumber(currentBlock.firstLineNumber());
+ currentBlockNumber = currentBlock.blockNumber();
+ }
+ r = documentLayout->blockBoundingRect(currentBlock);
+ }
+ while (currentBlockNumber > blockNumber && offset.y() + maxVerticalOffset >= -textEdit->viewport()->height()) {
+ currentBlock = currentBlock.previous();
+ --currentBlockNumber;
+ while (!currentBlock.isVisible()) {
+ currentBlock = currentBlock.previous();
+ --currentBlockNumber;
+ }
+ if (!currentBlock.isValid())
+ break;
+
+ r = documentLayout->blockBoundingRect(currentBlock);
+ offset.ry() -= r.height();
+ }
+
+ if (currentBlockNumber != blockNumber) {
+ // fallback for blocks out of reach. Give it some geometry at
+ // least, and ensure the layout is up to date.
+ r = documentLayout->blockBoundingRect(block);
+ if (currentBlockNumber > blockNumber)
+ offset.ry() -= r.height();
+ }
+ r.translate(offset);
+ return r;
+}
+
+
+void QPlainTextEditPrivate::setTopLine(int visualTopLine, int dx)
+{
+ QTextDocument *doc = control->document();
+ QTextBlock block = doc->findBlockByLineNumber(visualTopLine);
+ int blockNumber = block.blockNumber();
+ int lineNumber = visualTopLine - block.firstLineNumber();
+ setTopBlock(blockNumber, lineNumber, dx);
+}
+
+void QPlainTextEditPrivate::setTopBlock(int blockNumber, int lineNumber, int dx)
+{
+ Q_Q(QPlainTextEdit);
+ blockNumber = qMax(0, blockNumber);
+ lineNumber = qMax(0, lineNumber);
+ QTextDocument *doc = control->document();
+ QTextBlock block = doc->findBlockByNumber(blockNumber);
+
+ int newTopLine = block.firstLineNumber() + lineNumber;
+ int maxTopLine = vbar->maximum();
+
+ if (newTopLine > maxTopLine) {
+ block = doc->findBlockByLineNumber(maxTopLine);
+ blockNumber = block.blockNumber();
+ lineNumber = maxTopLine - block.firstLineNumber();
+ }
+
+ bool vbarSignalsBlocked = vbar->blockSignals(true);
+ vbar->setValue(newTopLine);
+ vbar->blockSignals(vbarSignalsBlocked);
+
+ if (!dx && blockNumber == control->topBlock && lineNumber == topLine)
+ return;
+
+ if (viewport->updatesEnabled() && viewport->isVisible()) {
+ int dy = 0;
+ if (doc->findBlockByNumber(control->topBlock).isValid()) {
+ dy = (int)(-q->blockBoundingGeometry(block).y())
+ + verticalOffset() - verticalOffset(blockNumber, lineNumber);
+ }
+ control->topBlock = blockNumber;
+ topLine = lineNumber;
+
+ bool vbarSignalsBlocked = vbar->blockSignals(true);
+ vbar->setValue(block.firstLineNumber() + lineNumber);
+ vbar->blockSignals(vbarSignalsBlocked);
+
+ if (dx || dy)
+ viewport->scroll(q->isRightToLeft() ? -dx : dx, dy);
+ else
+ viewport->update();
+ emit q->updateRequest(viewport->rect(), dy);
+ } else {
+ control->topBlock = blockNumber;
+ topLine = lineNumber;
+ }
+
+}
+
+
+
+void QPlainTextEditPrivate::ensureVisible(int position, bool center, bool forceCenter) {
+ Q_Q(QPlainTextEdit);
+ QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset());
+ QTextBlock block = control->document()->findBlock(position);
+ if (!block.isValid())
+ return;
+ QRectF br = control->blockBoundingRect(block);
+ if (!br.isValid())
+ return;
+ QRectF lr = br;
+ QTextLine line = block.layout()->lineForTextPosition(position - block.position());
+ Q_ASSERT(line.isValid());
+ lr = line.naturalTextRect().translated(br.topLeft());
+
+ if (lr.bottom() >= visible.bottom() || (center && lr.top() < visible.top()) || forceCenter){
+
+ qreal height = visible.height();
+ if (center)
+ height /= 2;
+
+ qreal h = center ? line.naturalTextRect().center().y() : line.naturalTextRect().bottom();
+
+ QTextBlock previousVisibleBlock = block;
+ while (h < height && block.previous().isValid()) {
+ previousVisibleBlock = block;
+ do {
+ block = block.previous();
+ } while (!block.isVisible() && block.previous().isValid());
+ h += q->blockBoundingRect(block).height();
+ }
+
+ int l = 0;
+ int lineCount = block.layout()->lineCount();
+ int voffset = verticalOffset(block.blockNumber(), 0);
+ while (l < lineCount) {
+ QRectF lineRect = block.layout()->lineAt(l).naturalTextRect();
+ if (h - voffset - lineRect.top() <= height)
+ break;
+ ++l;
+ }
+
+ if (l >= lineCount) {
+ block = previousVisibleBlock;
+ l = 0;
+ }
+ setTopBlock(block.blockNumber(), l);
+ } else if (lr.top() < visible.top()) {
+ setTopBlock(block.blockNumber(), line.lineNumber());
+ }
+
+}
+
+
+void QPlainTextEditPrivate::updateViewport()
+{
+ Q_Q(QPlainTextEdit);
+ viewport->update();
+ emit q->updateRequest(viewport->rect(), 0);
+}
+
+QPlainTextEditPrivate::QPlainTextEditPrivate()
+ : control(0),
+ tabChangesFocus(false),
+ lineWrap(QPlainTextEdit::WidgetWidth),
+ wordWrap(QTextOption::WrapAtWordBoundaryOrAnywhere),
+ clickCausedFocus(0),topLine(0),
+ pageUpDownLastCursorYIsValid(false)
+{
+ showCursorOnInitialShow = true;
+ backgroundVisible = false;
+ centerOnScroll = false;
+ inDrag = false;
+}
+
+
+void QPlainTextEditPrivate::init(const QString &txt)
+{
+ Q_Q(QPlainTextEdit);
+ control = new QPlainTextEditControl(q);
+
+ QTextDocument *doc = new QTextDocument(control);
+ QAbstractTextDocumentLayout *layout = new QPlainTextDocumentLayout(doc);
+ doc->setDocumentLayout(layout);
+ control->setDocument(doc);
+
+ control->setPalette(q->palette());
+
+ QObject::connect(vbar, SIGNAL(actionTriggered(int)), q, SLOT(_q_verticalScrollbarActionTriggered(int)));
+
+ QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(updateMicroFocus()));
+ QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), q, SLOT(_q_adjustScrollbars()));
+ QObject::connect(control, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int)));
+ QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(_q_repaintContents(QRectF)));
+ QObject::connect(control, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool)));
+
+ QObject::connect(control, SIGNAL(textChanged()), q, SIGNAL(textChanged()));
+ QObject::connect(control, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
+ QObject::connect(control, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
+ QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool)));
+ QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
+ QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(_q_cursorPositionChanged()));
+ QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
+
+ QObject::connect(control, SIGNAL(textChanged()), q, SLOT(updateMicroFocus()));
+
+ // set a null page size initially to avoid any relayouting until the textedit
+ // is shown. relayoutDocument() will take care of setting the page size to the
+ // viewport dimensions later.
+ doc->setTextWidth(-1);
+ doc->documentLayout()->setPaintDevice(viewport);
+ doc->setDefaultFont(q->font());
+
+
+ if (!txt.isEmpty())
+ control->setPlainText(txt);
+
+ hbar->setSingleStep(20);
+ vbar->setSingleStep(1);
+
+ viewport->setBackgroundRole(QPalette::Base);
+ q->setAcceptDrops(true);
+ q->setFocusPolicy(Qt::WheelFocus);
+ q->setAttribute(Qt::WA_KeyCompression);
+ q->setAttribute(Qt::WA_InputMethodEnabled);
+
+#ifndef QT_NO_CURSOR
+ viewport->setCursor(Qt::IBeamCursor);
+#endif
+ originalOffsetY = 0;
+#ifdef Q_WS_WIN
+ setSingleFingerPanEnabled(true);
+#endif
+}
+
+void QPlainTextEditPrivate::_q_repaintContents(const QRectF &contentsRect)
+{
+ Q_Q(QPlainTextEdit);
+ if (!contentsRect.isValid()) {
+ updateViewport();
+ return;
+ }
+ const int xOffset = horizontalOffset();
+ const int yOffset = verticalOffset();
+ const QRect visibleRect(xOffset, yOffset, viewport->width(), viewport->height());
+
+ QRect r = contentsRect.adjusted(-1, -1, 1, 1).intersected(visibleRect).toAlignedRect();
+ if (r.isEmpty())
+ return;
+
+ r.translate(-xOffset, -yOffset);
+ viewport->update(r);
+ emit q->updateRequest(r, 0);
+}
+
+void QPlainTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode, bool moveCursor)
+{
+
+ Q_Q(QPlainTextEdit);
+
+ QTextCursor cursor = control->textCursor();
+ if (moveCursor) {
+ ensureCursorVisible();
+ if (!pageUpDownLastCursorYIsValid)
+ pageUpDownLastCursorY = control->cursorRect(cursor).top() - verticalOffset();
+ }
+
+ qreal lastY = pageUpDownLastCursorY;
+
+
+ if (op == QTextCursor::Down) {
+ QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset());
+ QTextBlock firstVisibleBlock = q->firstVisibleBlock();
+ QTextBlock block = firstVisibleBlock;
+ QRectF br = q->blockBoundingRect(block);
+ qreal h = 0;
+ int atEnd = false;
+ while (h + br.height() <= visible.bottom()) {
+ if (!block.next().isValid()) {
+ atEnd = true;
+ lastY = visible.bottom(); // set cursor to last line
+ break;
+ }
+ h += br.height();
+ block = block.next();
+ br = q->blockBoundingRect(block);
+ }
+
+ if (!atEnd) {
+ int line = 0;
+ qreal diff = visible.bottom() - h;
+ int lineCount = block.layout()->lineCount();
+ while (line < lineCount - 1) {
+ if (block.layout()->lineAt(line).naturalTextRect().bottom() > diff) {
+ // the first line that did not completely fit the screen
+ break;
+ }
+ ++line;
+ }
+ setTopBlock(block.blockNumber(), line);
+ }
+
+ if (moveCursor) {
+ // move using movePosition to keep the cursor's x
+ lastY += verticalOffset();
+ bool moved = false;
+ do {
+ moved = cursor.movePosition(op, moveMode);
+ } while (moved && control->cursorRect(cursor).top() < lastY);
+ }
+
+ } else if (op == QTextCursor::Up) {
+
+ QRectF visible = QRectF(viewport->rect()).translated(-q->contentOffset());
+ visible.translate(0, -visible.height()); // previous page
+ QTextBlock block = q->firstVisibleBlock();
+ qreal h = 0;
+ while (h >= visible.top()) {
+ if (!block.previous().isValid()) {
+ if (control->topBlock == 0 && topLine == 0) {
+ lastY = 0; // set cursor to first line
+ }
+ break;
+ }
+ block = block.previous();
+ QRectF br = q->blockBoundingRect(block);
+ h -= br.height();
+ }
+
+ int line = 0;
+ if (block.isValid()) {
+ qreal diff = visible.top() - h;
+ int lineCount = block.layout()->lineCount();
+ while (line < lineCount) {
+ if (block.layout()->lineAt(line).naturalTextRect().top() >= diff)
+ break;
+ ++line;
+ }
+ if (line == lineCount) {
+ if (block.next().isValid() && block.next() != q->firstVisibleBlock()) {
+ block = block.next();
+ line = 0;
+ } else {
+ --line;
+ }
+ }
+ }
+ setTopBlock(block.blockNumber(), line);
+
+ if (moveCursor) {
+ cursor.setVisualNavigation(true);
+ // move using movePosition to keep the cursor's x
+ lastY += verticalOffset();
+ bool moved = false;
+ do {
+ moved = cursor.movePosition(op, moveMode);
+ } while (moved && control->cursorRect(cursor).top() > lastY);
+ }
+ }
+
+ if (moveCursor) {
+ control->setTextCursor(cursor);
+ pageUpDownLastCursorYIsValid = true;
+ }
+}
+
+#ifndef QT_NO_SCROLLBAR
+
+void QPlainTextEditPrivate::_q_adjustScrollbars()
+{
+ Q_Q(QPlainTextEdit);
+ QTextDocument *doc = control->document();
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
+ Q_ASSERT(documentLayout);
+ bool documentSizeChangedBlocked = documentLayout->priv()->blockDocumentSizeChanged;
+ documentLayout->priv()->blockDocumentSizeChanged = true;
+ qreal margin = doc->documentMargin();
+
+ int vmax = 0;
+
+ int vSliderLength = 0;
+ if (!centerOnScroll && q->isVisible()) {
+ QTextBlock block = doc->lastBlock();
+ const qreal visible = viewport->rect().height() - margin - 1;
+ qreal y = 0;
+ int visibleFromBottom = 0;
+
+ while (block.isValid()) {
+ if (!block.isVisible()) {
+ block = block.previous();
+ continue;
+ }
+ y += documentLayout->blockBoundingRect(block).height();
+
+ QTextLayout *layout = block.layout();
+ int layoutLineCount = layout->lineCount();
+ if (y > visible) {
+ int lineNumber = 0;
+ while (lineNumber < layoutLineCount) {
+ QTextLine line = layout->lineAt(lineNumber);
+ const QRectF lr = line.naturalTextRect();
+ if (lr.top() >= y - visible)
+ break;
+ ++lineNumber;
+ }
+ if (lineNumber < layoutLineCount)
+ visibleFromBottom += (layoutLineCount - lineNumber);
+ break;
+
+ }
+ visibleFromBottom += layoutLineCount;
+ block = block.previous();
+ }
+ vmax = qMax(0, doc->lineCount() - visibleFromBottom);
+ vSliderLength = visibleFromBottom;
+
+ } else {
+ vmax = qMax(0, doc->lineCount() - 1);
+ vSliderLength = viewport->height() / q->fontMetrics().lineSpacing();
+ }
+
+
+
+ QSizeF documentSize = documentLayout->documentSize();
+ vbar->setRange(0, qMax(0, vmax));
+ vbar->setPageStep(vSliderLength);
+ int visualTopLine = vmax;
+ QTextBlock firstVisibleBlock = q->firstVisibleBlock();
+ if (firstVisibleBlock.isValid())
+ visualTopLine = firstVisibleBlock.firstLineNumber() + topLine;
+ bool vbarSignalsBlocked = vbar->blockSignals(true);
+ vbar->setValue(visualTopLine);
+ vbar->blockSignals(vbarSignalsBlocked);
+
+ hbar->setRange(0, (int)documentSize.width() - viewport->width());
+ hbar->setPageStep(viewport->width());
+ documentLayout->priv()->blockDocumentSizeChanged = documentSizeChangedBlocked;
+ setTopLine(vbar->value());
+}
+
+#endif
+
+
+void QPlainTextEditPrivate::ensureViewportLayouted()
+{
+}
+
+/*!
+ \class QPlainTextEdit
+ \since 4.4
+ \brief The QPlainTextEdit class provides a widget that is used to edit and display
+ plain text.
+
+ \ingroup richtext-processing
+
+
+ \tableofcontents
+
+ \section1 Introduction and Concepts
+
+ QPlainTextEdit is an advanced viewer/editor supporting plain
+ text. It is optimized to handle large documents and to respond
+ quickly to user input.
+
+ QPlainText uses very much the same technology and concepts as
+ QTextEdit, but is optimized for plain text handling.
+
+ QPlainTextEdit works on paragraphs and characters. A paragraph is
+ a formatted string which is word-wrapped to fit into the width of
+ the widget. By default when reading plain text, one newline
+ signifies a paragraph. A document consists of zero or more
+ paragraphs. Paragraphs are separated by hard line breaks. Each
+ character within a paragraph has its own attributes, for example,
+ font and color.
+
+ The shape of the mouse cursor on a QPlainTextEdit is
+ Qt::IBeamCursor by default. It can be changed through the
+ viewport()'s cursor property.
+
+ \section1 Using QPlainTextEdit as a Display Widget
+
+ The text is set or replaced using setPlainText() which deletes the
+ existing text and replaces it with the text passed to setPlainText().
+
+ Text can be inserted using the QTextCursor class or using the
+ convenience functions insertPlainText(), appendPlainText() or
+ paste().
+
+ By default, the text edit wraps words at whitespace to fit within
+ the text edit widget. The setLineWrapMode() function is used to
+ specify the kind of line wrap you want, \l WidgetWidth or \l
+ NoWrap if you don't want any wrapping. If you use word wrap to
+ the widget's width \l WidgetWidth, you can specify whether to
+ break on whitespace or anywhere with setWordWrapMode().
+
+ The find() function can be used to find and select a given string
+ within the text.
+
+ If you want to limit the total number of paragraphs in a
+ QPlainTextEdit, as it is for example useful in a log viewer, then
+ you can use the maximumBlockCount property. The combination of
+ setMaximumBlockCount() and appendPlainText() turns QPlainTextEdit
+ into an efficient viewer for log text. The scrolling can be
+ reduced with the centerOnScroll() property, making the log viewer
+ even faster. Text can be formatted in a limited way, either using
+ a syntax highlighter (see below), or by appending html-formatted
+ text with appendHtml(). While QPlainTextEdit does not support
+ complex rich text rendering with tables and floats, it does
+ support limited paragraph-based formatting that you may need in a
+ log viewer.
+
+ \section2 Read-only Key Bindings
+
+ When QPlainTextEdit is used read-only the key bindings are limited to
+ navigation, and text may only be selected with the mouse:
+ \table
+ \header \i Keypresses \i Action
+ \row \i Qt::UpArrow \i Moves one line up.
+ \row \i Qt::DownArrow \i Moves one line down.
+ \row \i Qt::LeftArrow \i Moves one character to the left.
+ \row \i Qt::RightArrow \i Moves one character to the right.
+ \row \i PageUp \i Moves one (viewport) page up.
+ \row \i PageDown \i Moves one (viewport) page down.
+ \row \i Home \i Moves to the beginning of the text.
+ \row \i End \i Moves to the end of the text.
+ \row \i Alt+Wheel
+ \i Scrolls the page horizontally (the Wheel is the mouse wheel).
+ \row \i Ctrl+Wheel \i Zooms the text.
+ \row \i Ctrl+A \i Selects all text.
+ \endtable
+
+
+ \section1 Using QPlainTextEdit as an Editor
+
+ All the information about using QPlainTextEdit as a display widget also
+ applies here.
+
+ Selection of text is handled by the QTextCursor class, which provides
+ functionality for creating selections, retrieving the text contents or
+ deleting selections. You can retrieve the object that corresponds with
+ the user-visible cursor using the textCursor() method. If you want to set
+ a selection in QPlainTextEdit just create one on a QTextCursor object and
+ then make that cursor the visible cursor using setCursor(). The selection
+ can be copied to the clipboard with copy(), or cut to the clipboard with
+ cut(). The entire text can be selected using selectAll().
+
+ QPlainTextEdit holds a QTextDocument object which can be retrieved using the
+ document() method. You can also set your own document object using setDocument().
+ QTextDocument emits a textChanged() signal if the text changes and it also
+ provides a isModified() function which will return true if the text has been
+ modified since it was either loaded or since the last call to setModified
+ with false as argument. In addition it provides methods for undo and redo.
+
+ \section2 Syntax Highlighting
+
+ Just like QTextEdit, QPlainTextEdit works together with
+ QSyntaxHighlighter.
+
+ \section2 Editing Key Bindings
+
+ The list of key bindings which are implemented for editing:
+ \table
+ \header \i Keypresses \i Action
+ \row \i Backspace \i Deletes the character to the left of the cursor.
+ \row \i Delete \i Deletes the character to the right of the cursor.
+ \row \i Ctrl+C \i Copy the selected text to the clipboard.
+ \row \i Ctrl+Insert \i Copy the selected text to the clipboard.
+ \row \i Ctrl+K \i Deletes to the end of the line.
+ \row \i Ctrl+V \i Pastes the clipboard text into text edit.
+ \row \i Shift+Insert \i Pastes the clipboard text into text edit.
+ \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard.
+ \row \i Shift+Delete \i Deletes the selected text and copies it to the clipboard.
+ \row \i Ctrl+Z \i Undoes the last operation.
+ \row \i Ctrl+Y \i Redoes the last operation.
+ \row \i LeftArrow \i Moves the cursor one character to the left.
+ \row \i Ctrl+LeftArrow \i Moves the cursor one word to the left.
+ \row \i RightArrow \i Moves the cursor one character to the right.
+ \row \i Ctrl+RightArrow \i Moves the cursor one word to the right.
+ \row \i UpArrow \i Moves the cursor one line up.
+ \row \i Ctrl+UpArrow \i Moves the cursor one word up.
+ \row \i DownArrow \i Moves the cursor one line down.
+ \row \i Ctrl+Down Arrow \i Moves the cursor one word down.
+ \row \i PageUp \i Moves the cursor one page up.
+ \row \i PageDown \i Moves the cursor one page down.
+ \row \i Home \i Moves the cursor to the beginning of the line.
+ \row \i Ctrl+Home \i Moves the cursor to the beginning of the text.
+ \row \i End \i Moves the cursor to the end of the line.
+ \row \i Ctrl+End \i Moves the cursor to the end of the text.
+ \row \i Alt+Wheel \i Scrolls the page horizontally (the Wheel is the mouse wheel).
+ \row \i Ctrl+Wheel \i Zooms the text.
+ \endtable
+
+ To select (mark) text hold down the Shift key whilst pressing one
+ of the movement keystrokes, for example, \e{Shift+Right Arrow}
+ will select the character to the right, and \e{Shift+Ctrl+Right
+ Arrow} will select the word to the right, etc.
+
+ \section1 Differences to QTextEdit
+
+ QPlainTextEdit is a thin class, implemented by using most of the
+ technology that is behind QTextEdit and QTextDocument. Its
+ performance benefits over QTextEdit stem mostly from using a
+ different and simplified text layout called
+ QPlainTextDocumentLayout on the text document (see
+ QTextDocument::setDocumentLayout()). The plain text document layout
+ does not support tables nor embedded frames, and \e{replaces a
+ pixel-exact height calculation with a line-by-line respectively
+ paragraph-by-paragraph scrolling approach}. This makes it possible
+ to handle significantly larger documents, and still resize the
+ editor with line wrap enabled in real time. It also makes for a
+ fast log viewer (see setMaximumBlockCount()).
+
+
+ \sa QTextDocument, QTextCursor, {Application Example},
+ {Code Editor Example}, {Syntax Highlighter Example},
+ {Rich Text Processing}
+
+*/
+
+/*!
+ \property QPlainTextEdit::plainText
+
+ This property gets and sets the plain text editor's contents. The previous
+ contents are removed and undo/redo history is reset when this property is set.
+
+ By default, for an editor with no contents, this property contains an empty string.
+*/
+
+/*!
+ \property QPlainTextEdit::undoRedoEnabled
+ \brief whether undo and redo are enabled
+
+ Users are only able to undo or redo actions if this property is
+ true, and if there is an action that can be undone (or redone).
+
+ By default, this property is true.
+*/
+
+/*!
+ \enum QPlainTextEdit::LineWrapMode
+
+ \value NoWrap
+ \value WidgetWidth
+*/
+
+
+/*!
+ Constructs an empty QPlainTextEdit with parent \a
+ parent.
+*/
+QPlainTextEdit::QPlainTextEdit(QWidget *parent)
+ : QAbstractScrollArea(*new QPlainTextEditPrivate, parent)
+{
+ Q_D(QPlainTextEdit);
+ d->init();
+}
+
+/*!
+ \internal
+*/
+QPlainTextEdit::QPlainTextEdit(QPlainTextEditPrivate &dd, QWidget *parent)
+ : QAbstractScrollArea(dd, parent)
+{
+ Q_D(QPlainTextEdit);
+ d->init();
+}
+
+/*!
+ Constructs a QPlainTextEdit with parent \a parent. The text edit will display
+ the plain text \a text.
+*/
+QPlainTextEdit::QPlainTextEdit(const QString &text, QWidget *parent)
+ : QAbstractScrollArea(*new QPlainTextEditPrivate, parent)
+{
+ Q_D(QPlainTextEdit);
+ d->init(text);
+}
+
+
+/*!
+ Destructor.
+*/
+QPlainTextEdit::~QPlainTextEdit()
+{
+ Q_D(QPlainTextEdit);
+ if (d->documentLayoutPtr) {
+ if (d->documentLayoutPtr->priv()->mainViewPrivate == d)
+ d->documentLayoutPtr->priv()->mainViewPrivate = 0;
+ }
+}
+
+/*!
+ Makes \a document the new document of the text editor.
+
+ The parent QObject of the provided document remains the owner
+ of the object. If the current document is a child of the text
+ editor, then it is deleted.
+
+ The document must have a document layout that inherits
+ QPlainTextDocumentLayout (see QTextDocument::setDocumentLayout()).
+
+ \sa document()
+*/
+void QPlainTextEdit::setDocument(QTextDocument *document)
+{
+ Q_D(QPlainTextEdit);
+ QPlainTextDocumentLayout *documentLayout = 0;
+
+ if (!document) {
+ document = new QTextDocument(d->control);
+ documentLayout = new QPlainTextDocumentLayout(document);
+ document->setDocumentLayout(documentLayout);
+ } else {
+ documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document->documentLayout());
+ if (!documentLayout) {
+ qWarning("QPlainTextEdit::setDocument: Document set does not support QPlainTextDocumentLayout");
+ return;
+ }
+ }
+ d->control->setDocument(document);
+ if (!documentLayout->priv()->mainViewPrivate)
+ documentLayout->priv()->mainViewPrivate = d;
+ d->documentLayoutPtr = documentLayout;
+ d->updateDefaultTextOption();
+ d->relayoutDocument();
+ d->_q_adjustScrollbars();
+}
+
+/*!
+ Returns a pointer to the underlying document.
+
+ \sa setDocument()
+*/
+QTextDocument *QPlainTextEdit::document() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->document();
+}
+
+/*!
+ Sets the visible \a cursor.
+*/
+void QPlainTextEdit::setTextCursor(const QTextCursor &cursor)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setTextCursor(cursor);
+}
+
+/*!
+ Returns a copy of the QTextCursor that represents the currently visible cursor.
+ Note that changes on the returned cursor do not affect QPlainTextEdit's cursor; use
+ setTextCursor() to update the visible cursor.
+ */
+QTextCursor QPlainTextEdit::textCursor() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->textCursor();
+}
+
+/*!
+ Returns the reference of the anchor at position \a pos, or an
+ empty string if no anchor exists at that point.
+
+ \since 4.7
+ */
+QString QPlainTextEdit::anchorAt(const QPoint &pos) const
+{
+ Q_D(const QPlainTextEdit);
+ int cursorPos = d->control->hitTest(pos + QPoint(d->horizontalOffset(),
+ d->verticalOffset()),
+ Qt::ExactHit);
+ if (cursorPos < 0)
+ return QString();
+
+ QTextDocumentPrivate *pieceTable = document()->docHandle();
+ QTextDocumentPrivate::FragmentIterator it = pieceTable->find(cursorPos);
+ QTextCharFormat fmt = pieceTable->formatCollection()->charFormat(it->format);
+ return fmt.anchorHref();
+}
+
+/*!
+ Undoes the last operation.
+
+ If there is no operation to undo, i.e. there is no undo step in
+ the undo/redo history, nothing happens.
+
+ \sa redo()
+*/
+void QPlainTextEdit::undo()
+{
+ Q_D(QPlainTextEdit);
+ d->control->undo();
+}
+
+void QPlainTextEdit::redo()
+{
+ Q_D(QPlainTextEdit);
+ d->control->redo();
+}
+
+/*!
+ \fn void QPlainTextEdit::redo()
+
+ Redoes the last operation.
+
+ If there is no operation to redo, i.e. there is no redo step in
+ the undo/redo history, nothing happens.
+
+ \sa undo()
+*/
+
+#ifndef QT_NO_CLIPBOARD
+/*!
+ Copies the selected text to the clipboard and deletes it from
+ the text edit.
+
+ If there is no selected text nothing happens.
+
+ \sa copy() paste()
+*/
+
+void QPlainTextEdit::cut()
+{
+ Q_D(QPlainTextEdit);
+ d->control->cut();
+}
+
+/*!
+ Copies any selected text to the clipboard.
+
+ \sa copyAvailable()
+*/
+
+void QPlainTextEdit::copy()
+{
+ Q_D(QPlainTextEdit);
+ d->control->copy();
+}
+
+/*!
+ Pastes the text from the clipboard into the text edit at the
+ current cursor position.
+
+ If there is no text in the clipboard nothing happens.
+
+ To change the behavior of this function, i.e. to modify what
+ QPlainTextEdit can paste and how it is being pasted, reimplement the
+ virtual canInsertFromMimeData() and insertFromMimeData()
+ functions.
+
+ \sa cut() copy()
+*/
+
+void QPlainTextEdit::paste()
+{
+ Q_D(QPlainTextEdit);
+ d->control->paste();
+}
+#endif
+
+/*!
+ Deletes all the text in the text edit.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa cut() setPlainText()
+*/
+void QPlainTextEdit::clear()
+{
+ Q_D(QPlainTextEdit);
+ // clears and sets empty content
+ d->control->topBlock = d->topLine = 0;
+ d->control->clear();
+}
+
+
+/*!
+ Selects all text.
+
+ \sa copy() cut() textCursor()
+ */
+void QPlainTextEdit::selectAll()
+{
+ Q_D(QPlainTextEdit);
+ d->control->selectAll();
+}
+
+/*! \internal
+*/
+bool QPlainTextEdit::event(QEvent *e)
+{
+ Q_D(QPlainTextEdit);
+
+#ifndef QT_NO_CONTEXTMENU
+ if (e->type() == QEvent::ContextMenu
+ && static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) {
+ ensureCursorVisible();
+ const QPoint cursorPos = cursorRect().center();
+ QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos));
+ ce.setAccepted(e->isAccepted());
+ const bool result = QAbstractScrollArea::event(&ce);
+ e->setAccepted(ce.isAccepted());
+ return result;
+ }
+#endif // QT_NO_CONTEXTMENU
+ if (e->type() == QEvent::ShortcutOverride
+ || e->type() == QEvent::ToolTip) {
+ d->sendControlEvent(e);
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ else if (e->type() == QEvent::EnterEditFocus || e->type() == QEvent::LeaveEditFocus) {
+ if (QApplication::keypadNavigationEnabled())
+ d->sendControlEvent(e);
+ }
+#endif
+#ifndef QT_NO_GESTURES
+ else if (e->type() == QEvent::Gesture) {
+ QGestureEvent *ge = static_cast<QGestureEvent *>(e);
+ QPanGesture *g = static_cast<QPanGesture *>(ge->gesture(Qt::PanGesture));
+ if (g) {
+ QScrollBar *hBar = horizontalScrollBar();
+ QScrollBar *vBar = verticalScrollBar();
+ if (g->state() == Qt::GestureStarted)
+ d->originalOffsetY = vBar->value();
+ QPointF offset = g->offset();
+ if (!offset.isNull()) {
+ if (QApplication::isRightToLeft())
+ offset.rx() *= -1;
+ // QPlainTextEdit scrolls by lines only in vertical direction
+ QFontMetrics fm(document()->defaultFont());
+ int lineHeight = fm.height();
+ int newX = hBar->value() - g->delta().x();
+ int newY = d->originalOffsetY - offset.y()/lineHeight;
+ hBar->setValue(newX);
+ vBar->setValue(newY);
+ }
+ }
+ return true;
+ }
+#endif // QT_NO_GESTURES
+ return QAbstractScrollArea::event(e);
+}
+
+/*! \internal
+*/
+
+void QPlainTextEdit::timerEvent(QTimerEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ if (e->timerId() == d->autoScrollTimer.timerId()) {
+ QRect visible = d->viewport->rect();
+ QPoint pos;
+ if (d->inDrag) {
+ pos = d->autoScrollDragPos;
+ visible.adjust(qMin(visible.width()/3,20), qMin(visible.height()/3,20),
+ -qMin(visible.width()/3,20), -qMin(visible.height()/3,20));
+ } else {
+ const QPoint globalPos = QCursor::pos();
+ pos = d->viewport->mapFromGlobal(globalPos);
+ QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
+ mouseMoveEvent(&ev);
+ }
+ int deltaY = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height();
+ int deltaX = qMax(pos.x() - visible.left(), visible.right() - pos.x()) - visible.width();
+ int delta = qMax(deltaX, deltaY);
+ if (delta >= 0) {
+ if (delta < 7)
+ delta = 7;
+ int timeout = 4900 / (delta * delta);
+ d->autoScrollTimer.start(timeout, this);
+
+ if (deltaY > 0)
+ d->vbar->triggerAction(pos.y() < visible.center().y() ?
+ QAbstractSlider::SliderSingleStepSub
+ : QAbstractSlider::SliderSingleStepAdd);
+ if (deltaX > 0)
+ d->hbar->triggerAction(pos.x() < visible.center().x() ?
+ QAbstractSlider::SliderSingleStepSub
+ : QAbstractSlider::SliderSingleStepAdd);
+ }
+ }
+#ifdef QT_KEYPAD_NAVIGATION
+ else if (e->timerId() == d->deleteAllTimer.timerId()) {
+ d->deleteAllTimer.stop();
+ clear();
+ }
+#endif
+}
+
+/*!
+ Changes the text of the text edit to the string \a text.
+ Any previous text is removed.
+
+ \a text is interpreted as plain text.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa toText()
+*/
+
+void QPlainTextEdit::setPlainText(const QString &text)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setPlainText(text);
+}
+
+/*!
+ \fn QString QPlainTextEdit::toPlainText() const
+
+ Returns the text of the text edit as plain text.
+
+ \sa QPlainTextEdit::setPlainText()
+ */
+
+/*! \reimp
+*/
+void QPlainTextEdit::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QPlainTextEdit);
+
+#ifdef QT_KEYPAD_NAVIGATION
+ switch (e->key()) {
+ case Qt::Key_Select:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!(d->control->textInteractionFlags() & Qt::LinksAccessibleByKeyboard))
+ setEditFocus(!hasEditFocus());
+ else {
+ if (!hasEditFocus())
+ setEditFocus(true);
+ else {
+ QTextCursor cursor = d->control->textCursor();
+ QTextCharFormat charFmt = cursor.charFormat();
+ if (!cursor.hasSelection() || charFmt.anchorHref().isEmpty()) {
+ setEditFocus(false);
+ }
+ }
+ }
+ }
+ break;
+ case Qt::Key_Back:
+ case Qt::Key_No:
+ if (!QApplication::keypadNavigationEnabled()
+ || (QApplication::keypadNavigationEnabled() && !hasEditFocus())) {
+ e->ignore();
+ return;
+ }
+ break;
+ default:
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!hasEditFocus() && !(e->modifiers() & Qt::ControlModifier)) {
+ if (e->text()[0].isPrint()) {
+ setEditFocus(true);
+ clear();
+ } else {
+ e->ignore();
+ return;
+ }
+ }
+ }
+ break;
+ }
+#endif
+
+#ifndef QT_NO_SHORTCUT
+
+ Qt::TextInteractionFlags tif = d->control->textInteractionFlags();
+
+ if (tif & Qt::TextSelectableByKeyboard){
+ if (e == QKeySequence::SelectPreviousPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Up, QTextCursor::KeepAnchor);
+ return;
+ } else if (e ==QKeySequence::SelectNextPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Down, QTextCursor::KeepAnchor);
+ return;
+ }
+ }
+ if (tif & (Qt::TextSelectableByKeyboard | Qt::TextEditable)) {
+ if (e == QKeySequence::MoveToPreviousPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor);
+ return;
+ } else if (e == QKeySequence::MoveToNextPage) {
+ e->accept();
+ d->pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor);
+ return;
+ }
+ }
+
+ if (!(tif & Qt::TextEditable)) {
+ switch (e->key()) {
+ case Qt::Key_Space:
+ e->accept();
+ if (e->modifiers() & Qt::ShiftModifier)
+ d->vbar->triggerAction(QAbstractSlider::SliderPageStepSub);
+ else
+ d->vbar->triggerAction(QAbstractSlider::SliderPageStepAdd);
+ break;
+ default:
+ d->sendControlEvent(e);
+ if (!e->isAccepted() && e->modifiers() == Qt::NoModifier) {
+ if (e->key() == Qt::Key_Home) {
+ d->vbar->triggerAction(QAbstractSlider::SliderToMinimum);
+ e->accept();
+ } else if (e->key() == Qt::Key_End) {
+ d->vbar->triggerAction(QAbstractSlider::SliderToMaximum);
+ e->accept();
+ }
+ }
+ if (!e->isAccepted()) {
+ QAbstractScrollArea::keyPressEvent(e);
+ }
+ }
+ return;
+ }
+#endif // QT_NO_SHORTCUT
+
+ d->sendControlEvent(e);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (!e->isAccepted()) {
+ switch (e->key()) {
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ if (QApplication::keypadNavigationEnabled()) {
+ // Cursor position didn't change, so we want to leave
+ // these keys to change focus.
+ e->ignore();
+ return;
+ }
+ break;
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ if (QApplication::keypadNavigationEnabled()
+ && QApplication::navigationMode() == Qt::NavigationModeKeypadDirectional) {
+ // Same as for Key_Up and Key_Down.
+ e->ignore();
+ return;
+ }
+ break;
+ case Qt::Key_Back:
+ if (!e->isAutoRepeat()) {
+ if (QApplication::keypadNavigationEnabled()) {
+ if (document()->isEmpty()) {
+ setEditFocus(false);
+ e->accept();
+ } else if (!d->deleteAllTimer.isActive()) {
+ e->accept();
+ d->deleteAllTimer.start(750, this);
+ }
+ } else {
+ e->ignore();
+ return;
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+#endif
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::keyReleaseEvent(QKeyEvent *e)
+{
+#ifdef QT_KEYPAD_NAVIGATION
+ Q_D(QPlainTextEdit);
+ if (QApplication::keypadNavigationEnabled()) {
+ if (!e->isAutoRepeat() && e->key() == Qt::Key_Back
+ && d->deleteAllTimer.isActive()) {
+ d->deleteAllTimer.stop();
+ QTextCursor cursor = d->control->textCursor();
+ QTextBlockFormat blockFmt = cursor.blockFormat();
+
+ QTextList *list = cursor.currentList();
+ if (list && cursor.atBlockStart()) {
+ list->remove(cursor.block());
+ } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
+ blockFmt.setIndent(blockFmt.indent() - 1);
+ cursor.setBlockFormat(blockFmt);
+ } else {
+ cursor.deletePreviousChar();
+ }
+ setTextCursor(cursor);
+ }
+ }
+#else
+ Q_UNUSED(e);
+#endif
+}
+
+/*!
+ Loads the resource specified by the given \a type and \a name.
+
+ This function is an extension of QTextDocument::loadResource().
+
+ \sa QTextDocument::loadResource()
+*/
+QVariant QPlainTextEdit::loadResource(int type, const QUrl &name)
+{
+ Q_UNUSED(type);
+ Q_UNUSED(name);
+ return QVariant();
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::resizeEvent(QResizeEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ if (e->oldSize().width() != e->size().width())
+ d->relayoutDocument();
+ d->_q_adjustScrollbars();
+}
+
+void QPlainTextEditPrivate::relayoutDocument()
+{
+ QTextDocument *doc = control->document();
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout());
+ Q_ASSERT(documentLayout);
+ documentLayoutPtr = documentLayout;
+
+ int width = viewport->width();
+
+ if (documentLayout->priv()->mainViewPrivate == 0
+ || documentLayout->priv()->mainViewPrivate == this
+ || width > documentLayout->textWidth()) {
+ documentLayout->priv()->mainViewPrivate = this;
+ documentLayout->setTextWidth(width);
+ }
+}
+
+static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, QRectF gradientRect = QRectF())
+{
+ p->save();
+ if (brush.style() >= Qt::LinearGradientPattern && brush.style() <= Qt::ConicalGradientPattern) {
+ if (!gradientRect.isNull()) {
+ QTransform m = QTransform::fromTranslate(gradientRect.left(), gradientRect.top());
+ m.scale(gradientRect.width(), gradientRect.height());
+ brush.setTransform(m);
+ const_cast<QGradient *>(brush.gradient())->setCoordinateMode(QGradient::LogicalMode);
+ }
+ } else {
+ p->setBrushOrigin(rect.topLeft());
+ }
+ p->fillRect(rect, brush);
+ p->restore();
+}
+
+
+
+/*! \reimp
+*/
+void QPlainTextEdit::paintEvent(QPaintEvent *e)
+{
+ QPainter painter(viewport());
+ Q_ASSERT(qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout()));
+
+ QPointF offset(contentOffset());
+
+ QRect er = e->rect();
+ QRect viewportRect = viewport()->rect();
+
+ bool editable = !isReadOnly();
+
+ QTextBlock block = firstVisibleBlock();
+ qreal maximumWidth = document()->documentLayout()->documentSize().width();
+
+ // Set a brush origin so that the WaveUnderline knows where the wave started
+ painter.setBrushOrigin(offset);
+
+ // keep right margin clean from full-width selection
+ int maxX = offset.x() + qMax((qreal)viewportRect.width(), maximumWidth)
+ - document()->documentMargin();
+ er.setRight(qMin(er.right(), maxX));
+ painter.setClipRect(er);
+
+
+ QAbstractTextDocumentLayout::PaintContext context = getPaintContext();
+
+ while (block.isValid()) {
+
+ QRectF r = blockBoundingRect(block).translated(offset);
+ QTextLayout *layout = block.layout();
+
+ if (!block.isVisible()) {
+ offset.ry() += r.height();
+ block = block.next();
+ continue;
+ }
+
+ if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
+
+ QTextBlockFormat blockFormat = block.blockFormat();
+
+ QBrush bg = blockFormat.background();
+ if (bg != Qt::NoBrush) {
+ QRectF contentsRect = r;
+ contentsRect.setWidth(qMax(r.width(), maximumWidth));
+ fillBackground(&painter, contentsRect, bg);
+ }
+
+
+ QVector<QTextLayout::FormatRange> selections;
+ int blpos = block.position();
+ int bllen = block.length();
+ for (int i = 0; i < context.selections.size(); ++i) {
+ const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i);
+ const int selStart = range.cursor.selectionStart() - blpos;
+ const int selEnd = range.cursor.selectionEnd() - blpos;
+ if (selStart < bllen && selEnd > 0
+ && selEnd > selStart) {
+ QTextLayout::FormatRange o;
+ o.start = selStart;
+ o.length = selEnd - selStart;
+ o.format = range.format;
+ selections.append(o);
+ } else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection)
+ && block.contains(range.cursor.position())) {
+ // for full width selections we don't require an actual selection, just
+ // a position to specify the line. that's more convenience in usage.
+ QTextLayout::FormatRange o;
+ QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos);
+ o.start = l.textStart();
+ o.length = l.textLength();
+ if (o.start + o.length == bllen - 1)
+ ++o.length; // include newline
+ o.format = range.format;
+ selections.append(o);
+ }
+ }
+
+ bool drawCursor = (editable
+ && context.cursorPosition >= blpos
+ && context.cursorPosition < blpos + bllen);
+
+ bool drawCursorAsBlock = drawCursor && overwriteMode() ;
+
+ if (drawCursorAsBlock) {
+ if (context.cursorPosition == blpos + bllen - 1) {
+ drawCursorAsBlock = false;
+ } else {
+ QTextLayout::FormatRange o;
+ o.start = context.cursorPosition - blpos;
+ o.length = 1;
+ o.format.setForeground(palette().base());
+ o.format.setBackground(palette().text());
+ selections.append(o);
+ }
+ }
+
+
+ layout->draw(&painter, offset, selections, er);
+ if ((drawCursor && !drawCursorAsBlock)
+ || (editable && context.cursorPosition < -1
+ && !layout->preeditAreaText().isEmpty())) {
+ int cpos = context.cursorPosition;
+ if (cpos < -1)
+ cpos = layout->preeditAreaPosition() - (cpos + 2);
+ else
+ cpos -= blpos;
+ layout->drawCursor(&painter, offset, cpos, cursorWidth());
+ }
+ }
+
+ offset.ry() += r.height();
+ if (offset.y() > viewportRect.height())
+ break;
+ block = block.next();
+ }
+
+ if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom()
+ && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) {
+ painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background());
+ }
+}
+
+
+void QPlainTextEditPrivate::updateDefaultTextOption()
+{
+ QTextDocument *doc = control->document();
+
+ QTextOption opt = doc->defaultTextOption();
+ QTextOption::WrapMode oldWrapMode = opt.wrapMode();
+
+ if (lineWrap == QPlainTextEdit::NoWrap)
+ opt.setWrapMode(QTextOption::NoWrap);
+ else
+ opt.setWrapMode(wordWrap);
+
+ if (opt.wrapMode() != oldWrapMode)
+ doc->setDefaultTextOption(opt);
+}
+
+
+/*! \reimp
+*/
+void QPlainTextEdit::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QPlainTextEdit);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (QApplication::keypadNavigationEnabled() && !hasEditFocus())
+ setEditFocus(true);
+#endif
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->inDrag = false; // paranoia
+ const QPoint pos = e->pos();
+ d->sendControlEvent(e);
+ if (!(e->buttons() & Qt::LeftButton))
+ return;
+ QRect visible = d->viewport->rect();
+ if (visible.contains(pos))
+ d->autoScrollTimer.stop();
+ else if (!d->autoScrollTimer.isActive())
+ d->autoScrollTimer.start(100, this);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->sendControlEvent(e);
+ if (d->autoScrollTimer.isActive()) {
+ d->autoScrollTimer.stop();
+ d->ensureCursorVisible();
+ }
+
+ if (!isReadOnly() && rect().contains(e->pos()))
+ d->handleSoftwareInputPanel(e->button(), d->clickCausedFocus);
+ d->clickCausedFocus = 0;
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+bool QPlainTextEdit::focusNextPrevChild(bool next)
+{
+ Q_D(const QPlainTextEdit);
+ if (!d->tabChangesFocus && d->control->textInteractionFlags() & Qt::TextEditable)
+ return false;
+ return QAbstractScrollArea::focusNextPrevChild(next);
+}
+
+#ifndef QT_NO_CONTEXTMENU
+/*!
+ \fn void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
+
+ Shows the standard context menu created with createStandardContextMenu().
+
+ If you do not want the text edit to have a context menu, you can set
+ its \l contextMenuPolicy to Qt::NoContextMenu. If you want to
+ customize the context menu, reimplement this function. If you want
+ to extend the standard context menu, reimplement this function, call
+ createStandardContextMenu() and extend the menu returned.
+
+ Information about the event is passed in the \a event object.
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qplaintextedit.cpp 0
+*/
+void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->sendControlEvent(e);
+}
+#endif // QT_NO_CONTEXTMENU
+
+#ifndef QT_NO_DRAGANDDROP
+/*! \reimp
+*/
+void QPlainTextEdit::dragEnterEvent(QDragEnterEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->inDrag = true;
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::dragLeaveEvent(QDragLeaveEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->inDrag = false;
+ d->autoScrollTimer.stop();
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::dragMoveEvent(QDragMoveEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->autoScrollDragPos = e->pos();
+ if (!d->autoScrollTimer.isActive())
+ d->autoScrollTimer.start(100, this);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::dropEvent(QDropEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ d->inDrag = false;
+ d->autoScrollTimer.stop();
+ d->sendControlEvent(e);
+}
+
+#endif // QT_NO_DRAGANDDROP
+
+/*! \reimp
+ */
+void QPlainTextEdit::inputMethodEvent(QInputMethodEvent *e)
+{
+ Q_D(QPlainTextEdit);
+#ifdef QT_KEYPAD_NAVIGATION
+ if (d->control->textInteractionFlags() & Qt::TextEditable
+ && QApplication::keypadNavigationEnabled()
+ && !hasEditFocus()) {
+ setEditFocus(true);
+ selectAll(); // so text is replaced rather than appended to
+ }
+#endif
+ d->sendControlEvent(e);
+ ensureCursorVisible();
+}
+
+/*!\reimp
+*/
+void QPlainTextEdit::scrollContentsBy(int dx, int /*dy*/)
+{
+ Q_D(QPlainTextEdit);
+ d->setTopLine(d->vbar->value(), dx);
+}
+
+/*!\reimp
+*/
+QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
+{
+ Q_D(const QPlainTextEdit);
+ QVariant v = d->control->inputMethodQuery(property);
+ const QPoint offset(-d->horizontalOffset(), -0);
+ if (v.type() == QVariant::RectF)
+ v = v.toRectF().toRect().translated(offset);
+ else if (v.type() == QVariant::PointF)
+ v = v.toPointF().toPoint() + offset;
+ else if (v.type() == QVariant::Rect)
+ v = v.toRect().translated(offset);
+ else if (v.type() == QVariant::Point)
+ v = v.toPoint() + offset;
+ return v;
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::focusInEvent(QFocusEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ if (e->reason() == Qt::MouseFocusReason) {
+ d->clickCausedFocus = 1;
+ }
+ QAbstractScrollArea::focusInEvent(e);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::focusOutEvent(QFocusEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ QAbstractScrollArea::focusOutEvent(e);
+ d->sendControlEvent(e);
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::showEvent(QShowEvent *)
+{
+ Q_D(QPlainTextEdit);
+ if (d->showCursorOnInitialShow) {
+ d->showCursorOnInitialShow = false;
+ ensureCursorVisible();
+ }
+}
+
+/*! \reimp
+*/
+void QPlainTextEdit::changeEvent(QEvent *e)
+{
+ Q_D(QPlainTextEdit);
+ QAbstractScrollArea::changeEvent(e);
+ if (e->type() == QEvent::ApplicationFontChange
+ || e->type() == QEvent::FontChange) {
+ d->control->document()->setDefaultFont(font());
+ } else if(e->type() == QEvent::ActivationChange) {
+ if (!isActiveWindow())
+ d->autoScrollTimer.stop();
+ } else if (e->type() == QEvent::EnabledChange) {
+ e->setAccepted(isEnabled());
+ d->sendControlEvent(e);
+ } else if (e->type() == QEvent::PaletteChange) {
+ d->control->setPalette(palette());
+ } else if (e->type() == QEvent::LayoutDirectionChange) {
+ d->sendControlEvent(e);
+ }
+}
+
+/*! \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void QPlainTextEdit::wheelEvent(QWheelEvent *e)
+{
+ QAbstractScrollArea::wheelEvent(e);
+ updateMicroFocus();
+}
+#endif
+
+#ifndef QT_NO_CONTEXTMENU
+/*! This function creates the standard context menu which is shown
+ when the user clicks on the line edit with the right mouse
+ button. It is called from the default contextMenuEvent() handler.
+ The popup menu's ownership is transferred to the caller.
+*/
+
+QMenu *QPlainTextEdit::createStandardContextMenu()
+{
+ Q_D(QPlainTextEdit);
+ return d->control->createStandardContextMenu(QPointF(), this);
+}
+#endif // QT_NO_CONTEXTMENU
+
+/*!
+ returns a QTextCursor at position \a pos (in viewport coordinates).
+*/
+QTextCursor QPlainTextEdit::cursorForPosition(const QPoint &pos) const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->cursorForPosition(d->mapToContents(pos));
+}
+
+/*!
+ returns a rectangle (in viewport coordinates) that includes the
+ \a cursor.
+ */
+QRect QPlainTextEdit::cursorRect(const QTextCursor &cursor) const
+{
+ Q_D(const QPlainTextEdit);
+ if (cursor.isNull())
+ return QRect();
+
+ QRect r = d->control->cursorRect(cursor).toRect();
+ r.translate(-d->horizontalOffset(),-d->verticalOffset());
+ return r;
+}
+
+/*!
+ returns a rectangle (in viewport coordinates) that includes the
+ cursor of the text edit.
+ */
+QRect QPlainTextEdit::cursorRect() const
+{
+ Q_D(const QPlainTextEdit);
+ QRect r = d->control->cursorRect().toRect();
+ r.translate(-d->horizontalOffset(),-d->verticalOffset());
+ return r;
+}
+
+
+/*!
+ \property QPlainTextEdit::overwriteMode
+ \brief whether text entered by the user will overwrite existing text
+
+ As with many text editors, the plain text editor widget can be configured
+ to insert or overwrite existing text with new text entered by the user.
+
+ If this property is true, existing text is overwritten, character-for-character
+ by new text; otherwise, text is inserted at the cursor position, displacing
+ existing text.
+
+ By default, this property is false (new text does not overwrite existing text).
+*/
+
+bool QPlainTextEdit::overwriteMode() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->overwriteMode();
+}
+
+void QPlainTextEdit::setOverwriteMode(bool overwrite)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setOverwriteMode(overwrite);
+}
+
+/*!
+ \property QPlainTextEdit::tabStopWidth
+ \brief the tab stop width in pixels
+
+ By default, this property contains a value of 80.
+*/
+
+int QPlainTextEdit::tabStopWidth() const
+{
+ Q_D(const QPlainTextEdit);
+ return qRound(d->control->document()->defaultTextOption().tabStop());
+}
+
+void QPlainTextEdit::setTabStopWidth(int width)
+{
+ Q_D(QPlainTextEdit);
+ QTextOption opt = d->control->document()->defaultTextOption();
+ if (opt.tabStop() == width || width < 0)
+ return;
+ opt.setTabStop(width);
+ d->control->document()->setDefaultTextOption(opt);
+}
+
+/*!
+ \property QPlainTextEdit::cursorWidth
+
+ This property specifies the width of the cursor in pixels. The default value is 1.
+*/
+int QPlainTextEdit::cursorWidth() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->cursorWidth();
+}
+
+void QPlainTextEdit::setCursorWidth(int width)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setCursorWidth(width);
+}
+
+
+
+/*!
+ This function allows temporarily marking certain regions in the document
+ with a given color, specified as \a selections. This can be useful for
+ example in a programming editor to mark a whole line of text with a given
+ background color to indicate the existence of a breakpoint.
+
+ \sa QTextEdit::ExtraSelection, extraSelections()
+*/
+void QPlainTextEdit::setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setExtraSelections(selections);
+}
+
+/*!
+ Returns previously set extra selections.
+
+ \sa setExtraSelections()
+*/
+QList<QTextEdit::ExtraSelection> QPlainTextEdit::extraSelections() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->extraSelections();
+}
+
+/*!
+ This function returns a new MIME data object to represent the contents
+ of the text edit's current selection. It is called when the selection needs
+ to be encapsulated into a new QMimeData object; for example, when a drag
+ and drop operation is started, or when data is copied to the clipboard.
+
+ If you reimplement this function, note that the ownership of the returned
+ QMimeData object is passed to the caller. The selection can be retrieved
+ by using the textCursor() function.
+*/
+QMimeData *QPlainTextEdit::createMimeDataFromSelection() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->QWidgetTextControl::createMimeDataFromSelection();
+}
+
+/*!
+ This function returns true if the contents of the MIME data object, specified
+ by \a source, can be decoded and inserted into the document. It is called
+ for example when during a drag operation the mouse enters this widget and it
+ is necessary to determine whether it is possible to accept the drag.
+ */
+bool QPlainTextEdit::canInsertFromMimeData(const QMimeData *source) const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->QWidgetTextControl::canInsertFromMimeData(source);
+}
+
+/*!
+ This function inserts the contents of the MIME data object, specified
+ by \a source, into the text edit at the current cursor position. It is
+ called whenever text is inserted as the result of a clipboard paste
+ operation, or when the text edit accepts data from a drag and drop
+ operation.
+*/
+void QPlainTextEdit::insertFromMimeData(const QMimeData *source)
+{
+ Q_D(QPlainTextEdit);
+ d->control->QWidgetTextControl::insertFromMimeData(source);
+}
+
+/*!
+ \property QPlainTextEdit::readOnly
+ \brief whether the text edit is read-only
+
+ In a read-only text edit the user can only navigate through the
+ text and select text; modifying the text is not possible.
+
+ This property's default is false.
+*/
+
+bool QPlainTextEdit::isReadOnly() const
+{
+ Q_D(const QPlainTextEdit);
+ return !(d->control->textInteractionFlags() & Qt::TextEditable);
+}
+
+void QPlainTextEdit::setReadOnly(bool ro)
+{
+ Q_D(QPlainTextEdit);
+ Qt::TextInteractionFlags flags = Qt::NoTextInteraction;
+ if (ro) {
+ flags = Qt::TextSelectableByMouse;
+ } else {
+ flags = Qt::TextEditorInteraction;
+ }
+ setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(this));
+ d->control->setTextInteractionFlags(flags);
+}
+
+/*!
+ \property QPlainTextEdit::textInteractionFlags
+
+ Specifies how the label should interact with user input if it displays text.
+
+ If the flags contain either Qt::LinksAccessibleByKeyboard or Qt::TextSelectableByKeyboard
+ then the focus policy is also automatically set to Qt::ClickFocus.
+
+ The default value depends on whether the QPlainTextEdit is read-only
+ or editable.
+*/
+
+void QPlainTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setTextInteractionFlags(flags);
+}
+
+Qt::TextInteractionFlags QPlainTextEdit::textInteractionFlags() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->textInteractionFlags();
+}
+
+/*!
+ Merges the properties specified in \a modifier into the current character
+ format by calling QTextCursor::mergeCharFormat on the editor's cursor.
+ If the editor has a selection then the properties of \a modifier are
+ directly applied to the selection.
+
+ \sa QTextCursor::mergeCharFormat()
+ */
+void QPlainTextEdit::mergeCurrentCharFormat(const QTextCharFormat &modifier)
+{
+ Q_D(QPlainTextEdit);
+ d->control->mergeCurrentCharFormat(modifier);
+}
+
+/*!
+ Sets the char format that is be used when inserting new text to \a
+ format by calling QTextCursor::setCharFormat() on the editor's
+ cursor. If the editor has a selection then the char format is
+ directly applied to the selection.
+ */
+void QPlainTextEdit::setCurrentCharFormat(const QTextCharFormat &format)
+{
+ Q_D(QPlainTextEdit);
+ d->control->setCurrentCharFormat(format);
+}
+
+/*!
+ Returns the char format that is used when inserting new text.
+ */
+QTextCharFormat QPlainTextEdit::currentCharFormat() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->currentCharFormat();
+}
+
+
+
+/*!
+ Convenience slot that inserts \a text at the current
+ cursor position.
+
+ It is equivalent to
+
+ \snippet doc/src/snippets/code/src_gui_widgets_qplaintextedit.cpp 1
+ */
+void QPlainTextEdit::insertPlainText(const QString &text)
+{
+ Q_D(QPlainTextEdit);
+ d->control->insertPlainText(text);
+}
+
+
+/*!
+ Moves the cursor by performing the given \a operation.
+
+ If \a mode is QTextCursor::KeepAnchor, the cursor selects the text it moves over.
+ This is the same effect that the user achieves when they hold down the Shift key
+ and move the cursor with the cursor keys.
+
+ \sa QTextCursor::movePosition()
+*/
+void QPlainTextEdit::moveCursor(QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode)
+{
+ Q_D(QPlainTextEdit);
+ d->control->moveCursor(operation, mode);
+}
+
+/*!
+ Returns whether text can be pasted from the clipboard into the textedit.
+*/
+bool QPlainTextEdit::canPaste() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->canPaste();
+}
+
+/*!
+ Convenience function to print the text edit's document to the given \a printer. This
+ is equivalent to calling the print method on the document directly except that this
+ function also supports QPrinter::Selection as print range.
+
+ \sa QTextDocument::print()
+*/
+void QPlainTextEdit::print(QPagedPaintDevice *printer) const
+{
+ Q_D(const QPlainTextEdit);
+ d->control->print(printer);
+}
+
+/*! \property QPlainTextEdit::tabChangesFocus
+ \brief whether \gui Tab changes focus or is accepted as input
+
+ In some occasions text edits should not allow the user to input
+ tabulators or change indentation using the \gui Tab key, as this breaks
+ the focus chain. The default is false.
+
+*/
+
+bool QPlainTextEdit::tabChangesFocus() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->tabChangesFocus;
+}
+
+void QPlainTextEdit::setTabChangesFocus(bool b)
+{
+ Q_D(QPlainTextEdit);
+ d->tabChangesFocus = b;
+}
+
+/*!
+ \property QPlainTextEdit::documentTitle
+ \brief the title of the document parsed from the text.
+
+ By default, this property contains an empty string.
+*/
+
+/*!
+ \property QPlainTextEdit::lineWrapMode
+ \brief the line wrap mode
+
+ The default mode is WidgetWidth which causes words to be
+ wrapped at the right edge of the text edit. Wrapping occurs at
+ whitespace, keeping whole words intact. If you want wrapping to
+ occur within words use setWordWrapMode().
+*/
+
+QPlainTextEdit::LineWrapMode QPlainTextEdit::lineWrapMode() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->lineWrap;
+}
+
+void QPlainTextEdit::setLineWrapMode(LineWrapMode wrap)
+{
+ Q_D(QPlainTextEdit);
+ if (d->lineWrap == wrap)
+ return;
+ d->lineWrap = wrap;
+ d->updateDefaultTextOption();
+ d->relayoutDocument();
+ d->_q_adjustScrollbars();
+ ensureCursorVisible();
+}
+
+/*!
+ \property QPlainTextEdit::wordWrapMode
+ \brief the mode QPlainTextEdit will use when wrapping text by words
+
+ By default, this property is set to QTextOption::WrapAtWordBoundaryOrAnywhere.
+
+ \sa QTextOption::WrapMode
+*/
+
+QTextOption::WrapMode QPlainTextEdit::wordWrapMode() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->wordWrap;
+}
+
+void QPlainTextEdit::setWordWrapMode(QTextOption::WrapMode mode)
+{
+ Q_D(QPlainTextEdit);
+ if (mode == d->wordWrap)
+ return;
+ d->wordWrap = mode;
+ d->updateDefaultTextOption();
+}
+
+/*!
+ \property QPlainTextEdit::backgroundVisible
+ \brief whether the palette background is visible outside the document area
+
+ If set to true, the plain text edit paints the palette background
+ on the viewport area not covered by the text document. Otherwise,
+ if set to false, it won't. The feature makes it possible for
+ the user to visually distinguish between the area of the document,
+ painted with the base color of the palette, and the empty
+ area not covered by any document.
+
+ The default is false.
+*/
+
+bool QPlainTextEdit::backgroundVisible() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->backgroundVisible;
+}
+
+void QPlainTextEdit::setBackgroundVisible(bool visible)
+{
+ Q_D(QPlainTextEdit);
+ if (visible == d->backgroundVisible)
+ return;
+ d->backgroundVisible = visible;
+ d->updateViewport();
+}
+
+/*!
+ \property QPlainTextEdit::centerOnScroll
+ \brief whether the cursor should be centered on screen
+
+ If set to true, the plain text edit scrolls the document
+ vertically to make the cursor visible at the center of the
+ viewport. This also allows the text edit to scroll below the end
+ of the document. Otherwise, if set to false, the plain text edit
+ scrolls the smallest amount possible to ensure the cursor is
+ visible. The same algorithm is applied to any new line appended
+ through appendPlainText().
+
+ The default is false.
+
+ \sa centerCursor(), ensureCursorVisible()
+*/
+
+bool QPlainTextEdit::centerOnScroll() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->centerOnScroll;
+}
+
+void QPlainTextEdit::setCenterOnScroll(bool enabled)
+{
+ Q_D(QPlainTextEdit);
+ if (enabled == d->centerOnScroll)
+ return;
+ d->centerOnScroll = enabled;
+}
+
+
+
+/*!
+ Finds the next occurrence of the string, \a exp, using the given
+ \a options. Returns true if \a exp was found and changes the
+ cursor to select the match; otherwise returns false.
+*/
+bool QPlainTextEdit::find(const QString &exp, QTextDocument::FindFlags options)
+{
+ Q_D(QPlainTextEdit);
+ return d->control->find(exp, options);
+}
+
+/*!
+ \fn void QPlainTextEdit::copyAvailable(bool yes)
+
+ This signal is emitted when text is selected or de-selected in the
+ text edit.
+
+ When text is selected this signal will be emitted with \a yes set
+ to true. If no text has been selected or if the selected text is
+ de-selected this signal is emitted with \a yes set to false.
+
+ If \a yes is true then copy() can be used to copy the selection to
+ the clipboard. If \a yes is false then copy() does nothing.
+
+ \sa selectionChanged()
+*/
+
+
+/*!
+ \fn void QPlainTextEdit::selectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa copyAvailable()
+*/
+
+/*!
+ \fn void QPlainTextEdit::cursorPositionChanged()
+
+ This signal is emitted whenever the position of the
+ cursor changed.
+*/
+
+
+
+/*!
+ \fn void QPlainTextEdit::updateRequest(const QRect &rect, int dy)
+
+ This signal is emitted when the text document needs an update of
+ the specified \a rect. If the text is scrolled, \a rect will cover
+ the entire viewport area. If the text is scrolled vertically, \a
+ dy carries the amount of pixels the viewport was scrolled.
+
+ The purpose of the signal is to support extra widgets in plain
+ text edit subclasses that e.g. show line numbers, breakpoints, or
+ other extra information.
+*/
+
+/*! \fn void QPlainTextEdit::blockCountChanged(int newBlockCount);
+
+ This signal is emitted whenever the block count changes. The new
+ block count is passed in \a newBlockCount.
+*/
+
+/*! \fn void QPlainTextEdit::modificationChanged(bool changed);
+
+ This signal is emitted whenever the content of the document
+ changes in a way that affects the modification state. If \a
+ changed is true, the document has been modified; otherwise it is
+ false.
+
+ For example, calling setModified(false) on a document and then
+ inserting text causes the signal to get emitted. If you undo that
+ operation, causing the document to return to its original
+ unmodified state, the signal will get emitted again.
+*/
+
+
+
+
+void QPlainTextEditPrivate::append(const QString &text, Qt::TextFormat format)
+{
+ Q_Q(QPlainTextEdit);
+
+ QTextDocument *document = control->document();
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document->documentLayout());
+ Q_ASSERT(documentLayout);
+
+ int maximumBlockCount = document->maximumBlockCount();
+ if (maximumBlockCount)
+ document->setMaximumBlockCount(0);
+
+ const bool atBottom = q->isVisible()
+ && (control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset()
+ <= viewport->rect().bottom());
+
+ if (!q->isVisible())
+ showCursorOnInitialShow = true;
+
+ bool documentSizeChangedBlocked = documentLayout->priv()->blockDocumentSizeChanged;
+ documentLayout->priv()->blockDocumentSizeChanged = true;
+
+ if (format == Qt::RichText)
+ control->appendHtml(text);
+ else if (format == Qt::PlainText)
+ control->appendPlainText(text);
+ else
+ control->append(text);
+
+ if (maximumBlockCount > 0) {
+ if (document->blockCount() > maximumBlockCount) {
+ bool blockUpdate = false;
+ if (control->topBlock) {
+ control->topBlock--;
+ blockUpdate = true;
+ emit q->updateRequest(viewport->rect(), 0);
+ }
+
+ bool updatesBlocked = documentLayout->priv()->blockUpdate;
+ documentLayout->priv()->blockUpdate = blockUpdate;
+ QTextCursor cursor(document);
+ cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
+ cursor.removeSelectedText();
+ documentLayout->priv()->blockUpdate = updatesBlocked;
+ }
+ document->setMaximumBlockCount(maximumBlockCount);
+ }
+
+ documentLayout->priv()->blockDocumentSizeChanged = documentSizeChangedBlocked;
+ _q_adjustScrollbars();
+
+
+ if (atBottom) {
+ const bool needScroll = !centerOnScroll
+ || control->blockBoundingRect(document->lastBlock()).bottom() - verticalOffset()
+ > viewport->rect().bottom();
+ if (needScroll)
+ vbar->setValue(vbar->maximum());
+ }
+}
+
+
+/*!
+ Appends a new paragraph with \a text to the end of the text edit.
+
+ \sa appendHtml()
+*/
+
+void QPlainTextEdit::appendPlainText(const QString &text)
+{
+ Q_D(QPlainTextEdit);
+ d->append(text, Qt::PlainText);
+}
+
+/*!
+ Appends a new paragraph with \a html to the end of the text edit.
+
+ appendPlainText()
+*/
+
+void QPlainTextEdit::appendHtml(const QString &html)
+{
+ Q_D(QPlainTextEdit);
+ d->append(html, Qt::RichText);
+}
+
+void QPlainTextEditPrivate::ensureCursorVisible(bool center)
+{
+ Q_Q(QPlainTextEdit);
+ QRect visible = viewport->rect();
+ QRect cr = q->cursorRect();
+ if (cr.top() < visible.top() || cr.bottom() > visible.bottom()) {
+ ensureVisible(control->textCursor().position(), center);
+ }
+
+ const bool rtl = q->isRightToLeft();
+ if (cr.left() < visible.left() || cr.right() > visible.right()) {
+ int x = cr.center().x() + horizontalOffset() - visible.width()/2;
+ hbar->setValue(rtl ? hbar->maximum() - x : x);
+ }
+}
+
+/*!
+ Ensures that the cursor is visible by scrolling the text edit if
+ necessary.
+
+ \sa centerCursor(), centerOnScroll
+*/
+void QPlainTextEdit::ensureCursorVisible()
+{
+ Q_D(QPlainTextEdit);
+ d->ensureCursorVisible(d->centerOnScroll);
+}
+
+
+/*! Scrolls the document in order to center the cursor vertically.
+
+\sa ensureCursorVisible(), centerOnScroll
+ */
+void QPlainTextEdit::centerCursor()
+{
+ Q_D(QPlainTextEdit);
+ d->ensureVisible(textCursor().position(), true, true);
+}
+
+/*!
+ Returns the first visible block.
+
+ \sa blockBoundingRect()
+ */
+QTextBlock QPlainTextEdit::firstVisibleBlock() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->firstVisibleBlock();
+}
+
+/*! Returns the content's origin in viewport coordinates.
+
+ The origin of the content of a plain text edit is always the top
+ left corner of the first visible text block. The content offset
+ is different from (0,0) when the text has been scrolled
+ horizontally, or when the first visible block has been scrolled
+ partially off the screen, i.e. the visible text does not start
+ with the first line of the first visible block, or when the first
+ visible block is the very first block and the editor displays a
+ margin.
+
+ \sa firstVisibleBlock(), horizontalScrollBar(), verticalScrollBar()
+ */
+QPointF QPlainTextEdit::contentOffset() const
+{
+ Q_D(const QPlainTextEdit);
+ return QPointF(-d->horizontalOffset(), -d->verticalOffset());
+}
+
+
+/*! Returns the bounding rectangle of the text \a block in content
+ coordinates. Translate the rectangle with the contentOffset() to get
+ visual coordinates on the viewport.
+
+ \sa firstVisibleBlock(), blockBoundingRect()
+ */
+QRectF QPlainTextEdit::blockBoundingGeometry(const QTextBlock &block) const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->blockBoundingRect(block);
+}
+
+/*!
+ Returns the bounding rectangle of the text \a block in the block's own coordinates.
+
+ \sa blockBoundingGeometry()
+ */
+QRectF QPlainTextEdit::blockBoundingRect(const QTextBlock &block) const
+{
+ QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(document()->documentLayout());
+ Q_ASSERT(documentLayout);
+ return documentLayout->blockBoundingRect(block);
+}
+
+/*!
+ \property QPlainTextEdit::blockCount
+ \brief the number of text blocks in the document.
+
+ By default, in an empty document, this property contains a value of 1.
+*/
+int QPlainTextEdit::blockCount() const
+{
+ return document()->blockCount();
+}
+
+/*! Returns the paint context for the viewport(), useful only when
+ reimplementing paintEvent().
+ */
+QAbstractTextDocumentLayout::PaintContext QPlainTextEdit::getPaintContext() const
+{
+ Q_D(const QPlainTextEdit);
+ return d->control->getPaintContext(d->viewport);
+}
+
+/*!
+ \property QPlainTextEdit::maximumBlockCount
+ \brief the limit for blocks in the document.
+
+ Specifies the maximum number of blocks the document may have. If there are
+ more blocks in the document that specified with this property blocks are removed
+ from the beginning of the document.
+
+ A negative or zero value specifies that the document may contain an unlimited
+ amount of blocks.
+
+ The default value is 0.
+
+ Note that setting this property will apply the limit immediately to the document
+ contents. Setting this property also disables the undo redo history.
+
+*/
+
+
+/*!
+ \fn void QPlainTextEdit::textChanged()
+
+ This signal is emitted whenever the document's content changes; for
+ example, when text is inserted or deleted, or when formatting is applied.
+*/
+
+/*!
+ \fn void QPlainTextEdit::undoAvailable(bool available)
+
+ This signal is emitted whenever undo operations become available
+ (\a available is true) or unavailable (\a available is false).
+*/
+
+/*!
+ \fn void QPlainTextEdit::redoAvailable(bool available)
+
+ This signal is emitted whenever redo operations become available
+ (\a available is true) or unavailable (\a available is false).
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qplaintextedit.cpp"
+#include "moc_qplaintextedit_p.cpp"
+
+#endif // QT_NO_TEXTEDIT