diff --git a/libs/resources/KisResourceLocator.cpp b/libs/resources/KisResourceLocator.cpp index 1acace3867..d175d6322a 100644 --- a/libs/resources/KisResourceLocator.cpp +++ b/libs/resources/KisResourceLocator.cpp @@ -1,694 +1,665 @@ /* * 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 #include #include "KoResourcePaths.h" #include "KisResourceStorage.h" #include "KisResourceCacheDb.h" #include "KisResourceLoaderRegistry.h" #include "KisMemoryStorage.h" #include "KisResourceModelProvider.h" #include #include 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() { // Not a regular Q_GLOBAL_STATIC, because we want this deleted as // part of the app destructor. KisResourceLocator *locator = qApp->findChild(QString()); if (!locator) { locator = new KisResourceLocator(qApp); } return locator; } KisResourceLocator::~KisResourceLocator() { } KisResourceLocator::LocatorError KisResourceLocator::initialize(const QString &installationResourcesLocation) { InitializationStatus initializationStatus = InitializationStatus::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; } initializationStatus = InitializationStatus::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 (initializationStatus != InitializationStatus::FirstRun) { QFile fi(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION"); if (!fi.exists()) { initializationStatus = InitializationStatus::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) { initializationStatus = InitializationStatus::Updating; } else { initializationStatus = InitializationStatus::Initialized; } } } if (initializationStatus != InitializationStatus::Initialized) { KisResourceLocator::LocatorError res = firstTimeInstallation(initializationStatus, installationResourcesLocation); if (res != LocatorError::Ok) { return res; } initializationStatus = InitializationStatus::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); } void KisResourceLocator::loadRequiredResources(KoResourceSP resource) { QList requiredResources = resource->requiredResources(KisGlobalResourcesInterface::instance()); Q_FOREACH (KoResourceSP res, requiredResources) { if (res->resourceId() < 0) { // we put all the embedded resources into the global shared "memory" storage this->addResource(res->resourceType().first, res, "memory"); } } } 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 { KisResourceStorageSP storage = d->storages[storageLocation]; if (!storage) { qWarning() << "Could not find storage" << storageLocation; return 0; } resource = storage->resource(resourceType + "/" + filename); // Try to locate bundle in bundle modificated resources location. if (QFileInfo(storage->location() + "_modified" + "/" + resourceType + "/" + filename).exists()) { QFileInfo bundleLoc(storage->location()); storage = d->storages[bundleLoc.path() + "/"]; QString bundleFolderLocation(bundleLoc.fileName() + "_modified" + "/" + resourceType + "/" + filename); resource = storage->resource(bundleFolderLocation); key = QPair (storageLocation, bundleFolderLocation); } else { resource = storage->resource(resourceType + "/" + filename); } if (resource) { KIS_SAFE_ASSERT_RECOVER(!resource->filename().startsWith(resourceType)) {}; d->resourceCache[key] = resource; // load all the embedded resources into temporary "memory" storage loadRequiredResources(resource); } } if (!resource) { qDebug() << "KoResourceSP KisResourceLocator::resource" << storageLocation << resourceType << filename; } Q_ASSERT(resource); resource->setStorageLocation(storageLocation); Q_ASSERT(!resource->storageLocation().isEmpty()); if (resource->resourceId() < 0 || resource->version() < 0) { QSqlQuery q; if (!q.prepare("SELECT resources.id\n" ", resources.version\n" "FROM resources\n" ", storages\n" ", resource_types\n" "WHERE storages.id = resources.storage_id\n" "AND storages.location = :storage_location\n" "AND resource_types.id = resources.resource_type_id\n" "AND resource_types.name = :resource_type\n" "AND resources.filename = :filename")) { qWarning() << "Could not prepare id/version query" << q.lastError(); } q.bindValue(":storage_location", makeStorageLocationRelative(storageLocation)); q.bindValue(":resource_type", resourceType); q.bindValue(":filename", filename); if (!q.exec()) { qWarning() << "Could not execute id/version quert" << q.lastError() << q.boundValues(); } if (!q.first()) { qWarning() << "Could not find the resource in the database" << storageLocation << resourceType << filename; } resource->setResourceId(q.value(0).toInt()); Q_ASSERT(resource->resourceId() >= 0); resource->setVersion(q.value(1).toInt()); Q_ASSERT(resource->version() >= 0); } if (!resource) { qWarning() << "Could not find resource" << resourceType + "/" + filename; return 0; } return resource; } KoResourceSP KisResourceLocator::resourceForId(int resourceId) { ResourceStorage rs = getResourceStorage(resourceId); KoResourceSP r = resource(rs.storageLocation, rs.resourceType, rs.resourceFileName); return r; } bool KisResourceLocator::removeResource(int resourceId, const QString &/*storageLocation*/) { // 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, const QString &storageLocation) { 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, KisGlobalResourcesInterface::instance()); if (!resource) { qWarning() << "Could not import" << fileName << ": resource doesn't load."; return false; } KisResourceStorageSP storage = d->storages[makeStorageLocationAbsolute(storageLocation)]; Q_ASSERT(storage); if (!storage->addResource(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, const QString &storageLocation) { if (!resource || !resource->valid()) return false; KisResourceStorageSP storage = d->storages[makeStorageLocationAbsolute(storageLocation)]; Q_ASSERT(storage); //If we have gotten this far and the resource still doesn't have a filename to save to, we should generate one. if (resource->filename().isEmpty()) { if (storageLocation == "memory") { resource->setFilename("memory/" + resourceType + "/" + resource->name()); } else { resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension()); } } // Save the resource to the storage storage if (!storage->addResource(resource)) { qWarning() << "Could not add resource" << resource->filename() << "to the folder storage"; return false; } // And the database return KisResourceCacheDb::addResource(storage, storage->timeStampForResource(resourceType, resource->filename()), resource, resourceType); } bool KisResourceLocator::updateResource(const QString &resourceType, const KoResourceSP resource) { QString storageLocation = makeStorageLocationAbsolute(resource->storageLocation()); qDebug() << ">>>>>>>>>>>>>>>> storageLocation"<< storageLocation << "resource storage location" << resource->storageLocation(); Q_ASSERT(d->storages.contains(storageLocation)); Q_ASSERT(resource->resourceId() > -1); KisResourceStorageSP storage = d->storages[storageLocation]; resource->updateThumbnail(); int version = resource->version(); // This increments the version in the resource if (!storage->addResource(resource)) { qWarning() << "Failed to save the new version of " << resource->name() << "to storage" << storageLocation; return false; } // Memory storages don't store versioned resources if (storage->type() == KisResourceStorage::StorageType::Memory) { return true; } // It's the storages that keep track of the version Q_ASSERT(resource->version() == version + 1); // The version needs already to have been incremented 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"); } QMap KisResourceLocator::metaDataForStorage(const QString &storageLocation) const { QMap metadata; if (!d->storages.contains(makeStorageLocationAbsolute(storageLocation))) { qWarning() << storageLocation << "not in" << d->storages.keys(); return metadata; } KisResourceStorageSP st = d->storages[makeStorageLocationAbsolute(storageLocation)]; if (d->storages[makeStorageLocationAbsolute(storageLocation)].isNull()) { return metadata; } Q_FOREACH(const QString key, st->metaDataKeys()) { metadata[key] = st->metaData(key); } return metadata; } void KisResourceLocator::setMetaDataForStorage(const QString &storageLocation, QMap map) const { Q_ASSERT(d->storages.contains(storageLocation)); Q_FOREACH(const QString &key, map.keys()) { d->storages[storageLocation]->setMetaData(key, map[key]); } } -bool KisResourceLocator::storageContainsResourceByFile(const QString &storageLocation, const QString &resourceType, const QString &filename) const -{ - QSqlQuery q; - if (!q.prepare("SELECT *\n" - "FROM storages\n" - ", resources\n" - ", resource_types\n" - "WHERE resources.filename = :filename\n" - "AND resources.storage_id = storages.id\n" - "AND storages.location = :storage_location\n" - "AND resources.resource_type_id = resource_types.id\n" - "AND resource_types.name = :resource_type")) - { - qWarning() << "Could not prepare storageCOntainsResourceByFile query" << q.lastError(); - return false; - } - - q.bindValue(":filename", filename); - q.bindValue(":storage_location", storageLocation); - q.bindValue(":resource_type", resourceType); - - if (!q.exec()) { - qWarning() << "Could not execute storageCOntainsResourceByFile query" << q.lastError() << q.boundValues(); - return false; - } - - return q.first(); -} - void KisResourceLocator::purge() { d->resourceCache.clear(); } bool KisResourceLocator::addStorage(const QString &storageLocation, KisResourceStorageSP storage) { Q_ASSERT(!d->storages.contains(storageLocation)); d->storages[storageLocation] = storage; if (!KisResourceCacheDb::addStorage(storage, false)) { d->errorMessages.append(i18n("Could not add %1 to the database", storage->location())); return false; } KisResourceModelProvider::resetAllModels(); emit storageAdded(storage->location()); return true; } bool KisResourceLocator::removeStorage(const QString &document) { // Cloned documents have a document storage, but that isn't in the locator. if (!d->storages.contains(document)) return true; purge(); 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(); emit storageRemoved(storage->location()); return true; } bool KisResourceLocator::hasStorage(const QString &document) { return d->storages.contains(document); } KisResourceLocator::LocatorError KisResourceLocator::firstTimeInstallation(InitializationStatus initializationStatus, const QString &installationResourcesLocation) { emit progressMessage(i18n("Krita is running for the first time. Initialization will take some time.")); Q_UNUSED(initializationStatus); 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", f.fileName(), 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", f.fileName(), 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("Initializing the resources.")); d->errorMessages.clear(); findStorages(); Q_FOREACH(KisResourceStorageSP storage, d->storages) { QElapsedTimer 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"; } 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"); d->storages["memory"]->setMetaData(KisResourceStorage::s_meta_name, i18n("Temporary Resources")); // 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::storageByLocation(const QString &location) const { if (!d->storages.contains(location)) { qWarning() << "No" << location << "storage defined:" << d->storages.keys(); return 0; } KisResourceStorageSP storage = d->storages[location]; if (!storage || !storage->valid()) { qWarning() << "Could not retrieve the" << location << "storage object or the object is not valid"; return 0; } return storage; } KisResourceStorageSP KisResourceLocator::folderStorage() const { return storageByLocation(d->resourceLocation); } KisResourceStorageSP KisResourceLocator::memoryStorage() const { return storageByLocation("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(); rs.storageLocation = makeStorageLocationAbsolute(storageLocation); rs.resourceType = resourceType; rs.resourceFileName = resourceFilename; return rs; } QString KisResourceLocator::makeStorageLocationAbsolute(QString storageLocation) const { // qDebug() << "makeStorageLocationAbsolute" << storageLocation; if (storageLocation.isEmpty()) { return resourceLocationBase(); } if (QFileInfo(storageLocation).isRelative() && (storageLocation.endsWith("bundle") || storageLocation.endsWith("asl") || storageLocation.endsWith("abr"))) { if (resourceLocationBase().endsWith('/') || resourceLocationBase().endsWith("\\")) { storageLocation = resourceLocationBase() + storageLocation; } else { storageLocation = resourceLocationBase() + '/' + storageLocation; } } // qDebug() << "\t" << storageLocation; return storageLocation; } bool KisResourceLocator::synchronizeDb() { d->errorMessages.clear(); findStorages(); Q_FOREACH(const KisResourceStorageSP storage, d->storages) { if (!KisResourceCacheDb::synchronizeStorage(storage)) { d->errorMessages.append(i18n("Could not synchronize %1 with the database", storage->location())); } } return d->errorMessages.isEmpty(); } QString KisResourceLocator::makeStorageLocationRelative(QString location) const { // qDebug() << "makeStorageLocationRelative" << location << "locationbase" << resourceLocationBase(); return location.remove(resourceLocationBase()); } diff --git a/libs/resources/KisResourceLocator.h b/libs/resources/KisResourceLocator.h index 6e5cc35a0a..b3ec0f0162 100644 --- a/libs/resources/KisResourceLocator.h +++ b/libs/resources/KisResourceLocator.h @@ -1,284 +1,276 @@ /* * 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 addStorage Adds a new resource storage to the database. The storage is * will be marked as not pre-installed. * @param storageLocation a unique name for the given storage * @param storage a storage object * @return true if the storage has been added successfully */ bool addStorage(const QString &storageLocation, KisResourceStorageSP storage); /** * @brief removeStorage removes the temporary storage from the database * @param document the unique name of the document * @return true is successful. */ bool removeStorage(const QString &storageLocation); /** * @brief hasStorage can be used to check whether the given storage already exists * @param storageLocation the name of the storage * @return true if the storage is known */ bool hasStorage(const QString &storageLocation); Q_SIGNALS: void progressMessage(const QString&); /// Emitted whenever a storage is added void storageAdded(const QString &location); /// Emitted whenever a storage is removed void storageRemoved(const QString &location); private: friend class KisResourceModel; friend class KisTagModel; friend class KisStorageModel; friend class TestResourceLocator; friend class TestResourceModel; friend class Resource; friend class KisResourceCacheDb; friend class KisStorageFilterProxyModel; /// @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 * @param optional: the storage that contains the given resource * @return */ bool removeResource(int resourceId, const QString &storageLocation = QString()); /** * @brief importResourceFromFile * @param resourceType * @param fileName * @param storageLocation: optional, the storage where the resource will be stored. Empty means in the default Folder storage. * @return */ bool importResourceFromFile(const QString &resourceType, const QString &fileName, const QString &storageLocation = QString()); /** * @brief addResource adds the given resource to the database and potentially a storage * @param resourceType the type of the resource * @param resource the actual resource object * @param storageLocation the storage where the resource will be saved. By default this is the the default folder storage. * @return true if successful */ bool addResource(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation = QString()); /** * @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; /** * @brief setMetaDataForResource * @param id * @param map * @return */ bool setMetaDataForResource(int id, QMap map) const; /** * @brief metaDataForStorage * @param storage * @return */ QMap metaDataForStorage(const QString &storageLocation) const; /** * @brief setMetaDataForStorage * @param storage * @param map */ void setMetaDataForStorage(const QString &storageLocation, QMap map) const; - /** - * @brief storageContainsResourceByFile - * @param storageLocation - * @param filename - * @return - */ - bool storageContainsResourceByFile(const QString &storageLocation, const QString &resourceType, const QString &filename) const; - /** * Loads all the resources required by \p resource into the cache * * loadRequiredResources() also loads embedded resources and adds them * into the database. */ void loadRequiredResources(KoResourceSP resource); KisResourceLocator(QObject *parent); KisResourceLocator(const KisResourceLocator&); KisResourceLocator operator=(const KisResourceLocator&); enum class InitializationStatus { 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(InitializationStatus initializationStatus, 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 storageByLocation(const QString &location) 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; QString makeStorageLocationRelative(QString location) const; class Private; QScopedPointer d; }; #endif // KISRESOURCELOCATOR_H diff --git a/libs/resources/KisResourceModel.cpp b/libs/resources/KisResourceModel.cpp index 2d76159de1..75f829859e 100644 --- a/libs/resources/KisResourceModel.cpp +++ b/libs/resources/KisResourceModel.cpp @@ -1,586 +1,601 @@ /* * 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 "KisResourceModel.h" #include #include #include #include #include #include #include #include struct KisResourceModel::Private { QSqlQuery resourcesQuery; QSqlQuery tagQuery; QString resourceType; int columnCount {10}; int cachedRowCount {-1}; }; //static int s_i = 0; KisResourceModel::KisResourceModel(const QString &resourceType, QObject *parent) : QAbstractTableModel(parent) , d(new Private) { //qDebug() << "ResourceModel" << s_i << resourceType; s_i++; + connect(KisResourceLocator::instance(), SIGNAL(storageAdded(const QString&)), this, SLOT(addStorage(const QString&))); + connect(KisResourceLocator::instance(), SIGNAL(storageRemoved(const QString&)), this, SLOT(removeStorage(const QString&))); + d->resourceType = resourceType; bool r = d->resourcesQuery.prepare("SELECT resources.id\n" ", resources.storage_id\n" ", resources.name\n" ", resources.filename\n" ", resources.tooltip\n" ", resources.thumbnail\n" ", resources.status\n" ", storages.location\n" ", resources.version\n" ", resource_types.name as resource_type\n" ", resources.status as resource_active\n" ", storages.active as storage_active\n" "FROM resources\n" ", resource_types\n" ", storages\n" "WHERE resources.resource_type_id = resource_types.id\n" "AND resources.storage_id = storages.id\n" "AND resource_types.name = :resource_type\n" "ORDER BY resources.id"); if (!r) { qWarning() << "Could not prepare KisResourceModel query" << d->resourcesQuery.lastError(); } d->resourcesQuery.bindValue(":resource_type", d->resourceType); resetQuery(); r = d->tagQuery.prepare("SELECT tags.id\n" ", tags.url\n" ", tags.name\n" ", tags.comment\n" "FROM tags\n" ", resource_tags\n" "WHERE tags.active > 0\n" // make sure the tag is active "AND tags.id = resource_tags.tag_id\n" // join tags + resource_tags by tag_id "AND resource_tags.resource_id = :resource_id\n" "ORDER BY tags.id"); // make sure we're looking for tags for a specific resource if (!r) { qWarning() << "Could not prepare TagsForResource query" << d->tagQuery.lastError(); } } KisResourceModel::~KisResourceModel() { delete d; } int KisResourceModel::columnCount(const QModelIndex &/*parent*/) const { return d->columnCount; } QVariant KisResourceModel::data(const QModelIndex &index, int role) const { QVariant v; if (!index.isValid()) return v; if (index.row() > rowCount()) return v; if (index.column() > d->columnCount) return v; bool pos = const_cast(this)->d->resourcesQuery.seek(index.row()); if (pos) { switch(role) { case Qt::DisplayRole: { switch(index.column()) { case Id: return d->resourcesQuery.value("id"); case StorageId: return d->resourcesQuery.value("storage_id"); case Name: return d->resourcesQuery.value("name"); case Filename: return d->resourcesQuery.value("filename"); case Tooltip: return d->resourcesQuery.value("tooltip"); case Thumbnail: { QByteArray ba = d->resourcesQuery.value("thumbnail").toByteArray(); QBuffer buf(&ba); buf.open(QBuffer::ReadOnly); QImage img; img.load(&buf, "PNG"); return QVariant::fromValue(img); } case Status: return d->resourcesQuery.value("status"); case Location: return d->resourcesQuery.value("location"); case ResourceType: return d->resourcesQuery.value("resource_type"); case ResourceActive: return d->resourcesQuery.value("resource_active"); case StorageActive: return d->resourcesQuery.value("storage_active"); default: ; }; Q_FALLTHROUGH(); } case Qt::DecorationRole: { if (index.column() == Thumbnail) { QByteArray ba = d->resourcesQuery.value("thumbnail").toByteArray(); QBuffer buf(&ba); buf.open(QBuffer::ReadOnly); QImage img; img.load(&buf, "PNG"); return QVariant::fromValue(img); } return QVariant(); } case Qt::ToolTipRole: Q_FALLTHROUGH(); case Qt::StatusTipRole: Q_FALLTHROUGH(); case Qt::WhatsThisRole: return d->resourcesQuery.value("tooltip"); case Qt::UserRole + Id: return d->resourcesQuery.value("id"); case Qt::UserRole + StorageId: return d->resourcesQuery.value("storage_id"); case Qt::UserRole + Name: return d->resourcesQuery.value("name"); case Qt::UserRole + Filename: return d->resourcesQuery.value("filename"); case Qt::UserRole + Tooltip: return d->resourcesQuery.value("tooltip"); case Qt::UserRole + Thumbnail: { QByteArray ba = d->resourcesQuery.value("thumbnail").toByteArray(); QBuffer buf(&ba); buf.open(QBuffer::ReadOnly); QImage img; img.load(&buf, "PNG"); return QVariant::fromValue(img); } case Qt::UserRole + Status: return d->resourcesQuery.value("status"); case Qt::UserRole + Location: return d->resourcesQuery.value("location"); case Qt::UserRole + ResourceType: return d->resourcesQuery.value("resource_type"); case Qt::UserRole + Tags: { QVector tags = tagsForResource(d->resourcesQuery.value("id").toInt()); QStringList tagNames; Q_FOREACH(const KisTagSP tag, tags) { tagNames << tag->name(); } return tagNames; } case Qt::UserRole + Dirty: { QString storageLocation = d->resourcesQuery.value("location").toString(); QString filename = d->resourcesQuery.value("filename").toString(); // An uncached resource has not been loaded, so it cannot be dirty if (!KisResourceLocator::instance()->resourceCached(storageLocation, d->resourceType, filename)) { return false; } else { // Now we have to check the resource, but that's cheap since it's been loaded in any case KoResourceSP resource = resourceForIndex(index); return resource->isDirty(); } } case Qt::UserRole + MetaData: { QMap r = KisResourceLocator::instance()->metaDataForResource(d->resourcesQuery.value("id").toInt()); return r; } case Qt::UserRole + KoResourceRole: { KoResourceSP tag = resourceForIndex(index); QVariant response; response.setValue(tag); return response; } case Qt::UserRole + ResourceActive: { return d->resourcesQuery.value("resource_active"); } case Qt::UserRole + StorageActive: { return d->resourcesQuery.value("storage_active"); } default: ; } } return v; } QVariant KisResourceModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant v = QVariant(); if (role != Qt::DisplayRole) { return v; } if (orientation == Qt::Horizontal) { switch(section) { case Id: v = i18n("Id"); break; case StorageId: v = i18n("Storage ID"); break; case Name: v = i18n("Name"); break; case Filename: v = i18n("File Name"); break; case Tooltip: v = i18n("Tooltip"); break; case Thumbnail: v = i18n("Image"); break; case Status: v = i18n("Status"); break; case Location: v = i18n("Location"); break; case ResourceType: v = i18n("Resource Type"); break; case ResourceActive: v = i18n("Active"); case StorageActive: v = i18n("Storage Active"); default: v = QString::number(section); } return v; } return QAbstractItemModel::headerData(section, orientation, role); } //static int s_i2 {0}; KoResourceSP KisResourceModel::resourceForIndex(QModelIndex index) const { KoResourceSP resource = 0; if (!index.isValid()) return resource; if (index.row() > rowCount()) return resource; if (index.column() > d->columnCount) return resource; //qDebug() << "KisResourceModel::resourceForIndex" << s_i2 << d->resourceType; s_i2++; bool pos = const_cast(this)->d->resourcesQuery.seek(index.row()); if (pos) { int id = d->resourcesQuery.value("id").toInt(); resource = resourceForId(id); } return resource; } KoResourceSP KisResourceModel::resourceForId(int id) const { return KisResourceLocator::instance()->resourceForId(id); } KoResourceSP KisResourceModel::resourceForFilename(QString filename) const { KoResourceSP resource = 0; QSqlQuery q; bool r = q.prepare("SELECT resources.id AS id\n" "FROM resources\n" ", resource_types\n" ", storages\n" "WHERE resources.resource_type_id = resource_types.id\n" "AND resources.storage_id = storages.id\n" "AND resources.filename = :resource_filename\n" "AND resource_types.name = :resource_type\n" "AND resources.status = 1\n" "AND storages.active = 1"); if (!r) { qWarning() << "Could not prepare KisResourceModel query for resource name" << q.lastError(); } q.bindValue(":resource_filename", filename); q.bindValue(":resource_type", d->resourceType); r = q.exec(); if (!r) { qWarning() << "Could not select" << d->resourceType << "resources by filename" << q.lastError() << q.boundValues(); } if (q.first()) { int id = q.value("id").toInt(); resource = KisResourceLocator::instance()->resourceForId(id); } return resource; } KoResourceSP KisResourceModel::resourceForName(QString name) const { KoResourceSP resource = 0; QSqlQuery q; bool r = q.prepare("SELECT resources.id AS id\n" "FROM resources\n" ", resource_types\n" ", storages\n" "WHERE resources.resource_type_id = resource_types.id\n" "AND resources.storage_id = storages.id\n" "AND resources.name = :resource_name\n" "AND resource_types.name = :resource_type\n" "AND resources.status = 1\n" "AND storages.active = 1"); if (!r) { qWarning() << "Could not prepare KisResourceModel query for resource name" << q.lastError(); } q.bindValue(":resource_type", d->resourceType); q.bindValue(":resource_name", name); r = q.exec(); if (!r) { qWarning() << "Could not select" << d->resourceType << "resources by name" << q.lastError() << q.boundValues(); } if (q.first()) { int id = q.value("id").toInt(); resource = KisResourceLocator::instance()->resourceForId(id); } return resource; } KoResourceSP KisResourceModel::resourceForMD5(const QByteArray md5sum) const { KoResourceSP resource = 0; QSqlQuery q; bool r = q.prepare("SELECT resource_id AS id\n" "FROM versioned_resources\n" "WHERE md5sum = :md5sum"); if (!r) { qWarning() << "Could not prepare KisResourceModel query for resource md5" << q.lastError(); } q.bindValue(":md5sum", md5sum.toHex()); r = q.exec(); if (!r) { qWarning() << "Could not select" << d->resourceType << "resources by md5" << q.lastError() << q.boundValues(); } if (q.first()) { int id = q.value("id").toInt(); resource = KisResourceLocator::instance()->resourceForId(id); } return resource; } //static int s_i3 {0}; QModelIndex KisResourceModel::indexFromResource(KoResourceSP resource) const { if (!resource || !resource->valid()) return QModelIndex(); //qDebug() << "KisResourceModel::indexFromResource" << s_i3 << d->resourceType; s_i3++; // For now a linear seek to find the first resource with the right id d->resourcesQuery.first(); do { if (d->resourcesQuery.value("id").toInt() == resource->resourceId()) { return createIndex(d->resourcesQuery.at(), 0); } } while (d->resourcesQuery.next()); return QModelIndex(); } //static int s_i4 {0}; bool KisResourceModel::removeResource(const QModelIndex &index) { if (index.row() > rowCount()) return false; if (index.column() > d->columnCount) return false; //qDebug() << "KisResourceModel::removeResource" << s_i4 << d->resourceType; s_i4++; bool pos = d->resourcesQuery.seek(index.row()); if (!pos) return false; int resourceId = d->resourcesQuery.value("id").toInt(); if (!KisResourceLocator::instance()->removeResource(resourceId)) { qWarning() << "Failed to remove resource" << resourceId; return false; } return resetQuery(); } //static int s_i5 {0}; bool KisResourceModel::removeResource(KoResourceSP resource) { if (!resource || !resource->valid()) return false; //qDebug() << "KisResourceModel::remvoeResource 2" << s_i5 << d->resourceType; s_i5++; if (!KisResourceLocator::instance()->removeResource(resource->resourceId())) { qWarning() << "Failed to remove resource" << resource->resourceId(); return false; } return resetQuery(); } //static int s_i6 {0}; bool KisResourceModel::importResourceFile(const QString &filename) { //qDebug() << "KisResourceModel::importResource" << s_i6 << d->resourceType; s_i6++; if (!KisResourceLocator::instance()->importResourceFromFile(d->resourceType, filename)) { qWarning() << "Failed to import resource" << filename; return false; } return resetQuery(); } //static int s_i7 {0}; bool KisResourceModel::addResource(KoResourceSP resource, const QString &storageId) { if (!resource || !resource->valid()) { qWarning() << "Cannot add resource. Resource is null or not valid"; return false; } //qDebug() << "KisResourceModel::addResource" << s_i7 << d->resourceType; s_i7++; if (!KisResourceLocator::instance()->addResource(d->resourceType, resource, storageId)) { qWarning() << "Failed to add resource" << resource->name(); return false; } return resetQuery(); } //static int s_i8 {0}; bool KisResourceModel::updateResource(KoResourceSP resource) { if (!resource || !resource->valid()) { qWarning() << "Cannot update resource. Resource is null or not valid"; return false; } //qDebug() << "KisResourceModel::updateResource" << s_i8 << d->resourceType; s_i8++; if (!KisResourceLocator::instance()->updateResource(d->resourceType, resource)) { qWarning() << "Failed to update resource" << resource; return false; } return resetQuery(); } bool KisResourceModel::renameResource(KoResourceSP resource, const QString &name) { if (!resource || !resource->valid() || name.isEmpty()) { qWarning() << "Cannot rename resources. Resource is NULL or not valid or name is empty"; return false; } resource->setName(name); if (!KisResourceLocator::instance()->updateResource(d->resourceType, resource)) { qWarning() << "Failed to rename resource" << resource << name; return false; } return resetQuery(); } //static int s_i9 {0}; bool KisResourceModel::setResourceMetaData(KoResourceSP resource, QMap metadata) { //qDebug() << "KisResourceModel::setResourceMetaData" << s_i9 << d->resourceType; s_i9++; Q_ASSERT(resource->resourceId() > -1); return KisResourceLocator::instance()->setMetaDataForResource(resource->resourceId(), metadata); } bool KisResourceModel::resetQuery() { QElapsedTimer t; t.start(); emit beforeResourcesLayoutReset(QModelIndex()); beginResetModel(); bool r = d->resourcesQuery.exec(); if (!r) { qWarning() << "Could not select" << d->resourceType << "resources" << d->resourcesQuery.lastError() << d->resourcesQuery.boundValues(); } d->cachedRowCount = -1; endResetModel(); emit afterResourcesLayoutReset(); qDebug() << "KisResourceModel::resetQuery for" << d->resourceType << "took" << t.elapsed() << "ms"; return r; } QVector KisResourceModel::tagsForResource(int resourceId) const { return KisTagModelProvider::tagModel(d->resourceType)->tagsForResource(resourceId); } int KisResourceModel::rowCount(const QModelIndex &) const { if (d->cachedRowCount < 0) { QSqlQuery q; q.prepare("SELECT count(*)\n" "FROM resources\n" ", resource_types\n" ", storages\n" "WHERE resources.resource_type_id = resource_types.id\n" "AND resource_types.name = :resource_type\n" "AND resources.storage_id = storages.id\n"); q.bindValue(":resource_type", d->resourceType); q.exec(); q.first(); const_cast(this)->d->cachedRowCount = q.value(0).toInt(); } return d->cachedRowCount; } + + +void KisResourceModel::addStorage(const QString &location) +{ + +} + + +void KisResourceModel::removeStorage(const QString &location) +{ + +} diff --git a/libs/resources/KisResourceModel.h b/libs/resources/KisResourceModel.h index c099cc80e3..370562d674 100644 --- a/libs/resources/KisResourceModel.h +++ b/libs/resources/KisResourceModel.h @@ -1,222 +1,227 @@ /* * 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 KISRESOURCEMODEL_H #define KISRESOURCEMODEL_H #include #include #include #include /** * KisAbstractResourceModel defines the interface for accessing resources * that is used in KisResourceModel and the various filter/proxy models */ class KRITARESOURCES_EXPORT KisAbstractResourceModel { public: virtual ~KisAbstractResourceModel(){} /** * @brief resourceForIndex * @param index * @return */ virtual KoResourceSP resourceForIndex(QModelIndex index = QModelIndex()) const = 0; /** * @brief indexFromResource * @param resource * @return */ virtual QModelIndex indexFromResource(KoResourceSP resource) const = 0; /** * @brief removeResource * @param index * @return */ virtual bool removeResource(const QModelIndex &index) = 0; /** * @brief importResourceFile * @param filename * @return */ virtual bool importResourceFile(const QString &filename) = 0; /** * @brief addResource adds the given resource to the database and storage * @param resource the resource itself * @param storageId the id of the storage (could be "memory" for temporary * resources, the document's storage id for document storages or empty to save * to the default resources folder * @return true if adding the resoruce succeeded. */ virtual bool addResource(KoResourceSP resource, const QString &storageId = QString()) = 0; /** * @brief updateResource * @param resource * @return */ virtual bool updateResource(KoResourceSP resource) = 0; /** * @brief renameResource name the given resource. The resource will have its * name field reset, will be saved to the storage and there will be a new * version created in the database. * @param resource The resource to rename * @param name The new name * @return true if the operation succeeded. */ virtual bool renameResource(KoResourceSP resource, const QString &name) = 0; /** * @brief removeResource * @param resource * @return */ virtual bool removeResource(KoResourceSP resource) = 0; /** * @brief setResourceMetaData * @param metadata * @return */ virtual bool setResourceMetaData(KoResourceSP resource, QMap metadata) = 0; }; /** * @brief The KisResourceModel class provides access to the cache database * for a particular resource type. Instances should be retrieved using * KisResourceModelProvider. All resources are part of this model, active and * inactive, from all storages, active and inactive. */ class KRITARESOURCES_EXPORT KisResourceModel : public QAbstractTableModel, public KisAbstractResourceModel { Q_OBJECT public: /** * @brief The Columns enum indexes the columns in the model. To get * the thumbnail for a particular resource, create the index with * QModelIndex(row, Thumbnail). */ enum Columns { Id = 0, StorageId, Name, Filename, Tooltip, Thumbnail, Status, Location, ResourceType, Tags, /// A larger thumbnail for displaying in a tooltip. 200x200 or so. LargeThumbnail, /// A dirty resource is one that has been modified locally but not saved Dirty, /// MetaData is a map of key, value pairs that is associated with this resource MetaData, /// XXX: what is this used for, again? KoResourceRole, /// Whether the current resource is active ResourceActive, /// Whether the current resource's storage isa ctive StorageActive }; private: friend class KisResourceModelProvider; friend class TestResourceModel; KisResourceModel(const QString &resourceType, QObject *parent = 0); public: ~KisResourceModel() override; // QAbstractItemModel API int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; // Resources API /** * @brief resourceForIndex returns a properly versioned and id's resource object */ KoResourceSP resourceForIndex(QModelIndex index = QModelIndex()) const override; KoResourceSP resourceForId(int id) const; /** * resourceForFilename returns the first resource with the given filename that * is active and is in an active store. Note that the filename does not include * a path to the storage, and if there are resources with the same filename * in several active storages, only one resource is returned. * * @return a resource if one is found, or 0 if none are found */ KoResourceSP resourceForFilename(QString fileName) const; /** * resourceForName returns the first resource with the given name that * is active and is in an active store. Note that if there are resources * with the same name in several active storages, only one resource * is returned. * * @return a resource if one is found, or 0 if none are found */ KoResourceSP resourceForName(QString name) const; KoResourceSP resourceForMD5(const QByteArray md5sum) const; QModelIndex indexFromResource(KoResourceSP resource) const override; bool removeResource(const QModelIndex &index) override; bool removeResource(KoResourceSP resource) override; bool importResourceFile(const QString &filename) override; bool addResource(KoResourceSP resource, const QString &storageId = QString()) override; bool updateResource(KoResourceSP resource) override; bool renameResource(KoResourceSP resource, const QString &name) override; bool setResourceMetaData(KoResourceSP resource, QMap metadata) override; QVector tagsForResource(int resourceId) const; Q_SIGNALS: // XXX: emit these signals void beforeResourcesLayoutReset(QModelIndex activateAfterReformat); void afterResourcesLayoutReset(); +private Q_SLOTS: + + void addStorage(const QString &location); + void removeStorage(const QString &location); + private: bool resetQuery(); struct Private; Private *const d; }; #endif // KISRESOURCEMODEL_H diff --git a/libs/resources/KisStorageFilterProxyModel.cpp b/libs/resources/KisStorageFilterProxyModel.cpp index 82210f9b11..10c3d86307 100644 --- a/libs/resources/KisStorageFilterProxyModel.cpp +++ b/libs/resources/KisStorageFilterProxyModel.cpp @@ -1,102 +1,106 @@ /* * 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 "KisStorageFilterProxyModel.h" #include #include #include #include #include struct KisStorageFilterProxyModel::Private { FilterType filterType {KisStorageFilterProxyModel::ByStorageType}; QVariant filter; }; KisStorageFilterProxyModel::KisStorageFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) , d(new Private) { } KisStorageFilterProxyModel::~KisStorageFilterProxyModel() { delete d; } KisResourceStorageSP KisStorageFilterProxyModel::storageForIndex(QModelIndex index) const { KisStorageModel *source = dynamic_cast(sourceModel()); if (source) { return source->storageForIndex(mapToSource(index)); } return 0; } void KisStorageFilterProxyModel::setFilter(KisStorageFilterProxyModel::FilterType filterType, QVariant filter) { d->filter = filter; d->filterType = filterType; } bool KisStorageFilterProxyModel::filterAcceptsColumn(int /*source_column*/, const QModelIndex &/*source_parent*/) const { return true; } bool KisStorageFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { if (d->filter.isNull()) return true; QModelIndex idx = sourceModel()->index(source_row, KisResourceModel::Name, source_parent); switch (d->filterType) { case ByFileName: { - QMap v = d->filter.toMap(); - return KisResourceLocator::instance()->storageContainsResourceByFile(sourceModel()->data(idx, Qt::UserRole + KisStorageModel::Location).toString() - , v["resourcetype"].toString() - , v["filename"].toString()); + QString filename = d->filter.toString(); + return (sourceModel()->data(idx, Qt::UserRole + KisStorageModel::Location).toString().contains(filename)); } case ByStorageType: { QString storageType = sourceModel()->data(idx, Qt::UserRole + KisStorageModel::StorageType).toString(); return (d->filter.toStringList().contains(storageType)); } + case ByActive: + { + bool active = d->filter.toBool(); + bool isActive = sourceModel()->data(idx, Qt::UserRole + KisStorageModel::Active).toBool(); + return (active == isActive); + } default: ; } return false; } bool KisStorageFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { QString nameLeft = sourceModel()->data(source_left, Qt::UserRole + KisResourceModel::Name).toString(); QString nameRight = sourceModel()->data(source_right, Qt::UserRole + KisResourceModel::Name).toString(); return nameLeft < nameRight; } void KisStorageFilterProxyModel::slotModelReset() { invalidateFilter(); } diff --git a/libs/resources/KisStorageFilterProxyModel.h b/libs/resources/KisStorageFilterProxyModel.h index 80ce1e2006..a6de2f6d86 100644 --- a/libs/resources/KisStorageFilterProxyModel.h +++ b/libs/resources/KisStorageFilterProxyModel.h @@ -1,71 +1,74 @@ /* * 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 KISSTORAGEFILTERPROXYMODEL_H #define KISSTORAGEFILTERPROXYMODEL_H #include #include #include #include #include "kritaresources_export.h" /** * KisStorageFilterProxyModel provides a filtered view on the available storages. * It can be used to find the storages that have resource with a particular file * name, or storages of particular types. * * Filtering by file name takes a string, filtering by storage type a list * of untranslated strings (there is a method in KisResourceStorage for retrieving * those strings from the ResourceType). */ class KRITARESOURCES_EXPORT KisStorageFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: KisStorageFilterProxyModel(QObject *parent = 0); ~KisStorageFilterProxyModel() override; enum FilterType { - ByFileName = 0, - ByStorageType + ByFileName = 0, ///< Pass a string: all storages whose name contains the + /// string will be returned. + ByStorageType, ///< Pass a string list of storage types + ByActive ///< Pass a boolean, false to filter out active bundles, + /// true to filter out inactive bundles }; KisResourceStorageSP storageForIndex(QModelIndex index = QModelIndex()) const; void setFilter(FilterType filterType, QVariant filter); protected: bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override; bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; private Q_SLOTS: void slotModelReset(); private: struct Private; Private *const d; Q_DISABLE_COPY(KisStorageFilterProxyModel) }; #endif diff --git a/libs/resources/KisStorageModel.h b/libs/resources/KisStorageModel.h index a1b7e339ae..f9044f19a5 100644 --- a/libs/resources/KisStorageModel.h +++ b/libs/resources/KisStorageModel.h @@ -1,83 +1,81 @@ /* * Copyright (c) 2019 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 KISSTORAGEMODEL_H #define KISSTORAGEMODEL_H #include #include #include #include "KisResourceStorage.h" #include "kritaresources_export.h" /** * KisStorageModel provides a model of all registered storages, like * the folder storages, the bundle storages or the memory storages. Note * that inactive storages are also part of this model. */ class KRITARESOURCES_EXPORT KisStorageModel : public QAbstractTableModel { Q_OBJECT public: enum Columns { Id = 0, StorageType, Location, TimeStamp, PreInstalled, Active, Thumbnail, DisplayName, MetaData }; KisStorageModel(QObject *parent = 0); ~KisStorageModel() override; static KisStorageModel * instance(); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; Qt::ItemFlags flags(const QModelIndex &index) const override; KisResourceStorageSP storageForIndex(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; private Q_SLOTS: - friend class KisResourceLocator; - void addStorage(const QString &location); void removeStorage(const QString &location); private: KisStorageModel(const KisStorageModel&); KisStorageModel operator=(const KisStorageModel&); struct Private; QScopedPointer d; }; #endif // KISSTORAGEMODEL_H diff --git a/libs/resources/KisTagFilterResourceProxyModel.h b/libs/resources/KisTagFilterResourceProxyModel.h index 7585c7e608..35344432e9 100644 --- a/libs/resources/KisTagFilterResourceProxyModel.h +++ b/libs/resources/KisTagFilterResourceProxyModel.h @@ -1,78 +1,81 @@ /* * 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 KISTAGFILTERRESOURCEPROXYMODEL_H #define KISTAGFILTERRESOURCEPROXYMODEL_H #include #include #include #include #include #include #include "kritaresources_export.h" +/** + * @brief The KisTagFilterResourceProxyModel class filters the resources by tag or resource name + */ class KRITARESOURCES_EXPORT KisTagFilterResourceProxyModel : public QSortFilterProxyModel, public KisAbstractResourceModel { Q_OBJECT public: KisTagFilterResourceProxyModel(KisTagModel* model = 0, QObject *parent = 0); ~KisTagFilterResourceProxyModel() override; // KisAbstractResourceModel interface public: KoResourceSP resourceForIndex(QModelIndex index = QModelIndex()) const override; QModelIndex indexFromResource(KoResourceSP resource) const override; bool removeResource(const QModelIndex &index) override; bool importResourceFile(const QString &filename) override; bool addResource(KoResourceSP resource, const QString &storageId = QString()) override; bool updateResource(KoResourceSP resource) override; bool renameResource(KoResourceSP resource, const QString &name) override; bool removeResource(KoResourceSP resource) override; bool setResourceMetaData(KoResourceSP resource, QMap metadata) override; /** * @brief setTag * @param tag */ void setTag(const KisTagSP tag); void setSearchBoxText(const QString& seatchBoxText); void setFilterByCurrentTag(bool filterInCurrentTag); protected: bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override; bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; bool resourceHasCurrentTag(KisTagSP currentTag, QVector tagsForResource) const; private Q_SLOTS: void slotModelReset(); private: struct Private; Private *const d; Q_DISABLE_COPY(KisTagFilterResourceProxyModel) }; #endif // KISTAGFILTERRESOURCEPROXYMODEL_H diff --git a/libs/resources/tests/CMakeLists.txt b/libs/resources/tests/CMakeLists.txt index fd18530727..71e4d57b19 100644 --- a/libs/resources/tests/CMakeLists.txt +++ b/libs/resources/tests/CMakeLists.txt @@ -1,26 +1,27 @@ add_definitions(-DFILES_DEST_DIR="${CMAKE_CURRENT_BINARY_DIR}/data/") set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) include(ECMAddTests) include(KritaAddBrokenUnitTest) macro_add_unittest_definitions() ecm_add_tests( TestResourceCacheDb TestResourceLoaderRegistry TestResourceLocator TestResourceStorage TestFolderStorage TestMemoryStorage TestTag TestBundleStorage TestResourceModel TestTagFilterResourceProxyModel TestTagModel TestResourceTypeModel TestStorageModel TestResourceSearchBoxFilter + TestStorageFilterProxyModel NAME_PREFIX "libs-kritaresources-" LINK_LIBRARIES kritaglobal kritaplugin kritaresources kritaversion KF5::ConfigCore Qt5::Sql Qt5::Test) diff --git a/libs/resources/tests/TestResourceLocator.cpp b/libs/resources/tests/TestResourceLocator.cpp index 7cb22b6212..b645e6e54e 100644 --- a/libs/resources/tests/TestResourceLocator.cpp +++ b/libs/resources/tests/TestResourceLocator.cpp @@ -1,188 +1,182 @@ /* * 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 #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::testLocatorInitialization() { 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(); QCOMPARE(query.value(0).toInt(), m_locator->storages().count()); } void TestResourceLocator::testLocatorSynchronization() { QVERIFY(m_locator->synchronizeDb()); { QSqlQuery query; bool r = query.exec("SELECT COUNT(*) FROM resources"); QVERIFY(r); QVERIFY(query.lastError() == QSqlError()); query.first(); QCOMPARE(query.value(0).toInt(), 7); } { 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("", ResourceType::PaintOpPresets, "test0.kpp"); QVERIFY(res); } void TestResourceLocator::testResourceForId() { 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::testStorageContainsResourceByFile() -{ - QVERIFY(m_locator->storageContainsResourceByFile("", "paintoppresets", "test0.kpp") > 0); - QVERIFY(m_locator->storageContainsResourceByFile("", "paintoppresets", "XSLKDJSADLKSAJDA") == 0); -} - 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(resource); m_locator->addStorage(documentName, documentStorage); QVERIFY(model->rowCount() > rowcount); QVERIFY(m_locator->hasStorage(documentName)); m_locator->removeStorage(documentName); QVERIFY(!m_locator->hasStorage(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 115a3d7966..d1a525a136 100644 --- a/libs/resources/tests/TestResourceLocator.h +++ b/libs/resources/tests/TestResourceLocator.h @@ -1,53 +1,52 @@ /* * 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 testLocatorInitialization(); void testStorageInitialization(); void testLocatorSynchronization(); void testResourceLocationBase(); void testResource(); void testResourceForId(); - void testStorageContainsResourceByFile(); void testDocumentStorage(); void cleanupTestCase(); private: QString m_srcLocation; QString m_dstLocation; KisResourceLocator *m_locator; }; #endif diff --git a/libs/resources/tests/TestStorageFilterProxyModel.cpp b/libs/resources/tests/TestStorageFilterProxyModel.cpp new file mode 100644 index 0000000000..f6a156f01b --- /dev/null +++ b/libs/resources/tests/TestStorageFilterProxyModel.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019 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. + */ +#include "TestStorageFilterProxyModel.h" + +#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 TestStorageFilterProxyModel::initTestCase() +{ + ResourceTestHelper::initTestDb(); + ResourceTestHelper::createDummyLoaderRegistry(); + + 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(); + + if (!KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))) { + qDebug() << "Could not initialize KisResourceCacheDb on" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + } + QVERIFY(KisResourceCacheDb::isValid()); + + 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()); +} + + +void TestStorageFilterProxyModel::testFilterByName() +{ + QScopedPointer proxyModel(new KisStorageFilterProxyModel()); + proxyModel->setSourceModel(KisStorageModel::instance()); + + QString fileName = "test1"; + + proxyModel->setFilter(KisStorageFilterProxyModel::ByStorageType, fileName); + +} + +void TestStorageFilterProxyModel::testFilterByType() +{ + QScopedPointer proxyModel(new KisStorageFilterProxyModel()); + proxyModel->setSourceModel(KisStorageModel::instance()); + proxyModel->setFilter(KisStorageFilterProxyModel::ByStorageType, + QStringList() + << KisResourceStorage::storageTypeToUntranslatedString(KisResourceStorage::StorageType::Bundle) + << KisResourceStorage::storageTypeToUntranslatedString(KisResourceStorage::StorageType::Folder)); + +} + +void TestStorageFilterProxyModel::testFilterByActive() +{ + QScopedPointer proxyModel(new KisStorageFilterProxyModel()); + proxyModel->setSourceModel(KisStorageModel::instance()); + proxyModel->setFilter(KisStorageFilterProxyModel::ByStorageType, true); +} + + +void TestStorageFilterProxyModel::cleanupTestCase() +{ + ResourceTestHelper::rmTestDb(); + ResourceTestHelper::cleanDstLocation(m_dstLocation); +} + + +QTEST_MAIN(TestStorageFilterProxyModel) + diff --git a/libs/resources/tests/TestStorageFilterProxyModel.h b/libs/resources/tests/TestStorageFilterProxyModel.h new file mode 100644 index 0000000000..c5b5c4dada --- /dev/null +++ b/libs/resources/tests/TestStorageFilterProxyModel.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 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 TESTAGFILTERTRESOURCEPROXYMODEL_H +#define TESTAGFILTERTRESOURCEPROXYMODEL_H + +#include +#include + +class KisResourceLocator; + +class TestStorageFilterProxyModel : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void testFilterByName(); + void testFilterByType(); + void testFilterByActive(); + void cleanupTestCase(); +private: + + QString m_srcLocation; + QString m_dstLocation; + + KisResourceLocator *m_locator; + +}; + +#endif