File indexing completed on 2024-05-12 15:59:53
0001 /* 0002 * SPDX-FileCopyrightText: 2018 Boudewijn Rempt <boud@valdyas.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 #include "KisTagFilterResourceProxyModel.h" 0007 0008 #include <QDebug> 0009 0010 #include <KisResourceModelProvider.h> 0011 #include <KisResourceModel.h> 0012 #include <KisTagResourceModel.h> 0013 #include <KisTagModel.h> 0014 0015 #include <kis_debug.h> 0016 #include <KisResourceSearchBoxFilter.h> 0017 0018 struct KisTagFilterResourceProxyModel::Private 0019 { 0020 Private() 0021 : filter(new KisResourceSearchBoxFilter()) 0022 { 0023 } 0024 0025 QString resourceType; 0026 0027 KisResourceModel *resourceModel {0}; // This is the source model if we are _not_ filtering by tag 0028 KisTagResourceModel *tagResourceModel {0}; // This is the source model if we are filtering by tag 0029 0030 QScopedPointer<KisResourceSearchBoxFilter> filter; 0031 bool filteringWithinCurrentTag {false}; 0032 0033 QMap<QString, QVariant> metaDataMapFilter; 0034 KisTagSP currentTagFilter; 0035 KoResourceSP currentResourceFilter; 0036 0037 int storageId {-1}; 0038 bool useStorageIdFilter {false}; 0039 0040 }; 0041 0042 KisTagFilterResourceProxyModel::KisTagFilterResourceProxyModel(const QString &resourceType, QObject *parent) 0043 : QSortFilterProxyModel(parent) 0044 , d(new Private) 0045 { 0046 d->resourceType = resourceType; 0047 d->resourceModel = new KisResourceModel(resourceType); 0048 d->tagResourceModel = new KisTagResourceModel(resourceType); 0049 0050 setSourceModel(d->resourceModel); 0051 } 0052 0053 KisTagFilterResourceProxyModel::~KisTagFilterResourceProxyModel() 0054 { 0055 delete d->resourceModel; 0056 delete d->tagResourceModel; 0057 delete d; 0058 } 0059 0060 void KisTagFilterResourceProxyModel::setResourceFilter(ResourceFilter filter) 0061 { 0062 emit beforeFilterChanges(); 0063 d->resourceModel->setResourceFilter(filter); 0064 d->tagResourceModel->setResourceFilter(filter); 0065 invalidateFilter(); 0066 emit afterFilterChanged(); 0067 } 0068 0069 void KisTagFilterResourceProxyModel::setStorageFilter(StorageFilter filter) 0070 { 0071 emit beforeFilterChanges(); 0072 d->resourceModel->setStorageFilter(filter); 0073 d->tagResourceModel->setStorageFilter(filter); 0074 invalidateFilter(); 0075 emit afterFilterChanged(); 0076 } 0077 0078 void KisTagFilterResourceProxyModel::setResourceModel(KisResourceModel *resourceModel) 0079 { 0080 d->resourceModel = resourceModel; 0081 } 0082 0083 KoResourceSP KisTagFilterResourceProxyModel::resourceForIndex(QModelIndex index) const 0084 { 0085 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0086 if (source) { 0087 return source->resourceForIndex(mapToSource(index)); 0088 } 0089 return 0; 0090 } 0091 0092 QModelIndex KisTagFilterResourceProxyModel::indexForResource(KoResourceSP resource) const 0093 { 0094 if (!resource || !resource->valid() || resource->resourceId() < 0) return QModelIndex(); 0095 0096 for (int i = 0; i < rowCount(); ++i) { 0097 QModelIndex idx = index(i, 0); 0098 Q_ASSERT(idx.isValid()); 0099 if (idx.data() == resource->resourceId()) { 0100 return idx; 0101 } 0102 } 0103 return QModelIndex(); 0104 } 0105 0106 QModelIndex KisTagFilterResourceProxyModel::indexForResourceId(int resourceId) const 0107 { 0108 if (resourceId < 0) return QModelIndex(); 0109 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0110 if (source) { 0111 return mapFromSource(source->indexForResourceId(resourceId)); 0112 } 0113 return QModelIndex(); 0114 } 0115 0116 bool KisTagFilterResourceProxyModel::setResourceActive(const QModelIndex &index, bool value) 0117 { 0118 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0119 if (source) { 0120 return source->setResourceActive(mapToSource(index), value); 0121 } 0122 return false; 0123 } 0124 0125 KoResourceSP KisTagFilterResourceProxyModel::importResourceFile(const QString &filename, const bool allowOverwrite, const QString &storageId) 0126 { 0127 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0128 KoResourceSP res; 0129 if (source) { 0130 res = source->importResourceFile(filename, allowOverwrite, storageId); 0131 } 0132 return res; 0133 } 0134 0135 KoResourceSP KisTagFilterResourceProxyModel::importResource(const QString &filename, QIODevice *device, const bool allowOverwrite, const QString &storageId) 0136 { 0137 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0138 KoResourceSP res; 0139 if (source) { 0140 res = source->importResource(filename, device, allowOverwrite, storageId); 0141 } 0142 return res; 0143 } 0144 0145 bool KisTagFilterResourceProxyModel::importWillOverwriteResource(const QString &fileName, const QString &storageLocation) const 0146 { 0147 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0148 return source && source->importWillOverwriteResource(fileName, storageLocation); 0149 } 0150 0151 bool KisTagFilterResourceProxyModel::exportResource(KoResourceSP resource, QIODevice *device) 0152 { 0153 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0154 bool res = false; 0155 if (source) { 0156 res = source->exportResource(resource, device); 0157 } 0158 return res; 0159 } 0160 0161 bool KisTagFilterResourceProxyModel::addResource(KoResourceSP resource, const QString &storageId) 0162 { 0163 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0164 if (source) { 0165 return source->addResource(resource, storageId); 0166 } 0167 return false; 0168 } 0169 0170 bool KisTagFilterResourceProxyModel::updateResource(KoResourceSP resource) 0171 { 0172 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0173 if (source) { 0174 return source->updateResource(resource); 0175 } 0176 return false; 0177 } 0178 0179 bool KisTagFilterResourceProxyModel::reloadResource(KoResourceSP resource) 0180 { 0181 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0182 if (source) { 0183 return source->reloadResource(resource); 0184 } 0185 return false; 0186 } 0187 0188 bool KisTagFilterResourceProxyModel::renameResource(KoResourceSP resource, const QString &name) 0189 { 0190 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0191 if (source) { 0192 return source->renameResource(resource, name); 0193 } 0194 return false; 0195 } 0196 0197 bool KisTagFilterResourceProxyModel::setResourceMetaData(KoResourceSP resource, QMap<QString, QVariant> metadata) 0198 { 0199 KisAbstractResourceModel *source = dynamic_cast<KisAbstractResourceModel*>(sourceModel()); 0200 if (source) { 0201 return source->setResourceMetaData(resource, metadata); 0202 } 0203 return false; 0204 } 0205 0206 void KisTagFilterResourceProxyModel::setMetaDataFilter(QMap<QString, QVariant> metaDataMap) 0207 { 0208 emit beforeFilterChanges(); 0209 d->metaDataMapFilter = metaDataMap; 0210 invalidateFilter(); 0211 emit afterFilterChanged(); 0212 } 0213 0214 void KisTagFilterResourceProxyModel::setTagFilter(const KisTagSP tag) 0215 { 0216 d->currentTagFilter = tag; 0217 updateTagFilter(); 0218 } 0219 0220 void KisTagFilterResourceProxyModel::setStorageFilter(bool useFilter, int storageId) 0221 { 0222 emit beforeFilterChanges(); 0223 d->useStorageIdFilter = useFilter; 0224 if (useFilter) { 0225 d->storageId = storageId; 0226 } 0227 invalidateFilter(); 0228 emit afterFilterChanged(); 0229 } 0230 0231 void KisTagFilterResourceProxyModel::updateTagFilter() 0232 { 0233 emit beforeFilterChanges(); 0234 const bool ignoreTagFiltering = 0235 !d->filteringWithinCurrentTag && !d->filter->isEmpty(); 0236 0237 QAbstractItemModel *desiredModel = 0; 0238 0239 if (d->currentResourceFilter) { 0240 QVector<KisTagSP> filter; 0241 0242 if (d->currentTagFilter && 0243 !ignoreTagFiltering && 0244 d->currentTagFilter->url() != KisAllTagsModel::urlAll() && 0245 d->currentTagFilter->url() != KisAllTagsModel::urlAllUntagged()) { 0246 0247 filter << d->currentTagFilter; 0248 } else { 0249 // combination with for untagged resources in not implemented 0250 // in KisTagResourceModel 0251 KIS_SAFE_ASSERT_RECOVER_NOOP(!d->currentTagFilter || 0252 d->currentTagFilter->url() != KisAllTagsModel::urlAllUntagged()); 0253 } 0254 0255 d->tagResourceModel->setTagsFilter(filter); 0256 d->tagResourceModel->setResourcesFilter({d->currentResourceFilter}); 0257 desiredModel = d->tagResourceModel; 0258 0259 } else { 0260 d->tagResourceModel->setResourcesFilter(QVector<KoResourceSP>()); 0261 if (ignoreTagFiltering || 0262 !d->currentTagFilter || 0263 d->currentTagFilter->url() == KisAllTagsModel::urlAll()) { 0264 0265 d->tagResourceModel->setTagsFilter(QVector<KisTagSP>()); 0266 desiredModel = d->resourceModel; 0267 d->resourceModel->showOnlyUntaggedResources(false); 0268 } 0269 else { 0270 if (d->currentTagFilter->url() == KisAllTagsModel::urlAllUntagged()) { 0271 desiredModel = d->resourceModel; 0272 d->resourceModel->showOnlyUntaggedResources(true); 0273 } 0274 else { 0275 desiredModel = d->tagResourceModel; 0276 d->tagResourceModel->setTagsFilter(QVector<KisTagSP>() << d->currentTagFilter); 0277 } 0278 } 0279 } 0280 0281 // TODO: when model changes the current selection in the 0282 // view disappears. We should try to keep it somehow. 0283 if (sourceModel() != desiredModel) { 0284 setSourceModel(desiredModel); 0285 } 0286 0287 invalidateFilter(); 0288 emit afterFilterChanged(); 0289 } 0290 0291 void KisTagFilterResourceProxyModel::setResourceFilter(const KoResourceSP resource) 0292 { 0293 d->currentResourceFilter = resource; 0294 updateTagFilter(); 0295 } 0296 0297 void KisTagFilterResourceProxyModel::setSearchText(const QString& searchText) 0298 { 0299 d->filter->setFilter(searchText); 0300 updateTagFilter(); 0301 } 0302 0303 void KisTagFilterResourceProxyModel::setFilterInCurrentTag(bool filterInCurrentTag) 0304 { 0305 d->filteringWithinCurrentTag = filterInCurrentTag; 0306 updateTagFilter(); 0307 } 0308 0309 bool KisTagFilterResourceProxyModel::tagResources(const KisTagSP tag, const QVector<int> &resourceIds) 0310 { 0311 return d->tagResourceModel->tagResources(tag, resourceIds); 0312 } 0313 0314 bool KisTagFilterResourceProxyModel::untagResources(const KisTagSP tag, const QVector<int> &resourceIds) 0315 { 0316 return d->tagResourceModel->untagResources(tag, resourceIds); 0317 } 0318 0319 int KisTagFilterResourceProxyModel::isResourceTagged(const KisTagSP tag, const int resourceId) 0320 { 0321 return d->tagResourceModel->isResourceTagged(tag, resourceId); 0322 } 0323 0324 bool KisTagFilterResourceProxyModel::filterAcceptsColumn(int /*source_column*/, const QModelIndex &/*source_parent*/) const 0325 { 0326 return true; 0327 } 0328 0329 bool KisTagFilterResourceProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const 0330 { 0331 // if both filters are empty, just accept everything 0332 if (d->filter->isEmpty() && d->metaDataMapFilter.isEmpty() && !d->useStorageIdFilter) { 0333 return true; 0334 } 0335 0336 // If there's a tag set to filter on, we use the tagResourceModel, so that already filters for the tag 0337 // Here, we only have to filter by the search string. 0338 QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); 0339 0340 if (!idx.isValid()) { 0341 return false; 0342 } 0343 0344 // checking the storage filter 0345 if (d->useStorageIdFilter) { 0346 int storageId = sourceModel()->data(idx, Qt::UserRole + KisAbstractResourceModel::StorageId).toInt(); 0347 if (storageId != d->storageId) { 0348 return false; 0349 } 0350 } 0351 0352 bool metaDataMatches = true; 0353 QMap<QString, QVariant> resourceMetaData = sourceModel()->data(idx, Qt::UserRole + KisAbstractResourceModel::MetaData).toMap(); 0354 Q_FOREACH(const QString &key, d->metaDataMapFilter.keys()) { 0355 if (resourceMetaData.contains(key)) { 0356 metaDataMatches = (resourceMetaData[key] == d->metaDataMapFilter[key]); 0357 if (!metaDataMatches) { 0358 return false; 0359 } 0360 } 0361 } 0362 0363 QString resourceName = sourceModel()->data(idx, Qt::UserRole + KisAbstractResourceModel::Name).toString(); 0364 if (sourceModel()->data(idx, Qt::UserRole + KisAbstractResourceModel::ResourceType).toString() == ResourceType::PaintOpPresets) { 0365 resourceName = resourceName.replace("_", " "); 0366 } 0367 QStringList resourceTags = sourceModel()->data(idx, Qt::UserRole + KisAbstractResourceModel::Tags).toStringList(); 0368 bool resourceNameMatches = d->filter->matchesResource(resourceName, resourceTags); 0369 0370 0371 return (resourceNameMatches && metaDataMatches); 0372 } 0373 0374 bool KisTagFilterResourceProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const 0375 { 0376 QString nameLeft = sourceModel()->data(source_left, Qt::UserRole + KisAbstractResourceModel::Name).toString(); 0377 QString nameRight = sourceModel()->data(source_right, Qt::UserRole + KisAbstractResourceModel::Name).toString(); 0378 return nameLeft.toLower() < nameRight.toLower(); 0379 } 0380