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"