]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodel.cpp
port Dolphin from KUrl to QUrl
[dolphin.git] / src / kitemviews / kfileitemmodel.cpp
index f21edbf4a85d7d428bda2d75328697626afdc188..711b0797be1d416fc7b94694963516fdb542e8aa 100644 (file)
 
 #include "kfileitemmodel.h"
 
-#include <KDirModel>
 #include <KGlobalSettings>
-#include <KLocale>
+#include <KLocalizedString>
 #include <KStringHandler>
 #include <KDebug>
+#include <kstringhandler_deprecated.h> //TODO: port to QCollator
 
 #include "private/kfileitemmodelsortalgorithm.h"
 #include "private/kfileitemmodeldirlister.h"
 
-#include <QApplication>
 #include <QMimeData>
 #include <QTimer>
 #include <QWidget>
@@ -69,18 +68,17 @@ KFileItemModel::KFileItemModel(QObject* parent) :
         m_dirLister->setMainWindow(parentWidget->window());
     }
 
-    connect(m_dirLister, SIGNAL(started(KUrl)), this, SIGNAL(directoryLoadingStarted()));
-    connect(m_dirLister, SIGNAL(canceled()), this, SLOT(slotCanceled()));
-    connect(m_dirLister, SIGNAL(completed(KUrl)), this, SLOT(slotCompleted()));
-    connect(m_dirLister, SIGNAL(itemsAdded(KUrl,KFileItemList)), this, SLOT(slotItemsAdded(KUrl,KFileItemList)));
-    connect(m_dirLister, SIGNAL(itemsDeleted(KFileItemList)), this, SLOT(slotItemsDeleted(KFileItemList)));
-    connect(m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem,KFileItem> >)), this, SLOT(slotRefreshItems(QList<QPair<KFileItem,KFileItem> >)));
-    connect(m_dirLister, SIGNAL(clear()), this, SLOT(slotClear()));
-    connect(m_dirLister, SIGNAL(clear(KUrl)), this, SLOT(slotClear(KUrl)));
-    connect(m_dirLister, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString)));
-    connect(m_dirLister, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString)));
-    connect(m_dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SIGNAL(directoryRedirection(KUrl,KUrl)));
-    connect(m_dirLister, SIGNAL(urlIsFileError(KUrl)), this, SIGNAL(urlIsFileError(KUrl)));
+    connect(m_dirLister, &KFileItemModelDirLister::started, this, &KFileItemModel::directoryLoadingStarted);
+    connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)()>(&KFileItemModelDirLister::canceled), this, &KFileItemModel::slotCanceled);
+    connect(m_dirLister, static_cast<void(KFileItemModelDirLister::*)(const QUrl&)>(&KFileItemModelDirLister::completed), this, &KFileItemModel::slotCompleted);
+    connect(m_dirLister, &KFileItemModelDirLister::itemsAdded, this, &KFileItemModel::slotItemsAdded);
+    connect(m_dirLister, &KFileItemModelDirLister::itemsDeleted, this, &KFileItemModel::slotItemsDeleted);
+    connect(m_dirLister, &KFileItemModelDirLister::refreshItems, this, &KFileItemModel::slotRefreshItems);
+    connect(m_dirLister, static_cast<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, static_cast<void(KFileItemModelDirLister::*)(const QUrl&, const QUrl&)>(&KFileItemModelDirLister::redirection), this, &KFileItemModel::directoryRedirection);
+    connect(m_dirLister, &KFileItemModelDirLister::urlIsFileError, this, &KFileItemModel::urlIsFileError);
 
     // Apply default roles that should be determined
     resetRoles();
@@ -96,7 +94,7 @@ KFileItemModel::KFileItemModel(QObject* parent) :
     m_maximumUpdateIntervalTimer = new QTimer(this);
     m_maximumUpdateIntervalTimer->setInterval(2000);
     m_maximumUpdateIntervalTimer->setSingleShot(true);
-    connect(m_maximumUpdateIntervalTimer, SIGNAL(timeout()), this, SLOT(dispatchPendingItemsToInsert()));
+    connect(m_maximumUpdateIntervalTimer, &QTimer::timeout, this, &KFileItemModel::dispatchPendingItemsToInsert);
 
     // When changing the value of an item which represents the sort-role a resorting must be
     // triggered. Especially in combination with KFileItemModelRolesUpdater this might be done
@@ -105,9 +103,10 @@ KFileItemModel::KFileItemModel(QObject* parent) :
     m_resortAllItemsTimer = new QTimer(this);
     m_resortAllItemsTimer->setInterval(500);
     m_resortAllItemsTimer->setSingleShot(true);
-    connect(m_resortAllItemsTimer, SIGNAL(timeout()), this, SLOT(resortAllItems()));
+    connect(m_resortAllItemsTimer, &QTimer::timeout, this, &KFileItemModel::resortAllItems);
 
-    connect(KGlobalSettings::self(), SIGNAL(naturalSortingChanged()), this, SLOT(slotNaturalSortingChanged()));
+    connect(KGlobalSettings::self(), &KGlobalSettings::naturalSortingChanged,
+            this, &KFileItemModel::slotNaturalSortingChanged);
 }
 
 KFileItemModel::~KFileItemModel()
@@ -117,15 +116,15 @@ KFileItemModel::~KFileItemModel()
     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 +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<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);
     }
 
@@ -218,7 +218,7 @@ void KFileItemModel::setShowHiddenFiles(bool show)
     m_dirLister->setShowingDotFiles(show);
     m_dirLister->emitChanges();
     if (show) {
-        slotCompleted();
+        dispatchPendingItemsToInsert();
     }
 }
 
@@ -237,21 +237,33 @@ bool KFileItemModel::showDirectoriesOnly() const
     return m_dirLister->dirOnlyMode();
 }
 
-QMimeData* KFileItemModel::createMimeData(const QSet<int>& indexes) const
+QMimeData* KFileItemModel::createMimeData(const KItemSet& indexes) const
 {
     QMimeData* data = new QMimeData();
 
     // 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;
 
-    QSetIterator<int> it(indexes);
-    while (it.hasNext()) {
-        const int index = it.next();
-        const KFileItem item = fileItem(index);
+    foreach (int index, indexes) {
+        const ItemData* itemData = m_itemData.at(index);
+        const ItemData* parent = itemData->parent;
+
+        while (parent && parent != lastAddedItem) {
+            parent = parent->parent;
+        }
+
+        if (parent && parent == lastAddedItem) {
+            // A parent of 'itemData' has been added already.
+            continue;
+        }
+
+        lastAddedItem = itemData;
+        const KFileItem& item = itemData->item;
         if (!item.isNull()) {
             urls << item.targetUrl();
 
@@ -264,12 +276,10 @@ QMimeData* KFileItemModel::createMimeData(const QSet<int>& indexes) const
     }
 
     const bool different = canUseMostLocalUrls && mostLocalUrls != urls;
-    urls = KDirModel::simplifiedUrlList(urls); // TODO: Check if we still need KDirModel for this in KDE 5.0
     if (different) {
-        mostLocalUrls = KDirModel::simplifiedUrlList(mostLocalUrls);
-        urls.populateMimeData(mostLocalUrls, data);
+        data->setUrls(mostLocalUrls);
     } else {
-        urls.populateMimeData(data);
+        data->setUrls(urls);
     }
 
     return data;
@@ -344,29 +354,85 @@ KFileItem KFileItemModel::fileItem(int index) const
     return KFileItem();
 }
 
-KFileItem KFileItemModel::fileItem(const KUrl& url) const
+KFileItem KFileItemModel::fileItem(const QUrl &url) const
 {
-    const int index = m_items.value(url, -1);
-    if (index >= 0) {
-        return m_itemData.at(index)->item;
+    const int indexForUrl = index(url);
+    if (indexForUrl >= 0) {
+        return m_itemData.at(indexForUrl)->item;
     }
     return KFileItem();
 }
 
 int KFileItemModel::index(const KFileItem& item) const
 {
-    if (item.isNull()) {
-        return -1;
-    }
-
-    return m_items.value(item.url(), -1);
+    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);
-    return m_items.value(urlToFind, -1);
+    const QUrl urlToFind = url.adjusted(QUrl::StripTrailingSlash);
+
+    const int itemCount = m_itemData.count();
+    int itemsInHash = m_items.count();
+
+    int index = m_items.value(urlToFind, -1);
+    while (index < 0 && itemsInHash < itemCount) {
+        // Not all URLs are stored yet in m_items. We grow m_items until either
+        // urlToFind is found, or all URLs have been stored in m_items.
+        // Note that we do not add the URLs to m_items one by one, but in
+        // larger blocks. After each block, we check if urlToFind is in
+        // m_items. We could in principle compare urlToFind with each URL while
+        // we are going through m_itemData, but comparing two QUrls will,
+        // unlike calling qHash for the URLs, trigger a parsing of the URLs
+        // which costs both CPU cycles and memory.
+        const int blockSize = 1000;
+        const int currentBlockEnd = qMin(itemsInHash + blockSize, itemCount);
+        for (int i = itemsInHash; i < currentBlockEnd; ++i) {
+            const QUrl nextUrl = m_itemData.at(i)->item.url();
+            m_items.insert(nextUrl, i);
+        }
+
+        itemsInHash = currentBlockEnd;
+        index = m_items.value(urlToFind, -1);
+    }
+
+    if (index < 0) {
+        // The item could not be found, even though all items from m_itemData
+        // should be in m_items now. We print some diagnostic information which
+        // might help to find the cause of the problem, but only once. This
+        // prevents that obtaining and printing the debugging information
+        // wastes CPU cycles and floods the shell or .xsession-errors.
+        static bool printDebugInfo = true;
+
+        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();
+
+            // Check if there are multiple items with the same URL.
+            QMultiHash<QUrl, int> indexesForUrl;
+            for (int i = 0; i < m_itemData.count(); ++i) {
+                indexesForUrl.insert(m_itemData.at(i)->item.url(), i);
+            }
+
+            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;
+                        if (data->parent) {
+                            kWarning() << "parent" << data->parent->item;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return index;
 }
 
 KFileItem KFileItemModel::rootItem() const
@@ -384,6 +450,8 @@ void KFileItemModel::setRoles(const QSet<QByteArray>& roles)
     if (m_roles == roles) {
         return;
     }
+
+    const QSet<QByteArray> changedRoles = (roles - m_roles) + (m_roles - roles);
     m_roles = roles;
 
     if (count() > 0) {
@@ -412,8 +480,16 @@ void KFileItemModel::setRoles(const QSet<QByteArray>& roles)
             m_itemData[i]->values = retrieveData(m_itemData.at(i)->item, m_itemData.at(i)->parent);
         }
 
-        kWarning() << "TODO: Emitting itemsChanged() with no information what has changed!";
-        emit itemsChanged(KItemRangeList() << KItemRange(0, count()), QSet<QByteArray>());
+        emit itemsChanged(KItemRangeList() << KItemRange(0, count()), changedRoles);
+    }
+
+    // Clear the 'values' of all filtered items. They will be re-populated with the
+    // correct roles the next time 'values' will be accessed via data(int).
+    QHash<KFileItem, ItemData*>::iterator filteredIt = m_filteredItems.begin();
+    const QHash<KFileItem, ItemData*>::iterator filteredEnd = m_filteredItems.end();
+    while (filteredIt != filteredEnd) {
+        (*filteredIt)->values.clear();
+        ++filteredIt;
     }
 }
 
@@ -435,17 +511,29 @@ 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
+        // m_pendingItemsToInsert. To prevent that they will be inserted into the model later,
+        // possibly without a parent, which might result in a crash, we insert all pending items
+        // right now. All new items which would be without a parent will then be removed.
+        dispatchPendingItemsToInsert();
+
+        // Check if the index of the collapsed folder has changed. If that is the case, then items
+        // were inserted before the collapsed folder, and its index needs to be updated.
+        if (m_itemData.at(index)->item != item) {
+            index = this->index(item);
+        }
+
         m_expandedDirs.remove(targetUrl);
         m_dirLister->stop(url);
 
@@ -453,14 +541,16 @@ 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 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);
             }
             ++childIndex;
@@ -502,17 +592,17 @@ int KFileItemModel::expandedParentsCount(int index) const
     return 0;
 }
 
-QSet<KUrl> KFileItemModel::expandedDirectories() const
+QSet<QUrl> KFileItemModel::expandedDirectories() const
 {
     return m_expandedDirs.values().toSet();
 }
 
-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();
 
@@ -520,17 +610,17 @@ void KFileItemModel::expandParentDirectories(const KUrl& url)
     // expanded is added to m_urlsToExpand. KDirLister
     // does not care whether the parent-URL has already been
     // expanded.
-    KUrl urlToExpand = m_dirLister->url();
+    QUrl urlToExpand = m_dirLister->url();
     const QStringList subDirs = url.path().mid(pos).split(QDir::separator());
     for (int i = 0; i < subDirs.count() - 1; ++i) {
-        urlToExpand.addPath(subDirs.at(i));
+        urlToExpand.setPath(urlToExpand.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)) {
@@ -655,7 +745,7 @@ QList<KFileItemModel::RoleInfo> KFileItemModel::rolesInformation()
                     // menus tries to put the actions into sub menus otherwise.
                     info.group = QString();
                 }
-                info.requiresNepomuk = map[i].requiresNepomuk;
+                info.requiresBaloo = map[i].requiresBaloo;
                 info.requiresIndexer = map[i].requiresIndexer;
                 rolesInfo.append(info);
             }
@@ -711,13 +801,14 @@ void KFileItemModel::resortAllItems()
     // 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());
     }
 
     m_items.clear();
+    m_items.reserve(itemCount);
 
     // Resort the items
     sort(m_itemData.begin(), m_itemData.end());
@@ -779,11 +870,11 @@ 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) {
-            const int index = m_items.value(url, -1);
-            if (index >= 0) {
+        foreach (const QUrl& url, m_urlsToExpand) {
+            const int indexForUrl = index(url);
+            if (indexForUrl >= 0) {
                 m_urlsToExpand.remove(url);
-                if (setExpanded(index, true)) {
+                if (setExpanded(indexForUrl, true)) {
                     // The dir lister has been triggered. This slot will be called
                     // again after the directory has been expanded.
                     return;
@@ -807,28 +898,24 @@ 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]) {
-        KFileItem item = items.first();
-
         // If the expanding of items is enabled, the call
         // dirLister->openUrl(url, KDirLister::Keep) in KFileItemModel::setExpanded()
         // 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.
-        const int index = m_items.value(item.url(), -1);
-        if (index >= 0) {
+        if (index(items.first().url()) >= 0) {
             // The items are already part of the model.
             return;
         }
@@ -842,7 +929,7 @@ void KFileItemModel::slotItemsAdded(const KUrl& directoryUrl, const KFileItemLis
         // KDirLister keeps the children of items that got expanded once even if
         // they got collapsed again with KFileItemModel::setExpanded(false). So it must be
         // checked whether the parent for new items is still expanded.
-        const int parentIndex = m_items.value(parentUrl, -1);
+        const int parentIndex = index(parentUrl);
         if (parentIndex >= 0 && !m_itemData[parentIndex]->values.value("isExpanded").toBool()) {
             // The parent is not expanded.
             return;
@@ -881,10 +968,9 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
     indexesToRemove.reserve(items.count());
 
     foreach (const KFileItem& item, items) {
-        const KUrl url = item.url();
-        const int index = m_items.value(url, -1);
-        if (index >= 0) {
-            indexesToRemove.append(index);
+        const int indexForItem = index(item);
+        if (indexForItem >= 0) {
+            indexesToRemove.append(indexForItem);
         } else {
             // Probably the item has been filtered.
             QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.find(item);
@@ -900,7 +986,7 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
     if (m_requestRole[ExpandedParentsCountRole] && !m_expandedDirs.isEmpty()) {
         // Assure that removing a parent item also results in removing all children
         QVector<int> indexesToRemoveWithChildren;
-        indexesToRemoveWithChildren.reserve(m_items.count());
+        indexesToRemoveWithChildren.reserve(m_itemData.count());
 
         const int itemCount = m_itemData.count();
         foreach (int index, indexesToRemove) {
@@ -940,14 +1026,14 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
         const QPair<KFileItem, KFileItem>& itemPair = it.next();
         const KFileItem& oldItem = itemPair.first;
         const KFileItem& newItem = itemPair.second;
-        const int index = m_items.value(oldItem.url(), -1);
-        if (index >= 0) {
-            m_itemData[index]->item = newItem;
+        const int indexForItem = index(oldItem);
+        if (indexForItem >= 0) {
+            m_itemData[indexForItem]->item = newItem;
 
             // Keep old values as long as possible if they could not retrieved synchronously yet.
             // The update of the values will be done asynchronously by KFileItemModelRolesUpdater.
-            QHashIterator<QByteArray, QVariant> it(retrieveData(newItem, m_itemData.at(index)->parent));
-            QHash<QByteArray, QVariant>& values = m_itemData[index]->values;
+            QHashIterator<QByteArray, QVariant> it(retrieveData(newItem, m_itemData.at(indexForItem)->parent));
+            QHash<QByteArray, QVariant>& values = m_itemData[indexForItem]->values;
             while (it.hasNext()) {
                 it.next();
                 const QByteArray& role = it.key();
@@ -958,8 +1044,22 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
             }
 
             m_items.remove(oldItem.url());
-            m_items.insert(newItem.url(), index);
-            indexes.append(index);
+            m_items.insert(newItem.url(), indexForItem);
+            indexes.append(indexForItem);
+        } else {
+            // Check if 'oldItem' is one of the filtered items.
+            QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.find(oldItem);
+            if (it != m_filteredItems.end()) {
+                ItemData* itemData = it.value();
+                itemData->item = newItem;
+
+                // The data stored in 'values' might have changed. Therefore, we clear
+                // 'values' and re-populate it the next time it is requested via data(int).
+                itemData->values.clear();
+
+                m_filteredItems.erase(it);
+                m_filteredItems.insert(newItem, itemData);
+            }
         }
     }
 
@@ -1002,11 +1102,6 @@ void KFileItemModel::slotClear()
     m_expandedDirs.clear();
 }
 
-void KFileItemModel::slotClear(const KUrl& url)
-{
-    Q_UNUSED(url);
-}
-
 void KFileItemModel::slotNaturalSortingChanged()
 {
     m_naturalSorting = KGlobalSettings::naturalSorting();
@@ -1035,6 +1130,15 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
 #endif
 
     m_groups.clear();
+    prepareItemsForSorting(newItems);
+
+    if (m_sortRole == NameRole && m_naturalSorting) {
+        // Natural sorting of items can be very slow. However, it becomes much
+        // faster if the input sequence is already mostly sorted. Therefore, we
+        // first sort 'newItems' according to the QStrings returned by
+        // KFileItem::text() using QString::operator<(), which is quite fast.
+        parallelMergeSort(newItems.begin(), newItems.end(), nameLessThan, QThread::idealThreadCount());
+    }
 
     sort(newItems.begin(), newItems.end());
 
@@ -1058,7 +1162,7 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
             m_itemData.append(0);
         }
 
-        // We build the new list m_items in reverse order to minimize
+        // We build the new list m_itemData in reverse order to minimize
         // the number of moves and guarantee O(N) complexity.
         int targetIndex = totalItemCount - 1;
         int sourceIndexExistingItems = existingItemCount - 1;
@@ -1096,11 +1200,9 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
         std::reverse(itemRanges.begin(), itemRanges.end());
     }
 
-    // The indexes starting from the first inserted item must be adjusted.
-    m_items.reserve(totalItemCount);
-    for (int i = itemRanges.front().index; i < totalItemCount; ++i) {
-        m_items.insert(m_itemData.at(i)->item.url(), i);
-    }
+    // 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 QUrl&) is called.
+    m_items.clear();
 
     emit itemsInserted(itemRanges);
 
@@ -1117,19 +1219,12 @@ void KFileItemModel::removeItems(const KItemRangeList& itemRanges, RemoveItemsBe
 
     m_groups.clear();
 
-    // Step 1: Remove the items from the hash m_items, and free the ItemData.
+    // Step 1: Remove the items from m_itemData, and free the ItemData.
     int removedItemsCount = 0;
     foreach (const KItemRange& range, itemRanges) {
         removedItemsCount += range.count;
 
         for (int index = range.index; index < range.index + range.count; ++index) {
-            const KUrl url = m_itemData.at(index)->item.url();
-
-            // Prevent repeated expensive rehashing by using QHash::erase(),
-            // rather than QHash::remove().
-            QHash<KUrl, int>::iterator it = m_items.find(url);
-            m_items.erase(it);
-
             if (behavior == DeleteItemData) {
                 delete m_itemData.at(index);
             }
@@ -1158,17 +1253,14 @@ void KFileItemModel::removeItems(const KItemRangeList& itemRanges, RemoveItemsBe
 
     m_itemData.erase(m_itemData.end() - removedItemsCount, m_itemData.end());
 
-    // Step 3: Adjust indexes in the hash m_items, starting from the
-    //         index of the first removed item.
-    const int newItemDataCount = m_itemData.count();
-    for (int i = itemRanges.front().index; i < newItemDataCount; ++i) {
-        m_items.insert(m_itemData.at(i)->item.url(), i);
-    }
+    // 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 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
@@ -1177,7 +1269,7 @@ QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl&
         determineMimeTypes(items, 200);
     }
 
-    const int parentIndex = m_items.value(parentUrl, -1);
+    const int parentIndex = index(parentUrl);
     ItemData* parentItem = parentIndex < 0 ? 0 : m_itemData.at(parentIndex);
 
     QList<ItemData*> itemDataList;
@@ -1190,6 +1282,11 @@ QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl&
         itemDataList.append(itemData);
     }
 
+    return itemDataList;
+}
+
+void KFileItemModel::prepareItemsForSorting(QList<ItemData*>& itemDataList)
+{
     switch (m_sortRole) {
     case PermissionsRole:
     case OwnerRole:
@@ -1199,16 +1296,20 @@ QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl&
         // These roles can be determined with retrieveData, and they have to be stored
         // in the QHash "values" for the sorting.
         foreach (ItemData* itemData, itemDataList) {
-            itemData->values = retrieveData(itemData->item, parentItem);
+            if (itemData->values.isEmpty()) {
+                itemData->values = retrieveData(itemData->item, itemData->parent);
+            }
         }
         break;
 
     case TypeRole:
         // At least store the data including the file type for items with known MIME type.
         foreach (ItemData* itemData, itemDataList) {
-            const KFileItem item = itemData->item;
-            if (item.isDir() || item.isMimeTypeKnown()) {
-                itemData->values = retrieveData(itemData->item, parentItem);
+            if (itemData->values.isEmpty()) {
+                const KFileItem item = itemData->item;
+                if (item.isDir() || item.isMimeTypeKnown()) {
+                    itemData->values = retrieveData(itemData->item, itemData->parent);
+                }
             }
         }
         break;
@@ -1217,12 +1318,10 @@ QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl&
         // The other roles are either resolved by KFileItemModelRolesUpdater
         // (this includes the SizeRole for directories), or they do not need
         // to be stored in the QHash "values" for sorting because the data can
-        // be retrieved directly from the KFileItem (NameRole, SiezRole for files,
+        // be retrieved directly from the KFileItem (NameRole, SizeRole for files,
         // DateRole).
         break;
     }
-
-    return itemDataList;
 }
 
 int KFileItemModel::expandedParentsCount(const ItemData* data)
@@ -1411,8 +1510,8 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
         // 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 KDateTime dateTime = item.time(KFileItem::ModificationTime);
-        data.insert(sharedValue("date"), dateTime.dateTime());
+        const QDateTime dateTime = item.time(KFileItem::ModificationTime);
+        data.insert(sharedValue("date"), dateTime);
     }
 
     if (m_requestRole[PermissionsRole]) {
@@ -1437,7 +1536,7 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
 
     if (m_requestRole[PathRole]) {
         QString path;
-        if (item.url().protocol() == QLatin1String("trash")) {
+        if (item.url().scheme() == QLatin1String("trash")) {
             path = item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA);
         } else {
             // For performance reasons cache the home-path in a static QString
@@ -1616,8 +1715,8 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const
     }
 
     case DateRole: {
-        const KDateTime dateTimeA = itemA.time(KFileItem::ModificationTime);
-        const KDateTime dateTimeB = itemB.time(KFileItem::ModificationTime);
+        const QDateTime dateTimeA = itemA.time(KFileItem::ModificationTime);
+        const QDateTime dateTimeB = itemB.time(KFileItem::ModificationTime);
         if (dateTimeA < dateTimeB) {
             result = -1;
         } else if (dateTimeA > dateTimeB) {
@@ -1809,7 +1908,7 @@ QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
     const int maxIndex = count() - 1;
     QList<QPair<int, QVariant> > groups;
 
-    const QDate currentDate = KDateTime::currentLocalDateTime().date();
+    const QDate currentDate = QDate::currentDate();
 
     QDate previousModifiedDate;
     QString groupValue;
@@ -1818,7 +1917,7 @@ QList<QPair<int, QVariant> > KFileItemModel::dateRoleGroups() const
             continue;
         }
 
-        const KDateTime modifiedTime = m_itemData.at(i)->item.time(KFileItem::ModificationTime);
+        const QDateTime modifiedTime = m_itemData.at(i)->item.time(KFileItem::ModificationTime);
         const QDate modifiedDate = modifiedTime.date();
         if (modifiedDate == previousModifiedDate) {
             // The current item is in the same group as the previous item
@@ -1905,7 +2004,7 @@ QList<QPair<int, QVariant> > KFileItemModel::permissionRoleGroups() const
         }
         permissionsString = newPermissionsString;
 
-        const QFileInfo info(itemData->item.url().pathOrUrl());
+        const QFileInfo info(itemData->item.url().toLocalFile());
 
         // Set user string
         QString user;
@@ -2033,7 +2132,7 @@ void KFileItemModel::emitSortProgress(int resolvedCount)
 const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count)
 {
     static const RoleInfoMap rolesInfoMap[] = {
-    //  | role         | roleType       | role translation                                | group translation           | requires Nepomuk | requires indexer
+    //  | 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 },
@@ -2099,7 +2198,9 @@ QByteArray KFileItemModel::sharedValue(const QByteArray& value)
 
 bool KFileItemModel::isConsistent() const
 {
-    if (m_items.count() != m_itemData.count()) {
+    // m_items may contain less items than m_itemData because m_items
+    // is populated lazily, see KFileItemModel::index(const QUrl& url).
+    if (m_items.count() > m_itemData.count()) {
         return false;
     }
 
@@ -2144,4 +2245,3 @@ bool KFileItemModel::isConsistent() const
     return true;
 }
 
-#include "kfileitemmodel.moc"