diff --git a/libs/resources/KisResourceModel.cpp b/libs/resources/KisResourceModel.cpp index f7eafaef3c..ab7ecc4959 100644 --- a/libs/resources/KisResourceModel.cpp +++ b/libs/resources/KisResourceModel.cpp @@ -1,606 +1,606 @@ /* * 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 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: ; }; } 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: /* Falls through. */ case Qt::StatusTipRole: /* Falls through. */ 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"; + 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++; qDebug() << "KisResourceModel::updateResource" << resource->name(); 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; */ } 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/KisTagsResourcesModel.cpp b/libs/resources/KisTagsResourcesModel.cpp index 17f4167902..062a7c4e97 100644 --- a/libs/resources/KisTagsResourcesModel.cpp +++ b/libs/resources/KisTagsResourcesModel.cpp @@ -1,279 +1,326 @@ /* * 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 "KisTagsResourcesModel.h" #include #include #include +#include struct KisTagsResourcesModel::Private { QSqlQuery query; + QSqlQuery resourcesForTagQuery; QString resourceType; int columnCount {3}; int cachedRowCount {-1}; }; KisTagsResourcesModel::KisTagsResourcesModel(const QString &resourceType, QObject *parent) : QAbstractTableModel(parent) , d(new Private()) { d->resourceType = resourceType; if (!d->resourceType.isEmpty()) { prepareQuery(); } } KisTagsResourcesModel::~KisTagsResourcesModel() { delete d; } int KisTagsResourcesModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return 0; if (d->cachedRowCount < 0) { QSqlQuery q; q.prepare("SELECT count(*)\n" "FROM resource_tags\n" ", resource_types\n" ", tags\n" "WHERE tags.resource_type_id = resource_types.id\n" "AND resource_types.name = :resource_type\n" "AND tags.id = resource_tags.id\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(); } return d->cachedRowCount; } int KisTagsResourcesModel::columnCount(const QModelIndex &parent) const { if (!parent.isValid()) return 0; return d->columnCount; } QVariant KisTagsResourcesModel::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->query.seek(index.row()); if (pos) { switch(role) { case Qt::DisplayRole: return QVariant(d->query.value("id").toString() + ": " + d->query.value("resource_id").toString() + ", " + d->query.value("tag_id").toString()); case Qt::ToolTipRole: // fallthrough case Qt::StatusTipRole: // fallthrough case Qt::WhatsThisRole: return QVariant("Tag/Resource relationship: " + d->query.value("id").toString() + ": " + d->query.value("resource_id").toString() + ", " + d->query.value("tag_id").toString()); case Qt::UserRole + Id: return d->query.value("id"); case Qt::UserRole + ResourceId: return d->query.value("resource_id"); case Qt::UserRole + TagId: return d->query.value("tag_id"); default: ; } } return v; } bool KisTagsResourcesModel::tagResource(const KisTagSP tag, const KoResourceSP resource) { if (!tag) return false; if (!tag->valid()) return false; if (tag->id() < 0) return false; 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" " 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(":resource_type", d->resourceType); if (!q.exec()) { qWarning() << "Could not execute insert into resource tags statement" << q.boundValues() << q.lastError(); return false; } return prepareQuery(); } bool KisTagsResourcesModel::untagResource(const KisTagSP tag, const KoResourceSP 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 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 KisTagsResourcesModel query" << query.lastError(); } query.bindValue(":resource_id", resource->resourceId()); query.bindValue(":tag_id", tag->id()); r = query.exec(); if (!r) { qWarning() << "Could not select tags" << query.lastError(); } return prepareQuery(); } QVector KisTagsResourcesModel::tagsForResource(int resourceId) const { d->query.bindValue(":resource_id", resourceId); bool r = d->query.exec(); if (!r) { qWarning() << "Could not select tags for" << resourceId << d->query.lastError() << d->query.boundValues(); } QVector tags; while (d->query.next()) { //qDebug() << d->tagQuery.value(0).toString() << d->tagQuery.value(1).toString() << d->tagQuery.value(2).toString(); KisTagSP tag(new KisTag()); tag->setId(d->query.value("id").toInt()); tag->setUrl(d->query.value("url").toString()); tag->setName(d->query.value("name").toString()); tag->setComment(d->query.value("comment").toString()); tag->setValid(true); tag->setActive(true); tags << tag; } return tags; } +QVector KisTagsResourcesModel::resourcesForTag(int tagId) const +{ + d->resourcesForTagQuery.bindValue(":tag_id", tagId); + d->resourcesForTagQuery.bindValue(":resource_type", d->resourceType); + + bool r = d->resourcesForTagQuery.exec(); + if (!r) { + qWarning() << "Could not select resources for tag " << tagId << d->resourcesForTagQuery.lastError() << d->resourcesForTagQuery.boundValues(); + } + + return QVector(); +} + void KisTagsResourcesModel::setResourceType(const QString &resourceType) { d->resourceType = resourceType; prepareQuery(); } bool KisTagsResourcesModel::resetQuery() { + // since it always execute a query, maybe there is no need to reset anything? + // which also means we don't keep anything here... + return true; + + /* QTime t; t.start(); beginResetModel(); bool r = d->query.exec(); if (!r) { qWarning() << "Could not select" << d->resourceType << "resources" << d->query.lastError() << d->query.boundValues(); } d->cachedRowCount = -1; endResetModel(); //emit afterResourcesLayoutReset(); qDebug() << "KisTagsResourcesModel::resetQuery for" << d->resourceType << "took" << t.elapsed() << "ms"; return r; + */ } bool KisTagsResourcesModel::prepareQuery() { beginResetModel(); bool r = d->query.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->query.lastError(); } + r = d->resourcesForTagQuery.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_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->cachedRowCount = -1; endResetModel(); return r; } diff --git a/libs/resources/KisTagsResourcesModel.h b/libs/resources/KisTagsResourcesModel.h index fc19e76fa7..1d99a10a0c 100644 --- a/libs/resources/KisTagsResourcesModel.h +++ b/libs/resources/KisTagsResourcesModel.h @@ -1,72 +1,73 @@ /* * 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. */ #ifndef KIS_TAGS_RESOURCES_MODEL_H #define KIS_TAGS_RESOURCES_MODEL_H #include #include "kritaresources_export.h" #include #include #include class KRITARESOURCES_EXPORT KisTagsResourcesModel : public QAbstractTableModel { Q_OBJECT private: KisTagsResourcesModel(const QString &resourceType, QObject *parent = 0); public: enum Columns { Id = 0, ResourceId, TagId, }; ~KisTagsResourcesModel() 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; void setResourceType(const QString &resourceType); bool tagResource(const KisTagSP tag, const KoResourceSP resource); bool untagResource(const KisTagSP tag, const KoResourceSP resource); QVector tagsForResource(int resourceId) const; + QVector resourcesForTag(int tagId) const; bool resetQuery(); private: bool prepareQuery(); friend class KisTagsResourcesModelProvider; struct Private; Private * d; }; #endif // KIS_TAGS_RESOURCES_MODEL_H diff --git a/libs/widgets/KoResourceServer.h b/libs/widgets/KoResourceServer.h index cb3002372d..8a08547038 100644 --- a/libs/widgets/KoResourceServer.h +++ b/libs/widgets/KoResourceServer.h @@ -1,284 +1,295 @@ /* 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 "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 { return m_resourceModel->tagsForResource(resource->resourceId()); } + QVector resourcesForTag(KisTagSP tag) const + { + return m_tagsResourcesModel->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