m_minimumUpdateIntervalTimer(0),
m_maximumUpdateIntervalTimer(0),
m_pendingItemsToInsert(),
- m_rootExpansionLevel(-1)
+ m_pendingEmitLoadingCompleted(false),
+ m_rootExpansionLevel(-1),
+ m_expandedUrls(),
+ m_restoredExpandedUrls()
{
resetRoles();
m_requestRole[NameRole] = true;
return false;
}
+ const KUrl url = m_sortedItems.at(index).url();
if (expanded) {
- const KUrl url = m_sortedItems.at(index).url();
+ m_expandedUrls.insert(url);
+
KDirLister* dirLister = m_dirLister.data();
if (dirLister) {
dirLister->openUrl(url, KDirLister::Keep);
return true;
}
} else {
+ m_expandedUrls.remove(url);
+
KFileItemList itemsToRemove;
const int expansionLevel = data(index)["expansionLevel"].toInt();
++index;
return false;
}
+QSet<KUrl> KFileItemModel::expandedUrls() const
+{
+ return m_expandedUrls;
+}
+
+void KFileItemModel::restoreExpandedUrls(const QSet<KUrl>& urls)
+{
+ m_restoredExpandedUrls = urls;
+}
+
void KFileItemModel::onGroupRoleChanged(const QByteArray& current, const QByteArray& previous)
{
Q_UNUSED(previous);
void KFileItemModel::slotCompleted()
{
- if (m_minimumUpdateIntervalTimer->isActive()) {
+ if (m_restoredExpandedUrls.isEmpty() && m_minimumUpdateIntervalTimer->isActive()) {
// dispatchPendingItems() will be called when the timer
// has been expired.
+ m_pendingEmitLoadingCompleted = true;
return;
}
+ m_pendingEmitLoadingCompleted = false;
dispatchPendingItemsToInsert();
+
+ if (!m_restoredExpandedUrls.isEmpty()) {
+ // Try to find a URL that can be expanded.
+ // 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_restoredExpandedUrls) {
+ const int index = m_items.value(url, -1);
+ if (index >= 0) {
+ // We have found an expandable URL. Expand it and return - when
+ // the dir lister has finished, this slot will be called again.
+ m_restoredExpandedUrls.remove(url);
+ setExpanded(index, true);
+ return;
+ }
+ }
+
+ // None of the URLs in m_restoredExpandedUrls could be found in the model. This can happen
+ // if these URLs have been deleted in the meantime.
+ m_restoredExpandedUrls.clear();
+ }
+
+ emit loadingCompleted();
m_minimumUpdateIntervalTimer->start();
}
m_data.clear();
emit itemsRemoved(KItemRangeList() << KItemRange(0, removedCount));
}
+
+ m_expandedUrls.clear();
}
void KFileItemModel::slotClear(const KUrl& url)
insertItems(m_pendingItemsToInsert);
m_pendingItemsToInsert.clear();
}
+
+ if (m_pendingEmitLoadingCompleted) {
+ emit loadingCompleted();
+ }
}
void KFileItemModel::insertItems(const KFileItemList& items)
removeItems(expandedItems);
m_rootExpansionLevel = -1;
+ m_expandedUrls.clear();
}
void KFileItemModel::resetRoles()
bool setExpanded(int index, bool expanded);
bool isExpanded(int index) const;
bool isExpandable(int index) const;
+ QSet<KUrl> expandedUrls() const;
+ void restoreExpandedUrls(const QSet<KUrl>& urls);
+
+signals:
+ void loadingCompleted();
protected:
virtual void onGroupRoleChanged(const QByteArray& current, const QByteArray& previous);
QTimer* m_minimumUpdateIntervalTimer;
QTimer* m_maximumUpdateIntervalTimer;
KFileItemList m_pendingItemsToInsert;
+ bool m_pendingEmitLoadingCompleted;
// Stores the smallest expansion level of the root-URL. Is required to calculate
// the "expansionLevel" role in an efficient way. A value < 0 indicates that
// it has not been initialized yet.
mutable int m_rootExpansionLevel;
+ // Stores the URLs of the expanded folders.
+ QSet<KUrl> m_expandedUrls;
+
+ // Stores the URLs which have to be expanded in order to restore a previous state of the model.
+ QSet<KUrl> m_restoredExpandedUrls;
+
friend class KFileItemModelTest; // For unit testing
};
files << "a/a/1" << "a/a-1/1"; // missing folders are created automatically
m_testDir->createFiles(files);
+ // Store the URLs of all folders in a set.
+ QSet<KUrl> allFolders;
+ allFolders << KUrl(m_testDir->name() + "a") << KUrl(m_testDir->name() + "a/a") << KUrl(m_testDir->name() + "a/a-1");
+
m_dirLister->openUrl(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 1);
QVERIFY(m_model->isExpandable(0));
QVERIFY(!m_model->isExpanded(0));
+ QVERIFY(m_model->expandedUrls().empty());
QSignalSpy spyInserted(m_model, SIGNAL(itemsInserted(KItemRangeList)));
QVERIFY(m_model->isExpanded(0));
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/a/", "a/a-1/"
+ QCOMPARE(m_model->expandedUrls(), QSet<KUrl>() << KUrl(m_testDir->name() + "a"));
QCOMPARE(spyInserted.count(), 1);
KItemRangeList itemRangeList = spyInserted.takeFirst().at(0).value<KItemRangeList>();
QVERIFY(m_model->isExpanded(1));
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 4); // 4 items: "a/", "a/a/", "a/a/1", "a/a-1/"
+ QCOMPARE(m_model->expandedUrls(), QSet<KUrl>() << KUrl(m_testDir->name() + "a") << KUrl(m_testDir->name() + "a/a"));
QCOMPARE(spyInserted.count(), 1);
itemRangeList = spyInserted.takeFirst().at(0).value<KItemRangeList>();
m_model->setExpanded(3, true);
QVERIFY(m_model->isExpanded(3));
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
- QCOMPARE(m_model->count(), 5); // 4 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
+ QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
+ QCOMPARE(m_model->expandedUrls(), allFolders);
QCOMPARE(spyInserted.count(), 1);
itemRangeList = spyInserted.takeFirst().at(0).value<KItemRangeList>();
// Collapse the top-level folder -> all other items should disappear
m_model->setExpanded(0, false);
QVERIFY(!m_model->isExpanded(0));
+ QCOMPARE(m_model->count(), 1);
+ QVERIFY(!m_model->expandedUrls().contains(KUrl(m_testDir->name() + "a"))); // TODO: Make sure that child URLs are also removed
+
QCOMPARE(spyRemoved.count(), 1);
itemRangeList = spyRemoved.takeFirst().at(0).value<KItemRangeList>();
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 4)); // 4 items removed
+
+ // Clear the model, reload the folder and try to restore the expanded folders.
+ m_model->clear();
+ QCOMPARE(m_model->count(), 0);
+ QVERIFY(m_model->expandedUrls().empty());
+
+ m_dirLister->openUrl(m_testDir->url());
+ m_model->restoreExpandedUrls(allFolders);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
+ QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
+ 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));
+ QCOMPARE(m_model->expandedUrls(), allFolders);
}
void KFileItemModelTest::testExpansionLevelsCompare_data()
connect(m_dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SLOT(slotRedirection(KUrl,KUrl)));
connect(m_dirLister, SIGNAL(started(KUrl)), this, SLOT(slotDirListerStarted(KUrl)));
- connect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
connect(m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem,KFileItem> >)),
this, SLOT(slotRefreshItems()));
connect(controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*)));
connect(controller, SIGNAL(modelChanged(KItemModelBase*,KItemModelBase*)), this, SLOT(slotModelChanged(KItemModelBase*,KItemModelBase*)));
+ KFileItemModel* model = fileItemModel();
+ if (model) {
+ connect(model, SIGNAL(loadingCompleted()), this, SLOT(slotLoadingCompleted()));
+ }
+
KItemListSelectionManager* selectionManager = controller->selectionManager();
connect(selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)),
this, SLOT(slotSelectionChanged(QSet<int>,QSet<int>)));
m_toolTipManager = new ToolTipManager(this);
m_versionControlObserver = new VersionControlObserver(this);
- m_versionControlObserver->setModel(fileItemModel());
+ m_versionControlObserver->setModel(model);
connect(m_versionControlObserver, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString)));
connect(m_versionControlObserver, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString)));
connect(m_versionControlObserver, SIGNAL(operationCompletedMessage(QString)), this, SIGNAL(operationCompletedMessage(QString)));
void DolphinView::slotModelChanged(KItemModelBase* current, KItemModelBase* previous)
{
- Q_UNUSED(previous);
+ if (previous != 0) {
+ disconnect(previous, SIGNAL(loadingCompleted()), this, SLOT(slotLoadingCompleted()));
+ }
+
Q_ASSERT(qobject_cast<KFileItemModel*>(current));
+ connect(current, SIGNAL(loadingCompleted()), this, SLOT(slotLoadingCompleted()));
KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(current);
m_versionControlObserver->setModel(fileItemModel);
stream >> m_restoredContentsPosition;
// Restore expanded folders (only relevant for the details view - will be ignored by the view in other view modes)
- QSet<KUrl> urlsToExpand;
- stream >> urlsToExpand;
- /*const DolphinDetailsViewExpander* expander = m_viewAccessor.setExpandedUrls(urlsToExpand);
- if (expander) {
- m_expanderActive = true;
- connect (expander, SIGNAL(completed()), this, SLOT(slotLoadingCompleted()));
- }
- else {
- m_expanderActive = false;
- }*/
+ QSet<KUrl> urls;
+ stream >> urls;
+ fileItemModel()->restoreExpandedUrls(urls);
}
void DolphinView::saveState(QDataStream& stream)
stream << QPoint(x, y);
// Save expanded folders (only relevant for the details view - the set will be empty in other view modes)
- //stream << m_viewAccessor.expandedUrls();
+ stream << fileItemModel()->expandedUrls();
}
bool DolphinView::hasSelection() const
void DolphinView::slotDirListerStarted(const KUrl& url)
{
// Disable the writestate temporary until it can be determined in a fast way
- // in DolphinView::slotDirListerCompleted()
+ // in DolphinView::slotLoadingCompleted()
if (m_isFolderWritable) {
m_isFolderWritable = false;
emit writeStateChanged(m_isFolderWritable);
emit startedPathLoading(url);
}
-void DolphinView::slotDirListerCompleted()
+void DolphinView::slotLoadingCompleted()
{
// Update the view-state. This has to be done using a Qt::QueuedConnection
// because the view might not be in its final state yet (the view also
void slotDirListerStarted(const KUrl& url);
/**
- * Invoked when the directory lister has completed the loading of
- * items. Assures that pasted items and renamed items get seleced.
+ * Invoked when the file item model indicates that the directory lister has completed the loading
+ * of items, and that expanded folders have been restored (if the view mode is 'Details', and the
+ * view state is restored after navigating back or forward in history). Assures that pasted items
+ * and renamed items get seleced.
*/
- void slotDirListerCompleted();
+ void slotLoadingCompleted();
/**
* Is invoked when the KDirLister indicates refreshed items.