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 }