X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/85d7a8a2f60ffac614cc96120181cd8f1c4269b0..b65576a131eb59eaab4d33af830bdb2d2f9fde9f:/src/kitemviews/kfileitemmodel.cpp diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 9db3a2e5a..ac3c33e22 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -22,6 +22,7 @@ #include "kfileitemmodel.h" #include "dolphin_generalsettings.h" +#include "dolphin_detailsmodesettings.h" #include "dolphindebug.h" #include "private/kfileitemmodeldirlister.h" #include "private/kfileitemmodelsortalgorithm.h" @@ -33,6 +34,9 @@ #include #include #include +#include + +Q_GLOBAL_STATIC_WITH_ARGS(QMutex, s_collatorMutex, (QMutex::Recursive)) // #define KFILEITEMMODEL_DEBUG @@ -68,16 +72,16 @@ KFileItemModel::KFileItemModel(QObject* parent) : } connect(m_dirLister, &KFileItemModelDirLister::started, this, &KFileItemModel::directoryLoadingStarted); - connect(m_dirLister, static_cast(&KFileItemModelDirLister::canceled), this, &KFileItemModel::slotCanceled); - connect(m_dirLister, static_cast(&KFileItemModelDirLister::completed), this, &KFileItemModel::slotCompleted); + connect(m_dirLister, QOverload<>::of(&KCoreDirLister::canceled), this, &KFileItemModel::slotCanceled); + connect(m_dirLister, QOverload::of(&KCoreDirLister::completed), this, &KFileItemModel::slotCompleted); connect(m_dirLister, &KFileItemModelDirLister::itemsAdded, this, &KFileItemModel::slotItemsAdded); connect(m_dirLister, &KFileItemModelDirLister::itemsDeleted, this, &KFileItemModel::slotItemsDeleted); connect(m_dirLister, &KFileItemModelDirLister::refreshItems, this, &KFileItemModel::slotRefreshItems); - connect(m_dirLister, static_cast(&KFileItemModelDirLister::clear), this, &KFileItemModel::slotClear); + connect(m_dirLister, QOverload<>::of(&KCoreDirLister::clear), this, &KFileItemModel::slotClear); connect(m_dirLister, &KFileItemModelDirLister::infoMessage, this, &KFileItemModel::infoMessage); connect(m_dirLister, &KFileItemModelDirLister::errorMessage, this, &KFileItemModel::errorMessage); connect(m_dirLister, &KFileItemModelDirLister::percent, this, &KFileItemModel::directoryLoadingProgress); - connect(m_dirLister, static_cast(&KFileItemModelDirLister::redirection), this, &KFileItemModel::directoryRedirection); + connect(m_dirLister, QOverload::of(&KCoreDirLister::redirection), this, &KFileItemModel::directoryRedirection); connect(m_dirLister, &KFileItemModelDirLister::urlIsFileError, this, &KFileItemModel::urlIsFileError); // Apply default roles that should be determined @@ -267,7 +271,7 @@ QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const urls << item.url(); bool isLocal; - mostLocalUrls << item.mostLocalUrl(isLocal); + mostLocalUrls << item.mostLocalUrl(&isLocal); } } @@ -304,6 +308,9 @@ QString KFileItemModel::roleDescription(const QByteArray& role) const int count = 0; const RoleInfoMap* map = rolesInfoMap(count); for (int i = 0; i < count; ++i) { + if (!map[i].roleTranslation) { + continue; + } description.insert(map[i].role, i18nc(map[i].roleTranslationContext, map[i].roleTranslation)); } } @@ -782,13 +789,13 @@ QList KFileItemModel::rolesInformation() void KFileItemModel::onGroupedSortingChanged(bool current) { - Q_UNUSED(current); + Q_UNUSED(current) m_groups.clear(); } -void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArray& previous) +void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArray& previous, bool resortItems) { - Q_UNUSED(previous); + Q_UNUSED(previous) m_sortRole = typeForRole(current); if (!m_requestRole[m_sortRole]) { @@ -797,13 +804,15 @@ void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArr setRoles(newRoles); } - resortAllItems(); + if (resortItems) { + resortAllItems(); + } } void KFileItemModel::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) resortAllItems(); } @@ -826,6 +835,9 @@ void KFileItemModel::loadSortingSettings() default: Q_UNREACHABLE(); } + // Workaround for bug https://bugreports.qt.io/browse/QTBUG-69361 + // Force the clean state of QCollator in single thread to avoid thread safety problems in sort + m_collator.compare(QString(), QString()); } void KFileItemModel::resortAllItems() @@ -909,6 +921,7 @@ void KFileItemModel::resortAllItems() void KFileItemModel::slotCompleted() { + m_maximumUpdateIntervalTimer->stop(); dispatchPendingItemsToInsert(); if (!m_urlsToExpand.isEmpty()) { @@ -999,7 +1012,7 @@ void KFileItemModel::slotItemsAdded(const QUrl &directoryUrl, const KFileItemLis } } - if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) { + if (!m_maximumUpdateIntervalTimer->isActive()) { // Assure that items get dispatched if no completed() or canceled() signal is // emitted during the maximum update interval. m_maximumUpdateIntervalTimer->start(); @@ -1116,7 +1129,7 @@ void KFileItemModel::slotRefreshItems(const QList >& } // Extract the item-ranges out of the changed indexes - qSort(indexes); + std::sort(indexes.begin(), indexes.end()); const KItemRangeList itemRangeList = KItemRangeList::fromSortedContainer(indexes); emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles); } @@ -1178,12 +1191,20 @@ void KFileItemModel::insertItems(QList& newItems) m_groups.clear(); prepareItemsForSorting(newItems); - if (m_sortRole == NameRole && m_naturalSorting) { - // Natural sorting of items can be very slow. However, it becomes much - // faster if the input sequence is already mostly sorted. Therefore, we - // first sort 'newItems' according to the QStrings returned by - // KFileItem::text() using QString::operator<(), which is quite fast. - parallelMergeSort(newItems.begin(), newItems.end(), nameLessThan, QThread::idealThreadCount()); + // Natural sorting of items can be very slow. However, it becomes much faster + // if the input sequence is already mostly sorted. Therefore, we first sort + // 'newItems' according to the QStrings using QString::operator<(), which is quite fast. + if (m_naturalSorting) { + if (m_sortRole == NameRole) { + parallelMergeSort(newItems.begin(), newItems.end(), nameLessThan, QThread::idealThreadCount()); + } else if (isRoleValueNatural(m_sortRole)) { + auto lambdaLessThan = [&] (const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) + { + const QByteArray role = roleForType(m_sortRole); + return a->values.value(role).toString() < b->values.value(role).toString(); + }; + parallelMergeSort(newItems.begin(), newItems.end(), lambdaLessThan, QThread::idealThreadCount()); + } } sort(newItems.begin(), newItems.end()); @@ -1547,8 +1568,8 @@ QHash KFileItemModel::retrieveData(const KFileItem& item, data.insert(sharedValue("isLink"), true); } - if (m_requestRole[IsHiddenRole] && item.isHidden()) { - data.insert(sharedValue("isHidden"), true); + if (m_requestRole[IsHiddenRole]) { + data.insert(sharedValue("isHidden"), item.isHidden()); } if (m_requestRole[NameRole]) { @@ -1560,26 +1581,26 @@ QHash KFileItemModel::retrieveData(const KFileItem& item, } if (m_requestRole[ModificationTimeRole]) { - // Don't use KFileItem::timeString() as this is too expensive when - // having several thousands of items. Instead the formatting of the - // date-time will be done on-demand by the view when the date will be shown. - const QDateTime dateTime = item.time(KFileItem::ModificationTime); + // Don't use KFileItem::timeString() or KFileItem::time() as this is too expensive when + // having several thousands of items. Instead read the raw number from UDSEntry directly + // and the formatting of the date-time will be done on-demand by the view when the date will be shown. + const long long dateTime = item.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); data.insert(sharedValue("modificationtime"), dateTime); } if (m_requestRole[CreationTimeRole]) { - // Don't use KFileItem::timeString() as this is too expensive when - // having several thousands of items. Instead the formatting of the - // date-time will be done on-demand by the view when the date will be shown. - const QDateTime dateTime = item.time(KFileItem::CreationTime); + // Don't use KFileItem::timeString() or KFileItem::time() as this is too expensive when + // having several thousands of items. Instead read the raw number from UDSEntry directly + // and the formatting of the date-time will be done on-demand by the view when the date will be shown. + const long long dateTime = item.entry().numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1); data.insert(sharedValue("creationtime"), dateTime); } if (m_requestRole[AccessTimeRole]) { - // Don't use KFileItem::timeString() as this is too expensive when - // having several thousands of items. Instead the formatting of the - // date-time will be done on-demand by the view when the date will be shown. - const QDateTime dateTime = item.time(KFileItem::AccessTime); + // Don't use KFileItem::timeString() or KFileItem::time() as this is too expensive when + // having several thousands of items. Instead read the raw number from UDSEntry directly + // and the formatting of the date-time will be done on-demand by the view when the date will be shown. + const long long dateTime = item.entry().numberValue(KIO::UDSEntry::UDS_ACCESS_TIME, -1); data.insert(sharedValue("accesstime"), dateTime); } @@ -1598,7 +1619,7 @@ QHash KFileItemModel::retrieveData(const KFileItem& item, if (m_requestRole[DestinationRole]) { QString destination = item.linkDest(); if (destination.isEmpty()) { - destination = QStringLiteral("-"); + destination = QLatin1Char('-'); } data.insert(sharedValue("destination"), destination); } @@ -1709,63 +1730,24 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b, const QColla return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0; } -/** - * Helper class for KFileItemModel::sort(). - */ -class KFileItemModelLessThan +void KFileItemModel::sort(const QList::iterator &begin, + const QList::iterator &end) const { -public: - KFileItemModelLessThan(const KFileItemModel* model, const QCollator& collator) : - m_model(model), - m_collator(collator) + auto lambdaLessThan = [&] (const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) { - } - - KFileItemModelLessThan(const KFileItemModelLessThan& other) : - m_model(other.m_model), - m_collator() - { - m_collator.setCaseSensitivity(other.m_collator.caseSensitivity()); - m_collator.setIgnorePunctuation(other.m_collator.ignorePunctuation()); - m_collator.setLocale(other.m_collator.locale()); - m_collator.setNumericMode(other.m_collator.numericMode()); - } - - ~KFileItemModelLessThan() = default; - //We do not delete m_model as the pointer was passed from outside ant it will be deleted elsewhere. - - KFileItemModelLessThan& operator=(const KFileItemModelLessThan& other) - { - m_model = other.m_model; - m_collator = other.m_collator; - return *this; - } - - bool operator()(const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) const - { - return m_model->lessThan(a, b, m_collator); - } - -private: - const KFileItemModel* m_model; - QCollator m_collator; -}; - -void KFileItemModel::sort(QList::iterator begin, - QList::iterator end) const -{ - KFileItemModelLessThan lessThan(this, m_collator); + return lessThan(a, b, m_collator); + }; - if (m_sortRole == NameRole) { - // Sorting by name can be expensive, in particular if natural sorting is + if (m_sortRole == NameRole || isRoleValueNatural(m_sortRole)) { + // Sorting by string can be expensive, in particular if natural sorting is // enabled. Use all CPU cores to speed up the sorting process. static const int numberOfThreads = QThread::idealThreadCount(); - parallelMergeSort(begin, end, lessThan, numberOfThreads); + parallelMergeSort(begin, end, lambdaLessThan, numberOfThreads); } else { // Sorting by other roles is quite fast. Use only one thread to prevent // problems caused by non-reentrant comparison functions, see // https://bugs.kde.org/show_bug.cgi?id=312679 - mergeSort(begin, end, lessThan); + mergeSort(begin, end, lambdaLessThan); } } @@ -1786,8 +1768,15 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): Q_ASSERT(itemB.isDir()); - const QVariant valueA = a->values.value("size"); - const QVariant valueB = b->values.value("size"); + QVariant valueA, valueB; + if (DetailsModeSettings::directorySizeCount()) { + valueA = a->values.value("count"); + valueB = b->values.value("count"); + } else { + // use dir size then + valueA = a->values.value("size"); + valueB = b->values.value("size"); + } if (valueA.isNull() && valueB.isNull()) { result = 0; } else if (valueA.isNull()) { @@ -1795,7 +1784,11 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const } else if (valueB.isNull()) { result = +1; } else { - result = valueA.toInt() - valueB.toInt(); + if (valueA < valueB) { + return -1; + } else { + return +1; + } } } else { // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): @@ -1814,8 +1807,8 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const } case ModificationTimeRole: { - const QDateTime dateTimeA = itemA.time(KFileItem::ModificationTime); - const QDateTime dateTimeB = itemB.time(KFileItem::ModificationTime); + const long long dateTimeA = itemA.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); + const long long dateTimeB = itemB.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); if (dateTimeA < dateTimeB) { result = -1; } else if (dateTimeA > dateTimeB) { @@ -1825,8 +1818,8 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const } case CreationTimeRole: { - const QDateTime dateTimeA = itemA.time(KFileItem::CreationTime); - const QDateTime dateTimeB = itemB.time(KFileItem::CreationTime); + const long long dateTimeA = itemA.entry().numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1); + const long long dateTimeB = itemB.entry().numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1); if (dateTimeA < dateTimeB) { result = -1; } else if (dateTimeA > dateTimeB) { @@ -1859,8 +1852,17 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const default: { const QByteArray role = roleForType(m_sortRole); - result = QString::compare(a->values.value(role).toString(), - b->values.value(role).toString()); + const QString roleValueA = a->values.value(role).toString(); + const QString roleValueB = b->values.value(role).toString(); + if (!roleValueA.isEmpty() && roleValueB.isEmpty()) { + result = -1; + } else if (roleValueA.isEmpty() && !roleValueB.isEmpty()) { + result = +1; + } else if (isRoleValueNatural(m_sortRole)) { + result = stringCompare(roleValueA, roleValueB, collator); + } else { + result = QString::compare(roleValueA, roleValueB); + } break; } @@ -1891,6 +1893,8 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const int KFileItemModel::stringCompare(const QString& a, const QString& b, const QCollator& collator) const { + QMutexLocker collatorLock(s_collatorMutex()); + if (m_naturalSorting) { return collator.compare(a, b); } @@ -1906,11 +1910,6 @@ int KFileItemModel::stringCompare(const QString& a, const QString& b, const QCol return QString::compare(a, b, Qt::CaseSensitive); } -bool KFileItemModel::useMaximumUpdateInterval() const -{ - return !m_dirLister->url().isLocalFile(); -} - QList > KFileItemModel::nameRoleGroups() const { Q_ASSERT(!m_itemData.isEmpty()); @@ -1936,28 +1935,35 @@ QList > KFileItemModel::nameRoleGroups() const if (firstChar != newFirstChar) { QString newGroupValue; if (newFirstChar.isLetter()) { - // Try to find a matching group in the range 'A' to 'Z'. - static std::vector lettersAtoZ; - lettersAtoZ.reserve('Z' - 'A' + 1); - if (lettersAtoZ.empty()) { - for (char c = 'A'; c <= 'Z'; ++c) { - lettersAtoZ.push_back(QLatin1Char(c)); + + if (m_collator.compare(newFirstChar, QChar(QLatin1Char('A'))) >= 0 && m_collator.compare(newFirstChar, QChar(QLatin1Char('Z'))) <= 0) { + // WARNING! Symbols based on latin 'Z' like 'Z' with acute are treated wrong as non Latin and put in a new group. + + // Try to find a matching group in the range 'A' to 'Z'. + static std::vector lettersAtoZ; + lettersAtoZ.reserve('Z' - 'A' + 1); + if (lettersAtoZ.empty()) { + for (char c = 'A'; c <= 'Z'; ++c) { + lettersAtoZ.push_back(QLatin1Char(c)); + } } - } - auto localeAwareLessThan = [this](QChar c1, QChar c2) -> bool { - return m_collator.compare(c1, c2) < 0; - }; + auto localeAwareLessThan = [this](QChar c1, QChar c2) -> bool { + return m_collator.compare(c1, c2) < 0; + }; - std::vector::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan); - if (it != lettersAtoZ.end()) { - if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) { - // newFirstChar belongs to the group preceding *it. - // Example: for an umlaut 'A' in the German locale, *it would be 'B' now. - --it; + std::vector::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan); + if (it != lettersAtoZ.end()) { + if (localeAwareLessThan(newFirstChar, *it)) { + // newFirstChar belongs to the group preceding *it. + // Example: for an umlaut 'A' in the German locale, *it would be 'B' now. + --it; + } + newGroupValue = *it; } - newGroupValue = *it; + } else { + // Symbols from non Latin-based scripts newGroupValue = newFirstChar; } } else if (newFirstChar >= QLatin1Char('0') && newFirstChar <= QLatin1Char('9')) { @@ -2013,7 +2019,7 @@ QList > KFileItemModel::sizeRoleGroups() const return groups; } -QList > KFileItemModel::timeRoleGroups(std::function fileTimeCb) const +QList > KFileItemModel::timeRoleGroups(const std::function &fileTimeCb) const { Q_ASSERT(!m_itemData.isEmpty()); @@ -2077,12 +2083,20 @@ QList > KFileItemModel::timeRoleGroups(std::function > KFileItemModel::timeRoleGroups(std::function