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