Commit 0718fb65 authored by Wolthera van Hövell's avatar Wolthera van Hövell 🛍

Merge branch 'master' into krita-testing-wolthera

parents 541b596e bba9f1c8
krita/data/brushes/3_texture.png

100 KB | W: | H:

krita/data/brushes/3_texture.png

165 KB | W: | H:

krita/data/brushes/3_texture.png
krita/data/brushes/3_texture.png
krita/data/brushes/3_texture.png
krita/data/brushes/3_texture.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -55,7 +55,6 @@ struct Q_DECL_HIDDEN KisUpdateScheduler::Private { ...@@ -55,7 +55,6 @@ struct Q_DECL_HIDDEN KisUpdateScheduler::Private {
KisSimpleUpdateQueue updatesQueue; KisSimpleUpdateQueue updatesQueue;
KisStrokesQueue strokesQueue; KisStrokesQueue strokesQueue;
KisUpdaterContext updaterContext;
bool processingBlocked = false; bool processingBlocked = false;
qreal balancingRatio = 1.0; // updates-queue-size/strokes-queue-size qreal balancingRatio = 1.0; // updates-queue-size/strokes-queue-size
KisProjectionUpdateListener *projectionUpdateListener; KisProjectionUpdateListener *projectionUpdateListener;
...@@ -64,6 +63,11 @@ struct Q_DECL_HIDDEN KisUpdateScheduler::Private { ...@@ -64,6 +63,11 @@ struct Q_DECL_HIDDEN KisUpdateScheduler::Private {
QAtomicInt updatesLockCounter; QAtomicInt updatesLockCounter;
QReadWriteLock updatesStartLock; QReadWriteLock updatesStartLock;
KisLazyWaitCondition updatesFinishedCondition; KisLazyWaitCondition updatesFinishedCondition;
// KisUpdaterContext can emit signals to KisUpdateScheduler in the dtor, so it
// must to be deleted before anything else.
// That means updaterContext must be declared last.
KisUpdaterContext updaterContext;
}; };
KisUpdateScheduler::KisUpdateScheduler(KisProjectionUpdateListener *projectionUpdateListener) KisUpdateScheduler::KisUpdateScheduler(KisProjectionUpdateListener *projectionUpdateListener)
...@@ -163,9 +167,11 @@ void KisUpdateScheduler::fullRefresh(KisNodeSP root, const QRect& rc, const QRec ...@@ -163,9 +167,11 @@ void KisUpdateScheduler::fullRefresh(KisNodeSP root, const QRect& rc, const QRec
Q_ASSERT(m_d->updaterContext.isJobAllowed(walker)); Q_ASSERT(m_d->updaterContext.isJobAllowed(walker));
m_d->updaterContext.addMergeJob(walker); m_d->updaterContext.addMergeJob(walker);
m_d->updaterContext.waitForDone();
m_d->updaterContext.unlock(); m_d->updaterContext.unlock();
m_d->updaterContext.waitForDone();
if(needLock) unlock(true); if(needLock) unlock(true);
} }
...@@ -408,7 +414,9 @@ bool KisUpdateScheduler::haveUpdatesRunning() ...@@ -408,7 +414,9 @@ bool KisUpdateScheduler::haveUpdatesRunning()
QWriteLocker locker(&m_d->updatesStartLock); QWriteLocker locker(&m_d->updatesStartLock);
qint32 numMergeJobs, numStrokeJobs; qint32 numMergeJobs, numStrokeJobs;
m_d->updaterContext.lock();
m_d->updaterContext.getJobsSnapshot(numMergeJobs, numStrokeJobs); m_d->updaterContext.getJobsSnapshot(numMergeJobs, numStrokeJobs);
m_d->updaterContext.unlock();
return numMergeJobs; return numMergeJobs;
} }
......
...@@ -21,18 +21,14 @@ ...@@ -21,18 +21,14 @@
#include <QThread> #include <QThread>
#include <QThreadPool> #include <QThreadPool>
#include "kis_safe_read_list.h"
#include "kis_update_job_item.h" #include "kis_update_job_item.h"
#include "kis_stroke_job.h" #include "kis_stroke_job.h"
KisUpdaterContext::KisUpdaterContext(qint32 threadCount) KisUpdaterContext::KisUpdaterContext(qint32 threadCount):
m_jobs(threadCount > 0 ? threadCount : defaultThreadCount())
{ {
if(threadCount <= 0) {
threadCount = QThread::idealThreadCount();
threadCount = threadCount > 0 ? threadCount : 1;
}
m_jobs.resize(threadCount);
for(qint32 i = 0; i < m_jobs.size(); i++) { for(qint32 i = 0; i < m_jobs.size(); i++) {
m_jobs[i] = new KisUpdateJobItem(&m_exclusiveJobLock); m_jobs[i] = new KisUpdateJobItem(&m_exclusiveJobLock);
connect(m_jobs[i], SIGNAL(sigContinueUpdate(const QRect&)), connect(m_jobs[i], SIGNAL(sigContinueUpdate(const QRect&)),
...@@ -45,6 +41,10 @@ KisUpdaterContext::KisUpdaterContext(qint32 threadCount) ...@@ -45,6 +41,10 @@ KisUpdaterContext::KisUpdaterContext(qint32 threadCount)
connect(m_jobs[i], SIGNAL(sigJobFinished()), connect(m_jobs[i], SIGNAL(sigJobFinished()),
SLOT(slotJobFinished()), Qt::DirectConnection); SLOT(slotJobFinished()), Qt::DirectConnection);
} }
#ifdef SANITY_CHECK_CONTEXT_LOCKING
m_lockedBy = (Qt::HANDLE) -1;
#endif
} }
KisUpdaterContext::~KisUpdaterContext() KisUpdaterContext::~KisUpdaterContext()
...@@ -54,9 +54,19 @@ KisUpdaterContext::~KisUpdaterContext() ...@@ -54,9 +54,19 @@ KisUpdaterContext::~KisUpdaterContext()
delete m_jobs[i]; delete m_jobs[i];
} }
qint32 KisUpdaterContext::defaultThreadCount() const
{
int threadCount = QThread::idealThreadCount();
return threadCount > 0 ? threadCount : 1;
}
void KisUpdaterContext::getJobsSnapshot(qint32 &numMergeJobs, void KisUpdaterContext::getJobsSnapshot(qint32 &numMergeJobs,
qint32 &numStrokeJobs) qint32 &numStrokeJobs)
{ {
#ifdef SANITY_CHECK_CONTEXT_LOCKING
KIS_ASSERT(m_lockedBy == QThread::currentThreadId());
#endif
numMergeJobs = 0; numMergeJobs = 0;
numStrokeJobs = 0; numStrokeJobs = 0;
...@@ -91,6 +101,10 @@ bool KisUpdaterContext::hasSpareThread() ...@@ -91,6 +101,10 @@ bool KisUpdaterContext::hasSpareThread()
bool KisUpdaterContext::isJobAllowed(KisBaseRectsWalkerSP walker) bool KisUpdaterContext::isJobAllowed(KisBaseRectsWalkerSP walker)
{ {
#ifdef SANITY_CHECK_CONTEXT_LOCKING
KIS_ASSERT(m_lockedBy == QThread::currentThreadId());
#endif
int lod = this->currentLevelOfDetail(); int lod = this->currentLevelOfDetail();
if (lod >= 0 && walker->levelOfDetail() != lod) return false; if (lod >= 0 && walker->levelOfDetail() != lod) return false;
...@@ -116,9 +130,13 @@ bool KisUpdaterContext::isJobAllowed(KisBaseRectsWalkerSP walker) ...@@ -116,9 +130,13 @@ bool KisUpdaterContext::isJobAllowed(KisBaseRectsWalkerSP walker)
*/ */
void KisUpdaterContext::addMergeJob(KisBaseRectsWalkerSP walker) void KisUpdaterContext::addMergeJob(KisBaseRectsWalkerSP walker)
{ {
#ifdef SANITY_CHECK_CONTEXT_LOCKING
KIS_ASSERT(m_lockedBy == QThread::currentThreadId());
#endif
m_lodCounter.addLod(walker->levelOfDetail()); m_lodCounter.addLod(walker->levelOfDetail());
qint32 jobIndex = findSpareThread(); qint32 jobIndex = findSpareThread();
Q_ASSERT(jobIndex >= 0); KIS_ASSERT(jobIndex >= 0);
m_jobs[jobIndex]->setWalker(walker); m_jobs[jobIndex]->setWalker(walker);
m_threadPool.start(m_jobs[jobIndex]); m_threadPool.start(m_jobs[jobIndex]);
...@@ -129,9 +147,13 @@ void KisUpdaterContext::addMergeJob(KisBaseRectsWalkerSP walker) ...@@ -129,9 +147,13 @@ void KisUpdaterContext::addMergeJob(KisBaseRectsWalkerSP walker)
*/ */
void KisTestableUpdaterContext::addMergeJob(KisBaseRectsWalkerSP walker) void KisTestableUpdaterContext::addMergeJob(KisBaseRectsWalkerSP walker)
{ {
#ifdef SANITY_CHECK_CONTEXT_LOCKING
KIS_ASSERT(m_lockedBy == QThread::currentThreadId());
#endif
m_lodCounter.addLod(walker->levelOfDetail()); m_lodCounter.addLod(walker->levelOfDetail());
qint32 jobIndex = findSpareThread(); qint32 jobIndex = findSpareThread();
Q_ASSERT(jobIndex >= 0); KIS_ASSERT(jobIndex >= 0);
m_jobs[jobIndex]->setWalker(walker); m_jobs[jobIndex]->setWalker(walker);
// HINT: Not calling start() here // HINT: Not calling start() here
...@@ -139,9 +161,13 @@ void KisTestableUpdaterContext::addMergeJob(KisBaseRectsWalkerSP walker) ...@@ -139,9 +161,13 @@ void KisTestableUpdaterContext::addMergeJob(KisBaseRectsWalkerSP walker)
void KisUpdaterContext::addStrokeJob(KisStrokeJob *strokeJob) void KisUpdaterContext::addStrokeJob(KisStrokeJob *strokeJob)
{ {
#ifdef SANITY_CHECK_CONTEXT_LOCKING
KIS_ASSERT(m_lockedBy == QThread::currentThreadId());
#endif
m_lodCounter.addLod(strokeJob->levelOfDetail()); m_lodCounter.addLod(strokeJob->levelOfDetail());
qint32 jobIndex = findSpareThread(); qint32 jobIndex = findSpareThread();
Q_ASSERT(jobIndex >= 0); KIS_ASSERT(jobIndex >= 0);
m_jobs[jobIndex]->setStrokeJob(strokeJob); m_jobs[jobIndex]->setStrokeJob(strokeJob);
m_threadPool.start(m_jobs[jobIndex]); m_threadPool.start(m_jobs[jobIndex]);
...@@ -152,9 +178,13 @@ void KisUpdaterContext::addStrokeJob(KisStrokeJob *strokeJob) ...@@ -152,9 +178,13 @@ void KisUpdaterContext::addStrokeJob(KisStrokeJob *strokeJob)
*/ */
void KisTestableUpdaterContext::addStrokeJob(KisStrokeJob *strokeJob) void KisTestableUpdaterContext::addStrokeJob(KisStrokeJob *strokeJob)
{ {
#ifdef SANITY_CHECK_CONTEXT_LOCKING
KIS_ASSERT(m_lockedBy == QThread::currentThreadId());
#endif
m_lodCounter.addLod(strokeJob->levelOfDetail()); m_lodCounter.addLod(strokeJob->levelOfDetail());
qint32 jobIndex = findSpareThread(); qint32 jobIndex = findSpareThread();
Q_ASSERT(jobIndex >= 0); KIS_ASSERT(jobIndex >= 0);
m_jobs[jobIndex]->setStrokeJob(strokeJob); m_jobs[jobIndex]->setStrokeJob(strokeJob);
// HINT: Not calling start() here // HINT: Not calling start() here
...@@ -162,9 +192,13 @@ void KisTestableUpdaterContext::addStrokeJob(KisStrokeJob *strokeJob) ...@@ -162,9 +192,13 @@ void KisTestableUpdaterContext::addStrokeJob(KisStrokeJob *strokeJob)
void KisUpdaterContext::addSpontaneousJob(KisSpontaneousJob *spontaneousJob) void KisUpdaterContext::addSpontaneousJob(KisSpontaneousJob *spontaneousJob)
{ {
#ifdef SANITY_CHECK_CONTEXT_LOCKING
KIS_ASSERT(m_lockedBy == QThread::currentThreadId());
#endif
m_lodCounter.addLod(spontaneousJob->levelOfDetail()); m_lodCounter.addLod(spontaneousJob->levelOfDetail());
qint32 jobIndex = findSpareThread(); qint32 jobIndex = findSpareThread();
Q_ASSERT(jobIndex >= 0); KIS_ASSERT(jobIndex >= 0);
m_jobs[jobIndex]->setSpontaneousJob(spontaneousJob); m_jobs[jobIndex]->setSpontaneousJob(spontaneousJob);
m_threadPool.start(m_jobs[jobIndex]); m_threadPool.start(m_jobs[jobIndex]);
...@@ -177,7 +211,7 @@ void KisTestableUpdaterContext::addSpontaneousJob(KisSpontaneousJob *spontaneous ...@@ -177,7 +211,7 @@ void KisTestableUpdaterContext::addSpontaneousJob(KisSpontaneousJob *spontaneous
{ {
m_lodCounter.addLod(spontaneousJob->levelOfDetail()); m_lodCounter.addLod(spontaneousJob->levelOfDetail());
qint32 jobIndex = findSpareThread(); qint32 jobIndex = findSpareThread();
Q_ASSERT(jobIndex >= 0); KIS_ASSERT(jobIndex >= 0);
m_jobs[jobIndex]->setSpontaneousJob(spontaneousJob); m_jobs[jobIndex]->setSpontaneousJob(spontaneousJob);
// HINT: Not calling start() here // HINT: Not calling start() here
...@@ -185,7 +219,35 @@ void KisTestableUpdaterContext::addSpontaneousJob(KisSpontaneousJob *spontaneous ...@@ -185,7 +219,35 @@ void KisTestableUpdaterContext::addSpontaneousJob(KisSpontaneousJob *spontaneous
void KisUpdaterContext::waitForDone() void KisUpdaterContext::waitForDone()
{ {
m_threadPool.waitForDone(); lock();
while(true) {
bool allDone = true;
QVector<KisUpdateJobItem*>::const_iterator iter;
FOREACH_SAFE(iter, m_jobs) {
if ((*iter)->isRunning()) {
allDone = false;
break;
}
}
if (!allDone) {
#ifdef SANITY_CHECK_CONTEXT_LOCKING
m_lockedBy = (Qt::HANDLE) -1;
#endif
m_waitAllCond.wait(&m_lock);
#ifdef SANITY_CHECK_CONTEXT_LOCKING
m_lockedBy = QThread::currentThreadId();
#endif
} else {
break;
}
}
unlock();
} }
bool KisUpdaterContext::walkerIntersectsJob(KisBaseRectsWalkerSP walker, bool KisUpdaterContext::walkerIntersectsJob(KisBaseRectsWalkerSP walker,
...@@ -208,6 +270,7 @@ void KisUpdaterContext::slotJobFinished() ...@@ -208,6 +270,7 @@ void KisUpdaterContext::slotJobFinished()
{ {
m_lodCounter.removeLod(); m_lodCounter.removeLod();
m_waitAllCond.wakeOne();
// Be careful. This slot can be called asynchronously without locks. // Be careful. This slot can be called asynchronously without locks.
emit sigSpareThreadAppeared(); emit sigSpareThreadAppeared();
} }
...@@ -215,10 +278,22 @@ void KisUpdaterContext::slotJobFinished() ...@@ -215,10 +278,22 @@ void KisUpdaterContext::slotJobFinished()
void KisUpdaterContext::lock() void KisUpdaterContext::lock()
{ {
m_lock.lock(); m_lock.lock();
#ifdef SANITY_CHECK_CONTEXT_LOCKING
KIS_ASSERT_X(m_lockedBy == (Qt::HANDLE) -1, "KisUpdaterContext",
"context is already locked");
m_lockedBy = QThread::currentThreadId();
#endif
} }
void KisUpdaterContext::unlock() void KisUpdaterContext::unlock()
{ {
#ifdef SANITY_CHECK_CONTEXT_LOCKING
KIS_ASSERT(m_lockedBy == QThread::currentThreadId());
m_lockedBy = (Qt::HANDLE) -1;
#endif
m_lock.unlock(); m_lock.unlock();
} }
......
...@@ -23,11 +23,16 @@ ...@@ -23,11 +23,16 @@
#include <QMutex> #include <QMutex>
#include <QReadWriteLock> #include <QReadWriteLock>
#include <QThreadPool> #include <QThreadPool>
#include <QWaitCondition>
#include "kis_base_rects_walker.h" #include "kis_base_rects_walker.h"
#include "kis_async_merger.h" #include "kis_async_merger.h"
#include "kis_lock_free_lod_counter.h" #include "kis_lock_free_lod_counter.h"
// TODO: uncomment ifndef for release on 3.0.1
// #ifndef QT_NO_DEBUG
#define SANITY_CHECK_CONTEXT_LOCKING
// #endif // QT_NO_DEBUG
class KisUpdateJobItem; class KisUpdateJobItem;
class KisSpontaneousJob; class KisSpontaneousJob;
...@@ -128,6 +133,9 @@ protected Q_SLOTS: ...@@ -128,6 +133,9 @@ protected Q_SLOTS:
protected: protected:
static bool walkerIntersectsJob(KisBaseRectsWalkerSP walker, static bool walkerIntersectsJob(KisBaseRectsWalkerSP walker,
const KisUpdateJobItem* job); const KisUpdateJobItem* job);
qint32 defaultThreadCount() const;
qint32 findSpareThread(); qint32 findSpareThread();
protected: protected:
...@@ -141,8 +149,14 @@ protected: ...@@ -141,8 +149,14 @@ protected:
QMutex m_lock; QMutex m_lock;
QVector<KisUpdateJobItem*> m_jobs; QVector<KisUpdateJobItem*> m_jobs;
QWaitCondition m_waitAllCond;
QThreadPool m_threadPool; QThreadPool m_threadPool;
KisLockFreeLodCounter m_lodCounter; KisLockFreeLodCounter m_lodCounter;
#ifdef SANITY_CHECK_CONTEXT_LOCKING
// Thread ID of the owner or -1 if not locked
volatile Qt::HANDLE m_lockedBy;
#endif
}; };
class KRITAIMAGE_EXPORT KisTestableUpdaterContext : public KisUpdaterContext class KRITAIMAGE_EXPORT KisTestableUpdaterContext : public KisUpdaterContext
......
...@@ -114,7 +114,9 @@ void KisStrokesQueueTest::testExclusiveStrokes() ...@@ -114,7 +114,9 @@ void KisStrokesQueueTest::testExclusiveStrokes()
KisTestableUpdaterContext context(2); KisTestableUpdaterContext context(2);
QVector<KisUpdateJobItem*> jobs; QVector<KisUpdateJobItem*> jobs;
context.lock();
context.addMergeJob(walker); context.addMergeJob(walker);
context.unlock();
queue.processQueue(context, false); queue.processQueue(context, false);
jobs = context.getJobs(); jobs = context.getJobs();
...@@ -139,7 +141,9 @@ void KisStrokesQueueTest::testExclusiveStrokes() ...@@ -139,7 +141,9 @@ void KisStrokesQueueTest::testExclusiveStrokes()
QCOMPARE(queue.needsExclusiveAccess(), true); QCOMPARE(queue.needsExclusiveAccess(), true);
context.clear(); context.clear();
context.lock();
context.addMergeJob(walker); context.addMergeJob(walker);
context.unlock();
queue.processQueue(context, false); queue.processQueue(context, false);
COMPARE_WALKER(jobs[0], walker); COMPARE_WALKER(jobs[0], walker);
...@@ -201,7 +205,9 @@ void KisStrokesQueueTest::testBarrierStrokeJobs() ...@@ -201,7 +205,9 @@ void KisStrokesQueueTest::testBarrierStrokeJobs()
VERIFY_EMPTY(jobs[2]); VERIFY_EMPTY(jobs[2]);
// Now some updates has come... // Now some updates has come...
context.lock();
context.addMergeJob(walker); context.addMergeJob(walker);
context.unlock();
jobs = context.getJobs(); jobs = context.getJobs();
COMPARE_NAME(jobs[0], "nor_dab"); COMPARE_NAME(jobs[0], "nor_dab");
...@@ -239,7 +245,9 @@ void KisStrokesQueueTest::testBarrierStrokeJobs() ...@@ -239,7 +245,9 @@ void KisStrokesQueueTest::testBarrierStrokeJobs()
VERIFY_EMPTY(jobs[2]); VERIFY_EMPTY(jobs[2]);
// Process the last update... // Process the last update...
context.lock();
context.addMergeJob(walker); context.addMergeJob(walker);
context.unlock();
externalJobsPending = false; externalJobsPending = false;
// Yep, the queue is still waiting // Yep, the queue is still waiting
...@@ -416,7 +424,9 @@ void KisStrokesQueueTest::testStrokesLevelOfDetail() ...@@ -416,7 +424,9 @@ void KisStrokesQueueTest::testStrokesLevelOfDetail()
KisTestableUpdaterContext context(2); KisTestableUpdaterContext context(2);
QVector<KisUpdateJobItem*> jobs; QVector<KisUpdateJobItem*> jobs;
context.lock();
context.addMergeJob(walker); context.addMergeJob(walker);
context.unlock();
queue.processQueue(context, false); queue.processQueue(context, false);
jobs = context.getJobs(); jobs = context.getJobs();
...@@ -439,10 +449,14 @@ void KisStrokesQueueTest::testStrokesLevelOfDetail() ...@@ -439,10 +449,14 @@ void KisStrokesQueueTest::testStrokesLevelOfDetail()
QCOMPARE(queue.needsExclusiveAccess(), false); QCOMPARE(queue.needsExclusiveAccess(), false);
// walker of a different LOD must not be allowed // walker of a different LOD must not be allowed
context.lock();
QCOMPARE(context.isJobAllowed(walker), false); QCOMPARE(context.isJobAllowed(walker), false);
context.unlock();
context.clear(); context.clear();
context.lock();
context.addMergeJob(walker); context.addMergeJob(walker);
context.unlock();
queue.processQueue(context, false); queue.processQueue(context, false);
jobs = context.getJobs(); jobs = context.getJobs();
......
...@@ -223,7 +223,9 @@ void KisUpdaterContextTest::stressTestExclusiveJobs() ...@@ -223,7 +223,9 @@ void KisUpdaterContextTest::stressTestExclusiveJobs()
KisStrokeJobStrategy *strategy = KisStrokeJobStrategy *strategy =
new ExclusivenessCheckerStrategy(counter, hadConcurrency); new ExclusivenessCheckerStrategy(counter, hadConcurrency);
context.lock();
context.addStrokeJob(new KisStrokeJob(strategy, data, 0, true)); context.addStrokeJob(new KisStrokeJob(strategy, data, 0, true));
context.unlock();
} }
else { else {
QTest::qSleep(CHECK_DELAY); QTest::qSleep(CHECK_DELAY);
......
...@@ -289,7 +289,7 @@ void KisTemplateCreateDia::slotOk() { ...@@ -289,7 +289,7 @@ void KisTemplateCreateDia::slotOk() {
while (QFile(dest).exists()); while (QFile(dest).exists());
} }
bool ignore = false; bool ignore = false;
KisTemplate *t = new KisTemplate(d->m_name->text(), QString(), ".source/ "+ file + ext, tmpIcon, "", "", false, true); KisTemplate *t = new KisTemplate(d->m_name->text(), QString(), ".source/"+ file + ext, tmpIcon, "", "", false, true);
if (!group->add(t)) { if (!group->add(t)) {
KisTemplate *existingTemplate=group->find(d->m_name->text()); KisTemplate *existingTemplate=group->find(d->m_name->text());
if (existingTemplate && !existingTemplate->isHidden()) { if (existingTemplate && !existingTemplate->isHidden()) {
......
...@@ -166,7 +166,17 @@ QRect KisNodeViewColorScheme::relExpandButtonRect() const ...@@ -166,7 +166,17 @@ QRect KisNodeViewColorScheme::relExpandButtonRect() const
QColor KisNodeViewColorScheme::colorLabel(int index) const QColor KisNodeViewColorScheme::colorLabel(int index) const
{ {
return m_d->colorLabels[index % m_d->colorLabels.size()]; /**
* We should ensure that the index of the overflowing range
* will never be zero again.
*/
if (index >= m_d->colorLabels.size()) {
index = 1 + index % (m_d->colorLabels.size() - 1);
} else {
index = index % m_d->colorLabels.size();
}
return m_d->colorLabels[index];
} }
QVector<QColor> KisNodeViewColorScheme::allColorLabels() const QVector<QColor> KisNodeViewColorScheme::allColorLabels() const
......
...@@ -78,6 +78,7 @@ ...@@ -78,6 +78,7 @@
#include "kis_guides_config.h" #include "kis_guides_config.h"
#include "kis_image_config.h" #include "kis_image_config.h"
#include "KisProofingConfiguration.h" #include "KisProofingConfiguration.h"
#include "kis_node_view_color_scheme.h"
/* /*
...@@ -652,7 +653,11 @@ KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageWSP image, ...@@ -652,7 +653,11 @@ KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageWSP image,
const bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true; const bool visible = element.attribute(VISIBLE, "1") == "0" ? false : true;
const bool locked = element.attribute(LOCKED, "0") == "0" ? false : true; const bool locked = element.attribute(LOCKED, "0") == "0" ? false : true;
const bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true; const bool collapsed = element.attribute(COLLAPSED, "0") == "0" ? false : true;
const int colorLabelIndex = element.attribute(COLOR_LABEL, "0").toInt(); int colorLabelIndex = element.attribute(COLOR_LABEL, "0").toInt();
QVector<QColor> labels = KisNodeViewColorScheme::instance()->allColorLabels();
if (colorLabelIndex >= labels.size()) {
colorLabelIndex = labels.size() - 1;
}
// Now find out the layer type and do specific handling // Now find out the layer type and do specific handling
QString nodeType; QString nodeType;
......
...@@ -272,7 +272,7 @@ void KisPainterBasedStrokeStrategy::cancelStrokeCallback() ...@@ -272,7 +272,7 @@ void KisPainterBasedStrokeStrategy::cancelStrokeCallback()
KisIndirectPaintingSupport *indirect = KisIndirectPaintingSupport *indirect =
dynamic_cast<KisIndirectPaintingSupport*>(node.data()); dynamic_cast<KisIndirectPaintingSupport*>(node.data());
bool revert = true;
if (indirect) { if (indirect) {
KisPaintDeviceSP t = indirect->temporaryTarget(); KisPaintDeviceSP t = indirect->temporaryTarget();
if (t) { if (t) {
...@@ -282,8 +282,11 @@ void KisPainterBasedStrokeStrategy::cancelStrokeCallback() ...@@ -282,8 +282,11 @@ void KisPainterBasedStrokeStrategy::cancelStrokeCallback()
QRegion region = t->region(); QRegion region = t->region();
indirect->setTemporaryTarget(0); indirect->setTemporaryTarget(0);
node->setDirty(region); node->setDirty(region);
revert = false;
} }
} else { }
if (revert) {
m_transaction->revert(); m_transaction->revert();
delete m_transaction; delete m_transaction;
deletePainters(); deletePainters();
......
...@@ -52,72 +52,76 @@ ...@@ -52,72 +52,76 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_5"> <layout class="QGridLayout" name="gridLayout_2">
<item> <item row="0" column="0">
<layout class="QFormLayout" name="formLayout"> <widget class="QLabel" name="lblXSpacing">
<item row="0" column="0"> <property name="sizePolicy">
<widget class="QLabel" name="lblXSpacing"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<property name="text"> <horstretch>0</horstretch>
<string>X spacing:</string> <verstretch>0</verstretch>
</property> </sizepolicy>
</widget> </property>
</item> <property name="text">
<item row="0" column="1"> <string>X spacing:</string>
<widget class="KisIntParseSpinBox" name="intHSpacing"> </property>
<property name="suffix"> </widget>
<string> px</string> </item>
</property> <item row="0" column="1">
<property name="minimum"> <widget class="KisIntParseSpinBox" name="intHSpacing">
<number>1</number> <property name="suffix">
</property> <string> px</string>
<property name="maximum"> </property>