1 // vim: set tabstop=4 shiftwidth=4 expandtab:
2 /*
3 Gwenview: an image viewer
4 Copyright 2008 Aurélien Gâteau <agateau@kde.org>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
19
20 */
21 // Self
22 #include "semanticinfodirmodel.h"
23 #include <config-gwenview.h>
24
25 // Qt
26 #include <QHash>
27
28 // KF
29
30 // Local
31 #include "../archiveutils.h"
32 #include "abstractsemanticinfobackend.h"
33 #include "gwenview_lib_debug.h"
34
35 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_FAKE
36 #include "fakesemanticinfobackend.h"
37
38 #elif defined(GWENVIEW_SEMANTICINFO_BACKEND_BALOO)
39 #include "baloosemanticinfobackend.h"
40
41 #else
42 #ifdef __GNUC__
43 #error No metadata backend defined
44 #endif
45 #endif
46
47 namespace Gwenview
48 {
49 struct SemanticInfoCacheItem {
SemanticInfoCacheItemGwenview::SemanticInfoCacheItem50 SemanticInfoCacheItem()
51 : mValid(false)
52 {
53 }
54 QPersistentModelIndex mIndex;
55 bool mValid;
56 SemanticInfo mInfo;
57 };
58
59 using SemanticInfoCache = QHash<QUrl, SemanticInfoCacheItem>;
60
61 struct SemanticInfoDirModelPrivate {
62 SemanticInfoCache mSemanticInfoCache;
63 AbstractSemanticInfoBackEnd *mBackEnd;
64 };
65
SemanticInfoDirModel(QObject * parent)66 SemanticInfoDirModel::SemanticInfoDirModel(QObject *parent)
67 : KDirModel(parent)
68 , d(new SemanticInfoDirModelPrivate)
69 {
70 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_FAKE
71 d->mBackEnd = new FakeSemanticInfoBackEnd(this, FakeSemanticInfoBackEnd::InitializeRandom);
72 #elif defined(GWENVIEW_SEMANTICINFO_BACKEND_BALOO)
73 d->mBackEnd = new BalooSemanticInfoBackend(this);
74 #endif
75
76 connect(d->mBackEnd, &AbstractSemanticInfoBackEnd::semanticInfoRetrieved, this, &SemanticInfoDirModel::slotSemanticInfoRetrieved, Qt::QueuedConnection);
77
78 connect(this, &SemanticInfoDirModel::modelAboutToBeReset, this, &SemanticInfoDirModel::slotModelAboutToBeReset);
79
80 connect(this, &SemanticInfoDirModel::rowsAboutToBeRemoved, this, &SemanticInfoDirModel::slotRowsAboutToBeRemoved);
81 }
82
~SemanticInfoDirModel()83 SemanticInfoDirModel::~SemanticInfoDirModel()
84 {
85 delete d;
86 }
87
clearSemanticInfoCache()88 void SemanticInfoDirModel::clearSemanticInfoCache()
89 {
90 d->mSemanticInfoCache.clear();
91 }
92
semanticInfoAvailableForIndex(const QModelIndex & index) const93 bool SemanticInfoDirModel::semanticInfoAvailableForIndex(const QModelIndex &index) const
94 {
95 if (!index.isValid()) {
96 return false;
97 }
98 KFileItem item = itemForIndex(index);
99 if (item.isNull()) {
100 return false;
101 }
102 SemanticInfoCache::const_iterator it = d->mSemanticInfoCache.constFind(item.targetUrl());
103 if (it == d->mSemanticInfoCache.constEnd()) {
104 return false;
105 }
106 return it.value().mValid;
107 }
108
semanticInfoForIndex(const QModelIndex & index) const109 SemanticInfo SemanticInfoDirModel::semanticInfoForIndex(const QModelIndex &index) const
110 {
111 if (!index.isValid()) {
112 qCWarning(GWENVIEW_LIB_LOG) << "invalid index";
113 return SemanticInfo();
114 }
115 KFileItem item = itemForIndex(index);
116 if (item.isNull()) {
117 qCWarning(GWENVIEW_LIB_LOG) << "no item for index";
118 return SemanticInfo();
119 }
120 return d->mSemanticInfoCache.value(item.targetUrl()).mInfo;
121 }
122
retrieveSemanticInfoForIndex(const QModelIndex & index)123 void SemanticInfoDirModel::retrieveSemanticInfoForIndex(const QModelIndex &index)
124 {
125 if (!index.isValid()) {
126 return;
127 }
128 KFileItem item = itemForIndex(index);
129 if (item.isNull()) {
130 qCWarning(GWENVIEW_LIB_LOG) << "invalid item";
131 return;
132 }
133 if (ArchiveUtils::fileItemIsDirOrArchive(item)) {
134 return;
135 }
136 SemanticInfoCacheItem cacheItem;
137 cacheItem.mIndex = QPersistentModelIndex(index);
138 d->mSemanticInfoCache[item.targetUrl()] = cacheItem;
139 d->mBackEnd->retrieveSemanticInfo(item.targetUrl());
140 }
141
data(const QModelIndex & index,int role) const142 QVariant SemanticInfoDirModel::data(const QModelIndex &index, int role) const
143 {
144 if (role == RatingRole || role == DescriptionRole || role == TagsRole) {
145 KFileItem item = itemForIndex(index);
146 if (item.isNull()) {
147 return QVariant();
148 }
149 SemanticInfoCache::ConstIterator it = d->mSemanticInfoCache.constFind(item.targetUrl());
150 if (it != d->mSemanticInfoCache.constEnd()) {
151 if (!it.value().mValid) {
152 return QVariant();
153 }
154 const SemanticInfo &info = it.value().mInfo;
155 if (role == RatingRole) {
156 return info.mRating;
157 } else if (role == DescriptionRole) {
158 return info.mDescription;
159 } else if (role == TagsRole) {
160 return info.mTags.toVariant();
161 } else {
162 // We should never reach this part
163 Q_ASSERT(0);
164 return QVariant();
165 }
166 } else {
167 const_cast<SemanticInfoDirModel *>(this)->retrieveSemanticInfoForIndex(index);
168 return QVariant();
169 }
170 } else {
171 return KDirModel::data(index, role);
172 }
173 }
174
setData(const QModelIndex & index,const QVariant & data,int role)175 bool SemanticInfoDirModel::setData(const QModelIndex &index, const QVariant &data, int role)
176 {
177 if (role == RatingRole || role == DescriptionRole || role == TagsRole) {
178 KFileItem item = itemForIndex(index);
179 if (item.isNull()) {
180 qCWarning(GWENVIEW_LIB_LOG) << "no item found for this index";
181 return false;
182 }
183 QUrl url = item.targetUrl();
184 SemanticInfoCache::iterator it = d->mSemanticInfoCache.find(url);
185 if (it == d->mSemanticInfoCache.end()) {
186 qCWarning(GWENVIEW_LIB_LOG) << "No index for" << url;
187 return false;
188 }
189 if (!it.value().mValid) {
190 qCWarning(GWENVIEW_LIB_LOG) << "Semantic info cache for" << url << "is invalid";
191 return false;
192 }
193 SemanticInfo &semanticInfo = it.value().mInfo;
194 if (role == RatingRole) {
195 semanticInfo.mRating = data.toInt();
196 } else if (role == DescriptionRole) {
197 semanticInfo.mDescription = data.toString();
198 } else if (role == TagsRole) {
199 semanticInfo.mTags = TagSet::fromVariant(data);
200 } else {
201 // We should never reach this part
202 Q_ASSERT(0);
203 }
204 Q_EMIT dataChanged(index, index);
205
206 d->mBackEnd->storeSemanticInfo(url, semanticInfo);
207 return true;
208 } else {
209 return KDirModel::setData(index, data, role);
210 }
211 }
212
slotSemanticInfoRetrieved(const QUrl & url,const SemanticInfo & semanticInfo)213 void SemanticInfoDirModel::slotSemanticInfoRetrieved(const QUrl &url, const SemanticInfo &semanticInfo)
214 {
215 SemanticInfoCache::iterator it = d->mSemanticInfoCache.find(url);
216 if (it == d->mSemanticInfoCache.end()) {
217 qCWarning(GWENVIEW_LIB_LOG) << "No index for" << url;
218 return;
219 }
220 SemanticInfoCacheItem &cacheItem = it.value();
221 if (!cacheItem.mIndex.isValid()) {
222 qCWarning(GWENVIEW_LIB_LOG) << "Index for" << url << "is invalid";
223 return;
224 }
225 cacheItem.mInfo = semanticInfo;
226 cacheItem.mValid = true;
227 Q_EMIT dataChanged(cacheItem.mIndex, cacheItem.mIndex);
228 }
229
slotRowsAboutToBeRemoved(const QModelIndex & parent,int start,int end)230 void SemanticInfoDirModel::slotRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
231 {
232 for (int pos = start; pos <= end; ++pos) {
233 QModelIndex idx = index(pos, 0, parent);
234 KFileItem item = itemForIndex(idx);
235 if (item.isNull()) {
236 continue;
237 }
238 d->mSemanticInfoCache.remove(item.targetUrl());
239 }
240 }
241
slotModelAboutToBeReset()242 void SemanticInfoDirModel::slotModelAboutToBeReset()
243 {
244 d->mSemanticInfoCache.clear();
245 }
246
semanticInfoBackEnd() const247 AbstractSemanticInfoBackEnd *SemanticInfoDirModel::semanticInfoBackEnd() const
248 {
249 return d->mBackEnd;
250 }
251
252 } // namespace
253