]> 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 c0dedff396d98f375358e1439a7cba994ce906d0..bd905bf07da126d8ddf2d1c3fe7a49e0c7bc3ac9 100644 (file)
@@ -190,11 +190,7 @@ bool KFileItemModel::setData(int index, const QHash<QByteArray, QVariant>& value
         m_itemData[index]->item.setUrl(url);
     }
 
-    emit itemsChanged(KItemRangeList() << KItemRange(index, 1), changedRoles);
-
-    if (changedRoles.contains(sortRole())) {
-        m_resortAllItemsTimer->start();
-    }
+    emitItemsChangedAndTriggerResorting(KItemRangeList() << KItemRange(index, 1), changedRoles);
 
     return true;
 }
@@ -472,10 +468,7 @@ bool KFileItemModel::isExpandable(int index) const
 int KFileItemModel::expandedParentsCount(int index) const
 {
     if (index >= 0 && index < count()) {
-        const int parentsCount = m_itemData.at(index)->values.value("expandedParentsCount").toInt();
-        if (parentsCount > 0) {
-            return parentsCount;
-        }
+        return expandedParentsCount(m_itemData.at(index));
     }
     return 0;
 }
@@ -689,7 +682,6 @@ void KFileItemModel::resortAllItems()
         oldUrls.append(itemData->item.url());
     }
 
-    m_groups.clear();
     m_items.clear();
 
     // Resort the items
@@ -698,20 +690,45 @@ void KFileItemModel::resortAllItems()
         m_items.insert(m_itemData.at(i)->item.url(), i);
     }
 
-    // Determine the indexes that have been moved
-    QList<int> movedToIndexes;
-    movedToIndexes.reserve(itemCount);
-    for (int i = 0; i < itemCount; i++) {
-        const int newIndex = m_items.value(oldUrls.at(i));
-        movedToIndexes.append(newIndex);
+    // Determine the first index that has been moved.
+    int firstMovedIndex = 0;
+    while (firstMovedIndex < itemCount
+           && firstMovedIndex == m_items.value(oldUrls.at(firstMovedIndex))) {
+        ++firstMovedIndex;
     }
 
-    // Don't check whether items have really been moved and always emit a
-    // itemsMoved() signal after resorting: In case of grouped items
-    // the groups might change even if the items themselves don't change their
-    // position. Let the receiver of the signal decide whether a check for moved
-    // items makes sense.
-    emit itemsMoved(KItemRange(0, itemCount), movedToIndexes);
+    const bool itemsHaveMoved = firstMovedIndex < itemCount;
+    if (itemsHaveMoved) {
+        m_groups.clear();
+
+        int lastMovedIndex = itemCount - 1;
+        while (lastMovedIndex > firstMovedIndex
+               && lastMovedIndex == m_items.value(oldUrls.at(lastMovedIndex))) {
+            --lastMovedIndex;
+        }
+
+        Q_ASSERT(firstMovedIndex <= lastMovedIndex);
+
+        // Create a list movedToIndexes, which has the property that
+        // movedToIndexes[i] is the new index of the item with the old index
+        // firstMovedIndex + i.
+        const int movedItemsCount = lastMovedIndex - firstMovedIndex + 1;
+        QList<int> movedToIndexes;
+        movedToIndexes.reserve(movedItemsCount);
+        for (int i = firstMovedIndex; i <= lastMovedIndex; ++i) {
+            const int newIndex = m_items.value(oldUrls.at(i));
+            movedToIndexes.append(newIndex);
+        }
+
+        emit itemsMoved(KItemRange(firstMovedIndex, movedItemsCount), movedToIndexes);
+    } else if (groupedSorting()) {
+        // The groups might have changed even if the order of the items has not.
+        const QList<QPair<int, QVariant> > oldGroups = m_groups;
+        m_groups.clear();
+        if (groups() != oldGroups) {
+            emit groupsChanged();
+        }
+    }
 
 #ifdef KFILEITEMMODEL_DEBUG
     kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
@@ -899,35 +916,8 @@ 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));
-    }
-
-    emit itemsChanged(itemRangeList, changedRoles);
-
-    if (changedRoles.contains(sortRole())) {
-        resortAllItems();
-    }
+    const KItemRangeList itemRangeList = KItemRangeList::fromSortedContainer(indexes);
+    emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles);
 }
 
 void KFileItemModel::slotClear()
@@ -1064,36 +1054,6 @@ void KFileItemModel::insertItems(QList<ItemData*>& newItems)
 #endif
 }
 
-static KItemRangeList sortedIndexesToKItemRangeList(const QList<int>& sortedNumbers)
-{
-    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;
-    }
-
-    result << KItemRange(index, count);
-    return result;
-}
-
 void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior behavior)
 {
 #ifdef KFILEITEMMODEL_DEBUG
@@ -1132,7 +1092,7 @@ void KFileItemModel::removeItems(const KFileItemList& items, RemoveItemsBehavior
     std::sort(indexesToRemove.begin(), indexesToRemove.end());
 
     // Step 2: Remove the ItemData pointers from the list m_itemData.
-    const KItemRangeList itemRanges = sortedIndexesToKItemRangeList(indexesToRemove);
+    const KItemRangeList itemRanges = KItemRangeList::fromSortedContainer(indexesToRemove);
     int target = itemRanges.at(0).index;
     int source = itemRanges.at(0).index + itemRanges.at(0).count;
     int nextRange = 1;
@@ -1188,6 +1148,23 @@ QList<KFileItemModel::ItemData*> KFileItemModel::createItemDataList(const KUrl&
     return itemDataList;
 }
 
+int KFileItemModel::expandedParentsCount(const ItemData* data)
+{
+    // The hash 'values' is only guaranteed to contain the key "expandedParentsCount"
+    // if the corresponding item is expanded, and it is not a top-level item.
+    const ItemData* parent = data->parent;
+    if (parent) {
+        if (parent->parent) {
+            Q_ASSERT(parent->values.contains("expandedParentsCount"));
+            return parent->values.value("expandedParentsCount").toInt() + 1;
+        } else {
+            return 1;
+        }
+    } else {
+        return 0;
+    }
+}
+
 void KFileItemModel::removeExpandedItems()
 {
     KFileItemList expandedItems;
@@ -1195,16 +1172,78 @@ void KFileItemModel::removeExpandedItems()
     const int maxIndex = m_itemData.count() - 1;
     for (int i = 0; i <= maxIndex; ++i) {
         const ItemData* itemData = m_itemData.at(i);
-        if (itemData->values.value("expandedParentsCount").toInt() > 0) {
+        if (itemData->parent) {
             expandedItems.append(itemData->item);
         }
     }
 
-    // The m_expandedParentsCountRoot may not get reset before all items with
-    // a bigger count have been removed.
     removeItems(expandedItems, 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)
+{
+    emit itemsChanged(itemRanges, changedRoles);
+
+    // Trigger a resorting if necessary. Note that this can happen even if the sort
+    // role has not changed at all because the file name can be used as a fallback.
+    if (changedRoles.contains(sortRole()) || changedRoles.contains(roleForType(NameRole))) {
+        foreach (const KItemRange& range, itemRanges) {
+            bool needsResorting = false;
+
+            const int first = range.index;
+            const int last = range.index + range.count - 1;
+
+            // Resorting the model is necessary if
+            // (a)  The first item in the range is "lessThan" its predecessor,
+            // (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))) {
+                needsResorting = true;
+            } else if (last < count() - 1
+                && lessThan(m_itemData.at(last + 1), m_itemData.at(last))) {
+                needsResorting = true;
+            } else {
+                for (int index = first; index < last; ++index) {
+                    if (lessThan(m_itemData.at(index + 1), m_itemData.at(index))) {
+                        needsResorting = true;
+                        break;
+                    }
+                }
+            }
+
+            if (needsResorting) {
+                m_resortAllItemsTimer->start();
+                return;
+            }
+        }
+    }
+
+    if (groupedSorting() && changedRoles.contains(sortRole())) {
+        // The position is still correct, but the groups might have changed
+        // if the changed item is either the first or the last item in a
+        // group.
+        // In principle, we could try to find out if the item really is the
+        // first or last one in its group and then update the groups
+        // (possibly with a delayed timer to make sure that we don't
+        // re-calculate the groups very often if items are updated one by
+        // one), but starting m_resortAllItemsTimer is easier.
+        m_resortAllItemsTimer->start();
+    }
 }
 
 void KFileItemModel::resetRoles()
@@ -1348,7 +1387,7 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
 
     if (m_requestRole[ExpandedParentsCountRole]) {
         if (parent) {
-            const int level = parent->values["expandedParentsCount"].toInt() + 1;
+            const int level = expandedParentsCount(parent) + 1;
             data.insert(sharedValue("expandedParentsCount"), level);
         }
     }
@@ -1359,6 +1398,9 @@ QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item,
         if (m_requestRole[TypeRole]) {
             data.insert(sharedValue("type"), item.mimeComment());
         }
+    } else if (m_requestRole[TypeRole] && isDir) {
+        static const QString folderMimeType = item.mimeComment();
+        data.insert(sharedValue("type"), folderMimeType);
     }
 
     return data;
@@ -1369,8 +1411,8 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
     int result = 0;
 
     if (a->parent != b->parent) {
-        const int expansionLevelA = a->values.value("expandedParentsCount").toInt();
-        const int expansionLevelB = b->values.value("expandedParentsCount").toInt();
+        const int expansionLevelA = expandedParentsCount(a);
+        const int expansionLevelB = expandedParentsCount(b);
 
         // If b has a higher expansion level than a, check if a is a parent
         // of b, and make sure that both expansion levels are equal otherwise.
@@ -1390,7 +1432,7 @@ bool KFileItemModel::lessThan(const ItemData* a, const ItemData* b) const
             a = a->parent;
         }
 
-        Q_ASSERT(a->values.value("expandedParentsCount").toInt() == b->values.value("expandedParentsCount").toInt());
+        Q_ASSERT(expandedParentsCount(a) == expandedParentsCount(b));
 
         // Compare the last parents of a and b which are different.
         while (a->parent != b->parent) {
@@ -1889,9 +1931,9 @@ KFileItemList KFileItemModel::childItems(const KFileItem& item) const
 
     int index = m_items.value(item.url(), -1);
     if (index >= 0) {
-        const int parentLevel = m_itemData.at(index)->values.value("expandedParentsCount").toInt();
+        const int parentLevel = expandedParentsCount(index);
         ++index;
-        while (index < m_itemData.count() && m_itemData.at(index)->values.value("expandedParentsCount").toInt() > parentLevel) {
+        while (index < m_itemData.count() && expandedParentsCount(index) > parentLevel) {
             items.append(m_itemData.at(index)->item);
             ++index;
         }
@@ -1965,7 +2007,15 @@ void KFileItemModel::determineMimeTypes(const KFileItemList& items, int timeout)
     QElapsedTimer timer;
     timer.start();
     foreach (const KFileItem& item, items) { // krazy:exclude=foreach
-        item.determineMimeType();
+        // Only determine mime types for files here. For directories,
+        // KFileItem::determineMimeType() reads the .directory file inside to
+        // load the icon, but this is not necessary at all if we just need the
+        // type. Some special code for setting the correct mime type for
+        // directories is in retrieveData().
+        if (!item.isDir()) {
+            item.determineMimeType();
+        }
+
         if (timer.elapsed() > timeout) {
             // Don't block the user interface, let the remaining items
             // be resolved asynchronously.
@@ -2018,7 +2068,7 @@ bool KFileItemModel::isConsistent() const
         const ItemData* data = m_itemData.at(i);
         const ItemData* parent = data->parent;
         if (parent) {
-            if (data->values.value("expandedParentsCount").toInt() != parent->values.value("expandedParentsCount").toInt() + 1) {
+            if (expandedParentsCount(data) != expandedParentsCount(parent) + 1) {
                 qWarning() << "expandedParentsCount is inconsistent for parent" << parent->item << "and child" << data->item;
                 return false;
             }