]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kfileitemmodel.cpp
Merge remote-tracking branch 'origin/KDE/4.11'
[dolphin.git] / src / kitemviews / kfileitemmodel.cpp
index 51ff142e796a381ba16faae5533f32e522083157..ea7ac2fc1bdf166a922de70f70c18b718d344c1d 100644 (file)
@@ -152,7 +152,12 @@ int KFileItemModel::count() const
 QHash<QByteArray, QVariant> KFileItemModel::data(int index) const
 {
     if (index >= 0 && index < count()) {
-        return m_itemData.at(index)->values;
+        ItemData* data = m_itemData.at(index);
+        if (data->values.isEmpty()) {
+            data->values = retrieveData(data->item, data->parent);
+        }
+
+        return data->values;
     }
     return QHash<QByteArray, QVariant>();
 }
@@ -163,7 +168,7 @@ bool KFileItemModel::setData(int index, const QHash<QByteArray, QVariant>& value
         return false;
     }
 
-    QHash<QByteArray, QVariant> currentValues = m_itemData.at(index)->values;
+    QHash<QByteArray, QVariant> currentValues = data(index);
 
     // Determine which roles have been changed
     QSet<QByteArray> changedRoles;
@@ -274,12 +279,12 @@ int KFileItemModel::indexForKeyboardSearch(const QString& text, int startFromInd
 {
     startFromIndex = qMax(0, startFromIndex);
     for (int i = startFromIndex; i < count(); ++i) {
-        if (data(i)["text"].toString().startsWith(text, Qt::CaseInsensitive)) {
+        if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
             return i;
         }
     }
     for (int i = 0; i < startFromIndex; ++i) {
-        if (data(i)["text"].toString().startsWith(text, Qt::CaseInsensitive)) {
+        if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
             return i;
         }
     }
@@ -439,11 +444,18 @@ bool KFileItemModel::setExpanded(int index, bool expanded)
         m_expandedDirs.remove(targetUrl);
         m_dirLister->stop(url);
 
-        removeFilteredChildren(KFileItemList() << item);
+        const int parentLevel = expandedParentsCount(index);
+        const int itemCount = m_itemData.count();
+        const int firstChildIndex = index + 1;
+
+        int childIndex = firstChildIndex;
+        while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) {
+            ++childIndex;
+        }
+        const int childrenCount = childIndex - firstChildIndex;
 
-        const KFileItemList itemsToRemove = childItems(item);
-        removeFilteredChildren(itemsToRemove);
-        removeItems(itemsToRemove, DeleteItemData);
+        removeFilteredChildren(KItemRangeList() << KItemRange(index, 1 + childrenCount));
+        removeItems(KItemRangeList() << KItemRange(firstChildIndex, childrenCount), DeleteItemData);
     }
 
     return true;
@@ -460,7 +472,9 @@ bool KFileItemModel::isExpanded(int index) const
 bool KFileItemModel::isExpandable(int index) const
 {
     if (index >= 0 && index < count()) {
-        return m_itemData.at(index)->values.value("isExpandable").toBool();
+        // Call data (instead of accessing m_itemData directly)
+        // to ensure that the value is initialized.
+        return data(index).value("isExpandable").toBool();
     }
     return false;
 }
@@ -544,21 +558,25 @@ void KFileItemModel::applyFilters()
 {
     // Check which shown items from m_itemData must get
     // hidden and hence moved to m_filteredItems.
-    KFileItemList newFilteredItems;
+    QVector<int> newFilteredIndexes;
+
+    const int itemCount = m_itemData.count();
+    for (int index = 0; index < itemCount; ++index) {
+        ItemData* itemData = m_itemData.at(index);
 
-    foreach (ItemData* itemData, m_itemData) {
         // Only filter non-expanded items as child items may never
         // exist without a parent item
         if (!itemData->values.value("isExpanded").toBool()) {
             const KFileItem item = itemData->item;
             if (!m_filter.matches(item)) {
-                newFilteredItems.append(item);
+                newFilteredIndexes.append(index);
                 m_filteredItems.insert(item, itemData);
             }
         }
     }
 
-    removeItems(newFilteredItems, KeepItemData);
+    const KItemRangeList removedRanges = KItemRangeList::fromSortedContainer(newFilteredIndexes);
+    removeItems(removedRanges, KeepItemData);
 
     // Check which hidden items from m_filteredItems should
     // get visible again and hence removed from m_filteredItems.
@@ -577,22 +595,24 @@ void KFileItemModel::applyFilters()
     insertItems(newVisibleItems);
 }
 
-void KFileItemModel::removeFilteredChildren(const KFileItemList& parentsList)
+void KFileItemModel::removeFilteredChildren(const KItemRangeList& itemRanges)
 {
-    if (m_filteredItems.isEmpty()) {
+    if (m_filteredItems.isEmpty() || !m_requestRole[ExpandedParentsCountRole]) {
+        // There are either no filtered items, or it is not possible to expand
+        // folders -> there cannot be any filtered children.
         return;
     }
 
-    // First, we put the parent items into a set to provide fast lookup
-    // while iterating over m_filteredItems and prevent quadratic
-    // complexity if there are N parents and N filtered items.
-    const QSet<KFileItem> parents = parentsList.toSet();
+    QSet<ItemData*> parents;
+    foreach (const KItemRange& range, itemRanges) {
+        for (int index = range.index; index < range.index + range.count; ++index) {
+            parents.insert(m_itemData.at(index));
+        }
+    }
 
     QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.begin();
     while (it != m_filteredItems.end()) {
-        const ItemData* parent = it.value()->parent;
-
-        if (parent && parents.contains(parent->item)) {
+        if (parents.contains(it.value()->parent)) {
             delete it.value();
             it = m_filteredItems.erase(it);
         } else {
@@ -842,29 +862,49 @@ void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
 {
     dispatchPendingItemsToInsert();
 
-    KFileItemList itemsToRemove = items;
-    if (m_requestRole[ExpandedParentsCountRole]) {
-        // Assure that removing a parent item also results in removing all children
-        foreach (const KFileItem& item, items) {
-            itemsToRemove.append(childItems(item));
-        }
-    }
+    QVector<int> indexesToRemove;
+    indexesToRemove.reserve(items.count());
 
-    if (!m_filteredItems.isEmpty()) {
-        foreach (const KFileItem& item, itemsToRemove) {
+    foreach (const KFileItem& item, items) {
+        const KUrl url = item.url();
+        const int index = m_items.value(url, -1);
+        if (index >= 0) {
+            indexesToRemove.append(index);
+        } else {
+            // Probably the item has been filtered.
             QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.find(item);
             if (it != m_filteredItems.end()) {
                 delete it.value();
                 m_filteredItems.erase(it);
             }
         }
+    }
 
-        if (m_requestRole[ExpandedParentsCountRole]) {
-            removeFilteredChildren(itemsToRemove);
+    std::sort(indexesToRemove.begin(), indexesToRemove.end());
+
+    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());
+
+        const int itemCount = m_itemData.count();
+        foreach (int index, indexesToRemove) {
+            indexesToRemoveWithChildren.append(index);
+
+            const int parentLevel = expandedParentsCount(index);
+            int childIndex = index + 1;
+            while (childIndex < itemCount && expandedParentsCount(childIndex) > parentLevel) {
+                indexesToRemoveWithChildren.append(childIndex);
+                ++childIndex;
+            }
         }
+
+        indexesToRemove = indexesToRemoveWithChildren;
     }
 
-    removeItems(itemsToRemove, DeleteItemData);
+    const KItemRangeList itemRanges = KItemRangeList::fromSortedContainer(indexesToRemove);
+    removeFilteredChildren(itemRanges);
+    removeItems(itemRanges, DeleteItemData);
 }
 
 void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >& items)
@@ -916,30 +956,7 @@ void KFileItemModel::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&
 
     // Extract the item-ranges out of the changed indexes
     qSort(indexes);
-
-    KItemRangeList itemRangeList;
-    int previousIndex = indexes.at(0);
-    int rangeIndex = previousIndex;
-    int rangeCount = 1;
-
-    const int maxIndex = indexes.count() - 1;
-    for (int i = 1; i <= maxIndex; ++i) {
-        const int currentIndex = indexes.at(i);
-        if (currentIndex == previousIndex + 1) {
-            ++rangeCount;
-        } else {
-            itemRangeList.append(KItemRange(rangeIndex, rangeCount));
-
-            rangeIndex = currentIndex;
-            rangeCount = 1;
-        }
-        previousIndex = currentIndex;
-    }
-
-    if (rangeCount > 0) {
-        itemRangeList.append(KItemRange(rangeIndex, rangeCount));
-    }
-
+    const KItemRangeList itemRangeList = KItemRangeList::fromSortedContainer(indexes);
     emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles);
 }
 
@@ -1077,53 +1094,21 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
 #endif
 }
 
-static KItemRangeList sortedIndexesToKItemRangeList(const QList<int>& sortedNumbers)
+void KFileItemModel::removeItems(const KItemRangeList& itemRanges, RemoveItemsBehavior behavior)
 {
-    if (sortedNumbers.empty()) {
-        return KItemRangeList();
-    }
-
-    KItemRangeList result;
-
-    QList<int>::const_iterator it = sortedNumbers.begin();
-    int index = *it;
-    int count = 1;
-
-    ++it;
-
-    QList<int>::const_iterator end = sortedNumbers.end();
-    while (it != end) {
-        if (*it == index + count) {
-            ++count;
-        } else {
-            result << KItemRange(index, count);
-            index = *it;
-            count = 1;
-        }
-        ++it;
+    if (itemRanges.isEmpty()) {
+        return;
     }
 
-    result << KItemRange(index, count);
-    return result;
-}
-
-void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior behavior)
-{
-#ifdef KFILEITEMMODEL_DEBUG
-    kDebug() << "Removing " << items.count() << "items";
-#endif
-
     m_groups.clear();
 
-    // Step 1: Determine the indexes of the removed items, remove them from
-    //         the hash m_items, and free the ItemData.
-    QList<int> indexesToRemove;
-    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);
+    // Step 1: Remove the items from the hash m_items, 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().
@@ -1138,14 +1123,7 @@ void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior
         }
     }
 
-    if (indexesToRemove.isEmpty()) {
-        return;
-    }
-
-    std::sort(indexesToRemove.begin(), indexesToRemove.end());
-
     // Step 2: Remove the ItemData pointers from the list m_itemData.
-    const KItemRangeList itemRanges = sortedIndexesToKItemRangeList(indexesToRemove);
     int target = itemRanges.at(0).index;
     int source = itemRanges.at(0).index + itemRanges.at(0).count;
     int nextRange = 1;
@@ -1163,7 +1141,7 @@ void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior
         }
     }
 
-    m_itemData.erase(m_itemData.end() - indexesToRemove.count(), m_itemData.end());
+    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.
@@ -1193,11 +1171,42 @@ QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl&
     foreach (const KFileItem& item, items) {
         ItemData* itemData = new ItemData();
         itemData->item = item;
-        itemData->values = retrieveData(item, parentItem);
         itemData->parent = parentItem;
         itemDataList.append(itemData);
     }
 
+    switch (m_sortRole) {
+    case PermissionsRole:
+    case OwnerRole:
+    case GroupRole:
+    case DestinationRole:
+    case PathRole:
+        // 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);
+        }
+        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);
+            }
+        }
+        break;
+
+    default:
+        // 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,
+        // DateRole).
+        break;
+    }
+
     return itemDataList;
 }
 
@@ -1220,18 +1229,31 @@ int KFileItemModel::expandedParentsCount(const ItemData* data)
 
 void KFileItemModel::removeExpandedItems()
 {
-    KFileItemList expandedItems;
+    QVector<int> indexesToRemove;
 
     const int maxIndex = m_itemData.count() - 1;
     for (int i = 0; i <= maxIndex; ++i) {
         const ItemData* itemData = m_itemData.at(i);
         if (itemData->parent) {
-            expandedItems.append(itemData->item);
+            indexesToRemove.append(i);
         }
     }
 
-    removeItems(expandedItems, DeleteItemData);
+    removeItems(KItemRangeList::fromSortedContainer(indexesToRemove), DeleteItemData);
     m_expandedDirs.clear();
+
+    // Also remove all filtered items which have a parent.
+    QHash<KFileItem, ItemData*>::iterator it = m_filteredItems.begin();
+    const QHash<KFileItem, ItemData*>::iterator end = m_filteredItems.end();
+
+    while (it != end) {
+        if (it.value()->parent) {
+            delete it.value();
+            it = m_filteredItems.erase(it);
+        } else {
+            ++it;
+        }
+    }
 }
 
 void KFileItemModel::emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet<QByteArray>& changedRoles)
@@ -1682,7 +1704,7 @@ QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
             continue;
         }
 
-        const QString name = m_itemData.at(i)->values.value("text").toString();
+        const QString name = m_itemData.at(i)->item.text();
 
         // Use the first character of the name as group indication
         QChar newFirstChar = name.at(0).toUpper();
@@ -1965,23 +1987,6 @@ QList<QPair<int, QVariant> > KFileItemModel::genericStringRoleGroups(const QByte
     return groups;
 }
 
-KFileItemList KFileItemModel::childItems(const KFileItem& item) const
-{
-    KFileItemList items;
-
-    int index = m_items.value(item.url(), -1);
-    if (index >= 0) {
-        const int parentLevel = expandedParentsCount(index);
-        ++index;
-        while (index < m_itemData.count() && expandedParentsCount(index) > parentLevel) {
-            items.append(m_itemData.at(index)->item);
-            ++index;
-        }
-    }
-
-    return items;
-}
-
 void KFileItemModel::emitSortProgress(int resolvedCount)
 {
     // Be tolerant against a resolvedCount with a wrong range.