]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Implement restoring expanded folders in Details View
authorFrank Reininghaus <frank78ac@googlemail.com>
Sat, 17 Sep 2011 12:35:25 +0000 (14:35 +0200)
committerFrank Reininghaus <frank78ac@googlemail.com>
Sat, 17 Sep 2011 12:35:25 +0000 (14:35 +0200)
src/kitemviews/kfileitemmodel.cpp
src/kitemviews/kfileitemmodel.h
src/tests/kfileitemmodeltest.cpp
src/views/dolphinview.cpp
src/views/dolphinview.h

index ca15837529282f60dcb7728d0950b671ba6468b4..f5d8d41a2e5391f51ae97aedfac08ab42cd683df 100644 (file)
@@ -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;
     }
 
+    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;
@@ -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()
index 16081704314d5cbb3cce6824581ef0032220be37..4156b2be807c1bf213cb6143dcbcca889f8669a9 100644 (file)
@@ -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
 };
 
index f8398c57816b07726f9f93bcd261c510c6d7d0a4..d3404782cb59432200504a51f22221dfbf3654ec 100644 (file)
@@ -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()
index 7cb594d8e91c825e2a90e67737c1cd745c863ac5..cdcb9f7cbba5bb2e48ba25ee2c832748a2ac8b6f 100644 (file)
@@ -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
index 65bd775299b1eb390307f2ebc355b553b6464737..8d10942733cec7f8996663ec8095f3bca765dee7 100644 (file)
@@ -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.