KIO

kdirsortfilterproxymodel.cpp
1/*
2 SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
3 SPDX-FileCopyrightText: 2006 Dominic Battre <dominic@battre.de>
4 SPDX-FileCopyrightText: 2006 Martin Pool <mbp@canonical.com>
5
6 Separated from Dolphin by Nick Shaforostoff <shafff@ukr.net>
7
8 SPDX-License-Identifier: LGPL-2.0-only
9*/
10
11#include "kdirsortfilterproxymodel.h"
12
13#include "defaults-kfile.h"
14
15#include <KConfigGroup>
16#include <KLocalizedString>
17#include <KSharedConfig>
18#include <kdirmodel.h>
19#include <kfileitem.h>
20
21#include <QCollator>
22
23class Q_DECL_HIDDEN KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate
24{
25public:
26 KDirSortFilterProxyModelPrivate();
27
28 int compare(const QString &, const QString &, Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive);
29 void slotNaturalSortingChanged();
30
31 bool m_sortFoldersFirst;
32 bool m_sortHiddenFilesLast;
33 bool m_naturalSorting;
34 QCollator m_collator;
35};
36
37KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::KDirSortFilterProxyModelPrivate()
38 : m_sortFoldersFirst(true)
39 , m_sortHiddenFilesLast(DefaultHiddenFilesLast)
40{
41 slotNaturalSortingChanged();
42}
43
44int KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::compare(const QString &a, const QString &b, Qt::CaseSensitivity caseSensitivity)
45{
46 int result;
47
48 if (m_naturalSorting) {
49 m_collator.setCaseSensitivity(caseSensitivity);
50 result = m_collator.compare(a, b);
51 } else {
52 result = QString::compare(a, b, caseSensitivity);
53 }
54
55 if (caseSensitivity == Qt::CaseSensitive || result != 0) {
56 // Only return the result, if the strings are not equal. If they are equal by a case insensitive
57 // comparison, still a deterministic sort order is required. A case sensitive
58 // comparison is done as fallback.
59 return result;
60 }
61
63}
64
65void KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::slotNaturalSortingChanged()
66{
67 KConfigGroup g(KSharedConfig::openConfig(), QStringLiteral("KDE"));
68 m_naturalSorting = g.readEntry("NaturalSorting", true);
69 m_collator.setNumericMode(m_naturalSorting);
70}
71
72KDirSortFilterProxyModel::KDirSortFilterProxyModel(QObject *parent)
74 , d(new KDirSortFilterProxyModelPrivate)
75{
76 setDynamicSortFilter(true);
77
78 // sort by the user visible string for now
79 setSortCaseSensitivity(Qt::CaseInsensitive);
80 sort(KDirModel::Name, Qt::AscendingOrder);
81}
82
83Qt::DropActions KDirSortFilterProxyModel::supportedDragOptions() const
84{
86}
87
88KDirSortFilterProxyModel::~KDirSortFilterProxyModel() = default;
89
91{
92 const QModelIndex sourceParent = mapToSource(parent);
93 return sourceModel()->hasChildren(sourceParent);
94}
95
97{
98 const QModelIndex sourceParent = mapToSource(parent);
99 return sourceModel()->canFetchMore(sourceParent);
100}
101
103{
104 int points = 0;
105
106 const QFile::Permission permissionsCheck[] = {QFile::ReadUser,
115
116 for (QFile::Permission perm : permissionsCheck) {
117 points += info.permission(perm) ? 1 : 0;
118 }
119
120 return points;
121}
122
124{
125 if (d->m_sortFoldersFirst == foldersFirst) {
126 return;
127 }
128
129 d->m_sortFoldersFirst = foldersFirst;
130 invalidate();
131}
132
134{
135 return d->m_sortFoldersFirst;
136}
137
139{
140 if (d->m_sortHiddenFilesLast == hiddenFilesLast) {
141 return;
142 }
143
144 d->m_sortHiddenFilesLast = hiddenFilesLast;
145 invalidate();
146}
147
148bool KDirSortFilterProxyModel::sortHiddenFilesLast() const
149{
150 return d->m_sortHiddenFilesLast;
151}
152
154{
155 KDirModel *dirModel = static_cast<KDirModel *>(sourceModel());
156
157 const KFileItem leftFileItem = dirModel->itemForIndex(left);
158 const KFileItem rightFileItem = dirModel->itemForIndex(right);
159
160 const bool isLessThan = (sortOrder() == Qt::AscendingOrder);
161
162 // Show hidden files and folders last
163 if (d->m_sortHiddenFilesLast) {
164 const bool leftItemIsHidden = leftFileItem.isHidden();
165 const bool rightItemIsHidden = rightFileItem.isHidden();
166 if (leftItemIsHidden && !rightItemIsHidden) {
167 return !isLessThan;
168 } else if (!leftItemIsHidden && rightItemIsHidden) {
169 return isLessThan;
170 }
171 }
172
173 // Folders go before files if the corresponding setting is set.
174 if (d->m_sortFoldersFirst) {
175 const bool leftItemIsDir = leftFileItem.isDir();
176 const bool rightItemIsDir = rightFileItem.isDir();
177 if (leftItemIsDir && !rightItemIsDir) {
178 return isLessThan;
179 } else if (!leftItemIsDir && rightItemIsDir) {
180 return !isLessThan;
181 }
182 }
183
184 switch (left.column()) {
185 case KDirModel::Name: {
186 int result = d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity());
187 if (result == 0) {
188 // KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used
189 result = d->compare(leftFileItem.name(sortCaseSensitivity() == Qt::CaseInsensitive),
190 rightFileItem.name(sortCaseSensitivity() == Qt::CaseInsensitive),
192 if (result == 0) {
193 // If KFileItem::text() is also not unique most probably a search protocol is used
194 // that allows showing the same file names from different directories
195 result = d->compare(leftFileItem.url().toString(), rightFileItem.url().toString(), sortCaseSensitivity());
196 }
197 }
198
199 return result < 0;
200 }
201
202 case KDirModel::Size: {
203 // If we have two folders, what we have to measure is the number of
204 // items that contains each other
205 if (leftFileItem.isDir() && rightFileItem.isDir()) {
206 QVariant leftValue = dirModel->data(left, KDirModel::ChildCountRole);
207 int leftCount = (leftValue.typeId() == QMetaType::Int) ? leftValue.toInt() : KDirModel::ChildCountUnknown;
208
209 QVariant rightValue = dirModel->data(right, KDirModel::ChildCountRole);
210 int rightCount = (rightValue.typeId() == QMetaType::Int) ? rightValue.toInt() : KDirModel::ChildCountUnknown;
211
212 // In the case they two have the same child items, we sort them by
213 // their names. So we have always everything ordered. We also check
214 // if we are taking in count their cases.
215 if (leftCount == rightCount) {
216 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
217 }
218
219 // If one of them has unknown child items, place them on the end. If we
220 // were comparing two unknown childed items, the previous comparison
221 // sorted them by QCollator between them. This case is when we
222 // have an unknown childed item, and another known.
223 if (leftCount == KDirModel::ChildCountUnknown) {
224 return false;
225 }
226
227 if (rightCount == KDirModel::ChildCountUnknown) {
228 return true;
229 }
230
231 // If they had different number of items, we sort them depending
232 // on how many items had each other.
233 return leftCount < rightCount;
234 }
235
236 // If what we are measuring is two files and they have the same size,
237 // sort them by their file names.
238 if (leftFileItem.size() == rightFileItem.size()) {
239 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
240 }
241
242 // If their sizes are different, sort them by their sizes, as expected.
243 return leftFileItem.size() < rightFileItem.size();
244 }
245
246 case KDirModel::ModifiedTime: {
247 QDateTime leftModifiedTime = leftFileItem.time(KFileItem::ModificationTime).toLocalTime();
248 QDateTime rightModifiedTime = rightFileItem.time(KFileItem::ModificationTime).toLocalTime();
249
250 if (leftModifiedTime == rightModifiedTime) {
251 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
252 }
253
254 return leftModifiedTime < rightModifiedTime;
255 }
256
257 case KDirModel::Permissions: {
258 const int leftPermissions = leftFileItem.permissions();
259 const int rightPermissions = rightFileItem.permissions();
260
261 if (leftPermissions == rightPermissions) {
262 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
263 }
264
265 return leftPermissions > rightPermissions;
266 }
267
268 case KDirModel::Owner: {
269 if (leftFileItem.user() == rightFileItem.user()) {
270 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
271 }
272
273 return d->compare(leftFileItem.user(), rightFileItem.user()) < 0;
274 }
275
276 case KDirModel::Group: {
277 if (leftFileItem.group() == rightFileItem.group()) {
278 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
279 }
280
281 return d->compare(leftFileItem.group(), rightFileItem.group()) < 0;
282 }
283
284 case KDirModel::Type: {
285 if (leftFileItem.mimetype() == rightFileItem.mimetype()) {
286 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
287 }
288
289 return d->compare(leftFileItem.mimeComment(), rightFileItem.mimeComment()) < 0;
290 }
291 }
292
293 // We have set a SortRole and trust the ProxyModel to do
294 // the right thing for now.
296}
297
298#include "moc_kdirsortfilterproxymodel.cpp"
virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const
Qt::SortOrder sortOrder() const
A model for a KIO-based directory tree.
Definition kdirmodel.h:42
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Reimplemented from QAbstractItemModel.
@ ChildCountRole
returns the number of items in a directory, or ChildCountUnknown. roleName is "childCount".
Definition kdirmodel.h:159
KFileItem itemForIndex(const QModelIndex &index) const
Return the fileitem for a given index.
Acts as proxy model for KDirModel to sort and filter KFileItems.
static int pointsForPermissions(const QFileInfo &info)
Returns the permissions in "points".
bool hasChildren(const QModelIndex &parent=QModelIndex()) const override
Reimplemented from QAbstractItemModel.
virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const override
Reimplemented from KCategorizedSortFilterProxyModel.
void setSortHiddenFilesLast(bool hiddenFilesLast)
Sets a separate sorting with hidden files and folders last (true) or not (false).
bool sortFoldersFirst() const
Returns if files and folders are sorted separately (with folders first) or not.
bool canFetchMore(const QModelIndex &parent) const override
Reimplemented from QAbstractItemModel.
void setSortFoldersFirst(bool foldersFirst)
Choose if files and folders are sorted separately (with folders first) or not.
A KFileItem is a generic class to handle a file, local or remote.
Definition kfileitem.h:36
KIO::filesize_t size() const
Returns the size of the file, if known.
Q_INVOKABLE QDateTime time(KFileItem::FileTimes which) const
Requests the modification, access or creation time, depending on which.
mode_t permissions() const
Returns the permissions of the file (stat.st_mode containing only permissions).
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
int compare(QStringView s1, QStringView s2) const const
void setCaseSensitivity(Qt::CaseSensitivity cs)
QDateTime toLocalTime() const const
bool permission(QFile::Permissions permissions) const const
QObject * parent() const const
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const override
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
CaseSensitivity
typedef DropActions
AscendingOrder
QString toString(FormattingOptions options) const const
int toInt(bool *ok) const const
int typeId() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:52 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.