src/gui/widgets/qplaintextedit.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/widgets/qplaintextedit.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,2964 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $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 "private/qtextcontrol_p.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();
+    QFontMetrics fm(doc->defaultFont());
+    qreal blockMaximumWidth = 0;
+
+    int leading = qMax(0, fm.leading());
+    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.setLineWidth(availableWidth);
+
+        height += leading;
+        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)
+    : QTextControl(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 QTextControl::createMimeDataFromSelection();
+        return ed->createMimeDataFromSelection();
+    }
+bool QPlainTextEditControl::canInsertFromMimeData(const QMimeData *source) const {
+    QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
+    if (!ed)
+        return QTextControl::canInsertFromMimeData(source);
+    return ed->canInsertFromMimeData(source);
+}
+void QPlainTextEditControl::insertFromMimeData(const QMimeData *source) {
+    QPlainTextEdit *ed = qobject_cast<QPlainTextEdit *>(parent());
+    if (!ed)
+        QTextControl::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);
+        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;
+        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();
+
+        while (h < height && block.previous().isValid()) {
+            block = block.previous();
+            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 (block.next().isValid() && l >= lineCount) {
+            block = block.next();
+            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;
+#ifdef Q_WS_WIN
+    singleFingerPanEnabled = true;
+#endif
+}
+
+
+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()));
+
+
+    // 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;
+}
+
+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) {
+            // 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 int visible = static_cast<int>(viewport->rect().height() - margin - 1);
+        int y = 0;
+        int visibleFromBottom = 0;
+
+        while (block.isValid()) {
+            if (!block.isVisible()) {
+                block = block.previous();
+                continue;
+            }
+            y += int(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 (int(lr.top()) >= y - visible)
+                        break;
+                    ++lineNumber;
+                }
+                if (lineNumber < layoutLineCount)
+                    visibleFromBottom += (layoutLineCount - lineNumber - 1);
+                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();
+}
+
+
+/*!
+    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
+    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;
+        }
+    }
+#endif // QT_NO_SHORTCUT
+
+    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;
+    }
+
+    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();
+
+    // 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();
+    }
+
+    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->QTextControl::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->QTextControl::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->QTextControl::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, and whether it is a QTextBrowser or not.
+*/
+
+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();
+}
+
+#ifndef QT_NO_PRINTER
+/*!
+    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(QPrinter *printer) const
+{
+    Q_D(const QPlainTextEdit);
+    d->control->print(printer);
+}
+#endif // QT _NO_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).
+*/
+
+//void QPlainTextEditPrivate::_q_gestureTriggered()
+//{
+//    Q_Q(QPlainTextEdit);
+//    QPanGesture *g = qobject_cast<QPanGesture*>(q->sender());
+//    if (!g)
+//        return;
+//    QScrollBar *hBar = q->horizontalScrollBar();
+//    QScrollBar *vBar = q->verticalScrollBar();
+//    if (g->state() == Qt::GestureStarted)
+//        originalOffsetY = vBar->value();
+//    QSizeF totalOffset = g->totalOffset();
+//    if (!totalOffset.isNull()) {
+//        if (QApplication::isRightToLeft())
+//            totalOffset.rwidth() *= -1;
+//        // QPlainTextEdit scrolls by lines only in vertical direction
+//        QFontMetrics fm(q->document()->defaultFont());
+//        int lineHeight = fm.height();
+//        int newX = hBar->value() - g->lastOffset().width();
+//        int newY = originalOffsetY - totalOffset.height()/lineHeight;
+//        hbar->setValue(newX);
+//        vbar->setValue(newY);
+//    }
+//}
+
+QT_END_NAMESPACE
+
+#include "moc_qplaintextedit.cpp"
+#include "moc_qplaintextedit_p.cpp"
+
+#endif // QT_NO_TEXTEDIT