diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index e6b911209..0ad224cbc 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -217,6 +217,7 @@ DolphinMainWindow::DolphinMainWindow() : toggleSplitView(); } updateEditActions(); + updatePasteAction(); updateViewActions(); updateGoActions(); @@ -356,6 +357,7 @@ void DolphinMainWindow::changeUrl(const KUrl& url) if (view) { view->setUrl(url); updateEditActions(); + updatePasteAction(); updateViewActions(); updateGoActions(); setUrlAsCaption(url); @@ -1455,6 +1457,7 @@ void DolphinMainWindow::setActiveViewContainer(DolphinViewContainer* viewContain updateHistory(); updateEditActions(); + updatePasteAction(); updateViewActions(); updateGoActions(); @@ -1819,7 +1822,6 @@ void DolphinMainWindow::updateEditActions() deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); cutAction->setEnabled(capabilities.supportsMoving()); } - updatePasteAction(); } void DolphinMainWindow::updateViewActions() diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index a0f9305cb..de3c3eb22 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -486,6 +486,18 @@ bool KFileItemModel::setExpanded(int index, bool expanded) m_urlsToExpand.insert(url); } } 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); @@ -500,7 +512,9 @@ bool KFileItemModel::setExpanded(int index, bool expanded) ItemData* itemData = m_itemData.at(childIndex); if (itemData->values.value("isExpanded").toBool()) { const KUrl targetUrl = itemData->item.targetUrl(); + const KUrl 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; diff --git a/src/settings/additionalinfodialog.cpp b/src/settings/additionalinfodialog.cpp index e9d5f606d..0de639540 100644 --- a/src/settings/additionalinfodialog.cpp +++ b/src/settings/additionalinfodialog.cpp @@ -75,7 +75,6 @@ AdditionalInfoDialog::AdditionalInfoDialog(QWidget* parent, QVBoxLayout* layout = new QVBoxLayout(mainWidget); layout->addWidget(header); layout->addWidget(m_listWidget); - layout->addStretch(1); setMainWidget(mainWidget); diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp index 99ee3368e..48e72e83f 100644 --- a/src/tests/kfileitemmodeltest.cpp +++ b/src/tests/kfileitemmodeltest.cpp @@ -93,6 +93,7 @@ private slots: void testChangeRolesForFilteredItems(); void testChangeSortRoleWhileFiltering(); void testRefreshFilteredItems(); + void testCollapseFolderWhileLoading(); void testCreateMimeData(); private: @@ -1619,6 +1620,83 @@ void KFileItemModelTest::testCreateMimeData() delete mimeData; } +void KFileItemModelTest::testCollapseFolderWhileLoading() +{ + QSet modelRoles = m_model->roles(); + modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + m_model->setRoles(modelRoles); + + QStringList files; + files << "a2/b/c1.txt"; + m_testDir->createFiles(files); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a2"); + + // Expand "a2/". + m_model->setExpanded(0, true); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b"); + + // Expand "a2/b/". + m_model->setExpanded(1, true); + QVERIFY(m_model->isExpanded(1)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b" << "c1.txt"); + + // Simulate that a new item "c2.txt" appears, but that the dir lister's completed() + // signal is not emitted yet. + const KFileItem fileItemC1 = m_model->fileItem(2); + KFileItem fileItemC2 = fileItemC1; + KUrl urlC2 = fileItemC2.url(); + urlC2.setFileName("c2.txt"); + fileItemC2.setUrl(urlC2); + + const KUrl urlB = m_model->fileItem(1).url(); + m_model->slotItemsAdded(urlB, KFileItemList() << fileItemC2); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b" << "c1.txt"); + + // Collapse "a2/". This should also remove all its (indirect) children from + // the model and from the model's m_pendingItemsToInsert member. + m_model->setExpanded(0, false); + QCOMPARE(itemsInModel(), QStringList() << "a2"); + + // Simulate that the dir lister's completed() signal is emitted. If "c2.txt" + // is still in m_pendingItemsToInsert, then we might get a crash, see + // https://bugs.kde.org/show_bug.cgi?id=332102. Even if the crash is not + // reproducible here, Valgrind will complain, and the item "c2.txt" will appear + // without parent in the model. + m_model->slotCompleted(); + QCOMPARE(itemsInModel(), QStringList() << "a2"); + + // Expand "a2/" again. + m_model->setExpanded(0, true); + QVERIFY(m_model->isExpanded(0)); + QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout)); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b"); + + // Now simulate that a new folder "a1/" is appears, but that the dir lister's + // completed() signal is not emitted yet. + const KFileItem fileItemA2 = m_model->fileItem(0); + KFileItem fileItemA1 = fileItemA2; + KUrl urlA1 = fileItemA1.url(); + urlA1.setFileName("a1"); + fileItemA1.setUrl(urlA1); + + m_model->slotItemsAdded(m_model->directory(), KFileItemList() << fileItemA1); + QCOMPARE(itemsInModel(), QStringList() << "a2" << "b"); + + // Collapse "a2/". Note that this will cause "a1/" to be added to the model, + // i.e., the index of "a2/" will change from 0 to 1. Check that this does not + // confuse the code which collapses the folder. + m_model->setExpanded(0, false); + QCOMPARE(itemsInModel(), QStringList() << "a1" << "a2"); + QVERIFY(!m_model->isExpanded(0)); + QVERIFY(!m_model->isExpanded(1)); +} + QStringList KFileItemModelTest::itemsInModel() const { QStringList items;