diff --git a/krita/krita.action b/krita/krita.action --- a/krita/krita.action +++ b/krita/krita.action @@ -355,6 +355,17 @@ true + + transform_icons_penPressure + Use Pen Pressure + + Use Pen Pressure + Use Pen Pressure + 10000 + + true + + symmetry-horizontal Horizontal Mirror Tool diff --git a/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.png b/krita/pics/tool_transform/dark_transform_icons_penPressure_locked.png new file mode 100644 index 0000000000000000000000000000000000000000..f18f35ffec6143d4be825a8c4a41f9ccee06a649 GIT binary patch literal 3440 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 literal 0 Hc$@ - - + + dark_transform_icons_cage.png dark_transform_icons_liquify_erase.png dark_transform_icons_liquify_main.png @@ -18,7 +17,6 @@ dark_transform_icons_mirror_y.svg dark_transform_icons_rotate_cw.svg dark_transform_icons_rotate_ccw.svg - light_transform_icons_cage.png light_transform_icons_liquify_erase.png light_transform_icons_liquify_main.png @@ -36,5 +34,7 @@ light_transform_icons_mirror_y.svg light_transform_icons_rotate_cw.svg light_transform_icons_rotate_ccw.svg + light_transform_icons_penPressure_locked.png + dark_transform_icons_penPressure_locked.png diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt --- a/libs/flake/CMakeLists.txt +++ b/libs/flake/CMakeLists.txt @@ -16,6 +16,7 @@ KoCanvasBase.cpp KoResourceManager_p.cpp KoDerivedResourceConverter.cpp + KoResourceUpdateMediator.cpp KoCanvasResourceManager.cpp KoDocumentResourceManager.cpp KoCanvasObserverBase.cpp diff --git a/libs/flake/KoCanvasResourceManager.h b/libs/flake/KoCanvasResourceManager.h --- a/libs/flake/KoCanvasResourceManager.h +++ b/libs/flake/KoCanvasResourceManager.h @@ -25,6 +25,7 @@ #include "kritaflake_export.h" #include "KoDerivedResourceConverter.h" +#include "KoResourceUpdateMediator.h" class KoShape; class KoShapeStroke; @@ -250,6 +251,22 @@ */ void removeDerivedResourceConverter(int key); + /** + * @see KoReosurceManager::addResourceUpdateMediator + */ + void addResourceUpdateMediator(KoResourceUpdateMediatorSP mediator); + + /** + * @see KoReosurceManager::hasResourceUpdateMediator + */ + bool hasResourceUpdateMediator(int key); + + /** + + * @see KoReosurceManager::removeResourceUpdateMediator + */ + void removeResourceUpdateMediator(int key); + Q_SIGNALS: /** * This signal is emitted every time a resource is set that is either diff --git a/libs/flake/KoCanvasResourceManager.cpp b/libs/flake/KoCanvasResourceManager.cpp --- a/libs/flake/KoCanvasResourceManager.cpp +++ b/libs/flake/KoCanvasResourceManager.cpp @@ -180,3 +180,18 @@ { d->manager.removeDerivedResourceConverter(key); } + +void KoCanvasResourceManager::addResourceUpdateMediator(KoResourceUpdateMediatorSP mediator) +{ + d->manager.addResourceUpdateMediator(mediator); +} + +bool KoCanvasResourceManager::hasResourceUpdateMediator(int key) +{ + return d->manager.hasResourceUpdateMediator(key); +} + +void KoCanvasResourceManager::removeResourceUpdateMediator(int key) +{ + d->manager.removeResourceUpdateMediator(key); +} diff --git a/libs/flake/KoDerivedResourceConverter.h b/libs/flake/KoDerivedResourceConverter.h --- a/libs/flake/KoDerivedResourceConverter.h +++ b/libs/flake/KoDerivedResourceConverter.h @@ -49,6 +49,14 @@ int key() const; int sourceKey() const; + QVariant readFromSource(const QVariant &value); + QVariant writeToSource(const QVariant &value, + const QVariant &sourceValue, + bool *changed); + + virtual bool notifySourceChanged(const QVariant &sourceValue); + +protected: /** * Converts the \p value of the source resource into the space of * the "derived" resource. E.g. preset -> opacity. diff --git a/libs/flake/KoDerivedResourceConverter.cpp b/libs/flake/KoDerivedResourceConverter.cpp --- a/libs/flake/KoDerivedResourceConverter.cpp +++ b/libs/flake/KoDerivedResourceConverter.cpp @@ -19,15 +19,17 @@ #include "KoDerivedResourceConverter.h" #include "QVariant" - +#include "kis_assert.h" struct KoDerivedResourceConverter::Private { Private(int _key, int _sourceKey) : key(_key), sourceKey(_sourceKey) {} int key; int sourceKey; + + QVariant lastKnownValue; }; @@ -50,3 +52,42 @@ return m_d->sourceKey; } +bool KoDerivedResourceConverter::notifySourceChanged(const QVariant &sourceValue) +{ + const QVariant newValue = fromSource(sourceValue); + + const bool valueChanged = m_d->lastKnownValue != newValue; + m_d->lastKnownValue = newValue; + + return valueChanged; +} + +QVariant KoDerivedResourceConverter::readFromSource(const QVariant &sourceValue) +{ + const QVariant result = fromSource(sourceValue); + + KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->lastKnownValue.isNull() || + result == m_d->lastKnownValue); + + m_d->lastKnownValue = result; + + return m_d->lastKnownValue; +} + +QVariant KoDerivedResourceConverter::writeToSource(const QVariant &value, + const QVariant &sourceValue, + bool *changed) +{ + if (changed) { + *changed = m_d->lastKnownValue != value; + } + + QVariant newSourceValue = sourceValue; + + if (*changed || value != fromSource(sourceValue)) { + m_d->lastKnownValue = value; + newSourceValue = toSource(value, sourceValue); + } + + return newSourceValue; +} diff --git a/libs/flake/KoResourceManager_p.h b/libs/flake/KoResourceManager_p.h --- a/libs/flake/KoResourceManager_p.h +++ b/libs/flake/KoResourceManager_p.h @@ -29,6 +29,8 @@ #include #include #include "KoDerivedResourceConverter.h" +#include "KoResourceUpdateMediator.h" + class KoShape; class QVariant; @@ -144,15 +146,15 @@ void clearResource(int key); /** - * Some of the resources may be "derive" from the other. For + * Some of the resources may be "derived" from the others. For * example opacity, composite op and erase mode properties are * contained inside a paintop preset, so we need not create a * separate resource for them. Instead we created a derived resource, * that loads/saves values from/to another resource, but has its own * "resource changed" signal (via a different key). * * When a parent resource changes, the resource manager emits - * update signals for all its derived resources. + * update signals for all its derived resources. */ void addDerivedResourceConverter(KoDerivedResourceConverterSP converter); @@ -172,11 +174,35 @@ */ void removeDerivedResourceConverter(int key); + /** + * Some resources can "mutate", that is their value doesn't change + * (a pointer), whereas the contents changes. But we should still + * notify all the derived resources subscribers that the resource + * has changed. For that purpose we use a special mediator class + * that connects the resource (which is not a QObject at all) and + * the resource manager controls that connection. + */ + void addResourceUpdateMediator(KoResourceUpdateMediatorSP mediator); + + /** + * \see addResourceUpdateMediator() + */ + bool hasResourceUpdateMediator(int key); + + /** + * \see addResourceUpdateMediator() + */ + void removeResourceUpdateMediator(int key); + Q_SIGNALS: void resourceChanged(int key, const QVariant &value); private: void notifyResourceChanged(int key, const QVariant &value); + void notifyDerivedResourcesChanged(int key, const QVariant &value); + +private Q_SLOTS: + void slotResourceInternalsChanged(int key); private: KoResourceManager(const KoResourceManager&); @@ -186,6 +212,8 @@ QHash m_derivedResources; QMultiHash m_derivedFromSource; + + QHash m_updateMediators; }; #endif diff --git a/libs/flake/KoResourceManager_p.cpp b/libs/flake/KoResourceManager_p.cpp --- a/libs/flake/KoResourceManager_p.cpp +++ b/libs/flake/KoResourceManager_p.cpp @@ -25,43 +25,68 @@ #include #include "KoShape.h" +#include "kis_assert.h" +#include "kis_debug.h" -void KoResourceManager::setResource(int key, const QVariant &value) +void KoResourceManager::slotResourceInternalsChanged(int key) { - QVariant realValue = value; - int realKey = key; - QVariant currentValue = m_resources.value(key, QVariant()); + KIS_SAFE_ASSERT_RECOVER_RETURN(m_resources.contains(key)); + notifyDerivedResourcesChanged(key, m_resources[key]); +} +void KoResourceManager::setResource(int key, const QVariant &value) +{ KoDerivedResourceConverterSP converter = m_derivedResources.value(key, KoDerivedResourceConverterSP()); if (converter) { - realKey = converter->sourceKey(); - currentValue = m_resources.value(realKey, QVariant()); - realValue = converter->toSource(value, currentValue); - } + const int sourceKey = converter->sourceKey(); + const QVariant oldSourceValue = m_resources.value(sourceKey, QVariant()); + + bool valueChanged = false; + const QVariant newSourceValue = converter->writeToSource(value, oldSourceValue, &valueChanged); + + if (valueChanged) { + notifyResourceChanged(key, value); + } + + if (oldSourceValue != newSourceValue) { + m_resources[sourceKey] = newSourceValue; + notifyResourceChanged(sourceKey, newSourceValue); + } - if (m_resources.contains(realKey)) { - if (!converter && currentValue == realValue) - return; - m_resources[realKey] = realValue; } else { - m_resources.insert(realKey, realValue); - } + const QVariant oldValue = m_resources.value(key, QVariant()); + m_resources[key] = value; - notifyResourceChanged(key, value); + if (m_updateMediators.contains(key)) { + m_updateMediators[key]->connectResource(value); + } + + if (oldValue != value) { + notifyResourceChanged(key, value); + } + } } void KoResourceManager::notifyResourceChanged(int key, const QVariant &value) { emit resourceChanged(key, value); + notifyDerivedResourcesChanged(key, value); +} +void KoResourceManager::notifyDerivedResourcesChanged(int key, const QVariant &value) +{ QMultiHash::const_iterator it = m_derivedFromSource.constFind(key); QMultiHash::const_iterator end = m_derivedFromSource.constEnd(); while (it != end && it.key() == key) { KoDerivedResourceConverterSP converter = it.value(); - notifyResourceChanged(converter->key(), converter->fromSource(value)); + + if (converter->notifySourceChanged(value)) { + notifyResourceChanged(converter->key(), converter->readFromSource(value)); + } + it++; } } @@ -74,7 +99,7 @@ const int realKey = converter ? converter->sourceKey() : key; QVariant value = m_resources.value(realKey, QVariant()); - return converter ? converter->fromSource(value) : value; + return converter ? converter->readFromSource(value) : value; } void KoResourceManager::setResource(int key, const KoColor &color) @@ -175,7 +200,7 @@ void KoResourceManager::addDerivedResourceConverter(KoDerivedResourceConverterSP converter) { - Q_ASSERT(!m_derivedResources.contains(converter->key())); + KIS_SAFE_ASSERT_RECOVER_NOOP(!m_derivedResources.contains(converter->key())); m_derivedResources.insert(converter->key(), converter); m_derivedFromSource.insertMulti(converter->sourceKey(), converter); @@ -194,3 +219,21 @@ m_derivedResources.remove(key); m_derivedFromSource.remove(converter->sourceKey(), converter); } + +void KoResourceManager::addResourceUpdateMediator(KoResourceUpdateMediatorSP mediator) +{ + KIS_SAFE_ASSERT_RECOVER_NOOP(!m_updateMediators.contains(mediator->key())); + m_updateMediators.insert(mediator->key(), mediator); + connect(mediator.data(), SIGNAL(sigResourceChanged(int)), SLOT(slotResourceInternalsChanged(int))); +} + +bool KoResourceManager::hasResourceUpdateMediator(int key) +{ + return m_updateMediators.contains(key); +} + +void KoResourceManager::removeResourceUpdateMediator(int key) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN(m_updateMediators.contains(key)); + m_updateMediators.remove(key); +} diff --git a/libs/flake/KoResourceUpdateMediator.h b/libs/flake/KoResourceUpdateMediator.h new file mode 100644 --- /dev/null +++ b/libs/flake/KoResourceUpdateMediator.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Dmitry Kazakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __KO_RESOURCE_UPDATE_MEDIATOR_H +#define __KO_RESOURCE_UPDATE_MEDIATOR_H + +#include +#include +#include "kritaflake_export.h" + +/** + * A special mediator class that connects the resource and the + * resource manager. The resource manager connects to a + * sigResourceChanged() changed and when a resource changes, the + * manager calls connectResource() for this resource. After that, the + * mediator should notify the manager about every change that happens + * to the resource by emitting the corresponding signal. + * + * There is only one mediator for one type (key) of the resource. + */ + +class KRITAFLAKE_EXPORT KoResourceUpdateMediator : public QObject +{ + Q_OBJECT +public: + KoResourceUpdateMediator(int key); + virtual ~KoResourceUpdateMediator(); + + int key() const; + virtual void connectResource(QVariant sourceResource) = 0; + +Q_SIGNALS: + void sigResourceChanged(int key); + +private: + struct Private; + const QScopedPointer m_d; +}; + +typedef QSharedPointer KoResourceUpdateMediatorSP; + +#endif /* __KO_RESOURCE_UPDATE_MEDIATOR_H */ diff --git a/plugins/paintops/particle/kis_particle_paintop_settings.h b/libs/flake/KoResourceUpdateMediator.cpp copy from plugins/paintops/particle/kis_particle_paintop_settings.h copy to libs/flake/KoResourceUpdateMediator.cpp --- a/plugins/paintops/particle/kis_particle_paintop_settings.h +++ b/libs/flake/KoResourceUpdateMediator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Lukáš Tvrdý + * Copyright (c) 2016 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,20 +16,26 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef KIS_PARTICLE_PAINTOP_SETTINGS_H_ -#define KIS_PARTICLE_PAINTOP_SETTINGS_H_ +#include "KoResourceUpdateMediator.h" -#include -#include -class KisParticlePaintOpSettings : public KisPaintOpSettings +struct KoResourceUpdateMediator::Private { + Private(int _key) : key(_key) {} + int key; +}; -public: - bool paintIncremental(); - bool isAirbrushing() const; - int rate() const; -}; +KoResourceUpdateMediator::KoResourceUpdateMediator(int key) + : m_d(new Private(key)) +{ +} -#endif +KoResourceUpdateMediator::~KoResourceUpdateMediator() +{ +} + +int KoResourceUpdateMediator::key() const +{ + return m_d->key; +} diff --git a/libs/flake/tests/TestResourceManager.h b/libs/flake/tests/TestResourceManager.h --- a/libs/flake/tests/TestResourceManager.h +++ b/libs/flake/tests/TestResourceManager.h @@ -30,6 +30,7 @@ void testUnitChanged(); void testConverters(); void testDerivedChanged(); + void testComplexResource(); }; #endif // TESTRESOURCEMANAGER_H diff --git a/libs/flake/tests/TestResourceManager.cpp b/libs/flake/tests/TestResourceManager.cpp --- a/libs/flake/tests/TestResourceManager.cpp +++ b/libs/flake/tests/TestResourceManager.cpp @@ -26,6 +26,8 @@ #include #include +#include "kis_debug.h" + void TestResourceManager::koShapeResource() { KoPathShape * shape = new KoPathShape(); @@ -118,27 +120,270 @@ m.setResource(derivedKey, 16); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.count(), 3); QList args; args = spy[0]; QCOMPARE(args[0].toInt(), derivedKey); QCOMPARE(args[1].toInt(), 16); + args = spy[1]; + QCOMPARE(args[0].toInt(), key2); + QCOMPARE(args[1].toInt(), 6); + + args = spy[2]; + QCOMPARE(args[0].toInt(), otherDerivedKey); + QCOMPARE(args[1].toInt(), 16); + + spy.clear(); + m.setResource(key2, 7); - QCOMPARE(spy.count(), 4); + QCOMPARE(spy.count(), 3); - args = spy[1]; + args = spy[0]; QCOMPARE(args[0].toInt(), key2); QCOMPARE(args[1].toInt(), 7); - args = spy[2]; + args = spy[1]; QCOMPARE(args[0].toInt(), otherDerivedKey); QCOMPARE(args[1].toInt(), 17); - args = spy[3]; + args = spy[2]; QCOMPARE(args[0].toInt(), derivedKey); QCOMPARE(args[1].toInt(), 17); } +struct ComplexResource { + QHash m_resources; +}; + +typedef QSharedPointer ComplexResourceSP; +Q_DECLARE_METATYPE(ComplexResourceSP); + +struct ComplexConverter : public KoDerivedResourceConverter +{ + ComplexConverter(int key, int sourceKey) : KoDerivedResourceConverter(key, sourceKey) {} + + QVariant fromSource(const QVariant &value) { + KIS_ASSERT(value.canConvert()); + ComplexResourceSP res = value.value(); + + return res->m_resources[key()]; + } + + QVariant toSource(const QVariant &value, const QVariant &sourceValue) { + KIS_ASSERT(sourceValue.canConvert()); + ComplexResourceSP res = sourceValue.value(); + + res->m_resources[key()] = value; + return QVariant::fromValue(res); + } +}; + +struct ComplexMediator : public KoResourceUpdateMediator +{ + ComplexMediator(int key) : KoResourceUpdateMediator(key) {} + + void connectResource(QVariant sourceResource) { + m_res = sourceResource; + } + + void forceNotify() { + emit sigResourceChanged(key()); + } + + QVariant m_res; +}; +typedef QSharedPointer ComplexMediatorSP; + +void TestResourceManager::testComplexResource() +{ + const int key = 2; + const int complex1 = 3; + const int complex2 = 4; + + KoCanvasResourceManager m(0); + m.addDerivedResourceConverter(toQShared(new ComplexConverter(complex1, key))); + m.addDerivedResourceConverter(toQShared(new ComplexConverter(complex2, key))); + + ComplexMediatorSP mediator(new ComplexMediator(key)); + m.addResourceUpdateMediator(mediator); + + QSignalSpy spy(&m, SIGNAL(canvasResourceChanged(int, const QVariant &))); + + ComplexResourceSP r1(new ComplexResource()); + r1->m_resources[complex1] = 10; + r1->m_resources[complex2] = 20; + + ComplexResourceSP r2(new ComplexResource()); + r2->m_resources[complex1] = 15; + r2->m_resources[complex2] = 25; + + + // #################################################### + // Initial assignment + // #################################################### + m.setResource(key, QVariant::fromValue(r1)); + + QCOMPARE(mediator->m_res.value(), r1); + QCOMPARE(m.resource(key).value(), r1); + QCOMPARE(m.resource(complex1).toInt(), 10); + QCOMPARE(m.resource(complex2).toInt(), 20); + + QCOMPARE(spy[0][0].toInt(), key); + QCOMPARE(spy[0][1].value(), r1); + QCOMPARE(spy[1][0].toInt(), complex2); + QCOMPARE(spy[1][1].toInt(), 20); + QCOMPARE(spy[2][0].toInt(), complex1); + QCOMPARE(spy[2][1].toInt(), 10); + spy.clear(); + + // #################################################### + // Change the whole resource + // #################################################### + m.setResource(key, QVariant::fromValue(r2)); + + QCOMPARE(mediator->m_res.value(), r2); + QCOMPARE(m.resource(key).value(), r2); + QCOMPARE(m.resource(complex1).toInt(), 15); + QCOMPARE(m.resource(complex2).toInt(), 25); + + QCOMPARE(spy[0][0].toInt(), key); + QCOMPARE(spy[0][1].value(), r2); + QCOMPARE(spy[1][0].toInt(), complex2); + QCOMPARE(spy[1][1].toInt(), 25); + QCOMPARE(spy[2][0].toInt(), complex1); + QCOMPARE(spy[2][1].toInt(), 15); + spy.clear(); + + // #################################################### + // Change a derived resource + // #################################################### + m.setResource(complex1, 16); + + QCOMPARE(mediator->m_res.value(), r2); + QCOMPARE(m.resource(key).value(), r2); + QCOMPARE(m.resource(complex1).toInt(), 16); + QCOMPARE(m.resource(complex2).toInt(), 25); + + QCOMPARE(spy[0][0].toInt(), complex1); + QCOMPARE(spy[0][1].toInt(), 16); + spy.clear(); + + // #################################################### + // Change another derived resource + // #################################################### + m.setResource(complex2, 26); + + QCOMPARE(mediator->m_res.value(), r2); + QCOMPARE(m.resource(key).value(), r2); + QCOMPARE(m.resource(complex1).toInt(), 16); + QCOMPARE(m.resource(complex2).toInt(), 26); + + QCOMPARE(spy[0][0].toInt(), complex2); + QCOMPARE(spy[0][1].toInt(), 26); + spy.clear(); + + // #################################################### + // Switch back the whole source resource + // #################################################### + m.setResource(key, QVariant::fromValue(r1)); + + QCOMPARE(mediator->m_res.value(), r1); + QCOMPARE(m.resource(key).value(), r1); + QCOMPARE(m.resource(complex1).toInt(), 10); + QCOMPARE(m.resource(complex2).toInt(), 20); + + QCOMPARE(spy[0][0].toInt(), key); + QCOMPARE(spy[0][1].value(), r1); + QCOMPARE(spy[1][0].toInt(), complex2); + QCOMPARE(spy[1][1].toInt(), 20); + QCOMPARE(spy[2][0].toInt(), complex1); + QCOMPARE(spy[2][1].toInt(), 10); + spy.clear(); + + // #################################################### + // The value keep unchanged case! + // #################################################### + m.setResource(complex1, 10); + + QCOMPARE(mediator->m_res.value(), r1); + QCOMPARE(m.resource(key).value(), r1); + QCOMPARE(m.resource(complex1).toInt(), 10); + QCOMPARE(m.resource(complex2).toInt(), 20); + + QCOMPARE(spy.size(), 0); + spy.clear(); + + // #################################################### + // While switching a complex resource one derived value + // is kept unchanged + // #################################################### + r2->m_resources[complex1] = 10; + m.setResource(key, QVariant::fromValue(r2)); + + QCOMPARE(mediator->m_res.value(), r2); + QCOMPARE(m.resource(key).value(), r2); + QCOMPARE(m.resource(complex1).toInt(), 10); + QCOMPARE(m.resource(complex2).toInt(), 26); + + QCOMPARE(spy[0][0].toInt(), key); + QCOMPARE(spy[0][1].value(), r2); + QCOMPARE(spy[1][0].toInt(), complex2); + QCOMPARE(spy[1][1].toInt(), 26); + spy.clear(); + + // #################################################### + // No devived values are changed! + // #################################################### + *r1 = *r2; + m.setResource(key, QVariant::fromValue(r1)); + + QCOMPARE(mediator->m_res.value(), r1); + QCOMPARE(m.resource(key).value(), r1); + QCOMPARE(m.resource(complex1).toInt(), 10); + QCOMPARE(m.resource(complex2).toInt(), 26); + + QCOMPARE(spy[0][0].toInt(), key); + QCOMPARE(spy[0][1].value(), r1); + spy.clear(); + + // #################################################### + // Try to set the same pointer. No signals emitted! + // #################################################### + m.setResource(key, QVariant::fromValue(r1)); + + QCOMPARE(mediator->m_res.value(), r1); + QCOMPARE(m.resource(key).value(), r1); + QCOMPARE(m.resource(complex1).toInt(), 10); + QCOMPARE(m.resource(complex2).toInt(), 26); + + QCOMPARE(spy.size(), 0); + spy.clear(); + + + // #################################################### + // Internals 'officially' changed, but the values not + // #################################################### + mediator->forceNotify(); + + QCOMPARE(spy.size(), 0); + spy.clear(); + + // #################################################### + // We changed the values, but didn't notify anyone :) + // #################################################### + r1->m_resources[complex1] = 11; + r1->m_resources[complex2] = 21; + + mediator->forceNotify(); + + QCOMPARE(spy[0][0].toInt(), complex2); + QCOMPARE(spy[0][1].toInt(), 21); + QCOMPARE(spy[1][0].toInt(), complex1); + QCOMPARE(spy[1][1].toInt(), 11); + spy.clear(); +} + + QTEST_MAIN(TestResourceManager) diff --git a/libs/global/kis_global.h b/libs/global/kis_global.h --- a/libs/global/kis_global.h +++ b/libs/global/kis_global.h @@ -251,6 +251,47 @@ return newList; } + +/** + * Convert a list of strong pointers into a list of weak pointers + */ +template