]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphinsortfilterproxymodel.cpp
5f6cb357511d41b8cfe96e6e9a42704ef1f89808
[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 = 7;
28 static int dolphinViewToDirModelColumn[] =
29 {
30 KDirModel::Name, // DolphinView::SortByName
31 KDirModel::Size, // DolphinView::SortBySize
32 KDirModel::ModifiedTime, // DolphinView::SortByDate
33 KDirModel::Permissions, // DolphinView::SortByPermissions
34 KDirModel::Owner, // DolphinView::SortByOwner
35 KDirModel::Group, // DolphinView::SortByGroup
36 KDirModel::Type // DolphinView::SortByType
37 };
38
39 static DolphinView::Sorting dirModelColumnToDolphinView[] =
40 {
41 DolphinView::SortByName, // KDirModel::Name
42 DolphinView::SortBySize, // KDirModel::Size
43 DolphinView::SortByDate, // KDirModel::ModifiedTime
44 DolphinView::SortByPermissions, // KDirModel::Permissions
45 DolphinView::SortByOwner, // KDirModel::Owner
46 DolphinView::SortByGroup, // KDirModel::Group
47 DolphinView::SortByType // KDirModel::Type
48 };
49
50
51 DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) :
52 QSortFilterProxyModel(parent),
53 m_sortColumn(0),
54 m_sorting(DolphinView::SortByName),
55 m_sortOrder(Qt::AscendingOrder)
56 {
57 setDynamicSortFilter(true);
58
59 // sort by the user visible string for now
60 setSortRole(Qt::DisplayRole);
61 setSortCaseSensitivity(Qt::CaseInsensitive);
62 sort(KDirModel::Name, Qt::AscendingOrder);
63 }
64
65 DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel()
66 {}
67
68 void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting)
69 {
70 // Update the sort column by mapping DolpginView::Sorting to
71 // KDirModel::ModelColumns. We will keep the sortOrder.
72 Q_ASSERT(static_cast<int>(sorting) >= 0 && static_cast<int>(sorting) < dolphinMapSize);
73 sort(dolphinViewToDirModelColumn[static_cast<int>(sorting)],
74 m_sortOrder);
75 }
76
77 void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder)
78 {
79 // change the sort order by keeping the current column
80 sort(dolphinViewToDirModelColumn[m_sorting], sortOrder);
81 }
82
83 void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder)
84 {
85 m_sortColumn = column;
86 m_sortOrder = sortOrder;
87 m_sorting = (column >= 0) && (column < dolphinMapSize) ?
88 dirModelColumnToDolphinView[column] :
89 DolphinView::SortByName;
90 QSortFilterProxyModel::sort(column, sortOrder);
91 }
92
93 bool DolphinSortFilterProxyModel::hasChildren(const QModelIndex& parent) const
94 {
95 const QModelIndex sourceParent = mapToSource(parent);
96 return sourceModel()->hasChildren(sourceParent);
97 }
98
99 bool DolphinSortFilterProxyModel::canFetchMore(const QModelIndex& parent) const
100 {
101 const QModelIndex sourceParent = mapToSource(parent);
102 return sourceModel()->canFetchMore(sourceParent);
103 }
104
105 DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column)
106 {
107 if ((column >= 0) && (column < dolphinMapSize)) {
108 return dirModelColumnToDolphinView[column];
109 }
110 return DolphinView::SortByName;
111 }
112
113 bool DolphinSortFilterProxyModel::lessThan(const QModelIndex& left,
114 const QModelIndex& right) const
115 {
116 KDirModel* dirModel = static_cast<KDirModel*>(sourceModel());
117
118 QVariant leftData = dirModel->data(left, sortRole());
119 QVariant rightData = dirModel->data(right, sortRole());
120
121 if ((leftData.type() == QVariant::String) && (rightData.type() == QVariant::String)) {
122 // assure that directories are always sorted before files
123 // if the sorting is done by the 'Name' column
124 if (m_sortColumn == KDirModel::Name) {
125 const bool leftIsDir = dirModel->itemForIndex(left)->isDir();
126 const bool rightIsDir = dirModel->itemForIndex(right)->isDir();
127 if (leftIsDir && !rightIsDir) {
128 return true;
129 }
130
131 if (!leftIsDir && rightIsDir) {
132 return false;
133 }
134 }
135
136 const QString leftStr = leftData.toString();
137 const QString rightStr = rightData.toString();
138
139 return sortCaseSensitivity() ? (naturalCompare(leftStr, rightStr) < 0) :
140 (naturalCompare(leftStr.toLower(), rightStr.toLower()) < 0);
141 }
142
143 // We have set a SortRole and trust the ProxyModel to do
144 // the right thing for now.
145 return QSortFilterProxyModel::lessThan(left, right);
146 }
147
148 int DolphinSortFilterProxyModel::naturalCompare(const QString& a,
149 const QString& b)
150 {
151 // This method chops the input a and b into pieces of
152 // digits and non-digits (a1.05 becomes a | 1 | . | 05)
153 // and compares these pieces of a and b to each other
154 // (first with first, second with second, ...).
155 //
156 // This is based on the natural sort order code code by Martin Pool
157 // http://sourcefrog.net/projects/natsort/
158 // Martin Pool agreed to license this under LGPL or GPL.
159
160 const QChar* currA = a.unicode(); // iterator over a
161 const QChar* currB = b.unicode(); // iterator over b
162
163 if (currA == currB) {
164 return 0;
165 }
166
167 const QChar* begSeqA = currA; // beginning of a new character sequence of a
168 const QChar* begSeqB = currB;
169
170 while (!currA->isNull() && !currB->isNull()) {
171 // find sequence of characters ending at the first non-character
172 while (!currA->isNull() && !currA->isDigit()) {
173 ++currA;
174 }
175
176 while (!currB->isNull() && !currB->isDigit()) {
177 ++currB;
178 }
179
180 // compare these sequences
181 const QString subA(begSeqA, currA - begSeqA);
182 const QString subB(begSeqB, currB - begSeqB);
183 const int cmp = QString::localeAwareCompare(subA, subB);
184 if (cmp != 0) {
185 return cmp;
186 }
187
188 if (currA->isNull() || currB->isNull()) {
189 break;
190 }
191
192 // now some digits follow...
193 if ((*currA == '0') || (*currB == '0')) {
194 // one digit-sequence starts with 0 -> assume we are in a fraction part
195 // do left aligned comparison (numbers are considered left aligned)
196 while (1) {
197 if (!currA->isDigit() && !currB->isDigit()) {
198 break;
199 } else if (!currA->isDigit()) {
200 return -1;
201 } else if (!currB->isDigit()) {
202 return + 1;
203 } else if (*currA < *currB) {
204 return -1;
205 } else if (*currA > *currB) {
206 return + 1;
207 }
208 ++currA;
209 ++currB;
210 }
211 } else {
212 // No digit-sequence starts with 0 -> assume we are looking at some integer
213 // do right aligned comparison.
214 //
215 // The longest run of digits wins. That aside, the greatest
216 // value wins, but we can't know that it will until we've scanned
217 // both numbers to know that they have the same magnitude.
218
219 int weight = 0;
220 while (1) {
221 if (!currA->isDigit() && !currB->isDigit()) {
222 if (weight != 0) {
223 return weight;
224 }
225 break;
226 } else if (!currA->isDigit()) {
227 return -1;
228 } else if (!currB->isDigit()) {
229 return + 1;
230 } else if ((*currA < *currB) && (weight == 0)) {
231 weight = -1;
232 } else if ((*currA > *currB) && (weight == 0)) {
233 weight = + 1;
234 }
235 ++currA;
236 ++currB;
237 }
238 }
239
240 begSeqA = currA;
241 begSeqB = currB;
242 }
243
244 if (currA->isNull() && currB->isNull()) {
245 return 0;
246 }
247
248 return currA->isNull() ? -1 : + 1;
249 }
250
251 #include "dolphinsortfilterproxymodel.moc"