]>
cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinsortfilterproxymodel.cpp
648b6133b66b8a5b103c3912334e10e0690e504c
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>
31 #include <kdirmodel.h>
32 #include <kfileitem.h>
33 #include <kdatetime.h>
35 static DolphinView::Sorting sortingTypeTable
[] =
37 DolphinView::SortByName
, // KDirModel::Name
38 DolphinView::SortBySize
, // KDirModel::Size
39 DolphinView::SortByDate
, // KDirModel::ModifiedTime
40 DolphinView::SortByPermissions
, // KDirModel::Permissions
41 DolphinView::SortByOwner
, // KDirModel::Owner
42 DolphinView::SortByGroup
, // KDirModel::Group
43 DolphinView::SortByType
// KDirModel::Type
45 , DolphinView::SortByRating
46 , DolphinView::SortByTags
50 DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject
* parent
) :
51 KSortFilterProxyModel(parent
),
52 m_sorting(DolphinView::SortByName
),
53 m_sortOrder(Qt::AscendingOrder
)
55 setDynamicSortFilter(true);
57 // sort by the user visible string for now
58 setSortRole(DolphinView::SortByName
);
59 setSortCaseSensitivity(Qt::CaseInsensitive
);
60 sort(KDirModel::Name
, Qt::AscendingOrder
);
63 DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel()
67 void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting
)
69 // change the sorting column by keeping the current sort order
70 sort(sorting
, m_sortOrder
);
73 void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder
)
75 // change the sort order by keeping the current column
76 sort(m_sorting
, sortOrder
);
79 void DolphinSortFilterProxyModel::sort(int column
, Qt::SortOrder sortOrder
)
81 m_sorting
= sortingForColumn(column
);
82 m_sortOrder
= sortOrder
;
83 setSortRole(m_sorting
);
84 KSortFilterProxyModel::sort(column
, sortOrder
);
87 bool DolphinSortFilterProxyModel::hasChildren(const QModelIndex
& parent
) const
89 const QModelIndex sourceParent
= mapToSource(parent
);
90 return sourceModel()->hasChildren(sourceParent
);
93 bool DolphinSortFilterProxyModel::canFetchMore(const QModelIndex
& parent
) const
95 const QModelIndex sourceParent
= mapToSource(parent
);
96 return sourceModel()->canFetchMore(sourceParent
);
99 DolphinView::Sorting
DolphinSortFilterProxyModel::sortingForColumn(int column
)
101 Q_ASSERT(column
>= 0);
102 Q_ASSERT(column
< static_cast<int>(sizeof(sortingTypeTable
) / sizeof(DolphinView::Sorting
)));
103 return sortingTypeTable
[column
];
106 bool DolphinSortFilterProxyModel::lessThanGeneralPurpose(const QModelIndex
&left
,
107 const QModelIndex
&right
) const
109 KDirModel
* dirModel
= static_cast<KDirModel
*>(sourceModel());
111 const KFileItem
* leftFileItem
= dirModel
->itemForIndex(left
);
112 const KFileItem
* rightFileItem
= dirModel
->itemForIndex(right
);
114 switch (sortRole()) {
115 case DolphinView::SortByName
: {
116 QString
leftFileName(leftFileItem
->name());
117 if (leftFileName
.at(0) == '.') {
118 leftFileName
= leftFileName
.mid(1);
121 QString
rightFileName(rightFileItem
->name());
122 if (rightFileName
.at(0) == '.') {
123 rightFileName
= rightFileName
.mid(1);
126 // We don't care about case for building categories. We also don't
127 // want here to compare by a natural comparison.
128 return naturalCompare(leftFileName
, rightFileName
) < 0;
131 case DolphinView::SortBySize
:
132 // If we are sorting by size, show folders first. We will sort them
134 return leftFileItem
->isDir() && !rightFileItem
->isDir();
136 case DolphinView::SortByDate
: {
137 KDateTime leftTime
, rightTime
;
138 leftTime
.setTime_t(leftFileItem
->time(KIO::UDS_MODIFICATION_TIME
));
139 rightTime
.setTime_t(rightFileItem
->time(KIO::UDS_MODIFICATION_TIME
));
140 return leftTime
> rightTime
;
143 case DolphinView::SortByPermissions
: {
144 return naturalCompare(leftFileItem
->permissionsString(),
145 rightFileItem
->permissionsString()) < 0;
148 case DolphinView::SortByOwner
: {
149 return naturalCompare(leftFileItem
->user().toLower(),
150 rightFileItem
->user().toLower()) < 0;
153 case DolphinView::SortByGroup
: {
154 return naturalCompare(leftFileItem
->group().toLower(),
155 rightFileItem
->group().toLower()) < 0;
158 case DolphinView::SortByType
: {
159 // If we are sorting by size, show folders first. We will sort them
161 if (leftFileItem
->isDir() && !rightFileItem
->isDir()) {
163 } else if (!leftFileItem
->isDir() && rightFileItem
->isDir()) {
167 return naturalCompare(leftFileItem
->mimeComment().toLower(),
168 rightFileItem
->mimeComment().toLower()) < 0;
171 case DolphinView::SortByRating
: {
172 const quint32 leftRating
= ratingForIndex(left
);
173 const quint32 rightRating
= ratingForIndex(right
);
174 return leftRating
> rightRating
;
183 bool DolphinSortFilterProxyModel::lessThan(const QModelIndex
& left
,
184 const QModelIndex
& right
) const
186 KDirModel
* dirModel
= static_cast<KDirModel
*>(sourceModel());
188 const KFileItem
* leftFileItem
= dirModel
->itemForIndex(left
);
189 const KFileItem
* rightFileItem
= dirModel
->itemForIndex(right
);
191 // If we are sorting by rating, folders and files are citizens of the same
193 if (sortRole() != DolphinView::SortByRating
)
195 // On our priority, folders go above regular files.
196 if (leftFileItem
->isDir() && !rightFileItem
->isDir()) {
198 } else if (!leftFileItem
->isDir() && rightFileItem
->isDir()) {
203 // Hidden elements go before visible ones, if they both are
205 if (leftFileItem
->isHidden() && !rightFileItem
->isHidden()) {
207 } else if (!leftFileItem
->isHidden() && rightFileItem
->isHidden()) {
211 switch (sortRole()) {
212 case DolphinView::SortByName
: {
213 // So we are in the same priority, what counts now is their names.
214 const QVariant leftData
= dirModel
->data(left
, KDirModel::Name
);
215 const QVariant rightData
= dirModel
->data(right
, KDirModel::Name
);
216 const QString
leftValueString(leftData
.toString());
217 const QString
rightValueString(rightData
.toString());
219 return sortCaseSensitivity() ?
220 (naturalCompare(leftValueString
, rightValueString
) < 0) :
221 (naturalCompare(leftValueString
.toLower(), rightValueString
.toLower()) < 0);
224 case DolphinView::SortBySize
: {
225 // If we have two folders, what we have to measure is the number of
226 // items that contains each other
227 if (leftFileItem
->isDir() && rightFileItem
->isDir()) {
228 QVariant leftValue
= dirModel
->data(left
, KDirModel::ChildCountRole
);
229 int leftCount
= leftValue
.type() == QVariant::Int
? leftValue
.toInt() : KDirModel::ChildCountUnknown
;
231 QVariant rightValue
= dirModel
->data(right
, KDirModel::ChildCountRole
);
232 int rightCount
= rightValue
.type() == QVariant::Int
? rightValue
.toInt() : KDirModel::ChildCountUnknown
;
234 // In the case they two have the same child items, we sort them by
235 // their names. So we have always everything ordered. We also check
236 // if we are taking in count their cases.
237 if (leftCount
== rightCount
) {
238 return sortCaseSensitivity() ? (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
239 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
242 // If they had different number of items, we sort them depending
243 // on how many items had each other.
244 return leftCount
< rightCount
;
247 // If what we are measuring is two files and they have the same size,
248 // sort them by their file names.
249 if (leftFileItem
->size() == rightFileItem
->size()) {
250 return sortCaseSensitivity() ? (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
251 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
254 // If their sizes are different, sort them by their sizes, as expected.
255 return leftFileItem
->size() < rightFileItem
->size();
258 case DolphinView::SortByDate
: {
259 KDateTime leftTime
, rightTime
;
260 leftTime
.setTime_t(leftFileItem
->time(KIO::UDS_MODIFICATION_TIME
));
261 rightTime
.setTime_t(rightFileItem
->time(KIO::UDS_MODIFICATION_TIME
));
263 if (leftTime
== rightTime
) {
264 return sortCaseSensitivity() ?
265 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
266 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
269 return leftTime
> rightTime
;
272 case DolphinView::SortByPermissions
: {
273 if (leftFileItem
->permissionsString() == rightFileItem
->permissionsString()) {
274 return sortCaseSensitivity() ?
275 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
276 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
279 return naturalCompare(leftFileItem
->permissionsString(),
280 rightFileItem
->permissionsString()) < 0;
283 case DolphinView::SortByOwner
: {
284 if (leftFileItem
->user() == rightFileItem
->user()) {
285 return sortCaseSensitivity() ?
286 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
287 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
290 return naturalCompare(leftFileItem
->user(), rightFileItem
->user()) < 0;
293 case DolphinView::SortByGroup
: {
294 if (leftFileItem
->group() == rightFileItem
->group()) {
295 return sortCaseSensitivity() ? (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
296 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
299 return naturalCompare(leftFileItem
->group(),
300 rightFileItem
->group()) < 0;
303 case DolphinView::SortByType
: {
304 if (leftFileItem
->mimetype() == rightFileItem
->mimetype()) {
305 return sortCaseSensitivity() ?
306 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
307 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
310 return naturalCompare(leftFileItem
->mimeComment(),
311 rightFileItem
->mimeComment()) < 0;
315 case DolphinView::SortByRating
: {
316 const quint32 leftRating
= ratingForIndex(left
);
317 const quint32 rightRating
= ratingForIndex(right
);
319 if (leftRating
== rightRating
) {
320 // On our priority, folders go above regular files.
321 if (leftFileItem
->isDir() && !rightFileItem
->isDir()) {
323 } else if (!leftFileItem
->isDir() && rightFileItem
->isDir()) {
327 return sortCaseSensitivity() ?
328 (naturalCompare(leftFileItem
->name(), rightFileItem
->name()) < 0) :
329 (naturalCompare(leftFileItem
->name().toLower(), rightFileItem
->name().toLower()) < 0);
332 return leftRating
> rightRating
;
337 // We have set a SortRole and trust the ProxyModel to do
338 // the right thing for now.
339 return QSortFilterProxyModel::lessThan(left
, right
);
342 quint32
DolphinSortFilterProxyModel::ratingForIndex(const QModelIndex
& index
) const
347 const KDirModel
* dirModel
= static_cast<const KDirModel
*>(sourceModel());
348 KFileItem
* item
= dirModel
->itemForIndex(index
);
350 const Nepomuk::Resource
resource(item
->url().url(), Nepomuk::NFO::File());
351 rating
= resource
.rating();
360 int DolphinSortFilterProxyModel::naturalCompare(const QString
& a
,
363 // This method chops the input a and b into pieces of
364 // digits and non-digits (a1.05 becomes a | 1 | . | 05)
365 // and compares these pieces of a and b to each other
366 // (first with first, second with second, ...).
368 // This is based on the natural sort order code code by Martin Pool
369 // http://sourcefrog.net/projects/natsort/
370 // Martin Pool agreed to license this under LGPL or GPL.
372 const QChar
* currA
= a
.unicode(); // iterator over a
373 const QChar
* currB
= b
.unicode(); // iterator over b
375 if (currA
== currB
) {
379 const QChar
* begSeqA
= currA
; // beginning of a new character sequence of a
380 const QChar
* begSeqB
= currB
;
382 while (!currA
->isNull() && !currB
->isNull()) {
383 // find sequence of characters ending at the first non-character
384 while (!currA
->isNull() && !currA
->isDigit()) {
388 while (!currB
->isNull() && !currB
->isDigit()) {
392 // compare these sequences
393 const QString
subA(begSeqA
, currA
- begSeqA
);
394 const QString
subB(begSeqB
, currB
- begSeqB
);
395 const int cmp
= QString::localeAwareCompare(subA
, subB
);
400 if (currA
->isNull() || currB
->isNull()) {
404 // now some digits follow...
405 if ((*currA
== '0') || (*currB
== '0')) {
406 // one digit-sequence starts with 0 -> assume we are in a fraction part
407 // do left aligned comparison (numbers are considered left aligned)
409 if (!currA
->isDigit() && !currB
->isDigit()) {
411 } else if (!currA
->isDigit()) {
413 } else if (!currB
->isDigit()) {
415 } else if (*currA
< *currB
) {
417 } else if (*currA
> *currB
) {
424 // No digit-sequence starts with 0 -> assume we are looking at some integer
425 // do right aligned comparison.
427 // The longest run of digits wins. That aside, the greatest
428 // value wins, but we can't know that it will until we've scanned
429 // both numbers to know that they have the same magnitude.
433 if (!currA
->isDigit() && !currB
->isDigit()) {
438 } else if (!currA
->isDigit()) {
440 } else if (!currB
->isDigit()) {
442 } else if ((*currA
< *currB
) && (weight
== 0)) {
444 } else if ((*currA
> *currB
) && (weight
== 0)) {
456 if (currA
->isNull() && currB
->isNull()) {
460 return currA
->isNull() ? -1 : + 1;
463 #include "dolphinsortfilterproxymodel.moc"