File indexing completed on 2024-05-12 04:19:48

0001 // vim: set tabstop=4 shiftwidth=4 expandtab:
0002 /*
0003 Gwenview: an image viewer
0004 Copyright 2008 Aurélien Gâteau <agateau@kde.org>
0005 
0006 This program is free software; you can redistribute it and/or
0007 modify it under the terms of the GNU General Public License
0008 as published by the Free Software Foundation; either version 2
0009 of the License, or (at your option) any later version.
0010 
0011 This program is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU General Public License for more details.
0015 
0016 You should have received a copy of the GNU General Public License
0017 along with this program; if not, write to the Free Software
0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
0019 
0020 */
0021 // Self
0022 #include "semanticinfodirmodel.h"
0023 #include <config-gwenview.h>
0024 
0025 // Qt
0026 #include <QHash>
0027 
0028 // KF
0029 
0030 // Local
0031 #include "../archiveutils.h"
0032 #include "abstractsemanticinfobackend.h"
0033 #include "gwenview_lib_debug.h"
0034 
0035 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_FAKE
0036 #include "fakesemanticinfobackend.h"
0037 
0038 #elif defined(GWENVIEW_SEMANTICINFO_BACKEND_BALOO)
0039 #include "baloosemanticinfobackend.h"
0040 
0041 #else
0042 #ifdef __GNUC__
0043 #error No metadata backend defined
0044 #endif
0045 #endif
0046 
0047 namespace Gwenview
0048 {
0049 struct SemanticInfoCacheItem {
0050     SemanticInfoCacheItem() = default;
0051     QPersistentModelIndex mIndex;
0052     bool mValid = false;
0053     SemanticInfo mInfo;
0054 };
0055 
0056 using SemanticInfoCache = QHash<QUrl, SemanticInfoCacheItem>;
0057 
0058 struct SemanticInfoDirModelPrivate {
0059     SemanticInfoCache mSemanticInfoCache;
0060     AbstractSemanticInfoBackEnd *mBackEnd;
0061 };
0062 
0063 SemanticInfoDirModel::SemanticInfoDirModel(QObject *parent)
0064     : KDirModel(parent)
0065     , d(new SemanticInfoDirModelPrivate)
0066 {
0067 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_FAKE
0068     d->mBackEnd = new FakeSemanticInfoBackEnd(this, FakeSemanticInfoBackEnd::InitializeRandom);
0069 #elif defined(GWENVIEW_SEMANTICINFO_BACKEND_BALOO)
0070     d->mBackEnd = new BalooSemanticInfoBackend(this);
0071 #endif
0072 
0073     connect(d->mBackEnd, &AbstractSemanticInfoBackEnd::semanticInfoRetrieved, this, &SemanticInfoDirModel::slotSemanticInfoRetrieved, Qt::QueuedConnection);
0074 
0075     connect(this, &SemanticInfoDirModel::modelAboutToBeReset, this, &SemanticInfoDirModel::slotModelAboutToBeReset);
0076 
0077     connect(this, &SemanticInfoDirModel::rowsAboutToBeRemoved, this, &SemanticInfoDirModel::slotRowsAboutToBeRemoved);
0078 }
0079 
0080 SemanticInfoDirModel::~SemanticInfoDirModel()
0081 {
0082     delete d;
0083 }
0084 
0085 void SemanticInfoDirModel::clearSemanticInfoCache()
0086 {
0087     d->mSemanticInfoCache.clear();
0088 }
0089 
0090 bool SemanticInfoDirModel::semanticInfoAvailableForIndex(const QModelIndex &index) const
0091 {
0092     if (!index.isValid()) {
0093         return false;
0094     }
0095     KFileItem item = itemForIndex(index);
0096     if (item.isNull()) {
0097         return false;
0098     }
0099     SemanticInfoCache::const_iterator it = d->mSemanticInfoCache.constFind(item.targetUrl());
0100     if (it == d->mSemanticInfoCache.constEnd()) {
0101         return false;
0102     }
0103     return it.value().mValid;
0104 }
0105 
0106 SemanticInfo SemanticInfoDirModel::semanticInfoForIndex(const QModelIndex &index) const
0107 {
0108     if (!index.isValid()) {
0109         qCWarning(GWENVIEW_LIB_LOG) << "invalid index";
0110         return {};
0111     }
0112     KFileItem item = itemForIndex(index);
0113     if (item.isNull()) {
0114         qCWarning(GWENVIEW_LIB_LOG) << "no item for index";
0115         return {};
0116     }
0117     return d->mSemanticInfoCache.value(item.targetUrl()).mInfo;
0118 }
0119 
0120 void SemanticInfoDirModel::retrieveSemanticInfoForIndex(const QModelIndex &index)
0121 {
0122     if (!index.isValid()) {
0123         return;
0124     }
0125     KFileItem item = itemForIndex(index);
0126     if (item.isNull()) {
0127         qCWarning(GWENVIEW_LIB_LOG) << "invalid item";
0128         return;
0129     }
0130     if (ArchiveUtils::fileItemIsDirOrArchive(item)) {
0131         return;
0132     }
0133     SemanticInfoCacheItem cacheItem;
0134     cacheItem.mIndex = QPersistentModelIndex(index);
0135     d->mSemanticInfoCache[item.targetUrl()] = cacheItem;
0136     d->mBackEnd->retrieveSemanticInfo(item.targetUrl());
0137 }
0138 
0139 QVariant SemanticInfoDirModel::data(const QModelIndex &index, int role) const
0140 {
0141     if (role == RatingRole || role == DescriptionRole || role == TagsRole) {
0142         KFileItem item = itemForIndex(index);
0143         if (item.isNull()) {
0144             return {};
0145         }
0146         SemanticInfoCache::ConstIterator it = d->mSemanticInfoCache.constFind(item.targetUrl());
0147         if (it != d->mSemanticInfoCache.constEnd()) {
0148             if (!it.value().mValid) {
0149                 return {};
0150             }
0151             const SemanticInfo &info = it.value().mInfo;
0152             if (role == RatingRole) {
0153                 return info.mRating;
0154             } else if (role == DescriptionRole) {
0155                 return info.mDescription;
0156             } else if (role == TagsRole) {
0157                 return info.mTags.toVariant();
0158             } else {
0159                 // We should never reach this part
0160                 Q_ASSERT(0);
0161                 return {};
0162             }
0163         } else {
0164             const_cast<SemanticInfoDirModel *>(this)->retrieveSemanticInfoForIndex(index);
0165             return {};
0166         }
0167     } else {
0168         return KDirModel::data(index, role);
0169     }
0170 }
0171 
0172 bool SemanticInfoDirModel::setData(const QModelIndex &index, const QVariant &data, int role)
0173 {
0174     if (role == RatingRole || role == DescriptionRole || role == TagsRole) {
0175         KFileItem item = itemForIndex(index);
0176         if (item.isNull()) {
0177             qCWarning(GWENVIEW_LIB_LOG) << "no item found for this index";
0178             return false;
0179         }
0180         QUrl url = item.targetUrl();
0181         SemanticInfoCache::iterator it = d->mSemanticInfoCache.find(url);
0182         if (it == d->mSemanticInfoCache.end()) {
0183             qCWarning(GWENVIEW_LIB_LOG) << "No index for" << url;
0184             return false;
0185         }
0186         if (!it.value().mValid) {
0187             qCWarning(GWENVIEW_LIB_LOG) << "Semantic info cache for" << url << "is invalid";
0188             return false;
0189         }
0190         SemanticInfo &semanticInfo = it.value().mInfo;
0191         if (role == RatingRole) {
0192             semanticInfo.mRating = data.toInt();
0193         } else if (role == DescriptionRole) {
0194             semanticInfo.mDescription = data.toString();
0195         } else if (role == TagsRole) {
0196             semanticInfo.mTags = TagSet::fromVariant(data);
0197         } else {
0198             // We should never reach this part
0199             Q_ASSERT(0);
0200         }
0201         Q_EMIT dataChanged(index, index);
0202 
0203         d->mBackEnd->storeSemanticInfo(url, semanticInfo);
0204         return true;
0205     } else {
0206         return KDirModel::setData(index, data, role);
0207     }
0208 }
0209 
0210 void SemanticInfoDirModel::slotSemanticInfoRetrieved(const QUrl &url, const SemanticInfo &semanticInfo)
0211 {
0212     SemanticInfoCache::iterator it = d->mSemanticInfoCache.find(url);
0213     if (it == d->mSemanticInfoCache.end()) {
0214         qCWarning(GWENVIEW_LIB_LOG) << "No index for" << url;
0215         return;
0216     }
0217     SemanticInfoCacheItem &cacheItem = it.value();
0218     if (!cacheItem.mIndex.isValid()) {
0219         qCWarning(GWENVIEW_LIB_LOG) << "Index for" << url << "is invalid";
0220         return;
0221     }
0222     cacheItem.mInfo = semanticInfo;
0223     cacheItem.mValid = true;
0224     Q_EMIT dataChanged(cacheItem.mIndex, cacheItem.mIndex);
0225 }
0226 
0227 void SemanticInfoDirModel::slotRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
0228 {
0229     for (int pos = start; pos <= end; ++pos) {
0230         QModelIndex idx = index(pos, 0, parent);
0231         KFileItem item = itemForIndex(idx);
0232         if (item.isNull()) {
0233             continue;
0234         }
0235         d->mSemanticInfoCache.remove(item.targetUrl());
0236     }
0237 }
0238 
0239 void SemanticInfoDirModel::slotModelAboutToBeReset()
0240 {
0241     d->mSemanticInfoCache.clear();
0242 }
0243 
0244 AbstractSemanticInfoBackEnd *SemanticInfoDirModel::semanticInfoBackEnd() const
0245 {
0246     return d->mBackEnd;
0247 }
0248 
0249 } // namespace
0250 
0251 #include "moc_semanticinfodirmodel.cpp"