]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodel.cpp
Remove unused #include
[dolphin.git] / src / kitemviews / kfileitemmodel.cpp
index 4d5879f4da02781c9ea57f22841a804007529b85..70014e1a758ba754570222abc70a3e0aa245bc89 100644 (file)
 
 #include "kfileitemmodel.h"
 
-#include <KGlobalSettings>
-#include <KLocale>
-#include <KStringHandler>
-#include <KDebug>
-#include <kstringhandler_deprecated.h> //TODO: port to QCollator
+#include "dolphin_generalsettings.h"
+
+#include <KLocalizedString>
+#include <KUrlMimeData>
+
+#include "dolphindebug.h"
 
 #include "private/kfileitemmodelsortalgorithm.h"
 #include "private/kfileitemmodeldirlister.h"
 
-#include <QApplication>
+#include <QElapsedTimer>
 #include <QMimeData>
 #include <QTimer>
 #include <QWidget>
 
-#include <algorithm>
-#include <vector>
-
 // #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 +78,7 @@ KFileItemModel::KFileItemModel(QObject* parent) :
     connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)()>(&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<void(KFileItemModelDirLister::*)(const QUrl&, const QUrl&)>(&KFileItemModelDirLister::redirection), this, &KFileItemModel::directoryRedirection);
     connect(m_dirLister, &KFileItemModelDirLister::urlIsFileError, this, &KFileItemModel::urlIsFileError);
 
@@ -89,6 +90,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 +108,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<KUrl, KUrl> expandedDirs(m_expandedDirs);
+    QHashIterator<QUrl, QUrl> expandedDirs(m_expandedDirs);
     while (expandedDirs.hasNext()) {
         expandedDirs.next();
         m_dirLister->openUrl(expandedDirs.value(), KDirLister::Reload);
@@ -134,7 +135,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 +191,9 @@ bool KFileItemModel::setData(int index, const QHash<QByteArray, QVariant>& 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 +246,12 @@ 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 <faure@kde.org>
-    KUrl::List urls;
-    KUrl::List mostLocalUrls;
+    QList<QUrl> urls;
+    QList<QUrl> mostLocalUrls;
     bool canUseMostLocalUrls = true;
-    const ItemData* lastAddedItem = 0;
+    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,7 +267,7 @@ 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);
@@ -275,13 +277,7 @@ QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const
         }
     }
 
-    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 +327,33 @@ QList<QPair<int, QVariant> > 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 +369,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 +380,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 +403,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 +422,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<KUrl, int> indexesForUrl;
+            QMultiHash<QUrl, int> 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 +529,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<KUrl::List>();
-        foreach (const KUrl& url, previouslyExpandedChildren) {
-            m_urlsToExpand.insert(url);
+        const QVariantList previouslyExpandedChildren = m_itemData.at(index)->values.value("previouslyExpandedChildren").value<QVariantList>();
+        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 +559,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 +610,48 @@ int KFileItemModel::expandedParentsCount(int index) const
     return 0;
 }
 
-QSet<KUrl> KFileItemModel::expandedDirectories() const
+QSet<QUrl> KFileItemModel::expandedDirectories() const
 {
-    return m_expandedDirs.values().toSet();
+    QSet<QUrl> result;
+    const auto dirs = m_expandedDirs;
+    for (const auto &dir : dirs) {
+        result.insert(dir);
+    }
+    return result;
 }
 
-void KFileItemModel::restoreExpandedDirectories(const QSet<KUrl>& urls)
+void KFileItemModel::restoreExpandedDirectories(const QSet<QUrl> &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<KUrl> it2(m_urlsToExpand);
+    QSetIterator<QUrl> it2(m_urlsToExpand);
     while (it2.hasNext()) {
         const int idx = index(it2.next());
         if (idx >= 0 && !isExpanded(idx)) {
@@ -783,6 +813,27 @@ 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();
+    }
+}
+
 void KFileItemModel::resortAllItems()
 {
     m_resortAllItemsTimer->stop();
@@ -795,14 +846,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<KUrl> oldUrls;
+    QList<QUrl> oldUrls;
     oldUrls.reserve(itemCount);
     foreach (const ItemData* itemData, m_itemData) {
         oldUrls.append(itemData->item.url());
@@ -858,7 +909,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 +922,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 +950,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 +967,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 +1064,7 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
 {
     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 +1130,10 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
 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 +1154,9 @@ void KFileItemModel::slotClear()
     m_expandedDirs.clear();
 }
 
-void KFileItemModel::slotNaturalSortingChanged()
+void KFileItemModel::slotSortingChoiceChanged()
 {
-    m_naturalSorting = KGlobalSettings::naturalSorting();
+    loadSortingSettings();
     resortAllItems();
 }
 
@@ -1127,8 +1177,8 @@ void KFileItemModel::insertItems(QList<ItemData*>& 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 +1195,7 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
     sort(newItems.begin(), newItems.end());
 
 #ifdef KFILEITEMMODEL_DEBUG
-    kDebug() << "[TIME] Sorting:" << timer.elapsed();
+    qCDebug(DolphinDebug) << "[TIME] Sorting:" << timer.elapsed();
 #endif
 
     KItemRangeList itemRanges;
@@ -1174,7 +1224,7 @@ void KFileItemModel::insertItems(QList<ItemData*>& 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 +1253,13 @@ void KFileItemModel::insertItems(QList<ItemData*>& 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
 }
 
@@ -1256,13 +1306,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::ItemData*> KFileItemModel::createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const
+QList<KFileItemModel::ItemData*> 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
@@ -1295,6 +1345,7 @@ void KFileItemModel::prepareItemsForSorting(QList<ItemData*>& 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 +1441,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 +1498,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 +1525,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 +1553,10 @@ QHash<QByteArray, QVariant> 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 +1565,28 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
         data.insert(sharedValue("size"), item.size());
     }
 
-    if (m_requestRole[DateRole]) {
+    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);
-        data.insert(sharedValue("date"), dateTime);
+        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);
+        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);
+        data.insert(sharedValue("accesstime"), dateTime);
     }
 
     if (m_requestRole[PermissionsRole]) {
@@ -1531,7 +1604,7 @@ QHash<QByteArray, QVariant> 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 +1632,14 @@ QHash<QByteArray, QVariant> 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 +1665,7 @@ QHash<QByteArray, QVariant> 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,7 +1710,7 @@ 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;
 }
@@ -1640,24 +1721,46 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
 class KFileItemModelLessThan
 {
 public:
-    KFileItemModelLessThan(const KFileItemModel* model) :
-        m_model(model)
+    KFileItemModelLessThan(const KFileItemModel* model, const QCollator& collator) :
+        m_model(model),
+        m_collator(collator)
+    {
+    }
+
+    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);
+        return m_model->lessThan(a, b, m_collator);
     }
 
 private:
     const KFileItemModel* m_model;
+    QCollator m_collator;
 };
 
 void KFileItemModel::sort(QList<KFileItemModel::ItemData*>::iterator begin,
                           QList<KFileItemModel::ItemData*>::iterator end) const
 {
-    KFileItemModelLessThan lessThan(this);
+    KFileItemModelLessThan lessThan(this, m_collator);
 
     if (m_sortRole == NameRole) {
         // Sorting by name can be expensive, in particular if natural sorting is
@@ -1672,7 +1775,7 @@ void KFileItemModel::sort(QList<KFileItemModel::ItemData*>::iterator begin,
     }
 }
 
-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,7 +1819,7 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
         break;
     }
 
-    case DateRole: {
+    case ModificationTimeRole: {
         const QDateTime dateTimeA = itemA.time(KFileItem::ModificationTime);
         const QDateTime dateTimeB = itemB.time(KFileItem::ModificationTime);
         if (dateTimeA < dateTimeB) {
@@ -1727,6 +1830,28 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
         break;
     }
 
+    case CreationTimeRole: {
+        const QDateTime dateTimeA = itemA.time(KFileItem::CreationTime);
+        const QDateTime dateTimeB = itemB.time(KFileItem::CreationTime);
+        if (dateTimeA < dateTimeB) {
+            result = -1;
+        } else if (dateTimeA > dateTimeB) {
+            result = +1;
+        }
+        break;
+    }
+
+    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: {
         result = a->values.value("rating").toInt() - b->values.value("rating").toInt();
         break;
@@ -1735,9 +1860,8 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
     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);
+        result = collator.compare(a->values.value("imageSize").toString(),
+                                  b->values.value("imageSize").toString());
         break;
     }
 
@@ -1756,14 +1880,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 +1897,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 <peter.penz@gmx.at>
-    // Copyright (C) 2006 by Dominic Battre <dominic@battre.de>
-    // Copyright (C) 2006 by Martin Pool <mbp@canonical.com>
+    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 +1919,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<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
 {
     Q_ASSERT(!m_itemData.isEmpty());
@@ -1833,12 +1946,17 @@ QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
             if (newFirstChar.isLetter()) {
                 // Try to find a matching group in the range 'A' to 'Z'.
                 static std::vector<QChar> 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<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan);
                 if (it != lettersAtoZ.end()) {
                     if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) {
@@ -1903,7 +2021,7 @@ QList<QPair<int, QVariant> > KFileItemModel::sizeRoleGroups() const
     return groups;
 }
 
-QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
+QList<QPair<int, QVariant> > KFileItemModel::timeRoleGroups(std::function<QDateTime(const ItemData *)> fileTimeCb) const
 {
     Q_ASSERT(!m_itemData.isEmpty());
 
@@ -1912,31 +2030,37 @@ QList<QPair<int, QVariant> > 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 +2081,60 @@ QList<QPair<int, QVariant> > 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 +2297,33 @@ 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  },
+        { "imageDateTime",   ImageDateTimeRole,   I18N_NOOP2_NOSTRIP("@label", "Date Photographed"),       I18N_NOOP2_NOSTRIP("@label", "Image"),    true,  true  },
         { "imageSize",   ImageSizeRole,   I18N_NOOP2_NOSTRIP("@label", "Image Size"),       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 +2371,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 +2402,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 +2416,3 @@ bool KFileItemModel::isConsistent() const
 
     return true;
 }
-