]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinsortfilterproxymodel.cpp
b81f1800f3323f71e33d0d3e67def7dfba2a127f
[dolphin.git] / src / dolphinsortfilterproxymodel.cpp
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 * *
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. *
10 * *
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. *
15 * *
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 ***************************************************************************/
21
22 #include "dolphinsortfilterproxymodel.h"
23
24 #include <kdirmodel.h>
25 #include <kfileitem.h>
26
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
35 };
36
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
44 };
45
46
47 DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) :
48 QSortFilterProxyModel(parent),
49 m_sortColumn(0),
50 m_sorting(DolphinView::SortByName),
51 m_sortOrder(Qt::Ascending)
52 {
53 setDynamicSortFilter(true);
54
55 // sort by the user visible string for now
56 setSortRole(Qt::DisplayRole);
57 setSortCaseSensitivity(Qt::CaseInsensitive);
58 sort(KDirModel::Name, Qt::Ascending);
59 }
60
61 DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel()
62 {
63 }
64
65 void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting)
66 {
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)],
71 m_sortOrder);
72 }
73
74 void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder)
75 {
76 // change the sort order by keeping the current column
77 sort(dolphinViewToDirModelColumn[m_sorting], sortOrder);
78 }
79
80 void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder)
81 {
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);
88 }
89
90 bool DolphinSortFilterProxyModel::hasChildren(const QModelIndex& parent) const
91 {
92 const QModelIndex sourceParent = mapToSource(parent);
93 return sourceModel()->hasChildren(sourceParent);
94 }
95
96 bool DolphinSortFilterProxyModel::canFetchMore(const QModelIndex& parent) const
97 {
98 const QModelIndex sourceParent = mapToSource(parent);
99 return sourceModel()->canFetchMore(sourceParent);
100 }
101
102 DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column)
103 {
104 if ((column >= 0) && (column < dolphinMapSize)) {
105 return dirModelColumnToDolphinView[column];
106 }
107 return DolphinView::SortByName;
108 }
109
110 bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left,
111 const QModelIndex& right) const
112 {
113 KDirModel* dirModel = static_cast<KDirModel*>(sourceModel());
114
115 QVariant leftData = dirModel->data(left, sortRole());
116 QVariant rightData = dirModel->data(right, sortRole());
117
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) {
125 return true;
126 }
127
128 if (!leftIsDir && rightIsDir) {
129 return false;
130 }
131 }
132
133 const QString leftStr = leftData.toString();
134 const QString rightStr = rightData.toString();
135
136 return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) :
137 (naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0);
138 }
139
140 // We have set a SortRole and trust the ProxyModel to do
141 // the right thing for now.
142 return QSortFilterProxyModel::lessThan(left, right);
143 }
144
145 int DolphinSortFilterProxyModel::naturalCompare(const QString& a,
146 const QString& b)
147 {
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, ...).
152 //
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.
156
157 const QChar* currA = a.unicode(); // iterator over a
158 const QChar* currB = b.unicode(); // iterator over b
159
160 if (currA == currB) {
161 return 0;
162 }
163
164 const QChar* begSeqA = currA; // beginning of a new character sequence of a
165 const QChar* begSeqB = currB;
166
167 while (!currA->isNull() && !currB->isNull()) {
168 // find sequence of characters ending at the first non-character
169 while (!currA->isNull() && !currA->isDigit()) {
170 ++currA;
171 }
172
173 while (!currB->isNull() && !currB->isDigit()) {
174 ++currB;
175 }
176
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);
181 if (cmp != 0) {
182 return cmp;
183 }
184
185 if (currA->isNull() || currB->isNull()) {
186 break;
187 }
188
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)
193 while (1) {
194 if (!currA->isDigit() && !currB->isDigit()) {
195 break;
196 }
197 else if (!currA->isDigit()) {
198 return -1;
199 }
200 else if (!currB->isDigit()) {
201 return +1;
202 }
203 else if (*currA < *currB ) {
204 return -1;
205 }
206 else if (*currA > *currB) {
207 return +1;
208 }
209 ++currA;
210 ++currB;
211 }
212 }
213 else {
214 // No digit-sequence starts with 0 -> assume we are looking at some integer
215 // do right aligned comparison.
216 //
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.
220
221 int weight = 0;
222 while (1) {
223 if (!currA->isDigit() && !currB->isDigit()) {
224 if (weight != 0) {
225 return weight;
226 }
227 break;
228 }
229 else if (!currA->isDigit()) {
230 return -1;
231 }
232 else if (!currB->isDigit()) {
233 return +1;
234 }
235 else if ((*currA < *currB) && (weight == 0)) {
236 weight = -1;
237 }
238 else if ((*currA > *currB) && (weight == 0)) {
239 weight = +1;
240 }
241 ++currA;
242 ++currB;
243 }
244 }
245
246 begSeqA = currA;
247 begSeqB = currB;
248 }
249
250 if (currA->isNull() && currB->isNull()) {
251 return 0;
252 }
253
254 return currA->isNull() ? -1 : +1;
255 }
256
257 #include "dolphinsortfilterproxymodel.moc"