1 /*
2 Gwenview: an image viewer
3 Copyright 2007 Aurélien Gâteau <agateau@kde.org>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19 */
20 #include "sorteddirmodel.h"
21
22 // Qt
23 #include <QTimer>
24 #include <QUrl>
25
26 // KF
27 #include <KDirLister>
28 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE
29 #include <KDirModel>
30 #endif
31 // Local
32 #include "gwenview_lib_debug.h"
33 #include <lib/archiveutils.h>
34 #include <lib/timeutils.h>
35 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
36 #include "abstractsemanticinfobackend.h"
37 #include "semanticinfodirmodel.h"
38 #include <lib/sorting.h>
39 #endif
40
41 namespace Gwenview
42 {
AbstractSortedDirModelFilter(SortedDirModel * model)43 AbstractSortedDirModelFilter::AbstractSortedDirModelFilter(SortedDirModel *model)
44 : QObject(model)
45 , mModel(model)
46 {
47 if (mModel) {
48 mModel->addFilter(this);
49 }
50 }
51
~AbstractSortedDirModelFilter()52 AbstractSortedDirModelFilter::~AbstractSortedDirModelFilter()
53 {
54 if (mModel) {
55 mModel->removeFilter(this);
56 }
57 }
58
59 struct SortedDirModelPrivate {
60 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE
61 KDirModel *mSourceModel;
62 #else
63 SemanticInfoDirModel *mSourceModel;
64 #endif
65 QStringList mBlackListedExtensions;
66 QList<AbstractSortedDirModelFilter *> mFilters;
67 QTimer mDelayedApplyFiltersTimer;
68 MimeTypeUtils::Kinds mKindFilter;
69 };
70
SortedDirModel(QObject * parent)71 SortedDirModel::SortedDirModel(QObject *parent)
72 : KDirSortFilterProxyModel(parent)
73 , d(new SortedDirModelPrivate)
74 {
75 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE
76 d->mSourceModel = new KDirModel(this);
77 #else
78 d->mSourceModel = new SemanticInfoDirModel(this);
79 #endif
80 setSourceModel(d->mSourceModel);
81
82 #ifdef KIO_REQUEST_MIMETYPE
83 d->mSourceModel->dirLister()->setRequestMimeTypeWhileListing(true);
84 #endif
85
86 d->mDelayedApplyFiltersTimer.setInterval(0);
87 d->mDelayedApplyFiltersTimer.setSingleShot(true);
88 connect(&d->mDelayedApplyFiltersTimer, &QTimer::timeout, this, &SortedDirModel::doApplyFilters);
89 }
90
~SortedDirModel()91 SortedDirModel::~SortedDirModel()
92 {
93 delete d;
94 }
95
kindFilter() const96 MimeTypeUtils::Kinds SortedDirModel::kindFilter() const
97 {
98 return d->mKindFilter;
99 }
100
setKindFilter(MimeTypeUtils::Kinds kindFilter)101 void SortedDirModel::setKindFilter(MimeTypeUtils::Kinds kindFilter)
102 {
103 if (d->mKindFilter == kindFilter) {
104 return;
105 }
106 d->mKindFilter = kindFilter;
107 applyFilters();
108 }
109
adjustKindFilter(MimeTypeUtils::Kinds kinds,bool set)110 void SortedDirModel::adjustKindFilter(MimeTypeUtils::Kinds kinds, bool set)
111 {
112 MimeTypeUtils::Kinds kindFilter = d->mKindFilter;
113 if (set) {
114 kindFilter |= kinds;
115 } else {
116 kindFilter &= ~kinds;
117 }
118 setKindFilter(kindFilter);
119 }
120
addFilter(AbstractSortedDirModelFilter * filter)121 void SortedDirModel::addFilter(AbstractSortedDirModelFilter *filter)
122 {
123 d->mFilters << filter;
124 applyFilters();
125 }
126
removeFilter(AbstractSortedDirModelFilter * filter)127 void SortedDirModel::removeFilter(AbstractSortedDirModelFilter *filter)
128 {
129 d->mFilters.removeAll(filter);
130 applyFilters();
131 }
132
dirLister() const133 KDirLister *SortedDirModel::dirLister() const
134 {
135 return d->mSourceModel->dirLister();
136 }
137
reload()138 void SortedDirModel::reload()
139 {
140 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
141 d->mSourceModel->clearSemanticInfoCache();
142 #endif
143 dirLister()->updateDirectory(dirLister()->url());
144 }
145
setBlackListedExtensions(const QStringList & list)146 void SortedDirModel::setBlackListedExtensions(const QStringList &list)
147 {
148 d->mBlackListedExtensions = list;
149 }
150
itemForIndex(const QModelIndex & index) const151 KFileItem SortedDirModel::itemForIndex(const QModelIndex &index) const
152 {
153 if (!index.isValid()) {
154 return KFileItem();
155 }
156
157 QModelIndex sourceIndex = mapToSource(index);
158 return d->mSourceModel->itemForIndex(sourceIndex);
159 }
160
urlForIndex(const QModelIndex & index) const161 QUrl SortedDirModel::urlForIndex(const QModelIndex &index) const
162 {
163 KFileItem item = itemForIndex(index);
164 return item.isNull() ? QUrl() : item.url();
165 }
166
itemForSourceIndex(const QModelIndex & sourceIndex) const167 KFileItem SortedDirModel::itemForSourceIndex(const QModelIndex &sourceIndex) const
168 {
169 if (!sourceIndex.isValid()) {
170 return KFileItem();
171 }
172 return d->mSourceModel->itemForIndex(sourceIndex);
173 }
174
indexForItem(const KFileItem & item) const175 QModelIndex SortedDirModel::indexForItem(const KFileItem &item) const
176 {
177 if (item.isNull()) {
178 return QModelIndex();
179 }
180
181 QModelIndex sourceIndex = d->mSourceModel->indexForItem(item);
182 return mapFromSource(sourceIndex);
183 }
184
indexForUrl(const QUrl & url) const185 QModelIndex SortedDirModel::indexForUrl(const QUrl &url) const
186 {
187 if (!url.isValid()) {
188 return QModelIndex();
189 }
190 QModelIndex sourceIndex = d->mSourceModel->indexForUrl(url);
191 return mapFromSource(sourceIndex);
192 }
193
filterAcceptsRow(int row,const QModelIndex & parent) const194 bool SortedDirModel::filterAcceptsRow(int row, const QModelIndex &parent) const
195 {
196 QModelIndex index = d->mSourceModel->index(row, 0, parent);
197 KFileItem fileItem = d->mSourceModel->itemForIndex(index);
198
199 MimeTypeUtils::Kinds kind = MimeTypeUtils::fileItemKind(fileItem);
200 if (d->mKindFilter != MimeTypeUtils::Kinds() && !(d->mKindFilter & kind)) {
201 return false;
202 }
203
204 if (kind != MimeTypeUtils::KIND_DIR && kind != MimeTypeUtils::KIND_ARCHIVE) {
205 int dotPos = fileItem.name().lastIndexOf(QLatin1Char('.'));
206 if (dotPos >= 1) {
207 QString extension = fileItem.name().mid(dotPos + 1).toLower();
208 if (d->mBlackListedExtensions.contains(extension)) {
209 return false;
210 }
211 }
212 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
213 if (!d->mSourceModel->semanticInfoAvailableForIndex(index)) {
214 for (const AbstractSortedDirModelFilter *filter : qAsConst(d->mFilters)) {
215 // Make sure we have semanticinfo, otherwise retrieve it and
216 // return false, we will be called again later when it is
217 // there.
218 if (filter->needsSemanticInfo()) {
219 d->mSourceModel->retrieveSemanticInfoForIndex(index);
220 return false;
221 }
222 }
223 }
224 #endif
225
226 for (const AbstractSortedDirModelFilter *filter : qAsConst(d->mFilters)) {
227 if (!filter->acceptsIndex(index)) {
228 return false;
229 }
230 }
231 }
232 return KDirSortFilterProxyModel::filterAcceptsRow(row, parent);
233 }
234
semanticInfoBackEnd() const235 AbstractSemanticInfoBackEnd *SortedDirModel::semanticInfoBackEnd() const
236 {
237 #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE
238 return 0;
239 #else
240 return d->mSourceModel->semanticInfoBackEnd();
241 #endif
242 }
243
244 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
semanticInfoForSourceIndex(const QModelIndex & sourceIndex) const245 SemanticInfo SortedDirModel::semanticInfoForSourceIndex(const QModelIndex &sourceIndex) const
246 {
247 return d->mSourceModel->semanticInfoForIndex(sourceIndex);
248 }
249 #endif
250
applyFilters()251 void SortedDirModel::applyFilters()
252 {
253 d->mDelayedApplyFiltersTimer.start();
254 }
255
doApplyFilters()256 void SortedDirModel::doApplyFilters()
257 {
258 QSortFilterProxyModel::invalidateFilter();
259 }
260
lessThan(const QModelIndex & left,const QModelIndex & right) const261 bool SortedDirModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
262 {
263 const KFileItem leftItem = itemForSourceIndex(left);
264 const KFileItem rightItem = itemForSourceIndex(right);
265
266 const bool leftIsDirOrArchive = ArchiveUtils::fileItemIsDirOrArchive(leftItem);
267 const bool rightIsDirOrArchive = ArchiveUtils::fileItemIsDirOrArchive(rightItem);
268
269 if (leftIsDirOrArchive != rightIsDirOrArchive) {
270 return sortOrder() == Qt::AscendingOrder ? leftIsDirOrArchive : rightIsDirOrArchive;
271 }
272
273 // Apply special sort handling only to images. For folders/archives or when
274 // a secondary criterion is needed, delegate sorting to the parent class.
275 if (!leftIsDirOrArchive) {
276 if (sortColumn() == KDirModel::ModifiedTime) {
277 const QDateTime leftDate = TimeUtils::dateTimeForFileItem(leftItem);
278 const QDateTime rightDate = TimeUtils::dateTimeForFileItem(rightItem);
279
280 if (leftDate != rightDate) {
281 return leftDate < rightDate;
282 }
283 }
284 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE
285 if (sortRole() == SemanticInfoDirModel::RatingRole) {
286 const int leftRating = d->mSourceModel->data(left, SemanticInfoDirModel::RatingRole).toInt();
287 const int rightRating = d->mSourceModel->data(right, SemanticInfoDirModel::RatingRole).toInt();
288
289 if (leftRating != rightRating) {
290 return leftRating < rightRating;
291 }
292 }
293 #endif
294 }
295
296 return KDirSortFilterProxyModel::lessThan(left, right);
297 }
298
hasDocuments() const299 bool SortedDirModel::hasDocuments() const
300 {
301 const int count = rowCount();
302 if (count == 0) {
303 return false;
304 }
305 for (int row = 0; row < count; ++row) {
306 const QModelIndex idx = index(row, 0);
307 const KFileItem item = itemForIndex(idx);
308 if (!ArchiveUtils::fileItemIsDirOrArchive(item)) {
309 return true;
310 }
311 }
312 return false;
313 }
314
setDirLister(KDirLister * dirLister)315 void SortedDirModel::setDirLister(KDirLister *dirLister)
316 {
317 d->mSourceModel->setDirLister(dirLister);
318 }
319
320 } // namespace
321