From 045ec92c3255be2ed8d369eaac376a8ba4818b01 Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Mon, 6 Jan 2020 15:05:05 +0000 Subject: [PATCH] add QTextCursor test Signed-off-by: Ivailo Monev --- tests/auto/qtextcursor/CMakeLists.txt | 3 + tests/auto/qtextcursor/tst_qtextcursor.cpp | 1869 ++++++++++++++++++++++++++++ 2 files changed, 1872 insertions(+) create mode 100644 tests/auto/qtextcursor/CMakeLists.txt create mode 100644 tests/auto/qtextcursor/tst_qtextcursor.cpp diff --git a/tests/auto/qtextcursor/CMakeLists.txt b/tests/auto/qtextcursor/CMakeLists.txt new file mode 100644 index 000000000..f95afe7ff --- /dev/null +++ b/tests/auto/qtextcursor/CMakeLists.txt @@ -0,0 +1,3 @@ +katie_gui_test(tst_qtextcursor + ${CMAKE_CURRENT_SOURCE_DIR}/tst_qtextcursor.cpp +) diff --git a/tests/auto/qtextcursor/tst_qtextcursor.cpp b/tests/auto/qtextcursor/tst_qtextcursor.cpp new file mode 100644 index 000000000..1da0e5f16 --- /dev/null +++ b/tests/auto/qtextcursor/tst_qtextcursor.cpp @@ -0,0 +1,1869 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016-2020 Ivailo Monev +** +** This file is part of the test suite of the Katie 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. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//TESTED_FILES=gui/text/qtextcursor.cpp gui/text/qtextcursor_p.h + +class tst_QTextCursor : public QObject +{ + Q_OBJECT + +public: + tst_QTextCursor(); + + +public slots: + void init(); + void cleanup(); +private slots: + void navigation1(); + void navigation2_data(); + void navigation2(); + void navigation3(); + void navigation4(); + void navigation5(); + void navigation6(); + void navigation7(); + void navigation8(); + void navigation9(); + void navigation10(); + void movePositionEndOfLine(); + void insertBlock(); + void insertWithBlockSeparator1(); + void insertWithBlockSeparator2(); + void insertWithBlockSeparator3(); + void insertWithBlockSeparator4(); + void clearObjectType1(); + void clearObjectType2(); + void clearObjectType3(); + void comparisonOperators1(); + void comparisonOperators2(); + void selection1(); + void dontCopyTableAttributes(); + + void checkFrame1(); + void checkFrame2(); + + void tableMovement(); + void selectionsInTable(); + + void insertBlockToUseCharFormat(); + + void selectedText(); + + void insertBlockShouldRemoveSelection(); + void insertBlockShouldRemoveSelection2(); + void mergeCellShouldUpdateSelection(); + + void joinPreviousEditBlock(); + + void setBlockFormatInTable(); + + void blockCharFormat(); + void blockCharFormat2(); + void blockCharFormat3(); + void blockCharFormatOnSelection(); + + void anchorInitialized1(); + void anchorInitialized2(); + void anchorInitialized3(); + + void selectWord(); + void selectWordWithSeparators_data(); + void selectWordWithSeparators(); + void startOfWord(); + void selectBlock(); + void selectVisually(); + + void insertText(); + + void insertFragmentShouldUseCurrentCharFormat(); + + void endOfLine(); + + void editBlocksDuringRemove(); + void selectAllDuringRemove(); + + void update_data(); + void update(); + + void disallowSettingObjectIndicesOnCharFormats(); + + void blockAndColumnNumber(); + + void clearCells(); + + void task244408_wordUnderCursor_data(); + void task244408_wordUnderCursor(); + + void adjustCursorsOnInsert(); + + void cursorPositionWithBlockUndoAndRedo(); + void cursorPositionWithBlockUndoAndRedo2(); + void cursorPositionWithBlockUndoAndRedo3(); + + void joinNonEmptyRemovedBlockUserState(); + +private: + int blockCount(); + + QTextDocument *doc; + QTextCursor cursor; +}; + +Q_DECLARE_METATYPE(QList) + +tst_QTextCursor::tst_QTextCursor() +{} + +void tst_QTextCursor::init() +{ + doc = new QTextDocument; + cursor = QTextCursor(doc); +} + +void tst_QTextCursor::cleanup() +{ + cursor = QTextCursor(); + delete doc; + doc = Q_NULLPTR; +} + +void tst_QTextCursor::navigation1() +{ + + cursor.insertText("Hello World"); + QVERIFY(doc->toPlainText() == "Hello World"); + + cursor.movePosition(QTextCursor::End); + QVERIFY(cursor.position() == 11); + cursor.deletePreviousChar(); + QVERIFY(cursor.position() == 10); + cursor.deletePreviousChar(); + cursor.deletePreviousChar(); + cursor.deletePreviousChar(); + cursor.deletePreviousChar(); + cursor.deletePreviousChar(); + QVERIFY(doc->toPlainText() == "Hello"); + + QTextCursor otherCursor(doc); + otherCursor.movePosition(QTextCursor::Start); + otherCursor.movePosition(QTextCursor::Right); + cursor = otherCursor; + cursor.movePosition(QTextCursor::Right); + QVERIFY(cursor != otherCursor); + otherCursor.insertText("Hey"); + QVERIFY(cursor.position() == 5); + + doc->undo(); + QVERIFY(cursor.position() == 2); + doc->redo(); + QVERIFY(cursor.position() == 5); + + doc->undo(); + + doc->undo(); + QVERIFY(doc->toPlainText() == "Hello World"); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 6); + QVERIFY(cursor.position() == 6); + otherCursor = cursor; + otherCursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2); + otherCursor.deletePreviousChar(); + otherCursor.deletePreviousChar(); + otherCursor.deletePreviousChar(); + QVERIFY(cursor.position() == 5); + + cursor.movePosition(QTextCursor::End); + cursor.insertBlock(); + { + int oldPos = cursor.position(); + cursor.movePosition(QTextCursor::End); + QVERIFY(cursor.position() == oldPos); + } + QVERIFY(cursor.atBlockStart()); + QVERIFY(cursor.position() == 9); + + QTextCharFormat fmt; + fmt.setForeground(Qt::blue); + cursor.insertText("Test", fmt); + QVERIFY(fmt == cursor.charFormat()); + QVERIFY(cursor.position() == 13); +} + +void tst_QTextCursor::navigation2_data() +{ + QTest::addColumn("sl"); + QTest::addColumn >("movement"); + QTest::addColumn("finalPos"); + + QTest::newRow("startBlock1") << QStringList("Happy happy happy joy joy joy") + << (QList() << QVariant(QTextCursor::StartOfBlock)) << 0; + QTest::newRow("endBlock1") << QStringList("Happy happy happy joy joy joy") + << (QList() << QVariant(QTextCursor::StartOfBlock) + << QVariant(QTextCursor::EndOfBlock)) << 29; + QTest::newRow("startBlock2") << QStringList("Happy happy happy joy joy joy") + << (QList() << QVariant(QTextCursor::StartOfBlock) + << QVariant(QTextCursor::EndOfBlock) + << QVariant(QTextCursor::StartOfBlock)) << 0; + QTest::newRow("endBlock2") << QStringList("Happy happy happy joy joy joy") + << (QList() << QVariant(QTextCursor::StartOfBlock) + << QVariant(QTextCursor::EndOfBlock) + << QVariant(QTextCursor::StartOfBlock) + << QVariant(QTextCursor::EndOfBlock) + ) << 29; + QTest::newRow("multiBlock1") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::StartOfBlock)) + << 18; + QTest::newRow("multiBlock2") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::StartOfBlock) + << QVariant(QTextCursor::EndOfBlock)) + << 29; + QTest::newRow("multiBlock3") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::StartOfBlock) + << QVariant(QTextCursor::StartOfBlock)) + << 18; + QTest::newRow("multiBlock4") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::Start) + << QVariant(QTextCursor::EndOfBlock)) + << 17; + QTest::newRow("multiBlock5") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::Start) + << QVariant(QTextCursor::EndOfBlock) + << QVariant(QTextCursor::EndOfBlock)) + << 17; + QTest::newRow("multiBlock6") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::End) + << QVariant(QTextCursor::StartOfBlock)) + << 18; + QTest::newRow("multiBlock7") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousBlock)) + << 0; + QTest::newRow("multiBlock8") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousBlock) + << QVariant(QTextCursor::EndOfBlock)) + << 17; + QTest::newRow("multiBlock9") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousBlock) + << QVariant(QTextCursor::NextBlock)) + << 18; + QTest::newRow("multiBlock10") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousBlock) + << QVariant(QTextCursor::NextBlock) + << QVariant(QTextCursor::NextBlock)) + << 18; + QTest::newRow("multiBlock11") << (QStringList() << QString("Happy happy happy") + << QString("Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousBlock) + << QVariant(QTextCursor::NextBlock) + << QVariant(QTextCursor::EndOfBlock)) + << 29; + QTest::newRow("PreviousWord1") << (QStringList() << QString("Happy happy happy Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousWord)) + << 26; + QTest::newRow("PreviousWord2") << (QStringList() << QString("Happy happy happy Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousWord) + << QVariant(QTextCursor::PreviousWord)) + << 22; + QTest::newRow("EndWord1") << (QStringList() << QString("Happy happy happy Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousWord) + << QVariant(QTextCursor::PreviousWord) + << QVariant(QTextCursor::EndOfWord)) + << 25; + QTest::newRow("NextWord1") << (QStringList() << QString("Happy happy happy Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousWord) + << QVariant(QTextCursor::PreviousWord) + << QVariant(QTextCursor::NextWord)) + << 26; + QTest::newRow("NextWord2") << (QStringList() << QString("Happy happy happy Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::Start) + << QVariant(QTextCursor::NextWord) + << QVariant(QTextCursor::EndOfWord)) + << 11; + QTest::newRow("StartWord1") << (QStringList() << QString("Happy happy happy Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousWord) + << QVariant(QTextCursor::PreviousWord) + << QVariant(QTextCursor::StartOfWord)) + << 22; + QTest::newRow("StartWord3") << (QStringList() << QString("Happy happy happy Joy Joy Joy")) + << (QList() << QVariant(QTextCursor::Start) + << QVariant(QTextCursor::NextWord) + << QVariant(QTextCursor::EndOfWord) + << QVariant(QTextCursor::StartOfWord)) + << 6; + + QTest::newRow("PreviousCharacter") << (QStringList() << QString("Happy happy Joy Joy")) + << (QList() << QVariant(QTextCursor::PreviousCharacter) + << QVariant(QTextCursor::PreviousCharacter)) + << 17; +} + +void tst_QTextCursor::navigation2() +{ + QFETCH(QStringList, sl); + QFETCH(QList, movement); + int i; + for (i = 0; i < sl.size(); ++i) { + cursor.insertText(sl.at(i)); + if (i < sl.size() - 1) + cursor.insertBlock(); + } + + for (i = 0; i < movement.size(); ++i) + cursor.movePosition(QTextCursor::MoveOperation(movement.at(i).toInt())); + QTEST(cursor.position(), "finalPos"); +} + +void tst_QTextCursor::navigation3() +{ + cursor.insertText("a"); + cursor.deletePreviousChar(); + QCOMPARE(cursor.position(), 0); + QVERIFY(doc->toPlainText().isEmpty()); +} + +void tst_QTextCursor::navigation4() +{ + cursor.insertText(" Test "); + + cursor.setPosition(4); + cursor.movePosition(QTextCursor::EndOfWord); + QCOMPARE(cursor.position(), 6); +} + +void tst_QTextCursor::navigation5() +{ + cursor.insertText("Test"); + cursor.insertBlock(); + cursor.insertText("Test"); + + cursor.setPosition(0); + cursor.movePosition(QTextCursor::EndOfBlock); + QCOMPARE(cursor.position(), 4); +} + +void tst_QTextCursor::navigation6() +{ + // triger creation of document layout, so that QTextLines are there + doc->documentLayout(); + doc->setTextWidth(1000); + + cursor.insertText("Test "); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::EndOfLine); + QCOMPARE(cursor.position(), 8); +} + +void tst_QTextCursor::navigation7() +{ + QVERIFY(doc->isEmpty()); + for (int i = QTextCursor::Start; i <= QTextCursor::WordRight; ++i) + QVERIFY(!cursor.movePosition(QTextCursor::MoveOperation(i))); + + doc->setPlainText("Hello World"); + cursor.movePosition(QTextCursor::Start); + do { + } while (cursor.movePosition(QTextCursor::NextCharacter)); + QVERIFY(true /*reached*/); +} + +void tst_QTextCursor::navigation8() +{ + cursor.insertList(QTextListFormat::ListDecimal); + QCOMPARE(cursor.position(), 1); + cursor.insertText("foo"); + QCOMPARE(cursor.position(), 4); + + cursor.insertList(QTextListFormat::ListCircle); + QCOMPARE(cursor.position(), 5); + cursor.insertText("something"); + QCOMPARE(cursor.position(), 14); + + cursor.movePosition(QTextCursor::PreviousCharacter); + QCOMPARE(cursor.position(), 13); + + cursor.setPosition(2); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.position(), 3); +} + +void tst_QTextCursor::navigation9() +{ + cursor.insertText("Hello &-=+\t World"); + cursor.movePosition(QTextCursor::PreviousWord); + QCOMPARE(cursor.position(), 15); + cursor.movePosition(QTextCursor::PreviousWord); + QCOMPARE(cursor.position(), 7); + cursor.movePosition(QTextCursor::PreviousWord); + QCOMPARE(cursor.position(), 0); + cursor.movePosition(QTextCursor::NextWord); + QCOMPARE(cursor.position(), 7); + cursor.movePosition(QTextCursor::NextWord); + QCOMPARE(cursor.position(), 15); +} + +void tst_QTextCursor::navigation10() +{ + doc->setHtml("

just a simple paragraph.

" + "" + "" + "" + "" + "
Cell number 1another cellprevious
is
empty
row 2foo barlast cell
row 3a
clear(); + doc->setHtml("tr>
ab
c
"); + cursor.setPosition(1); // a + ok = cursor.movePosition(QTextCursor::NextCell); + QVERIFY(ok); + QCOMPARE(cursor.position(), 3); // b + ok = cursor.movePosition(QTextCursor::NextCell); + QVERIFY(ok); + QCOMPARE(cursor.position(), 5); // c + ok = cursor.movePosition(QTextCursor::PreviousCell); + QVERIFY(ok); + QCOMPARE(cursor.position(), 3); // b + ok = cursor.movePosition(QTextCursor::PreviousCell); + QVERIFY(ok); + QCOMPARE(cursor.position(), 1); // a +} + +void tst_QTextCursor::insertBlock() +{ + QTextBlockFormat fmt; + fmt.setTopMargin(100); + cursor.insertBlock(fmt); + QVERIFY(cursor.position() == 1); + QVERIFY(cursor.blockFormat() == fmt); +} + +void tst_QTextCursor::insertWithBlockSeparator1() +{ + QString text = "Hello" + QString(QChar::ParagraphSeparator) + "World"; + + cursor.insertText(text); + + cursor.movePosition(QTextCursor::PreviousBlock); + QVERIFY(cursor.position() == 0); + + cursor.movePosition(QTextCursor::NextBlock); + QVERIFY(cursor.position() == 6); +} + +void tst_QTextCursor::insertWithBlockSeparator2() +{ + cursor.insertText(QString(QChar::ParagraphSeparator)); + QVERIFY(cursor.position() == 1); +} + +void tst_QTextCursor::insertWithBlockSeparator3() +{ + cursor.insertText(QString(QChar::ParagraphSeparator) + "Hi" + QString(QChar::ParagraphSeparator)); + QVERIFY(cursor.position() == 4); +} + +void tst_QTextCursor::insertWithBlockSeparator4() +{ + cursor.insertText(QString(QChar::ParagraphSeparator) + QString(QChar::ParagraphSeparator)); + QVERIFY(cursor.position() == 2); +} + +void tst_QTextCursor::clearObjectType1() +{ + cursor.insertImage("test.png"); + QVERIFY(cursor.charFormat().isValid()); + QVERIFY(cursor.charFormat().isImageFormat()); + cursor.insertText("Hey"); + QVERIFY(cursor.charFormat().isValid()); + QVERIFY(!cursor.charFormat().isImageFormat()); +} + +void tst_QTextCursor::clearObjectType2() +{ + cursor.insertImage("test.png"); + QVERIFY(cursor.charFormat().isValid()); + QVERIFY(cursor.charFormat().isImageFormat()); + cursor.insertBlock(); + QVERIFY(cursor.charFormat().isValid()); + QVERIFY(!cursor.charFormat().isImageFormat()); +} + +void tst_QTextCursor::clearObjectType3() +{ + // like clearObjectType2 but tests different insertBlock overload + cursor.insertImage("test.png"); + QVERIFY(cursor.charFormat().isValid()); + QVERIFY(cursor.charFormat().isImageFormat()); + QTextBlockFormat bfmt; + bfmt.setAlignment(Qt::AlignRight); + cursor.insertBlock(bfmt); + QVERIFY(cursor.charFormat().isValid()); + QVERIFY(!cursor.charFormat().isImageFormat()); +} + +void tst_QTextCursor::comparisonOperators1() +{ + cursor.insertText("Hello World"); + + cursor.movePosition(QTextCursor::PreviousWord); + + QTextCursor startCursor = cursor; + startCursor.movePosition(QTextCursor::Start); + + QVERIFY(startCursor < cursor); + + QTextCursor midCursor = startCursor; + midCursor.movePosition(QTextCursor::NextWord); + + QVERIFY(midCursor <= cursor); + QVERIFY(midCursor == cursor); + QVERIFY(midCursor >= cursor); + + QVERIFY(midCursor > startCursor); + + QVERIFY(midCursor != startCursor); + QVERIFY(!(midCursor == startCursor)); + + QTextCursor nullCursor; + + QVERIFY(!(startCursor < nullCursor)); + QVERIFY(!(nullCursor < nullCursor)); + QVERIFY(nullCursor < startCursor); + + QVERIFY(nullCursor <= startCursor); + QVERIFY(!(startCursor <= nullCursor)); + + QVERIFY(!(nullCursor >= startCursor)); + QVERIFY(startCursor >= nullCursor); + + QVERIFY(!(nullCursor > startCursor)); + QVERIFY(!(nullCursor > nullCursor)); + QVERIFY(startCursor > nullCursor); +} + +void tst_QTextCursor::comparisonOperators2() +{ + QTextDocument doc1; + QTextDocument doc2; + + QTextCursor cursor1(&doc1); + QTextCursor cursor2(&doc2); + + QVERIFY(cursor1 != cursor2); + QVERIFY(cursor1 == QTextCursor(&doc1)); +} + +void tst_QTextCursor::selection1() +{ + cursor.insertText("Hello World"); + + cursor.setPosition(0); + cursor.clearSelection(); + cursor.setPosition(4, QTextCursor::KeepAnchor); + + QCOMPARE(cursor.selectionStart(), 0); + QCOMPARE(cursor.selectionEnd(), 4); +} + +void tst_QTextCursor::dontCopyTableAttributes() +{ + /* when pressing 'enter' inside a cell it shouldn't + * enlarge the table by adding another cell but just + * extend the cell */ + QTextTable *table = cursor.insertTable(2, 2); + QVERIFY(cursor == table->cellAt(0, 0).firstCursorPosition()); + cursor.insertBlock(); + QCOMPARE(table->columns(), 2); +} + +void tst_QTextCursor::checkFrame1() +{ + QVERIFY(cursor.position() == 0); + QPointer frame = cursor.insertFrame(QTextFrameFormat()); + QVERIFY(frame != 0); + + QTextFrame *root = frame->parentFrame(); + QVERIFY(root != 0); + + QVERIFY(frame->firstPosition() == 1); + QVERIFY(frame->lastPosition() == 1); + QVERIFY(frame->parentFrame() != 0); + QVERIFY(root->childFrames().size() == 1); + + QVERIFY(cursor.position() == 1); + QVERIFY(cursor.selectionStart() == 1); + QVERIFY(cursor.selectionEnd() == 1); + + doc->undo(); + + QVERIFY(!frame); + QVERIFY(root->childFrames().size() == 0); + + QVERIFY(cursor.position() == 0); + QVERIFY(cursor.selectionStart() == 0); + QVERIFY(cursor.selectionEnd() == 0); + + doc->redo(); + + frame = doc->frameAt(1); + + QVERIFY(frame); + QVERIFY(frame->firstPosition() == 1); + QVERIFY(frame->lastPosition() == 1); + QVERIFY(frame->parentFrame() != 0); + QVERIFY(root->childFrames().size() == 1); + + QVERIFY(cursor.position() == 1); + QVERIFY(cursor.selectionStart() == 1); + QVERIFY(cursor.selectionEnd() == 1); + +// cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); +// QVERIFY(cursor.position() == 2); +// QVERIFY(cursor.selectionStart() == 0); +// QVERIFY(cursor.selectionEnd() == 2); +} + +void tst_QTextCursor::checkFrame2() +{ + QVERIFY(cursor.position() == 0); + cursor.insertText("A"); + QVERIFY(cursor.position() == 1); + cursor.movePosition(QTextCursor::Start, QTextCursor::KeepAnchor); + + QPointer frame = cursor.insertFrame(QTextFrameFormat()); + QTextFrame *root = frame->parentFrame(); + + QVERIFY(frame->firstPosition() == 1); + QVERIFY(frame->lastPosition() == 2); + QVERIFY(frame->parentFrame() != 0); + QVERIFY(root->childFrames().size() == 1); + + QVERIFY(cursor.position() == 1); + QVERIFY(cursor.selectionStart() == 1); + QVERIFY(cursor.selectionEnd() == 2); + + doc->undo(); + + QVERIFY(!frame); + QVERIFY(root->childFrames().size() == 0); + + QVERIFY(cursor.position() == 0); + QVERIFY(cursor.selectionStart() == 0); + QVERIFY(cursor.selectionEnd() == 1); + + doc->redo(); + + frame = doc->frameAt(1); + + QVERIFY(frame); + QVERIFY(frame->firstPosition() == 1); + QVERIFY(frame->lastPosition() == 2); + QVERIFY(frame->parentFrame() != 0); + QVERIFY(root->childFrames().size() == 1); + + QVERIFY(cursor.position() == 1); + QVERIFY(cursor.selectionStart() == 1); + QVERIFY(cursor.selectionEnd() == 2); + + cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor); + QVERIFY(cursor.position() == 0); + QVERIFY(cursor.selectionStart() == 0); + QVERIFY(cursor.selectionEnd() == 3); +} + +void tst_QTextCursor::insertBlockToUseCharFormat() +{ + QTextCharFormat fmt; + fmt.setForeground(Qt::blue); + cursor.insertText("Hello", fmt); + QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::blue)); + + cursor.insertBlock(); + QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::blue)); + + fmt.setForeground(Qt::red); + cursor.insertText("Hello\nWorld", fmt); + cursor.insertText("Blah"); + QCOMPARE(cursor.charFormat().foreground().color(), QColor(Qt::red)); + + // ### we might want a testcase for createTable, too, as it calls insertBlock, too, + // and we might want to have the char format copied (the one that gets inserted + // as table separators, that are undeletable) +} + +void tst_QTextCursor::tableMovement() +{ + QVERIFY(cursor.position() == 0); + cursor.insertText("AA"); + QVERIFY(cursor.position() == 2); + cursor.movePosition(QTextCursor::Left); + + cursor.insertTable(3, 3); + QCOMPARE(cursor.position(), 2); + + cursor.movePosition(QTextCursor::Down); + QCOMPARE(cursor.position(), 5); + + cursor.movePosition(QTextCursor::Right); + QCOMPARE(cursor.position(), 6); + + cursor.movePosition(QTextCursor::Up); + QCOMPARE(cursor.position(), 3); + + cursor.movePosition(QTextCursor::Right); + QCOMPARE(cursor.position(), 4); + + cursor.movePosition(QTextCursor::Right); + QCOMPARE(cursor.position(), 5); + + cursor.movePosition(QTextCursor::Up); + QCOMPARE(cursor.position(), 2); + + cursor.movePosition(QTextCursor::Up); + QCOMPARE(cursor.position(), 0); + +} + +void tst_QTextCursor::selectionsInTable() +{ + QTextTable *table = cursor.insertTable(2, 2); + table->cellAt(0, 0).firstCursorPosition().insertText("First"); + table->cellAt(0, 1).firstCursorPosition().insertText("Second"); + table->cellAt(1, 0).firstCursorPosition().insertText("Third"); + table->cellAt(1, 1).firstCursorPosition().insertText("Fourth"); + + cursor = table->cellAt(0, 0).lastCursorPosition(); + QVERIFY(cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor)); + QVERIFY(cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor) == false); + + cursor = table->cellAt(1, 0).lastCursorPosition(); + QVERIFY(cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor)); + QVERIFY(cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor) == false); + + cursor = table->cellAt(0, 1).firstCursorPosition(); + QVERIFY(cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor)); + QVERIFY(cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor) == false); + + cursor = table->cellAt(1, 1).firstCursorPosition(); + QVERIFY(cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor)); + QVERIFY(cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor) == false); +} + +void tst_QTextCursor::selectedText() +{ + cursor.insertText("Hello World"); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); + + QCOMPARE(cursor.selectedText(), QString("Hello World")); +} + +void tst_QTextCursor::insertBlockShouldRemoveSelection() +{ + cursor.insertText("Hello World"); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + + QVERIFY(cursor.hasSelection()); + QCOMPARE(cursor.selectedText(), QString("Hello")); + + cursor.insertBlock(); + + QVERIFY(!cursor.hasSelection()); + QVERIFY(doc->toPlainText().indexOf("Hello") == -1); +} + +void tst_QTextCursor::insertBlockShouldRemoveSelection2() +{ + cursor.insertText("Hello World"); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + + QVERIFY(cursor.hasSelection()); + QCOMPARE(cursor.selectedText(), QString("Hello")); + + QTextBlockFormat fmt = cursor.blockFormat(); + cursor.insertBlock(fmt); + + QVERIFY(!cursor.hasSelection()); + QVERIFY(doc->toPlainText().indexOf("Hello") == -1); +} + +void tst_QTextCursor::mergeCellShouldUpdateSelection() +{ + QTextTable *table = cursor.insertTable(4, 4); + cursor.setPosition(table->cellAt(0, 0).firstPosition()); + cursor.setPosition(table->cellAt(3, 0).firstPosition(), QTextCursor::KeepAnchor); // aka bottom left + int firstRow, numRows, firstColumn, numColumns; + cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns); + QCOMPARE(firstRow, 0); + QCOMPARE(numRows, 4); + QCOMPARE(firstColumn, 0); + QCOMPARE(numColumns, 1); + + table->removeColumns(firstColumn, numColumns); + + QCOMPARE(cursor.anchor(), table->cellAt(0, 0).firstPosition()); + QCOMPARE(cursor.position(), table->cellAt(0, 0).firstPosition()); + QCOMPARE(cursor.position(), cursor.anchor()); // empty. I don't really care where it ends up. + + // prepare for another test with multiple cursors. + // note we have a 4 rows, 3 cols table now. + cursor.setPosition(table->cellAt(0, 0).firstPosition()); + cursor.setPosition(table->cellAt(0, 2).firstPosition(), QTextCursor::KeepAnchor); + + // now create a selection of a whole row. + QTextCursor c2 = table->cellAt(2, 0).firstCursorPosition(); + c2.setPosition(table->cellAt(2, 2).firstPosition(), QTextCursor::KeepAnchor); + + // just for good measure, another one for a block of cells. + QTextCursor c3 = table->cellAt(2, 1).firstCursorPosition(); + c3.setPosition(table->cellAt(3, 2).firstPosition(), QTextCursor::KeepAnchor); + + table->removeRows(2, 1); + + QCOMPARE(cursor.anchor(), table->cellAt(0, 0).firstPosition()); + QCOMPARE(cursor.position(), table->cellAt(0, 2).firstPosition()); + + QCOMPARE(c2.position(), c2.anchor()); // empty. I don't really care where it ends up. + + QCOMPARE(c3.anchor(), table->cellAt(2, 1).firstPosition()); + QCOMPARE(c3.position(), table->cellAt(2, 2).firstPosition()); + + + // prepare for another test where we remove a column + // note we have a 3 rows, 3 cols table now. + cursor.setPosition(table->cellAt(0, 0).firstPosition()); + cursor.setPosition(table->cellAt(2, 1).firstPosition(), QTextCursor::KeepAnchor); + + c2.setPosition(table->cellAt(0, 1).firstPosition()); + c2.setPosition(table->cellAt(2, 2).firstPosition(), QTextCursor::KeepAnchor); + + table->removeColumns(1, 1); + + QCOMPARE(cursor.anchor(), table->cellAt(0, 0).firstPosition()); + QCOMPARE(cursor.position(), table->cellAt(2, 0).firstPosition()); + + QCOMPARE(c2.anchor(), table->cellAt(0, 1).firstPosition()); + QCOMPARE(c2.position(), table->cellAt(2, 1).firstPosition()); + + // test for illegal cursor positions. + // note we have a 3 rows, 2 cols table now. + cursor.setPosition(table->cellAt(2, 0).firstPosition()); + cursor.setPosition(table->cellAt(2, 1).firstPosition(), QTextCursor::KeepAnchor); + + c2.setPosition(table->cellAt(0, 0).firstPosition()); + c2.setPosition(table->cellAt(2, 1).firstPosition(), QTextCursor::KeepAnchor); + + c3.setPosition(table->cellAt(2, 1).firstPosition()); + + table->removeRows(2, 1); + + QCOMPARE(cursor.anchor(), table->cellAt(1, 1).lastPosition()+1); + QCOMPARE(cursor.position(), cursor.anchor()); + + QCOMPARE(c2.anchor(), table->cellAt(0, 0).firstPosition()); + QCOMPARE(c2.position(), table->cellAt(1, 1).firstPosition()); + + QCOMPARE(c3.anchor(), table->cellAt(1, 1).firstPosition()); + QCOMPARE(c3.position(), table->cellAt(1, 1).firstPosition()); +} + +void tst_QTextCursor::joinPreviousEditBlock() +{ + cursor.beginEditBlock(); + cursor.insertText("Hello"); + cursor.insertText("World"); + cursor.endEditBlock(); + QVERIFY(doc->toPlainText().startsWith("HelloWorld")); + + cursor.joinPreviousEditBlock(); + cursor.insertText("Hey"); + cursor.endEditBlock(); + QVERIFY(doc->toPlainText().startsWith("HelloWorldHey")); + + doc->undo(); + QVERIFY(!doc->toPlainText().contains("HelloWorldHey")); +} + +void tst_QTextCursor::setBlockFormatInTable() +{ + // someone reported this on qt4-preview-feedback + QTextBlockFormat fmt; + fmt.setBackground(Qt::blue); + cursor.setBlockFormat(fmt); + + QTextTable *table = cursor.insertTable(2, 2); + cursor = table->cellAt(0, 0).firstCursorPosition(); + fmt.setBackground(Qt::red); + cursor.setBlockFormat(fmt); + + cursor.movePosition(QTextCursor::Start); + QVERIFY(cursor.blockFormat().background().color() == Qt::blue); +} + +void tst_QTextCursor::blockCharFormat2() +{ + QTextCharFormat fmt; + fmt.setForeground(Qt::green); + cursor.mergeBlockCharFormat(fmt); + + fmt.setForeground(Qt::red); + + cursor.insertText("Test", fmt); + cursor.movePosition(QTextCursor::Start); + cursor.insertText("Red"); + cursor.movePosition(QTextCursor::PreviousCharacter); + QVERIFY(cursor.charFormat().foreground().color() == Qt::red); +} + +void tst_QTextCursor::blockCharFormat3() +{ + QVERIFY(cursor.atBlockStart()); + QVERIFY(cursor.atBlockEnd()); + QVERIFY(cursor.atStart()); + + QTextCharFormat fmt; + fmt.setForeground(Qt::green); + cursor.setBlockCharFormat(fmt); + cursor.insertText("Test"); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter); + QVERIFY(cursor.charFormat().foreground().color() == Qt::green); + + cursor.movePosition(QTextCursor::Start); + QVERIFY(cursor.charFormat().foreground().color() == Qt::green); + + fmt.setForeground(Qt::red); + cursor.setBlockCharFormat(fmt); + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::red); + + cursor.movePosition(QTextCursor::End); + cursor.movePosition(QTextCursor::Start); + QVERIFY(cursor.charFormat().foreground().color() == Qt::green); + + cursor.insertText("Test"); + QVERIFY(cursor.charFormat().foreground().color() == Qt::green); + + cursor.select(QTextCursor::Document); + cursor.removeSelectedText(); + QVERIFY(cursor.atBlockStart()); + QVERIFY(cursor.atBlockEnd()); + QVERIFY(cursor.atStart()); + + cursor.insertText("Test"); + QVERIFY(cursor.charFormat().foreground().color() == Qt::red); +} + +void tst_QTextCursor::blockCharFormat() +{ + QTextCharFormat fmt; + fmt.setForeground(Qt::blue); + cursor.insertBlock(QTextBlockFormat(), fmt); + cursor.insertText("Hm"); + + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::blue); + + fmt.setForeground(Qt::red); + + cursor.setBlockCharFormat(fmt); + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::red); +} + +void tst_QTextCursor::blockCharFormatOnSelection() +{ + QTextCharFormat fmt; + fmt.setForeground(Qt::blue); + cursor.insertBlock(QTextBlockFormat(), fmt); + + fmt.setForeground(Qt::green); + cursor.insertText("Hm", fmt); + + fmt.setForeground(Qt::red); + cursor.insertBlock(QTextBlockFormat(), fmt); + cursor.insertText("Ah"); + + fmt.setForeground(Qt::white); + cursor.insertBlock(QTextBlockFormat(), fmt); + cursor.insertText("bleh"); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock); + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::blue); + cursor.movePosition(QTextCursor::NextBlock); + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::red); + cursor.movePosition(QTextCursor::NextBlock); + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::white); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock); + cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor); + + fmt.setForeground(Qt::cyan); + cursor.setBlockCharFormat(fmt); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock); + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::cyan); + + cursor.movePosition(QTextCursor::Right); + cursor.movePosition(QTextCursor::Right); + QVERIFY(cursor.charFormat().foreground().color() == Qt::green); + + cursor.movePosition(QTextCursor::NextBlock); + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::cyan); + + cursor.movePosition(QTextCursor::NextBlock); + QVERIFY(cursor.blockCharFormat().foreground().color() == Qt::white); +} + +void tst_QTextCursor::anchorInitialized1() +{ + cursor.insertBlock(); + cursor = QTextCursor(cursor.block()); + QCOMPARE(cursor.position(), 1); + QCOMPARE(cursor.anchor(), 1); + QCOMPARE(cursor.selectionStart(), 1); + QCOMPARE(cursor.selectionEnd(), 1); +} + +void tst_QTextCursor::anchorInitialized2() +{ + cursor.insertBlock(); + cursor = QTextCursor(cursor.block().docHandle(), 1); + QCOMPARE(cursor.position(), 1); + QCOMPARE(cursor.anchor(), 1); + QCOMPARE(cursor.selectionStart(), 1); + QCOMPARE(cursor.selectionEnd(), 1); +} + +void tst_QTextCursor::anchorInitialized3() +{ + QTextFrame *frame = cursor.insertFrame(QTextFrameFormat()); + cursor = QTextCursor(frame); + QCOMPARE(cursor.position(), 1); + QCOMPARE(cursor.anchor(), 1); + QCOMPARE(cursor.selectionStart(), 1); + QCOMPARE(cursor.selectionEnd(), 1); +} + +void tst_QTextCursor::selectWord() +{ + cursor.insertText("first second third"); + cursor.insertBlock(); + cursor.insertText("words in second paragraph"); + + cursor.setPosition(9); + cursor.select(QTextCursor::WordUnderCursor); + QVERIFY(cursor.hasSelection()); + QCOMPARE(cursor.selectionStart(), 6); + QCOMPARE(cursor.selectionEnd(), 12); + + cursor.setPosition(5); + cursor.select(QTextCursor::WordUnderCursor); + QVERIFY(cursor.hasSelection()); + QCOMPARE(cursor.selectionStart(), 0); + QCOMPARE(cursor.selectionEnd(), 5); + + cursor.setPosition(6); + cursor.select(QTextCursor::WordUnderCursor); + QVERIFY(cursor.hasSelection()); + QCOMPARE(cursor.selectionStart(), 6); + QCOMPARE(cursor.selectionEnd(), 12); + + cursor.setPosition(14); + cursor.select(QTextCursor::WordUnderCursor); + QVERIFY(cursor.hasSelection()); + QCOMPARE(cursor.selectionStart(), 6); + QCOMPARE(cursor.selectionEnd(), 12); + + cursor.movePosition(QTextCursor::Start); + cursor.select(QTextCursor::WordUnderCursor); + QVERIFY(cursor.hasSelection()); + QCOMPARE(cursor.selectionStart(), 0); + QCOMPARE(cursor.selectionEnd(), 5); + + cursor.movePosition(QTextCursor::EndOfBlock); + cursor.select(QTextCursor::WordUnderCursor); + QVERIFY(cursor.hasSelection()); + QCOMPARE(cursor.selectionStart(), 17); + QCOMPARE(cursor.selectionEnd(), 22); +} + +void tst_QTextCursor::selectWordWithSeparators_data() +{ + QTest::addColumn("text"); + QTest::addColumn("initialPosition"); + QTest::addColumn("expectedSelectedText"); + + QTest::newRow("dereference") << QString::fromLatin1("foo->bar()") << 1 << QString::fromLatin1("foo"); + QTest::newRow("funcsignature") << QString::fromLatin1("bar(int x);") << 1 << QString::fromLatin1("bar"); + QTest::newRow("def") << QString::fromLatin1("foo *f;") << 1 << QString::fromLatin1("foo"); +} + +void tst_QTextCursor::selectWordWithSeparators() +{ + QFETCH(QString, text); + QFETCH(int, initialPosition); + QFETCH(QString, expectedSelectedText); + + cursor.insertText(text); + cursor.setPosition(initialPosition); + cursor.select(QTextCursor::WordUnderCursor); + + QCOMPARE(cursor.selectedText(), expectedSelectedText); +} + +void tst_QTextCursor::startOfWord() +{ + cursor.insertText("first second"); + cursor.setPosition(7); + cursor.movePosition(QTextCursor::StartOfWord); + QCOMPARE(cursor.position(), 0); +} + +void tst_QTextCursor::selectBlock() +{ + cursor.insertText("foobar"); + QTextBlockFormat blockFmt; + blockFmt.setAlignment(Qt::AlignHCenter); + cursor.insertBlock(blockFmt); + cursor.insertText("blah"); + cursor.insertBlock(QTextBlockFormat()); + + cursor.movePosition(QTextCursor::PreviousBlock); + QCOMPARE(cursor.block().text(), QString("blah")); + + cursor.select(QTextCursor::BlockUnderCursor); + QVERIFY(cursor.hasSelection()); + + QTextDocumentFragment fragment(cursor); + doc->clear(); + cursor.insertFragment(fragment); + QCOMPARE(blockCount(), 2); + + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock); + QVERIFY(cursor.blockFormat().alignment() == Qt::AlignHCenter); + QCOMPARE(cursor.block().text(), QString("blah")); +} + +void tst_QTextCursor::selectVisually() +{ + cursor.insertText("Foo\nlong line which is probably going to be cut in two when shown in a widget\nparagraph 3\n"); + + cursor.setPosition(6); // somewhere in the long paragraph. + cursor.select(QTextCursor::LineUnderCursor); + // since we are not yet laid-out, we expect the whole paragraph to be selected. + QCOMPARE(cursor.position(), 77); + QCOMPARE(cursor.anchor(), 4); +} + +void tst_QTextCursor::insertText() +{ + QString txt = "Foo\nBar\r\nMeep"; + txt += QChar::LineSeparator; + txt += "Baz"; + txt += QChar::ParagraphSeparator; + txt += "yoyodyne"; + cursor.insertText(txt); + QCOMPARE(blockCount(), 4); + cursor.movePosition(QTextCursor::Start); + QCOMPARE(cursor.block().text(), QString("Foo")); + cursor.movePosition(QTextCursor::NextBlock); + QCOMPARE(cursor.block().text(), QString("Bar")); + cursor.movePosition(QTextCursor::NextBlock); + QCOMPARE(cursor.block().text(), QString(QString("Meep") + QChar(QChar::LineSeparator) + QString("Baz"))); + cursor.movePosition(QTextCursor::NextBlock); + QCOMPARE(cursor.block().text(), QString("yoyodyne")); +} + +void tst_QTextCursor::insertFragmentShouldUseCurrentCharFormat() +{ + QTextDocumentFragment fragment = QTextDocumentFragment::fromPlainText("Hello World"); + QTextCharFormat fmt; + fmt.setFontUnderline(true); + + cursor.clearSelection(); + cursor.setCharFormat(fmt); + cursor.insertFragment(fragment); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextCharacter); + QVERIFY(cursor.charFormat() == fmt); +} + +int tst_QTextCursor::blockCount() +{ + int cnt = 0; + for (QTextBlock blk = doc->begin(); blk.isValid(); blk = blk.next()) + ++cnt; + return cnt; +} + +void tst_QTextCursor::endOfLine() +{ + doc->setPageSize(QSizeF(100000, INT_MAX)); + + QString text("First Line \nSecond Line "); + text.replace(QLatin1Char('\n'), QChar(QChar::LineSeparator)); + cursor.insertText(text); + + // ensure layouted + doc->documentLayout()->documentSize(); + + cursor.movePosition(QTextCursor::Start); + + QCOMPARE(cursor.block().layout()->lineCount(), 2); + + cursor.movePosition(QTextCursor::EndOfLine); + QCOMPARE(cursor.position(), 14); + cursor.movePosition(QTextCursor::NextCharacter); + QCOMPARE(cursor.position(), 15); + cursor.movePosition(QTextCursor::EndOfLine); + QCOMPARE(cursor.position(), 28); +} + +class CursorListener : public QObject +{ + Q_OBJECT +public: + CursorListener(QTextCursor *_cursor) : lastRecordedPosition(-1), lastRecordedAnchor(-1), recordingCount(0), cursor(_cursor) {} + + int lastRecordedPosition; + int lastRecordedAnchor; + int recordingCount; + +public slots: + void recordCursorPosition() + { + lastRecordedPosition = cursor->position(); + lastRecordedAnchor = cursor->anchor(); + ++recordingCount; + } + + void selectAllContents() + { + // Only test the first time + if (!recordingCount) { + recordingCount++; + cursor->select(QTextCursor::Document); + lastRecordedPosition = cursor->position(); + lastRecordedAnchor = cursor->anchor(); + } + } + +private: + QTextCursor *cursor; +}; + +void tst_QTextCursor::editBlocksDuringRemove() +{ + CursorListener listener(&cursor); + + cursor.insertText("Hello World"); + cursor.movePosition(QTextCursor::Start, QTextCursor::KeepAnchor); + QCOMPARE(cursor.selectedText(), QString("Hello World")); + + connect(doc, SIGNAL(contentsChanged()), &listener, SLOT(recordCursorPosition())); + listener.recordingCount = 0; + cursor.deleteChar(); + + QCOMPARE(listener.recordingCount, 1); + QCOMPARE(listener.lastRecordedPosition, 0); + QCOMPARE(listener.lastRecordedAnchor, 0); + + QVERIFY(doc->toPlainText().isEmpty()); +} + +void tst_QTextCursor::selectAllDuringRemove() +{ + CursorListener listener(&cursor); + + cursor.insertText("Hello World"); + cursor.movePosition(QTextCursor::End); + + connect(doc, SIGNAL(contentsChanged()), &listener, SLOT(selectAllContents())); + listener.recordingCount = 0; + QTextCursor localCursor = cursor; + localCursor.deletePreviousChar(); + + QCOMPARE(listener.lastRecordedPosition, 10); + QCOMPARE(listener.lastRecordedAnchor, 0); +} + +void tst_QTextCursor::update_data() +{ + QTest::addColumn("text"); + QTest::addColumn("position"); + QTest::addColumn("anchor"); + QTest::addColumn("modifyPosition"); + QTest::addColumn("modifyAnchor"); + QTest::addColumn("insertText"); + QTest::addColumn("expectedPosition"); + QTest::addColumn("expectedAnchor"); + + QString text("Hello big world"); + int charsToDelete = 3; + QTest::newRow("removeInsideSelection") + << text + << /*position*/ 0 + << /*anchor*/ text.length() + // delete 'big' + << 6 + << 6 + charsToDelete + << QString() // don't insert anything, just remove + << /*expectedPosition*/ 0 + << /*expectedAnchor*/ text.length() - charsToDelete + ; + + text = "Hello big world"; + charsToDelete = 3; + QTest::newRow("removeInsideSelectionWithSwappedAnchorAndPosition") + << text + << /*position*/ text.length() + << /*anchor*/ 0 + // delete 'big' + << 6 + << 6 + charsToDelete + << QString() // don't insert anything, just remove + << /*expectedPosition*/ text.length() - charsToDelete + << /*expectedAnchor*/ 0 + ; + + + text = "Hello big world"; + charsToDelete = 3; + QString textToInsert("small"); + QTest::newRow("replaceInsideSelection") + << text + << /*position*/ 0 + << /*anchor*/ text.length() + // delete 'big' ... + << 6 + << 6 + charsToDelete + << textToInsert // ... and replace 'big' with 'small' + << /*expectedPosition*/ 0 + << /*expectedAnchor*/ text.length() - charsToDelete + textToInsert.length() + ; + + text = "Hello big world"; + charsToDelete = 3; + textToInsert = "small"; + QTest::newRow("replaceInsideSelectionWithSwappedAnchorAndPosition") + << text + << /*position*/ text.length() + << /*anchor*/ 0 + // delete 'big' ... + << 6 + << 6 + charsToDelete + << textToInsert // ... and replace 'big' with 'small' + << /*expectedPosition*/ text.length() - charsToDelete + textToInsert.length() + << /*expectedAnchor*/ 0 + ; + + + text = "Hello big world"; + charsToDelete = 3; + QTest::newRow("removeBeforeSelection") + << text + << /*position*/ text.length() - 5 + << /*anchor*/ text.length() + // delete 'big' + << 6 + << 6 + charsToDelete + << QString() // don't insert anything, just remove + << /*expectedPosition*/ text.length() - 5 - charsToDelete + << /*expectedAnchor*/ text.length() - charsToDelete + ; + + text = "Hello big world"; + charsToDelete = 3; + QTest::newRow("removeAfterSelection") + << text + << /*position*/ 0 + << /*anchor*/ 4 + // delete 'big' + << 6 + << 6 + charsToDelete + << QString() // don't insert anything, just remove + << /*expectedPosition*/ 0 + << /*expectedAnchor*/ 4 + ; + +} + +void tst_QTextCursor::update() +{ + QFETCH(QString, text); + + doc->setPlainText(text); + + QFETCH(int, position); + QFETCH(int, anchor); + + cursor.setPosition(anchor); + cursor.setPosition(position, QTextCursor::KeepAnchor); + + QCOMPARE(cursor.position(), position); + QCOMPARE(cursor.anchor(), anchor); + + QFETCH(int, modifyPosition); + QFETCH(int, modifyAnchor); + + QTextCursor modifyCursor = cursor; + modifyCursor.setPosition(modifyAnchor); + modifyCursor.setPosition(modifyPosition, QTextCursor::KeepAnchor); + + QCOMPARE(modifyCursor.position(), modifyPosition); + QCOMPARE(modifyCursor.anchor(), modifyAnchor); + + QFETCH(QString, insertText); + modifyCursor.insertText(insertText); + + QFETCH(int, expectedPosition); + QFETCH(int, expectedAnchor); + + QCOMPARE(cursor.position(), expectedPosition); + QCOMPARE(cursor.anchor(), expectedAnchor); +} + +void tst_QTextCursor::disallowSettingObjectIndicesOnCharFormats() +{ + QTextCharFormat fmt; + fmt.setObjectIndex(42); + cursor.insertText("Hey", fmt); + QCOMPARE(cursor.charFormat().objectIndex(), -1); + + cursor.select(QTextCursor::Document); + cursor.mergeCharFormat(fmt); + QCOMPARE(doc->begin().begin().fragment().charFormat().objectIndex(), -1); + + cursor.select(QTextCursor::Document); + cursor.setCharFormat(fmt); + QCOMPARE(doc->begin().begin().fragment().charFormat().objectIndex(), -1); + + cursor.setBlockCharFormat(fmt); + QCOMPARE(cursor.blockCharFormat().objectIndex(), -1); + + cursor.movePosition(QTextCursor::End); + cursor.insertBlock(QTextBlockFormat(), fmt); + QCOMPARE(cursor.blockCharFormat().objectIndex(), -1); + + doc->clear(); + + QTextTable *table = cursor.insertTable(1, 1); + cursor.select(QTextCursor::Document); + cursor.setCharFormat(fmt); + + cursor = table->cellAt(0, 0).firstCursorPosition(); + QVERIFY(!cursor.isNull()); + QCOMPARE(cursor.blockCharFormat().objectIndex(), table->objectIndex()); +} + +void tst_QTextCursor::blockAndColumnNumber() +{ + QCOMPARE(QTextCursor().columnNumber(), 0); + QCOMPARE(QTextCursor().blockNumber(), 0); + + QCOMPARE(cursor.columnNumber(), 0); + QCOMPARE(cursor.blockNumber(), 0); + cursor.insertText("Hello"); + QCOMPARE(cursor.columnNumber(), 5); + QCOMPARE(cursor.blockNumber(), 0); + + cursor.insertBlock(); + QCOMPARE(cursor.columnNumber(), 0); + QCOMPARE(cursor.blockNumber(), 1); + cursor.insertText("Blah"); + QCOMPARE(cursor.blockNumber(), 1); + + // trigger a layout + doc->documentLayout(); + + cursor.insertBlock(); + QCOMPARE(cursor.columnNumber(), 0); + QCOMPARE(cursor.blockNumber(), 2); + cursor.insertText("Test"); + QCOMPARE(cursor.columnNumber(), 4); + QCOMPARE(cursor.blockNumber(), 2); + cursor.insertText(QString(QChar(QChar::LineSeparator))); + QCOMPARE(cursor.columnNumber(), 0); + QCOMPARE(cursor.blockNumber(), 2); + cursor.insertText("A"); + QCOMPARE(cursor.columnNumber(), 1); + QCOMPARE(cursor.blockNumber(), 2); +} + +void tst_QTextCursor::movePositionEndOfLine() +{ + cursor.insertText("blah\nblah\n"); + // Select part of the second line ("la") + cursor.setPosition(6); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2); + QCOMPARE(cursor.selectedText(), QLatin1String("la")); + + // trigger a layout + doc->documentLayout(); + + // Remove "la" and append "something" to the end in one undo operation + cursor.beginEditBlock(); + cursor.removeSelectedText(); + QTextCursor c2(doc); + c2.setPosition(7); + c2.insertText("foo"); // append to doc without touching the cursor. + + QCOMPARE(cursor.position(), 6); + cursor.movePosition(QTextCursor::EndOfLine); // in an edit block visual movement is moved to the end of the paragraph + QCOMPARE(cursor.position(), 10); + cursor.endEditBlock(); +} + +void tst_QTextCursor::clearCells() +{ + QTextTable *table = cursor.insertTable(3, 5); + cursor.setPosition(table->cellAt(0,0).firstPosition()); // select cell 1 and cell 2 + cursor.setPosition(table->cellAt(0,1).firstPosition(), QTextCursor::KeepAnchor); + cursor.deleteChar(); // should clear the cells, and not crash ;) +} + +void tst_QTextCursor::task244408_wordUnderCursor_data() +{ + QTest::addColumn("input"); + QTest::addColumn("expected"); + QTest::newRow("trailingSpace") << QString::fromLatin1("foo ") << QString::fromLatin1(""); + QTest::newRow("noTrailingSpace") << QString::fromLatin1("foo") << QString::fromLatin1("foo"); +} + +void tst_QTextCursor::task244408_wordUnderCursor() +{ + QFETCH(QString, input); + QFETCH(QString, expected); + cursor.insertText(input); + cursor.movePosition(QTextCursor::End); + cursor.select(QTextCursor::WordUnderCursor); + QCOMPARE(cursor.selectedText(), expected); +} + +void tst_QTextCursor::adjustCursorsOnInsert() +{ + cursor.insertText("Some text before "); + int posBefore = cursor.position(); + cursor.insertText("selected text"); + int posAfter = cursor.position(); + cursor.insertText(" some text afterwards"); + + QTextCursor selection = cursor; + selection.setPosition(posBefore); + selection.setPosition(posAfter, QTextCursor::KeepAnchor); + + cursor.setPosition(posBefore-1); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.anchor(), posBefore+1); + QCOMPARE(selection.position(), posAfter+1); + doc->undo(); + + cursor.setPosition(posBefore); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.anchor(), posBefore+1); + QCOMPARE(selection.position(), posAfter+1); + doc->undo(); + + cursor.setPosition(posBefore+1); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.anchor(), posBefore); + QCOMPARE(selection.position(), posAfter+1); + doc->undo(); + + cursor.setPosition(posAfter-1); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.anchor(), posBefore); + QCOMPARE(selection.position(), posAfter+1); + doc->undo(); + + selection.setKeepPositionOnInsert(true); + cursor.setPosition(posAfter); + cursor.insertText(QLatin1String("x")); + selection.setKeepPositionOnInsert(false); + QCOMPARE(selection.anchor(), posBefore); + QCOMPARE(selection.position(), posAfter); + doc->undo(); + + cursor.setPosition(posAfter+1); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.anchor(), posBefore); + QCOMPARE(selection.position(), posAfter); + doc->undo(); + + selection.setPosition(posAfter); + selection.setPosition(posBefore, QTextCursor::KeepAnchor); + + cursor.setPosition(posBefore-1); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.position(), posBefore+1); + QCOMPARE(selection.anchor(), posAfter+1); + doc->undo(); + + cursor.setPosition(posBefore); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.position(), posBefore+1); + QCOMPARE(selection.anchor(), posAfter+1); + doc->undo(); + + cursor.setPosition(posBefore+1); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.position(), posBefore); + QCOMPARE(selection.anchor(), posAfter+1); + doc->undo(); + + cursor.setPosition(posAfter-1); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.position(), posBefore); + QCOMPARE(selection.anchor(), posAfter+1); + doc->undo(); + + cursor.setPosition(posAfter); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.position(), posBefore); + QCOMPARE(selection.anchor(), posAfter+1); + doc->undo(); + + cursor.setPosition(posAfter+1); + cursor.insertText(QLatin1String("x")); + QCOMPARE(selection.position(), posBefore); + QCOMPARE(selection.anchor(), posAfter); + doc->undo(); + +} +void tst_QTextCursor::cursorPositionWithBlockUndoAndRedo() +{ + cursor.insertText("AAAABBBBCCCCDDDD"); + cursor.setPosition(12); + int cursorPositionBefore = cursor.position(); + cursor.beginEditBlock(); + cursor.insertText("*"); + cursor.setPosition(8); + cursor.insertText("*"); + cursor.setPosition(4); + cursor.insertText("*"); + cursor.setPosition(0); + cursor.insertText("*"); + int cursorPositionAfter = cursor.position(); + cursor.endEditBlock(); + + QVERIFY(doc->toPlainText() == "*AAAA*BBBB*CCCC*DDDD"); + QCOMPARE(12, cursorPositionBefore); + QCOMPARE(1, cursorPositionAfter); + + doc->undo(&cursor); + QVERIFY(doc->toPlainText() == "AAAABBBBCCCCDDDD"); + QCOMPARE(cursor.position(), cursorPositionBefore); + doc->redo(&cursor); + QVERIFY(doc->toPlainText() == "*AAAA*BBBB*CCCC*DDDD"); + QCOMPARE(cursor.position(), cursorPositionAfter); +} + +void tst_QTextCursor::cursorPositionWithBlockUndoAndRedo2() +{ + cursor.insertText("AAAABBBB"); + int cursorPositionBefore = cursor.position(); + cursor.setPosition(0, QTextCursor::KeepAnchor); + cursor.beginEditBlock(); + cursor.removeSelectedText(); + cursor.insertText("AAAABBBBCCCCDDDD"); + cursor.endEditBlock(); + doc->undo(&cursor); + QVERIFY(doc->toPlainText() == "AAAABBBB"); + QCOMPARE(cursor.position(), cursorPositionBefore); + + cursor.insertText("CCCC"); + QVERIFY(doc->toPlainText() == "AAAABBBBCCCC"); + + cursorPositionBefore = cursor.position(); + cursor.setPosition(0, QTextCursor::KeepAnchor); + cursor.beginEditBlock(); + cursor.removeSelectedText(); + cursor.insertText("AAAABBBBCCCCDDDD"); + cursor.endEditBlock(); + + /* this undo now implicitely reinserts two segments, first "CCCCC", then + "AAAABBBB". The test ensures that the two are combined in order to + reconstruct the correct cursor position */ + doc->undo(&cursor); + + + QVERIFY(doc->toPlainText() == "AAAABBBBCCCC"); + QCOMPARE(cursor.position(), cursorPositionBefore); +} + +void tst_QTextCursor::cursorPositionWithBlockUndoAndRedo3() +{ + // verify that it's the position of the beginEditBlock that counts, and not the last edit position + cursor.insertText("AAAABBBB"); + int cursorPositionBefore = cursor.position(); + cursor.beginEditBlock(); + cursor.setPosition(4); + QVERIFY(cursor.position() != cursorPositionBefore); + cursor.insertText("*"); + cursor.endEditBlock(); + QCOMPARE(cursor.position(), 5); + doc->undo(&cursor); + QCOMPARE(cursor.position(), cursorPositionBefore); +} + +void tst_QTextCursor::joinNonEmptyRemovedBlockUserState() +{ + cursor.insertText("Hello"); + cursor.insertBlock(); + cursor.insertText("World"); + cursor.block().setUserState(10); + + cursor.movePosition(QTextCursor::EndOfBlock); + cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::KeepAnchor); + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + + QCOMPARE(cursor.block().userState(), 10); +} + +QTEST_MAIN(tst_QTextCursor) + +#include "moc_tst_qtextcursor.cpp" -- 2.11.0