File indexing completed on 2024-05-19 04:27:41

0001 /*
0002  * SPDX-FileCopyrightText: 2018 boud <boud@valdyas.org>
0003  * SPDX-FileCopyrightText: 2020 Agata Cacko <cacko.azh@gmail.com>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 #include "KisTagModel.h"
0008 
0009 #include <QtSql>
0010 #include <QStringList>
0011 #include <QElapsedTimer>
0012 
0013 #include <klocalizedstring.h>
0014 
0015 #include <KisResourceLocator.h>
0016 #include <KisResourceCacheDb.h>
0017 #include <KisTag.h>
0018 
0019 #include <KisResourceModelProvider.h>
0020 #include <KisTagResourceModel.h>
0021 #include <KisStorageModel.h>
0022 #include <QVector>
0023 
0024 #include <kis_assert.h>
0025 
0026 static int s_fakeRowsCount {2};
0027 
0028 struct KisAllTagsModel::Private {
0029     QSqlQuery query;
0030     QString resourceType;
0031     int columnCount {5};
0032     int cachedRowCount {-1};
0033 };
0034 
0035 
0036 KisAllTagsModel::KisAllTagsModel(const QString &resourceType, QObject *parent)
0037     : QAbstractTableModel(parent)
0038     , d(new Private())
0039 {
0040     d->resourceType = resourceType;
0041     if (!d->resourceType.isEmpty()) {
0042         resetQuery();
0043     }
0044 
0045     connect(KisResourceLocator::instance(), SIGNAL(storageAdded(const QString&)), this, SLOT(addStorage(const QString&)));
0046     connect(KisResourceLocator::instance(), SIGNAL(storageRemoved(const QString&)), this, SLOT(removeStorage(const QString&)));
0047     connect(KisStorageModel::instance(), SIGNAL(storageEnabled(const QString&)), this, SLOT(addStorage(const QString&)));
0048     connect(KisStorageModel::instance(), SIGNAL(storageDisabled(const QString&)), this, SLOT(removeStorage(const QString&)));
0049 }
0050 
0051 void KisAllTagsModel::untagAllResources(KisTagSP tag)
0052 {
0053     KisTagResourceModel model(d->resourceType);
0054     model.setTagsFilter(QVector<int>() << tag->id());
0055     QVector<int> taggedResources;
0056     for (int i = 0; i < model.rowCount(); i++) {
0057         QModelIndex idx = model.index(i, 0);
0058         taggedResources.append(model.data(idx, Qt::UserRole + KisTagResourceModel::Id).toInt());
0059     }
0060 
0061     model.untagResources(tag, taggedResources);
0062 
0063 }
0064 
0065 KisAllTagsModel::~KisAllTagsModel()
0066 {
0067     delete d;
0068 }
0069 
0070 int KisAllTagsModel::rowCount(const QModelIndex &parent) const
0071 {
0072     if (parent.isValid()) {
0073         return 0;
0074     }
0075 
0076     if (d->cachedRowCount < 0) {
0077         QSqlQuery q;
0078         const bool r = q.prepare("SELECT count(*)\n"
0079                   "FROM   tags\n"
0080                   ",      resource_types\n"
0081                   "LEFT JOIN tag_translations ON tag_translations.tag_id = tags.id AND tag_translations.language = :language\n"
0082                   "WHERE  tags.resource_type_id = resource_types.id\n"
0083                   "AND    resource_types.name = :resource_type\n");
0084         if (!r) {
0085             qWarning() << "Could not execute tags rowcount query" << q.lastError();
0086             return 0;
0087         }
0088         q.bindValue(":resource_type", d->resourceType);
0089         q.bindValue(":language", KisTag::currentLocale());
0090         if (!q.exec()) {
0091             qWarning() << "Could not execute tags rowcount query" << q.lastError();
0092             return 0;
0093         }
0094         q.first();
0095 
0096         const_cast<KisAllTagsModel*>(this)->d->cachedRowCount = q.value(0).toInt() + s_fakeRowsCount;
0097     }
0098     return d->cachedRowCount;
0099 }
0100 
0101 int KisAllTagsModel::columnCount(const QModelIndex &parent) const
0102 {
0103     if (parent.isValid()) {
0104         return 0;
0105     }
0106 
0107     return d->columnCount;
0108 }
0109 
0110 QVariant KisAllTagsModel::data(const QModelIndex &index, int role) const
0111 {
0112     QVariant v;
0113 
0114     if (!index.isValid()) return v;
0115     if (index.row() > rowCount()) return v;
0116     if (index.column() > d->columnCount) return v;
0117 
0118     if (index.row() < s_fakeRowsCount) {
0119         if (index.row() == KisAllTagsModel::All + s_fakeRowsCount) {
0120             switch(role) {
0121             case Qt::DisplayRole:   // fallthrough
0122             case Qt::ToolTipRole:   // fallthrough
0123             case Qt::StatusTipRole: // fallthrough
0124             case Qt::WhatsThisRole:
0125             case Qt::UserRole + Name:
0126                 return i18n("All");
0127             case Qt::UserRole + Id:
0128                 return QString::number(KisAllTagsModel::All);
0129             case Qt::UserRole + Url: {
0130                 return urlAll();
0131             }
0132             case Qt::UserRole + ResourceType:
0133                 return d->resourceType;
0134             case Qt::UserRole + Active:
0135                 return true;
0136             case Qt::UserRole + KisTagRole:
0137             {
0138                 KisTagSP tag = tagForIndex(index);
0139                 QVariant response;
0140                 response.setValue(tag);
0141                 return response;
0142             }
0143             default:
0144                 ;
0145             }
0146         } else if (index.row() == KisAllTagsModel::AllUntagged + s_fakeRowsCount) {
0147             switch(role) {
0148             case Qt::DisplayRole:   // fallthrough
0149             case Qt::ToolTipRole:   // fallthrough
0150             case Qt::StatusTipRole: // fallthrough
0151             case Qt::WhatsThisRole:
0152             case Qt::UserRole + Name:
0153                 return i18n("All Untagged");
0154             case Qt::UserRole + Id:
0155                 return QString::number(KisAllTagsModel::AllUntagged);
0156             case Qt::UserRole + Url: {
0157                 return urlAllUntagged();
0158             }
0159             case Qt::UserRole + ResourceType:
0160                 return d->resourceType;
0161             case Qt::UserRole + Active:
0162                 return true;
0163             case Qt::UserRole + KisTagRole:
0164             {
0165                 KisTagSP tag = tagForIndex(index);
0166                 QVariant response;
0167                 response.setValue(tag);
0168                 return response;
0169             }
0170             default:
0171                 ;
0172             }
0173         }
0174     }
0175     else {
0176         bool pos = const_cast<KisAllTagsModel*>(this)->d->query.seek(index.row() - s_fakeRowsCount);
0177         if (pos) {
0178             switch(role) {
0179             case Qt::DisplayRole:
0180             case Qt::UserRole + Name:
0181             {
0182                 QVariant name = d->query.value("translated_name");
0183                 if (name.isNull()) {
0184                     name = d->query.value("name");
0185                 }
0186                 return name;
0187             }
0188             case Qt::ToolTipRole:   // fallthrough
0189             case Qt::StatusTipRole: // fallthrough
0190             case Qt::WhatsThisRole:
0191             {
0192                 QVariant comment = d->query.value("translated_comment");
0193                 if (comment.isNull()) {
0194                     comment = d->query.value("comment");
0195                 }
0196                 return comment;
0197             }
0198             case Qt::UserRole + Id:
0199                 return d->query.value("id");
0200             case Qt::UserRole + Url:
0201                 return d->query.value("url");
0202             case Qt::UserRole + ResourceType:
0203                 return d->query.value("resource_type");
0204             case Qt::UserRole + Active:
0205                 return d->query.value("active");
0206             case Qt::UserRole + KisTagRole:
0207             {
0208                 KisTagSP tag = tagForIndex(index);
0209                 QVariant response;
0210                 response.setValue(tag);
0211                 return response;
0212             }
0213             default:
0214                 ;
0215             }
0216         }
0217     }
0218     return v;
0219 }
0220 
0221 bool KisAllTagsModel::setData(const QModelIndex &index, const QVariant &value, int role)
0222 {
0223     int id = data(index, Qt::UserRole + Id).toInt();
0224 
0225     if (index.isValid() &&
0226         (role == Qt::UserRole + Active)) {
0227 
0228         QSqlQuery q;
0229         if (!q.prepare("UPDATE tags\n"
0230                        "SET    active = :active\n"
0231                        "WHERE  id = :id\n")) {
0232             qWarning() << "Could not prepare make existing tag active query" << q.lastError();
0233             return false;
0234         }
0235         q.bindValue(":active", value.toBool());
0236         q.bindValue(":id", id);
0237 
0238         if (!q.exec()) {
0239             qWarning() << "Could not execute make existing tag active query" << q.boundValues(), q.lastError();
0240             return false;
0241         }
0242         KisResourceLocator::instance()->purgeTag(data(index, Qt::UserRole + Url).toString(), d->resourceType);
0243     }
0244     resetQuery();
0245     emit dataChanged(index, index, {role});
0246     return true;
0247 }
0248 
0249 Qt::ItemFlags KisAllTagsModel::flags(const QModelIndex &index) const
0250 {
0251     if (!index.isValid()) {
0252         return Qt::NoItemFlags;
0253     }
0254     return QAbstractTableModel::flags(index) | Qt::ItemIsEditable | Qt::ItemNeverHasChildren;
0255 }
0256 
0257 QModelIndex KisAllTagsModel::indexForTag(KisTagSP tag) const
0258 {
0259     if (!tag) return QModelIndex();
0260     // For now a linear seek to find the first tag
0261     if (tag->id() < 0 && (tag->url() == urlAll() || tag->url() == urlAllUntagged())) {
0262         // this must be either a fake tag id, or a "naked" tag
0263         // TODO: do we even use "naked tags"? won't it be better to just use QStrings?
0264         return index(tag->id() + s_fakeRowsCount, 0);
0265     }
0266 
0267     d->query.first();
0268     bool r = d->query.first();
0269     if (!r) {
0270         return QModelIndex();
0271     }
0272     do {
0273         if (tag->id() >= 0) {
0274             if (d->query.value("id").toInt() == tag->id()) {
0275                 return index(d->query.at() + s_fakeRowsCount, 0);
0276             }
0277         }
0278         else {
0279             // This is a naked tag, one that didn't come from the
0280             // database.
0281             // But not a "fake" tag (All or AllUntagged)!
0282             if (d->query.value("url").toString() == tag->url()
0283                     && d->query.value("resource_type") == d->resourceType) {
0284                 return index(d->query.at() + s_fakeRowsCount, 0);
0285             }
0286         }
0287     } while (d->query.next());
0288 
0289     return QModelIndex();
0290 }
0291 
0292 KisTagSP KisAllTagsModel::tagForIndex(QModelIndex index) const
0293 {
0294     KisTagSP tag = 0;
0295     if (!index.isValid()) return tag;
0296     if (index.row() > rowCount()) return tag;
0297     if (index.column() > columnCount()) return tag;
0298 
0299     if (index.row() < s_fakeRowsCount) {
0300         if (index.row() == KisAllTagsModel::All + s_fakeRowsCount) {
0301             tag.reset(new KisTag());
0302             tag->setName(i18n("All"));
0303             tag->setResourceType(d->resourceType);
0304             tag->setUrl(urlAll());
0305             tag->setComment(i18n("All Resources"));
0306             tag->setId(KisAllTagsModel::All);
0307             tag->setActive(true);
0308             tag->setValid(true);
0309         }
0310         else if (index.row() == KisAllTagsModel::AllUntagged + s_fakeRowsCount) {
0311             tag.reset(new KisTag());
0312             tag->setName(i18n("All Untagged"));
0313             tag->setResourceType(d->resourceType);
0314             tag->setUrl(urlAllUntagged());
0315             tag->setComment(i18n("All Untagged Resources"));
0316             tag->setId(KisAllTagsModel::AllUntagged);
0317             tag->setActive(true);
0318             tag->setValid(true);
0319         }
0320     }
0321     else {
0322         bool pos = const_cast<KisAllTagsModel*>(this)->d->query.seek(index.row() - s_fakeRowsCount);
0323         if (pos) {
0324             tag = KisResourceLocator::instance()->tagForUrl(d->query.value("url").toString(), d->resourceType);
0325         }
0326     }
0327 
0328     return tag;
0329 }
0330 
0331 KisTagSP KisAllTagsModel::addTag(const QString& tagName, const bool allowOverwrite, QVector<KoResourceSP> taggedResources)
0332 {
0333     KisTagSP tag = KisTagSP(new KisTag());
0334     tag->setName(tagName);
0335     tag->setUrl(tagName);
0336     tag->setValid(true);
0337     tag->setActive(true);
0338     tag->setResourceType(d->resourceType);
0339 
0340     if (addTag(tag, allowOverwrite, taggedResources)) {
0341         return tag;
0342     }
0343     else {
0344         return 0;
0345     }
0346 }
0347 
0348 
0349 bool KisAllTagsModel::addTag(const KisTagSP tag, const bool allowOverwrite, QVector<KoResourceSP> taggedResources)
0350 {
0351     if (!tag) return false;
0352     if (!tag->valid()) return false;
0353 
0354     bool r = true;
0355 
0356     if (!KisResourceCacheDb::hasTag(tag->url(), d->resourceType)) {
0357         beginInsertRows(QModelIndex(), rowCount(), rowCount());
0358         if (!KisResourceCacheDb::addTag(d->resourceType, "", tag)) {
0359             qWarning() << "Could not add tag" << tag;
0360             return false;
0361         }
0362         resetQuery();
0363         endInsertRows();
0364 
0365     }
0366     else if (allowOverwrite) {
0367         KisTagSP trueTag = tagForUrl(tag->url());
0368         r = setData(indexForTag(trueTag), QVariant::fromValue(true), Qt::UserRole + KisAllTagsModel::Active);
0369         untagAllResources(trueTag);
0370         tag->setComment(trueTag->comment()); // id will be set later, comment and filename are the only thing left
0371         tag->setFilename(trueTag->filename());
0372     }
0373     else {
0374         return false;
0375     }
0376 
0377     tag->setId(data(indexForTag(tag), Qt::UserRole + KisAllTagsModel::Id).toInt());
0378     tag->setValid(true);
0379     tag->setActive(data(indexForTag(tag), Qt::UserRole + KisAllTagsModel::Active).toInt());
0380 
0381     if (!taggedResources.isEmpty()) {
0382         QVector<int> resourceIds;
0383         Q_FOREACH(const KoResourceSP resource, taggedResources) {
0384 
0385             if (!resource) continue;
0386             if (!resource->valid()) continue;
0387             if (resource->resourceId() < 0) continue;
0388 
0389             resourceIds << resource->resourceId();
0390         }
0391         KisTagResourceModel(d->resourceType).tagResources(tag, resourceIds);
0392     }
0393 
0394     return r;
0395 }
0396 
0397 bool KisAllTagsModel::setTagActive(const KisTagSP tag)
0398 {
0399     if (!tag) return false;
0400     if (!tag->valid()) return false;
0401 
0402     tag->setActive(true);
0403 
0404     return setData(indexForTag(tag), QVariant::fromValue(true), Qt::UserRole + KisAllTagsModel::Active);
0405 
0406 }
0407 
0408 bool KisAllTagsModel::setTagInactive(const KisTagSP tag)
0409 {
0410     if (!tag) return false;
0411     if (!tag->valid()) return false;
0412 
0413     tag->setActive(false);
0414 
0415     return setData(indexForTag(tag), QVariant::fromValue(false), Qt::UserRole + KisAllTagsModel::Active);
0416 }
0417 
0418 bool KisAllTagsModel::renameTag(const KisTagSP tag, const QString &newName, const bool allowOverwrite)
0419 {
0420     if (!tag) return false;
0421     if (!tag->valid()) return false;
0422     if (tag->id() < 0) return false;
0423 
0424     if (newName.isEmpty()) return false;
0425 
0426     KisTagSP dstTag = tagForUrl(newName);
0427 
0428     if (dstTag && dstTag->active() && !allowOverwrite) {
0429         return false;
0430     }
0431 
0432     if (!dstTag) {
0433         dstTag = addTag(newName, false, {});
0434     } else {
0435         if (!dstTag->active()) {
0436             setTagActive(dstTag);
0437         }
0438         untagAllResources(dstTag);
0439     }
0440 
0441     QVector<int> resourceIds;
0442 
0443     KisTagResourceModel model(d->resourceType);
0444     model.setTagsFilter(QVector<int>() << tag->id());
0445 
0446     for (int i = 0; i < model.rowCount(); i++) {
0447         QModelIndex idx = model.index(i, 0);
0448         resourceIds.append(model.data(idx, Qt::UserRole + KisTagResourceModel::Id).toInt());
0449     }
0450 
0451     model.tagResources(dstTag, resourceIds);
0452     model.untagResources(tag, resourceIds);
0453     setTagInactive(tag);
0454 
0455     return true;
0456 }
0457 
0458 bool KisAllTagsModel::changeTagActive(const KisTagSP tag, bool active)
0459 {
0460     if (!tag) return false;
0461     if (!tag->valid()) return false;
0462 
0463     QModelIndex idx = indexForTag(tag);
0464     tag->setActive(active);
0465     return setData(idx, QVariant::fromValue(active), Qt::UserRole + KisAllTagsModel::Active);
0466 
0467 }
0468 
0469 KisTagSP KisAllTagsModel::tagForUrl(const QString& tagUrl) const
0470 {
0471     if (tagUrl.isEmpty()) {
0472         return KisTagSP();
0473     }
0474 
0475     if (tagUrl == urlAll()) {
0476         return tagForIndex(index(Ids::All + s_fakeRowsCount, 0));
0477     } else if (tagUrl == urlAllUntagged()) {
0478         return tagForIndex(index(Ids::AllUntagged + s_fakeRowsCount, 0));
0479     }
0480 
0481     return KisResourceLocator::instance()->tagForUrl(tagUrl, d->resourceType);
0482 }
0483 
0484 bool KisAllTagsModel::resetQuery()
0485 {
0486     bool r = d->query.prepare("SELECT tags.id\n"
0487                               ",      tags.url\n"
0488                               ",      tags.name\n"
0489                               ",      tags.comment\n"
0490                               ",      tags.active\n"
0491                               ",      tags.filename\n"
0492                               ",      resource_types.name as resource_type\n"
0493                               ",      tag_translations.name as translated_name\n"
0494                               ",      tag_translations.comment as translated_comment\n"
0495                               "FROM   tags\n"
0496                               ",      resource_types\n"
0497                               "LEFT JOIN tag_translations ON tag_translations.tag_id = tags.id AND tag_translations.language = :language\n"
0498                               "WHERE  tags.resource_type_id = resource_types.id\n"
0499                               "AND    resource_types.name = :resource_type\n"
0500                               "ORDER BY tags.id\n");
0501 
0502     if (!r) {
0503         qWarning() << "Could not prepare KisAllTagsModel query" << d->query.lastError();
0504     }
0505 
0506     d->query.bindValue(":resource_type", d->resourceType);
0507     d->query.bindValue(":language", KisTag::currentLocale());
0508 
0509     r = d->query.exec();
0510 
0511     if (!r) {
0512         qWarning() << "Could not select tags" << d->query.lastError();
0513     }
0514 
0515     d->cachedRowCount = -1;
0516     return r;
0517 }
0518 
0519 void KisAllTagsModel::addStorage(const QString &location)
0520 {
0521     Q_UNUSED(location)
0522     beginResetModel();
0523     resetQuery();
0524     endResetModel();
0525 }
0526 
0527 void KisAllTagsModel::removeStorage(const QString &location)
0528 {
0529     Q_UNUSED(location)
0530     beginResetModel();
0531     resetQuery();
0532     endResetModel();
0533 }
0534 
0535 struct KisTagModel::Private {
0536     TagFilter tagFilter{KisTagModel::ShowActiveTags};
0537     StorageFilter storageFilter {KisTagModel::ShowActiveStorages};
0538 };
0539 
0540 KisTagModel::KisTagModel(const QString &type, QObject *parent)
0541     : QSortFilterProxyModel(parent)
0542     , d(new Private())
0543 {
0544     setSourceModel(KisResourceModelProvider::tagModel(type));
0545     sort(KisAllTagsModel::Name);
0546 }
0547 
0548 KisTagModel::~KisTagModel()
0549 {
0550     delete d;
0551 }
0552 
0553 void KisTagModel::setTagFilter(KisTagModel::TagFilter filter)
0554 {
0555     if (d->tagFilter != filter) {
0556         d->tagFilter = filter;
0557         invalidateFilter();
0558     }
0559 }
0560 
0561 void KisTagModel::setStorageFilter(KisTagModel::StorageFilter filter)
0562 {
0563     if (d->storageFilter != filter) {
0564         d->storageFilter = filter;
0565         invalidateFilter();
0566     }
0567 }
0568 
0569 QModelIndex KisTagModel::indexForTag(KisTagSP tag) const
0570 {
0571     KisAbstractTagModel *source = dynamic_cast<KisAbstractTagModel*>(sourceModel());
0572     if (source) {
0573         return mapFromSource(source->indexForTag(tag));
0574     }
0575     return QModelIndex();
0576 
0577 }
0578 
0579 KisTagSP KisTagModel::tagForIndex(QModelIndex index) const
0580 {
0581     KisAbstractTagModel *source = dynamic_cast<KisAbstractTagModel*>(sourceModel());
0582     if (source) {
0583         return source->tagForIndex(mapToSource(index));
0584     }
0585     return 0;
0586 }
0587 
0588 
0589 KisTagSP KisTagModel::addTag(const QString &tagName, const bool allowOverwrite, QVector<KoResourceSP> taggedResources)
0590 {
0591     KisAbstractTagModel *source = dynamic_cast<KisAbstractTagModel*>(sourceModel());
0592     if (source) {
0593         return source->addTag(tagName, allowOverwrite, taggedResources);
0594     }
0595     return 0;
0596 }
0597 
0598 KisTagSP KisTagModel::tagForUrl(const QString& url) const
0599 {
0600     KisAbstractTagModel *source = dynamic_cast<KisAbstractTagModel*>(sourceModel());
0601     if (source) {
0602         return source->tagForUrl(url);
0603     }
0604     return 0;
0605 }
0606 
0607 
0608 bool KisTagModel::addTag(const KisTagSP tag, const bool allowOverwrite, QVector<KoResourceSP> taggedResources)
0609 {
0610     KisAbstractTagModel *source = dynamic_cast<KisAbstractTagModel*>(sourceModel());
0611     if (source) {
0612         return source->addTag(tag, allowOverwrite, taggedResources) ;
0613     }
0614     return false;
0615 }
0616 
0617 bool KisTagModel::setTagInactive(const KisTagSP tag)
0618 {
0619     KisAbstractTagModel *source = dynamic_cast<KisAbstractTagModel*>(sourceModel());
0620     if (source) {
0621         return source->setTagInactive(tag) ;
0622     }
0623     return false;
0624 }
0625 
0626 bool KisTagModel::setTagActive(const KisTagSP tag)
0627 {
0628     KisAbstractTagModel *source = dynamic_cast<KisAbstractTagModel*>(sourceModel());
0629     if (source) {
0630         return source->setTagActive(tag) ;
0631     }
0632     return false;
0633 
0634 }
0635 
0636 bool KisTagModel::renameTag(const KisTagSP tag, const QString &newName, const bool allowOverwrite)
0637 {
0638     KisAbstractTagModel *source = dynamic_cast<KisAbstractTagModel*>(sourceModel());
0639     if (source) {
0640         return source->renameTag(tag, newName, allowOverwrite);
0641     }
0642     return false;
0643 }
0644 
0645 bool KisTagModel::changeTagActive(const KisTagSP tag, bool active)
0646 {
0647     KisAbstractTagModel *source = dynamic_cast<KisAbstractTagModel*>(sourceModel());
0648     if (source) {
0649         return source->changeTagActive(tag, active);
0650     }
0651     return false;
0652 }
0653 
0654 
0655 bool KisTagModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0656 {
0657     if (d->tagFilter == ShowAllTags && d->storageFilter == ShowAllStorages) {
0658         return true;
0659     }
0660 
0661     QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
0662     if (!idx.isValid()) {
0663         return false;
0664     }
0665     int tagId = sourceModel()->data(idx, Qt::UserRole + KisAllTagsModel::Id).toInt();
0666 
0667     if (tagId < 0) {
0668         return true;
0669     }
0670 
0671     TagFilter tagActive = (TagFilter)sourceModel()->data(idx, Qt::UserRole + KisAllTagsModel::Active).toInt();
0672 
0673     StorageFilter storageActive = ShowAllStorages;
0674 
0675     if (d->storageFilter == ShowAllStorages) {
0676         return (tagActive == d->tagFilter);
0677     }
0678 
0679     {
0680         if (tagId > 0) {
0681             QSqlQuery q;
0682             const bool r = q.prepare("SELECT count(*)\n"
0683                       "FROM   tags_storages\n"
0684                       ",      storages\n"
0685                       "WHERE  tags_storages.tag_id = :tag_id\n"
0686                       "AND    tags_storages.storage_id = storages.id\n"
0687                       "AND    storages.active = 1\n");
0688             if (!r) {
0689                 qWarning() << "Could not execute tags in storages query" << q.lastError();
0690                 return true;
0691             }
0692 
0693             q.bindValue(":tag_id", tagId);
0694 
0695             if (!q.exec()) {
0696                 qWarning() << "Could not execute tags in storages query" << q.lastError() << q.boundValues();
0697                 return true;
0698             }
0699             else {
0700                 q.first();
0701 
0702                 if (q.value(0).toInt() > 0) {
0703                     storageActive = ShowActiveStorages;
0704                 }
0705             }
0706         }
0707     }
0708 
0709     if (d->tagFilter == ShowAllTags) {
0710         return (storageActive == d->storageFilter);
0711     }
0712 
0713     return ((storageActive == d->storageFilter) && (tagActive == d->tagFilter));
0714 }
0715 
0716 bool KisTagModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
0717 {
0718     const bool leftIsFakeRow = source_left.row() < s_fakeRowsCount;
0719     const bool rightIsFakeRow = source_right.row() < s_fakeRowsCount;
0720     // Always sort fake rows ("All" and "All Untagged") above the rest.
0721     if (leftIsFakeRow && rightIsFakeRow) {
0722         return source_left.row() < source_right.row();
0723     } else if (leftIsFakeRow) {
0724         return true;
0725     } else if (rightIsFakeRow) {
0726         return false;
0727     } else {
0728         QString nameLeft = sourceModel()->data(source_left, Qt::UserRole + KisAllTagsModel::Name).toString().toLower();
0729         QString nameRight = sourceModel()->data(source_right, Qt::UserRole + KisAllTagsModel::Name).toString().toLower();
0730         return (nameLeft < nameRight);
0731     }
0732 }