File indexing completed on 2024-12-01 03:40:50
0001 /* 0002 SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at> 0003 SPDX-FileCopyrightText: 2006 Dominic Battre <dominic@battre.de> 0004 SPDX-FileCopyrightText: 2006 Martin Pool <mbp@canonical.com> 0005 0006 Separated from Dolphin by Nick Shaforostoff <shafff@ukr.net> 0007 0008 SPDX-License-Identifier: LGPL-2.0-only 0009 */ 0010 0011 #include "kdirsortfilterproxymodel.h" 0012 0013 #include "defaults-kfile.h" 0014 0015 #include <KConfigGroup> 0016 #include <KLocalizedString> 0017 #include <KSharedConfig> 0018 #include <kdirmodel.h> 0019 #include <kfileitem.h> 0020 0021 #include <QCollator> 0022 0023 class Q_DECL_HIDDEN KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate 0024 { 0025 public: 0026 KDirSortFilterProxyModelPrivate(); 0027 0028 int compare(const QString &, const QString &, Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive); 0029 void slotNaturalSortingChanged(); 0030 0031 bool m_sortFoldersFirst; 0032 bool m_sortHiddenFilesLast; 0033 bool m_naturalSorting; 0034 QCollator m_collator; 0035 }; 0036 0037 KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::KDirSortFilterProxyModelPrivate() 0038 : m_sortFoldersFirst(true) 0039 , m_sortHiddenFilesLast(DefaultHiddenFilesLast) 0040 { 0041 slotNaturalSortingChanged(); 0042 } 0043 0044 int KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::compare(const QString &a, const QString &b, Qt::CaseSensitivity caseSensitivity) 0045 { 0046 int result; 0047 0048 if (m_naturalSorting) { 0049 m_collator.setCaseSensitivity(caseSensitivity); 0050 result = m_collator.compare(a, b); 0051 } else { 0052 result = QString::compare(a, b, caseSensitivity); 0053 } 0054 0055 if (caseSensitivity == Qt::CaseSensitive || result != 0) { 0056 // Only return the result, if the strings are not equal. If they are equal by a case insensitive 0057 // comparison, still a deterministic sort order is required. A case sensitive 0058 // comparison is done as fallback. 0059 return result; 0060 } 0061 0062 return QString::compare(a, b, Qt::CaseSensitive); 0063 } 0064 0065 void KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::slotNaturalSortingChanged() 0066 { 0067 KConfigGroup g(KSharedConfig::openConfig(), QStringLiteral("KDE")); 0068 m_naturalSorting = g.readEntry("NaturalSorting", true); 0069 m_collator.setNumericMode(m_naturalSorting); 0070 } 0071 0072 KDirSortFilterProxyModel::KDirSortFilterProxyModel(QObject *parent) 0073 : KCategorizedSortFilterProxyModel(parent) 0074 , d(new KDirSortFilterProxyModelPrivate) 0075 { 0076 setDynamicSortFilter(true); 0077 0078 // sort by the user visible string for now 0079 setSortCaseSensitivity(Qt::CaseInsensitive); 0080 sort(KDirModel::Name, Qt::AscendingOrder); 0081 } 0082 0083 Qt::DropActions KDirSortFilterProxyModel::supportedDragOptions() const 0084 { 0085 return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction | Qt::IgnoreAction; 0086 } 0087 0088 KDirSortFilterProxyModel::~KDirSortFilterProxyModel() = default; 0089 0090 bool KDirSortFilterProxyModel::hasChildren(const QModelIndex &parent) const 0091 { 0092 const QModelIndex sourceParent = mapToSource(parent); 0093 return sourceModel()->hasChildren(sourceParent); 0094 } 0095 0096 bool KDirSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const 0097 { 0098 const QModelIndex sourceParent = mapToSource(parent); 0099 return sourceModel()->canFetchMore(sourceParent); 0100 } 0101 0102 int KDirSortFilterProxyModel::pointsForPermissions(const QFileInfo &info) 0103 { 0104 int points = 0; 0105 0106 const QFile::Permission permissionsCheck[] = {QFile::ReadUser, 0107 QFile::WriteUser, 0108 QFile::ExeUser, 0109 QFile::ReadGroup, 0110 QFile::WriteGroup, 0111 QFile::ExeGroup, 0112 QFile::ReadOther, 0113 QFile::WriteOther, 0114 QFile::ExeOther}; 0115 0116 for (QFile::Permission perm : permissionsCheck) { 0117 points += info.permission(perm) ? 1 : 0; 0118 } 0119 0120 return points; 0121 } 0122 0123 void KDirSortFilterProxyModel::setSortFoldersFirst(bool foldersFirst) 0124 { 0125 if (d->m_sortFoldersFirst == foldersFirst) { 0126 return; 0127 } 0128 0129 d->m_sortFoldersFirst = foldersFirst; 0130 invalidate(); 0131 } 0132 0133 bool KDirSortFilterProxyModel::sortFoldersFirst() const 0134 { 0135 return d->m_sortFoldersFirst; 0136 } 0137 0138 void KDirSortFilterProxyModel::setSortHiddenFilesLast(bool hiddenFilesLast) 0139 { 0140 if (d->m_sortHiddenFilesLast == hiddenFilesLast) { 0141 return; 0142 } 0143 0144 d->m_sortHiddenFilesLast = hiddenFilesLast; 0145 invalidate(); 0146 } 0147 0148 bool KDirSortFilterProxyModel::sortHiddenFilesLast() const 0149 { 0150 return d->m_sortHiddenFilesLast; 0151 } 0152 0153 bool KDirSortFilterProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const 0154 { 0155 KDirModel *dirModel = static_cast<KDirModel *>(sourceModel()); 0156 0157 const KFileItem leftFileItem = dirModel->itemForIndex(left); 0158 const KFileItem rightFileItem = dirModel->itemForIndex(right); 0159 0160 const bool isLessThan = (sortOrder() == Qt::AscendingOrder); 0161 0162 // Show hidden files and folders last 0163 if (d->m_sortHiddenFilesLast) { 0164 const bool leftItemIsHidden = leftFileItem.isHidden(); 0165 const bool rightItemIsHidden = rightFileItem.isHidden(); 0166 if (leftItemIsHidden && !rightItemIsHidden) { 0167 return !isLessThan; 0168 } else if (!leftItemIsHidden && rightItemIsHidden) { 0169 return isLessThan; 0170 } 0171 } 0172 0173 // Folders go before files if the corresponding setting is set. 0174 if (d->m_sortFoldersFirst) { 0175 const bool leftItemIsDir = leftFileItem.isDir(); 0176 const bool rightItemIsDir = rightFileItem.isDir(); 0177 if (leftItemIsDir && !rightItemIsDir) { 0178 return isLessThan; 0179 } else if (!leftItemIsDir && rightItemIsDir) { 0180 return !isLessThan; 0181 } 0182 } 0183 0184 switch (left.column()) { 0185 case KDirModel::Name: { 0186 int result = d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()); 0187 if (result == 0) { 0188 // KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used 0189 result = d->compare(leftFileItem.name(sortCaseSensitivity() == Qt::CaseInsensitive), 0190 rightFileItem.name(sortCaseSensitivity() == Qt::CaseInsensitive), 0191 sortCaseSensitivity()); 0192 if (result == 0) { 0193 // If KFileItem::text() is also not unique most probably a search protocol is used 0194 // that allows showing the same file names from different directories 0195 result = d->compare(leftFileItem.url().toString(), rightFileItem.url().toString(), sortCaseSensitivity()); 0196 } 0197 } 0198 0199 return result < 0; 0200 } 0201 0202 case KDirModel::Size: { 0203 // If we have two folders, what we have to measure is the number of 0204 // items that contains each other 0205 if (leftFileItem.isDir() && rightFileItem.isDir()) { 0206 QVariant leftValue = dirModel->data(left, KDirModel::ChildCountRole); 0207 int leftCount = (leftValue.typeId() == QMetaType::Int) ? leftValue.toInt() : KDirModel::ChildCountUnknown; 0208 0209 QVariant rightValue = dirModel->data(right, KDirModel::ChildCountRole); 0210 int rightCount = (rightValue.typeId() == QMetaType::Int) ? rightValue.toInt() : KDirModel::ChildCountUnknown; 0211 0212 // In the case they two have the same child items, we sort them by 0213 // their names. So we have always everything ordered. We also check 0214 // if we are taking in count their cases. 0215 if (leftCount == rightCount) { 0216 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; 0217 } 0218 0219 // If one of them has unknown child items, place them on the end. If we 0220 // were comparing two unknown childed items, the previous comparison 0221 // sorted them by QCollator between them. This case is when we 0222 // have an unknown childed item, and another known. 0223 if (leftCount == KDirModel::ChildCountUnknown) { 0224 return false; 0225 } 0226 0227 if (rightCount == KDirModel::ChildCountUnknown) { 0228 return true; 0229 } 0230 0231 // If they had different number of items, we sort them depending 0232 // on how many items had each other. 0233 return leftCount < rightCount; 0234 } 0235 0236 // If what we are measuring is two files and they have the same size, 0237 // sort them by their file names. 0238 if (leftFileItem.size() == rightFileItem.size()) { 0239 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; 0240 } 0241 0242 // If their sizes are different, sort them by their sizes, as expected. 0243 return leftFileItem.size() < rightFileItem.size(); 0244 } 0245 0246 case KDirModel::ModifiedTime: { 0247 QDateTime leftModifiedTime = leftFileItem.time(KFileItem::ModificationTime).toLocalTime(); 0248 QDateTime rightModifiedTime = rightFileItem.time(KFileItem::ModificationTime).toLocalTime(); 0249 0250 if (leftModifiedTime == rightModifiedTime) { 0251 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; 0252 } 0253 0254 return leftModifiedTime < rightModifiedTime; 0255 } 0256 0257 case KDirModel::Permissions: { 0258 const int leftPermissions = leftFileItem.permissions(); 0259 const int rightPermissions = rightFileItem.permissions(); 0260 0261 if (leftPermissions == rightPermissions) { 0262 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; 0263 } 0264 0265 return leftPermissions > rightPermissions; 0266 } 0267 0268 case KDirModel::Owner: { 0269 if (leftFileItem.user() == rightFileItem.user()) { 0270 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; 0271 } 0272 0273 return d->compare(leftFileItem.user(), rightFileItem.user()) < 0; 0274 } 0275 0276 case KDirModel::Group: { 0277 if (leftFileItem.group() == rightFileItem.group()) { 0278 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; 0279 } 0280 0281 return d->compare(leftFileItem.group(), rightFileItem.group()) < 0; 0282 } 0283 0284 case KDirModel::Type: { 0285 if (leftFileItem.mimetype() == rightFileItem.mimetype()) { 0286 return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0; 0287 } 0288 0289 return d->compare(leftFileItem.mimeComment(), rightFileItem.mimeComment()) < 0; 0290 } 0291 } 0292 0293 // We have set a SortRole and trust the ProxyModel to do 0294 // the right thing for now. 0295 return KCategorizedSortFilterProxyModel::subSortLessThan(left, right); 0296 } 0297 0298 #include "moc_kdirsortfilterproxymodel.cpp"