Masterwork From Distant Lands
ActivePublic

Authored by dkazakov on Oct 20 2016, 4:36 PM.
diff --git a/libs/image/kis_image.cc b/libs/image/kis_image.cc
index 8457e82..3a52ef8 100644
--- a/libs/image/kis_image.cc
+++ b/libs/image/kis_image.cc
@@ -1050,7 +1050,15 @@ QRect KisImage::effectiveLodBounds() const
KisPostExecutionUndoAdapter* KisImage::postExecutionUndoAdapter() const
{
- return &m_d->postExecutionUndoAdapter;
+
+
+ const int lod = currentLevelOfDetail();
+
+ ENTER_FUNCTION() << ppVar(lod);
+
+ return lod > 0 ?
+ m_d->scheduler.lodNPostExecutionUndoAdapter() :
+ &m_d->postExecutionUndoAdapter;
}
void KisImage::setUndoStore(KisUndoStore *undoStore)
@@ -1299,6 +1307,11 @@ void KisImage::requestStrokeCancellation()
}
}
+UndoResult KisImage::requestUndoLastLod0Stroke()
+{
+ return m_d->scheduler.tryUndoLastStrokeAsync();
+}
+
void KisImage::requestStrokeEnd()
{
emit sigStrokeEndRequested();
diff --git a/libs/image/kis_image.h b/libs/image/kis_image.h
index ed2c77a..9b49dc2 100644
--- a/libs/image/kis_image.h
+++ b/libs/image/kis_image.h
@@ -34,6 +34,7 @@
#include "kis_node_graph_listener.h"
#include "kis_node_facade.h"
#include "kis_image_interfaces.h"
+#include "kis_strokes_queue_undo_result.h"
#include <kritaimage_export.h>
@@ -870,6 +871,15 @@ public Q_SLOTS:
void requestStrokeCancellation();
/**
+ * This method requests the last stroke executed on the image to become undone.
+ * If the stroke is not ended, or if all the Lod0 strokes are completed, the method
+ * returns UNDO_FAIL. If the last Lod0 is going to be finished soon, then UNDO_WAIT
+ * is returned and the caller should just wait for its completion and call global undo
+ * instead.
+ */
+ UndoResult requestUndoLastLod0Stroke();
+
+ /**
* This method is called when image or some other part of Krita
* (*not* the creator of the stroke) decides that the stroke
* should be ended. If the creator of the stroke supports it, it
diff --git a/libs/image/kis_stroke.cpp b/libs/image/kis_stroke.cpp
index 0cd90d4..8f031a1 100644
--- a/libs/image/kis_stroke.cpp
+++ b/libs/image/kis_stroke.cpp
@@ -172,6 +172,12 @@ void KisStroke::cancelStroke()
m_strokeEnded = true;
}
+bool KisStroke::canCancel() const
+{
+ return m_isCancelled || !m_strokeInitialized ||
+ !m_jobsQueue.isEmpty() || !m_strokeEnded;
+}
+
bool KisStroke::sanityCheckAllJobsAreCancellable() const
{
Q_FOREACH (KisStrokeJob *item, m_jobsQueue) {
diff --git a/libs/image/kis_stroke.h b/libs/image/kis_stroke.h
index 9d33e83..196c9a6 100644
--- a/libs/image/kis_stroke.h
+++ b/libs/image/kis_stroke.h
@@ -55,6 +55,8 @@ public:
void endStroke();
void cancelStroke();
+ bool canCancel() const;
+
bool supportsSuspension();
void suspendStroke(KisStrokeSP recipient);
diff --git a/libs/image/kis_strokes_queue.cpp b/libs/image/kis_strokes_queue.cpp
index da4e5ad..73f7517 100644
--- a/libs/image/kis_strokes_queue.cpp
+++ b/libs/image/kis_strokes_queue.cpp
@@ -25,20 +25,72 @@
#include "kis_updater_context.h"
#include "kis_stroke_job_strategy.h"
#include "kis_stroke_strategy.h"
+#include "kis_undo_stores.h"
+#include "kis_post_execution_undo_adapter.h"
typedef QQueue<KisStrokeSP> StrokesQueue;
typedef QQueue<KisStrokeSP>::iterator StrokesQueueIterator;
+#include "kis_image_interfaces.h"
+class KisStrokesQueue::LodNUndoStrokesFacade : public KisStrokesFacade
+{
+public:
+ LodNUndoStrokesFacade(KisStrokesQueue *_q) : q(_q) {}
+
+ KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy) override {
+ ENTER_FUNCTION() << strokeStrategy->id();
+
+ return q->startLodNUndoStroke(strokeStrategy);
+ }
+
+ void addJob(KisStrokeId id, KisStrokeJobData *data) override {
+
+ ENTER_FUNCTION();
+
+ KisStrokeSP stroke = id.toStrongRef();
+ KIS_SAFE_ASSERT_RECOVER_NOOP(stroke);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!stroke->lodBuddy());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(stroke->type() == KisStroke::LODN);
+
+ q->addJob(id, data);
+ }
+
+ void endStroke(KisStrokeId id) override {
+ ENTER_FUNCTION();
+
+ KisStrokeSP stroke = id.toStrongRef();
+ KIS_SAFE_ASSERT_RECOVER_NOOP(stroke);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!stroke->lodBuddy());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(stroke->type() == KisStroke::LODN);
+
+ q->endStroke(id);
+ }
+
+ bool cancelStroke(KisStrokeId id) override {
+ Q_UNUSED(id);
+ qFatal("Not implemented");
+ return false;
+ }
+
+private:
+ KisStrokesQueue *q;
+};
+
+
struct Q_DECL_HIDDEN KisStrokesQueue::Private {
- Private()
- : openedStrokesCounter(0),
+ Private(KisStrokesQueue *_q)
+ : q(_q),
+ openedStrokesCounter(0),
needsExclusiveAccess(false),
wrapAroundModeSupported(false),
currentStrokeLoaded(false),
lodNNeedsSynchronization(true),
desiredLevelOfDetail(0),
- nextDesiredLevelOfDetail(0) {}
+ nextDesiredLevelOfDetail(0),
+ lodNStrokesFacade(_q),
+ lodNPostExecutionUndoAdapter(&lodNUndoStore, &lodNStrokesFacade) {}
+ KisStrokesQueue *q;
StrokesQueue strokesQueue;
int openedStrokesCounter;
bool needsExclusiveAccess;
@@ -52,6 +104,9 @@ struct Q_DECL_HIDDEN KisStrokesQueue::Private {
KisLodSyncStrokeStrategyFactory lod0ToNStrokeStrategyFactory;
KisSuspendResumeStrategyFactory suspendUpdatesStrokeStrategyFactory;
KisSuspendResumeStrategyFactory resumeUpdatesStrokeStrategyFactory;
+ KisSurrogateUndoStore lodNUndoStore;
+ LodNUndoStrokesFacade lodNStrokesFacade;
+ KisPostExecutionUndoAdapter lodNPostExecutionUndoAdapter;
void cancelForgettableStrokes();
void startLod0ToNStroke(int levelOfDetail, bool forgettable);
@@ -63,11 +118,12 @@ struct Q_DECL_HIDDEN KisStrokesQueue::Private {
void switchDesiredLevelOfDetail(bool forced);
bool hasUnfinishedStrokes() const;
+ void findLastLod0Stroke(KisStrokeSP *lod0, KisStrokeSP *buddy);
};
KisStrokesQueue::KisStrokesQueue()
- : m_d(new Private)
+ : m_d(new Private(this))
{
}
@@ -211,6 +267,23 @@ StrokesQueueIterator KisStrokesQueue::Private::findNewLodNPos(KisStrokeSP lodN)
return it;
}
+KisStrokeId KisStrokesQueue::startLodNUndoStroke(KisStrokeStrategy *strokeStrategy)
+{
+ QMutexLocker locker(&m_d->mutex);
+
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!m_d->lodNNeedsSynchronization);
+ KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->desiredLevelOfDetail > 0);
+
+ KisStrokeSP buddy(new KisStroke(strokeStrategy, KisStroke::LODN, m_d->desiredLevelOfDetail));
+ strokeStrategy->setCancelStrokeId(buddy);
+ m_d->strokesQueue.insert(m_d->findNewLodNPos(buddy), buddy);
+
+ KisStrokeId id(buddy);
+ m_d->openedStrokesCounter++;
+
+ return id;
+}
+
KisStrokeId KisStrokesQueue::startStroke(KisStrokeStrategy *strokeStrategy)
{
QMutexLocker locker(&m_d->mutex);
@@ -225,6 +298,8 @@ KisStrokeId KisStrokesQueue::startStroke(KisStrokeStrategy *strokeStrategy)
(lodBuddyStrategy =
strokeStrategy->createLodClone(m_d->desiredLevelOfDetail))) {
+ ENTER_FUNCTION() << ppVar(m_d->lodNNeedsSynchronization);
+
if (m_d->lodNNeedsSynchronization) {
m_d->startLod0ToNStroke(m_d->desiredLevelOfDetail, false);
}
@@ -372,6 +447,91 @@ bool KisStrokesQueue::tryCancelCurrentStrokeAsync()
return anythingCanceled;
}
+void KisStrokesQueue::Private::findLastLod0Stroke(KisStrokeSP *lod0, KisStrokeSP *buddy)
+{
+ Q_UNUSED(lod0);
+ Q_UNUSED(buddy);
+}
+
+
+UndoResult KisStrokesQueue::tryUndoLastStrokeAsync()
+{
+ UndoResult result = UNDO_FAIL;
+ QMutexLocker locker(&m_d->mutex);
+
+ std::reverse_iterator<StrokesQueue::ConstIterator> it(m_d->strokesQueue.constEnd());
+ std::reverse_iterator<StrokesQueue::ConstIterator> end(m_d->strokesQueue.constBegin());
+
+ KisStrokeSP lastStroke;
+ KisStrokeSP lastBuddy;
+ bool buddyFound = false;
+
+ for (; it != end; ++it) {
+ if ((*it)->type() == KisStroke::LEGACY) {
+ break;
+ }
+
+ if (!lastStroke && (*it)->type() == KisStroke::LOD0 && !(*it)->isCancelled()) {
+ lastStroke = *it;
+ lastBuddy = lastStroke->lodBuddy();
+
+ KIS_SAFE_ASSERT_RECOVER(lastBuddy) {
+ lastStroke.clear();
+ lastBuddy.clear();
+ break;
+ }
+ }
+
+ KIS_SAFE_ASSERT_RECOVER(!lastStroke || *it == lastBuddy || (*it)->type() != KisStroke::LODN) {
+ lastStroke.clear();
+ lastBuddy.clear();
+ break;
+ }
+
+ if (lastStroke && *it == lastBuddy) {
+ KIS_SAFE_ASSERT_RECOVER(lastBuddy->type() == KisStroke::LODN) {
+ lastStroke.clear();
+ lastBuddy.clear();
+ break;
+ }
+ buddyFound = true;
+ break;
+ }
+ }
+
+ if (!lastStroke) return UNDO_FAIL;
+ if (!lastStroke->isEnded()) return UNDO_FAIL;
+ if (lastStroke->isCancelled()) return UNDO_FAIL;
+
+ KIS_SAFE_ASSERT_RECOVER_NOOP(!buddyFound ||
+ lastStroke->isCancelled() == lastBuddy->isCancelled());
+ KIS_SAFE_ASSERT_RECOVER_NOOP(lastBuddy->isEnded());
+
+ qDebug() << ppVar(lastStroke->name()) << ppVar(lastBuddy->name()) << ppVar(buddyFound);
+ qDebug() << ppVar(lastStroke->isInitialized());
+
+ qDebug() << ppVar(lastStroke->canCancel());
+ qDebug() << ppVar(lastBuddy->canCancel());
+
+ if (!lastStroke->canCancel()) {
+ return UNDO_WAIT;
+ }
+ lastStroke->cancelStroke();
+
+ if (buddyFound && lastBuddy->canCancel()) {
+ lastBuddy->cancelStroke();
+ } else {
+ // TODO: assert no other lodn strokes
+ locker.unlock();
+ m_d->lodNUndoStore.undo();
+ locker.relock();
+ }
+
+ result = UNDO_OK;
+
+ return result;
+}
+
void KisStrokesQueue::processQueue(KisUpdaterContext &updaterContext,
bool externalJobsPending)
{
@@ -455,6 +615,17 @@ void KisStrokesQueue::notifyUFOChangedImage()
m_d->lodNNeedsSynchronization = true;
}
+void KisStrokesQueue::debugPrintStrokes()
+{
+ QMutexLocker locker(&m_d->mutex);
+
+ qDebug() <<"===";
+ Q_FOREACH (KisStrokeSP stroke, m_d->strokesQueue) {
+ qDebug() << ppVar(stroke->name()) << ppVar(stroke->type()) << ppVar(stroke->numJobs()) << ppVar(stroke->isInitialized()) << ppVar(stroke->isCancelled());
+ }
+ qDebug() <<"===";
+}
+
void KisStrokesQueue::setLod0ToNStrokeStrategyFactory(const KisLodSyncStrokeStrategyFactory &factory)
{
m_d->lod0ToNStrokeStrategyFactory = factory;
@@ -470,6 +641,11 @@ void KisStrokesQueue::setResumeUpdatesStrokeStrategyFactory(const KisSuspendResu
m_d->resumeUpdatesStrokeStrategyFactory = factory;
}
+KisPostExecutionUndoAdapter *KisStrokesQueue::lodNPostExecutionUndoAdapter() const
+{
+ return &m_d->lodNPostExecutionUndoAdapter;
+}
+
KUndo2MagicString KisStrokesQueue::currentStrokeName() const
{
QMutexLocker locker(&m_d->mutex);
@@ -612,5 +788,5 @@ bool KisStrokesQueue::checkLevelOfDetailProperty(int runningLevelOfDetail)
KisStrokeSP stroke = m_d->strokesQueue.head();
return runningLevelOfDetail < 0 ||
- stroke->worksOnLevelOfDetail() == runningLevelOfDetail;
+ stroke->worksOnLevelOfDetail() == runningLevelOfDetail;
}
diff --git a/libs/image/kis_strokes_queue.h b/libs/image/kis_strokes_queue.h
index 794ca3c..345d4b2 100644
--- a/libs/image/kis_strokes_queue.h
+++ b/libs/image/kis_strokes_queue.h
@@ -25,6 +25,7 @@
#include "kis_stroke_job_strategy.h"
#include "kis_stroke_strategy.h"
#include "kis_stroke_strategy_factory.h"
+#include "kis_strokes_queue_undo_result.h"
@@ -32,6 +33,7 @@ class KisUpdaterContext;
class KisStroke;
class KisStrokeStrategy;
class KisStrokeJobData;
+class KisPostExecutionUndoAdapter;
class KRITAIMAGE_EXPORT KisStrokesQueue
@@ -48,6 +50,8 @@ public:
bool tryCancelCurrentStrokeAsync();
+ UndoResult tryUndoLastStrokeAsync();
+
void processQueue(KisUpdaterContext &updaterContext,
bool externalJobsPending);
bool needsExclusiveAccess() const;
@@ -64,6 +68,7 @@ public:
void setLod0ToNStrokeStrategyFactory(const KisLodSyncStrokeStrategyFactory &factory);
void setSuspendUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory);
void setResumeUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory);
+ KisPostExecutionUndoAdapter* lodNPostExecutionUndoAdapter() const;
/**
* Notifies the queue, that someone else (neither strokes nor the
@@ -72,6 +77,8 @@ public:
*/
void notifyUFOChangedImage();
+ void debugPrintStrokes();
+
private:
bool processOneJob(KisUpdaterContext &updaterContext,
bool externalJobsPending);
@@ -82,6 +89,10 @@ private:
bool checkBarrierProperty(qint32 numMergeJobs, qint32 numStrokeJobs,
bool externalJobsPending);
bool checkLevelOfDetailProperty(int runningLevelOfDetail);
+
+ class LodNUndoStrokesFacade;
+ KisStrokeId startLodNUndoStroke(KisStrokeStrategy *strokeStrategy);
+
private:
struct Private;
Private * const m_d;
diff --git a/libs/image/kis_strokes_queue_undo_result.h b/libs/image/kis_strokes_queue_undo_result.h
index 96ebff4..5749902 100644
--- a/libs/image/kis_strokes_queue_undo_result.h
+++ b/libs/image/kis_strokes_queue_undo_result.h
@@ -19,5 +19,11 @@
#ifndef KIS_STROKES_QUEUE_UNDO_RESULT_H
#define KIS_STROKES_QUEUE_UNDO_RESULT_H
+enum UndoResult {
+ UNDO_FAIL,
+ UNDO_OK,
+ UNDO_WAIT
+};
+
#endif // KIS_STROKES_QUEUE_UNDO_RESULT_H
diff --git a/libs/image/kis_update_scheduler.cpp b/libs/image/kis_update_scheduler.cpp
index cfb003b..1744073 100644
--- a/libs/image/kis_update_scheduler.cpp
+++ b/libs/image/kis_update_scheduler.cpp
@@ -206,6 +206,11 @@ bool KisUpdateScheduler::tryCancelCurrentStrokeAsync()
return m_d->strokesQueue.tryCancelCurrentStrokeAsync();
}
+UndoResult KisUpdateScheduler::tryUndoLastStrokeAsync()
+{
+ return m_d->strokesQueue.tryUndoLastStrokeAsync();
+}
+
bool KisUpdateScheduler::wrapAroundModeSupported() const
{
return m_d->strokesQueue.wrapAroundModeSupported();
@@ -265,6 +270,11 @@ void KisUpdateScheduler::setResumeUpdatesStrokeStrategyFactory(const KisSuspendR
m_d->strokesQueue.setResumeUpdatesStrokeStrategyFactory(factory);
}
+KisPostExecutionUndoAdapter *KisUpdateScheduler::lodNPostExecutionUndoAdapter() const
+{
+ return m_d->strokesQueue.lodNPostExecutionUndoAdapter();
+}
+
void KisUpdateScheduler::updateSettings()
{
m_d->updatesQueue.updateSettings();
diff --git a/libs/image/kis_update_scheduler.h b/libs/image/kis_update_scheduler.h
index f26c613..5bea480 100644
--- a/libs/image/kis_update_scheduler.h
+++ b/libs/image/kis_update_scheduler.h
@@ -25,11 +25,13 @@
#include "kis_image_interfaces.h"
#include "kis_stroke_strategy_factory.h"
+#include "kis_strokes_queue_undo_result.h"
class QRect;
class KoProgressProxy;
class KisProjectionUpdateListener;
class KisSpontaneousJob;
+class KisPostExecutionUndoAdapter;
class KRITAIMAGE_EXPORT KisUpdateScheduler : public QObject, public KisStrokesFacade
@@ -174,6 +176,9 @@ public:
*/
void setResumeUpdatesStrokeStrategyFactory(const KisSuspendResumeStrategyFactory &factory);
+ KisPostExecutionUndoAdapter* lodNPostExecutionUndoAdapter() const;
+
+
/**
* tryCancelCurrentStrokeAsync() checks whether there is a
* *running* stroke (which is being executed at this very moment)
@@ -188,6 +193,8 @@ public:
*/
bool tryCancelCurrentStrokeAsync();
+ UndoResult tryUndoLastStrokeAsync();
+
bool wrapAroundModeSupported() const;
int currentLevelOfDetail() const;
diff --git a/libs/image/tests/kis_strokes_queue_test.cpp b/libs/image/tests/kis_strokes_queue_test.cpp
index 77ab286..8e16067 100644
--- a/libs/image/tests/kis_strokes_queue_test.cpp
+++ b/libs/image/tests/kis_strokes_queue_test.cpp
@@ -384,24 +384,85 @@ void KisStrokesQueueTest::testAsyncCancelWhileOpenedStroke()
VERIFY_EMPTY(jobs[2]);
}
+struct KisStrokesQueueTest::LodStrokesQueueTester {
+
+ LodStrokesQueueTester(bool real = false)
+ : fakeContext(2),
+ realContext(2),
+ context(!real ? fakeContext : realContext)
+ {
+ queue.setSuspendUpdatesStrokeStrategyFactory(
+ []() {
+ return KisSuspendResumePair(
+ new KisTestingStrokeStrategy("susp_u_", false, true, true),
+ QList<KisStrokeJobData*>());
+ });
+
+ queue.setResumeUpdatesStrokeStrategyFactory(
+ []() {
+ return KisSuspendResumePair(
+ new KisTestingStrokeStrategy("resu_u_", false, true, true),
+ QList<KisStrokeJobData*>());
+ });
+ queue.setLod0ToNStrokeStrategyFactory(
+ [](bool forgettable) {
+ Q_UNUSED(forgettable);
+ return KisSuspendResumePair(
+ new KisTestingStrokeStrategy("sync_u_", false, true, true),
+ QList<KisStrokeJobData*>());
+ });
+ }
-void KisStrokesQueueTest::testStrokesLevelOfDetail()
-{
KisStrokesQueue queue;
- queue.setSuspendUpdatesStrokeStrategyFactory(
- []() {
- return KisSuspendResumePair(
- new KisTestingStrokeStrategy("susp_u_", false, true, true),
- QList<KisStrokeJobData*>());
- });
+ KisTestableUpdaterContext fakeContext;
+ KisUpdaterContext realContext;
+ KisUpdaterContext &context;
+ QVector<KisUpdateJobItem*> jobs;
+
+ void processQueueNoAdd() {
+ if (&context != &fakeContext) return;
+
+ fakeContext.clear();
+
+ jobs = fakeContext.getJobs();
+ VERIFY_EMPTY(jobs[0]);
+ VERIFY_EMPTY(jobs[1]);
+ }
+
+ void processQueue() {
+ processQueueNoAdd();
+ queue.processQueue(context, false);
+
+ if (&context == &realContext) {
+ context.waitForDone();
+ }
+ }
- queue.setResumeUpdatesStrokeStrategyFactory(
- []() {
- return KisSuspendResumePair(
- new KisTestingStrokeStrategy("resu_u_", false, true, true),
- QList<KisStrokeJobData*>());
- });
+ void checkOnlyJob(const QString &name) {
+ KIS_ASSERT(&context == &fakeContext);
+
+ jobs = fakeContext.getJobs();
+ COMPARE_NAME(jobs[0], name);
+ VERIFY_EMPTY(jobs[1]);
+ QCOMPARE(queue.needsExclusiveAccess(), false);
+ }
+
+ void checkOnlyExecutedJob(const QString &name) {
+ realContext.waitForDone();
+ QVERIFY(!globalExecutedDabs.isEmpty());
+ QCOMPARE(globalExecutedDabs[0], name);
+
+ QCOMPARE(globalExecutedDabs.size(), 1);
+ globalExecutedDabs.clear();
+ }
+};
+
+
+void KisStrokesQueueTest::testStrokesLevelOfDetail()
+{
+ LodStrokesQueueTester t;
+ KisStrokesQueue &queue = t.queue;
// create a stroke with LOD0 + LOD2
queue.setDesiredLevelOfDetail(2);
@@ -469,4 +530,118 @@ void KisStrokesQueueTest::testStrokesLevelOfDetail()
context.clear();
}
+#include <kundo2command.h>
+#include <kis_post_execution_undo_adapter.h>
+struct TestUndoCommand : public KUndo2Command
+{
+ TestUndoCommand(const QString &text) : KUndo2Command(kundo2_noi18n(text)) {}
+
+ void undo() {
+ ENTER_FUNCTION();
+ undoCount++;
+ }
+
+ void redo() {
+ ENTER_FUNCTION();
+ redoCount++;
+ }
+
+ int undoCount = 0;
+ int redoCount = 0;
+};
+
+void KisStrokesQueueTest::testLodUndoBase()
+{
+ LodStrokesQueueTester t;
+ KisStrokesQueue &queue = t.queue;
+
+ // create a stroke with LOD0 + LOD2
+ queue.setDesiredLevelOfDetail(2);
+ KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy("str1_", false, true));
+ queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
+ queue.endStroke(id1);
+
+ KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy("str2_", false, true));
+ queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
+ queue.endStroke(id2);
+
+ t.processQueue();
+ t.checkOnlyJob("clone2_str1_dab");
+
+
+ QSharedPointer<TestUndoCommand> undoStr1(new TestUndoCommand("str1_undo"));
+ queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr1);
+
+ t.processQueue();
+ t.checkOnlyJob("clone2_str2_dab");
+
+ QSharedPointer<TestUndoCommand> undoStr2(new TestUndoCommand("str2_undo"));
+ queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr2);
+
+ t.processQueue();
+ t.checkOnlyJob("susp_u_init");
+
+ t.processQueue();
+ t.checkOnlyJob("str1_dab");
+
+ t.processQueue();
+ t.checkOnlyJob("str2_dab");
+
+ t.processQueue();
+ t.checkOnlyJob("resu_u_init");
+}
+
+void KisStrokesQueueTest::testLodUndoBase2()
+{
+ LodStrokesQueueTester t(true);
+ KisStrokesQueue &queue = t.queue;
+
+ // create a stroke with LOD0 + LOD2
+ queue.setDesiredLevelOfDetail(2);
+ KisStrokeId id1 = queue.startStroke(new KisTestingStrokeStrategy("str1_", false, true, false, true));
+ queue.addJob(id1, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
+ queue.endStroke(id1);
+
+ KisStrokeId id2 = queue.startStroke(new KisTestingStrokeStrategy("str2_", false, true, false, true));
+ queue.addJob(id2, new KisTestingStrokeJobData(KisStrokeJobData::CONCURRENT));
+ queue.endStroke(id2);
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("sync_u_init");
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("clone2_str1_dab");
+
+ QSharedPointer<TestUndoCommand> undoStr1(new TestUndoCommand("str1_undo"));
+ queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr1);
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("clone2_str2_dab");
+
+ QSharedPointer<TestUndoCommand> undoStr2(new TestUndoCommand("str2_undo"));
+ queue.lodNPostExecutionUndoAdapter()->addCommand(undoStr2);
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("susp_u_init");
+
+ queue.tryUndoLastStrokeAsync();
+ t.processQueue();
+
+ while (queue.currentStrokeName() == kundo2_noi18n("str2_undo")) {
+ //queue.debugPrintStrokes();
+ t.processQueue();
+ }
+
+ QCOMPARE(undoStr2->undoCount, 1);
+
+ t.checkOnlyExecutedJob("str1_dab");
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("str2_cancel");
+
+ t.processQueue();
+ t.checkOnlyExecutedJob("resu_u_init");
+}
+
+
QTEST_MAIN(KisStrokesQueueTest)
diff --git a/libs/image/tests/kis_strokes_queue_test.h b/libs/image/tests/kis_strokes_queue_test.h
index f0765d4..3fdefdf 100644
--- a/libs/image/tests/kis_strokes_queue_test.h
+++ b/libs/image/tests/kis_strokes_queue_test.h
@@ -36,6 +36,11 @@ private Q_SLOTS:
void testOpenedStrokeCounter();
void testAsyncCancelWhileOpenedStroke();
void testStrokesLevelOfDetail();
+ void testLodUndoBase();
+ void testLodUndoBase2();
+
+private:
+ struct LodStrokesQueueTester;
};
#endif /* __KIS_STROKES_QUEUE_TEST_H */
diff --git a/libs/image/tests/scheduler_utils.h b/libs/image/tests/scheduler_utils.h
index 45d234f..c374269 100644
--- a/libs/image/tests/scheduler_utils.h
+++ b/libs/image/tests/scheduler_utils.h
@@ -85,6 +85,8 @@ private:
int m_lod;
};
+static QStringList globalExecutedDabs;
+
class KisNoopDabStrategy : public KisStrokeJobStrategy
{
public:
@@ -95,6 +97,8 @@ public:
void run(KisStrokeJobData *data) {
Q_UNUSED(data);
+
+ globalExecutedDabs << m_name;
}
QString name() {
@@ -140,10 +144,13 @@ public:
KisTestingStrokeStrategy(const QString &prefix = QString(),
bool exclusive = false,
bool inhibitServiceJobs = false,
- bool forceAllowInitJob = false)
- : m_prefix(prefix),
+ bool forceAllowInitJob = false,
+ bool forceAllowCancelJob = false)
+ : KisStrokeStrategy(prefix, kundo2_noi18n(prefix)),
+ m_prefix(prefix),
m_inhibitServiceJobs(inhibitServiceJobs),
m_forceAllowInitJob(forceAllowInitJob),
+ m_forceAllowCancelJob(forceAllowCancelJob),
m_cancelSeqNo(0)
{
setExclusive(exclusive);
@@ -170,7 +177,7 @@ public:
}
KisStrokeJobStrategy* createCancelStrategy() {
- return !m_inhibitServiceJobs ?
+ return m_forceAllowCancelJob || !m_inhibitServiceJobs ?
new KisNoopDabStrategy(m_prefix + "cancel") : 0;
}
@@ -200,6 +207,7 @@ private:
QString m_prefix;
bool m_inhibitServiceJobs;
int m_forceAllowInitJob;
+ bool m_forceAllowCancelJob;
int m_cancelSeqNo;
};
diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp
index 04536c6..cafbfcf 100644
--- a/libs/ui/KisDocument.cpp
+++ b/libs/ui/KisDocument.cpp
@@ -215,6 +215,11 @@ public:
void undo() override {
KisImageWSP image = this->image();
image->requestUndoDuringStroke();
+
+ if (image->requestUndoLastLod0Stroke() == UNDO_OK) {
+ return;
+ }
+
if(image->tryBarrierLock()) {
KUndo2Stack::undo();
image->unlock();
diff --git a/libs/ui/tool/kis_resources_snapshot.cpp b/libs/ui/tool/kis_resources_snapshot.cpp
index 1b8966f..0f077ba 100644
--- a/libs/ui/tool/kis_resources_snapshot.cpp
+++ b/libs/ui/tool/kis_resources_snapshot.cpp
@@ -185,7 +185,8 @@ void KisResourcesSnapshot::setupPaintAction(KisRecordedPaintAction *action)
KisPostExecutionUndoAdapter* KisResourcesSnapshot::postExecutionUndoAdapter() const
{
- return m_d->undoAdapter;
+ return m_d->image->postExecutionUndoAdapter();
+ //return m_d->undoAdapter;
}
void KisResourcesSnapshot::setCurrentNode(KisNodeSP node)
diff --git a/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp b/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp
index abe3b5f..7d9f803 100644
--- a/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp
+++ b/libs/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp
@@ -240,7 +240,7 @@ void KisPainterBasedStrokeStrategy::finishStrokeCallback()
QScopedPointer<KisUndoStore> dumbUndoStore;
- if (!m_undoEnabled) {
+ if (0 && !m_undoEnabled) {
dumbUndoStore.reset(new KisDumbUndoStore());
dumbUndoAdapter.reset(new KisPostExecutionUndoAdapter(dumbUndoStore.data(), 0));
dkazakov edited the content of this paste. (Show Details)Oct 20 2016, 4:36 PM
dkazakov changed the title of this paste from untitled to Masterwork From Distant Lands.
dkazakov updated the paste's language from autodetect to autodetect.