From 3f73995a03c4b40e2648cb9af5d3e7ca393bf597 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 10 Mar 2020 15:40:56 +0100 Subject: Move undo framework out of Qt Widgets - Moves QUndo* classes (except QUndoView) from src/widgets/utils to src/gui/utils - Moves related auto tests from widgets to gui - Replaces QUndoAction with lambdas that do text prefixing [ChangeLog][Undo Framework] QUndo* classes (except QUndoView) were moved from Qt Widgets to Qt GUI. Done-with: volker.hilsheimer@qt.io Fixes: QTBUG-40040 Change-Id: I3bd8d4d32c64f8dee548f62159a1df2126da89d8 Reviewed-by: Lars Knoll Reviewed-by: Volker Hilsheimer --- tests/auto/gui/util/CMakeLists.txt | 2 + tests/auto/gui/util/qundogroup/.gitignore | 1 + tests/auto/gui/util/qundogroup/CMakeLists.txt | 12 + tests/auto/gui/util/qundogroup/qundogroup.pro | 4 + .../gui/util/qundogroup/testdata/qundogroup.ts | 25 + tests/auto/gui/util/qundogroup/tst_qundogroup.cpp | 641 ++++ tests/auto/gui/util/qundostack/.gitignore | 1 + tests/auto/gui/util/qundostack/CMakeLists.txt | 12 + tests/auto/gui/util/qundostack/qundostack.pro | 4 + .../gui/util/qundostack/testdata/qundostack.ts | 25 + tests/auto/gui/util/qundostack/tst_qundostack.cpp | 3930 ++++++++++++++++++++ tests/auto/gui/util/util.pro | 4 +- 12 files changed, 4660 insertions(+), 1 deletion(-) create mode 100644 tests/auto/gui/util/qundogroup/.gitignore create mode 100644 tests/auto/gui/util/qundogroup/CMakeLists.txt create mode 100644 tests/auto/gui/util/qundogroup/qundogroup.pro create mode 100644 tests/auto/gui/util/qundogroup/testdata/qundogroup.ts create mode 100644 tests/auto/gui/util/qundogroup/tst_qundogroup.cpp create mode 100644 tests/auto/gui/util/qundostack/.gitignore create mode 100644 tests/auto/gui/util/qundostack/CMakeLists.txt create mode 100644 tests/auto/gui/util/qundostack/qundostack.pro create mode 100644 tests/auto/gui/util/qundostack/testdata/qundostack.ts create mode 100644 tests/auto/gui/util/qundostack/tst_qundostack.cpp (limited to 'tests/auto/gui/util') diff --git a/tests/auto/gui/util/CMakeLists.txt b/tests/auto/gui/util/CMakeLists.txt index 1c4a53265d..4feae26818 100644 --- a/tests/auto/gui/util/CMakeLists.txt +++ b/tests/auto/gui/util/CMakeLists.txt @@ -10,3 +10,5 @@ add_subdirectory(qshadergraphloader) add_subdirectory(qshadernodes) add_subdirectory(qshadernodesloader) add_subdirectory(qtexturefilereader) +add_subdirectory(qundogroup) +add_subdirectory(qundostack) diff --git a/tests/auto/gui/util/qundogroup/.gitignore b/tests/auto/gui/util/qundogroup/.gitignore new file mode 100644 index 0000000000..bc3e8c4691 --- /dev/null +++ b/tests/auto/gui/util/qundogroup/.gitignore @@ -0,0 +1 @@ +tst_qundogroup diff --git a/tests/auto/gui/util/qundogroup/CMakeLists.txt b/tests/auto/gui/util/qundogroup/CMakeLists.txt new file mode 100644 index 0000000000..3325556a3e --- /dev/null +++ b/tests/auto/gui/util/qundogroup/CMakeLists.txt @@ -0,0 +1,12 @@ +# Generated from qundogroup.pro. + +##################################################################### +## tst_qundogroup Test: +##################################################################### + +add_qt_test(tst_qundogroup + SOURCES + tst_qundogroup.cpp + PUBLIC_LIBRARIES + Qt::Gui +) diff --git a/tests/auto/gui/util/qundogroup/qundogroup.pro b/tests/auto/gui/util/qundogroup/qundogroup.pro new file mode 100644 index 0000000000..fbae557207 --- /dev/null +++ b/tests/auto/gui/util/qundogroup/qundogroup.pro @@ -0,0 +1,4 @@ +CONFIG += testcase +QT += testlib +SOURCES += tst_qundogroup.cpp +TARGET = tst_qundogroup diff --git a/tests/auto/gui/util/qundogroup/testdata/qundogroup.ts b/tests/auto/gui/util/qundogroup/testdata/qundogroup.ts new file mode 100644 index 0000000000..a059bcb486 --- /dev/null +++ b/tests/auto/gui/util/qundogroup/testdata/qundogroup.ts @@ -0,0 +1,25 @@ + + + + + QUndoGroup + + Undo %1 + undo-prefix %1 undo-suffix + + + Undo + Default text for undo action + Undo-default-text + + + Redo %1 + redo-prefix %1 redo-suffix + + + Redo + Default text for redo action + Redo-default-text + + + diff --git a/tests/auto/gui/util/qundogroup/tst_qundogroup.cpp b/tests/auto/gui/util/qundogroup/tst_qundogroup.cpp new file mode 100644 index 0000000000..ba238105ec --- /dev/null +++ b/tests/auto/gui/util/qundogroup/tst_qundogroup.cpp @@ -0,0 +1,641 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include + +/****************************************************************************** +** Commands +*/ + +class InsertCommand : public QUndoCommand +{ +public: + InsertCommand(QString *str, int idx, const QString &text, + QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + +private: + QString *m_str; + int m_idx; + QString m_text; +}; + +class RemoveCommand : public QUndoCommand +{ +public: + RemoveCommand(QString *str, int idx, int len, QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + +private: + QString *m_str; + int m_idx; + QString m_text; +}; + +class AppendCommand : public QUndoCommand +{ +public: + AppendCommand(QString *str, const QString &text, QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + virtual int id() const; + virtual bool mergeWith(const QUndoCommand *other); + + bool merged; + +private: + QString *m_str; + QString m_text; +}; + +InsertCommand::InsertCommand(QString *str, int idx, const QString &text, + QUndoCommand *parent) + : QUndoCommand(parent) +{ + QVERIFY(str->length() >= idx); + + setText("insert"); + + m_str = str; + m_idx = idx; + m_text = text; +} + +void InsertCommand::redo() +{ + QVERIFY(m_str->length() >= m_idx); + + m_str->insert(m_idx, m_text); +} + +void InsertCommand::undo() +{ + QCOMPARE(m_str->mid(m_idx, m_text.length()), m_text); + + m_str->remove(m_idx, m_text.length()); +} + +RemoveCommand::RemoveCommand(QString *str, int idx, int len, QUndoCommand *parent) + : QUndoCommand(parent) +{ + QVERIFY(str->length() >= idx + len); + + setText("remove"); + + m_str = str; + m_idx = idx; + m_text = m_str->mid(m_idx, len); +} + +void RemoveCommand::redo() +{ + QCOMPARE(m_str->mid(m_idx, m_text.length()), m_text); + + m_str->remove(m_idx, m_text.length()); +} + +void RemoveCommand::undo() +{ + QVERIFY(m_str->length() >= m_idx); + + m_str->insert(m_idx, m_text); +} + +AppendCommand::AppendCommand(QString *str, const QString &text, QUndoCommand *parent) + : QUndoCommand(parent) +{ + setText("append"); + + m_str = str; + m_text = text; + merged = false; +} + +void AppendCommand::redo() +{ + m_str->append(m_text); +} + +void AppendCommand::undo() +{ + QCOMPARE(m_str->mid(m_str->length() - m_text.length()), m_text); + + m_str->truncate(m_str->length() - m_text.length()); +} + +int AppendCommand::id() const +{ + return 1; +} + +bool AppendCommand::mergeWith(const QUndoCommand *other) +{ + if (other->id() != id()) + return false; + m_text += static_cast(other)->m_text; + merged = true; + return true; +} + +/****************************************************************************** +** tst_QUndoStack +*/ + +class tst_QUndoGroup : public QObject +{ + Q_OBJECT +public: + tst_QUndoGroup(); + +private slots: + void setActive(); + void addRemoveStack(); + void deleteStack(); + void checkSignals(); + void addStackAndDie(); + void commandTextFormat(); +}; + +tst_QUndoGroup::tst_QUndoGroup() +{ +} + +void tst_QUndoGroup::setActive() +{ + QUndoGroup group; + QUndoStack stack1(&group), stack2(&group); + + QCOMPARE(group.activeStack(), nullptr); + QCOMPARE(stack1.isActive(), false); + QCOMPARE(stack2.isActive(), false); + + QUndoStack stack3; + QCOMPARE(stack3.isActive(), true); + + group.addStack(&stack3); + QCOMPARE(stack3.isActive(), false); + + stack1.setActive(); + QCOMPARE(group.activeStack(), &stack1); + QCOMPARE(stack1.isActive(), true); + QCOMPARE(stack2.isActive(), false); + QCOMPARE(stack3.isActive(), false); + + group.setActiveStack(&stack2); + QCOMPARE(group.activeStack(), &stack2); + QCOMPARE(stack1.isActive(), false); + QCOMPARE(stack2.isActive(), true); + QCOMPARE(stack3.isActive(), false); + + group.removeStack(&stack2); + QCOMPARE(group.activeStack(), nullptr); + QCOMPARE(stack1.isActive(), false); + QCOMPARE(stack2.isActive(), true); + QCOMPARE(stack3.isActive(), false); + + group.removeStack(&stack2); + QCOMPARE(group.activeStack(), nullptr); + QCOMPARE(stack1.isActive(), false); + QCOMPARE(stack2.isActive(), true); + QCOMPARE(stack3.isActive(), false); +} + +void tst_QUndoGroup::addRemoveStack() +{ + QUndoGroup group; + + QUndoStack stack1(&group); + QCOMPARE(group.stacks(), QList() << &stack1); + + QUndoStack stack2; + group.addStack(&stack2); + QCOMPARE(group.stacks(), QList() << &stack1 << &stack2); + + group.addStack(&stack1); + QCOMPARE(group.stacks(), QList() << &stack1 << &stack2); + + group.removeStack(&stack1); + QCOMPARE(group.stacks(), QList() << &stack2); + + group.removeStack(&stack1); + QCOMPARE(group.stacks(), QList() << &stack2); + + group.removeStack(&stack2); + QCOMPARE(group.stacks(), QList()); +} + +void tst_QUndoGroup::deleteStack() +{ + QUndoGroup group; + + QUndoStack *stack1 = new QUndoStack(&group); + QCOMPARE(group.stacks(), QList() << stack1); + QCOMPARE(group.activeStack(), nullptr); + + stack1->setActive(); + QCOMPARE(group.activeStack(), stack1); + + QUndoStack *stack2 = new QUndoStack(&group); + QCOMPARE(group.stacks(), QList() << stack1 << stack2); + QCOMPARE(group.activeStack(), stack1); + + QUndoStack *stack3 = new QUndoStack(&group); + QCOMPARE(group.stacks(), QList() << stack1 << stack2 << stack3); + QCOMPARE(group.activeStack(), stack1); + + delete stack2; + QCOMPARE(group.stacks(), QList() << stack1 << stack3); + QCOMPARE(group.activeStack(), stack1); + + delete stack1; + QCOMPARE(group.stacks(), QList() << stack3); + QCOMPARE(group.activeStack(), nullptr); + + stack3->setActive(false); + QCOMPARE(group.activeStack(), nullptr); + + stack3->setActive(true); + QCOMPARE(group.activeStack(), stack3); + + group.removeStack(stack3); + QCOMPARE(group.stacks(), QList()); + QCOMPARE(group.activeStack(), nullptr); + + delete stack3; +} + +static QString glue(const QString &s1, const QString &s2) +{ + QString result; + + result.append(s1); + if (!s1.isEmpty() && !s2.isEmpty()) + result.append(' '); + result.append(s2); + + return result; +} + +#define CHECK_STATE(_activeStack, _clean, _canUndo, _undoText, _canRedo, _redoText, \ + _cleanChanged, _indexChanged, _undoChanged, _redoChanged) \ + QCOMPARE(group.activeStack(), (QUndoStack*)_activeStack); \ + QCOMPARE(group.isClean(), _clean); \ + QCOMPARE(group.canUndo(), _canUndo); \ + QCOMPARE(group.undoText(), QString(_undoText)); \ + QCOMPARE(group.canRedo(), _canRedo); \ + QCOMPARE(group.redoText(), QString(_redoText)); \ + if (_indexChanged) { \ + QCOMPARE(indexChangedSpy.count(), 1); \ + indexChangedSpy.clear(); \ + } else { \ + QCOMPARE(indexChangedSpy.count(), 0); \ + } \ + if (_cleanChanged) { \ + QCOMPARE(cleanChangedSpy.count(), 1); \ + QCOMPARE(cleanChangedSpy.at(0).at(0).toBool(), _clean); \ + cleanChangedSpy.clear(); \ + } else { \ + QCOMPARE(cleanChangedSpy.count(), 0); \ + } \ + if (_undoChanged) { \ + QCOMPARE(canUndoChangedSpy.count(), 1); \ + QCOMPARE(canUndoChangedSpy.at(0).at(0).toBool(), _canUndo); \ + QCOMPARE(undo_action->isEnabled(), _canUndo); \ + QCOMPARE(undoTextChangedSpy.count(), 1); \ + QCOMPARE(undoTextChangedSpy.at(0).at(0).toString(), QString(_undoText)); \ + QCOMPARE(undo_action->text(), glue("foo", _undoText)); \ + canUndoChangedSpy.clear(); \ + undoTextChangedSpy.clear(); \ + } else { \ + QCOMPARE(canUndoChangedSpy.count(), 0); \ + QCOMPARE(undoTextChangedSpy.count(), 0); \ + } \ + if (_redoChanged) { \ + QCOMPARE(canRedoChangedSpy.count(), 1); \ + QCOMPARE(canRedoChangedSpy.at(0).at(0).toBool(), _canRedo); \ + QCOMPARE(redo_action->isEnabled(), _canRedo); \ + QCOMPARE(redoTextChangedSpy.count(), 1); \ + QCOMPARE(redoTextChangedSpy.at(0).at(0).toString(), QString(_redoText)); \ + QCOMPARE(redo_action->text(), glue("bar", _redoText)); \ + canRedoChangedSpy.clear(); \ + redoTextChangedSpy.clear(); \ + } else { \ + QCOMPARE(canRedoChangedSpy.count(), 0); \ + QCOMPARE(redoTextChangedSpy.count(), 0); \ + } + +void tst_QUndoGroup::checkSignals() +{ + QUndoGroup group; + QScopedPointer undo_action(group.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redo_action(group.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&group, &QUndoGroup::indexChanged); + QSignalSpy cleanChangedSpy(&group, &QUndoGroup::cleanChanged); + QSignalSpy canUndoChangedSpy(&group, &QUndoGroup::canUndoChanged); + QSignalSpy undoTextChangedSpy(&group, &QUndoGroup::undoTextChanged); + QSignalSpy canRedoChangedSpy(&group, &QUndoGroup::canRedoChanged); + QSignalSpy redoTextChangedSpy(&group, &QUndoGroup::redoTextChanged); + + QString str; + + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + group.undo(); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + group.redo(); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + QUndoStack *stack1 = new QUndoStack(&group); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + stack1->push(new AppendCommand(&str, "foo")); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + stack1->setActive(); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->push(new InsertCommand(&str, 0, "bar")); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->undo(); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "append", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->undo(); + CHECK_STATE(stack1, // activeStack + true, // clean + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->undo(); + CHECK_STATE(stack1, // activeStack + true, // clean + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + group.undo(); + CHECK_STATE(stack1, // activeStack + true, // clean + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + group.redo(); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "append", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->setActive(false); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + QUndoStack *stack2 = new QUndoStack(&group); + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false) // redoChanged + + stack2->setActive(); + CHECK_STATE(stack2, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + stack1->setActive(); + CHECK_STATE(stack1, // activeStack + false, // clean + true, // canUndo + "append", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged + + delete stack1; + CHECK_STATE(0, // activeStack + true, // clean + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true) // redoChanged +} + +void tst_QUndoGroup::addStackAndDie() +{ + // Test that QUndoStack doesn't keep a reference to QUndoGroup after the + // group is deleted. + QUndoStack *stack = new QUndoStack; + QUndoGroup *group = new QUndoGroup; + group->addStack(stack); + delete group; + stack->setActive(true); + delete stack; +} + +void tst_QUndoGroup::commandTextFormat() +{ +#if !QT_CONFIG(process) + QSKIP("No QProcess available"); +#else + QString binDir = QLibraryInfo::location(QLibraryInfo::BinariesPath); + + if (QProcess::execute(binDir + "/lrelease -version") != 0) + QSKIP("lrelease is missing or broken"); + + const QString tsFile = QFINDTESTDATA("testdata/qundogroup.ts"); + QVERIFY(!tsFile.isEmpty()); + QFile::remove("qundogroup.qm"); // Avoid confusion by strays. + QVERIFY(!QProcess::execute(binDir + "/lrelease -silent " + tsFile + " -qm qundogroup.qm")); + + QTranslator translator; + + QVERIFY(translator.load("qundogroup.qm")); + QFile::remove("qundogroup.qm"); + qApp->installTranslator(&translator); + + QUndoGroup group; + QScopedPointer undo_action(group.createUndoAction(nullptr)); + QScopedPointer redo_action(group.createRedoAction(nullptr)); + + QCOMPARE(undo_action->text(), QString("Undo-default-text")); + QCOMPARE(redo_action->text(), QString("Redo-default-text")); + + QUndoStack stack(&group); + stack.setActive(); + QString str; + + stack.push(new AppendCommand(&str, "foo")); + QCOMPARE(undo_action->text(), QString("undo-prefix append undo-suffix")); + QCOMPARE(redo_action->text(), QString("Redo-default-text")); + + stack.push(new InsertCommand(&str, 0, "bar")); + stack.undo(); + QCOMPARE(undo_action->text(), QString("undo-prefix append undo-suffix")); + QCOMPARE(redo_action->text(), QString("redo-prefix insert redo-suffix")); + + stack.undo(); + QCOMPARE(undo_action->text(), QString("Undo-default-text")); + QCOMPARE(redo_action->text(), QString("redo-prefix append redo-suffix")); + + qApp->removeTranslator(&translator); +#endif +} + +QTEST_MAIN(tst_QUndoGroup) + +#include "tst_qundogroup.moc" + diff --git a/tests/auto/gui/util/qundostack/.gitignore b/tests/auto/gui/util/qundostack/.gitignore new file mode 100644 index 0000000000..f8faf2771f --- /dev/null +++ b/tests/auto/gui/util/qundostack/.gitignore @@ -0,0 +1 @@ +tst_qundostack diff --git a/tests/auto/gui/util/qundostack/CMakeLists.txt b/tests/auto/gui/util/qundostack/CMakeLists.txt new file mode 100644 index 0000000000..d6cf36187e --- /dev/null +++ b/tests/auto/gui/util/qundostack/CMakeLists.txt @@ -0,0 +1,12 @@ +# Generated from qundostack.pro. + +##################################################################### +## tst_qundostack Test: +##################################################################### + +add_qt_test(tst_qundostack + SOURCES + tst_qundostack.cpp + PUBLIC_LIBRARIES + Qt::Gui +) diff --git a/tests/auto/gui/util/qundostack/qundostack.pro b/tests/auto/gui/util/qundostack/qundostack.pro new file mode 100644 index 0000000000..bbade9635f --- /dev/null +++ b/tests/auto/gui/util/qundostack/qundostack.pro @@ -0,0 +1,4 @@ +CONFIG += testcase +QT += gui testlib +SOURCES += tst_qundostack.cpp +TARGET = tst_qundostack diff --git a/tests/auto/gui/util/qundostack/testdata/qundostack.ts b/tests/auto/gui/util/qundostack/testdata/qundostack.ts new file mode 100644 index 0000000000..4584036af2 --- /dev/null +++ b/tests/auto/gui/util/qundostack/testdata/qundostack.ts @@ -0,0 +1,25 @@ + + + + + QUndoStack + + Undo %1 + undo-prefix %1 undo-suffix + + + Undo + Default text for undo action + Undo-default-text + + + Redo %1 + redo-prefix %1 redo-suffix + + + Redo + Default text for redo action + Redo-default-text + + + diff --git a/tests/auto/gui/util/qundostack/tst_qundostack.cpp b/tests/auto/gui/util/qundostack/tst_qundostack.cpp new file mode 100644 index 0000000000..a24798cba7 --- /dev/null +++ b/tests/auto/gui/util/qundostack/tst_qundostack.cpp @@ -0,0 +1,3930 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include + +/****************************************************************************** +** Commands +*/ + +class InsertCommand : public QUndoCommand +{ +public: + InsertCommand(QString *str, int idx, const QString &text, + QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + +private: + QString *m_str; + int m_idx; + QString m_text; +}; + +class RemoveCommand : public QUndoCommand +{ +public: + RemoveCommand(QString *str, int idx, int len, QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + +private: + QString *m_str; + int m_idx; + QString m_text; +}; + +class AppendCommand : public QUndoCommand +{ +public: + AppendCommand(QString *str, const QString &text, bool _fail_merge = false, + QUndoCommand *parent = 0); + ~AppendCommand(); + + virtual void undo(); + virtual void redo(); + virtual int id() const; + virtual bool mergeWith(const QUndoCommand *other); + + bool merged; + bool fail_merge; + static int delete_cnt; + +private: + QString *m_str; + QString m_text; +}; + +class IdleCommand : public QUndoCommand +{ +public: + IdleCommand(QUndoCommand *parent = 0); + ~IdleCommand(); + + virtual void undo(); + virtual void redo(); +}; + +class MoveMouseCommand : public QUndoCommand +{ +public: + MoveMouseCommand(QPoint *mouse, QPoint oldPoint, QPoint newPoint, QUndoCommand *parent = 0); + ~MoveMouseCommand(); + + virtual void undo(); + virtual void redo(); + virtual int id() const; + virtual bool mergeWith(const QUndoCommand *other); + +private: + QPoint *m_mouse; + QPoint m_oldPoint; + QPoint m_newPoint; +}; + +InsertCommand::InsertCommand(QString *str, int idx, const QString &text, + QUndoCommand *parent) + : QUndoCommand(parent) +{ + QVERIFY(str->length() >= idx); + + setText("insert"); + + m_str = str; + m_idx = idx; + m_text = text; +} + +void InsertCommand::redo() +{ + QVERIFY(m_str->length() >= m_idx); + + m_str->insert(m_idx, m_text); +} + +void InsertCommand::undo() +{ + QCOMPARE(m_str->mid(m_idx, m_text.length()), m_text); + + m_str->remove(m_idx, m_text.length()); +} + +RemoveCommand::RemoveCommand(QString *str, int idx, int len, QUndoCommand *parent) + : QUndoCommand(parent) +{ + QVERIFY(str->length() >= idx + len); + + setText("remove"); + + m_str = str; + m_idx = idx; + m_text = m_str->mid(m_idx, len); +} + +void RemoveCommand::redo() +{ + QCOMPARE(m_str->mid(m_idx, m_text.length()), m_text); + + m_str->remove(m_idx, m_text.length()); +} + +void RemoveCommand::undo() +{ + QVERIFY(m_str->length() >= m_idx); + + m_str->insert(m_idx, m_text); +} + +int AppendCommand::delete_cnt = 0; + +AppendCommand::AppendCommand(QString *str, const QString &text, bool _fail_merge, + QUndoCommand *parent) + : QUndoCommand(parent) +{ + setText("append"); + + m_str = str; + m_text = text; + merged = false; + fail_merge = _fail_merge; +} + +AppendCommand::~AppendCommand() +{ + ++delete_cnt; +} + +void AppendCommand::redo() +{ + m_str->append(m_text); +} + +void AppendCommand::undo() +{ + QCOMPARE(m_str->mid(m_str->length() - m_text.length()), m_text); + + m_str->truncate(m_str->length() - m_text.length()); +} + +int AppendCommand::id() const +{ + return 1; +} + +bool AppendCommand::mergeWith(const QUndoCommand *other) +{ + if (other->id() != id()) + return false; + if (fail_merge) + return false; + m_text += static_cast(other)->m_text; + merged = true; + return true; +} + +IdleCommand::IdleCommand(QUndoCommand *parent) + : QUndoCommand(parent) +{ + // "idle-item" goes to QUndoStack::{redo,undo}Text + // "idle-action" goes to all other places (e.g. QUndoView) + setText("idle-item\nidle-action"); +} + +IdleCommand::~IdleCommand() +{ +} + +void IdleCommand::redo() +{ +} + +void IdleCommand::undo() +{ +} + +MoveMouseCommand::MoveMouseCommand(QPoint *mouse, QPoint oldPoint, QPoint newPoint, QUndoCommand *parent) + : QUndoCommand(parent) +{ + setText("move mouse"); + + m_mouse = mouse; + m_oldPoint = oldPoint; + m_newPoint = newPoint; + + if (m_oldPoint == m_newPoint) + setObsolete(true); +} + +MoveMouseCommand::~MoveMouseCommand() +{ +} + +void MoveMouseCommand::redo() +{ + *m_mouse = m_newPoint; +} + +void MoveMouseCommand::undo() +{ + *m_mouse = m_oldPoint; +} + +int MoveMouseCommand::id() const +{ + return 2; +} + +bool MoveMouseCommand::mergeWith(const QUndoCommand *other) +{ + m_newPoint = static_cast(other)->m_newPoint; + + if (m_newPoint == m_oldPoint) + setObsolete(true); + + return true; +} + +/****************************************************************************** +** tst_QUndoStack +*/ + +class tst_QUndoStack : public QObject +{ + Q_OBJECT +public: + tst_QUndoStack(); + +private slots: + void undoRedo(); + void setIndex(); + void setClean(); + void clear(); + void childCommand(); + void macroBeginEnd(); + void compression(); + void obsolete(); + void undoLimit(); + void commandTextFormat(); + void separateUndoText(); +}; + +tst_QUndoStack::tst_QUndoStack() +{ +} + +static QString glue(const QString &s1, const QString &s2) +{ + QString result; + + result.append(s1); + if (!s1.isEmpty() && !s2.isEmpty()) + result.append(' '); + result.append(s2); + + return result; +} + +static void checkState(QSignalSpy &redoTextChangedSpy, + QSignalSpy &canRedoChangedSpy, + QSignalSpy &undoTextChangedSpy, + const QScopedPointer &redoAction, + const QScopedPointer &undoAction, + QSignalSpy &canUndoChangedSpy, + QSignalSpy &cleanChangedSpy, + QSignalSpy &indexChangedSpy, + QUndoStack &stack, + const bool _clean, + const int _count, + const int _index, + const bool _canUndo, + const QString &_undoText, + const bool _canRedo, + const QString &_redoText, + const bool _cleanChanged, + const bool _indexChanged, + const bool _undoChanged, + const bool _redoChanged) +{ + QCOMPARE(stack.count(), _count); + QCOMPARE(stack.isClean(), _clean); + QCOMPARE(stack.index(), _index); + QCOMPARE(stack.canUndo(), _canUndo); + QCOMPARE(stack.undoText(), QString(_undoText)); + QCOMPARE(stack.canRedo(), _canRedo); + QCOMPARE(stack.redoText(), QString(_redoText)); + if (_indexChanged) { + QCOMPARE(indexChangedSpy.count(), 1); + QCOMPARE(indexChangedSpy.at(0).at(0).toInt(), _index); + indexChangedSpy.clear(); + } else { + QCOMPARE(indexChangedSpy.count(), 0); + } + if (_cleanChanged) { + QCOMPARE(cleanChangedSpy.count(), 1); + QCOMPARE(cleanChangedSpy.at(0).at(0).toBool(), _clean); + cleanChangedSpy.clear(); + } else { + QCOMPARE(cleanChangedSpy.count(), 0); + } + if (_undoChanged) { + QCOMPARE(canUndoChangedSpy.count(), 1); + QCOMPARE(canUndoChangedSpy.at(0).at(0).toBool(), _canUndo); + QCOMPARE(undoAction->isEnabled(), _canUndo); + QCOMPARE(undoTextChangedSpy.count(), 1); + QCOMPARE(undoTextChangedSpy.at(0).at(0).toString(), QString(_undoText)); + QCOMPARE(undoAction->text(), glue("foo", _undoText)); + canUndoChangedSpy.clear(); + undoTextChangedSpy.clear(); + } else { + QCOMPARE(canUndoChangedSpy.count(), 0); + QCOMPARE(undoTextChangedSpy.count(), 0); + } + if (_redoChanged) { + QCOMPARE(canRedoChangedSpy.count(), 1); + QCOMPARE(canRedoChangedSpy.at(0).at(0).toBool(), _canRedo); + QCOMPARE(redoAction->isEnabled(), _canRedo); + QCOMPARE(redoTextChangedSpy.count(), 1); + QCOMPARE(redoTextChangedSpy.at(0).at(0).toString(), QString(_redoText)); + QCOMPARE(redoAction->text(), glue("bar", _redoText)); + canRedoChangedSpy.clear(); + redoTextChangedSpy.clear(); + } else { + QCOMPARE(canRedoChangedSpy.count(), 0); + QCOMPARE(redoTextChangedSpy.count(), 0); + } +} + +void tst_QUndoStack::undoRedo() +{ + QUndoStack stack; + QScopedPointer undoAction(stack.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redoAction(stack.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&stack, &QUndoStack::indexChanged); + QSignalSpy cleanChangedSpy(&stack, &QUndoStack::cleanChanged); + QSignalSpy canUndoChangedSpy(&stack, &QUndoStack::canUndoChanged); + QSignalSpy undoTextChangedSpy(&stack, &QUndoStack::undoTextChanged); + QSignalSpy canRedoChangedSpy(&stack, &QUndoStack::canRedoChanged); + QSignalSpy redoTextChangedSpy(&stack, &QUndoStack::redoTextChanged); + QString str; + + // push, undo, redo + + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.undo(); // nothing to undo + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 2, "123")); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + + stack.undo(); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.redo(); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.redo(); // nothing to redo + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); // nothing to undo + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + // push after undo - check that undone commands get deleted + + stack.redo(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new RemoveCommand(&str, 2, 2)); + QCOMPARE(str, QString("heo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count - still 2, last command got deleted + 2, // index + true, // canUndo + "remove", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "remove", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 0, "goodbye")); + QCOMPARE(str, QString("goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count - two commands got deleted + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::setIndex() +{ + QUndoStack stack; + QScopedPointer undoAction(stack.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redoAction(stack.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&stack, &QUndoStack::indexChanged); + QSignalSpy cleanChangedSpy(&stack, &QUndoStack::cleanChanged); + QSignalSpy canUndoChangedSpy(&stack, &QUndoStack::canUndoChanged); + QSignalSpy undoTextChangedSpy(&stack, &QUndoStack::undoTextChanged); + QSignalSpy canRedoChangedSpy(&stack, &QUndoStack::canRedoChanged); + QSignalSpy redoTextChangedSpy(&stack, &QUndoStack::redoTextChanged); + QString str; + + stack.setIndex(10); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setIndex(0); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setIndex(-10); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 2, "123")); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(2); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setIndex(0); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(10); // should set index to 2 + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(-10); // should set index to 0 + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(1); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(2); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::setClean() +{ + QUndoStack stack; + QScopedPointer undoAction(stack.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redoAction(stack.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&stack, &QUndoStack::indexChanged); + QSignalSpy cleanChangedSpy(&stack, &QUndoStack::cleanChanged); + QSignalSpy canUndoChangedSpy(&stack, &QUndoStack::canUndoChanged); + QSignalSpy undoTextChangedSpy(&stack, &QUndoStack::undoTextChanged); + QSignalSpy canRedoChangedSpy(&stack, &QUndoStack::canRedoChanged); + QSignalSpy redoTextChangedSpy(&stack, &QUndoStack::redoTextChanged); + QString str; + + QCOMPARE(stack.cleanIndex(), 0); + stack.setClean(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), 0); + + stack.push(new InsertCommand(&str, 0, "goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 0); + + stack.setClean(); + QCOMPARE(str, QString("goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.push(new AppendCommand(&str, " cowboy")); + QCOMPARE(str, QString("goodbye cowboy")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.undo(); // reaching clean state from above + QCOMPARE(str, QString("goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.redo(); // reaching clean state from below + QCOMPARE(str, QString("goodbye")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.push(new InsertCommand(&str, 0, "foo")); // the clean state gets deleted! + QCOMPARE(str, QString("foo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); + + stack.setClean(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), 0); + + stack.resetClean(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); + + stack.redo(); + QCOMPARE(str, QString("foo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); + + stack.setClean(); + QCOMPARE(str, QString("foo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.undo(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), 1); + + stack.resetClean(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); +} + +void tst_QUndoStack::clear() +{ + QUndoStack stack; + QScopedPointer undoAction(stack.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redoAction(stack.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&stack, &QUndoStack::indexChanged); + QSignalSpy cleanChangedSpy(&stack, &QUndoStack::cleanChanged); + QSignalSpy canUndoChangedSpy(&stack, &QUndoStack::canUndoChanged); + QSignalSpy undoTextChangedSpy(&stack, &QUndoStack::undoTextChanged); + QSignalSpy canRedoChangedSpy(&stack, &QUndoStack::canRedoChanged); + QSignalSpy redoTextChangedSpy(&stack, &QUndoStack::redoTextChanged); + QString str; + + stack.clear(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 2, "123")); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.clear(); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + str.clear(); + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 2, "123")); + QCOMPARE(str, QString("he123llo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(0); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.clear(); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::childCommand() +{ + QUndoStack stack; + QScopedPointer undoAction(stack.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redoAction(stack.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&stack, &QUndoStack::indexChanged); + QSignalSpy cleanChangedSpy(&stack, &QUndoStack::cleanChanged); + QSignalSpy canUndoChangedSpy(&stack, &QUndoStack::canUndoChanged); + QSignalSpy undoTextChangedSpy(&stack, &QUndoStack::undoTextChanged); + QSignalSpy canRedoChangedSpy(&stack, &QUndoStack::canRedoChanged); + QSignalSpy redoTextChangedSpy(&stack, &QUndoStack::redoTextChanged); + QString str; + + stack.push(new InsertCommand(&str, 0, "hello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + QUndoCommand *cmd = new QUndoCommand(); + cmd->setText("ding"); + new InsertCommand(&str, 5, "world", cmd); + new RemoveCommand(&str, 4, 1, cmd); + stack.push(cmd); + QCOMPARE(str, QString("hellworld")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "ding", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.redo(); + QCOMPARE(str, QString("hellworld")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::macroBeginEnd() +{ + QUndoStack stack; + QScopedPointer undoAction(stack.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redoAction(stack.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&stack, &QUndoStack::indexChanged); + QSignalSpy cleanChangedSpy(&stack, &QUndoStack::cleanChanged); + QSignalSpy canUndoChangedSpy(&stack, &QUndoStack::canUndoChanged); + QSignalSpy undoTextChangedSpy(&stack, &QUndoStack::undoTextChanged); + QSignalSpy canRedoChangedSpy(&stack, &QUndoStack::canRedoChanged); + QSignalSpy redoTextChangedSpy(&stack, &QUndoStack::redoTextChanged); + QString str; + + stack.beginMacro("ding"); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setClean(); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.undo(); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.redo(); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setIndex(0); // should do nothing + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index - endMacro() increments index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 0, "h")); + QCOMPARE(str, QString("h")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 1, "owdy")); + QCOMPARE(str, QString("howdy")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(2); + QCOMPARE(str, QString("h")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.beginMacro("dong"); // the "owdy" command gets deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 1, "ello")); + QCOMPARE(str, QString("hello")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new RemoveCommand(&str, 1, 2)); + QCOMPARE(str, QString("hlo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.beginMacro("dong2"); + QCOMPARE(str, QString("hlo")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new RemoveCommand(&str, 1, 1)); + QCOMPARE(str, QString("ho")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + QCOMPARE(str, QString("ho")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + QCOMPARE(str, QString("ho")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "dong", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("h")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "dong", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 1, // index + true, // canUndo + "ding", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(3); + QCOMPARE(str, QString("ho")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "dong", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setIndex(1); + QCOMPARE(str, QString()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 1, // index + true, // canUndo + "ding", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::compression() +{ + QUndoStack stack; + QScopedPointer undoAction(stack.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redoAction(stack.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&stack, &QUndoStack::indexChanged); + QSignalSpy cleanChangedSpy(&stack, &QUndoStack::cleanChanged); + QSignalSpy canUndoChangedSpy(&stack, &QUndoStack::canUndoChanged); + QSignalSpy undoTextChangedSpy(&stack, &QUndoStack::undoTextChanged); + QSignalSpy canRedoChangedSpy(&stack, &QUndoStack::canRedoChanged); + QSignalSpy redoTextChangedSpy(&stack, &QUndoStack::redoTextChanged); + QString str; + + AppendCommand::delete_cnt = 0; + + stack.push(new InsertCommand(&str, 0, "ene")); + QCOMPARE(str, QString("ene")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, " due")); // #1 + QCOMPARE(str, QString("ene due")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, " rike")); // #2 should merge + QCOMPARE(str, QString("ene due rike")); + QCOMPARE(AppendCommand::delete_cnt, 1); // #2 should be deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setClean(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new AppendCommand(&str, " fake")); // #3 should NOT merge, since the stack was clean + QCOMPARE(str, QString("ene due rike fake")); // and we want to be able to return to this state + QCOMPARE(AppendCommand::delete_cnt, 1); // #3 should not be deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("ene due rike")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 3, // count + 2, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("ene")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "ma", true)); // #4 clean state gets deleted! + QCOMPARE(str, QString("enema")); + QCOMPARE(AppendCommand::delete_cnt, 3); // #1 got deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "trix")); // #5 should NOT merge + QCOMPARE(str, QString("enematrix")); + QCOMPARE(AppendCommand::delete_cnt, 3); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("enema")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + // and now for command compression inside macros + + stack.setClean(); + QCOMPARE(str, QString("enema")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 3, // count + 2, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.beginMacro("ding"); + QCOMPARE(str, QString("enema")); + QCOMPARE(AppendCommand::delete_cnt, 4); // #5 gets deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + true, // undoChanged + true); // redoChanged + + AppendCommand *merge_cmd = new AppendCommand(&str, "top"); + stack.push(merge_cmd); // #6 + QCOMPARE(merge_cmd->merged, false); + QCOMPARE(str, QString("enematop")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new AppendCommand(&str, "eja")); // #7 should merge + QCOMPARE(str, QString("enematopeja")); + QCOMPARE(merge_cmd->merged, true); + QCOMPARE(AppendCommand::delete_cnt, 5); // #7 gets deleted + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + merge_cmd->merged = false; + + stack.push(new InsertCommand(&str, 2, "123")); // should not merge + QCOMPARE(str, QString("en123ematopeja")); + QCOMPARE(merge_cmd->merged, false); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + QCOMPARE(str, QString("en123ematopeja")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("enema")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 3, // count + 2, // index + true, // canUndo + "append", // undoText + true, // canRedo + "ding", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.redo(); + QCOMPARE(str, QString("en123ematopeja")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::obsolete() +{ + QUndoStack stack; + QScopedPointer undoAction(stack.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redoAction(stack.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&stack, &QUndoStack::indexChanged); + QSignalSpy cleanChangedSpy(&stack, &QUndoStack::cleanChanged); + QSignalSpy canUndoChangedSpy(&stack, &QUndoStack::canUndoChanged); + QSignalSpy undoTextChangedSpy(&stack, &QUndoStack::undoTextChanged); + QSignalSpy canRedoChangedSpy(&stack, &QUndoStack::canRedoChanged); + QSignalSpy redoTextChangedSpy(&stack, &QUndoStack::redoTextChanged); + QPoint mouse(0, 0); + QString str; + MoveMouseCommand *cmd1 = 0; + MoveMouseCommand *cmd2 = 0; + + stack.resetClean(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new MoveMouseCommand(&mouse, mouse, QPoint(0, 0))); // #1 should not merge but will be deleted (b/c oldPoint == newPoint) + QCOMPARE(mouse, QPoint(0, 0)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new MoveMouseCommand(&mouse, mouse, QPoint(12, 0))); // #2 should not merge or be deleted (b/c oldPoint != newPoint) + QCOMPARE(mouse, QPoint(12, 0)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new MoveMouseCommand(&mouse, mouse, QPoint(8, 2))); // #3 should merge and not be deleted (b/c oldPoint != newPoint) + QCOMPARE(mouse, QPoint(8, 2)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new MoveMouseCommand(&mouse, mouse, QPoint(0, 0))); // #4 should merge and be deleted (b/c oldPoint == newPoint) + QCOMPARE(mouse, QPoint(0, 0)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 0, // count + 0, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + + + + stack.push(new InsertCommand(&str, 0, "ene")); // #5 should not merge or be deleted + QCOMPARE(str, QString("ene")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + cmd1 = new MoveMouseCommand(&mouse, mouse, QPoint(6, 5)); + stack.push(cmd1); // #6 should not merge or be deleted (b/c oldPoint != newPoint) + QCOMPARE(mouse, QPoint(6, 5)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 3, "ma")); // #7 should not merge or be deleted + QCOMPARE(str, QString("enema")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + cmd2 = new MoveMouseCommand(&mouse, mouse, QPoint(12, 4)); + stack.push(cmd2); // #8 should not merge or be deleted (b/c oldPoint != newPoint) + QCOMPARE(mouse, QPoint(12, 4)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 4, // count + 4, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setClean(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 4, // count + 4, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), 4); + + cmd2->setObsolete(true); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 4, // count + 4, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.undo(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); + + stack.undo(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + true, // canUndo + "move mouse", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setClean(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 3, // count + 2, // index + true, // canUndo + "move mouse", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), 2); + + stack.undo(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "move mouse", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + cmd1->setObsolete(true); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "move mouse", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.redo(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "insert", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); + + stack.redo(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); + + cmd1 = new MoveMouseCommand(&mouse, mouse, QPoint(13, 2)); + stack.push(cmd1); // #9 should not merge or be deleted (b/c oldPoint != newPoint) + QCOMPARE(mouse, QPoint(13, 2)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new InsertCommand(&str, 3, "ma")); // #10 should not merge or be deleted + QCOMPARE(str, QString("enemama")); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 4, // count + 4, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + cmd2 = new MoveMouseCommand(&mouse, mouse, QPoint(6, 20)); + stack.push(cmd2); // #11 should not merge or be deleted (b/c oldPoint != newPoint) + QCOMPARE(mouse, QPoint(6, 20)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 5, // count + 5, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + cmd1->setObsolete(true); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 5, // count + 5, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setClean(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 5, // count + 5, // index + true, // canUndo + "move mouse", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + QCOMPARE(stack.cleanIndex(), 5); + + stack.setIndex(0); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 4, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + QCOMPARE(stack.cleanIndex(), -1); + + cmd2->setObsolete(true); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 4, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "insert", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.setIndex(stack.count()); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 3, // index + true, // canUndo + "insert", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + mouse = QPoint(0, 0); // Reset mouse position + stack.beginMacro("ding"); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 4, // count + 3, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new MoveMouseCommand(&mouse, mouse, QPoint(7, 7))); // #12 should not merge or be deleted (b/c oldPoint != newPoint & in macro) + QCOMPARE(mouse, QPoint(7, 7)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 4, // count + 3, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new MoveMouseCommand(&mouse, mouse, QPoint(0, 0))); // #13 should merge and be deleted (b/c oldPoint = newPoint) + QCOMPARE(mouse, QPoint(0, 0)); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 4, // count + 3, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 4, // count + 4, // index + true, // canUndo + "ding", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::undoLimit() +{ + QUndoStack stack; + QScopedPointer undoAction(stack.createUndoAction(nullptr, QString("foo"))); + QScopedPointer redoAction(stack.createRedoAction(nullptr, QString("bar"))); + QSignalSpy indexChangedSpy(&stack, &QUndoStack::indexChanged); + QSignalSpy cleanChangedSpy(&stack, &QUndoStack::cleanChanged); + QSignalSpy canUndoChangedSpy(&stack, &QUndoStack::canUndoChanged); + QSignalSpy undoTextChangedSpy(&stack, &QUndoStack::undoTextChanged); + QSignalSpy canRedoChangedSpy(&stack, &QUndoStack::canRedoChanged); + QSignalSpy redoTextChangedSpy(&stack, &QUndoStack::redoTextChanged); + AppendCommand::delete_cnt = 0; + QString str; + + QCOMPARE(stack.undoLimit(), 0); + stack.setUndoLimit(2); + QCOMPARE(stack.undoLimit(), 2); + + stack.push(new AppendCommand(&str, "1", true)); + QCOMPARE(str, QString("1")); + QCOMPARE(AppendCommand::delete_cnt, 0); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "2", true)); + QCOMPARE(str, QString("12")); + QCOMPARE(AppendCommand::delete_cnt, 0); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.setClean(); + QCOMPARE(str, QString("12")); + QCOMPARE(AppendCommand::delete_cnt, 0); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new AppendCommand(&str, "3", true)); + QCOMPARE(str, QString("123")); + QCOMPARE(AppendCommand::delete_cnt, 1); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "4", true)); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 2); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("123")); + QCOMPARE(AppendCommand::delete_cnt, 2); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("12")); + QCOMPARE(AppendCommand::delete_cnt, 2); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + true, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "3", true)); + QCOMPARE(str, QString("123")); + QCOMPARE(AppendCommand::delete_cnt, 4); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + true, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "4", true)); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 4); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "5", true)); + QCOMPARE(str, QString("12345")); + QCOMPARE(AppendCommand::delete_cnt, 5); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 5); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "append", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("123")); + QCOMPARE(AppendCommand::delete_cnt, 5); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "4", true)); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 7); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 1, // count + 1, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "5")); + QCOMPARE(str, QString("12345")); + QCOMPARE(AppendCommand::delete_cnt, 7); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "6", true)); // should be merged + QCOMPARE(str, QString("123456")); + QCOMPARE(AppendCommand::delete_cnt, 8); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "append", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.beginMacro("foo"); + QCOMPARE(str, QString("123456")); + QCOMPARE(AppendCommand::delete_cnt, 8); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.push(new AppendCommand(&str, "7", true)); + QCOMPARE(str, QString("1234567")); + QCOMPARE(AppendCommand::delete_cnt, 8); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.push(new AppendCommand(&str, "8")); + QCOMPARE(str, QString("12345678")); + QCOMPARE(AppendCommand::delete_cnt, 8); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 3, // count + 2, // index + false, // canUndo + "", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + false, // indexChanged + false, // undoChanged + false); // redoChanged + + stack.endMacro(); + QCOMPARE(str, QString("12345678")); + QCOMPARE(AppendCommand::delete_cnt, 9); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 2, // index + true, // canUndo + "foo", // undoText + false, // canRedo + "", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("123456")); + QCOMPARE(AppendCommand::delete_cnt, 9); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 1, // index + true, // canUndo + "append", // undoText + true, // canRedo + "foo", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged + + stack.undo(); + QCOMPARE(str, QString("1234")); + QCOMPARE(AppendCommand::delete_cnt, 9); + checkState(redoTextChangedSpy, + canRedoChangedSpy, + undoTextChangedSpy, + redoAction, + undoAction, + canUndoChangedSpy, + cleanChangedSpy, + indexChangedSpy, + stack, + false, // clean + 2, // count + 0, // index + false, // canUndo + "", // undoText + true, // canRedo + "append", // redoText + false, // cleanChanged + true, // indexChanged + true, // undoChanged + true); // redoChanged +} + +void tst_QUndoStack::commandTextFormat() +{ +#if !QT_CONFIG(process) + QSKIP("No QProcess available"); +#else + QString binDir = QLibraryInfo::location(QLibraryInfo::BinariesPath); + + if (QProcess::execute(binDir + "/lrelease -version") != 0) + QSKIP("lrelease is missing or broken"); + + const QString tsFile = QFINDTESTDATA("testdata/qundostack.ts"); + QVERIFY(!tsFile.isEmpty()); + QFile::remove("qundostack.qm"); // Avoid confusion by strays. + QVERIFY(!QProcess::execute(binDir + "/lrelease -silent " + tsFile + " -qm qundostack.qm")); + + QTranslator translator; + QVERIFY(translator.load("qundostack.qm")); + QFile::remove("qundostack.qm"); + qApp->installTranslator(&translator); + + QUndoStack stack; + QScopedPointer undo_action(stack.createUndoAction(nullptr)); + QScopedPointer redo_action(stack.createRedoAction(nullptr)); + + QCOMPARE(undo_action->text(), QString("Undo-default-text")); + QCOMPARE(redo_action->text(), QString("Redo-default-text")); + + QString str; + + stack.push(new AppendCommand(&str, "foo")); + QCOMPARE(undo_action->text(), QString("undo-prefix append undo-suffix")); + QCOMPARE(redo_action->text(), QString("Redo-default-text")); + + stack.push(new InsertCommand(&str, 0, "bar")); + stack.undo(); + QCOMPARE(undo_action->text(), QString("undo-prefix append undo-suffix")); + QCOMPARE(redo_action->text(), QString("redo-prefix insert redo-suffix")); + + stack.undo(); + QCOMPARE(undo_action->text(), QString("Undo-default-text")); + QCOMPARE(redo_action->text(), QString("redo-prefix append redo-suffix")); + + qApp->removeTranslator(&translator); +#endif +} + +void tst_QUndoStack::separateUndoText() +{ + QUndoStack stack; + QScopedPointer undo_action(stack.createUndoAction(nullptr)); + QScopedPointer redo_action(stack.createRedoAction(nullptr)); + + QUndoCommand *command1 = new IdleCommand(); + QUndoCommand *command2 = new IdleCommand(); + stack.push(command1); + stack.push(command2); + stack.undo(); + + QCOMPARE(undo_action->text(), QString("Undo idle-action")); + QCOMPARE(redo_action->text(), QString("Redo idle-action")); + QCOMPARE(command1->actionText(), QString("idle-action")); + + QCOMPARE(command1->text(), QString("idle-item")); + QCOMPARE(stack.text(0), QString("idle-item")); + + command1->setText("idle"); + QCOMPARE(command1->actionText(), QString("idle")); + QCOMPARE(command1->text(), QString("idle")); + + command1->setText("idle-item\nidle-action"); + QCOMPARE(command1->actionText(), QString("idle-action")); + QCOMPARE(command1->text(), QString("idle-item")); +} + +QTEST_MAIN(tst_QUndoStack) + +#include "tst_qundostack.moc" diff --git a/tests/auto/gui/util/util.pro b/tests/auto/gui/util/util.pro index 961424cc35..7523e01ca4 100644 --- a/tests/auto/gui/util/util.pro +++ b/tests/auto/gui/util/util.pro @@ -9,4 +9,6 @@ SUBDIRS= \ qshadergraphloader \ qshadernodes \ qshadernodesloader \ - qtexturefilereader + qtexturefilereader \ + qundogroup \ + qundostack -- cgit v1.2.3