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> *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
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. *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20 ***************************************************************************/
22 #include "dolphinsortfilterproxymodel.h"
24 #include <kdirmodel.h>
25 #include <kfileitem.h>
27 static const int dolphinMapSize
= 6;
28 static int dolphinViewToDirModelColumn
[] = {
29 KDirModel::Name
, // DolphinView::SortByName
30 KDirModel::Size
, // DolphinView::SortBySize
31 KDirModel::ModifiedTime
, // DolphinView::SortByDate
32 KDirModel::Permissions
, // DolphinView::SortByPermissions
33 KDirModel::Owner
, // DolphinView::SortByOwner
34 KDirModel::Group
// DolphinView::SortByGroup
37 static DolphinView::Sorting dirModelColumnToDolphinView
[] = {
38 DolphinView::SortByName
, // KDirModel::Name
39 DolphinView::SortBySize
, // KDirModel::Size
40 DolphinView::SortByDate
, // KDirModel::ModifiedTime
41 DolphinView::SortByPermissions
, // KDirModel::Permissions
42 DolphinView::SortByOwner
, // KDirModel::Owner
43 DolphinView::SortByGroup
// KDirModel::Group
47 DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject
* parent
) :
48 QSortFilterProxyModel(parent
),
50 m_sorting(DolphinView::SortByName
),
51 m_sortOrder(Qt::Ascending
)
53 setDynamicSortFilter(true);
55 // sort by the user visible string for now
56 setSortRole(Qt::DisplayRole
);
57 setSortCaseSensitivity(Qt::CaseInsensitive
);
58 sort(KDirModel::Name
, Qt::Ascending
);
61 DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel()
65 void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting
)
67 // Update the sort column by mapping DolpginView::Sorting to
68 // KDirModel::ModelColumns. We will keep the sortOrder.
69 Q_ASSERT(static_cast<int>(sorting
) >= 0 && static_cast<int>(sorting
) < dolphinMapSize
);
70 sort(dolphinViewToDirModelColumn
[static_cast<int>(sorting
)],
74 void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder
)
76 // change the sort order by keeping the current column
77 sort(dolphinViewToDirModelColumn
[m_sorting
], sortOrder
);
80 void DolphinSortFilterProxyModel::sort(int column
, Qt::SortOrder sortOrder
)
82 m_sortColumn
= column
;
83 m_sortOrder
= sortOrder
;
84 m_sorting
= (column
>= 0) && (column
< dolphinMapSize
) ?
85 dirModelColumnToDolphinView
[column
] :
86 DolphinView::SortByName
;
87 QSortFilterProxyModel::sort(column
, sortOrder
);
90 bool DolphinSortFilterProxyModel::hasChildren(const QModelIndex
& parent
) const
92 const QModelIndex sourceParent
= mapToSource(parent
);
93 return sourceModel()->hasChildren(sourceParent
);
96 bool DolphinSortFilterProxyModel::canFetchMore(const QModelIndex
& parent
) const
98 const QModelIndex sourceParent
= mapToSource(parent
);
99 return sourceModel()->canFetchMore(sourceParent
);
102 DolphinView::Sorting
DolphinSortFilterProxyModel::sortingForColumn(int column
)
104 if ((column
>= 0) && (column
< dolphinMapSize
)) {
105 return dirModelColumnToDolphinView
[column
];
107 return DolphinView::SortByName
;
110 bool DolphinSortFilterProxyModel::lessThan(const QModelIndex
& left
,
111 const QModelIndex
& right
) const
113 KDirModel
* dirModel
= static_cast<KDirModel
*>(sourceModel());
115 QVariant leftData
= dirModel
->data(left
, sortRole());
116 QVariant rightData
= dirModel
->data(right
, sortRole());
118 if ((leftData
.type() == QVariant::String
) && (rightData
.type() == QVariant::String
)) {
119 // assure that directories are always sorted before files
120 // if the sorting is done by the 'Name' column
121 if (m_sortColumn
== KDirModel::Name
) {
122 const bool leftIsDir
= dirModel
->itemForIndex(left
)->isDir();
123 const bool rightIsDir
= dirModel
->itemForIndex(right
)->isDir();
124 if (leftIsDir
&& !rightIsDir
) {
128 if (!leftIsDir
&& rightIsDir
) {
133 const QString leftStr
= leftData
.toString();
134 const QString rightStr
= rightData
.toString();
136 return sortCaseSensitivity() ? (naturalCompare(leftStr
, rightStr
) < 0) :
137 (naturalCompare(leftStr
.toLower(), rightStr
.toLower()) < 0);
140 // We have set a SortRole and trust the ProxyModel to do
141 // the right thing for now.
142 return QSortFilterProxyModel::lessThan(left
, right
);
145 int DolphinSortFilterProxyModel::naturalCompare(const QString
& a
,
148 // This method chops the input a and b into pieces of
149 // digits and non-digits (a1.05 becomes a | 1 | . | 05)
150 // and compares these pieces of a and b to each other
151 // (first with first, second with second, ...).
153 // This is based on the natural sort order code code by Martin Pool
154 // http://sourcefrog.net/projects/natsort/
155 // Martin Pool agreed to license this under LGPL or GPL.
157 const QChar
* currA
= a
.unicode(); // iterator over a
158 const QChar
* currB
= b
.unicode(); // iterator over b
160 if (currA
== currB
) {
164 const QChar
* begSeqA
= currA
; // beginning of a new character sequence of a
165 const QChar
* begSeqB
= currB
;
167 while (!currA
->isNull() && !currB
->isNull()) {
168 // find sequence of characters ending at the first non-character
169 while (!currA
->isNull() && !currA
->isDigit()) {
173 while (!currB
->isNull() && !currB
->isDigit()) {
177 // compare these sequences
178 const QString
subA(begSeqA
, currA
- begSeqA
);
179 const QString
subB(begSeqB
, currB
- begSeqB
);
180 const int cmp
= QString::localeAwareCompare(subA
, subB
);
185 if (currA
->isNull() || currB
->isNull()) {
189 // now some digits follow...
190 if ((*currA
== '0') || (*currB
== '0')) {
191 // one digit-sequence starts with 0 -> assume we are in a fraction part
192 // do left aligned comparison (numbers are considered left aligned)
194 if (!currA
->isDigit() && !currB
->isDigit()) {
197 else if (!currA
->isDigit()) {
200 else if (!currB
->isDigit()) {
203 else if (*currA
< *currB
) {
206 else if (*currA
> *currB
) {
214 // No digit-sequence starts with 0 -> assume we are looking at some integer
215 // do right aligned comparison.
217 // The longest run of digits wins. That aside, the greatest
218 // value wins, but we can't know that it will until we've scanned
219 // both numbers to know that they have the same magnitude.
223 if (!currA
->isDigit() && !currB
->isDigit()) {
229 else if (!currA
->isDigit()) {
232 else if (!currB
->isDigit()) {
235 else if ((*currA
< *currB
) && (weight
== 0)) {
238 else if ((*currA
> *currB
) && (weight
== 0)) {
250 if (currA
->isNull() && currB
->isNull()) {
254 return currA
->isNull() ? -1 : +1;
257 #include "dolphinsortfilterproxymodel.moc"