File indexing completed on 2024-05-12 15:59:53

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