mirror of
https://invent.kde.org/system/dolphin
synced 2024-09-19 16:31:21 +00:00
Implement restoring expanded folders in Details View
This commit is contained in:
parent
5070666ad2
commit
9424f5a789
|
@ -45,7 +45,10 @@ KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) :
|
|||
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;
|
||||
|
@ -306,14 +309,18 @@ bool KFileItemModel::setExpanded(int index, bool expanded)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (expanded) {
|
||||
const KUrl url = m_sortedItems.at(index).url();
|
||||
if (expanded) {
|
||||
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;
|
||||
|
@ -344,6 +351,16 @@ bool KFileItemModel::isExpandable(int index) const
|
|||
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);
|
||||
|
@ -381,13 +398,38 @@ void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArr
|
|||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -492,6 +534,8 @@ void KFileItemModel::slotClear()
|
|||
m_data.clear();
|
||||
emit itemsRemoved(KItemRangeList() << KItemRange(0, removedCount));
|
||||
}
|
||||
|
||||
m_expandedUrls.clear();
|
||||
}
|
||||
|
||||
void KFileItemModel::slotClear(const KUrl& url)
|
||||
|
@ -505,6 +549,10 @@ void KFileItemModel::dispatchPendingItemsToInsert()
|
|||
insertItems(m_pendingItemsToInsert);
|
||||
m_pendingItemsToInsert.clear();
|
||||
}
|
||||
|
||||
if (m_pendingEmitLoadingCompleted) {
|
||||
emit loadingCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
void KFileItemModel::insertItems(const KFileItemList& items)
|
||||
|
@ -669,6 +717,7 @@ void KFileItemModel::removeExpandedItems()
|
|||
removeItems(expandedItems);
|
||||
|
||||
m_rootExpansionLevel = -1;
|
||||
m_expandedUrls.clear();
|
||||
}
|
||||
|
||||
void KFileItemModel::resetRoles()
|
||||
|
|
|
@ -113,6 +113,11 @@ public:
|
|||
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);
|
||||
|
@ -199,12 +204,19 @@ private:
|
|||
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
|
||||
};
|
||||
|
||||
|
|
|
@ -243,6 +243,10 @@ void KFileItemModelTest::testExpandItems()
|
|||
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));
|
||||
|
||||
|
@ -250,6 +254,7 @@ void KFileItemModelTest::testExpandItems()
|
|||
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)));
|
||||
|
||||
|
@ -258,6 +263,7 @@ void KFileItemModelTest::testExpandItems()
|
|||
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>();
|
||||
|
@ -273,6 +279,7 @@ void KFileItemModelTest::testExpandItems()
|
|||
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>();
|
||||
|
@ -285,7 +292,8 @@ void KFileItemModelTest::testExpandItems()
|
|||
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>();
|
||||
|
@ -299,9 +307,28 @@ void KFileItemModelTest::testExpandItems()
|
|||
// 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()
|
||||
|
|
|
@ -151,7 +151,6 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
|
|||
|
||||
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()));
|
||||
|
||||
|
@ -180,6 +179,11 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
|
|||
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>)));
|
||||
|
@ -187,7 +191,7 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
|
|||
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)));
|
||||
|
@ -812,8 +816,12 @@ void DolphinView::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* even
|
|||
|
||||
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);
|
||||
|
@ -921,16 +929,9 @@ void DolphinView::restoreState(QDataStream& stream)
|
|||
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)
|
||||
|
@ -944,7 +945,7 @@ 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
|
||||
|
@ -1044,7 +1045,7 @@ void DolphinView::slotDeleteFileFinished(KJob* job)
|
|||
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);
|
||||
|
@ -1053,7 +1054,7 @@ void DolphinView::slotDirListerStarted(const KUrl& url)
|
|||
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
|
||||
|
|
|
@ -640,10 +640,12 @@ private slots:
|
|||
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.
|
||||
|
|
Loading…
Reference in a new issue