diff --git a/lib/abstractimageoperation.h b/lib/abstractimageoperation.h --- a/lib/abstractimageoperation.h +++ b/lib/abstractimageoperation.h @@ -58,8 +58,6 @@ void applyToDocument(Document::Ptr); Document::Ptr document() const; - void finishUndoJob(); - protected: virtual void redo() = 0; virtual void undo() diff --git a/lib/abstractimageoperation.cpp b/lib/abstractimageoperation.cpp --- a/lib/abstractimageoperation.cpp +++ b/lib/abstractimageoperation.cpp @@ -52,14 +52,20 @@ mOp->undo(); } + void redo() override + { + mOp->redo(); + } + private: AbstractImageOperation* mOp; }; struct AbstractImageOperationPrivate { QString mText; QUrl mUrl; + ImageOperationCommand* mCommand; }; AbstractImageOperation::AbstractImageOperation() @@ -75,7 +81,11 @@ void AbstractImageOperation::applyToDocument(Document::Ptr doc) { d->mUrl = doc->url(); - redo(); + + d->mCommand = new ImageOperationCommand(this); + d->mCommand->setText(d->mText); + // QUndoStack::push() executes command by calling its redo() function + doc->undoStack()->push(d->mCommand); } Document::Ptr AbstractImageOperation::document() const @@ -88,27 +98,21 @@ void AbstractImageOperation::finish(bool ok) { if (ok) { - ImageOperationCommand* command = new ImageOperationCommand(this); - command->setText(d->mText); - document()->undoStack()->push(command); - document()->imageOperationCompleted(); + // Give QUndoStack time to update in case the redo/undo is executed immediately + // (e.g. undo crop just sets the previous image) + QTimer::singleShot(0, document().data(), &Document::imageOperationCompleted); } else { - deleteLater(); + // Remove command from undo stack without executing undo() + d->mCommand->setObsolete(true); + document()->undoStack()->undo(); } } void AbstractImageOperation::finishFromKJob(KJob* job) { finish(job->error() == KJob::NoError); } -void AbstractImageOperation::finishUndoJob() -{ - // Give QUndoStack time to update in case the undo is executed immediately - // (e.g. undo crop just sets the previous image) - QTimer::singleShot(0, document().data(), &Document::imageOperationCompleted); -} - void AbstractImageOperation::setText(const QString& text) { d->mText = text; diff --git a/lib/crop/cropimageoperation.cpp b/lib/crop/cropimageoperation.cpp --- a/lib/crop/cropimageoperation.cpp +++ b/lib/crop/cropimageoperation.cpp @@ -89,7 +89,7 @@ return; } document()->editor()->setImage(d->mOriginalImage); - finishUndoJob(); + finish(true); } } // namespace diff --git a/lib/redeyereduction/redeyereductionimageoperation.cpp b/lib/redeyereduction/redeyereductionimageoperation.cpp --- a/lib/redeyereduction/redeyereductionimageoperation.cpp +++ b/lib/redeyereduction/redeyereductionimageoperation.cpp @@ -104,7 +104,7 @@ painter.drawImage(rect.topLeft(), d->mOriginalImage); } document()->editor()->setImage(img); - finishUndoJob(); + finish(true); } /** diff --git a/lib/resize/resizeimageoperation.cpp b/lib/resize/resizeimageoperation.cpp --- a/lib/resize/resizeimageoperation.cpp +++ b/lib/resize/resizeimageoperation.cpp @@ -89,7 +89,7 @@ return; } document()->editor()->setImage(d->mOriginalImage); - finishUndoJob(); + finish(true); } } // namespace diff --git a/lib/transformimageoperation.cpp b/lib/transformimageoperation.cpp --- a/lib/transformimageoperation.cpp +++ b/lib/transformimageoperation.cpp @@ -102,9 +102,7 @@ break; } - TransformJob* job = new TransformJob(orientation); - connect(job, &TransformJob::result, this, &AbstractImageOperation::finishUndoJob); - document()->enqueueJob(job); + redoAsDocumentJob(new TransformJob(orientation)); } } // namespace diff --git a/tests/auto/documenttest.h b/tests/auto/documenttest.h --- a/tests/auto/documenttest.h +++ b/tests/auto/documenttest.h @@ -129,6 +129,7 @@ void testJobQueue(); void testCheckDocumentEditor(); void testUndoStackPush(); + void testUndoRedo(); void initTestCase(); void init(); diff --git a/tests/auto/documenttest.cpp b/tests/auto/documenttest.cpp --- a/tests/auto/documenttest.cpp +++ b/tests/auto/documenttest.cpp @@ -534,6 +534,7 @@ QVERIFY(doc->editor()); TestOperation* op = new TestOperation; op->applyToDocument(doc); + QTest::qWait(100); QVERIFY(doc->isModified()); QCOMPARE(modifiedDocumentListChangedSpy.count(), 1); modifiedDocumentListChangedSpy.clear(); @@ -841,3 +842,66 @@ QTest::qWait(100); QVERIFY(doc->undoStack()->isClean()); } + +void DocumentTest::testUndoRedo() +{ + class SuccessOperation : public AbstractImageOperation + { + public: + int mRedoCount = 0; + int mUndoCount = 0; + + protected: + virtual void redo() + { + mRedoCount++; + finish(true); + } + + virtual void undo() + { + mUndoCount++; + finish(true); + } + }; + + Document::Ptr doc = DocumentFactory::instance()->load(urlForTestFile("orient6.jpg")); + QSignalSpy modifiedSpy(doc.data(), &Document::modified); + QSignalSpy savedSpy(doc.data(), &Document::saved); + + SuccessOperation* op = new SuccessOperation; + QCOMPARE(op->mRedoCount, 0); + QCOMPARE(op->mUndoCount, 0); + + // Apply (redo) operation + op->applyToDocument(doc); + QVERIFY(modifiedSpy.wait()); + QCOMPARE(op->mRedoCount, 1); + QCOMPARE(op->mUndoCount, 0); + QCOMPARE(doc->undoStack()->count(), 1); + QVERIFY(!doc->undoStack()->isClean()); + + // Undo operation + doc->undoStack()->undo(); + QVERIFY(savedSpy.wait()); + QCOMPARE(op->mRedoCount, 1); + QCOMPARE(op->mUndoCount, 1); + QCOMPARE(doc->undoStack()->count(), 1); + QVERIFY(doc->undoStack()->isClean()); + + // Redo operation + doc->undoStack()->redo(); + QVERIFY(modifiedSpy.wait()); + QCOMPARE(op->mRedoCount, 2); + QCOMPARE(op->mUndoCount, 1); + QCOMPARE(doc->undoStack()->count(), 1); + QVERIFY(!doc->undoStack()->isClean()); + + // Undo operation again + doc->undoStack()->undo(); + QVERIFY(savedSpy.wait()); + QCOMPARE(op->mRedoCount, 2); + QCOMPARE(op->mUndoCount, 2); + QCOMPARE(doc->undoStack()->count(), 1); + QVERIFY(doc->undoStack()->isClean()); +}