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