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