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 | Separated from Dolphin by Nick Shaforostoff <shafff@ukr.net> |
7 | |
8 | This library is free software; you can redistribute it and/or |
9 | modify it under the terms of the GNU Library General Public |
10 | License version 2 as published by the Free Software Foundation. |
11 | |
12 | This library 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 GNU |
15 | Library General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU Library General Public License |
18 | along with this library; see the file COPYING.LIB. If not, write to |
19 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | Boston, MA 02110-1301, USA. |
21 | */ |
22 | |
23 | #include "kdirsortfilterproxymodel.h" |
24 | |
25 | #include <kdatetime.h> |
26 | #include <kdirmodel.h> |
27 | #include <kfileitem.h> |
28 | #include <kglobalsettings.h> |
29 | #include <klocale.h> |
30 | #include <kstringhandler.h> |
31 | |
32 | |
33 | class KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate |
34 | { |
35 | public: |
36 | KDirSortFilterProxyModelPrivate(KDirSortFilterProxyModel* q); |
37 | |
38 | int compare(const QString&, const QString&, Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive) const; |
39 | void slotNaturalSortingChanged(); |
40 | |
41 | bool m_sortFoldersFirst; |
42 | bool m_naturalSorting; |
43 | }; |
44 | |
45 | KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::KDirSortFilterProxyModelPrivate(KDirSortFilterProxyModel* q) : |
46 | m_sortFoldersFirst(true), |
47 | m_naturalSorting(KGlobalSettings::naturalSorting()) |
48 | { |
49 | connect(KGlobalSettings::self(), SIGNAL(naturalSortingChanged()), |
50 | q, SLOT(slotNaturalSortingChanged())); |
51 | } |
52 | |
53 | int KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::compare(const QString& a, |
54 | const QString& b, |
55 | Qt::CaseSensitivity caseSensitivity) const |
56 | { |
57 | if (caseSensitivity == Qt::CaseInsensitive) { |
58 | const int result = m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseInsensitive) |
59 | : QString::compare(a, b, Qt::CaseInsensitive); |
60 | if (result != 0) { |
61 | // Only return the result, if the strings are not equal. If they are equal by a case insensitive |
62 | // comparison, still a deterministic sort order is required. A case sensitive |
63 | // comparison is done as fallback. |
64 | return result; |
65 | } |
66 | } |
67 | |
68 | return m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseSensitive) |
69 | : QString::compare(a, b, Qt::CaseSensitive); |
70 | } |
71 | |
72 | |
73 | void KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::slotNaturalSortingChanged() |
74 | { |
75 | m_naturalSorting = KGlobalSettings::naturalSorting(); |
76 | } |
77 | |
78 | KDirSortFilterProxyModel::KDirSortFilterProxyModel(QObject* parent) |
79 | : KCategorizedSortFilterProxyModel(parent), d(new KDirSortFilterProxyModelPrivate(this)) |
80 | { |
81 | setDynamicSortFilter(true); |
82 | |
83 | // sort by the user visible string for now |
84 | setSortCaseSensitivity(Qt::CaseInsensitive); |
85 | sort(KDirModel::Name, Qt::AscendingOrder); |
86 | |
87 | setSupportedDragActions(Qt::CopyAction | Qt::MoveAction | Qt::LinkAction | Qt::IgnoreAction); |
88 | } |
89 | |
90 | KDirSortFilterProxyModel::~KDirSortFilterProxyModel() |
91 | { |
92 | delete d; |
93 | } |
94 | |
95 | bool KDirSortFilterProxyModel::hasChildren(const QModelIndex& parent) const |
96 | { |
97 | const QModelIndex sourceParent = mapToSource(parent); |
98 | return sourceModel()->hasChildren(sourceParent); |
99 | } |
100 | |
101 | bool KDirSortFilterProxyModel::canFetchMore(const QModelIndex& parent) const |
102 | { |
103 | const QModelIndex sourceParent = mapToSource(parent); |
104 | return sourceModel()->canFetchMore(sourceParent); |
105 | } |
106 | |
107 | int KDirSortFilterProxyModel::pointsForPermissions(const QFileInfo &info) |
108 | { |
109 | int points = 0; |
110 | |
111 | QFile::Permission permissionsCheck[] = { QFile::ReadUser, |
112 | QFile::WriteUser, |
113 | QFile::ExeUser, |
114 | QFile::ReadGroup, |
115 | QFile::WriteGroup, |
116 | QFile::ExeGroup, |
117 | QFile::ReadOther, |
118 | QFile::WriteOther, |
119 | QFile::ExeOther }; |
120 | |
121 | for (int i = 0; i < 9; i++) { |
122 | points += info.permission(permissionsCheck[i]) ? 1 : 0; |
123 | } |
124 | |
125 | return points; |
126 | } |
127 | |
128 | void KDirSortFilterProxyModel::setSortFoldersFirst(bool foldersFirst) |
129 | { |
130 | d->m_sortFoldersFirst = foldersFirst; |
131 | } |
132 | |
133 | bool KDirSortFilterProxyModel::sortFoldersFirst() const |
134 | { |
135 | return d->m_sortFoldersFirst; |
136 | } |
137 | |
138 | bool KDirSortFilterProxyModel::subSortLessThan(const QModelIndex& left, |
139 | const QModelIndex& right) const |
140 | { |
141 | KDirModel* dirModel = static_cast<KDirModel*>(sourceModel()); |
142 | |
143 | const KFileItem leftFileItem = dirModel->itemForIndex(left); |
144 | const KFileItem rightFileItem = dirModel->itemForIndex(right); |
145 | |
146 | const bool isLessThan = (sortOrder() == Qt::AscendingOrder); |
147 | |
148 | // Folders go before files if the corresponding setting is set. |
149 | if (d->m_sortFoldersFirst) { |
150 | const bool leftItemIsDir = leftFileItem.isDir(); |
151 | const bool rightItemIsDir = rightFileItem.isDir(); |
152 | if (leftItemIsDir && !rightItemIsDir) { |
153 | return isLessThan; |
154 | } else if (!leftItemIsDir && rightItemIsDir) { |
155 | return !isLessThan; |
156 | } |
157 | } |
158 | |
159 | |
160 | // Hidden elements go before visible ones. |
161 | const bool leftItemIsHidden = leftFileItem.isHidden(); |
162 | const bool rightItemIsHidden = rightFileItem.isHidden(); |
163 | if (leftItemIsHidden && !rightItemIsHidden) { |
164 | return isLessThan; |
165 | } else if (!leftItemIsHidden && rightItemIsHidden) { |
166 | return !isLessThan; |
167 | } |
168 | |
169 | switch (left.column()) { |
170 | case KDirModel::Name: { |
171 | int result = d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()); |
172 | if (result == 0) { |
173 | // KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used |
174 | result = d->compare(leftFileItem.name(sortCaseSensitivity() == Qt::CaseInsensitive), |
175 | rightFileItem.name(sortCaseSensitivity() == Qt::CaseInsensitive), |
176 | sortCaseSensitivity()); |
177 | if (result == 0) { |
178 | // If KFileItem::text() is also not unique most probably a search protocol is used |
179 | // that allows showing the same file names from different directories |
180 | result = d->compare(leftFileItem.url().url(), rightFileItem.url().url(), sortCaseSensitivity()); |
181 | } |
182 | } |
183 | |
184 | return result < 0; |
185 | } |
186 | |
187 | case KDirModel::Size: { |
188 | // If we have two folders, what we have to measure is the number of |
189 | // items that contains each other |
190 | if (leftFileItem.isDir() && rightFileItem.isDir()) { |
191 | QVariant leftValue = dirModel->data(left, KDirModel::ChildCountRole); |
192 | int leftCount = (leftValue.type() == QVariant::Int) ? leftValue.toInt() : KDirModel::ChildCountUnknown; |
193 | |
194 | QVariant rightValue = dirModel->data(right, KDirModel::ChildCountRole); |
195 | int rightCount = (rightValue.type() == QVariant::Int) ? rightValue.toInt() : KDirModel::ChildCountUnknown; |
196 | |
197 | // In the case they two have the same child items, we sort them by |
198 | // their names. So we have always everything ordered. We also check |
199 | // if we are taking in count their cases. |
200 | if (leftCount == rightCount) { |
201 | return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; |
202 | } |
203 | |
204 | // If one of them has unknown child items, place them on the end. If we |
205 | // were comparing two unknown childed items, the previous comparation |
206 | // sorted them by naturalCompare between them. This case is when we |
207 | // have an unknown childed item, and another known. |
208 | if (leftCount == KDirModel::ChildCountUnknown) { |
209 | return false; |
210 | } |
211 | |
212 | if (rightCount == KDirModel::ChildCountUnknown) { |
213 | return true; |
214 | } |
215 | |
216 | // If they had different number of items, we sort them depending |
217 | // on how many items had each other. |
218 | return leftCount < rightCount; |
219 | } |
220 | |
221 | // If what we are measuring is two files and they have the same size, |
222 | // sort them by their file names. |
223 | if (leftFileItem.size() == rightFileItem.size()) { |
224 | return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; |
225 | } |
226 | |
227 | // If their sizes are different, sort them by their sizes, as expected. |
228 | return leftFileItem.size() < rightFileItem.size(); |
229 | } |
230 | |
231 | case KDirModel::ModifiedTime: { |
232 | KDateTime leftModifiedTime = leftFileItem.time(KFileItem::ModificationTime).toLocalZone(); |
233 | KDateTime rightModifiedTime = rightFileItem.time(KFileItem::ModificationTime).toLocalZone(); |
234 | |
235 | if (leftModifiedTime == rightModifiedTime) { |
236 | return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; |
237 | } |
238 | |
239 | return leftModifiedTime < rightModifiedTime; |
240 | } |
241 | |
242 | case KDirModel::Permissions: { |
243 | // ### You can't use QFileInfo on urls!! Use the KFileItem instead. |
244 | QFileInfo leftFileInfo(leftFileItem.url().pathOrUrl()); |
245 | QFileInfo rightFileInfo(rightFileItem.url().pathOrUrl()); |
246 | |
247 | int leftPermissionsPoints = pointsForPermissions(leftFileInfo); |
248 | int rightPermissionsPoints = pointsForPermissions(rightFileInfo); |
249 | |
250 | if (leftPermissionsPoints == rightPermissionsPoints) { |
251 | return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; |
252 | } |
253 | |
254 | return leftPermissionsPoints > rightPermissionsPoints; |
255 | } |
256 | |
257 | case KDirModel::Owner: { |
258 | if (leftFileItem.user() == rightFileItem.user()) { |
259 | return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; |
260 | } |
261 | |
262 | return d->compare(leftFileItem.user(), rightFileItem.user()) < 0; |
263 | } |
264 | |
265 | case KDirModel::Group: { |
266 | if (leftFileItem.group() == rightFileItem.group()) { |
267 | return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; |
268 | } |
269 | |
270 | return d->compare(leftFileItem.group(), rightFileItem.group()) < 0; |
271 | } |
272 | |
273 | case KDirModel::Type: { |
274 | if (leftFileItem.mimetype() == rightFileItem.mimetype()) { |
275 | return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; |
276 | } |
277 | |
278 | return d->compare(leftFileItem.mimeComment(), rightFileItem.mimeComment()) < 0; |
279 | } |
280 | |
281 | } |
282 | |
283 | // We have set a SortRole and trust the ProxyModel to do |
284 | // the right thing for now. |
285 | return KCategorizedSortFilterProxyModel::subSortLessThan(left, right); |
286 | } |
287 | |
288 | #include "kdirsortfilterproxymodel.moc" |
289 | |