X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/e242d9548d11d92568a648efece5ff6c280b36cd..30bf49d5d1539e9d02944230cf4489ebbccf5a45:/src/kitemviews/kfileitemmodel.cpp diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index 4d5879f4d..d9c1e6bfb 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -21,46 +21,44 @@ #include "kfileitemmodel.h" -#include -#include -#include -#include -#include //TODO: port to QCollator - -#include "private/kfileitemmodelsortalgorithm.h" +#include "dolphin_generalsettings.h" +#include "dolphindebug.h" #include "private/kfileitemmodeldirlister.h" +#include "private/kfileitemmodelsortalgorithm.h" -#include +#include +#include + +#include #include #include #include -#include -#include - // #define KFILEITEMMODEL_DEBUG KFileItemModel::KFileItemModel(QObject* parent) : KItemModelBase("text", parent), - m_dirLister(0), - m_naturalSorting(KGlobalSettings::naturalSorting()), + m_dirLister(nullptr), m_sortDirsFirst(true), m_sortRole(NameRole), m_sortingProgressPercent(-1), m_roles(), - m_caseSensitivity(Qt::CaseInsensitive), m_itemData(), m_items(), m_filter(), m_filteredItems(), m_requestRole(), - m_maximumUpdateIntervalTimer(0), - m_resortAllItemsTimer(0), + m_maximumUpdateIntervalTimer(nullptr), + m_resortAllItemsTimer(nullptr), m_pendingItemsToInsert(), m_groups(), m_expandedDirs(), m_urlsToExpand() { + m_collator.setNumericMode(true); + + loadSortingSettings(); + m_dirLister = new KFileItemModelDirLister(this); m_dirLister->setDelayedMimeTypes(true); @@ -78,6 +76,7 @@ KFileItemModel::KFileItemModel(QObject* parent) : connect(m_dirLister, static_cast(&KFileItemModelDirLister::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, &KFileItemModelDirLister::urlIsFileError, this, &KFileItemModel::urlIsFileError); @@ -89,6 +88,7 @@ KFileItemModel::KFileItemModel(QObject* parent) : m_roles.insert("text"); m_roles.insert("isDir"); m_roles.insert("isLink"); + m_roles.insert("isHidden"); // For slow KIO-slaves like used for searching it makes sense to show results periodically even // before the completed() or canceled() signal has been emitted. @@ -106,26 +106,25 @@ KFileItemModel::KFileItemModel(QObject* parent) : m_resortAllItemsTimer->setSingleShot(true); connect(m_resortAllItemsTimer, &QTimer::timeout, this, &KFileItemModel::resortAllItems); - connect(KGlobalSettings::self(), &KGlobalSettings::naturalSortingChanged, - this, &KFileItemModel::slotNaturalSortingChanged); + connect(GeneralSettings::self(), &GeneralSettings::sortingChoiceChanged, this, &KFileItemModel::slotSortingChoiceChanged); } KFileItemModel::~KFileItemModel() { qDeleteAll(m_itemData); - qDeleteAll(m_filteredItems.values()); + qDeleteAll(m_filteredItems); qDeleteAll(m_pendingItemsToInsert); } -void KFileItemModel::loadDirectory(const KUrl& url) +void KFileItemModel::loadDirectory(const QUrl &url) { m_dirLister->openUrl(url); } -void KFileItemModel::refreshDirectory(const KUrl& url) +void KFileItemModel::refreshDirectory(const QUrl &url) { // Refresh all expanded directories first (Bug 295300) - QHashIterator expandedDirs(m_expandedDirs); + QHashIterator expandedDirs(m_expandedDirs); while (expandedDirs.hasNext()) { expandedDirs.next(); m_dirLister->openUrl(expandedDirs.value(), KDirLister::Reload); @@ -134,7 +133,7 @@ void KFileItemModel::refreshDirectory(const KUrl& url) m_dirLister->openUrl(url, KDirLister::Reload); } -KUrl KFileItemModel::directory() const +QUrl KFileItemModel::directory() const { return m_dirLister->url(); } @@ -190,8 +189,9 @@ bool KFileItemModel::setData(int index, const QHash& value m_itemData[index]->values = currentValues; if (changedRoles.contains("text")) { - KUrl url = m_itemData[index]->item.url(); - url.setFileName(currentValues["text"].toString()); + QUrl url = m_itemData[index]->item.url(); + url = url.adjusted(QUrl::RemoveFilename); + url.setPath(url.path() + currentValues["text"].toString()); m_itemData[index]->item.setUrl(url); } @@ -244,12 +244,11 @@ QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const // The following code has been taken from KDirModel::mimeData() // (kdelibs/kio/kio/kdirmodel.cpp) // Copyright (C) 2006 David Faure - KUrl::List urls; - KUrl::List mostLocalUrls; - bool canUseMostLocalUrls = true; - const ItemData* lastAddedItem = 0; + QList urls; + QList mostLocalUrls; + const ItemData* lastAddedItem = nullptr; - foreach (int index, indexes) { + for (int index : indexes) { const ItemData* itemData = m_itemData.at(index); const ItemData* parent = itemData->parent; @@ -265,23 +264,14 @@ QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const lastAddedItem = itemData; const KFileItem& item = itemData->item; if (!item.isNull()) { - urls << item.targetUrl(); + urls << item.url(); bool isLocal; mostLocalUrls << item.mostLocalUrl(isLocal); - if (!isLocal) { - canUseMostLocalUrls = false; - } } } - const bool different = canUseMostLocalUrls && mostLocalUrls != urls; - if (different) { - urls.populateMimeData(mostLocalUrls, data); - } else { - urls.populateMimeData(data); - } - + KUrlMimeData::setUrls(urls, mostLocalUrls, data); return data; } @@ -331,14 +321,33 @@ QList > KFileItemModel::groups() const switch (typeForRole(sortRole())) { case NameRole: m_groups = nameRoleGroups(); break; case SizeRole: m_groups = sizeRoleGroups(); break; - case DateRole: m_groups = dateRoleGroups(); break; + case ModificationTimeRole: + m_groups = timeRoleGroups([](const ItemData *item) { + return item->item.time(KFileItem::ModificationTime); + }); + break; + case CreationTimeRole: + m_groups = timeRoleGroups([](const ItemData *item) { + return item->item.time(KFileItem::CreationTime); + }); + break; + case AccessTimeRole: + m_groups = timeRoleGroups([](const ItemData *item) { + return item->item.time(KFileItem::AccessTime); + }); + break; + case DeletionTimeRole: + m_groups = timeRoleGroups([](const ItemData *item) { + return item->values.value("deletiontime").toDateTime(); + }); + break; case PermissionsRole: m_groups = permissionRoleGroups(); break; case RatingRole: m_groups = ratingRoleGroups(); break; default: m_groups = genericStringRoleGroups(sortRole()); break; } #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed(); + qCDebug(DolphinDebug) << "[TIME] Calculating groups for" << count() << "items:" << timer.elapsed(); #endif } @@ -354,7 +363,7 @@ KFileItem KFileItemModel::fileItem(int index) const return KFileItem(); } -KFileItem KFileItemModel::fileItem(const KUrl& url) const +KFileItem KFileItemModel::fileItem(const QUrl &url) const { const int indexForUrl = index(url); if (indexForUrl >= 0) { @@ -365,13 +374,12 @@ KFileItem KFileItemModel::fileItem(const KUrl& url) const int KFileItemModel::index(const KFileItem& item) const { - return index(KUrl(item.url())); + return index(item.url()); } -int KFileItemModel::index(const KUrl& url) const +int KFileItemModel::index(const QUrl& url) const { - KUrl urlToFind = url; - urlToFind.adjustPath(KUrl::RemoveTrailingSlash); + const QUrl urlToFind = url.adjusted(QUrl::StripTrailingSlash); const int itemCount = m_itemData.count(); int itemsInHash = m_items.count(); @@ -389,7 +397,7 @@ int KFileItemModel::index(const KUrl& url) const const int blockSize = 1000; const int currentBlockEnd = qMin(itemsInHash + blockSize, itemCount); for (int i = itemsInHash; i < currentBlockEnd; ++i) { - const KUrl nextUrl = m_itemData.at(i)->item.url(); + const QUrl nextUrl = m_itemData.at(i)->item.url(); m_items.insert(nextUrl, i); } @@ -408,25 +416,28 @@ int KFileItemModel::index(const KUrl& url) const if (m_items.count() != m_itemData.count() && printDebugInfo) { printDebugInfo = false; - kWarning() << "The model is in an inconsistent state."; - kWarning() << "m_items.count() ==" << m_items.count(); - kWarning() << "m_itemData.count() ==" << m_itemData.count(); + qCWarning(DolphinDebug) << "The model is in an inconsistent state."; + qCWarning(DolphinDebug) << "m_items.count() ==" << m_items.count(); + qCWarning(DolphinDebug) << "m_itemData.count() ==" << m_itemData.count(); // Check if there are multiple items with the same URL. - QMultiHash indexesForUrl; + QMultiHash indexesForUrl; for (int i = 0; i < m_itemData.count(); ++i) { indexesForUrl.insert(m_itemData.at(i)->item.url(), i); } - foreach (const KUrl& url, indexesForUrl.uniqueKeys()) { + foreach (const QUrl& url, indexesForUrl.uniqueKeys()) { if (indexesForUrl.count(url) > 1) { - kWarning() << "Multiple items found with the URL" << url; - foreach (int index, indexesForUrl.values(url)) { - const ItemData* data = m_itemData.at(index); - kWarning() << "index" << index << ":" << data->item; + qCWarning(DolphinDebug) << "Multiple items found with the URL" << url; + + auto it = indexesForUrl.find(url); + while (it != indexesForUrl.end() && it.key() == url) { + const ItemData* data = m_itemData.at(it.value()); + qCWarning(DolphinDebug) << "index" << it.value() << ":" << data->item; if (data->parent) { - kWarning() << "parent" << data->parent->item; + qCWarning(DolphinDebug) << "parent" << data->parent->item; } + ++it; } } } @@ -512,15 +523,15 @@ bool KFileItemModel::setExpanded(int index, bool expanded) } const KFileItem item = m_itemData.at(index)->item; - const KUrl url = item.url(); - const KUrl targetUrl = item.targetUrl(); + const QUrl url = item.url(); + const QUrl targetUrl = item.targetUrl(); if (expanded) { m_expandedDirs.insert(targetUrl, url); m_dirLister->openUrl(url, KDirLister::Keep); - const KUrl::List previouslyExpandedChildren = m_itemData.at(index)->values.value("previouslyExpandedChildren").value(); - foreach (const KUrl& url, previouslyExpandedChildren) { - m_urlsToExpand.insert(url); + const QVariantList previouslyExpandedChildren = m_itemData.at(index)->values.value("previouslyExpandedChildren").value(); + foreach (const QVariant& var, previouslyExpandedChildren) { + m_urlsToExpand.insert(var.toUrl()); } } else { // Note that there might be (indirect) children of the folder which is to be collapsed in @@ -542,14 +553,14 @@ bool KFileItemModel::setExpanded(int index, bool expanded) const int itemCount = m_itemData.count(); const int firstChildIndex = index + 1; - KUrl::List expandedChildren; + QVariantList expandedChildren; int childIndex = firstChildIndex; while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) { ItemData* itemData = m_itemData.at(childIndex); if (itemData->values.value("isExpanded").toBool()) { - const KUrl targetUrl = itemData->item.targetUrl(); - const KUrl url = itemData->item.url(); + const QUrl targetUrl = itemData->item.targetUrl(); + const QUrl url = itemData->item.url(); m_expandedDirs.remove(targetUrl); m_dirLister->stop(url); // TODO: try to unit-test this, see https://bugs.kde.org/show_bug.cgi?id=332102#c11 expandedChildren.append(targetUrl); @@ -593,35 +604,48 @@ int KFileItemModel::expandedParentsCount(int index) const return 0; } -QSet KFileItemModel::expandedDirectories() const +QSet KFileItemModel::expandedDirectories() const { - return m_expandedDirs.values().toSet(); + QSet result; + const auto dirs = m_expandedDirs; + for (const auto &dir : dirs) { + result.insert(dir); + } + return result; } -void KFileItemModel::restoreExpandedDirectories(const QSet& urls) +void KFileItemModel::restoreExpandedDirectories(const QSet &urls) { m_urlsToExpand = urls; } -void KFileItemModel::expandParentDirectories(const KUrl& url) +void KFileItemModel::expandParentDirectories(const QUrl &url) { - const int pos = m_dirLister->url().path().length(); // Assure that each sub-path of the URL that should be // expanded is added to m_urlsToExpand. KDirLister // does not care whether the parent-URL has already been // expanded. - KUrl urlToExpand = m_dirLister->url(); - const QStringList subDirs = url.path().mid(pos).split(QDir::separator()); + QUrl urlToExpand = m_dirLister->url(); + const int pos = urlToExpand.path().length(); + + // first subdir can be empty, if m_dirLister->url().path() does not end with '/' + // this happens if baseUrl is not root but a home directory, see FoldersPanel, + // so using QString::SkipEmptyParts + const QStringList subDirs = url.path().mid(pos).split(QDir::separator(), QString::SkipEmptyParts); for (int i = 0; i < subDirs.count() - 1; ++i) { - urlToExpand.addPath(subDirs.at(i)); + QString path = urlToExpand.path(); + if (!path.endsWith(QLatin1Char('/'))) { + path.append(QLatin1Char('/')); + } + urlToExpand.setPath(path + subDirs.at(i)); m_urlsToExpand.insert(urlToExpand); } // KDirLister::open() must called at least once to trigger an initial // loading. The pending URLs that must be restored are handled // in slotCompleted(). - QSetIterator it2(m_urlsToExpand); + QSetIterator it2(m_urlsToExpand); while (it2.hasNext()) { const int idx = index(it2.next()); if (idx >= 0 && !isExpanded(idx)) { @@ -783,6 +807,30 @@ void KFileItemModel::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder pre resortAllItems(); } +void KFileItemModel::loadSortingSettings() +{ + using Choice = GeneralSettings::EnumSortingChoice; + switch (GeneralSettings::sortingChoice()) { + case Choice::NaturalSorting: + m_naturalSorting = true; + m_collator.setCaseSensitivity(Qt::CaseInsensitive); + break; + case Choice::CaseSensitiveSorting: + m_naturalSorting = false; + m_collator.setCaseSensitivity(Qt::CaseSensitive); + break; + case Choice::CaseInsensitiveSorting: + m_naturalSorting = false; + m_collator.setCaseSensitivity(Qt::CaseInsensitive); + break; + 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() { m_resortAllItemsTimer->stop(); @@ -795,14 +843,14 @@ void KFileItemModel::resortAllItems() #ifdef KFILEITEMMODEL_DEBUG QElapsedTimer timer; timer.start(); - kDebug() << "==========================================================="; - kDebug() << "Resorting" << itemCount << "items"; + qCDebug(DolphinDebug) << "==========================================================="; + qCDebug(DolphinDebug) << "Resorting" << itemCount << "items"; #endif // Remember the order of the current URLs so // that it can be determined which indexes have // been moved because of the resorting. - QList oldUrls; + QList oldUrls; oldUrls.reserve(itemCount); foreach (const ItemData* itemData, m_itemData) { oldUrls.append(itemData->item.url()); @@ -858,7 +906,7 @@ void KFileItemModel::resortAllItems() } #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed(); + qCDebug(DolphinDebug) << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed(); #endif } @@ -871,7 +919,7 @@ void KFileItemModel::slotCompleted() // Note that the parent folder must be expanded before any of its subfolders become visible. // Therefore, some URLs in m_restoredExpandedUrls might not be visible yet // -> we expand the first visible URL we find in m_restoredExpandedUrls. - foreach (const KUrl& url, m_urlsToExpand) { + foreach (const QUrl& url, m_urlsToExpand) { const int indexForUrl = index(url); if (indexForUrl >= 0) { m_urlsToExpand.remove(url); @@ -899,16 +947,15 @@ void KFileItemModel::slotCanceled() emit directoryLoadingCanceled(); } -void KFileItemModel::slotItemsAdded(const KUrl& directoryUrl, const KFileItemList& items) +void KFileItemModel::slotItemsAdded(const QUrl &directoryUrl, const KFileItemList& items) { Q_ASSERT(!items.isEmpty()); - KUrl parentUrl; + QUrl parentUrl; if (m_expandedDirs.contains(directoryUrl)) { parentUrl = m_expandedDirs.value(directoryUrl); } else { - parentUrl = directoryUrl; - parentUrl.adjustPath(KUrl::RemoveTrailingSlash); + parentUrl = directoryUrl.adjusted(QUrl::StripTrailingSlash); } if (m_requestRole[ExpandedParentsCountRole]) { @@ -917,7 +964,7 @@ void KFileItemModel::slotItemsAdded(const KUrl& directoryUrl, const KFileItemLis // might result in emitting the same items twice due to the Keep-parameter. // This case happens if an item gets expanded, collapsed and expanded again // before the items could be loaded for the first expansion. - if (index(KUrl(items.first().url())) >= 0) { + if (index(items.first().url()) >= 0) { // The items are already part of the model. return; } @@ -1014,7 +1061,7 @@ void KFileItemModel::slotRefreshItems(const QList >& { Q_ASSERT(!items.isEmpty()); #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "Refreshing" << items.count() << "items"; + qCDebug(DolphinDebug) << "Refreshing" << items.count() << "items"; #endif // Get the indexes of all items that have been refreshed @@ -1080,10 +1127,10 @@ void KFileItemModel::slotRefreshItems(const QList >& void KFileItemModel::slotClear() { #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "Clearing all items"; + qCDebug(DolphinDebug) << "Clearing all items"; #endif - qDeleteAll(m_filteredItems.values()); + qDeleteAll(m_filteredItems); m_filteredItems.clear(); m_groups.clear(); @@ -1104,9 +1151,9 @@ void KFileItemModel::slotClear() m_expandedDirs.clear(); } -void KFileItemModel::slotNaturalSortingChanged() +void KFileItemModel::slotSortingChoiceChanged() { - m_naturalSorting = KGlobalSettings::naturalSorting(); + loadSortingSettings(); resortAllItems(); } @@ -1127,8 +1174,8 @@ void KFileItemModel::insertItems(QList& newItems) #ifdef KFILEITEMMODEL_DEBUG QElapsedTimer timer; timer.start(); - kDebug() << "==========================================================="; - kDebug() << "Inserting" << newItems.count() << "items"; + qCDebug(DolphinDebug) << "==========================================================="; + qCDebug(DolphinDebug) << "Inserting" << newItems.count() << "items"; #endif m_groups.clear(); @@ -1145,7 +1192,7 @@ void KFileItemModel::insertItems(QList& newItems) sort(newItems.begin(), newItems.end()); #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "[TIME] Sorting:" << timer.elapsed(); + qCDebug(DolphinDebug) << "[TIME] Sorting:" << timer.elapsed(); #endif KItemRangeList itemRanges; @@ -1161,7 +1208,7 @@ void KFileItemModel::insertItems(QList& newItems) } else { m_itemData.reserve(totalItemCount); for (int i = existingItemCount; i < totalItemCount; ++i) { - m_itemData.append(0); + m_itemData.append(nullptr); } // We build the new list m_itemData in reverse order to minimize @@ -1174,7 +1221,7 @@ void KFileItemModel::insertItems(QList& newItems) while (sourceIndexNewItems >= 0) { ItemData* newItem = newItems.at(sourceIndexNewItems); - if (sourceIndexExistingItems >= 0 && lessThan(newItem, m_itemData.at(sourceIndexExistingItems))) { + if (sourceIndexExistingItems >= 0 && lessThan(newItem, m_itemData.at(sourceIndexExistingItems), m_collator)) { // Move an existing item to its new position. If any new items // are behind it, push the item range to itemRanges. if (rangeCount > 0) { @@ -1203,13 +1250,13 @@ void KFileItemModel::insertItems(QList& newItems) } // The indexes in m_items are not correct anymore. Therefore, we clear m_items. - // It will be re-populated with the updated indices if index(const KUrl&) is called. + // It will be re-populated with the updated indices if index(const QUrl&) is called. m_items.clear(); emit itemsInserted(itemRanges); #ifdef KFILEITEMMODEL_DEBUG - kDebug() << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed(); + qCDebug(DolphinDebug) << "[TIME] Inserting of" << newItems.count() << "items:" << timer.elapsed(); #endif } @@ -1231,7 +1278,7 @@ void KFileItemModel::removeItems(const KItemRangeList& itemRanges, RemoveItemsBe delete m_itemData.at(index); } - m_itemData[index] = 0; + m_itemData[index] = nullptr; } } @@ -1256,13 +1303,13 @@ void KFileItemModel::removeItems(const KItemRangeList& itemRanges, RemoveItemsBe m_itemData.erase(m_itemData.end() - removedItemsCount, m_itemData.end()); // The indexes in m_items are not correct anymore. Therefore, we clear m_items. - // It will be re-populated with the updated indices if index(const KUrl&) is called. + // It will be re-populated with the updated indices if index(const QUrl&) is called. m_items.clear(); emit itemsRemoved(itemRanges); } -QList KFileItemModel::createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const +QList KFileItemModel::createItemDataList(const QUrl& parentUrl, const KFileItemList& items) const { if (m_sortRole == TypeRole) { // Try to resolve the MIME-types synchronously to prevent a reordering of @@ -1272,7 +1319,7 @@ QList KFileItemModel::createItemDataList(const KUrl& } const int parentIndex = index(parentUrl); - ItemData* parentItem = parentIndex < 0 ? 0 : m_itemData.at(parentIndex); + ItemData* parentItem = parentIndex < 0 ? nullptr : m_itemData.at(parentIndex); QList itemDataList; itemDataList.reserve(items.count()); @@ -1295,6 +1342,7 @@ void KFileItemModel::prepareItemsForSorting(QList& itemDataList) case GroupRole: case DestinationRole: case PathRole: + case DeletionTimeRole: // These roles can be determined with retrieveData, and they have to be stored // in the QHash "values" for the sorting. foreach (ItemData* itemData, itemDataList) { @@ -1390,14 +1438,14 @@ void KFileItemModel::emitItemsChangedAndTriggerResorting(const KItemRangeList& i // (b) the successor of the last item is "lessThan" the last item, or // (c) the internal order of the items in the range is incorrect. if (first > 0 - && lessThan(m_itemData.at(first), m_itemData.at(first - 1))) { + && lessThan(m_itemData.at(first), m_itemData.at(first - 1), m_collator)) { needsResorting = true; } else if (last < count() - 1 - && lessThan(m_itemData.at(last + 1), m_itemData.at(last))) { + && lessThan(m_itemData.at(last + 1), m_itemData.at(last), m_collator)) { needsResorting = true; } else { for (int index = first; index < last; ++index) { - if (lessThan(m_itemData.at(index + 1), m_itemData.at(index))) { + if (lessThan(m_itemData.at(index + 1), m_itemData.at(index), m_collator)) { needsResorting = true; break; } @@ -1447,6 +1495,7 @@ KFileItemModel::RoleType KFileItemModel::typeForRole(const QByteArray& role) con // with KFileItemModel::roleForType() in case if a change is done). roles.insert("isDir", IsDirRole); roles.insert("isLink", IsLinkRole); + roles.insert("isHidden", IsHiddenRole); roles.insert("isExpanded", IsExpandedRole); roles.insert("isExpandable", IsExpandableRole); roles.insert("expandedParentsCount", ExpandedParentsCountRole); @@ -1473,6 +1522,7 @@ QByteArray KFileItemModel::roleForType(RoleType roleType) const // with KFileItemModel::typeForRole() in case if a change is done). roles.insert(IsDirRole, "isDir"); roles.insert(IsLinkRole, "isLink"); + roles.insert(IsHiddenRole, "isHidden"); roles.insert(IsExpandedRole, "isExpanded"); roles.insert(IsExpandableRole, "isExpandable"); roles.insert(ExpandedParentsCountRole, "expandedParentsCount"); @@ -1500,6 +1550,10 @@ 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[NameRole]) { data.insert(sharedValue("text"), item.text()); } @@ -1508,12 +1562,28 @@ QHash KFileItemModel::retrieveData(const KFileItem& item, data.insert(sharedValue("size"), item.size()); } - if (m_requestRole[DateRole]) { - // 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); - data.insert(sharedValue("date"), dateTime); + if (m_requestRole[ModificationTimeRole]) { + // 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() 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() 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); } if (m_requestRole[PermissionsRole]) { @@ -1531,7 +1601,7 @@ QHash KFileItemModel::retrieveData(const KFileItem& item, if (m_requestRole[DestinationRole]) { QString destination = item.linkDest(); if (destination.isEmpty()) { - destination = QLatin1String("-"); + destination = QStringLiteral("-"); } data.insert(sharedValue("destination"), destination); } @@ -1559,6 +1629,14 @@ QHash KFileItemModel::retrieveData(const KFileItem& item, data.insert(sharedValue("path"), path); } + if (m_requestRole[DeletionTimeRole]) { + QDateTime deletionTime; + if (item.url().scheme() == QLatin1String("trash")) { + deletionTime = QDateTime::fromString(item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA + 1), Qt::ISODate); + } + data.insert(sharedValue("deletiontime"), deletionTime); + } + if (m_requestRole[IsExpandableRole] && isDir) { data.insert(sharedValue("isExpandable"), true); } @@ -1584,7 +1662,7 @@ QHash KFileItemModel::retrieveData(const KFileItem& item, return data; } -bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const +bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b, const QCollator& collator) const { int result = 0; @@ -1629,50 +1707,33 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const } } - result = sortRoleCompare(a, b); + result = sortRoleCompare(a, b, collator); return (sortOrder() == Qt::AscendingOrder) ? result < 0 : result > 0; } -/** - * Helper class for KFileItemModel::sort(). - */ -class KFileItemModelLessThan -{ -public: - KFileItemModelLessThan(const KFileItemModel* model) : - m_model(model) - { - } - - bool operator()(const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) const - { - return m_model->lessThan(a, b); - } - -private: - const KFileItemModel* m_model; -}; - void KFileItemModel::sort(QList::iterator begin, QList::iterator end) const { - KFileItemModelLessThan lessThan(this); + auto lambdaLessThan = [&] (const KFileItemModel::ItemData* a, const KFileItemModel::ItemData* b) + { + return lessThan(a, b, m_collator); + }; if (m_sortRole == NameRole) { // Sorting by name 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); } } -int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const +int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const QCollator& collator) const { const KFileItem& itemA = a->item; const KFileItem& itemB = b->item; @@ -1716,9 +1777,9 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const break; } - case DateRole: { - const QDateTime dateTimeA = itemA.time(KFileItem::ModificationTime); - const QDateTime dateTimeB = itemB.time(KFileItem::ModificationTime); + case ModificationTimeRole: { + 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) { @@ -1727,17 +1788,36 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const break; } - case RatingRole: { - result = a->values.value("rating").toInt() - b->values.value("rating").toInt(); + case CreationTimeRole: { + 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) { + result = +1; + } break; } - case ImageSizeRole: { - // Alway use a natural comparing to interpret the numbers of a string like - // "1600 x 1200" for having a correct sorting. - result = KStringHandler::naturalCompare(a->values.value("imageSize").toString(), - b->values.value("imageSize").toString(), - Qt::CaseSensitive); + case DeletionTimeRole: { + const QDateTime dateTimeA = a->values.value("deletiontime").toDateTime(); + const QDateTime dateTimeB = b->values.value("deletiontime").toDateTime(); + if (dateTimeA < dateTimeB) { + result = -1; + } else if (dateTimeA > dateTimeB) { + result = +1; + } + break; + } + + case RatingRole: + case WidthRole: + case HeightRole: + case WordCountRole: + case LineCountRole: + case TrackRole: + case ReleaseYearRole: { + result = a->values.value(roleForType(m_sortRole)).toInt() - b->values.value(roleForType(m_sortRole)).toInt(); break; } @@ -1756,14 +1836,13 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const } // Fallback #1: Compare the text of the items - result = stringCompare(itemA.text(), itemB.text()); + result = stringCompare(itemA.text(), itemB.text(), collator); if (result != 0) { return result; } // Fallback #2: KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used - result = stringCompare(itemA.name(m_caseSensitivity == Qt::CaseInsensitive), - itemB.name(m_caseSensitivity == Qt::CaseInsensitive)); + result = stringCompare(itemA.name(), itemB.name(), collator); if (result != 0) { return result; } @@ -1774,26 +1853,21 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const return QString::compare(itemA.url().url(), itemB.url().url(), Qt::CaseSensitive); } -int KFileItemModel::stringCompare(const QString& a, const QString& b) const +int KFileItemModel::stringCompare(const QString& a, const QString& b, const QCollator& collator) const { - // Taken from KDirSortFilterProxyModel (kdelibs/kfile/kdirsortfilterproxymodel.*) - // Copyright (C) 2006 by Peter Penz - // Copyright (C) 2006 by Dominic Battre - // Copyright (C) 2006 by Martin Pool + if (m_naturalSorting) { + return collator.compare(a, b); + } - if (m_caseSensitivity == Qt::CaseInsensitive) { - const int result = m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseInsensitive) - : QString::compare(a, b, Qt::CaseInsensitive); - if (result != 0) { - // Only return the result, if the strings are not equal. If they are equal by a case insensitive - // comparison, still a deterministic sort order is required. A case sensitive - // comparison is done as fallback. - return result; - } + const int result = QString::compare(a, b, collator.caseSensitivity()); + if (result != 0 || collator.caseSensitivity() == Qt::CaseSensitive) { + // Only return the result, if the strings are not equal. If they are equal by a case insensitive + // comparison, still a deterministic sort order is required. A case sensitive + // comparison is done as fallback. + return result; } - return m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseSensitive) - : QString::compare(a, b, Qt::CaseSensitive); + return QString::compare(a, b, Qt::CaseSensitive); } bool KFileItemModel::useMaximumUpdateInterval() const @@ -1801,11 +1875,6 @@ bool KFileItemModel::useMaximumUpdateInterval() const return !m_dirLister->url().isLocalFile(); } -static bool localeAwareLessThan(const QChar& c1, const QChar& c2) -{ - return QString::localeAwareCompare(c1, c2) < 0; -} - QList > KFileItemModel::nameRoleGroups() const { Q_ASSERT(!m_itemData.isEmpty()); @@ -1833,12 +1902,17 @@ QList > KFileItemModel::nameRoleGroups() const 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)); } } + 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()) { @@ -1903,7 +1977,7 @@ QList > KFileItemModel::sizeRoleGroups() const return groups; } -QList > KFileItemModel::dateRoleGroups() const +QList > KFileItemModel::timeRoleGroups(std::function fileTimeCb) const { Q_ASSERT(!m_itemData.isEmpty()); @@ -1912,31 +1986,37 @@ QList > KFileItemModel::dateRoleGroups() const const QDate currentDate = QDate::currentDate(); - QDate previousModifiedDate; + QDate previousFileDate; QString groupValue; for (int i = 0; i <= maxIndex; ++i) { if (isChildItem(i)) { continue; } - const QDateTime modifiedTime = m_itemData.at(i)->item.time(KFileItem::ModificationTime); - const QDate modifiedDate = modifiedTime.date(); - if (modifiedDate == previousModifiedDate) { + const QDateTime fileTime = fileTimeCb(m_itemData.at(i)); + const QDate fileDate = fileTime.date(); + if (fileDate == previousFileDate) { // The current item is in the same group as the previous item continue; } - previousModifiedDate = modifiedDate; + previousFileDate = fileDate; - const int daysDistance = modifiedDate.daysTo(currentDate); + const int daysDistance = fileDate.daysTo(currentDate); QString newGroupValue; - if (currentDate.year() == modifiedDate.year() && currentDate.month() == modifiedDate.month()) { + if (currentDate.year() == fileDate.year() && + currentDate.month() == fileDate.month()) { + switch (daysDistance / 7) { case 0: switch (daysDistance) { case 0: newGroupValue = i18nc("@title:group Date", "Today"); break; case 1: newGroupValue = i18nc("@title:group Date", "Yesterday"); break; - default: newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A", "%A")); + default: + newGroupValue = fileTime.toString( + i18nc("@title:group Date: The week day name: dddd", "dddd")); + newGroupValue = i18nc("Can be used to script translation of \"dddd\"" + "with context @title:group Date", "%1", newGroupValue); } break; case 1: @@ -1957,22 +2037,60 @@ QList > KFileItemModel::dateRoleGroups() const } } else { const QDate lastMonthDate = currentDate.addMonths(-1); - if (lastMonthDate.year() == modifiedDate.year() && lastMonthDate.month() == modifiedDate.month()) { + if (lastMonthDate.year() == fileDate.year() && + lastMonthDate.month() == fileDate.month()) { + if (daysDistance == 1) { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Yesterday (%B, %Y)")); + newGroupValue = fileTime.toString(i18nc("@title:group Date: " + "MMMM is full month name in current locale, and yyyy is " + "full year number", "'Yesterday' (MMMM, yyyy)")); + newGroupValue = i18nc("Can be used to script translation of " + "\"'Yesterday' (MMMM, yyyy)\" with context @title:group Date", + "%1", newGroupValue); } else if (daysDistance <= 7) { - newGroupValue = modifiedTime.toString(i18nc("@title:group The week day name: %A, %B is full month name in current locale, and %Y is full year number", "%A (%B, %Y)")); + newGroupValue = fileTime.toString(i18nc("@title:group Date: " + "The week day name: dddd, MMMM is full month name " + "in current locale, and yyyy is full year number", + "dddd (MMMM, yyyy)")); + newGroupValue = i18nc("Can be used to script translation of " + "\"dddd (MMMM, yyyy)\" with context @title:group Date", + "%1", newGroupValue); } else if (daysDistance <= 7 * 2) { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "One Week Ago (%B, %Y)")); + newGroupValue = fileTime.toString(i18nc("@title:group Date: " + "MMMM is full month name in current locale, and yyyy is " + "full year number", "'One Week Ago' (MMMM, yyyy)")); + newGroupValue = i18nc("Can be used to script translation of " + "\"'One Week Ago' (MMMM, yyyy)\" with context @title:group Date", + "%1", newGroupValue); } else if (daysDistance <= 7 * 3) { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Two Weeks Ago (%B, %Y)")); + newGroupValue = fileTime.toString(i18nc("@title:group Date: " + "MMMM is full month name in current locale, and yyyy is " + "full year number", "'Two Weeks Ago' (MMMM, yyyy)")); + newGroupValue = i18nc("Can be used to script translation of " + "\"'Two Weeks Ago' (MMMM, yyyy)\" with context @title:group Date", + "%1", newGroupValue); } else if (daysDistance <= 7 * 4) { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Three Weeks Ago (%B, %Y)")); + newGroupValue = fileTime.toString(i18nc("@title:group Date: " + "MMMM is full month name in current locale, and yyyy is " + "full year number", "'Three Weeks Ago' (MMMM, yyyy)")); + newGroupValue = i18nc("Can be used to script translation of " + "\"'Three Weeks Ago' (MMMM, yyyy)\" with context @title:group Date", + "%1", newGroupValue); } else { - newGroupValue = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Earlier on %B, %Y")); + newGroupValue = fileTime.toString(i18nc("@title:group Date: " + "MMMM is full month name in current locale, and yyyy is " + "full year number", "'Earlier on' MMMM, yyyy")); + newGroupValue = i18nc("Can be used to script translation of " + "\"'Earlier on' MMMM, yyyy\" with context @title:group Date", + "%1", newGroupValue); } } else { - newGroupValue = modifiedTime.toString(i18nc("@title:group The month and year: %B is full month name in current locale, and %Y is full year number", "%B, %Y")); + newGroupValue = fileTime.toString(i18nc("@title:group " + "The month and year: MMMM is full month name in current locale, " + "and yyyy is full year number", "MMMM, yyyy")); + newGroupValue = i18nc("Can be used to script translation of " + "\"MMMM, yyyy\" with context @title:group Date", + "%1", newGroupValue); } } @@ -2135,25 +2253,34 @@ const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) { static const RoleInfoMap rolesInfoMap[] = { // | role | roleType | role translation | group translation | requires Baloo | requires indexer - { 0, NoRole, 0, 0, 0, 0, false, false }, - { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), 0, 0, false, false }, - { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), 0, 0, false, false }, - { "date", DateRole, I18N_NOOP2_NOSTRIP("@label", "Date"), 0, 0, false, false }, - { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), 0, 0, false, false }, - { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), 0, 0, true, false }, - { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), 0, 0, true, false }, - { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), 0, 0, true, false }, + { nullptr, NoRole, nullptr, nullptr, nullptr, nullptr, false, false }, + { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), nullptr, nullptr, false, false }, + { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), nullptr, nullptr, false, false }, + { "modificationtime", ModificationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Modified"), nullptr, nullptr, false, false }, + { "creationtime", CreationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Created"), nullptr, nullptr, false, false }, + { "accesstime", AccessTimeRole, I18N_NOOP2_NOSTRIP("@label", "Accessed"), nullptr, nullptr, false, false }, + { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), nullptr, nullptr, false, false }, + { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), nullptr, nullptr, true, false }, + { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), nullptr, nullptr, true, false }, + { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), nullptr, nullptr, true, false }, + { "title", TitleRole, I18N_NOOP2_NOSTRIP("@label", "Title"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, { "wordCount", WordCountRole, I18N_NOOP2_NOSTRIP("@label", "Word Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, { "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, - { "imageSize", ImageSizeRole, I18N_NOOP2_NOSTRIP("@label", "Image Size"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "imageDateTime", ImageDateTimeRole, I18N_NOOP2_NOSTRIP("@label", "Date Photographed"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "width", WidthRole, I18N_NOOP2_NOSTRIP("@label", "Width"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "height", HeightRole, I18N_NOOP2_NOSTRIP("@label", "Height"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "genre", GenreRole, I18N_NOOP2_NOSTRIP("@label", "Genre"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, { "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, { "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "bitrate", BitrateRole, I18N_NOOP2_NOSTRIP("@label", "Bitrate"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, { "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "releaseYear", ReleaseYearRole, I18N_NOOP2_NOSTRIP("@label", "Release Year"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, { "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "deletiontime",DeletionTimeRole,I18N_NOOP2_NOSTRIP("@label", "Deletion Time"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "copiedFrom", CopiedFromRole, I18N_NOOP2_NOSTRIP("@label", "Copied From"), I18N_NOOP2_NOSTRIP("@label", "Other"), true, false }, + { "originUrl", OriginUrlRole, I18N_NOOP2_NOSTRIP("@label", "Downloaded From"), I18N_NOOP2_NOSTRIP("@label", "Other"), true, false }, { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, { "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, { "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "User Group"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, @@ -2201,28 +2328,28 @@ QByteArray KFileItemModel::sharedValue(const QByteArray& value) bool KFileItemModel::isConsistent() const { // m_items may contain less items than m_itemData because m_items - // is populated lazily, see KFileItemModel::index(const KUrl& url). + // is populated lazily, see KFileItemModel::index(const QUrl& url). if (m_items.count() > m_itemData.count()) { return false; } - for (int i = 0; i < count(); ++i) { + for (int i = 0, iMax = count(); i < iMax; ++i) { // Check if m_items and m_itemData are consistent. const KFileItem item = fileItem(i); if (item.isNull()) { - qWarning() << "Item" << i << "is null"; + qCWarning(DolphinDebug) << "Item" << i << "is null"; return false; } const int itemIndex = index(item); if (itemIndex != i) { - qWarning() << "Item" << i << "has a wrong index:" << itemIndex; + qCWarning(DolphinDebug) << "Item" << i << "has a wrong index:" << itemIndex; return false; } // Check if the items are sorted correctly. - if (i > 0 && !lessThan(m_itemData.at(i - 1), m_itemData.at(i))) { - qWarning() << "The order of items" << i - 1 << "and" << i << "is wrong:" + if (i > 0 && !lessThan(m_itemData.at(i - 1), m_itemData.at(i), m_collator)) { + qCWarning(DolphinDebug) << "The order of items" << i - 1 << "and" << i << "is wrong:" << fileItem(i - 1) << fileItem(i); return false; } @@ -2232,13 +2359,13 @@ bool KFileItemModel::isConsistent() const const ItemData* parent = data->parent; if (parent) { if (expandedParentsCount(data) != expandedParentsCount(parent) + 1) { - qWarning() << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item; + qCWarning(DolphinDebug) << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item; return false; } const int parentIndex = index(parent->item); if (parentIndex >= i) { - qWarning() << "Index" << parentIndex << "of parent" << parent->item << "is not smaller than index" << i << "of child" << data->item; + qCWarning(DolphinDebug) << "Index" << parentIndex << "of parent" << parent->item << "is not smaller than index" << i << "of child" << data->item; return false; } } @@ -2246,4 +2373,3 @@ bool KFileItemModel::isConsistent() const return true; } -