diff --git a/libs/resources/CMakeLists.txt b/libs/resources/CMakeLists.txt index f5761bee95..bf13fb4c08 100644 --- a/libs/resources/CMakeLists.txt +++ b/libs/resources/CMakeLists.txt @@ -1,71 +1,67 @@ include_directories(${QUAZIP_INCLUDE_DIRS}) add_subdirectory(tests) set(kritaresources_LIB_SRCS KisResourceCacheDb.cpp KisResourceLoader.cpp KisResourceLoaderRegistry.cpp KisResourceLocator.cpp KisResourceStorage.cpp KisResourceModel.cpp KisTagFilterResourceProxyModel.cpp KisResourceModelProvider.cpp KisResourceTypeModel.cpp KisStorageModel.cpp KisResourceIterator.cpp KisResourceSearchBoxFilter.cpp KisStoragePlugin.cpp KisBundleStorage.cpp KisFolderStorage.cpp KisMemoryStorage.cpp KisTag.cpp KisTagModel.cpp KisTagModelProvider.cpp KisActiveFilterTagProxyModel.cpp - KisTagsResourcesModel.cpp - KisTagsResourcesModelProvider.cpp - - KoResource.cpp KoResourceBundle.cpp KoResourceBundleManifest.cpp KoMD5Generator.cpp KoHashGeneratorProvider.cpp KoResourcePaths.cpp kconfigini.cpp kconfigdata.cpp kconfigbackend.cpp ) qt5_add_resources(kritaresources_LIB_SRCS sql.qrc) add_library(kritaresources SHARED ${kritaresources_LIB_SRCS}) generate_export_header(kritaresources BASE_NAME kritaresources) target_link_libraries(kritaresources PUBLIC Qt5::Core Qt5::Widgets PRIVATE Qt5::Sql kritaversion kritaglobal kritaplugin kritastore KF5::ConfigCore KF5::CoreAddons KF5::I18n ${QUAZIP_LIBRARIES} ) set_target_properties(kritaresources PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaresources ${INSTALL_TARGETS_DEFAULT_ARGS} ) diff --git a/libs/resources/KisResourceLocator.h b/libs/resources/KisResourceLocator.h index 055b1b5be3..6f3b43d4bd 100644 --- a/libs/resources/KisResourceLocator.h +++ b/libs/resources/KisResourceLocator.h @@ -1,241 +1,241 @@ /* * 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 succesfully */ 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 succesful. */ 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&); private: friend class KisResourceModel; - friend class KisTagsResourcesModel; + friend class KisTagModel; friend class KisStorageModel; friend class TestResourceLocator; friend class TestResourceModel; friend class Resource; friend class KisResourceCacheDb; /// @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 succesfull */ 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; 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 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 864e0d5052..3eeabc29a2 100644 --- a/libs/resources/KisResourceModel.cpp +++ b/libs/resources/KisResourceModel.cpp @@ -1,605 +1,579 @@ /* * 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 -#include +#include + struct KisResourceModel::Private { QSqlQuery resourcesQuery; QSqlQuery tagQuery; QString resourceType; int columnCount {9}; 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++; 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" "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" "AND resources.status = 1\n" "AND storages.active = 1"); 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" "AND tags.storage_id in (SELECT id\n" " FROM storages\n" " WHERE active > 0)"); // 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 Image: { 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"); default: ; }; Q_FALLTHROUGH(); } case Qt::DecorationRole: { if (index.column() == Image) { 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 + Image: { 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; } 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 Image: v = i18n("Image"); break; case Status: v = i18n("Status"); break; case Location: v = i18n("Location"); break; case ResourceType: v = i18n("Resource Type"); break; 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) { QString storageLocation = d->resourcesQuery.value("location").toString(); QString filename = d->resourcesQuery.value("filename").toString(); resource = KisResourceLocator::instance()->resource(storageLocation, d->resourceType, filename); resource->setResourceId(d->resourcesQuery.value("id").toInt()); resource->setVersion(d->resourcesQuery.value("version").toInt()); resource->setFilename(filename); resource->setStorageLocation(storageLocation); QString name = d->resourcesQuery.value("name").toString(); if (!name.isNull()) { resource->setName(name); } } 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\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" "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_type", d->resourceType); q.bindValue(":resource_filename", filename); r = q.exec(); if (!r) { qWarning() << "Could not select" << d->resourceType << "resources" << q.lastError() << q.boundValues(); } if (q.first()) { qDebug() << "setting up resource from filename"; QString storageLocation = q.value("location").toString(); QString filename = q.value("filename").toString(); resource = KisResourceLocator::instance()->resource(storageLocation, d->resourceType, filename); resource->setResourceId(q.value("id").toInt()); resource->setVersion(q.value("version").toInt()); resource->setFilename(filename); resource->setStorageLocation(storageLocation); QString name = q.value("name").toString(); if (!name.isNull()) { resource->setName(name); } } return resource; } KoResourceSP KisResourceModel::resourceForName(QString name) const { KoResourceSP resource = 0; QSqlQuery q; bool r = q.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" "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" << q.lastError() << q.boundValues(); } if (q.first()) { qDebug() << "setting up resource from name (1)"; QString storageLocation = q.value("location").toString(); QString filename = q.value("filename").toString(); resource = KisResourceLocator::instance()->resource(storageLocation, d->resourceType, filename); resource->setResourceId(q.value("id").toInt()); resource->setVersion(q.value("version").toInt()); resource->setFilename(filename); resource->setStorageLocation(storageLocation); QString name = q.value("name").toString(); if (!name.isNull()) { resource->setName(name); } } 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; } - // reset tags-resources model - KisTagsResourcesModelProvider::getModel(d->resourceType)->resetQuery(); 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; } - // reset tags-resources model - KisTagsResourcesModelProvider::getModel(d->resourceType)->resetQuery(); 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, bool save) { 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, save ? "" : "memory")) { 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"; 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() { QTime t; t.start(); beginResetModel(); bool r = d->resourcesQuery.exec(); if (!r) { qWarning() << "Could not select" << d->resourceType << "resources" << d->resourcesQuery.lastError() << d->resourcesQuery.boundValues(); } d->cachedRowCount = -1; - KisTagsResourcesModelProvider::resetModel(d->resourceType); - endResetModel(); emit afterResourcesLayoutReset(); qDebug() << "KisResourceModel::resetQuery for" << d->resourceType << "took" << t.elapsed() << "ms"; return r; } QVector KisResourceModel::tagsForResource(int resourceId) const { - return KisTagsResourcesModelProvider::getModel(d->resourceType)->tagsForResource(resourceId); - /* - d->tagQuery.bindValue(":resource_id", resourceId); - bool r = d->tagQuery.exec(); - if (!r) { - qWarning() << "Could not select tags for" << resourceId << d->tagQuery.lastError() << d->tagQuery.boundValues(); - } - QVector tags; - while (d->tagQuery.next()) { - //qDebug() << d->tagQuery.value(0).toString() << d->tagQuery.value(1).toString() << d->tagQuery.value(2).toString(); - KisTagSP tag(new KisTag()); - tag->setId(d->tagQuery.value("id").toInt()); - tag->setUrl(d->tagQuery.value("url").toString()); - tag->setName(d->tagQuery.value("name").toString()); - tag->setComment(d->tagQuery.value("comment").toString()); - tag->setValid(true); - tag->setActive(true); - tags << tag; - } - return tags; - */ + 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" "AND resources.status = 1\n" "AND storages.active = 1"); q.bindValue(":resource_type", d->resourceType); q.exec(); q.first(); const_cast(this)->d->cachedRowCount = q.value(0).toInt(); } return d->cachedRowCount; } diff --git a/libs/resources/KisResourceModelProvider.cpp b/libs/resources/KisResourceModelProvider.cpp index 8d291a6f86..0c79333506 100644 --- a/libs/resources/KisResourceModelProvider.cpp +++ b/libs/resources/KisResourceModelProvider.cpp @@ -1,71 +1,69 @@ /* * 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 -#include - #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(); } } void KisResourceModelProvider::resetModel(const QString& resourceType) { QMap::iterator found = s_instance->d->resourceModels.find(resourceType); if (found != s_instance->d->resourceModels.end()) { found.value()->resetQuery(); } } diff --git a/libs/resources/KisTag.h b/libs/resources/KisTag.h index 93d133f1ea..39da92383a 100644 --- a/libs/resources/KisTag.h +++ b/libs/resources/KisTag.h @@ -1,110 +1,109 @@ /* * 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 KISTAGLOADER_H #define KISTAGLOADER_H #include #include #include #include class QIODevice; #include "kritaresources_export.h" class KisTag; typedef QSharedPointer KisTagSP; /** * @brief The KisTag loads a tag from a .tag file. * A .tag file is a .desktop file. The following fields * are important: * * name: the name of the tag, which can be translated * comment: a tooltip for the tag, which can be translagted * url: the untranslated name of the tag * */ class KRITARESOURCES_EXPORT KisTag { public: KisTag(); virtual ~KisTag(); KisTag(const KisTag &rhs); KisTag &operator=(const KisTag &rhs); KisTagSP clone() const; bool valid() const; int id() const; bool active() const; /// The unique identifier for the tag. Since tag urls are compared COLLATE NOCASE, tag urls must be ASCII only. QString url() const; void setUrl(const QString &url); /// The translated name of the tag QString name() const; void setName(const QString &name); /// a translated tooltip for the tag QString comment() const; void setComment(const QString &comment); QStringList defaultResources() const; void setDefaultResources(const QStringList &defaultResources); bool load(QIODevice &io); bool save(QIODevice &io); static bool compareNamesAndUrls(KisTagSP left, KisTagSP right); private: friend class KisTagModel; friend class KisResourceModel; friend class KisTagChooserWidget; - friend class KisTagsResourcesModel; void setId(int id); void setActive(bool active); void setValid(bool valid); static const QByteArray s_group; static const QByteArray s_type; static const QByteArray s_tag; static const QByteArray s_name; static const QByteArray s_url; static const QByteArray s_comment; static const QByteArray s_defaultResources; class Private; QScopedPointer d; }; inline QDebug operator<<(QDebug dbg, const KisTagSP tag) { dbg.space() << "[TAG] Name" << tag->name() << "Url" << tag->url() << "Comment" << tag->comment() << "Default resources" << tag->defaultResources().join(", "); return dbg.space(); } #endif // KISTAGLOADER_H diff --git a/libs/resources/KisTagFilterResourceProxyModel.cpp b/libs/resources/KisTagFilterResourceProxyModel.cpp index bea2e73433..8b85ef6800 100644 --- a/libs/resources/KisTagFilterResourceProxyModel.cpp +++ b/libs/resources/KisTagFilterResourceProxyModel.cpp @@ -1,228 +1,228 @@ /* * 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 "KisTagFilterResourceProxyModel.h" #include #include #include #include struct KisTagFilterResourceProxyModel::Private { Private() : filter(new KisResourceSearchBoxFilter()) { } QList tags; - KisTagsResourcesModel* tagsResourcesModel; + KisTagModel* tagModel; QScopedPointer filter; }; -KisTagFilterResourceProxyModel::KisTagFilterResourceProxyModel(KisTagsResourcesModel* model, QObject *parent) +KisTagFilterResourceProxyModel::KisTagFilterResourceProxyModel(KisTagModel* model, QObject *parent) : QSortFilterProxyModel(parent) , d(new Private) { - d->tagsResourcesModel = model; - connect(model, SIGNAL(modelReset()), this, SLOT(slotModelReset())); + d->tagModel = model; + //connect(model, SIGNAL(modelReset()), this, SLOT(slotModelReset())); } KisTagFilterResourceProxyModel::~KisTagFilterResourceProxyModel() { delete d; } KoResourceSP KisTagFilterResourceProxyModel::resourceForIndex(QModelIndex index) const { KisAbstractResourceModel *source = dynamic_cast(sourceModel()); if (source) { return source->resourceForIndex(mapToSource(index)); } return 0; } QModelIndex KisTagFilterResourceProxyModel::indexFromResource(KoResourceSP resource) const { KisAbstractResourceModel *source = dynamic_cast(sourceModel()); if (source) { return mapFromSource(source->indexFromResource(resource)); } return QModelIndex(); } bool KisTagFilterResourceProxyModel::removeResource(const QModelIndex &index) { KisAbstractResourceModel *source = dynamic_cast(sourceModel()); if (source) { return source->removeResource(mapToSource(index)); } return false; } bool KisTagFilterResourceProxyModel::importResourceFile(const QString &filename) { KisAbstractResourceModel *source = dynamic_cast(sourceModel()); if (source) { return source->importResourceFile(filename); } return false; } bool KisTagFilterResourceProxyModel::addResource(KoResourceSP resource, bool save) { KisAbstractResourceModel *source = dynamic_cast(sourceModel()); if (source) { return source->addResource(resource, save); } return false; } bool KisTagFilterResourceProxyModel::updateResource(KoResourceSP resource) { KisAbstractResourceModel *source = dynamic_cast(sourceModel()); if (source) { return source->updateResource(resource); } return false; } bool KisTagFilterResourceProxyModel::removeResource(KoResourceSP resource) { KisAbstractResourceModel *source = dynamic_cast(sourceModel()); if (source) { return source->removeResource(resource); } return false; } bool KisTagFilterResourceProxyModel::setResourceMetaData(KoResourceSP resource, QMap metadata) { KisAbstractResourceModel *source = dynamic_cast(sourceModel()); if (source) { return source->setResourceMetaData(resource, metadata); } return false; } void KisTagFilterResourceProxyModel::setTag(const KisTagSP tag) { fprintf(stderr, "we're setting a tag!: %s\n", tag->name().toUtf8().toStdString().c_str()); ENTER_FUNCTION(); //d->tags = tag.split(QRegExp("[,]\\s*"), QString::SkipEmptyParts); d->tags.clear(); if (!tag.isNull()) { d->tags << tag; } invalidateFilter(); } void KisTagFilterResourceProxyModel::setSearchBoxText(const QString& seatchBoxText) { d->filter->setFilter(seatchBoxText); invalidateFilter(); } bool KisTagFilterResourceProxyModel::filterAcceptsColumn(int /*source_column*/, const QModelIndex &/*source_parent*/) const { return true; } bool KisTagFilterResourceProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { //fprintf(stderr, "1 "); - if (d->tagsResourcesModel == 0) { + if (d->tagModel == 0) { return true; } //fprintf(stderr, "2 "); QModelIndex idx = sourceModel()->index(source_row, KisResourceModel::Name, source_parent); int resourceId = sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Id).toInt(); QString resourceName = sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Name).toString(); //QStringList tags = sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Tags).toStringList(); - QVector tagsForResource = d->tagsResourcesModel->tagsForResource(resourceId); + QVector tagsForResource = d->tagModel->tagsForResource(resourceId); //QString name = sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Tags).toString(); // TODO: RESOURCES: proper filtering by tag //fprintf(stderr, "3 "); KisTagSP tag = d->tags.isEmpty() ? KisTagSP() : d->tags.first(); bool hasCurrentTag = false; //fprintf(stderr, "4 "); //fprintf(stderr, "tag_first:_%s ", (tag.isNull() ? "(null)" : tag->name().toStdString().c_str())); if (tag.isNull()) { hasCurrentTag = true; } if (!hasCurrentTag && !tag.isNull() && tag->url() == "All") { hasCurrentTag = true; } //fprintf(stderr, "5-%d ", hasCurrentTag); if (!hasCurrentTag && !tag.isNull()) { Q_FOREACH(KisTagSP temp, tagsForResource) { if (temp->url() == tag->url()) { hasCurrentTag = true; break; } } } //fprintf(stderr, "6-%d ", hasCurrentTag); if (!hasCurrentTag) { //fprintf(stderr, "end :( \n"); return false; } //fprintf(stderr, "7-%d ", hasCurrentTag); bool currentFilterMatches = d->filter->matchesResource(resourceName); //fprintf(stderr, "8-%d \n", hasCurrentTag); return currentFilterMatches; //sourceModel()->data(idx, ) /* QSet tagResult = tags.toSet().subtract(tags.toSet()); if (!tagResult.isEmpty()) { return true; } QString name = sourceModel()->data(idx, Qt::UserRole + KisResourceModel::Name).toString(); Q_FOREACH(const KisTagSP &tag, d->tags) { if (name.startsWith(tag)) { return true; } } */ return false; } bool KisTagFilterResourceProxyModel::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 KisTagFilterResourceProxyModel::slotModelReset() { invalidateFilter(); } diff --git a/libs/resources/KisTagFilterResourceProxyModel.h b/libs/resources/KisTagFilterResourceProxyModel.h index 478b80fc0b..4d6ef07c50 100644 --- a/libs/resources/KisTagFilterResourceProxyModel.h +++ b/libs/resources/KisTagFilterResourceProxyModel.h @@ -1,76 +1,76 @@ /* * 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 #include "kritaresources_export.h" class KRITARESOURCES_EXPORT KisTagFilterResourceProxyModel : public QSortFilterProxyModel, public KisAbstractResourceModel { Q_OBJECT public: - KisTagFilterResourceProxyModel(KisTagsResourcesModel* model = 0, QObject *parent = 0); + 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, bool save = true) override; bool updateResource(KoResourceSP resource) 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); 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(KisTagFilterResourceProxyModel) }; #endif // KISTAGFILTERRESOURCEPROXYMODEL_H diff --git a/libs/resources/KisTagModel.cpp b/libs/resources/KisTagModel.cpp index 913a49c260..e6d7ea1336 100644 --- a/libs/resources/KisTagModel.cpp +++ b/libs/resources/KisTagModel.cpp @@ -1,510 +1,536 @@ /* * Copyright (c) 2018 boud * Copyright (c) 2020 Agata Cacko * * 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 "KisTagModel.h" #include #include #include #include #include #include -#include -#include +#include Q_DECLARE_METATYPE(QSharedPointer) struct KisTagModel::Private { QSqlQuery query; - QSqlQuery tagForResourceQuery; + + QSqlQuery tagsForResourceQuery; + QSqlQuery resourcesForTagQuery; + QString resourceType; int columnCount {5}; int cachedRowCount {-1}; int fakeRowsCount {1}; }; KisTagModel::KisTagModel(const QString &resourceType, QObject *parent) : QAbstractTableModel(parent) , d(new Private()) { d->resourceType = resourceType; if (!d->resourceType.isEmpty()) { prepareQuery(); } } KisTagModel::~KisTagModel() { delete d; } int KisTagModel::rowCount(const QModelIndex &/*parent*/) const { if (d->cachedRowCount < 0) { QSqlQuery q; q.prepare("SELECT count(*)\n" "FROM tags\n" ", resource_types\n" "WHERE active = 1\n" "AND tags.resource_type_id = resource_types.id\n" "AND resource_types.name = :resource_type\n" "AND tags.storage_id in (SELECT id\n" " FROM storages\n" " WHERE active == 1)"); q.bindValue(":resource_type", d->resourceType); q.exec(); q.first(); const_cast(this)->d->cachedRowCount = q.value(0).toInt() + d->fakeRowsCount; } return d->cachedRowCount; } int KisTagModel::columnCount(const QModelIndex &/*parent*/) const { return d->columnCount; } QVariant KisTagModel::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; // The first row is All // XXX: Should we also add an "All Untagged"? if (index.row() < d->fakeRowsCount) { switch(role) { case Qt::DisplayRole: // fallthrough case Qt::ToolTipRole: // fallthrough case Qt::StatusTipRole: // fallthrough case Qt::WhatsThisRole: return i18n("All"); case Qt::UserRole + Id: return "-1"; case Qt::UserRole + Url: { return "All"; } case Qt::UserRole + ResourceType: return d->resourceType; case Qt::UserRole + Active: return true; case Qt::UserRole + KisTagRole: { KisTagSP tag = tagForIndex(index); QVariant response; response.setValue(tag); return response; } default: ; } } else { bool pos = const_cast(this)->d->query.seek(index.row() - d->fakeRowsCount); if (pos) { switch(role) { case Qt::DisplayRole: return d->query.value("name"); case Qt::ToolTipRole: // fallthrough case Qt::StatusTipRole: // fallthrough case Qt::WhatsThisRole: return d->query.value("comment"); case Qt::UserRole + Id: return d->query.value("id"); case Qt::UserRole + Url: return d->query.value("url"); case Qt::UserRole + ResourceType: return d->query.value("resource_type"); case Qt::UserRole + Active: return d->query.value("active"); case Qt::UserRole + KisTagRole: { KisTagSP tag = tagForIndex(index); QVariant response; response.setValue(tag); return response; } default: ; } } } return v; } void KisTagModel::setResourceType(const QString &resourceType) { d->resourceType = resourceType; prepareQuery(); } KisTagSP KisTagModel::tagForIndex(QModelIndex index) const { KisTagSP tag = 0; if (!index.isValid()) return tag; if (index.row() > rowCount()) return tag; if (index.column() > columnCount()) return tag; if (index.row() < d->fakeRowsCount) { tag.reset(new KisTag()); tag->setName("All"); tag->setUrl("All"); tag->setComment("All"); tag->setId(-1); tag->setActive(true); tag->setValid(true); } else { bool pos = const_cast(this)->d->query.seek(index.row() - d->fakeRowsCount); if (pos) { tag.reset(new KisTag()); tag->setUrl(d->query.value("url").toString()); tag->setName(d->query.value("name").toString()); tag->setComment(d->query.value("comment").toString()); tag->setId(d->query.value("id").toInt()); tag->setActive(d->query.value("active").toBool()); tag->setValid(true); } } return tag; } bool KisTagModel::addEmptyTag(const QString& tagName, QVector taggedResouces) { KisTagSP tag = KisTagSP(new KisTag()); tag->setName(tagName); tag->setUrl(tagName); return addEmptyTag(tag, taggedResouces); } bool KisTagModel::addEmptyTag(const KisTagSP tag, QVector taggedResouces) { tag->setValid(true); tag->setActive(true); return addTag(tag, taggedResouces); } bool KisTagModel::addTag(const KisTagSP tag, QVector taggedResouces) { if (tag.isNull()) return false; if (!tag) return false; if (!tag->valid()) return false; // A new tag doesn't have an ID yet, that comes from the database if (tag->id() >= 0) return false; if (!KisResourceCacheDb::hasTag(tag->url(), d->resourceType)) { if (!KisResourceCacheDb::addTag(d->resourceType, "", tag->url(), tag->name(), tag->comment())) { qWarning() << "Could not add tag" << tag; return false; } } else { QSqlQuery q; if (!q.prepare("UPDATE tags\n" "SET active = 1\n" "WHERE url = :url\n" "AND resource_type_id = (SELECT id\n" " FROM resource_types\n" " WHERE name = :resource_type\n)")) { qWarning() << "Couild not prepare make existing tag active query" << tag << q.lastError(); return false; } q.bindValue(":url", tag->url()); q.bindValue(":resource_type", d->resourceType); if (!q.exec()) { qWarning() << "Couild not execute make existing tag active query" << q.boundValues(), q.lastError(); return false; } } Q_FOREACH(const KoResourceSP resource, taggedResouces) { if (!resource) continue; if (!resource->valid()) continue; if (resource->resourceId() < 0) continue; tagResource(tag, resource); } return prepareQuery(); } bool KisTagModel::removeTag(const KisTagSP tag) { if (!tag) return false; if (!tag->valid()) return false; if (tag->id() < 0) return false; QSqlQuery q; if (!q.prepare("UPDATE tags\n" "SET active = 0\n" "WHERE id = :id")) { qWarning() << "Could not prepare remove tag query" << q.lastError(); return false; } q.bindValue(":id", tag->id()); if (!q.exec()) { qWarning() << "Could not execute remove tag query" << q.lastError(); return false; } - // reset tags-resources model - KisTagsResourcesModelProvider::getModel(d->resourceType)->resetQuery(); return prepareQuery(); } bool KisTagModel::tagResource(const KisTagSP tag, const KoResourceSP resource) { - return KisTagsResourcesModelProvider::getModel(d->resourceType)->tagResource(tag, resource); - - /* if (!tag) return false; if (!tag->valid()) return false; if (tag->id() < 0) return false; + qDebug() << tag << " tag id " << tag->id(); + if (!resource) return false; if (!resource->valid()) return false; if (resource->resourceId() < 0) return false; QSqlQuery q; bool r = q.prepare("INSERT INTO resource_tags\n" "(resource_id, tag_id)\n" "VALUES\n" "( (SELECT id\n" " FROM resources\n" " WHERE id = :resource_id)\n" ", (SELECT id\n" " FROM tags\n" - " WHERE url = :url\n" - " AND name = :name\n" - " AND comment = :comment\n" + " WHERE id = :tag_id\n" " AND resource_type_id = (SELECT id\n" " FROM resource_types\n" " WHERE name = :resource_type" " \n)" " )\n" ")\n"); if (!r) { qWarning() << "Could not prepare insert into resource tags statement" << q.lastError(); return false; } q.bindValue(":resource_id", resource->resourceId()); - q.bindValue(":url", tag->url()); - q.bindValue(":name", tag->name()); - q.bindValue(":comment", tag->comment()); + q.bindValue(":tag_id", tag->id()); q.bindValue(":resource_type", d->resourceType); if (!q.exec()) { qWarning() << "Could not execute insert into resource tags statement" << q.boundValues() << q.lastError(); return false; } - return prepareQuery(); - */ + KisResourceModelProvider::resetModel(d->resourceType); + return true; } bool KisTagModel::untagResource(const KisTagSP tag, const KoResourceSP resource) { - return KisTagsResourcesModelProvider::getModel(d->resourceType)->untagResource(tag, resource); - /* if (!tag) return false; if (!tag->valid()) return false; if (!tag->id()) return false; if (!resource) return false; if (!resource->valid()) return false; if (resource->resourceId() < 0) return false; // we need to delete an entry in resource_tags - bool r = d->query.prepare("DELETE FROM resource_tags\n" + QSqlQuery query; + bool r = query.prepare("DELETE FROM resource_tags\n" "WHERE resource_id = :resource_id\n" "AND tag_id = :tag_id"); if (!r) { - qWarning() << "Could not prepare KisTagModel query" << d->query.lastError(); + qWarning() << "Could not prepare KisTagModel query untagResource " << query.lastError(); } - d->query.bindValue(":resource_id", resource->resourceId()); - d->query.bindValue(":tag_id", tag->id()); + query.bindValue(":resource_id", resource->resourceId()); + query.bindValue(":tag_id", tag->id()); - r = d->query.exec(); + r = query.exec(); if (!r) { - qWarning() << "Could not select tags" << d->query.lastError(); + qWarning() << "Could not select tags" << query.lastError(); } - return prepareQuery(); - */ + KisResourceModelProvider::resetModel(d->resourceType); + return true; } bool KisTagModel::renameTag(const KisTagSP tag, const QString &name) { if (!tag) return false; if (!tag->valid()) return false; if (name.isEmpty()) return false; QSqlQuery q; if (!q.prepare("UPDATE tags\n" "SET name = :name\n" "WHERE url = :url\n" "AND resource_type_id = (SELECT id\n" " FROM resource_types\n" " WHERE name = :resource_type\n)")) { qWarning() << "Couild not prepare make existing tag active query" << tag << q.lastError(); return false; } q.bindValue(":name", name); q.bindValue(":url", tag->url()); q.bindValue(":resource_type", d->resourceType); if (!q.exec()) { qWarning() << "Couild not execute make existing tag active query" << q.boundValues(), q.lastError(); return false; } return prepareQuery(); } bool KisTagModel::changeTagActive(const KisTagSP tag, bool active) { if (!tag) return false; if (!tag->valid()) return false; QSqlQuery q; if (!q.prepare("UPDATE tags\n" "SET active = :active\n" "WHERE url = :url\n" "AND resource_type_id = (SELECT id\n" " FROM resource_types\n" " WHERE name = :resource_type\n)")) { qWarning() << "Couild not prepare make existing tag active query" << tag << q.lastError(); return false; } q.bindValue(":active", active); q.bindValue(":url", tag->url()); q.bindValue(":resource_type", d->resourceType); if (!q.exec()) { qWarning() << "Couild not execute make existing tag active query" << q.boundValues(), q.lastError(); return false; } return prepareQuery(); } QVector KisTagModel::tagsForResource(int resourceId) const { - return KisTagsResourcesModelProvider::getModel(d->resourceType)->tagsForResource(resourceId); + bool r = d->tagsForResourceQuery.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"); // make sure we're looking for tags for a specific resource + if (!r) { + qWarning() << "Could not prepare TagsForResource query" << d->tagsForResourceQuery.lastError(); + } - /* - d->tagForResourceQuery.bindValue(":resource_id", resourceId); - bool r = d->tagForResourceQuery.exec(); + d->tagsForResourceQuery.bindValue(":resource_id", resourceId); + r = d->tagsForResourceQuery.exec(); if (!r) { - qWarning() << "Could not select tags for" << resourceId << d->tagForResourceQuery.lastError() << d->tagForResourceQuery.boundValues(); + qWarning() << "Could not select tags for" << resourceId << d->tagsForResourceQuery.lastError() << d->tagsForResourceQuery.boundValues(); } QVector tags; - while (d->tagForResourceQuery.next()) { + while (d->tagsForResourceQuery.next()) { //qDebug() << d->tagQuery.value(0).toString() << d->tagQuery.value(1).toString() << d->tagQuery.value(2).toString(); KisTagSP tag(new KisTag()); - tag->setId(d->tagForResourceQuery.value("id").toInt()); - tag->setUrl(d->tagForResourceQuery.value("url").toString()); - tag->setName(d->tagForResourceQuery.value("name").toString()); - tag->setComment(d->tagForResourceQuery.value("comment").toString()); + tag->setId(d->tagsForResourceQuery.value("id").toInt()); + tag->setUrl(d->tagsForResourceQuery.value("url").toString()); + tag->setName(d->tagsForResourceQuery.value("name").toString()); + tag->setComment(d->tagsForResourceQuery.value("comment").toString()); tag->setValid(true); tag->setActive(true); tags << tag; } return tags; - */ +} + +QVector KisTagModel::resourcesForTag(int tagId) const +{ + bool r = d->resourcesForTagQuery.prepare("SELECT DISTINCT resources.id\n" + "FROM resources\n" + ", resource_tags\n" + ", storages\n" + ", resource_types\n" + "WHERE resources.id = resource_tags.resource_id\n" // join resources + resource_tags by resource_id + "AND resources.resource_type_id = resource_types.id\n" // join with resource types via resource type id + "AND resources.storage_id = storages.id\n" // join with storages via storage id + "AND resource_tags.tag_id = :tag_id\n" // must have the tag + "AND resource_types.name = :resource_type\n" // the type must match the current type + "AND resources.status = 1\n" // must be active itself + "AND storages.active = 1"); // must be from active storage + if (!r) { + qWarning() << "Could not prepare ResourcesForTag query" << d->resourcesForTagQuery.lastError(); + } + + d->resourcesForTagQuery.bindValue(":tag_id", tagId); + d->resourcesForTagQuery.bindValue(":resource_type", d->resourceType); + + r = d->resourcesForTagQuery.exec(); + if (!r) { + qWarning() << "Could not select resources for tag " << tagId << d->resourcesForTagQuery.lastError() << d->resourcesForTagQuery.boundValues(); + } + + QVector resources; + qDebug() << "it's around: " << d->resourcesForTagQuery.size() << " resources for " << d->resourceType; + while (d->resourcesForTagQuery.next()) { + qDebug() << "KisTagModel::resourcesForTag(int tagId): ### query: " << d->resourcesForTagQuery.value("id").toInt(); + KoResourceSP resource = KisResourceLocator::instance()->resourceForId(d->resourcesForTagQuery.value("id").toInt()); + resources << resource; + } + + return resources; + } bool KisTagModel::prepareQuery() { beginResetModel(); bool r = d->query.prepare("SELECT tags.id\n" ", tags.url\n" ", tags.name\n" ", tags.comment\n" ", resource_types.name as resource_type\n" "FROM tags\n" ", resource_types\n" "WHERE tags.resource_type_id = resource_types.id\n" "AND resource_types.name = :resource_type\n" "AND tags.active = 1\n" "AND tags.storage_id in (SELECT id\n" " FROM storages\n" " WHERE active == 1)"); if (!r) { qWarning() << "Could not prepare KisTagModel query" << d->query.lastError(); } d->query.bindValue(":resource_type", d->resourceType); r = d->query.exec(); if (!r) { qWarning() << "Could not select tags" << d->query.lastError(); } - r = d->tagForResourceQuery.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"); // make sure we're looking for tags for a specific resource - if (!r) { - qWarning() << "Could not prepare TagsForResource query" << d->tagForResourceQuery.lastError(); - } - - KisTagsResourcesModelProvider::resetModel(d->resourceType); - d->cachedRowCount = -1; endResetModel(); return r; } diff --git a/libs/resources/KisTagModel.h b/libs/resources/KisTagModel.h index e44aa290ff..fdc4179574 100644 --- a/libs/resources/KisTagModel.h +++ b/libs/resources/KisTagModel.h @@ -1,92 +1,94 @@ /* * Copyright (c) 2018 boud * Copyright (c) 2020 Agata Cacko * * 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 KISTAGMODEL_H #define KISTAGMODEL_H #include #include #include #include #include "kritaresources_export.h" class KRITARESOURCES_EXPORT KisTagModel : public QAbstractTableModel { Q_OBJECT private: KisTagModel(const QString &resourceType, QObject *parent = 0); public: enum Columns { Id = 0, Url, Name, Comment, ResourceType, Active, KisTagRole, }; ~KisTagModel() override; 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; // KisTagModel API KisTagSP tagForIndex(QModelIndex index = QModelIndex()) const; QList allTags() const; bool addEmptyTag(const QString &tagName, QVector taggedResouces); bool addEmptyTag(const KisTagSP tag, QVector taggedResouces); bool addTag(const KisTagSP tag, QVector taggedResouces = QVector()); bool removeTag(const KisTagSP tag); bool tagResource(const KisTagSP tag, const KoResourceSP resource); bool untagResource(const KisTagSP tag, const KoResourceSP resource); bool renameTag(const KisTagSP tag, const QString &name); bool changeTagActive(const KisTagSP tag, bool active); QVector tagsForResource(int resourceId) const; + QVector resourcesForTag(int tagId) const; + private: friend class DlgDbExplorer; friend class KisTagModelProvider; friend class TestTagModel; void setResourceType(const QString &resourceType); bool prepareQuery(); struct Private; Private* const d; }; typedef QSharedPointer KisTagModelSP; #endif // KISTAGMODEL_H diff --git a/libs/resources/KisTagModelProvider.cpp b/libs/resources/KisTagModelProvider.cpp index 59a5c64a1e..99e59bdf0f 100644 --- a/libs/resources/KisTagModelProvider.cpp +++ b/libs/resources/KisTagModelProvider.cpp @@ -1,84 +1,82 @@ /* * Copyright (c) 2019 Agata Cacko * * 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 "KisTagModelProvider.h" #include #include #include -#include - Q_GLOBAL_STATIC(KisTagModelProvider, s_instance) struct KisTagModelProvider::Private { std::map> tagModelsMap; }; KisTagModelProvider::KisTagModelProvider() : d(new Private()) { } KisTagModelProvider::~KisTagModelProvider() { delete d; } KisTagModel* KisTagModelProvider::tagModel(const QString& resourceType) { std::map >::const_iterator found = s_instance->d->tagModelsMap.find(resourceType); if (found == s_instance->d->tagModelsMap.end()) { KisTagModel* model = new KisTagModel(resourceType); s_instance->d->tagModelsMap.insert(std::make_pair(resourceType, model)); return model; } return found->second.get(); } void KisTagModelProvider::resetModels() { typedef std::map>::iterator mapIterator; mapIterator begin = s_instance->d->tagModelsMap.begin(); mapIterator end = s_instance->d->tagModelsMap.end(); for (mapIterator iter = begin; iter!=end; iter++) { begin->second->prepareQuery(); } } void KisTagModelProvider::resetModel(const QString& resourceType) { std::map >::const_iterator found = s_instance->d->tagModelsMap.find(resourceType); if (found != s_instance->d->tagModelsMap.end()) { found->second->prepareQuery(); } } diff --git a/libs/resourcewidgets/KisResourceItemChooser.cpp b/libs/resourcewidgets/KisResourceItemChooser.cpp index 809f67a257..eaf9e79c2d 100644 --- a/libs/resourcewidgets/KisResourceItemChooser.cpp +++ b/libs/resourcewidgets/KisResourceItemChooser.cpp @@ -1,547 +1,548 @@ /* This file is part of the KDE project Copyright (c) 2002 Patrick Julien Copyright (c) 2007 Jan Hambrecht Copyright (c) 2007 Sven Langkamp Copyright (C) 2011 Srikanth Tiyyagura Copyright (c) 2011 José Luis Vergara Copyright (c) 2013 Sascha Suelzer 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 "KisResourceItemChooser.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisResourceItemListView.h" #include "KisResourceItemDelegate.h" #include "KisTagFilterWidget.h" #include "KisTagChooserWidget.h" #include "KisResourceItemChooserSync.h" #include "KisResourceTaggingManager.h" -#include "KisTagsResourcesModelProvider.h" +#include "KisTagModelProvider.h" + #include "KisStorageChooserWidget.h" #include "kis_assert.h" class Q_DECL_HIDDEN KisResourceItemChooser::Private { public: Private(QString _resourceType) : resourceType(_resourceType) {} QString resourceType; KisResourceModel *resourceModel {0}; KisTagFilterResourceProxyModel *tagFilterProxyModel {0}; QSortFilterProxyModel *extraFilterModel {0}; KisResourceTaggingManager *tagManager {0}; KisResourceItemListView *view {0}; QButtonGroup *buttonGroup {0}; QToolButton *viewModeButton {0}; KisStorageChooserWidget *storagePopupButton {0}; QScrollArea *previewScroller {0}; QLabel *previewLabel {0}; QSplitter *splitter {0}; QGridLayout *buttonLayout {0}; QPushButton *importButton {0}; QPushButton *deleteButton {0}; bool usePreview {false}; bool tiledPreview {false}; bool grayscalePreview {false}; bool synced {false}; bool updatesBlocked {false}; QModelIndex savedResourceWhileReset; // Indexes on the proxyModel, not the source resource model QList customButtons; }; KisResourceItemChooser::KisResourceItemChooser(const QString &resourceType, bool usePreview, QWidget *parent, QSortFilterProxyModel *extraFilterProxy) : QWidget(parent) , d(new Private(resourceType)) { d->splitter = new QSplitter(this); d->resourceModel = KisResourceModelProvider::resourceModel(resourceType); - d->tagFilterProxyModel = new KisTagFilterResourceProxyModel(KisTagsResourcesModelProvider::getModel(resourceType), this); + d->tagFilterProxyModel = new KisTagFilterResourceProxyModel(KisTagModelProvider::tagModel(resourceType), this); d->extraFilterModel = extraFilterProxy; if (d->extraFilterModel) { d->extraFilterModel->setParent(this); d->extraFilterModel->setSourceModel(d->resourceModel); d->tagFilterProxyModel->setSourceModel(d->extraFilterModel); } else { d->tagFilterProxyModel->setSourceModel(d->resourceModel); } connect(d->resourceModel, SIGNAL(beforeResourcesLayoutReset(QModelIndex)), SLOT(slotBeforeResourcesLayoutReset(QModelIndex))); connect(d->resourceModel, SIGNAL(afterResourcesLayoutReset()), SLOT(slotAfterResourcesLayoutReset())); d->view = new KisResourceItemListView(this); d->view->setObjectName("ResourceItemview"); d->view->setModel(d->tagFilterProxyModel); d->view->setItemDelegate(new KisResourceItemDelegate(this)); d->view->setSelectionMode(QAbstractItemView::SingleSelection); d->view->viewport()->installEventFilter(this); connect(d->view, SIGNAL(currentResourceChanged(QModelIndex)), this, SLOT(activated(QModelIndex))); connect(d->view, SIGNAL(currentResourceClicked(QModelIndex)), this, SLOT(clicked(QModelIndex))); connect(d->view, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); connect(d->view, SIGNAL(sigSizeChanged()), this, SLOT(updateView())); d->splitter->addWidget(d->view); d->splitter->setStretchFactor(0, 2); d->usePreview = usePreview; if (d->usePreview) { d->previewScroller = new QScrollArea(this); d->previewScroller->setWidgetResizable(true); d->previewScroller->setBackgroundRole(QPalette::Dark); d->previewScroller->setVisible(true); d->previewScroller->setAlignment(Qt::AlignCenter); d->previewLabel = new QLabel(this); d->previewScroller->setWidget(d->previewLabel); d->splitter->addWidget(d->previewScroller); if (d->splitter->count() == 2) { d->splitter->setSizes(QList() << 280 << 160); } QScroller* scroller = KisKineticScroller::createPreconfiguredScroller(d->previewScroller); if (scroller) { connect(scroller, SIGNAL(stateChanged(QScroller::State)), this, SLOT(slotScrollerStateChanged(QScroller::State))); } } d->splitter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); connect(d->splitter, SIGNAL(splitterMoved(int,int)), SIGNAL(splitterMoved())); d->buttonGroup = new QButtonGroup(this); d->buttonGroup->setExclusive(false); QGridLayout *layout = new QGridLayout(this); d->buttonLayout = new QGridLayout(); d->importButton = new QPushButton(this); d->importButton->setToolTip(i18nc("@info:tooltip", "Import resource")); d->importButton->setEnabled(true); d->buttonGroup->addButton(d->importButton, Button_Import); d->buttonLayout->addWidget(d->importButton, 0, 0); d->deleteButton = new QPushButton(this); d->deleteButton->setToolTip(i18nc("@info:tooltip", "Delete resource")); d->deleteButton->setEnabled(false); d->buttonGroup->addButton(d->deleteButton, Button_Remove); d->buttonLayout->addWidget(d->deleteButton, 0, 1); connect(d->buttonGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotButtonClicked(int))); d->buttonLayout->setColumnStretch(0, 1); d->buttonLayout->setColumnStretch(1, 1); d->buttonLayout->setColumnStretch(2, 2); d->buttonLayout->setSpacing(0); d->buttonLayout->setMargin(0); d->viewModeButton = new QToolButton(this); d->viewModeButton->setPopupMode(QToolButton::InstantPopup); d->viewModeButton->setVisible(false); d->tagManager = new KisResourceTaggingManager(resourceType, d->tagFilterProxyModel, this); d->storagePopupButton = new KisStorageChooserWidget(this); layout->addWidget(d->tagManager->tagChooserWidget(), 0, 0); layout->addWidget(d->viewModeButton, 0, 1); layout->addWidget(d->storagePopupButton, 0, 2); layout->addWidget(d->splitter, 1, 0, 1, 3); layout->addWidget(d->tagManager->tagFilterWidget(), 2, 0, 1, 3); layout->addLayout(d->buttonLayout, 3, 0, 1, 3); layout->setMargin(0); layout->setSpacing(0); updateView(); updateButtonState(); showTaggingBar(false); activated(d->view->model()->index(0, 0)); } KisResourceItemChooser::~KisResourceItemChooser() { disconnect(); delete d; } void KisResourceItemChooser::slotButtonClicked(int button) { if (button == Button_Import) { QStringList mimeTypes = KisResourceLoaderRegistry::instance()->mimeTypes(d->resourceType); KoFileDialog dialog(0, KoFileDialog::OpenFiles, "OpenDocument"); dialog.setMimeTypeFilters(mimeTypes); dialog.setCaption(i18nc("@title:window", "Choose File to Add")); Q_FOREACH(const QString &filename, dialog.filenames()) { if (QFileInfo(filename).exists() && QFileInfo(filename).isReadable()) { d->tagFilterProxyModel->importResourceFile(filename); } } } else if (button == Button_Remove) { QModelIndex index = d->view->currentIndex(); if (index.isValid()) { d->tagFilterProxyModel->removeResource(index); } int row = index.row(); int rowMin = --row; row = qBound(0, rowMin, row); setCurrentItem(row); activated(d->tagFilterProxyModel->index(row, index.column())); } updateButtonState(); } void KisResourceItemChooser::showButtons(bool show) { foreach (QAbstractButton * button, d->buttonGroup->buttons()) { show ? button->show() : button->hide(); } Q_FOREACH (QAbstractButton *button, d->customButtons) { show ? button->show() : button->hide(); } } void KisResourceItemChooser::addCustomButton(QAbstractButton *button, int cell) { d->buttonLayout->addWidget(button, 0, cell); d->buttonLayout->setColumnStretch(2, 1); d->buttonLayout->setColumnStretch(3, 1); } void KisResourceItemChooser::showTaggingBar(bool show) { d->tagManager->showTaggingBar(show); } int KisResourceItemChooser::rowCount() const { return d->view->model()->rowCount(); } void KisResourceItemChooser::setRowHeight(int rowHeight) { d->view->setItemSize(QSize(d->view->gridSize().width(), rowHeight)); } void KisResourceItemChooser::setColumnWidth(int columnWidth) { d->view->setItemSize(QSize(columnWidth, d->view->gridSize().height())); } void KisResourceItemChooser::setItemDelegate(QAbstractItemDelegate *delegate) { d->view->setItemDelegate(delegate); } KoResourceSP KisResourceItemChooser::currentResource() const { QModelIndex index = d->view->currentIndex(); if (index.isValid()) { return resourceFromModelIndex(index); } return 0; } void KisResourceItemChooser::setCurrentResource(KoResourceSP resource) { // don't update if the change came from the same chooser if (d->updatesBlocked) { return; } QModelIndex index = d->resourceModel->indexFromResource(resource); d->view->setCurrentIndex(index); updatePreview(index); } void KisResourceItemChooser::slotBeforeResourcesLayoutReset(QModelIndex activateAfterReset) { QModelIndex proxyIndex = d->tagFilterProxyModel->mapFromSource(d->tagFilterProxyModel->mapFromSource(activateAfterReset)); d->savedResourceWhileReset = proxyIndex.isValid() ? proxyIndex : d->view->currentIndex(); } void KisResourceItemChooser::slotAfterResourcesLayoutReset() { if (d->savedResourceWhileReset.isValid()) { this->blockSignals(true); setCurrentItem(d->savedResourceWhileReset.row()); this->blockSignals(false); } d->savedResourceWhileReset = QModelIndex(); } void KisResourceItemChooser::setPreviewOrientation(Qt::Orientation orientation) { d->splitter->setOrientation(orientation); } void KisResourceItemChooser::setPreviewTiled(bool tiled) { d->tiledPreview = tiled; } void KisResourceItemChooser::setGrayscalePreview(bool grayscale) { d->grayscalePreview = grayscale; } void KisResourceItemChooser::setCurrentItem(int row) { QModelIndex index = d->view->model()->index(row, 0); if (!index.isValid()) return; d->view->setCurrentIndex(index); if (index.isValid()) { updatePreview(index); } } void KisResourceItemChooser::activated(const QModelIndex &index) { if (!index.isValid()) return; KoResourceSP resource = 0; if (index.isValid()) { resource = resourceFromModelIndex(index); } if (resource && resource->valid()) { d->updatesBlocked = true; emit resourceSelected(resource); d->updatesBlocked = false; updatePreview(index); updateButtonState(); } } void KisResourceItemChooser::clicked(const QModelIndex &index) { Q_UNUSED(index); KoResourceSP resource = currentResource(); if (resource) { emit resourceClicked(resource); } } void KisResourceItemChooser::updateButtonState() { QAbstractButton *removeButton = d->buttonGroup->button(Button_Remove); if (! removeButton) return; KoResourceSP resource = currentResource(); if (resource) { removeButton->setEnabled(!resource->permanent()); return; } removeButton->setEnabled(false); } void KisResourceItemChooser::updatePreview(const QModelIndex &idx) { if (!d->usePreview) return; if (!idx.isValid()) { d->previewLabel->setPixmap(QPixmap()); return; } QImage image = idx.data(Qt::UserRole + KisResourceModel::Image).value(); if (image.format() != QImage::Format_RGB32 && image.format() != QImage::Format_ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) { image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); } if (d->tiledPreview) { int width = d->previewScroller->width() * 4; int height = d->previewScroller->height() * 4; QImage img(width, height, image.format()); QPainter gc(&img); gc.fillRect(img.rect(), Qt::white); gc.setPen(Qt::NoPen); gc.setBrush(QBrush(image)); gc.drawRect(img.rect()); image = img; } // Only convert to grayscale if it is rgb. Otherwise, it's gray already. if (d->grayscalePreview && !image.isGrayscale()) { QRgb *pixel = reinterpret_cast(image.bits()); for (int row = 0; row < image.height(); ++row) { for (int col = 0; col < image.width(); ++col) { const QRgb currentPixel = pixel[row * image.width() + col]; const int red = qRed(currentPixel); const int green = qGreen(currentPixel); const int blue = qBlue(currentPixel); const int grayValue = (red * 11 + green * 16 + blue * 5) / 32; pixel[row * image.width() + col] = qRgb(grayValue, grayValue, grayValue); } } } d->previewLabel->setPixmap(QPixmap::fromImage(image)); } KoResourceSP KisResourceItemChooser::resourceFromModelIndex(const QModelIndex &index) const { if (!index.isValid()) { return 0; } KoResourceSP r = d->tagFilterProxyModel->resourceForIndex(index); return r; } QSize KisResourceItemChooser::viewSize() const { return d->view->size(); } KisResourceItemListView *KisResourceItemChooser::itemView() const { return d->view; } void KisResourceItemChooser::contextMenuRequested(const QPoint &pos) { d->tagManager->contextMenuRequested(currentResource(), pos); } void KisResourceItemChooser::setViewModeButtonVisible(bool visible) { d->viewModeButton->setVisible(visible); } QToolButton *KisResourceItemChooser::viewModeButton() const { return d->viewModeButton; } void KisResourceItemChooser::setSynced(bool sync) { if (d->synced == sync) return; d->synced = sync; KisResourceItemChooserSync *chooserSync = KisResourceItemChooserSync::instance(); if (sync) { connect(chooserSync, SIGNAL(baseLengthChanged(int)), SLOT(baseLengthChanged(int))); baseLengthChanged(chooserSync->baseLength()); } else { chooserSync->disconnect(this); } } void KisResourceItemChooser::baseLengthChanged(int length) { if (d->synced) { d->view->setItemSize(QSize(length, length)); } } bool KisResourceItemChooser::eventFilter(QObject *object, QEvent *event) { if (d->synced && event->type() == QEvent::Wheel) { KisResourceItemChooserSync *chooserSync = KisResourceItemChooserSync::instance(); QWheelEvent *qwheel = static_cast(event); if (qwheel->modifiers() & Qt::ControlModifier) { int degrees = qwheel->delta() / 8; int newBaseLength = chooserSync->baseLength() + degrees / 15 * 10; chooserSync->setBaseLength(newBaseLength); return true; } } return QObject::eventFilter(object, event); } void KisResourceItemChooser::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); updateView(); } void KisResourceItemChooser::showEvent(QShowEvent *event) { QWidget::showEvent(event); updateView(); } void KisResourceItemChooser::updateView() { if (d->synced) { KisResourceItemChooserSync *chooserSync = KisResourceItemChooserSync::instance(); baseLengthChanged(chooserSync->baseLength()); } /// helps to set icons here in case the theme is changed d->viewModeButton->setIcon(koIcon("view-choose")); d->importButton->setIcon(koIcon("document-open")); d->deleteButton->setIcon(koIcon("trash-empty")); d->storagePopupButton->setIcon(koIcon("bundle_archive")); } diff --git a/libs/resourcewidgets/KisResourceItemChooserContextMenu.cpp b/libs/resourcewidgets/KisResourceItemChooserContextMenu.cpp index 53175e42b2..4542a82433 100644 --- a/libs/resourcewidgets/KisResourceItemChooserContextMenu.cpp +++ b/libs/resourcewidgets/KisResourceItemChooserContextMenu.cpp @@ -1,335 +1,333 @@ /* This file is part of the KDE project * Copyright (c) 2013 Sascha Suelzer * Copyright (c) 2020 Agata Cacko * * 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 "KisResourceItemChooserContextMenu.h" #include #include #include #include #include #include -#include #include #include #include "kis_debug.h" KoLineEditAction::KoLineEditAction(QObject* parent) : QWidgetAction(parent) , m_closeParentOnTrigger(false) { fprintf(stderr, "created a new KoLineEditAction\n"); QWidget* pWidget = new QWidget (0); QHBoxLayout* pLayout = new QHBoxLayout(); m_label = new QLabel(0); m_editBox = new QLineEdit(0); m_editBox->setClearButtonEnabled(true); m_AddButton = new QPushButton(); m_AddButton->setIcon(koIcon("list-add")); pLayout->addWidget(m_label); pLayout->addWidget(m_editBox); pLayout->addWidget(m_AddButton); pWidget->setLayout(pLayout); setDefaultWidget(pWidget); connect (m_editBox, &QLineEdit::returnPressed, this, &KoLineEditAction::onTriggered); connect (m_AddButton, &QPushButton::clicked, this, &KoLineEditAction::onTriggered); } KoLineEditAction::~KoLineEditAction() { } void KoLineEditAction::setIcon(const QIcon &icon) { QPixmap pixmap = QPixmap(icon.pixmap(16,16)); m_label->setPixmap(pixmap); } void KoLineEditAction::closeParentOnTrigger(bool closeParent) { m_closeParentOnTrigger = closeParent; } bool KoLineEditAction::closeParentOnTrigger() { return m_closeParentOnTrigger; } void KoLineEditAction::onTriggered() { ENTER_FUNCTION(); fprintf(stderr, "void KoLineEditAction::onTriggered()"); if (! m_editBox->text().isEmpty()) { KisTagSP tag(new KisTag()); tag->setName(m_editBox->text()); tag->setUrl(m_editBox->text()); emit triggered(tag); m_editBox->text().clear(); if (m_closeParentOnTrigger) { this->parentWidget()->close(); m_editBox->clearFocus(); } } } void KoLineEditAction::setPlaceholderText(const QString& clickMessage) { m_editBox->setPlaceholderText(clickMessage); } void KoLineEditAction::setText(const QString& text) { ENTER_FUNCTION(); m_editBox->setText(text); } void KoLineEditAction::setVisible(bool showAction) { QLayout* currentLayout = defaultWidget()->layout(); this->QAction::setVisible(showAction); for(int i=0;icount();i++) { currentLayout->itemAt(i)->widget()->setVisible(showAction); } defaultWidget()->setVisible(showAction); } ContextMenuExistingTagAction::ContextMenuExistingTagAction(KoResourceSP resource, KisTagSP tag, QObject* parent) : QAction(parent) , m_resource(resource) , m_tag(tag) { fprintf(stderr, "ContextMenuExistingTagAction created for: %s\n", tag->name().toUtf8().toStdString().c_str()); setText(tag->name()); connect (this, SIGNAL(triggered()), this, SLOT(onTriggered())); } ContextMenuExistingTagAction::~ContextMenuExistingTagAction() { } void ContextMenuExistingTagAction::onTriggered() { fprintf(stderr, "void ContextMenuExistingTagAction::onTriggered()\n"); ENTER_FUNCTION(); emit triggered(m_resource, m_tag); } NewTagAction::~NewTagAction() { } NewTagAction::NewTagAction(KoResourceSP resource, QMenu* parent) :KoLineEditAction (parent) { fprintf(stderr, "NewTagAction created\n"); m_resource = resource; setIcon(koIcon("document-new")); setPlaceholderText(i18n("New tag")); closeParentOnTrigger(true); connect (this, SIGNAL(triggered(KisTagSP)), this, SLOT(onTriggered(KisTagSP))); } void NewTagAction::onTriggered(const KisTagSP tag) { emit triggered(m_resource,tag); } class CompareWithOtherTagFunctor { KisTagSP m_referenceTag; public: CompareWithOtherTagFunctor(KisTagSP referenceTag) { m_referenceTag = referenceTag; } bool operator()(KisTagSP otherTag) { return !otherTag.isNull() && otherTag->url() == m_referenceTag->url(); } void setReferenceTag(KisTagSP referenceTag) { m_referenceTag = referenceTag; } }; bool compareWithSpecialTags(KisTagSP tag) { // TODO: RESOURCES: id < 0? For now, "All" fits return !tag.isNull() && tag->id() < 0; } KisResourceItemChooserContextMenu::KisResourceItemChooserContextMenu(KoResourceSP resource, const KisTagSP currentlySelectedTag) : m_resourceType(resource->resourceType().first) { QImage image = resource->image(); QIcon icon(QPixmap::fromImage(image)); QAction * label = new QAction(resource->name(), this); label->setIcon(icon); addAction(label); QMenu * removableTagsMenu; QMenu * assignableTagsMenu; - m_tagsResourcesModel = KisTagsResourcesModelProvider::getModel(resource->resourceType().first); m_tagModel = KisTagModelProvider::tagModel(resource->resourceType().first); - QList removables = m_tagsResourcesModel->tagsForResource(resource->resourceId()).toList(); + QList removables = m_tagModel->tagsForResource(resource->resourceId()).toList(); QList list; for (int i = 0; i < m_tagModel->rowCount(); i++) { QModelIndex index = m_tagModel->index(i, 0); KisTagSP tag = m_tagModel->tagForIndex(index); if (!tag.isNull()) { list << tag; } } QList assignables2 = list; CompareWithOtherTagFunctor comparer(currentlySelectedTag); //std::sort(removables.begin(), removables.end(), KisTag::compareNamesAndUrls); //std::sort(assignables2.begin(), assignables2.end(), KisTag::compareNamesAndUrls); bool currentTagInRemovables = !currentlySelectedTag.isNull(); currentTagInRemovables = currentTagInRemovables && (std::find_if(removables.begin(), removables.end(), comparer) != removables.end()); // remove "All" tag from both "Remove from tag: " and "Assign to tag: " lists std::remove_if(removables.begin(), removables.end(), compareWithSpecialTags); std::remove_if(assignables2.begin(), assignables2.end(), compareWithSpecialTags); assignableTagsMenu = addMenu(koIcon("list-add"),i18n("Assign to tag")); if (!removables.isEmpty()) { addSeparator(); KisTagSP currentTag = currentlySelectedTag; if (!currentTag.isNull() && currentTagInRemovables) { // remove the current tag from both "Remove from tag: " and "Assign to tag: " lists std::remove_if(removables.begin(), removables.end(), comparer); std::remove_if(assignables2.begin(), assignables2.end(), comparer); ContextMenuExistingTagAction * removeTagAction = new ContextMenuExistingTagAction(resource, currentTag, this); removeTagAction->setText(i18n("Remove from this tag")); removeTagAction->setIcon(koIcon("list-remove")); connect(removeTagAction, SIGNAL(triggered(KoResourceSP, const KisTagSP)), this, SLOT(removeResourceExistingTag(KoResourceSP, const KisTagSP))); addAction(removeTagAction); } if (!removables.isEmpty()) { removableTagsMenu = addMenu(koIcon("list-remove"),i18n("Remove from other tag")); KisTagSP empty; CompareWithOtherTagFunctor compareWithRemovable(empty); foreach (const KisTagSP tag, removables) { if (tag.isNull()) { continue; } compareWithRemovable.setReferenceTag(tag); std::remove_if(assignables2.begin(), assignables2.end(), compareWithRemovable); ContextMenuExistingTagAction * removeTagAction = new ContextMenuExistingTagAction(resource, tag, this); connect(removeTagAction, SIGNAL(triggered(KoResourceSP, const KisTagSP)), this, SLOT(removeResourceExistingTag(KoResourceSP, const KisTagSP))); removableTagsMenu->addAction(removeTagAction); } } } foreach (const KisTagSP &tag, assignables2) { if (tag.isNull()) { continue; } ContextMenuExistingTagAction * addTagAction = new ContextMenuExistingTagAction(resource, tag, this); connect(addTagAction, SIGNAL(triggered(KoResourceSP, const KisTagSP)), this, SLOT(addResourceTag(KoResourceSP, const KisTagSP))); assignableTagsMenu->addAction(addTagAction); } assignableTagsMenu->addSeparator(); NewTagAction * addTagAction = new NewTagAction(resource, this); connect(addTagAction, SIGNAL(triggered(KoResourceSP, const KisTagSP)), this, SLOT(addResourceNewTag(KoResourceSP, const KisTagSP))); assignableTagsMenu->addAction(addTagAction); } KisResourceItemChooserContextMenu::~KisResourceItemChooserContextMenu() { } void KisResourceItemChooserContextMenu::addResourceTag(KoResourceSP resource, const KisTagSP tag) { - m_tagsResourcesModel->tagResource(tag, resource); + m_tagModel->tagResource(tag, resource); } void KisResourceItemChooserContextMenu::removeResourceExistingTag(KoResourceSP resource, const KisTagSP tag) { - m_tagsResourcesModel->untagResource(tag, resource); + m_tagModel->untagResource(tag, resource); } void KisResourceItemChooserContextMenu::addResourceNewTag(KoResourceSP resource, const KisTagSP tag) { QString name = tag->name().isEmpty() ? tag->url() : tag->name(); QVector resourceList; resourceList << resource; m_tagModel->addEmptyTag(tag, resourceList); } diff --git a/libs/resourcewidgets/KisResourceItemChooserContextMenu.h b/libs/resourcewidgets/KisResourceItemChooserContextMenu.h index b237d70ded..11f00fab5e 100644 --- a/libs/resourcewidgets/KisResourceItemChooserContextMenu.h +++ b/libs/resourcewidgets/KisResourceItemChooserContextMenu.h @@ -1,132 +1,129 @@ /* * This file is part of the KDE project * Copyright (c) 2013 Sascha Suelzer * Copyright (c) 2019 Boudewijn Rempt * Copyright (c) 2020 Agata Cacko * * 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 KISRESOURCEITEMCHOOSERCONTEXTMENU_H #define KISRESOURCEITEMCHOOSERCONTEXTMENU_H #include #include #include #include #include #include #include - -#include #include class ContextMenuExistingTagAction : public QAction { Q_OBJECT public: explicit ContextMenuExistingTagAction( KoResourceSP resource, KisTagSP tag, QObject* parent = 0); ~ContextMenuExistingTagAction() override; Q_SIGNALS: void triggered(KoResourceSP resource, KisTagSP tag); protected Q_SLOTS: void onTriggered(); private: KoResourceSP m_resource; KisTagSP m_tag; }; /*! * A line edit QWidgetAction. * Default behavior: Closes its parent upon triggering. */ class KoLineEditAction : public QWidgetAction { Q_OBJECT public: explicit KoLineEditAction(QObject* parent); ~KoLineEditAction() override; void setIcon(const QIcon &icon); void closeParentOnTrigger(bool closeParent); bool closeParentOnTrigger(); void setPlaceholderText(const QString& clickMessage); void setText(const QString& text); void setVisible(bool showAction); Q_SIGNALS: void triggered(const KisTagSP tag); protected Q_SLOTS: void onTriggered(); private: bool m_closeParentOnTrigger; QLabel * m_label; QLineEdit * m_editBox; QPushButton * m_AddButton; }; class NewTagAction : public KoLineEditAction { Q_OBJECT public: explicit NewTagAction (KoResourceSP resource, QMenu* parent); ~NewTagAction() override; Q_SIGNALS: void triggered(KoResourceSP resource, const KisTagSP tag); protected Q_SLOTS: void onTriggered(const KisTagSP tagName); private: KoResourceSP m_resource; }; class KisResourceItemChooserContextMenu : public QMenu { Q_OBJECT public: explicit KisResourceItemChooserContextMenu(KoResourceSP resource, const KisTagSP currentlySelectedTag); ~KisResourceItemChooserContextMenu() override; Q_SIGNALS: /// Emitted when a resource should be added to an existing tag. void resourceTagAdditionRequested(KoResourceSP resource, const KisTagSP tag); /// Emitted when a resource should be removed from an existing tag. void resourceTagRemovalRequested(KoResourceSP resource, const KisTagSP tag); /// Emitted when a resource should be added to a new tag, which will need to be created. void resourceAssignmentToNewTagRequested(KoResourceSP resource, const KisTagSP tag); public Q_SLOTS: void addResourceTag(KoResourceSP resource, const KisTagSP tag); void removeResourceExistingTag(KoResourceSP resource, const KisTagSP tag); void addResourceNewTag(KoResourceSP resource, const KisTagSP tag); private: QString m_resourceType; - KisTagsResourcesModel* m_tagsResourcesModel; KisTagModel* m_tagModel; }; #endif // KORESOURCEITEMCHOOSERCONTEXTMENU_H diff --git a/libs/resourcewidgets/dbexplorer/DlgDbExplorer.cpp b/libs/resourcewidgets/dbexplorer/DlgDbExplorer.cpp index 485f39e968..bd796e6cb6 100644 --- a/libs/resourcewidgets/dbexplorer/DlgDbExplorer.cpp +++ b/libs/resourcewidgets/dbexplorer/DlgDbExplorer.cpp @@ -1,221 +1,220 @@ /* * 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 "DlgDbExplorer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include DlgDbExplorer::DlgDbExplorer(QWidget *parent) : KoDialog(parent) { setCaption(i18n("Resources Cache Database Explorer")); setButtons(Ok); m_page = new WdgDbExplorer(this); Q_CHECK_PTR(m_page); setMainWidget(m_page); m_resourceTypeModel = new KisResourceTypeModel(this); m_tagModel = new KisTagModel("", this); { TableModel *storagesModel = new TableModel(this, QSqlDatabase::database()); TableDelegate *storagesDelegate = new TableDelegate(m_page->tableStorages); storagesModel->setTable("storages"); storagesModel->setHeaderData(0, Qt::Horizontal, i18n("Id")); storagesModel->setHeaderData(1, Qt::Horizontal, i18n("Type")); storagesModel->setRelation(1, QSqlRelation("storage_types", "id", "name")); storagesModel->setHeaderData(2, Qt::Horizontal, i18n("Location")); storagesModel->setHeaderData(3, Qt::Horizontal, i18n("Creation Date")); storagesModel->addDateTimeColumn(3); storagesDelegate->addDateTimeColumn(3); storagesModel->setHeaderData(4, Qt::Horizontal, i18n("Preinstalled")); storagesModel->addBooleanColumn(4); storagesDelegate->addBooleanColumn(4); storagesModel->setHeaderData(5, Qt::Horizontal, i18n("Active")); storagesModel->addBooleanColumn(5); storagesDelegate->addBooleanColumn(5); storagesModel->select(); m_page->tableStorages->setModel(storagesModel); m_page->tableStorages->hideColumn(0); m_page->tableStorages->setItemDelegate(storagesDelegate); m_page->tableStorages->setSelectionMode(QAbstractItemView::SingleSelection); m_page->tableStorages->resizeColumnsToContents(); } { KisResourceModel *resourcesModel = KisResourceModelProvider::resourceModel(ResourceType::Brushes); m_page->tableResources->setModel(resourcesModel); m_page->tableResources->hideColumn(0); m_page->tableResources->setSelectionMode(QAbstractItemView::SingleSelection); m_page->cmbResourceTypes->setModel(m_resourceTypeModel); m_page->cmbResourceTypes->setModelColumn(KisResourceTypeModel::Name); connect(m_page->cmbResourceTypes, SIGNAL(activated(int)), SLOT(slotTbResourceTypeSelected(int))); connect(m_page->tableResources, SIGNAL(clicked(QModelIndex)), SLOT(slotTbResourceItemSelected())); } { TableModel *tagsModel = new TableModel(this, QSqlDatabase::database()); TableDelegate *tagsDelegate = new TableDelegate(m_page->tableStorages); tagsDelegate->setEditable(true); tagsModel->setTable("tags"); tagsModel->setHeaderData(0, Qt::Horizontal, i18n("Id")); tagsModel->setHeaderData(1, Qt::Horizontal, i18n("Type")); tagsModel->setRelation(1, QSqlRelation("resource_types", "id", "name")); tagsModel->setHeaderData(2, Qt::Horizontal, i18n("Storage ID")); tagsModel->setHeaderData(3, Qt::Horizontal, i18n("Tag")); tagsModel->setHeaderData(4, Qt::Horizontal, i18n("Name")); tagsModel->setHeaderData(5, Qt::Horizontal, i18n("Comment")); tagsModel->setHeaderData(6, Qt::Horizontal, i18n("Active")); tagsModel->addBooleanColumn(6); tagsDelegate->addBooleanColumn(6); tagsModel->select(); m_page->tableTags->setModel(tagsModel); m_page->tableTags->hideColumn(0); m_page->tableTags->setItemDelegate(tagsDelegate); m_page->tableTags->setSelectionMode(QAbstractItemView::SingleSelection); m_page->tableTags->resizeColumnsToContents(); } { QSqlTableModel *versionModel = new QSqlTableModel(this, QSqlDatabase::database()); versionModel->setTable("version_information"); versionModel->setHeaderData(0, Qt::Horizontal, "id"); versionModel->setHeaderData(1, Qt::Horizontal, "database_version"); versionModel->setHeaderData(2, Qt::Horizontal, "krita_version"); versionModel->setHeaderData(3, Qt::Horizontal, "creation_date"); versionModel->select(); QSqlRecord r = versionModel->record(0); m_page->lblDatabaseVersion->setText(r.value("database_version").toString()); m_page->lblKritaVersion->setText(r.value("krita_version").toString()); m_page->lblCreationDate->setText(r.value("creation_date").toString()); } { m_page->cmbRvResourceTypes->setModel(m_resourceTypeModel); m_page->cmbRvResourceTypes->setModelColumn(KisResourceTypeModel::Name); connect(m_page->cmbRvResourceTypes, SIGNAL(activated(int)), SLOT(slotRvResourceTypeSelected(int))); m_page->cmbRvTags->setModelColumn(KisTagModel::Name); m_page->cmbRvTags->setModel(m_tagModel); connect(m_page->cmbRvTags, SIGNAL(activated(int)), SLOT(slotRvTagSelected(int))); m_page->cmbRvResourceTypes->setCurrentIndex(0); slotRvResourceTypeSelected(0); m_page->resourceItemView->setItemDelegate(new KisResourceItemDelegate(this)); m_page->resourceItemView->setSelectionMode(QAbstractItemView::SingleSelection); } } DlgDbExplorer::~DlgDbExplorer() { } void DlgDbExplorer::updateTagModel(const QString& resourceType) { m_tagModel = KisTagModelProvider::tagModel(resourceType); m_page->cmbRvTags->setModelColumn(KisTagModel::Name); m_page->cmbRvTags->setModel(m_tagModel); m_page->cmbRvTags->update(); qDebug() << "number of tags in " << resourceType << " tag model: " << m_tagModel->rowCount(); } void DlgDbExplorer::slotRvResourceTypeSelected(int index) { QModelIndex idx = m_page->cmbResourceTypes->model()->index(index, KisResourceTypeModel::ResourceType); QString resourceType = idx.data(Qt::DisplayRole).toString(); qDebug() << resourceType; updateTagModel(resourceType); KisResourceModel *resourceModel = KisResourceModelProvider::resourceModel(resourceType); - KisTagFilterResourceProxyModel *tagFilterModel = new KisTagFilterResourceProxyModel(KisTagsResourcesModelProvider::getModel(resourceType), this); + KisTagFilterResourceProxyModel *tagFilterModel = new KisTagFilterResourceProxyModel(KisTagModelProvider::tagModel(resourceType), this); tagFilterModel->setSourceModel(resourceModel); m_filterProxyModel = tagFilterModel; m_page->resourceItemView->setModel(tagFilterModel); } void DlgDbExplorer::slotTbResourceTypeSelected(int index) { QModelIndex idx = m_page->cmbRvResourceTypes->model()->index(index, KisResourceTypeModel::ResourceType); QString resourceType = idx.data(Qt::DisplayRole).toString(); qDebug() << resourceType; m_tagModel = KisTagModelProvider::tagModel(resourceType); KisResourceModel *resourceModel = KisResourceModelProvider::resourceModel(resourceType); m_page->tableResources->setModel(resourceModel); m_page->tableResources->setCurrentIndex(m_page->tableResources->model()->index(0, 0)); slotTbResourceItemSelected(); m_page->tableResources->resizeColumnsToContents(); } void DlgDbExplorer::slotTbResourceItemSelected() { QModelIndex idx = m_page->tableResources->selectionModel()->selectedIndexes().first(); QImage thumb = idx.data(Qt::UserRole + KisResourceModel::Image).value(); Qt::TransformationMode mode = Qt::SmoothTransformation; if (thumb.size().width() < 100 && thumb.size().height() < 100) { mode = Qt::FastTransformation; } m_page->lblThumbnail->setPixmap(QPixmap::fromImage(thumb.scaled(100, 100, Qt::KeepAspectRatio, mode))); //If we could get a list of versions for a given resource, this would be the moment to add them... } void DlgDbExplorer::slotRvTagSelected(int index) { qDebug() << "selected tag" << index; QModelIndex idx = m_tagModel->index(index, 0); KisTagSP tag = m_tagModel->tagForIndex(idx); if (m_filterProxyModel && !tag.isNull() && tag->valid()) { m_filterProxyModel->setTag(tag); } } diff --git a/libs/widgets/KoResourceServer.h b/libs/widgets/KoResourceServer.h index 9386eadef1..d7d4a1ce70 100644 --- a/libs/widgets/KoResourceServer.h +++ b/libs/widgets/KoResourceServer.h @@ -1,302 +1,298 @@ /* This file is part of the KDE project Copyright (c) 1999 Matthias Elter Copyright (c) 2003 Patrick Julien Copyright (c) 2005 Sven Langkamp Copyright (c) 2007 Jan Hambrecht Copyright (C) 2011 Srikanth Tiyyagura Copyright (c) 2013 Sascha Suelzer Copyright (c) 2003-2019 Boudewijn Rempt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KORESOURCESERVER_H #define KORESOURCESERVER_H #include #include #include #include #include #include #include #include #include #include #include #include "KoResource.h" #include "KoResourceServerObserver.h" #include "KoResourcePaths.h" #include #include #include -#include -#include #include #include #include "kritawidgets_export.h" #include "WidgetsDebug.h" class KoResource; /** * KoResourceServer manages the resources of one type. It stores, * loads and saves the resources. To keep track of changes the server * can be observed with a KoResourceServerObserver */ template class KoResourceServer { public: typedef KoResourceServerObserver ObserverType; KoResourceServer(const QString& type) : m_resourceModel(KisResourceModelProvider::resourceModel(type)) , m_tagModel(KisTagModelProvider::tagModel(type)) - , m_tagsResourcesModel(KisTagsResourcesModelProvider::getModel(type)) , m_type(type) { //Q_ASSERT(QThread::currentThread() == qApp->thread()); } virtual ~KoResourceServer() { Q_FOREACH (ObserverType* observer, m_observers) { observer->unsetResourceServer(); } } /// @return the active resource model KisResourceModel *resourceModel() const { QMutexLocker l(&m_mutex); //Q_ASSERT(QThread::currentThread() == qApp->thread()); return m_resourceModel; } /// Return the first resource available QSharedPointer firstResource() const { QMutexLocker l(&m_mutex); //Q_ASSERT(QThread::currentThread() == qApp->thread()); return m_resourceModel->resourceForIndex(m_resourceModel->index(0, 0)).dynamicCast(); } int resourceCount() const { QMutexLocker l(&m_mutex); //Q_ASSERT(QThread::currentThread() == qApp->thread()); return m_resourceModel->rowCount(); } /// Adds an already loaded resource to the server bool addResource(QSharedPointer resource, bool save = true) { QMutexLocker l(&m_mutex); //Q_ASSERT(QThread::currentThread() == qApp->thread()); if (!resource->valid()) { warnWidgets << "Tried to add an invalid resource!"; return false; } if (m_resourceModel->addResource(resource, save)) { notifyResourceAdded(resource); return true; } return false; } /// Remove a resource from Resource Server but not from a file bool removeResourceFromServer(QSharedPointer resource){ QMutexLocker l(&m_mutex); //Q_ASSERT(QThread::currentThread() == qApp->thread()); if (m_resourceModel->removeResource(resource)) { notifyRemovingResource(resource); return true; } return false; } QList> resources() { QMutexLocker l(&m_mutex); qDebug() << "KoResourceServer::resources()" << m_type; //Q_ASSERT(QThread::currentThread() == qApp->thread()); //Q_ASSERT(m_type != "paintoppresets"); QList> resourceList; for (int row = 0; row < m_resourceModel->rowCount(); ++row) { resourceList << m_resourceModel->resourceForIndex(m_resourceModel->index(row, 0)).dynamicCast(); } return resourceList; } /// Returns path where to save user defined and imported resources to QString saveLocation() { return KoResourcePaths::saveLocation(m_type.toLatin1()); } /** * Creates a new resource from a given file and adds them to the resource server * The base implementation does only load one resource per file, override to implement collections * @param filename file name of the resource file to be imported * @param fileCreation decides whether to create the file in the saveLocation() directory */ bool importResourceFile(const QString &filename) { QMutexLocker l(&m_mutex); //Q_ASSERT(QThread::currentThread() == qApp->thread()); return m_resourceModel->importResourceFile(filename); } /// Removes the resource file from the resource server void removeResourceFile(const QString & filename) { QFileInfo fi(filename); QSharedPointer resource = resourceByFilename(fi.fileName()); if (!resource) { warnWidgets << "Resource file do not exist "; return; } removeResourceFromServer(resource); } /** * Addes an observer to the server * @param observer the observer to be added * @param notifyLoadedResources determines if the observer should be notified about the already loaded resources */ void addObserver(ObserverType* observer) { if (observer && !m_observers.contains(observer)) { m_observers.append(observer); } } /** * Removes an observer from the server * @param observer the observer to be removed */ void removeObserver(ObserverType* observer) { int index = m_observers.indexOf(observer); if (index < 0) { return; } m_observers.removeAt( index ); } QSharedPointer resourceByFilename(const QString& filename) const { QMutexLocker l(&m_mutex); qDebug() << "resourceByFilename" << filename; // if (m_resourcesByFilename.contains(filename)) { // return m_resourcesByFilename[filename]; // } if (filename.isEmpty() || filename.isNull()) { return 0; } return m_resourceModel->resourceForFilename(filename).dynamicCast(); } QSharedPointer resourceByName(const QString& name) const { QMutexLocker l(&m_mutex); qDebug() << "resourceByName" << name; if (name.isEmpty() || name.isNull()) { return 0; } return m_resourceModel->resourceForName(name).dynamicCast(); // if (m_resourcesByName.contains(name)) { // return m_resourcesByName[name]; // } //return 0; } QSharedPointer resourceByMD5(const QByteArray& md5) const { //This needs MD5 sums to be implemented properly... QMutexLocker l(&m_mutex); qDebug() << "resourceByMD5" << md5; // return m_resourcesByMd5.value(md5); return 0; } /** * Call after changing the content of a resource; * Notifies the connected views. */ void updateResource(QSharedPointer resource) { QMutexLocker l(&m_mutex); //Q_ASSERT(QThread::currentThread() == qApp->thread()); m_resourceModel->updateResource(resource); notifyResourceChanged(resource); } QVector assignedTagsList(KoResourceSP resource) const { if (resource.isNull()) { return QVector(); } return m_resourceModel->tagsForResource(resource->resourceId()); } QVector resourcesForTag(KisTagSP tag) const { if (tag.isNull()) { return QVector(); } - return m_tagsResourcesModel->resourcesForTag(tag->id()); + return m_tagModel->resourcesForTag(tag->id()); } protected: void notifyResourceAdded(QSharedPointer resource) { Q_FOREACH (ObserverType* observer, m_observers) { observer->resourceAdded(resource); } } void notifyRemovingResource(QSharedPointer resource) { Q_FOREACH (ObserverType* observer, m_observers) { observer->removingResource(resource); } } void notifyResourceChanged(QSharedPointer resource) { Q_FOREACH (ObserverType* observer, m_observers) { observer->resourceChanged(resource); } } private: QList m_observers; KisTagModel *m_tagModel {0}; - KisTagsResourcesModel *m_tagsResourcesModel {0}; KisResourceModel *m_resourceModel {0}; QString m_type; mutable QMutex m_mutex; }; #endif // KORESOURCESERVER_H