]>
cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinsortfilterproxymodel.cpp
592b0aa1ca9b464f72a8aa8e3bc942adb8bde6af
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at> *
3 * Copyright (C) 2006 by Dominic Battre <dominic@battre.de> *
4 * Copyright (C) 2006 by Martin Pool <mbp@canonical.com> *
5 * Copyright (C) 2007 by Rafael Fernández López <ereslibre@gmail.com> *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
21 ***************************************************************************/
23 #include "dolphinsortfilterproxymodel.h"
26 #include <config-nepomuk.h>
27 #include <nepomuk/global.h>
28 #include <nepomuk/resource.h>
29 #include <nepomuk/tag.h>
32 #include <kdirmodel.h>
33 #include <kfileitem.h>
34 #include <kdatetime.h>
37 static DolphinView::Sorting sortingTypeTable
[] =
39 DolphinView::SortByName
, // KDirModel::Name
40 DolphinView::SortBySize
, // KDirModel::Size
41 DolphinView::SortByDate
, // KDirModel::ModifiedTime
42 DolphinView::SortByPermissions
, // KDirModel::Permissions
43 DolphinView::SortByOwner
, // KDirModel::Owner
44 DolphinView::SortByGroup
, // KDirModel::Group
45 DolphinView::SortByType
// KDirModel::Type
47 , DolphinView::SortByRating
48 , DolphinView::SortByTags
52 DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject
* parent
) :
53 KSortFilterProxyModel(parent
),
54 m_sorting(DolphinView::SortByName
),
55 m_sortOrder(Qt::AscendingOrder
)
57 setDynamicSortFilter(true);
59 // sort by the user visible string for now
60 setSortRole(DolphinView::SortByName
);
61 setSortCaseSensitivity(Qt::CaseInsensitive
);
62 sort(KDirModel::Name
, Qt::AscendingOrder
);
65 DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel()
69 void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting
)
71 // change the sorting column by keeping the current sort order
72 sort(sorting
, m_sortOrder
);
75 void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder
)
77 // change the sort order by keeping the current column
78 sort(m_sorting
, sortOrder
);
81 void DolphinSortFilterProxyModel::sort(int column
, Qt::SortOrder sortOrder
)
83 m_sorting
= sortingForColumn(column
);
84 m_sortOrder
= sortOrder
;
85 setSortRole(m_sorting
);
86 KSortFilterProxyModel::sort(column
, sortOrder
);
89 bool DolphinSortFilterProxyModel::hasChildren(const QModelIndex
& parent
) const
91 const QModelIndex sourceParent
= mapToSource(parent
);
92 return sourceModel()->hasChildren(sourceParent
);
95 bool DolphinSortFilterProxyModel::canFetchMore(const QModelIndex
& parent
) const
97 const QModelIndex sourceParent
= mapToSource(parent
);
98 return sourceModel()->canFetchMore(sourceParent
);
101 DolphinView::Sorting
DolphinSortFilterProxyModel::sortingForColumn(int column
)
103 Q_ASSERT(column
>= 0);
104 Q_ASSERT(column
< static_cast<int>(sizeof(sortingTypeTable
) / sizeof(DolphinView::Sorting
)));
105 return sortingTypeTable
[column
];
108 bool DolphinSortFilterProxyModel::lessThanGeneralPurpose(const QModelIndex
&left
,
109 const QModelIndex
&right
) const
111 KDirModel
* dirModel
= static_cast<KDirModel
*>(sourceModel());
113 const KFileItem
* leftFileItem
= dirModel
->itemForIndex(left
);
114 const KFileItem
* rightFileItem
= dirModel
->itemForIndex(right
);
116 switch (sortRole()) {
117 case DolphinView::SortByName
: {
118 QString
leftFileName(leftFileItem
->name());
119 if (leftFileName
.at(0) == '.') {
120 leftFileName
= leftFileName
.mid(1);
123 QString
rightFileName(rightFileItem
->name());
124 if (rightFileName
.at(0) == '.') {
125 rightFileName
= rightFileName
.mid(1);
128 // We don't care about case for building categories. We also don't
129 // want here to compare by a natural comparison.
130 return naturalCompare(leftFileName
, rightFileName
) < 0;
133 case DolphinView::SortBySize
:
134 // If we are sorting by size, show folders first. We will sort them
136 return leftFileItem
->isDir() && !rightFileItem
->isDir();
138 case DolphinView::SortByDate
: {
139 KDateTime leftTime
, rightTime
;
140 leftTime
.setTime_t(leftFileItem
->time(KIO::UDS_MODIFICATION_TIME
));
141 rightTime
.setTime_t(rightFileItem
->time(KIO::UDS_MODIFICATION_TIME
));
142 return leftTime
> rightTime
;
145 case DolphinView::SortByPermissions
: {
146 return naturalCompare(leftFileItem
->permissionsString(),
147 rightFileItem
->permissionsString()) < 0;
150 case DolphinView::SortByOwner
: {
151 return naturalCompare(leftFileItem
->user().toLower(),
152 rightFileItem
->user().toLower()) < 0;
155 case DolphinView::SortByGroup
: {
156 return naturalCompare(leftFileItem
->group().toLower(),
157 rightFileItem
->group().toLower()) < 0;
160 case DolphinView::SortByType
: {
161 // If we are sorting by size, show folders first. We will sort them
163 if (leftFileItem
->isDir() && !rightFileItem
->isDir()) {
165 } else if (!leftFileItem
->isDir() && rightFileItem
->isDir()) {
169 return naturalCompare(leftFileItem
->mimeComment().toLower(),
170 rightFileItem
->mimeComment().toLower()) < 0;
173 case DolphinView::SortByRating
: {
174 const quint32 leftRating
= ratingForIndex(left
);
175 const quint32 rightRating
= ratingForIndex(right
);
176 return leftRating
> rightRating
;
178 case DolphinView::SortByTags
: {
179 const QString leftTags
= tagsForIndex(left
);
180 const QString rightTags
= tagsForIndex(right
);
182 if (leftTags
.isEmpty() && !rightTags
.isEmpty())
184 else if (!leftTags
.isEmpty() && rightTags
.isEmpty())
187 return naturalCompare(tagsForIndex(left
), tagsForIndex(right
)) < 0;
196 bool DolphinSortFilterProxyModel::lessThan(const QModelIndex
& left
,
197 const QModelIndex
& right
) const
199 KDirModel
* dirModel
= static_cast<KDirModel
*>(sourceModel());
201 const KFileItem
* leftFileItem
= dirModel
->itemForIndex(left
);
202 const KFileItem
* rightFileItem
= dirModel
->itemForIndex(right
);
204 // If we are sorting by rating, folders and files are citizens of the same
205 // class. Same if we are sorting by tags.
207 if ((sortRole() != DolphinView::SortByRating
) &&
208 (sortRole() != DolphinView::SortByTags
))
211 // On our priority, folders go above regular files.
212 if (leftFileItem
->isDir() && !rightFileItem
->isDir()) {
214 } else if (!leftFileItem
->isDir() && rightFileItem
->isDir()) {
221 // Hidden elements go before visible ones, if they both are
223 if (leftFileItem
->isHidden() && !rightFileItem
->isHidden()) {
225 } else if (!leftFileItem
->isHidden() && rightFileItem
->isHidden()) {
229 switch (sortRole()) {
230 case DolphinView::SortByName
: {
231 // So we are in the same priority, what counts now is their names.
232 const QVariant leftData
= dirModel
->data(left
, KDirModel::Name
);
233 const QVariant rightData
= dirModel
->data(right
, KDirModel::Name
);
234 const QString
leftValueString(leftData
.toString());
235 const QString
rightValueString(rightData
.toString());
237 return sortCaseSensitivity() ?
238 (naturalCompare(leftValueString
, rightValueString
) < 0) :
239 (naturalCompare(leftValueString
.toLower(), rightValueString
.toLower()) < 0);
242 case DolphinView::SortBySize
: {
243 // If we have two folders, what we have to measure is the number of
244 // items that contains each other
245 if (leftFileItem
->isDir() && rightFileItem
->isDir()) {
246 QVariant leftValue
= dirModel
->data(left
, KDirModel::ChildCountRole
);
247 int leftCount
= leftValue
.type() == QVariant::Int
? leftValue
.toInt() : KDirModel::ChildCountUnknown
;
249 QVariant rightValue
= dirModel
->data(right
, KDirModel::ChildCountRole
);
250 int rightCount
= rightValue
.type() == QVariant::Int
? rightValue
.toInt() : KDirModel::ChildCountUnknown
;
252 // In the case they two have the same child items, we sort them by
253 // their names. So we have always everything ordered. We also check
254 // if we are taking in count their cases.
255 if (leftCount
== rightCount
) {
256 return sortCaseSensitivity() ? (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
257 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
260 // If they had different number of items, we sort them depending
261 // on how many items had each other.
262 return leftCount
< rightCount
;
265 // If what we are measuring is two files and they have the same size,
266 // sort them by their file names.
267 if (leftFileItem
->size() == rightFileItem
->size()) {
268 return sortCaseSensitivity() ? (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
269 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
272 // If their sizes are different, sort them by their sizes, as expected.
273 return leftFileItem
->size() < rightFileItem
->size();
276 case DolphinView::SortByDate
: {
277 KDateTime leftTime
, rightTime
;
278 leftTime
.setTime_t(leftFileItem
->time(KIO::UDS_MODIFICATION_TIME
));
279 rightTime
.setTime_t(rightFileItem
->time(KIO::UDS_MODIFICATION_TIME
));
281 if (leftTime
== rightTime
) {
282 return sortCaseSensitivity() ?
283 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
284 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
287 return leftTime
> rightTime
;
290 case DolphinView::SortByPermissions
: {
291 if (leftFileItem
->permissionsString() == rightFileItem
->permissionsString()) {
292 return sortCaseSensitivity() ?
293 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
294 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
297 return naturalCompare(leftFileItem
->permissionsString(),
298 rightFileItem
->permissionsString()) < 0;
301 case DolphinView::SortByOwner
: {
302 if (leftFileItem
->user() == rightFileItem
->user()) {
303 return sortCaseSensitivity() ?
304 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
305 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
308 return naturalCompare(leftFileItem
->user(), rightFileItem
->user()) < 0;
311 case DolphinView::SortByGroup
: {
312 if (leftFileItem
->group() == rightFileItem
->group()) {
313 return sortCaseSensitivity() ? (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
314 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
317 return naturalCompare(leftFileItem
->group(),
318 rightFileItem
->group()) < 0;
321 case DolphinView::SortByType
: {
322 if (leftFileItem
->mimetype() == rightFileItem
->mimetype()) {
323 return sortCaseSensitivity() ?
324 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
325 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
328 return naturalCompare(leftFileItem
->mimeComment(),
329 rightFileItem
->mimeComment()) < 0;
333 case DolphinView::SortByRating
: {
334 const quint32 leftRating
= ratingForIndex(left
);
335 const quint32 rightRating
= ratingForIndex(right
);
337 if (leftRating
== rightRating
) {
338 // On our priority, folders go above regular files.
339 // This checks are needed (don't think it's the same doing it here
340 // than above). Here we make dirs citizens of first class because
341 // we know we are on the same category. On the check we do on the
342 // top of the method we don't know, so we remove that check when we
343 // are sorting by rating. (ereslibre)
344 if (leftFileItem
->isDir() && !rightFileItem
->isDir()) {
346 } else if (!leftFileItem
->isDir() && rightFileItem
->isDir()) {
350 return sortCaseSensitivity() ?
351 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
352 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
355 return leftRating
> rightRating
;
358 case DolphinView::SortByTags
: {
359 const QString leftTags
= tagsForIndex(left
);
360 const QString rightTags
= tagsForIndex(right
);
362 if (leftTags
== rightTags
) {
363 // On our priority, folders go above regular files.
364 // This checks are needed (don't think it's the same doing it here
365 // than above). Here we make dirs citizens of first class because
366 // we know we are on the same category. On the check we do on the
367 // top of the method we don't know, so we remove that check when we
368 // are sorting by tags. (ereslibre)
369 if (leftFileItem
->isDir() && !rightFileItem
->isDir()) {
371 } else if (!leftFileItem
->isDir() && rightFileItem
->isDir()) {
375 return sortCaseSensitivity() ?
376 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
377 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
380 return naturalCompare(leftTags
, rightTags
) < 0;
385 // We have set a SortRole and trust the ProxyModel to do
386 // the right thing for now.
387 return QSortFilterProxyModel::lessThan(left
, right
);
390 quint32
DolphinSortFilterProxyModel::ratingForIndex(const QModelIndex
& index
)
395 const KDirModel
* dirModel
= static_cast<const KDirModel
*>(index
.model());
396 KFileItem
* item
= dirModel
->itemForIndex(index
);
398 const Nepomuk::Resource
resource(item
->url().url(), Nepomuk::NFO::File());
399 rating
= resource
.rating();
408 QString
DolphinSortFilterProxyModel::tagsForIndex(const QModelIndex
& index
)
413 const KDirModel
* dirModel
= static_cast<const KDirModel
*>(index
.model());
414 KFileItem
* item
= dirModel
->itemForIndex(index
);
416 const Nepomuk::Resource
resource(item
->url().url(), Nepomuk::NFO::File());
417 const QList
<Nepomuk::Tag
> tags
= resource
.tags();
418 QStringList stringList
;
419 foreach (const Nepomuk::Tag
& tag
, tags
) {
420 stringList
.append(tag
.label());
424 foreach (const QString
& str
, stringList
) {
429 if (!tagsString
.isEmpty())
430 tagsString
.resize(tagsString
.size() - 2);
439 int DolphinSortFilterProxyModel::naturalCompare(const QString
& a
,
442 // This method chops the input a and b into pieces of
443 // digits and non-digits (a1.05 becomes a | 1 | . | 05)
444 // and compares these pieces of a and b to each other
445 // (first with first, second with second, ...).
447 // This is based on the natural sort order code code by Martin Pool
448 // http://sourcefrog.net/projects/natsort/
449 // Martin Pool agreed to license this under LGPL or GPL.
451 const QChar
* currA
= a
.unicode(); // iterator over a
452 const QChar
* currB
= b
.unicode(); // iterator over b
454 if (currA
== currB
) {
458 const QChar
* begSeqA
= currA
; // beginning of a new character sequence of a
459 const QChar
* begSeqB
= currB
;
461 while (!currA
->isNull() && !currB
->isNull()) {
462 // find sequence of characters ending at the first non-character
463 while (!currA
->isNull() && !currA
->isDigit()) {
467 while (!currB
->isNull() && !currB
->isDigit()) {
471 // compare these sequences
472 const QString
subA(begSeqA
, currA
- begSeqA
);
473 const QString
subB(begSeqB
, currB
- begSeqB
);
474 const int cmp
= QString::localeAwareCompare(subA
, subB
);
479 if (currA
->isNull() || currB
->isNull()) {
483 // now some digits follow...
484 if ((*currA
== '0') || (*currB
== '0')) {
485 // one digit-sequence starts with 0 -> assume we are in a fraction part
486 // do left aligned comparison (numbers are considered left aligned)
488 if (!currA
->isDigit() && !currB
->isDigit()) {
490 } else if (!currA
->isDigit()) {
492 } else if (!currB
->isDigit()) {
494 } else if (*currA
< *currB
) {
496 } else if (*currA
> *currB
) {
503 // No digit-sequence starts with 0 -> assume we are looking at some integer
504 // do right aligned comparison.
506 // The longest run of digits wins. That aside, the greatest
507 // value wins, but we can't know that it will until we've scanned
508 // both numbers to know that they have the same magnitude.
512 if (!currA
->isDigit() && !currB
->isDigit()) {
517 } else if (!currA
->isDigit()) {
519 } else if (!currB
->isDigit()) {
521 } else if ((*currA
< *currB
) && (weight
== 0)) {
523 } else if ((*currA
> *currB
) && (weight
== 0)) {
535 if (currA
->isNull() && currB
->isNull()) {
539 return currA
->isNull() ? -1 : + 1;
542 #include "dolphinsortfilterproxymodel.moc"