diff --git a/libs/resources/KisMemoryStorage.cpp b/libs/resources/KisMemoryStorage.cpp index 6a24545a05..f4bcb504f7 100644 --- a/libs/resources/KisMemoryStorage.cpp +++ b/libs/resources/KisMemoryStorage.cpp @@ -1,197 +1,197 @@ /* * Copyright (C) 2018 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMemoryStorage.h" #include #include #include #include #include #include class KisMemoryStorage::Private { public: QHash > resources; QHash > tags; }; class MemoryTagIterator : public KisResourceStorage::TagIterator { public: MemoryTagIterator(QVector tags, const QString &resourceType) : m_tags(tags) , m_resourceType(resourceType) { } bool hasNext() const override { return m_currentPosition < m_tags.size(); } void next() const override { const_cast(this)->m_currentPosition += 1; } QString url() const override { return tag() ? tag()->url() : QString(); } QString name() const override { return tag() ? tag()->name() : QString(); } QString comment() const override {return tag() ? tag()->comment() : QString(); } KisTagSP tag() const override { if (m_currentPosition >= m_tags.size()) return 0; return m_tags.at(m_currentPosition); } private: int m_currentPosition {0}; QVector m_tags; QString m_resourceType; }; class MemoryItem : public KisResourceStorage::ResourceItem { public: ~MemoryItem() override {} }; class MemoryIterator : public KisResourceStorage::ResourceIterator { public: MemoryIterator(QVector resources, const QString &resourceType) : m_resources(resources) , m_resourceType(resourceType) { } ~MemoryIterator() override {} bool hasNext() const override { return m_currentPosition < m_resources.size(); } void next() const override { const_cast(this)->m_currentPosition++; } QString url() const override { if (resource()) { return resource()->filename(); } return QString(); } QString type() const override { return m_resourceType; } QDateTime lastModified() const override { return QDateTime::fromMSecsSinceEpoch(0); } KoResourceSP resource() const override { if (m_currentPosition >= m_resources.size()) return 0; return m_resources.at(m_currentPosition); } private: int m_currentPosition {0}; QVector m_resources; QString m_resourceType; }; -KisMemoryStorage::KisMemoryStorage() - : KisStoragePlugin("memory") +KisMemoryStorage::KisMemoryStorage(const QString &name) + : KisStoragePlugin(name) , d(new Private()) { } KisMemoryStorage::~KisMemoryStorage() { } bool KisMemoryStorage::addTag(const QString &resourceType, KisTagSP tag) { if (!d->tags.contains(resourceType)) { d->tags[resourceType] = QVector(); } if (!d->tags[resourceType].contains(tag)) { d->tags[resourceType].append(tag); } return true; } bool KisMemoryStorage::addResource(const QString &resourceType, KoResourceSP resource) { if (!d->resources.contains(resourceType)) { d->resources[resourceType] = QVector(); } if (!d->resources[resourceType].contains(resource)) { d->resources[resourceType].append(resource); } return true; } KisResourceStorage::ResourceItem KisMemoryStorage::resourceItem(const QString &url) { MemoryItem item; item.url = url; item.folder = QString(); item.lastModified = QDateTime::fromMSecsSinceEpoch(0); return item; } KoResourceSP KisMemoryStorage::resource(const QString &url) { KoResourceSP resource; QFileInfo fi(location() + '/' + url); const QString resourceType = fi.path().split("/").last(); Q_FOREACH(resource, d->resources[resourceType]) { if (resource->filename() == url) { break; } } return resource; } QSharedPointer KisMemoryStorage::resources(const QString &resourceType) { return QSharedPointer(new MemoryIterator(d->resources[resourceType], resourceType)); } QSharedPointer KisMemoryStorage::tags(const QString &resourceType) { return QSharedPointer(new MemoryTagIterator(d->tags[resourceType], resourceType)); } diff --git a/libs/resources/KisMemoryStorage.h b/libs/resources/KisMemoryStorage.h index 7d650d250f..fe5226e221 100644 --- a/libs/resources/KisMemoryStorage.h +++ b/libs/resources/KisMemoryStorage.h @@ -1,51 +1,52 @@ /* * Copyright (C) 2018 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KISMEMORYSTORAGE_H #define KISMEMORYSTORAGE_H #include #include /** * @brief The KisMemoryStorage class stores the temporary resources * that are not saved to disk or bundle. */ class KRITARESOURCES_EXPORT KisMemoryStorage : public KisStoragePlugin { public: - KisMemoryStorage(); + KisMemoryStorage(const QString &name = QString("memory")); virtual ~KisMemoryStorage(); bool addTag(const QString &resourceType, KisTagSP tag); bool addResource(const QString &resourceType, KoResourceSP resource); KisResourceStorage::ResourceItem resourceItem(const QString &url) override; KoResourceSP resource(const QString &url) override; QSharedPointer resources(const QString &resourceType) override; QSharedPointer tags(const QString &resourceType) override; private: class Private; QScopedPointer d; }; + #endif // KISMEMORYSTORAGE_H diff --git a/libs/resources/KisResourceLocator.cpp b/libs/resources/KisResourceLocator.cpp index 3115b698cf..ccd56eea3a 100644 --- a/libs/resources/KisResourceLocator.cpp +++ b/libs/resources/KisResourceLocator.cpp @@ -1,509 +1,555 @@ /* * Copyright (C) 2018 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisResourceLocator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoResourcePaths.h" #include "KisResourceStorage.h" #include "KisResourceCacheDb.h" #include "KisResourceLoaderRegistry.h" #include "KisMemoryStorage.h" +#include "KisResourceModelProvider.h" const QString KisResourceLocator::resourceLocationKey {"ResourceDirectory"}; class KisResourceLocator::Private { public: QString resourceLocation; QMap storages; QHash, KoResourceSP> resourceCache; QStringList errorMessages; }; KisResourceLocator::KisResourceLocator(QObject *parent) : QObject(parent) , d(new Private()) { } KisResourceLocator *KisResourceLocator::instance() { KisResourceLocator *locator = qApp->findChild(QString()); if (!locator) { locator = new KisResourceLocator(qApp); } return locator; } KisResourceLocator::~KisResourceLocator() { } KisResourceLocator::LocatorError KisResourceLocator::initialize(const QString &installationResourcesLocation) { InitalizationStatus initalizationStatus = InitalizationStatus::Unknown; KConfigGroup cfg(KSharedConfig::openConfig(), ""); d->resourceLocation = cfg.readEntry(resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); if (!d->resourceLocation.endsWith('/')) d->resourceLocation += '/'; QFileInfo fi(d->resourceLocation); if (!fi.exists()) { if (!QDir().mkpath(d->resourceLocation)) { d->errorMessages << i18n("1. Could not create the resource location at %1.", d->resourceLocation); return LocatorError::CannotCreateLocation; } initalizationStatus = InitalizationStatus::FirstRun; } if (!fi.isWritable()) { d->errorMessages << i18n("2. The resource location at %1 is not writable.", d->resourceLocation); return LocatorError::LocationReadOnly; } // Check whether we're updating from an older version if (initalizationStatus != InitalizationStatus::FirstRun) { QFile fi(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION"); if (!fi.exists()) { initalizationStatus = InitalizationStatus::FirstUpdate; } else { fi.open(QFile::ReadOnly); QVersionNumber resource_version = QVersionNumber::fromString(QString::fromUtf8(fi.readAll())); QVersionNumber krita_version = QVersionNumber::fromString(KritaVersionWrapper::versionString()); if (krita_version > resource_version) { initalizationStatus = InitalizationStatus::Updating; } else { initalizationStatus = InitalizationStatus::Initialized; } } } if (initalizationStatus != InitalizationStatus::Initialized) { KisResourceLocator::LocatorError res = firstTimeInstallation(initalizationStatus, installationResourcesLocation); if (res != LocatorError::Ok) { return res; } initalizationStatus = InitalizationStatus::Initialized; } else { if (!synchronizeDb()) { return LocatorError::CannotSynchronizeDb; } } return LocatorError::Ok; } QStringList KisResourceLocator::errorMessages() const { return d->errorMessages; } QString KisResourceLocator::resourceLocationBase() const { return d->resourceLocation; } bool KisResourceLocator::resourceCached(QString storageLocation, const QString &resourceType, const QString &filename) const { storageLocation = makeStorageLocationAbsolute(storageLocation); QPair key = QPair (storageLocation, resourceType + "/" + filename); return d->resourceCache.contains(key); } KoResourceSP KisResourceLocator::resource(QString storageLocation, const QString &resourceType, const QString &filename) { storageLocation = makeStorageLocationAbsolute(storageLocation); QPair key = QPair (storageLocation, resourceType + "/" + filename); KoResourceSP resource; if (d->resourceCache.contains(key)) { resource = d->resourceCache[key]; } else { Q_ASSERT(d->storages.contains(storageLocation)); KisResourceStorageSP storage = d->storages[storageLocation]; Q_ASSERT(storage); resource = storage->resource(resourceType + "/" + filename); Q_ASSERT(resource); if (resource) { d->resourceCache[key] = resource; } } resource->setStorageLocation(storageLocation); Q_ASSERT(!resource->storageLocation().isEmpty()); return resource; } KoResourceSP KisResourceLocator::resourceForId(int resourceId) { ResourceStorage rs = getResourceStorage(resourceId); KoResourceSP r = resource(rs.storageLocation, rs.resourceType, rs.resourceFileName); r->setResourceId(resourceId); return r; } bool KisResourceLocator::removeResource(int resourceId) { // First remove the resource from the cache ResourceStorage rs = getResourceStorage(resourceId); QPair key = QPair (rs.storageLocation, rs.resourceType + "/" + rs.resourceFileName); d->resourceCache.remove(key); return KisResourceCacheDb::removeResource(resourceId); } bool KisResourceLocator::importResourceFromFile(const QString &resourceType, const QString &fileName) { KisResourceLoaderBase *loader = KisResourceLoaderRegistry::instance()->loader(resourceType, KisMimeDatabase::mimeTypeForFile(fileName)); QFile f(fileName); if (!f.open(QFile::ReadOnly)) { qWarning() << "Could not open" << fileName << "for loading"; return false; } KoResourceSP resource = loader->load(QFileInfo(fileName).fileName(), f); KisResourceStorageSP storage = d->storages[d->resourceLocation]; Q_ASSERT(storage); if (!storage->addResource(resourceType, resource)) { qWarning() << "Could not add resource" << resource->filename() << "to the folder storage"; return false; } return KisResourceCacheDb::addResource(folderStorage(), QFileInfo(resource->filename()).lastModified(), resource, resourceType); } bool KisResourceLocator::addResource(const QString &resourceType, const KoResourceSP resource, bool save) { if (!resource || !resource->valid()) return false; if (save) { KisResourceStorageSP storage = d->storages[d->resourceLocation]; Q_ASSERT(storage); if (!storage->addResource(resourceType, resource)) { qWarning() << "Could not add resource" << resource->filename() << "to the folder storage"; return false; } } else { resource->setFilename("memory/" + resourceType + "/" + resource->name()); memoryStorage()->addResource(resourceType, resource); } return KisResourceCacheDb::addResource(save ? folderStorage() : memoryStorage(), QFileInfo(resource->filename()).lastModified(), resource, resourceType, !save); } bool KisResourceLocator::updateResource(const QString &resourceType, const KoResourceSP resource) { QString storageLocation = makeStorageLocationAbsolute(resource->storageLocation()); Q_ASSERT(d->storages.contains(storageLocation)); Q_ASSERT(resource->resourceId() > -1); KisResourceStorageSP storage = d->storages[storageLocation]; int version = resource->version(); if (!storage->addResource(resourceType, resource)) { qWarning() << "Failed to save the new version of " << resource->name() << "to storage" << storageLocation; return false; } // It's the storages that keep track of the version Q_ASSERT(resource->version() == version + 1); if (!KisResourceCacheDb::addResourceVersion(resource->resourceId(), QDateTime::currentDateTime(), storage, resource)) { qWarning() << "Failed to add a new version of the resource to the database" << resource->name(); return false; } // Update the resource in the cache QPair key = QPair (storageLocation, resourceType + "/" + QFileInfo(resource->filename()).fileName()); d->resourceCache[key] = resource; return true; } QMap KisResourceLocator::metaDataForResource(int id) const { return KisResourceCacheDb::metaDataForId(id, "resources"); } bool KisResourceLocator::setMetaDataForResource(int id, QMap map) const { return KisResourceCacheDb::updateMetaDataForId(map, id, "resources"); } void KisResourceLocator::purge() { d->resourceCache.clear(); } +bool KisResourceLocator::addDocumentStorage(const QString &document, KisResourceStorageSP storage) +{ + Q_ASSERT(!d->storages.contains(document)); + d->storages[document] = storage; + if (!KisResourceCacheDb::addStorage(storage, false)) { + d->errorMessages.append(i18n("Could not synchronize %1 with the database", storage->location())); + return false; + } + QMap typeResourceMap; + Q_FOREACH(const QString &resourceType, KisResourceLoaderRegistry::instance()->resourceTypes()) { + typeResourceMap.insert(resourceType, QStringList()); + QSharedPointer iter = storage->resources(resourceType); + while (iter->hasNext()) { + iter->next(); + KoResourceSP resource = iter->resource(); + typeResourceMap[resourceType] << iter->url(); + if (resource) { + if (!KisResourceCacheDb::addResource(storage, iter->lastModified(), resource, iter->type())) { + qWarning() << "Could not add/update resource" << resource->filename() << "to the database"; + } + } + } + } + KisResourceModelProvider::resetAllModels(); + return true; +} + +bool KisResourceLocator::removeDocumentStorage(const QString &document) +{ + purge(); + Q_ASSERT(d->storages.contains(document)); + KisResourceStorageSP storage = d->storages.take(document); + if (!KisResourceCacheDb::deleteStorage(storage)) { + d->errorMessages.append(i18n("Could not remove storage %1 from the database", storage->location())); + return false; + } + KisResourceModelProvider::resetAllModels(); + return true; +} + +bool KisResourceLocator::hasDocumentStorage(const QString &document) +{ + return d->storages.contains(document); +} + KisResourceLocator::LocatorError KisResourceLocator::firstTimeInstallation(InitalizationStatus initalizationStatus, const QString &installationResourcesLocation) { emit progressMessage(i18n("Krita is running for the first time. Intialization will take some time.")); Q_UNUSED(initalizationStatus); Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) { QDir dir(d->resourceLocation + '/' + folder + '/'); if (!dir.exists()) { if (!QDir().mkpath(d->resourceLocation + '/' + folder + '/')) { d->errorMessages << i18n("3. Could not create the resource location at %1.", dir.path()); return LocatorError::CannotCreateLocation; } } } Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) { QDir dir(installationResourcesLocation + '/' + folder + '/'); if (dir.exists()) { Q_FOREACH(const QString &entry, dir.entryList(QDir::Files | QDir::Readable)) { QFile f(dir.canonicalPath() + '/'+ entry); if (!QFileInfo(d->resourceLocation + '/' + folder + '/' + entry).exists()) { if (!f.copy(d->resourceLocation + '/' + folder + '/' + entry)) { d->errorMessages << i18n("Could not copy resource %1 to %2").arg(f.fileName()).arg(d->resourceLocation + '/' + folder + '/' + entry); } } } } } // And add bundles and adobe libraries QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl"; QDirIterator iter(installationResourcesLocation, filters, QDir::Files, QDirIterator::Subdirectories); while (iter.hasNext()) { iter.next(); emit progressMessage(i18n("Installing the resources from bundle %1.", iter.filePath())); QFile f(iter.filePath()); Q_ASSERT(f.exists()); if (!f.copy(d->resourceLocation + '/' + iter.fileName())) { d->errorMessages << i18n("Could not copy resource %1 to %2").arg(f.fileName()).arg(d->resourceLocation); } } QFile f(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION"); f.open(QFile::WriteOnly); f.write(KritaVersionWrapper::versionString().toUtf8()); f.close(); if (!initializeDb()) { return LocatorError::CannotInitializeDb; } return LocatorError::Ok; } bool KisResourceLocator::initializeDb() { emit progressMessage(i18n("Initalizing the resources.")); d->errorMessages.clear(); findStorages(); Q_FOREACH(KisResourceStorageSP storage, d->storages) { QTime t; t.start(); if (!KisResourceCacheDb::addStorage(storage, (storage->type() == KisResourceStorage::StorageType::Folder ? false : true))) { d->errorMessages.append(i18n("Could not add storage %1 to the cache database", storage->location())); } qDebug() << "Adding storage" << storage->location() << "to the database took" << t.elapsed() << "ms"; Q_FOREACH(const QString &resourceType, KisResourceLoaderRegistry::instance()->resourceTypes()) { t.start(); emit progressMessage(i18n("Adding %1 resources to folder %2", resourceType, storage->location())); if (!KisResourceCacheDb::addResources(storage, resourceType)) { d->errorMessages.append(i18n("Could not add resource type %1 to the cache database", resourceType)); } if (!KisResourceCacheDb::addTags(storage, resourceType)) { d->errorMessages.append(i18n("Could not add tags for resource type %1 to the cache database", resourceType)); } qDebug() << "\tAdding resources of type" << resourceType << "to the database took" << t.elapsed() << "ms"; } } return (d->errorMessages.isEmpty()); } void KisResourceLocator::findStorages() { d->storages.clear(); // Add the folder KisResourceStorageSP storage = QSharedPointer::create(d->resourceLocation); Q_ASSERT(storage->location() == d->resourceLocation); d->storages[d->resourceLocation] = storage; // Add the memory storage d->storages["memory"] = QSharedPointer::create("memory"); // And add bundles and adobe libraries QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl"; QDirIterator iter(d->resourceLocation, filters, QDir::Files, QDirIterator::Subdirectories); while (iter.hasNext()) { iter.next(); KisResourceStorageSP storage = QSharedPointer::create(iter.filePath()); d->storages[storage->location()] = storage; } } QList KisResourceLocator::storages() const { return d->storages.values(); } KisResourceStorageSP KisResourceLocator::storageByName(const QString &name) const { if (!d->storages.contains(name)) { qWarning() << "No" << name << "storage defined"; return 0; } KisResourceStorageSP storage = d->storages[name]; if (!storage || !storage->valid()) { qWarning() << "Could not retrieve the" << name << "storage object or the object is not valid"; return 0; } return storage; } KisResourceStorageSP KisResourceLocator::folderStorage() const { return storageByName(d->resourceLocation); } KisResourceStorageSP KisResourceLocator::memoryStorage() const { return storageByName("memory"); } KisResourceLocator::ResourceStorage KisResourceLocator::getResourceStorage(int resourceId) const { ResourceStorage rs; QSqlQuery q; bool r = q.prepare("SELECT storages.location\n" ", resource_types.name as resource_type\n" ", resources.filename\n" "FROM resources\n" ", storages\n" ", resource_types\n" "WHERE resources.id = :resource_id\n" "AND resources.storage_id = storages.id\n" "AND resource_types.id = resources.resource_type_id"); if (!r) { qWarning() << "KisResourceLocator::removeResource: could not prepare query." << q.lastError(); return rs; } q.bindValue(":resource_id", resourceId); r = q.exec(); if (!r) { qWarning() << "KisResourceLocator::removeResource: could not execute query." << q.lastError(); return rs; } q.first(); QString storageLocation = q.value("location").toString(); QString resourceType= q.value("resource_type").toString(); QString resourceFilename = q.value("filename").toString(); if (storageLocation.isEmpty()) { storageLocation = resourceLocationBase(); } else { storageLocation = resourceLocationBase() + storageLocation; } rs.storageLocation = storageLocation; rs.resourceType = resourceType; rs.resourceFileName = resourceFilename; return rs; } QString KisResourceLocator::makeStorageLocationAbsolute(QString storageLocation) const { if (storageLocation.isEmpty()) { storageLocation = resourceLocationBase(); } if (!storageLocation.startsWith('/') && storageLocation != "memory") { if (resourceLocationBase().endsWith('/')) { storageLocation = resourceLocationBase() + storageLocation; } else { storageLocation = resourceLocationBase() + '/' + storageLocation; } } return storageLocation; } bool KisResourceLocator::synchronizeDb() { d->errorMessages.clear(); findStorages(); Q_FOREACH(const KisResourceStorageSP storage, d->storages) { if (storage->name() == "memory") continue; if (!KisResourceCacheDb::synchronizeStorage(storage)) { d->errorMessages.append(i18n("Could not synchronize %1 with the database", storage->location())); } } return d->errorMessages.isEmpty(); } diff --git a/libs/resources/KisResourceLocator.h b/libs/resources/KisResourceLocator.h index c64817bde7..cc63073613 100644 --- a/libs/resources/KisResourceLocator.h +++ b/libs/resources/KisResourceLocator.h @@ -1,211 +1,233 @@ /* * Copyright (C) 2018 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KISRESOURCELOCATOR_H #define KISRESOURCELOCATOR_H #include #include #include #include #include "kritaresources_export.h" #include + /** * The KisResourceLocator class locates all resource storages (folders, * bundles, various adobe resource libraries) in the resource location. * * The resource location is always a writable folder. * * There is one resource locator which is owned by the QApplication * object. * * The resource location is configurable, but there is only one location * where Krita will look for resources. */ class KRITARESOURCES_EXPORT KisResourceLocator : public QObject { Q_OBJECT public: // The configuration key that holds the resource location // for this installation of Krita. The location is // QStandardPaths::AppDataLocation by default, but that // can be changed. static const QString resourceLocationKey; static KisResourceLocator *instance(); ~KisResourceLocator(); enum class LocatorError { Ok, LocationReadOnly, CannotCreateLocation, CannotInitializeDb, CannotSynchronizeDb }; /** * @brief initialize Setup the resource locator for use. * * @param installationResourcesLocation the place where the resources * that come packaged with Krita reside. */ LocatorError initialize(const QString &installationResourcesLocation); /** * @brief errorMessages * @return */ QStringList errorMessages() const; /** * @brief resourceLocationBase is the place where all resource storages (folder, * bundles etc. are located. This is a writable place. * @return the base location for all storages. */ QString resourceLocationBase() const; - /** * @brief purge purges the local resource cache */ void purge(); + /** + * @brief addDocumentStorage Adds a temporary resource storage to the database + * @param document a unique name for the given storage + * @param storage a storage that contains the resources stored in the document + * @return true if the storage has been added succesfully + */ + bool addDocumentStorage(const QString &document, KisResourceStorageSP storage); + + /** + * @brief removeDocumentStorage removes the temporary storage from the database + * @param document the unique name of the document + * @return true is succesful. + */ + bool removeDocumentStorage(const QString &document); + + /** + * @brief hasDocumentStorage can be used to check whether the given document storage already exists + * @param document the name of the storage + * @return true if the document is known + */ + bool hasDocumentStorage(const QString &document); + Q_SIGNALS: void progressMessage(const QString&); private: friend class KisResourceModel; friend class TestResourceLocator; friend class TestResourceModel; friend class Resource; /// @return true if the resource is present in the cache, false if it hasn't been loaded bool resourceCached(QString storageLocation, const QString &resourceType, const QString &filename) const; /** * @brief resource finds a physical resource in one of the storages * @param storageLocation the storage containing the resource. If empty, * this is the folder storage. * * Note that the resource does not have the version or id field set, so this cannot be used directly, * but only through KisResourceModel. * * @param resourceType the type of the resource * @param filename the filename of the resource including extension, but withou * any paths * @return A resource if found, or 0 */ KoResourceSP resource(QString storageLocation, const QString &resourceType, const QString &filename); /** * @brief resourceForId returns the resource with the given id, or 0 if no such resource exists. * The resource object will have its id set but not its version. * @param resourceId the id */ KoResourceSP resourceForId(int resourceId); /** * @brief removeResource * @param resourceId * @return */ bool removeResource(int resourceId); /** * @brief importResourceFromFile * @param resourceType * @param fileName * @return */ bool importResourceFromFile(const QString &resourceType, const QString &fileName); /** * @brief addResource * @param resourceType * @param resource * @param save * @return */ bool addResource(const QString &resourceType, const KoResourceSP resource, bool save = true); /** * @brief updateResource * @param resourceType * @param resource * @return */ bool updateResource(const QString &resourceType, const KoResourceSP resource); /** * @brief metaDataForResource * @param id * @return */ QMap metaDataForResource(int id) const; bool setMetaDataForResource(int id, QMap map) const; KisResourceLocator(QObject *parent); KisResourceLocator(const KisResourceLocator&); KisResourceLocator operator=(const KisResourceLocator&); enum class InitalizationStatus { Unknown, // We don't know whether Krita has run on this system for this resource location yet Initialized, // Everything is ready to start synchronizing the database FirstRun, // Krita hasn't run for this resource location yet FirstUpdate, // Krita was installed, but it's a version from before the resource locator existed, only user-defined resources are present Updating // Krita is updating from an older version with resource locator }; LocatorError firstTimeInstallation(InitalizationStatus initalizationStatus, const QString &installationResourcesLocation); // First time installation bool initializeDb(); // Synchronize on restarting Krita to see whether the user has added any storages or resources to the resources location bool synchronizeDb(); void findStorages(); QList storages() const; KisResourceStorageSP storageByName(const QString &name) const; KisResourceStorageSP folderStorage() const; KisResourceStorageSP memoryStorage() const; struct ResourceStorage { QString storageLocation; QString resourceType; QString resourceFileName; }; ResourceStorage getResourceStorage(int resourceId) const; QString makeStorageLocationAbsolute(QString storageLocation) const; class Private; QScopedPointer d; }; #endif // KISRESOURCELOCATOR_H diff --git a/libs/resources/KisResourceModelProvider.cpp b/libs/resources/KisResourceModelProvider.cpp index 239e4b41f7..89cffba459 100644 --- a/libs/resources/KisResourceModelProvider.cpp +++ b/libs/resources/KisResourceModelProvider.cpp @@ -1,50 +1,58 @@ /* * Copyright (c) 2018 Boudewijn Rempt * * 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. */ #include "KisResourceModelProvider.h" #include "KisResourceModel.h" #include "KoResource.h" #include Q_GLOBAL_STATIC(KisResourceModelProvider, s_instance) struct KisResourceModelProvider::Private { QMap resourceModels; }; KisResourceModelProvider::KisResourceModelProvider() : d(new Private()) { } KisResourceModelProvider::~KisResourceModelProvider() { qDeleteAll(d->resourceModels); delete d; } KisResourceModel *KisResourceModelProvider::resourceModel(const QString &resourceType) { if (!s_instance->d->resourceModels.contains(resourceType)) { s_instance->d->resourceModels[resourceType] = new KisResourceModel(resourceType); } return s_instance->d->resourceModels[resourceType]; } +void KisResourceModelProvider::resetAllModels() +{ + Q_FOREACH(KisResourceModel *model, s_instance->d->resourceModels.values()) { + model->resetQuery(); + } + +} + diff --git a/libs/resources/KisResourceModelProvider.h b/libs/resources/KisResourceModelProvider.h index 79df1022af..2d602c6f28 100644 --- a/libs/resources/KisResourceModelProvider.h +++ b/libs/resources/KisResourceModelProvider.h @@ -1,43 +1,44 @@ /* * Copyright (c) 2018 Boudewijn Rempt * * 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 KISRESOURCEMODELPROVIDER_H #define KISRESOURCEMODELPROVIDER_H #include #include "kritaresources_export.h" class KisResourceModel; class KRITARESOURCES_EXPORT KisResourceModelProvider { public: KisResourceModelProvider(); ~KisResourceModelProvider(); static KisResourceModel *resourceModel(const QString &resourceType); + static void resetAllModels(); private: struct Private; Private *const d; Q_DISABLE_COPY(KisResourceModelProvider) }; #endif // KISRESOURCEMODELPROVIDER_H diff --git a/libs/resources/KisResourceStorage.cpp b/libs/resources/KisResourceStorage.cpp index f12c608a8e..3fe603f613 100644 --- a/libs/resources/KisResourceStorage.cpp +++ b/libs/resources/KisResourceStorage.cpp @@ -1,163 +1,159 @@ /* * Copyright (C) 2018 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisResourceStorage.h" #include #include #include #include "KisStoragePlugin.h" #include "KisFolderStorage.h" #include "KisBundleStorage.h" #include "KisAbrStorage.h" #include "KisAslStorage.h" #include "KisMemoryStorage.h" const QString KisResourceStorage::s_meta_generator("meta:generator"); const QString KisResourceStorage::s_meta_author("dc:author"); const QString KisResourceStorage::s_meta_title("dc:title"); const QString KisResourceStorage::s_meta_description("dc:description"); const QString KisResourceStorage::s_meta_initial_creator("meta:initial-creator"); const QString KisResourceStorage::s_meta_creator("cd:creator"); const QString KisResourceStorage::s_meta_creation_date("meta:creation-data"); const QString KisResourceStorage::s_meta_dc_date("meta:dc-date"); const QString KisResourceStorage::s_meta_user_defined("meta:meta-userdefined"); const QString KisResourceStorage::s_meta_name("meta:name"); const QString KisResourceStorage::s_meta_value("meta:value"); const QString KisResourceStorage::s_meta_version("meta:bundle-version"); class KisResourceStorage::Private { public: QString name; QString location; bool valid {false}; KisResourceStorage::StorageType storageType {KisResourceStorage::StorageType::Unknown}; QSharedPointer storagePlugin; }; KisResourceStorage::KisResourceStorage(const QString &location) : d(new Private()) { d->location = location; d->name = QFileInfo(d->location).fileName(); QFileInfo fi(d->location); if (fi.isDir()) { d->storagePlugin.reset(new KisFolderStorage(location)); d->storageType = StorageType::Folder; d->valid = fi.isWritable(); } - else if (location == "memory") { - d->storagePlugin.reset(new KisMemoryStorage); - d->name = "memory"; - d->storageType = StorageType::Memory; - d->valid = true; - } - else { - if (d->location.endsWith(".bundle")) { + else if (d->location.endsWith(".bundle")) { d->storagePlugin.reset(new KisBundleStorage(location)); d->storageType = StorageType::Bundle; // XXX: should we also check whether there's a valid metadata entry? Or is this enough? d->valid = (fi.isReadable() && QuaZip(d->location).open(QuaZip::mdUnzip)); - } - else if (d->location.endsWith(".abr")) { + } else if (d->location.endsWith(".abr")) { d->storagePlugin.reset(new KisAbrStorage(location)); d->storageType = StorageType::AdobeBrushLibrary; d->valid = fi.isReadable(); - } - else if (d->location.endsWith(".asl")) { + } else if (d->location.endsWith(".asl")) { d->storagePlugin.reset(new KisAslStorage(location)); d->storageType = StorageType::AdobeStyleLibrary; d->valid = fi.isReadable(); - } + } + else if (!d->location.isEmpty()) { + d->storagePlugin.reset(new KisMemoryStorage); + d->name = location; + d->storageType = StorageType::Memory; + d->valid = true; } } KisResourceStorage::~KisResourceStorage() { } QString KisResourceStorage::name() const { return d->name; } QString KisResourceStorage::location() const { return d->location; } KisResourceStorage::StorageType KisResourceStorage::type() const { return d->storageType; } QDateTime KisResourceStorage::timestamp() const { return QFileInfo(d->location).lastModified(); } KisResourceStorage::ResourceItem KisResourceStorage::resourceItem(const QString &url) { return d->storagePlugin->resourceItem(url); } KoResourceSP KisResourceStorage::resource(const QString &url) { return d->storagePlugin->resource(url); } QSharedPointer KisResourceStorage::resources(const QString &resourceType) const { return d->storagePlugin->resources(resourceType); } QSharedPointer KisResourceStorage::tags(const QString &resourceType) const { return d->storagePlugin->tags(resourceType); } bool KisResourceStorage::addTag(const QString &resourceType, KisTagSP tag) { return d->storagePlugin->addTag(resourceType, tag); } bool KisResourceStorage::addResource(const QString &resourceType, KoResourceSP resource) { return d->storagePlugin->addResource(resourceType, resource); } bool KisResourceStorage::valid() const { return d->valid; } QStringList KisResourceStorage::metaDataKeys() const { return d->storagePlugin->metaDataKeys(); } QVariant KisResourceStorage::metaData(const QString &key) const { return d->storagePlugin->metaData(key); } diff --git a/libs/resources/tests/DummyResource.h b/libs/resources/tests/DummyResource.h index a317b04852..41c62904c5 100644 --- a/libs/resources/tests/DummyResource.h +++ b/libs/resources/tests/DummyResource.h @@ -1,89 +1,90 @@ /* * Copyright (c) 2018 boud * * 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 DUMMYRESOURCE_H #define DUMMYRESOURCE_H #include "KoResource.h" #include #include #include #include class DummyResource : public KoResource { public: - DummyResource(const QString &f) : KoResource(f) + DummyResource(const QString &f) + : KoResource(f) { QRandomGenerator64 qrg; QByteArray ba(1024, '0'); for (int i = 0; i < 1024 / 8; i+=8) { quint64 v = qrg.generate64(); ba[i] = v; } KoHashGenerator *hashGenerator = KoHashGeneratorProvider::instance()->getGenerator("MD5"); QByteArray hash = hashGenerator->generateHash(ba); setMD5(hash); } bool load() override { Q_ASSERT(false); setValid(true); return true; } bool loadFromDevice(QIODevice *dev) override { if (!dev->isOpen()) { dev->open(QIODevice::ReadOnly); } m_something = QString::fromUtf8(dev->readAll()); setValid(true); return true; } bool save() override { Q_ASSERT(false); return true; } bool saveToDevice(QIODevice *dev) const { if (!dev->isOpen()) { dev->open(QIODevice::WriteOnly); } dev->write(m_something.toUtf8()); return true; } void setSomething(const QString &something) { m_something = something; } QString something() const { return m_something; } private: QString m_something; }; #endif // DUMMYRESOURCE_H diff --git a/libs/resources/tests/TestResourceLocator.cpp b/libs/resources/tests/TestResourceLocator.cpp index 142f458659..3ba68a68a5 100644 --- a/libs/resources/tests/TestResourceLocator.cpp +++ b/libs/resources/tests/TestResourceLocator.cpp @@ -1,183 +1,205 @@ /* * Copyright (C) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "TestResourceLocator.h" #include #include #include #include #include #include #include #include #include -#include +#include #include #include +#include +#include +#include +#include #include #include + #ifndef FILES_DATA_DIR #error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources" #endif #ifndef FILES_DEST_DIR #error "FILES_DEST_DIR not set. A directory where data will be written to for testing installing resources" #endif void TestResourceLocator::initTestCase() { ResourceTestHelper::initTestDb(); m_srcLocation = QString(FILES_DATA_DIR); QVERIFY2(QDir(m_srcLocation).exists(), m_srcLocation.toUtf8()); m_dstLocation = QString(FILES_DEST_DIR); ResourceTestHelper::cleanDstLocation(m_dstLocation); KConfigGroup cfg(KSharedConfig::openConfig(), ""); cfg.writeEntry(KisResourceLocator::resourceLocationKey, m_dstLocation); m_locator = KisResourceLocator::instance(); ResourceTestHelper::createDummyLoaderRegistry(); } void TestResourceLocator::testLocatorInitalization() { KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); KisResourceLocator::LocatorError r = m_locator->initialize(m_srcLocation); if (!m_locator->errorMessages().isEmpty()) qDebug() << m_locator->errorMessages(); QVERIFY(r == KisResourceLocator::LocatorError::Ok); QVERIFY(QDir(m_dstLocation).exists()); Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) { QDir dstDir(m_dstLocation + '/' + folder + '/'); QDir srcDir(m_srcLocation + '/' + folder + '/'); QVERIFY(dstDir.exists()); QVERIFY(dstDir.entryList(QDir::Files | QDir::NoDotAndDotDot) == srcDir.entryList(QDir::Files | QDir::NoDotAndDotDot)); } QFile f(m_dstLocation + '/' + "KRITA_RESOURCE_VERSION"); QVERIFY(f.exists()); f.open(QFile::ReadOnly); QVersionNumber version = QVersionNumber::fromString(QString::fromUtf8(f.readAll())); QVERIFY(version == QVersionNumber::fromString(KritaVersionWrapper::versionString())); } void TestResourceLocator::testStorageInitialization() { Q_FOREACH(KisResourceStorageSP storage, m_locator->storages()) { QVERIFY(KisResourceCacheDb::addStorage(storage, true)); } QSqlQuery query; bool r = query.exec("SELECT COUNT(*) FROM storages"); QVERIFY(r); QVERIFY(query.lastError() == QSqlError()); query.first(); Q_FOREACH(KisResourceStorageSP storage, m_locator->storages()) { qDebug() << storage; } QCOMPARE(query.value(0).toInt(), m_locator->storages().count()); } void TestResourceLocator::testLocatorSynchronization() { QVERIFY(m_locator->synchronizeDb()); } void TestResourceLocator::testResources() { QSqlQuery query; bool r = query.exec("SELECT COUNT(*) FROM resources"); QVERIFY(r); QVERIFY(query.lastError() == QSqlError()); query.first(); QCOMPARE(query.value(0).toInt(), 7); } void TestResourceLocator::testTags() { QSqlQuery query; bool r = query.exec("SELECT COUNT(*) FROM tags"); QVERIFY(r); QVERIFY(query.lastError() == QSqlError()); query.first(); QCOMPARE(query.value(0).toInt(), 1); } void TestResourceLocator::testResourceLocationBase() { QCOMPARE(m_locator->resourceLocationBase(), QString(FILES_DEST_DIR)); } void TestResourceLocator::testResource() { - KoResourceSP res = m_locator->resource("", "paintoppresets", "test0.kpp"); + KoResourceSP res = m_locator->resource("", ResourceType::PaintOpPresets, "test0.kpp"); QVERIFY(res); } void TestResourceLocator::testResourceForId() { - KoResourceSP res = m_locator->resource("", "paintoppresets", "test0.kpp"); - int resourceId = KisResourceCacheDb::resourceIdForResource("test0.kpp", "paintoppresets", ""); + KoResourceSP res = m_locator->resource("", ResourceType::PaintOpPresets, "test0.kpp"); + int resourceId = KisResourceCacheDb::resourceIdForResource("test0.kpp", ResourceType::PaintOpPresets, ""); QVERIFY(resourceId > -1); KoResourceSP res2 = m_locator->resourceForId(resourceId); QCOMPARE(res, res2); } void TestResourceLocator::testRemoveResource() { } void TestResourceLocator::testImportResourceFromFile() { } void TestResourceLocator::testAddResource() { } void TestResourceLocator::testUpdateResource() { } +void TestResourceLocator::testDocumentStorage() +{ + const QString &documentName("document"); + + KisResourceModel *model = KisResourceModelProvider::resourceModel(ResourceType::PaintOpPresets); + int rowcount = model->rowCount(); + KisResourceStorageSP documentStorage = QSharedPointer::create(documentName); + KoResourceSP resource(new DummyResource("test")); + documentStorage->addResource(ResourceType::PaintOpPresets, resource); + m_locator->addDocumentStorage(documentName, documentStorage); + QVERIFY(model->rowCount() > rowcount); + QVERIFY(m_locator->hasDocumentStorage(documentName)); + m_locator->removeDocumentStorage(documentName); + QVERIFY(!m_locator->hasDocumentStorage(documentName)); + QVERIFY(model->rowCount() == rowcount); +} + void TestResourceLocator::cleanupTestCase() { ResourceTestHelper::rmTestDb(); ResourceTestHelper::cleanDstLocation(m_dstLocation); } QTEST_MAIN(TestResourceLocator) diff --git a/libs/resources/tests/TestResourceLocator.h b/libs/resources/tests/TestResourceLocator.h index d338e68311..89552e9b0c 100644 --- a/libs/resources/tests/TestResourceLocator.h +++ b/libs/resources/tests/TestResourceLocator.h @@ -1,57 +1,59 @@ /* * Copyright (C) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef TESTRESOURCELOCATOR_H #define TESTRESOURCELOCATOR_H #include class KisResourceLocator; class TestResourceLocator : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testLocatorInitalization(); void testStorageInitialization(); void testLocatorSynchronization(); void testResources(); void testTags(); void testResourceLocationBase(); void testResource(); void testResourceForId(); void testRemoveResource(); void testImportResourceFromFile(); void testAddResource(); void testUpdateResource(); + void testDocumentStorage(); + void cleanupTestCase(); private: QString m_srcLocation; QString m_dstLocation; KisResourceLocator *m_locator; }; #endif diff --git a/libs/resources/tests/TestResourceStorage.cpp b/libs/resources/tests/TestResourceStorage.cpp index 8141aa6f29..fa8cbb8a9e 100644 --- a/libs/resources/tests/TestResourceStorage.cpp +++ b/libs/resources/tests/TestResourceStorage.cpp @@ -1,65 +1,72 @@ /* * Copyright (C) 2017 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "TestResourceStorage.h" #include #include #ifndef FILES_DATA_DIR #error "FILES_DATA_DIR not set. A directory with the data used for testing installing resources" #endif void TestResourceStorage ::testStorage() { { KisResourceStorage storage(QString(FILES_DATA_DIR)); QVERIFY(storage.type() == KisResourceStorage::StorageType::Folder); QVERIFY(storage.valid()); } { KisResourceStorage storage(QString(FILES_DATA_DIR) + "/bundles/test1.bundle"); QVERIFY(storage.type() == KisResourceStorage::StorageType::Bundle); QVERIFY(storage.valid()); } { KisResourceStorage storage(QString(FILES_DATA_DIR) + "/bundles/test2.bundle"); QVERIFY(storage.type() == KisResourceStorage::StorageType::Bundle); QVERIFY(storage.valid()); } { KisResourceStorage storage(QString(FILES_DATA_DIR) + "/abr/test.abr"); QVERIFY(storage.type() == KisResourceStorage::StorageType::AdobeBrushLibrary); QVERIFY(storage.valid()); } { KisResourceStorage storage(QString(FILES_DATA_DIR) + "/brushes/test.gbr"); + QVERIFY(storage.type() == KisResourceStorage::StorageType::Memory); + QVERIFY(storage.valid()); + } + + { + KisResourceStorage storage(""); QVERIFY(storage.type() == KisResourceStorage::StorageType::Unknown); QVERIFY(!storage.valid()); } + } QTEST_MAIN(TestResourceStorage)