diff options
Diffstat (limited to 'tests/auto/qplaintextedit/tst_qplaintextedit.cpp')
-rw-r--r-- | tests/auto/qplaintextedit/tst_qplaintextedit.cpp | 1500 |
1 files changed, 1500 insertions, 0 deletions
diff --git a/tests/auto/qplaintextedit/tst_qplaintextedit.cpp b/tests/auto/qplaintextedit/tst_qplaintextedit.cpp new file mode 100644 index 0000000..6835c21 --- /dev/null +++ b/tests/auto/qplaintextedit/tst_qplaintextedit.cpp @@ -0,0 +1,1500 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> + + +#include <qtextedit.h> +#include <qtextcursor.h> +#include <qtextlist.h> +#include <qdebug.h> +#include <qapplication.h> +#include <qclipboard.h> +#include <qtextbrowser.h> +#include <private/qtextcontrol_p.h> +#include <qscrollbar.h> +#include <qtextobject.h> + +#include <qabstracttextdocumentlayout.h> +#include <qtextdocumentfragment.h> + +#include "qplaintextedit.h" + +//Used in copyAvailable +typedef QPair<Qt::Key, Qt::KeyboardModifier> keyPairType; +typedef QList<keyPairType> pairListType; +Q_DECLARE_METATYPE(pairListType); +Q_DECLARE_METATYPE(keyPairType); +Q_DECLARE_METATYPE(QList<bool>); + +#ifdef Q_WS_MAC +#include <Carbon/Carbon.h> +#endif + +QT_FORWARD_DECLARE_CLASS(QPlainTextEdit) + +//TESTED_CLASS= +//TESTED_FILES=gui/widgets/qtextedit.h gui/widgets/qtextedit.cpp + +class tst_QPlainTextEdit : public QObject +{ + Q_OBJECT +public: + tst_QPlainTextEdit(); + +public slots: + void initTestCase(); + void init(); + void cleanup(); +private slots: + void getSetCheck(); +#ifndef QT_NO_CLIPBOARD + void clearMustNotChangeClipboard(); +#endif + void clearMustNotResetRootFrameMarginToDefault(); + void paragSeparatorOnPlaintextAppend(); +#ifndef QT_NO_CLIPBOARD + void selectAllSetsNotSelection(); +#endif + void asciiTab(); + void setDocument(); + void emptyAppend(); + void appendOnEmptyDocumentShouldReuseInitialParagraph(); + void cursorPositionChanged(); + void setTextCursor(); +#ifndef QT_NO_CLIPBOARD + void undoAvailableAfterPaste(); +#endif + void undoRedoAvailableRepetition(); + void appendShouldNotTouchTheSelection(); + void backspace(); + void shiftBackspace(); + void undoRedo(); + void preserveCharFormatInAppend(); +#ifndef QT_NO_CLIPBOARD + void copyAndSelectAllInReadonly(); +#endif + void ctrlAltInput(); + void noPropertiesOnDefaultTextEditCharFormat(); + void setPlainTextShouldEmitTextChangedOnce(); + void overwriteMode(); + void shiftDownInLineLastShouldSelectToEnd_data(); + void shiftDownInLineLastShouldSelectToEnd(); + void undoRedoShouldRepositionTextEditCursor(); + void lineWrapModes(); + void mouseCursorShape(); + void implicitClear(); + void undoRedoAfterSetContent(); + void numPadKeyNavigation(); + void moveCursor(); +#ifndef QT_NO_CLIPBOARD + void mimeDataReimplementations(); +#endif + void shiftEnterShouldInsertLineSeparator(); + void selectWordsFromStringsContainingSeparators_data(); + void selectWordsFromStringsContainingSeparators(); +#ifndef QT_NO_CLIPBOARD + void canPaste(); + void copyAvailable_data(); + void copyAvailable(); +#endif + void ensureCursorVisibleOnInitialShow(); + void setTextInsideResizeEvent(); + void colorfulAppend(); + void ensureVisibleWithRtl(); + void preserveCharFormatAfterSetPlainText(); + void extraSelections(); + void adjustScrollbars(); + void textObscuredByScrollbars(); + void setTextPreservesUndoRedoEnabled(); + void wordWrapProperty(); + void lineWrapProperty(); + void selectionChanged(); + void blockCountChanged(); + +private: + void createSelection(); + int blockCount() const; + int lineCount() const; + bool nativeClipboardWorking(); + + QPlainTextEdit *ed; + qreal rootFrameMargin; +}; + +bool tst_QPlainTextEdit::nativeClipboardWorking() +{ +#ifdef Q_WS_MAC + PasteboardRef pasteboard; + OSStatus status = PasteboardCreate(0, &pasteboard); + if (status == noErr) + CFRelease(pasteboard); + return status == noErr; +#endif + return true; +} + +// Testing get/set functions +void tst_QPlainTextEdit::getSetCheck() +{ + QPlainTextEdit obj1; + // QTextDocument * QPlainTextEdit::document() + // void QPlainTextEdit::setDocument(QTextDocument *) + QTextDocument *var1 = new QTextDocument; + var1->setDocumentLayout(new QPlainTextDocumentLayout(var1)); + obj1.setDocument(var1); + QCOMPARE(var1, obj1.document()); + obj1.setDocument((QTextDocument *)0); + QVERIFY(var1 != obj1.document()); // QPlainTextEdit creates a new document when setting 0 + QVERIFY((QTextDocument *)0 != obj1.document()); + delete var1; + + + // bool QPlainTextEdit::tabChangesFocus() + // void QPlainTextEdit::setTabChangesFocus(bool) + obj1.setTabChangesFocus(false); + QCOMPARE(false, obj1.tabChangesFocus()); + obj1.setTabChangesFocus(true); + QCOMPARE(true, obj1.tabChangesFocus()); + + // LineWrapMode QPlainTextEdit::lineWrapMode() + // void QPlainTextEdit::setLineWrapMode(LineWrapMode) + obj1.setLineWrapMode(QPlainTextEdit::LineWrapMode(QPlainTextEdit::NoWrap)); + QCOMPARE(QPlainTextEdit::LineWrapMode(QPlainTextEdit::NoWrap), obj1.lineWrapMode()); + obj1.setLineWrapMode(QPlainTextEdit::LineWrapMode(QPlainTextEdit::WidgetWidth)); + QCOMPARE(QPlainTextEdit::LineWrapMode(QPlainTextEdit::WidgetWidth), obj1.lineWrapMode()); +// obj1.setLineWrapMode(QPlainTextEdit::LineWrapMode(QPlainTextEdit::FixedPixelWidth)); +// QCOMPARE(QPlainTextEdit::LineWrapMode(QPlainTextEdit::FixedPixelWidth), obj1.lineWrapMode()); +// obj1.setLineWrapMode(QPlainTextEdit::LineWrapMode(QPlainTextEdit::FixedColumnWidth)); +// QCOMPARE(QPlainTextEdit::LineWrapMode(QPlainTextEdit::FixedColumnWidth), obj1.lineWrapMode()); + + + // bool QPlainTextEdit::overwriteMode() + // void QPlainTextEdit::setOverwriteMode(bool) + obj1.setOverwriteMode(false); + QCOMPARE(false, obj1.overwriteMode()); + obj1.setOverwriteMode(true); + QCOMPARE(true, obj1.overwriteMode()); + + // int QPlainTextEdit::tabStopWidth() + // void QPlainTextEdit::setTabStopWidth(int) + obj1.setTabStopWidth(0); + QCOMPARE(0, obj1.tabStopWidth()); + obj1.setTabStopWidth(INT_MIN); + QCOMPARE(0, obj1.tabStopWidth()); // Makes no sense to set a negative tabstop value +#if defined(QT_ARCH_WINDOWSCE) + // due to rounding error in qRound when qreal==float + // we cannot use INT_MAX for this check + obj1.setTabStopWidth(SHRT_MAX*2); + QCOMPARE(SHRT_MAX*2, obj1.tabStopWidth()); +#else + obj1.setTabStopWidth(INT_MAX); + QCOMPARE(INT_MAX, obj1.tabStopWidth()); +#endif + +} + +class QtTestDocumentLayout : public QAbstractTextDocumentLayout +{ + Q_OBJECT +public: + inline QtTestDocumentLayout(QPlainTextEdit *edit, QTextDocument *doc, int &itCount) + : QAbstractTextDocumentLayout(doc), useBiggerSize(false), ed(edit), iterationCounter(itCount) {} + + virtual void draw(QPainter *, const QAbstractTextDocumentLayout::PaintContext &) {} + + virtual int hitTest(const QPointF &, Qt::HitTestAccuracy ) const { return 0; } + + virtual void documentChanged(int, int, int) {} + + virtual int pageCount() const { return 1; } + + virtual QSizeF documentSize() const { return usedSize; } + + virtual QRectF frameBoundingRect(QTextFrame *) const { return QRectF(); } + virtual QRectF blockBoundingRect(const QTextBlock &) const { return QRectF(); } + + bool useBiggerSize; + QSize usedSize; + + QPlainTextEdit *ed; + + int &iterationCounter; +}; + +tst_QPlainTextEdit::tst_QPlainTextEdit() +{} + +void tst_QPlainTextEdit::initTestCase() +{ +#ifdef Q_OS_WINCE //disable magic for WindowsCE + qApp->setAutoMaximizeThreshold(-1); +#endif +} + +void tst_QPlainTextEdit::init() +{ + ed = new QPlainTextEdit(0); + rootFrameMargin = ed->document()->documentMargin(); +} + +void tst_QPlainTextEdit::cleanup() +{ + delete ed; + ed = 0; +} + + +void tst_QPlainTextEdit::createSelection() +{ + QTest::keyClicks(ed, "Hello World"); + /* go to start */ +#ifndef Q_WS_MAC + QTest::keyClick(ed, Qt::Key_Home, Qt::ControlModifier); +#else + QTest::keyClick(ed, Qt::Key_Home); +#endif + QCOMPARE(ed->textCursor().position(), 0); + /* select until end of text */ +#ifndef Q_WS_MAC + QTest::keyClick(ed, Qt::Key_End, Qt::ControlModifier | Qt::ShiftModifier); +#else + QTest::keyClick(ed, Qt::Key_End, Qt::ShiftModifier); +#endif + QCOMPARE(ed->textCursor().position(), 11); +} +#ifndef QT_NO_CLIPBOARD +void tst_QPlainTextEdit::clearMustNotChangeClipboard() +{ + if (!nativeClipboardWorking()) + QSKIP("Clipboard not working with cron-started unit tests", SkipAll); + ed->textCursor().insertText("Hello World"); + QString txt("This is different text"); + QApplication::clipboard()->setText(txt); + ed->clear(); + QCOMPARE(QApplication::clipboard()->text(), txt); +} +#endif + +void tst_QPlainTextEdit::clearMustNotResetRootFrameMarginToDefault() +{ + QCOMPARE(ed->document()->rootFrame()->frameFormat().margin(), rootFrameMargin); + ed->clear(); + QCOMPARE(ed->document()->rootFrame()->frameFormat().margin(), rootFrameMargin); +} + + +void tst_QPlainTextEdit::paragSeparatorOnPlaintextAppend() +{ + ed->appendPlainText("Hello\nWorld"); + int cnt = 0; + QTextBlock blk = ed->document()->begin(); + while (blk.isValid()) { + ++cnt; + blk = blk.next(); + } + QCOMPARE(cnt, 2); +} + +#ifndef QT_NO_CLIPBOARD +void tst_QPlainTextEdit::selectAllSetsNotSelection() +{ + if (!QApplication::clipboard()->supportsSelection()) { + QSKIP("Test only relevant for systems with selection", SkipAll); + return; + } + + QApplication::clipboard()->setText(QString("foobar"), QClipboard::Selection); + QVERIFY(QApplication::clipboard()->text(QClipboard::Selection) == QString("foobar")); + + ed->insertPlainText("Hello World"); + ed->selectAll(); + + QCOMPARE(QApplication::clipboard()->text(QClipboard::Selection), QString::fromAscii("foobar")); +} +#endif + +void tst_QPlainTextEdit::asciiTab() +{ + QPlainTextEdit edit; + edit.setPlainText("\t"); + edit.show(); + qApp->processEvents(); + QCOMPARE(edit.toPlainText().at(0), QChar('\t')); +} + +void tst_QPlainTextEdit::setDocument() +{ + QTextDocument *document = new QTextDocument(ed); + document->setDocumentLayout(new QPlainTextDocumentLayout(document)); + QTextCursor(document).insertText("Test"); + ed->setDocument(document); + QCOMPARE(ed->toPlainText(), QString("Test")); +} + + +int tst_QPlainTextEdit::blockCount() const +{ + int blocks = 0; + for (QTextBlock block = ed->document()->begin(); block.isValid(); block = block.next()) + ++blocks; + return blocks; +} + +int tst_QPlainTextEdit::lineCount() const +{ + int lines = 0; + for (QTextBlock block = ed->document()->begin(); block.isValid(); block = block.next()) { + ed->document()->documentLayout()->blockBoundingRect(block); + lines += block.layout()->lineCount(); + } + return lines; +} + +// Supporter issue #56783 +void tst_QPlainTextEdit::emptyAppend() +{ + ed->appendPlainText("Blah"); + QCOMPARE(blockCount(), 1); + ed->appendPlainText(QString::null); + QCOMPARE(blockCount(), 2); + ed->appendPlainText(QString(" ")); + QCOMPARE(blockCount(), 3); +} + +void tst_QPlainTextEdit::appendOnEmptyDocumentShouldReuseInitialParagraph() +{ + QCOMPARE(blockCount(), 1); + ed->appendPlainText("Blah"); + QCOMPARE(blockCount(), 1); +} + + +class CursorPositionChangedRecorder : public QObject +{ + Q_OBJECT +public: + inline CursorPositionChangedRecorder(QPlainTextEdit *ed) + : editor(ed) + { + connect(editor, SIGNAL(cursorPositionChanged()), this, SLOT(recordCursorPos())); + } + + QList<int> cursorPositions; + +private slots: + void recordCursorPos() + { + cursorPositions.append(editor->textCursor().position()); + } + +private: + QPlainTextEdit *editor; +}; + +void tst_QPlainTextEdit::cursorPositionChanged() +{ + QSignalSpy spy(ed, SIGNAL(cursorPositionChanged())); + + spy.clear(); + QTest::keyClick(ed, Qt::Key_A); + QCOMPARE(spy.count(), 1); + + QTextCursor cursor = ed->textCursor(); + cursor.movePosition(QTextCursor::Start); + ed->setTextCursor(cursor); + cursor.movePosition(QTextCursor::End); + spy.clear(); + cursor.insertText("Test"); + QCOMPARE(spy.count(), 0); + + cursor.movePosition(QTextCursor::End); + ed->setTextCursor(cursor); + cursor.movePosition(QTextCursor::Start); + spy.clear(); + cursor.insertText("Test"); + QCOMPARE(spy.count(), 1); + + spy.clear(); + QTest::keyClick(ed, Qt::Key_Left); + QCOMPARE(spy.count(), 1); + + CursorPositionChangedRecorder spy2(ed); + QVERIFY(ed->textCursor().position() > 0); + ed->setPlainText("Hello World"); + QCOMPARE(spy2.cursorPositions.count(), 1); + QCOMPARE(spy2.cursorPositions.at(0), 0); + QCOMPARE(ed->textCursor().position(), 0); +} + +void tst_QPlainTextEdit::setTextCursor() +{ + QSignalSpy spy(ed, SIGNAL(cursorPositionChanged())); + + ed->setPlainText("Test"); + QTextCursor cursor = ed->textCursor(); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter); + + spy.clear(); + + ed->setTextCursor(cursor); + QCOMPARE(spy.count(), 1); +} + +#ifndef QT_NO_CLIPBOARD +void tst_QPlainTextEdit::undoAvailableAfterPaste() +{ + if (!nativeClipboardWorking()) + QSKIP("Clipboard not working with cron-started unit tests", SkipAll); + + QSignalSpy spy(ed->document(), SIGNAL(undoAvailable(bool))); + + const QString txt("Test"); + QApplication::clipboard()->setText(txt); + ed->paste(); + QVERIFY(spy.count() >= 1); + QCOMPARE(ed->toPlainText(), txt); +} +#endif + +class UndoRedoRecorder : public QObject +{ + Q_OBJECT +public: + UndoRedoRecorder(QTextDocument *doc) + : undoRepetitions(false) + , redoRepetitions(false) + , undoCount(0) + , redoCount(0) + { + connect(doc, SIGNAL(undoAvailable(bool)), this, SLOT(undoAvailable(bool))); + connect(doc, SIGNAL(redoAvailable(bool)), this, SLOT(redoAvailable(bool))); + } + + bool undoRepetitions; + bool redoRepetitions; + +private slots: + void undoAvailable(bool enabled) { + if (undoCount > 0 && enabled == lastUndoEnabled) + undoRepetitions = true; + + ++undoCount; + lastUndoEnabled = enabled; + } + + void redoAvailable(bool enabled) { + if (redoCount > 0 && enabled == lastRedoEnabled) + redoRepetitions = true; + + ++redoCount; + lastRedoEnabled = enabled; + } + +private: + bool lastUndoEnabled; + bool lastRedoEnabled; + + int undoCount; + int redoCount; +}; + +void tst_QPlainTextEdit::undoRedoAvailableRepetition() +{ + UndoRedoRecorder spy(ed->document()); + + ed->textCursor().insertText("ABC\n\nDEF\n\nGHI\n"); + ed->textCursor().insertText("foo\n"); + ed->textCursor().insertText("bar\n"); + ed->undo(); ed->undo(); ed->undo(); + ed->redo(); ed->redo(); ed->redo(); + + QVERIFY(!spy.undoRepetitions); + QVERIFY(!spy.redoRepetitions); +} + +void tst_QPlainTextEdit::appendShouldNotTouchTheSelection() +{ + QTextCursor cursor(ed->document()); + QTextCharFormat fmt; + fmt.setForeground(Qt::blue); + cursor.insertText("H", fmt); + fmt.setForeground(Qt::red); + cursor.insertText("ey", fmt); + + cursor.insertText("some random text inbetween"); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::blue)); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::red)); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::red)); + QCOMPARE(cursor.selectedText(), QString("Hey")); + + ed->setTextCursor(cursor); + QVERIFY(ed->textCursor().hasSelection()); + + ed->appendHtml("<b>Some Bold Text</b>"); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::blue)); +} + +void tst_QPlainTextEdit::backspace() +{ + QTextCursor cursor = ed->textCursor(); + + QTextListFormat listFmt; + listFmt.setStyle(QTextListFormat::ListDisc); + listFmt.setIndent(1); + cursor.insertList(listFmt); + cursor.insertText("A"); + + ed->setTextCursor(cursor); + + // delete 'A' + QTest::keyClick(ed, Qt::Key_Backspace); + QVERIFY(ed->textCursor().currentList()); + // delete list + QTest::keyClick(ed, Qt::Key_Backspace); + QVERIFY(!ed->textCursor().currentList()); + QCOMPARE(ed->textCursor().blockFormat().indent(), 1); + // outdent paragraph + QTest::keyClick(ed, Qt::Key_Backspace); + QCOMPARE(ed->textCursor().blockFormat().indent(), 0); +} + +void tst_QPlainTextEdit::shiftBackspace() +{ + QTextCursor cursor = ed->textCursor(); + + QTextListFormat listFmt; + listFmt.setStyle(QTextListFormat::ListDisc); + listFmt.setIndent(1); + cursor.insertList(listFmt); + cursor.insertText("A"); + + ed->setTextCursor(cursor); + + // delete 'A' + QTest::keyClick(ed, Qt::Key_Backspace, Qt::ShiftModifier); + QVERIFY(ed->textCursor().currentList()); + // delete list + QTest::keyClick(ed, Qt::Key_Backspace, Qt::ShiftModifier); + QVERIFY(!ed->textCursor().currentList()); + QCOMPARE(ed->textCursor().blockFormat().indent(), 1); + // outdent paragraph + QTest::keyClick(ed, Qt::Key_Backspace, Qt::ShiftModifier); + QCOMPARE(ed->textCursor().blockFormat().indent(), 0); +} + +void tst_QPlainTextEdit::undoRedo() +{ + ed->clear(); + QTest::keyClicks(ed, "abc d"); + QCOMPARE(ed->toPlainText(), QString("abc d")); + ed->undo(); + QCOMPARE(ed->toPlainText(), QString()); + ed->redo(); + QCOMPARE(ed->toPlainText(), QString("abc d")); +#ifdef Q_WS_WIN + // shortcut for undo + QTest::keyClick(ed, Qt::Key_Backspace, Qt::AltModifier); + QCOMPARE(ed->toPlainText(), QString()); + // shortcut for redo + QTest::keyClick(ed, Qt::Key_Backspace, Qt::ShiftModifier|Qt::AltModifier); + QCOMPARE(ed->toPlainText(), QString("abc d")); +#endif +} + +// Task #70465 +void tst_QPlainTextEdit::preserveCharFormatInAppend() +{ + ed->appendHtml("First para"); + ed->appendHtml("<b>Second para</b>"); + ed->appendHtml("third para"); + + QTextCursor cursor(ed->textCursor()); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.charFormat().fontWeight(), (int)QFont::Normal); + QCOMPARE(cursor.block().text(), QString("First para")); + + cursor.movePosition(QTextCursor::NextBlock); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.charFormat().fontWeight(), (int)QFont::Bold); + QCOMPARE(cursor.block().text(), QString("Second para")); + + cursor.movePosition(QTextCursor::NextBlock); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.charFormat().fontWeight(), (int)QFont::Normal); + QCOMPARE(cursor.block().text(), QString("third para")); +} + +#ifndef QT_NO_CLIPBOARD +void tst_QPlainTextEdit::copyAndSelectAllInReadonly() +{ + if (!nativeClipboardWorking()) + QSKIP("Clipboard not working with cron-started unit tests", SkipAll); + + ed->setReadOnly(true); + ed->setPlainText("Hello World"); + + QTextCursor cursor = ed->textCursor(); + cursor.clearSelection(); + ed->setTextCursor(cursor); + QVERIFY(!ed->textCursor().hasSelection()); + + QCOMPARE(ed->toPlainText(), QString("Hello World")); + + // shouldn't do anything + QTest::keyClick(ed, Qt::Key_A); + + QCOMPARE(ed->toPlainText(), QString("Hello World")); + + QTest::keyClick(ed, Qt::Key_A, Qt::ControlModifier); + + QVERIFY(ed->textCursor().hasSelection()); + + QApplication::clipboard()->setText(QString()); + QVERIFY(QApplication::clipboard()->text().isEmpty()); + + QTest::keyClick(ed, Qt::Key_C, Qt::ControlModifier); + QCOMPARE(QApplication::clipboard()->text(), QString("Hello World")); +} +#endif + +void tst_QPlainTextEdit::ctrlAltInput() +{ + QTest::keyClick(ed, Qt::Key_At, Qt::ControlModifier | Qt::AltModifier); + QCOMPARE(ed->toPlainText(), QString("@")); +} + +void tst_QPlainTextEdit::noPropertiesOnDefaultTextEditCharFormat() +{ + // there should be no properties set on the default/initial char format + // on a text edit. Font properties instead should be taken from the + // widget's font (in sync with defaultFont property in document) and the + // foreground color should be taken from the palette. + QCOMPARE(ed->textCursor().charFormat().properties().count(), 0); +} + +void tst_QPlainTextEdit::setPlainTextShouldEmitTextChangedOnce() +{ + QSignalSpy spy(ed, SIGNAL(textChanged())); + ed->setPlainText("Yankee Doodle"); + QCOMPARE(spy.count(), 1); + ed->setPlainText(""); + QCOMPARE(spy.count(), 2); +} + +void tst_QPlainTextEdit::overwriteMode() +{ + QVERIFY(!ed->overwriteMode()); + QTest::keyClicks(ed, "Some first text"); + + QCOMPARE(ed->toPlainText(), QString("Some first text")); + + ed->setOverwriteMode(true); + + QTextCursor cursor = ed->textCursor(); + cursor.setPosition(5); + ed->setTextCursor(cursor); + + QTest::keyClicks(ed, "shiny"); + QCOMPARE(ed->toPlainText(), QString("Some shiny text")); + + cursor.movePosition(QTextCursor::End); + ed->setTextCursor(cursor); + + QTest::keyClick(ed, Qt::Key_Enter); + + ed->setOverwriteMode(false); + QTest::keyClicks(ed, "Second paragraph"); + + QCOMPARE(blockCount(), 2); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::EndOfBlock); + + QCOMPARE(cursor.position(), 15); + ed->setTextCursor(cursor); + + ed->setOverwriteMode(true); + + QTest::keyClicks(ed, " blah"); + + QCOMPARE(blockCount(), 2); + + QTextBlock block = ed->document()->begin(); + QCOMPARE(block.text(), QString("Some shiny text blah")); + block = block.next(); + QCOMPARE(block.text(), QString("Second paragraph")); +} + +void tst_QPlainTextEdit::shiftDownInLineLastShouldSelectToEnd_data() +{ + // shift cursor-down in the last line should select to the end of the document + + QTest::addColumn<QString>("input"); + QTest::addColumn<int>("totalLineCount"); + + QTest::newRow("1") << QString("Foo\nBar") << 2; + QTest::newRow("2") << QString("Foo\nBar") + QChar(QChar::LineSeparator) + QString("Baz") << 3; +} + +void tst_QPlainTextEdit::shiftDownInLineLastShouldSelectToEnd() +{ + QFETCH(QString, input); + QFETCH(int, totalLineCount); + + ed->setPlainText(input); + ed->show(); + + // ensure we're layouted + for (QTextBlock block = ed->document()->begin(); block.isValid(); block = block.next()) + ed->document()->documentLayout()->blockBoundingRect(block); + + QCOMPARE(blockCount(), 2); + + int lineCount = 0; + for (QTextBlock block = ed->document()->begin(); block.isValid(); block = block.next()) + lineCount += block.layout()->lineCount(); + QCOMPARE(lineCount, totalLineCount); + + QTextCursor cursor = ed->textCursor(); + cursor.movePosition(QTextCursor::Start); + ed->setTextCursor(cursor); + + for (int i = 0; i < lineCount; ++i) { + QTest::keyClick(ed, Qt::Key_Down, Qt::ShiftModifier); + } + + input.replace(QLatin1Char('\n'), QChar(QChar::ParagraphSeparator)); + QCOMPARE(ed->textCursor().selectedText(), input); + QVERIFY(ed->textCursor().atEnd()); + + // also test that without shift modifier the cursor does not move to the end + // for Key_Down in the last line + cursor.movePosition(QTextCursor::Start); + ed->setTextCursor(cursor); + for (int i = 0; i < lineCount; ++i) { + QTest::keyClick(ed, Qt::Key_Down); + } + QVERIFY(!ed->textCursor().atEnd()); +} + +void tst_QPlainTextEdit::undoRedoShouldRepositionTextEditCursor() +{ + ed->setPlainText("five\nlines\nin\nthis\ntextedit"); + QTextCursor cursor = ed->textCursor(); + cursor.movePosition(QTextCursor::Start); + + ed->setUndoRedoEnabled(false); + ed->setUndoRedoEnabled(true); + + QVERIFY(!ed->document()->isUndoAvailable()); + QVERIFY(!ed->document()->isRedoAvailable()); + + cursor.insertText("Blah"); + + QVERIFY(ed->document()->isUndoAvailable()); + QVERIFY(!ed->document()->isRedoAvailable()); + + cursor.movePosition(QTextCursor::End); + ed->setTextCursor(cursor); + + QVERIFY(QMetaObject::invokeMethod(ed, "undo")); + + QVERIFY(!ed->document()->isUndoAvailable()); + QVERIFY(ed->document()->isRedoAvailable()); + + QCOMPARE(ed->textCursor().position(), 0); + + cursor.movePosition(QTextCursor::End); + ed->setTextCursor(cursor); + + QVERIFY(QMetaObject::invokeMethod(ed, "redo")); + + QVERIFY(ed->document()->isUndoAvailable()); + QVERIFY(!ed->document()->isRedoAvailable()); + + QCOMPARE(ed->textCursor().position(), 4); +} + +void tst_QPlainTextEdit::lineWrapModes() +{ + QWidget *window = new QWidget; + ed->setParent(window); + window->show(); + ed->show(); + ed->setPlainText("a b c d e f g h i j k l m n o p q r s t u v w x y z"); + ed->setLineWrapMode(QPlainTextEdit::NoWrap); + QCOMPARE(lineCount(), 1); + ed->setLineWrapMode(QPlainTextEdit::WidgetWidth); + ed->resize(1, 1000); + QCOMPARE(lineCount(), 26); + ed->setParent(0); + delete window; +} + +void tst_QPlainTextEdit::mouseCursorShape() +{ +#ifndef QT_NO_CURSOR + // always show an IBeamCursor, see change 170146 + QVERIFY(!ed->isReadOnly()); + QVERIFY(ed->viewport()->cursor().shape() == Qt::IBeamCursor); + + ed->setReadOnly(true); + QVERIFY(ed->viewport()->cursor().shape() == Qt::IBeamCursor); + + ed->setPlainText("Foo"); + QVERIFY(ed->viewport()->cursor().shape() == Qt::IBeamCursor); +#endif +} + +void tst_QPlainTextEdit::implicitClear() +{ + // test that QPlainTextEdit::setHtml, etc. avoid calling clear() but instead call + // QTextDocument::setHtml/etc. instead, which also clear the contents and + // cached resource but preserve manually added resources. setHtml on a textedit + // should behave the same as on a document with respect to that. + // see also clearResources() autotest in qtextdocument + + // regular resource for QTextDocument + QUrl testUrl(":/foobar"); + QVariant testResource("hello world"); + + ed->document()->addResource(QTextDocument::ImageResource, testUrl, testResource); + QVERIFY(ed->document()->resource(QTextDocument::ImageResource, testUrl) == testResource); + + ed->setPlainText("Blah"); + QVERIFY(ed->document()->resource(QTextDocument::ImageResource, testUrl) == testResource); + + ed->setPlainText("<b>Blah</b>"); + QVERIFY(ed->document()->resource(QTextDocument::ImageResource, testUrl) == testResource); + + ed->clear(); + QVERIFY(!ed->document()->resource(QTextDocument::ImageResource, testUrl).isValid()); + QVERIFY(ed->toPlainText().isEmpty()); +} + +#ifndef QT_NO_CLIPBOARD +void tst_QPlainTextEdit::copyAvailable_data() +{ + QTest::addColumn<pairListType>("keystrokes"); + QTest::addColumn<QList<bool> >("copyAvailable"); + QTest::addColumn<QString>("function"); + + pairListType keystrokes; + QList<bool> copyAvailable; + + keystrokes << qMakePair(Qt::Key_B, Qt::NoModifier) << qMakePair(Qt::Key_B, Qt::NoModifier) + << qMakePair(Qt::Key_Left, Qt::ShiftModifier); + copyAvailable << true ; + QTest::newRow(QString("Case1 B,B, <- + shift | signals: true").toLatin1()) + << keystrokes << copyAvailable << QString(); + + keystrokes.clear(); + copyAvailable.clear(); + + keystrokes << qMakePair(Qt::Key_T, Qt::NoModifier) << qMakePair(Qt::Key_A, Qt::NoModifier) + << qMakePair(Qt::Key_A, Qt::NoModifier) << qMakePair(Qt::Key_Left, Qt::ShiftModifier); + copyAvailable << true << false; + QTest::newRow(QString("Case2 T,A,A, <- + shift, cut() | signals: true, false").toLatin1()) + << keystrokes << copyAvailable << QString("cut"); + + keystrokes.clear(); + copyAvailable.clear(); + + keystrokes << qMakePair(Qt::Key_T, Qt::NoModifier) << qMakePair(Qt::Key_A, Qt::NoModifier) + << qMakePair(Qt::Key_A, Qt::NoModifier) << qMakePair(Qt::Key_Left, Qt::ShiftModifier) + << qMakePair(Qt::Key_Left, Qt::ShiftModifier) << qMakePair(Qt::Key_Left, Qt::ShiftModifier); + copyAvailable << true; + QTest::newRow(QString("Case3 T,A,A, <- + shift, <- + shift, <- + shift, copy() | signals: true").toLatin1()) + << keystrokes << copyAvailable << QString("copy"); + + keystrokes.clear(); + copyAvailable.clear(); + + keystrokes << qMakePair(Qt::Key_T, Qt::NoModifier) << qMakePair(Qt::Key_A, Qt::NoModifier) + << qMakePair(Qt::Key_A, Qt::NoModifier) << qMakePair(Qt::Key_Left, Qt::ShiftModifier) + << qMakePair(Qt::Key_Left, Qt::ShiftModifier) << qMakePair(Qt::Key_Left, Qt::ShiftModifier) + << qMakePair(Qt::Key_X, Qt::ControlModifier); + copyAvailable << true << false; + QTest::newRow(QString("Case4 T,A,A, <- + shift, <- + shift, <- + shift, ctrl + x, paste() | signals: true, false").toLatin1()) + << keystrokes << copyAvailable << QString("paste"); + + keystrokes.clear(); + copyAvailable.clear(); + + keystrokes << qMakePair(Qt::Key_B, Qt::NoModifier) << qMakePair(Qt::Key_B, Qt::NoModifier) + << qMakePair(Qt::Key_Left, Qt::ShiftModifier) << qMakePair(Qt::Key_Left, Qt::NoModifier); + copyAvailable << true << false; + QTest::newRow(QString("Case5 B,B, <- + shift, <- | signals: true, false").toLatin1()) + << keystrokes << copyAvailable << QString(); + + keystrokes.clear(); + copyAvailable.clear(); + + keystrokes << qMakePair(Qt::Key_B, Qt::NoModifier) << qMakePair(Qt::Key_A, Qt::NoModifier) + << qMakePair(Qt::Key_Left, Qt::ShiftModifier) << qMakePair(Qt::Key_Left, Qt::NoModifier) + << qMakePair(Qt::Key_Right, Qt::ShiftModifier); + copyAvailable << true << false << true << false; + QTest::newRow(QString("Case6 B,A, <- + shift, ->, <- + shift | signals: true, false, true, false").toLatin1()) + << keystrokes << copyAvailable << QString("cut"); + + keystrokes.clear(); + copyAvailable.clear(); + + keystrokes << qMakePair(Qt::Key_T, Qt::NoModifier) << qMakePair(Qt::Key_A, Qt::NoModifier) + << qMakePair(Qt::Key_A, Qt::NoModifier) << qMakePair(Qt::Key_Left, Qt::ShiftModifier) + << qMakePair(Qt::Key_Left, Qt::ShiftModifier) << qMakePair(Qt::Key_Left, Qt::ShiftModifier) + << qMakePair(Qt::Key_X, Qt::ControlModifier); + copyAvailable << true << false << true; + QTest::newRow(QString("Case7 T,A,A, <- + shift, <- + shift, <- + shift, ctrl + x, undo() | signals: true, false, true").toLatin1()) + << keystrokes << copyAvailable << QString("undo"); +} + +//Tests the copyAvailable slot for several cases +void tst_QPlainTextEdit::copyAvailable() +{ + QFETCH(pairListType,keystrokes); + QFETCH(QList<bool>, copyAvailable); + QFETCH(QString, function); + +#ifdef Q_WS_MAC + QSKIP("copyAvailable has never passed on Mac, task to fix is 132482", SkipAll); +#endif + ed->clear(); + QApplication::clipboard()->clear(); + QVERIFY(!ed->canPaste()); + QSignalSpy spyCopyAvailabe(ed, SIGNAL(copyAvailable(bool))); + + //Execute Keystrokes + foreach(keyPairType keyPair, keystrokes) { + QTest::keyClick(ed, keyPair.first, keyPair.second ); + } + + //Execute ed->"function" + if (function == "cut") + ed->cut(); + else if (function == "copy") + ed->copy(); + else if (function == "paste") + ed->paste(); + else if (function == "undo") + ed->paste(); + else if (function == "redo") + ed->paste(); + + //Compare spied signals + QEXPECT_FAIL("Case7 T,A,A, <- + shift, <- + shift, <- + shift, ctrl + x, undo() | signals: true, false, true", + "Wrong undo selection behaviour. Should be fixed in some future release. (See task: 132482)", Abort); + QCOMPARE(spyCopyAvailabe.count(), copyAvailable.count()); + for (int i=0;i<spyCopyAvailabe.count(); i++) { + QVariant variantSpyCopyAvailable = spyCopyAvailabe.at(i).at(0); + QVERIFY2(variantSpyCopyAvailable.toBool() == copyAvailable.at(i), QString("Spied singnal: %1").arg(i).toLatin1()); + } +} +#endif + +void tst_QPlainTextEdit::undoRedoAfterSetContent() +{ + QVERIFY(!ed->document()->isUndoAvailable()); + QVERIFY(!ed->document()->isRedoAvailable()); + ed->setPlainText("Foobar"); + QVERIFY(!ed->document()->isUndoAvailable()); + QVERIFY(!ed->document()->isRedoAvailable()); + ed->setPlainText("<p>bleh</p>"); + QVERIFY(!ed->document()->isUndoAvailable()); + QVERIFY(!ed->document()->isRedoAvailable()); +} + +void tst_QPlainTextEdit::numPadKeyNavigation() +{ + ed->setPlainText("Hello World"); + QCOMPARE(ed->textCursor().position(), 0); + QTest::keyClick(ed, Qt::Key_Right, Qt::KeypadModifier); + QCOMPARE(ed->textCursor().position(), 1); +} + +void tst_QPlainTextEdit::moveCursor() +{ + ed->setPlainText("Test"); + + QSignalSpy cursorMovedSpy(ed, SIGNAL(cursorPositionChanged())); + + QCOMPARE(ed->textCursor().position(), 0); + ed->moveCursor(QTextCursor::NextCharacter); + QCOMPARE(ed->textCursor().position(), 1); + QCOMPARE(cursorMovedSpy.count(), 1); + ed->moveCursor(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + QCOMPARE(ed->textCursor().position(), 2); + QCOMPARE(cursorMovedSpy.count(), 2); + QCOMPARE(ed->textCursor().selectedText(), QString("e")); +} + +class MyTextEdit : public QPlainTextEdit +{ +public: + inline MyTextEdit() + : createMimeDataCallCount(0), + canInsertCallCount(0), + insertCallCount(0) + {} + + mutable int createMimeDataCallCount; + mutable int canInsertCallCount; + mutable int insertCallCount; + + virtual QMimeData *createMimeDataFromSelection() const { + createMimeDataCallCount++; + return QPlainTextEdit::createMimeDataFromSelection(); + } + virtual bool canInsertFromMimeData(const QMimeData *source) const { + canInsertCallCount++; + return QPlainTextEdit::canInsertFromMimeData(source); + } + virtual void insertFromMimeData(const QMimeData *source) { + insertCallCount++; + QPlainTextEdit::insertFromMimeData(source); + } + +}; + +#ifndef QT_NO_CLIPBOARD +void tst_QPlainTextEdit::mimeDataReimplementations() +{ + MyTextEdit ed; + ed.setPlainText("Hello World"); + + QCOMPARE(ed.createMimeDataCallCount, 0); + QCOMPARE(ed.canInsertCallCount, 0); + QCOMPARE(ed.insertCallCount, 0); + + ed.selectAll(); + + QCOMPARE(ed.createMimeDataCallCount, 0); + QCOMPARE(ed.canInsertCallCount, 0); + QCOMPARE(ed.insertCallCount, 0); + + ed.copy(); + + QCOMPARE(ed.createMimeDataCallCount, 1); + QCOMPARE(ed.canInsertCallCount, 0); + QCOMPARE(ed.insertCallCount, 0); + + QTextControl *control = qFindChild<QTextControl *>(&ed); + QVERIFY(control); + + control->canInsertFromMimeData(QApplication::clipboard()->mimeData()); + + QCOMPARE(ed.createMimeDataCallCount, 1); + QCOMPARE(ed.canInsertCallCount, 1); + QCOMPARE(ed.insertCallCount, 0); + + ed.paste(); + + QCOMPARE(ed.createMimeDataCallCount, 1); + QCOMPARE(ed.canInsertCallCount, 1); + QCOMPARE(ed.insertCallCount, 1); +} +#endif + +void tst_QPlainTextEdit::shiftEnterShouldInsertLineSeparator() +{ + QTest::keyClick(ed, Qt::Key_A); + QTest::keyClick(ed, Qt::Key_Enter, Qt::ShiftModifier); + QTest::keyClick(ed, Qt::Key_B); + QString expected; + expected += 'a'; + expected += QChar::LineSeparator; + expected += 'b'; + QCOMPARE(ed->textCursor().block().text(), expected); +} + +void tst_QPlainTextEdit::selectWordsFromStringsContainingSeparators_data() +{ + QTest::addColumn<QString>("testString"); + QTest::addColumn<QString>("selectedWord"); + + QStringList wordSeparators; + wordSeparators << "." << "," << "?" << "!" << ":" << ";" << "-" << "<" << ">" << "[" + << "]" << "(" << ")" << "{" << "}" << "=" << "\t"<< QString(QChar::Nbsp); + + foreach (QString s, wordSeparators) + QTest::newRow(QString("separator: " + s).toLocal8Bit()) << QString("foo") + s + QString("bar") << QString("foo"); +} + +void tst_QPlainTextEdit::selectWordsFromStringsContainingSeparators() +{ + QFETCH(QString, testString); + QFETCH(QString, selectedWord); + ed->setPlainText(testString); + QTextCursor cursor = ed->textCursor(); + cursor.movePosition(QTextCursor::StartOfLine); + cursor.select(QTextCursor::WordUnderCursor); + QVERIFY(cursor.hasSelection()); + QCOMPARE(cursor.selection().toPlainText(), selectedWord); + cursor.clearSelection(); +} + +#ifndef QT_NO_CLIPBOARD +void tst_QPlainTextEdit::canPaste() +{ + if (!nativeClipboardWorking()) + QSKIP("Clipboard not working with cron-started unit tests", SkipAll); + + QApplication::clipboard()->setText(QString()); + QVERIFY(!ed->canPaste()); + QApplication::clipboard()->setText("Test"); + QVERIFY(ed->canPaste()); + ed->setTextInteractionFlags(Qt::NoTextInteraction); + QVERIFY(!ed->canPaste()); +} +#endif + +void tst_QPlainTextEdit::ensureCursorVisibleOnInitialShow() +{ + QString manyPagesOfPlainText; + for (int i = 0; i < 800; ++i) + manyPagesOfPlainText += QLatin1String("Blah blah blah blah blah blah\n"); + + ed->setPlainText(manyPagesOfPlainText); + QCOMPARE(ed->textCursor().position(), 0); + + ed->moveCursor(QTextCursor::End); + ed->show(); + QVERIFY(ed->verticalScrollBar()->value() > 10); + + ed->moveCursor(QTextCursor::Start); + QVERIFY(ed->verticalScrollBar()->value() < 10); + ed->hide(); + ed->verticalScrollBar()->setValue(ed->verticalScrollBar()->maximum()); + ed->show(); + QCOMPARE(ed->verticalScrollBar()->value(), ed->verticalScrollBar()->maximum()); +} + +class TestEdit : public QPlainTextEdit +{ +public: + TestEdit() : resizeEventCalled(false) {} + + bool resizeEventCalled; + +protected: + virtual void resizeEvent(QResizeEvent *e) + { + QPlainTextEdit::resizeEvent(e); + setPlainText("<img src=qtextbrowser-resizeevent.png width=" + QString::number(size().width()) + "><br>Size is " + QString::number(size().width()) + " x " + QString::number(size().height())); + resizeEventCalled = true; + } +}; + +void tst_QPlainTextEdit::setTextInsideResizeEvent() +{ + TestEdit edit; + edit.show(); + edit.resize(800, 600); + QVERIFY(edit.resizeEventCalled); +} + +void tst_QPlainTextEdit::colorfulAppend() +{ + QTextCharFormat fmt; + + fmt.setForeground(QBrush(Qt::red)); + ed->mergeCurrentCharFormat(fmt); + ed->appendPlainText("Red"); + fmt.setForeground(QBrush(Qt::blue)); + ed->mergeCurrentCharFormat(fmt); + ed->appendPlainText("Blue"); + fmt.setForeground(QBrush(Qt::green)); + ed->mergeCurrentCharFormat(fmt); + ed->appendPlainText("Green"); + + QCOMPARE(ed->document()->blockCount(), 3); + QTextBlock block = ed->document()->begin(); + QCOMPARE(block.begin().fragment().text(), QString("Red")); + QVERIFY(block.begin().fragment().charFormat().foreground().color() == Qt::red); + block = block.next(); + QCOMPARE(block.begin().fragment().text(), QString("Blue")); + QVERIFY(block.begin().fragment().charFormat().foreground().color() == Qt::blue); + block = block.next(); + QCOMPARE(block.begin().fragment().text(), QString("Green")); + QVERIFY(block.begin().fragment().charFormat().foreground().color() == Qt::green); +} + +void tst_QPlainTextEdit::ensureVisibleWithRtl() +{ + ed->setLayoutDirection(Qt::RightToLeft); + ed->setLineWrapMode(QPlainTextEdit::NoWrap); + QString txt(500, QChar(QLatin1Char('a'))); + QCOMPARE(txt.length(), 500); + ed->setPlainText(txt); + ed->resize(100, 100); + ed->show(); + + qApp->processEvents(); + + QVERIFY(ed->horizontalScrollBar()->maximum() > 0); + + ed->moveCursor(QTextCursor::Start); + QCOMPARE(ed->horizontalScrollBar()->value(), ed->horizontalScrollBar()->maximum()); + ed->moveCursor(QTextCursor::End); + QCOMPARE(ed->horizontalScrollBar()->value(), 0); + ed->moveCursor(QTextCursor::Start); + QCOMPARE(ed->horizontalScrollBar()->value(), ed->horizontalScrollBar()->maximum()); + ed->moveCursor(QTextCursor::End); + QCOMPARE(ed->horizontalScrollBar()->value(), 0); +} + +void tst_QPlainTextEdit::preserveCharFormatAfterSetPlainText() +{ + QTextCharFormat fmt; + fmt.setForeground(QBrush(Qt::blue)); + ed->mergeCurrentCharFormat(fmt); + ed->setPlainText("This is blue"); + ed->appendPlainText("This should still be blue"); + QTextBlock block = ed->document()->begin(); + block = block.next(); + QCOMPARE(block.text(), QString("This should still be blue")); + QVERIFY(block.begin().fragment().charFormat().foreground().color() == QColor(Qt::blue)); +} + +void tst_QPlainTextEdit::extraSelections() +{ + ed->setPlainText("Hello World"); + + QTextCursor c = ed->textCursor(); + c.movePosition(QTextCursor::Start); + c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + const int endPos = c.position(); + + QTextEdit::ExtraSelection sel; + sel.cursor = c; + ed->setExtraSelections(QList<QTextEdit::ExtraSelection>() << sel); + + c.movePosition(QTextCursor::Start); + c.movePosition(QTextCursor::NextWord); + const int wordPos = c.position(); + c.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + sel.cursor = c; + ed->setExtraSelections(QList<QTextEdit::ExtraSelection>() << sel); + + QList<QTextEdit::ExtraSelection> selections = ed->extraSelections(); + QCOMPARE(selections.count(), 1); + QCOMPARE(selections.at(0).cursor.position(), endPos); + QCOMPARE(selections.at(0).cursor.anchor(), wordPos); +} + +void tst_QPlainTextEdit::adjustScrollbars() +{ +// For some reason ff is defined to be << on Mac Panther / gcc 3.3 +#undef ff + QFont ff(ed->font()); + ff.setFamily("Tahoma"); + ff.setPointSize(11); + ed->setFont(ff); + ed->setMinimumSize(140, 100); + ed->setMaximumSize(140, 100); + ed->show(); + QLatin1String txt("\nabc def ghi jkl mno pqr stu vwx"); + ed->setPlainText(txt + txt + txt + txt); + + QVERIFY(ed->verticalScrollBar()->maximum() > 0); + + ed->moveCursor(QTextCursor::End); + int oldMaximum = ed->verticalScrollBar()->maximum(); + QTextCursor cursor = ed->textCursor(); + cursor.insertText(QLatin1String("\n")); + cursor.deletePreviousChar(); + QCOMPARE(ed->verticalScrollBar()->maximum(), oldMaximum); +} + +class SignalReceiver : public QObject +{ + Q_OBJECT +public: + SignalReceiver() : received(0) {} + + int receivedSignals() const { return received; } + QTextCharFormat charFormat() const { return format; } + +public slots: + void charFormatChanged(const QTextCharFormat &tcf) { ++received; format = tcf; } + +private: + QTextCharFormat format; + int received; +}; + +void tst_QPlainTextEdit::textObscuredByScrollbars() +{ + ed->textCursor().insertText( + "ab cab cab c abca kjsdf lka sjd lfk jsal df j kasdf abc ab abc " + "a b c d e f g h i j k l m n o p q r s t u v w x y z " + "abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc " + "ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab" + "abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc " + "ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab" + "abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc " + "ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab" + "abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc " + "ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab" + "abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc abc " + "ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab" + ); + ed->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + ed->show(); + + QSize documentSize = ed->document()->documentLayout()->documentSize().toSize(); + QSize viewportSize = ed->viewport()->size(); + + QVERIFY(documentSize.width() <= viewportSize.width()); +} + +void tst_QPlainTextEdit::setTextPreservesUndoRedoEnabled() +{ + QVERIFY(ed->isUndoRedoEnabled()); + + ed->setPlainText("Test"); + + QVERIFY(ed->isUndoRedoEnabled()); + + ed->setUndoRedoEnabled(false); + QVERIFY(!ed->isUndoRedoEnabled()); + ed->setPlainText("Test2"); + QVERIFY(!ed->isUndoRedoEnabled()); + + ed->setPlainText("<p>hello"); + QVERIFY(!ed->isUndoRedoEnabled()); +} + +void tst_QPlainTextEdit::wordWrapProperty() +{ + { + QPlainTextEdit edit; + QTextDocument *doc = new QTextDocument(&edit); + doc->setDocumentLayout(new QPlainTextDocumentLayout(doc)); + edit.setDocument(doc); + edit.setWordWrapMode(QTextOption::NoWrap); + QVERIFY(doc->defaultTextOption().wrapMode() == QTextOption::NoWrap); + } + { + QPlainTextEdit edit; + QTextDocument *doc = new QTextDocument(&edit); + doc->setDocumentLayout(new QPlainTextDocumentLayout(doc)); + edit.setWordWrapMode(QTextOption::NoWrap); + edit.setDocument(doc); + QVERIFY(doc->defaultTextOption().wrapMode() == QTextOption::NoWrap); + } +} + +void tst_QPlainTextEdit::lineWrapProperty() +{ + QVERIFY(ed->wordWrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere); + QVERIFY(ed->lineWrapMode() == QPlainTextEdit::WidgetWidth); + ed->setLineWrapMode(QPlainTextEdit::NoWrap); + QVERIFY(ed->lineWrapMode() == QPlainTextEdit::NoWrap); + QVERIFY(ed->wordWrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere); + QVERIFY(ed->document()->defaultTextOption().wrapMode() == QTextOption::NoWrap); +} + +void tst_QPlainTextEdit::selectionChanged() +{ + ed->setPlainText("Hello World"); + + ed->moveCursor(QTextCursor::Start); + + QSignalSpy selectionChangedSpy(ed, SIGNAL(selectionChanged())); + + QTest::keyClick(ed, Qt::Key_Right); + QCOMPARE(ed->textCursor().position(), 1); + QCOMPARE(selectionChangedSpy.count(), 0); + + QTest::keyClick(ed, Qt::Key_Right, Qt::ShiftModifier); + QCOMPARE(ed->textCursor().position(), 2); + QCOMPARE(selectionChangedSpy.count(), 1); + + QTest::keyClick(ed, Qt::Key_Right, Qt::ShiftModifier); + QCOMPARE(ed->textCursor().position(), 3); + QCOMPARE(selectionChangedSpy.count(), 2); + + QTest::keyClick(ed, Qt::Key_Right, Qt::ShiftModifier); + QCOMPARE(ed->textCursor().position(), 4); + QCOMPARE(selectionChangedSpy.count(), 3); + + QTest::keyClick(ed, Qt::Key_Right); + QCOMPARE(ed->textCursor().position(), 5); + QCOMPARE(selectionChangedSpy.count(), 4); + + QTest::keyClick(ed, Qt::Key_Right); + QCOMPARE(ed->textCursor().position(), 6); + QCOMPARE(selectionChangedSpy.count(), 4); +} + +void tst_QPlainTextEdit::blockCountChanged() +{ + QSignalSpy blockCountCpangedSpy(ed, SIGNAL(blockCountChanged(int))); + ed->setPlainText("Hello"); + QCOMPARE(blockCountCpangedSpy.count(), 0); + ed->setPlainText("Hello World"); + QCOMPARE(blockCountCpangedSpy.count(), 0); + ed->setPlainText("Hello \n World \n this \n has \n more \n blocks \n than \n just \n one"); + QCOMPARE(blockCountCpangedSpy.count(), 1); + ed->setPlainText("One"); + QCOMPARE(blockCountCpangedSpy.count(), 2); + ed->setPlainText("One \n Two"); + QCOMPARE(blockCountCpangedSpy.count(), 3); + ed->setPlainText("Three \n Four"); + QCOMPARE(blockCountCpangedSpy.count(), 3); +} + + +QTEST_MAIN(tst_QPlainTextEdit) +#include "tst_qplaintextedit.moc" |