From: Frank Reininghaus Date: Mon, 9 Sep 2013 19:43:29 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/KDE/4.11' X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/e03a687c92e1f5d4415bcf83c1fd4370fc518ffb?hp=-c Merge remote-tracking branch 'origin/KDE/4.11' --- e03a687c92e1f5d4415bcf83c1fd4370fc518ffb diff --combined src/kitemviews/kfileitemmodel.cpp index 51359bbce,b6b6ee0e2..51ff142e7 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@@ -190,33 -190,7 +190,7 @@@ bool KFileItemModel::setData(int index m_itemData[index]->item.setUrl(url); } - emit itemsChanged(KItemRangeList() << KItemRange(index, 1), changedRoles); - - // Trigger a resorting if the item's correct position has changed. 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))) { - // Compare the changed item with its neighbors to see - // if an expensive resorting is needed at all. - const ItemData* changedItem = m_itemData.at(index); - const ItemData* previousItem = (index == 0) ? 0 : m_itemData.at(index - 1); - const ItemData* nextItem = (index == m_itemData.count() - 1) ? 0 : m_itemData.at(index + 1); - - if ((previousItem && lessThan(changedItem, previousItem)) - || (nextItem && lessThan(nextItem, changedItem))) { - m_resortAllItemsTimer->start(); - } else 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(); - } - } + emitItemsChangedAndTriggerResorting(KItemRangeList() << KItemRange(index, 1), changedRoles); return true; } @@@ -494,7 -468,10 +468,7 @@@ bool KFileItemModel::isExpandable(int i 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; } @@@ -708,6 -685,7 +682,6 @@@ void KFileItemModel::resortAllItems( oldUrls.append(itemData->item.url()); } - m_groups.clear(); m_items.clear(); // Resort the items @@@ -716,45 -694,20 +690,45 @@@ m_items.insert(m_itemData.at(i)->item.url(), i); } - // Determine the indexes that have been moved - QList 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 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 > oldGroups = m_groups; + m_groups.clear(); + if (groups() != oldGroups) { + emit groupsChanged(); + } + } #ifdef KFILEITEMMODEL_DEBUG kDebug() << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed(); @@@ -966,11 -919,7 +940,7 @@@ void KFileItemModel::slotRefreshItems(c itemRangeList.append(KItemRange(rangeIndex, rangeCount)); } - emit itemsChanged(itemRangeList, changedRoles); - - if (changedRoles.contains(sortRole())) { - m_resortAllItemsTimer->start(); - } + emitItemsChangedAndTriggerResorting(itemRangeList, changedRoles); } void KFileItemModel::slotClear() @@@ -1231,23 -1180,6 +1201,23 @@@ QList KFileI 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; @@@ -1255,15 -1187,70 +1225,67 @@@ 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(); } + void KFileItemModel::emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet& 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() { for (int i = 0; i < RolesCount; ++i) { @@@ -1405,7 -1392,7 +1427,7 @@@ QHash KFileItemMo 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); } } @@@ -1429,8 -1416,8 +1451,8 @@@ bool KFileItemModel::lessThan(const Ite 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. @@@ -1450,7 -1437,7 +1472,7 @@@ 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) { @@@ -1949,9 -1936,9 +1971,9 @@@ KFileItemList KFileItemModel::childItem 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; } @@@ -2086,7 -2073,7 +2108,7 @@@ bool KFileItemModel::isConsistent() con 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; } diff --combined src/kitemviews/kfileitemmodel.h index f364e7ce0,c87ee9736..841016138 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@@ -319,10 -319,15 +319,17 @@@ private */ QList createItemDataList(const KUrl& parentUrl, const KFileItemList& items) const; + static int expandedParentsCount(const ItemData* data); + void removeExpandedItems(); + /** + * This function is called by setData() and slotRefreshItems(). It emits + * the itemsChanged() signal, checks if the sort order is still correct, + * and starts m_resortAllItemsTimer if that is not the case. + */ + void emitItemsChangedAndTriggerResorting(const KItemRangeList& itemRanges, const QSet& changedRoles); + /** * Resets all values from m_requestRole to false. */ diff --combined src/tests/kfileitemmodeltest.cpp index 797920246,391fe5be5..ff8dcd268 --- a/src/tests/kfileitemmodeltest.cpp +++ b/src/tests/kfileitemmodeltest.cpp @@@ -49,7 -49,6 +49,7 @@@ namespace const int DefaultTimeout = 5000; }; +Q_DECLARE_METATYPE(KItemRange) Q_DECLARE_METATYPE(KItemRangeList) Q_DECLARE_METATYPE(QList) @@@ -392,6 -391,18 +392,18 @@@ void KFileItemModelTest::testResortAfte QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList)), DefaultTimeout)); QCOMPARE(itemsInModel(), QStringList() << "b.txt" << "c.txt" << "d.txt"); + + // We rename d.txt back to a.txt using the dir lister's refreshItems() signal. + const KFileItem fileItemD = m_model->fileItem(2); + KFileItem fileItemA = fileItemD; + KUrl urlA = fileItemA.url(); + urlA.setFileName("a.txt"); + fileItemA.setUrl(urlA); + + m_model->slotRefreshItems(QList >() << qMakePair(fileItemD, fileItemA)); + + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt"); } void KFileItemModelTest::testModelConsistencyWhenInsertingItems() @@@ -486,8 -497,7 +498,8 @@@ void KFileItemModelTest::testExpandItem // KFileItemModel::expansionLevelsCompare(const KFileItem& a, const KFileItem& b) // yields the correct result for "a/a/1" and "a/a-1/", whis is non-trivial because they share the // first three characters. - QSet modelRoles = m_model->roles(); + QSet originalModelRoles = m_model->roles(); + QSet modelRoles = originalModelRoles; modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; m_model->setRoles(modelRoles); @@@ -594,18 -604,6 +606,18 @@@ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1" QCOMPARE(m_model->expandedDirectories(), allFolders); + + // Remove all expanded items by changing the roles + spyRemoved.clear(); + m_model->setRoles(originalModelRoles); + QVERIFY(!m_model->isExpanded(0)); + QCOMPARE(m_model->count(), 1); + QVERIFY(!m_model->expandedDirectories().contains(KUrl(m_testDir->name() + 'a'))); + + QCOMPARE(spyRemoved.count(), 1); + itemRangeList = spyRemoved.takeFirst().at(0).value(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 4)); // 4 items removed + QVERIFY(m_model->isConsistent()); } void KFileItemModelTest::testExpandParentItems() @@@ -658,28 -656,6 +670,28 @@@ QVERIFY(m_model->isExpanded(3)); QVERIFY(!m_model->isExpanded(4)); QVERIFY(m_model->isConsistent()); + + // Expand "a 1/b1/". + m_model->setExpanded(1, true); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout)); + QCOMPARE(m_model->count(), 6); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(m_model->isExpanded(1)); + QVERIFY(!m_model->isExpanded(2)); + QVERIFY(m_model->isExpanded(3)); + QVERIFY(m_model->isExpanded(4)); + QVERIFY(!m_model->isExpanded(5)); + QVERIFY(m_model->isConsistent()); + + // Collapse "a 1/b1/" again, and verify that the previous state is restored. + m_model->setExpanded(1, false); + QCOMPARE(m_model->count(), 5); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(!m_model->isExpanded(1)); + QVERIFY(m_model->isExpanded(2)); + QVERIFY(m_model->isExpanded(3)); + QVERIFY(!m_model->isExpanded(4)); + QVERIFY(m_model->isConsistent()); } /** @@@ -780,8 -756,7 +792,8 @@@ void KFileItemModelTest::testSorting( QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e"); QCOMPARE(spyItemsMoved.count(), 1); - QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 6)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1); // Sort by Name, descending m_model->setSortDirectoriesFirst(true); @@@ -790,10 -765,8 +802,10 @@@ QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder); QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "d" << "b" << "a"); QCOMPARE(spyItemsMoved.count(), 2); - QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 4 << 5 << 0 << 3 << 1 << 2 << 6 << 7); - QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 0 << 1 << 2 << 3 << 7 << 6 << 5 << 4); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 6)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 4 << 5 << 0 << 3 << 1 << 2); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(4, 4)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 6 << 5 << 4); // Sort by Date, descending m_model->setSortDirectoriesFirst(true); @@@ -802,8 -775,7 +814,8 @@@ QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder); QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "b" << "d" << "a" << "e"); QCOMPARE(spyItemsMoved.count(), 1); - QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 0 << 1 << 2 << 3 << 7 << 5 << 4 << 6); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(4, 4)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 5 << 4 << 6); // Sort by Date, ascending m_model->setSortOrder(Qt::AscendingOrder); @@@ -811,8 -783,7 +823,8 @@@ QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "a" << "d" << "b"); QCOMPARE(spyItemsMoved.count(), 1); - QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 0 << 1 << 2 << 3 << 7 << 6 << 5 << 4); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(4, 4)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 6 << 5 << 4); // Sort by Date, ascending, 'Sort Folders First' disabled m_model->setSortDirectoriesFirst(false); @@@ -821,8 -792,7 +833,8 @@@ QVERIFY(!m_model->sortDirectoriesFirst()); QCOMPARE(itemsInModel(), QStringList() << "e" << "a" << "c" << "c-1" << "c-2" << "c-3" << "d" << "b"); QCOMPARE(spyItemsMoved.count(), 1); - QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 6)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1); // Sort by Name, ascending, 'Sort Folders First' disabled m_model->setSortRole("text"); @@@ -830,7 -800,6 +842,7 @@@ QVERIFY(!m_model->sortDirectoriesFirst()); QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e"); QCOMPARE(spyItemsMoved.count(), 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 8)); QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 0 << 2 << 3 << 4 << 5 << 6 << 1); // Sort by Size, ascending, 'Sort Folders First' disabled @@@ -840,15 -809,19 +852,15 @@@ QVERIFY(!m_model->sortDirectoriesFirst()); QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "e" << "d"); QCOMPARE(spyItemsMoved.count(), 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(0, 8)); QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 4 << 5 << 0 << 3 << 1 << 2 << 7 << 6); - QSKIP("2 tests of testSorting() are temporary deactivated as in KFileItemModel resortAllItems() " - "always emits a itemsMoved() signal. Before adjusting the tests think about probably introducing " - "another signal", SkipSingle); - // Internal note: Check comment in KFileItemModel::resortAllItems() for details. - // In 'Sort by Size' mode, folders are always first -> changing 'Sort Folders First' does not resort the model m_model->setSortDirectoriesFirst(true); QCOMPARE(m_model->sortRole(), QByteArray("size")); QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); QVERIFY(m_model->sortDirectoriesFirst()); - QCOMPARE(itemsInModel(), QStringList() << "c" << "a" << "b" << "e" << "d"); + QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "e" << "d"); QCOMPARE(spyItemsMoved.count(), 0); // Sort by Size, descending, 'Sort Folders First' enabled @@@ -856,10 -829,9 +868,10 @@@ QCOMPARE(m_model->sortRole(), QByteArray("size")); QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder); QVERIFY(m_model->sortDirectoriesFirst()); - QCOMPARE(itemsInModel(), QStringList() << "c" << "d" << "e" << "b" << "a"); + QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "d" << "e" << "b" << "a"); QCOMPARE(spyItemsMoved.count(), 1); - QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 0 << 4 << 3 << 2 << 1); + QCOMPARE(spyItemsMoved.first().at(0).value(), KItemRange(4, 4)); + QCOMPARE(spyItemsMoved.takeFirst().at(1).value >(), QList() << 7 << 6 << 5 << 4); // TODO: Sort by other roles; show/hide hidden files } @@@ -1277,7 -1249,7 +1289,7 @@@ void KFileItemModelTest::testNameRoleGr // Rename c.txt to d.txt. data.insert("text", "d.txt"); m_model->setData(2, data); - QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList)), DefaultTimeout)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(groupsChanged()), DefaultTimeout)); QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "d.txt" << "e.txt"); expectedGroups.clear(); @@@ -1295,7 -1267,7 +1307,7 @@@ fileItemC.setUrl(urlC); m_model->slotRefreshItems(QList >() << qMakePair(fileItemD, fileItemC)); - QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList)), DefaultTimeout)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(groupsChanged()), DefaultTimeout)); QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "e.txt"); expectedGroups.clear();