]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Keep current item and selection when resorting, part 1
authorFrank Reininghaus <frank78ac@googlemail.com>
Thu, 29 Sep 2011 18:50:00 +0000 (20:50 +0200)
committerFrank Reininghaus <frank78ac@googlemail.com>
Thu, 29 Sep 2011 18:50:00 +0000 (20:50 +0200)
KFileItemModel now emits the itemsMoved signal when the model
is resorted, and KItemListSelectionManager has a new function
itemsMoved() which will be called indirectly when this signal
is emitted. Unit tests for the new functionality are included.
The following things are still needed to make the feature
work:
1. KFileItemMdel::resortAllItems() should not emit
   itemsAdded/itemsRemoved any more.
2. KItemListView::itemsMoved() must update the view according
   to the changes in the model, and it must call
   KItemListSelectionManager::itemsMoved().

src/kitemviews/kfileitemmodel.cpp
src/kitemviews/kitemlistselectionmanager.cpp
src/kitemviews/kitemlistselectionmanager.h
src/kitemviews/kitemlistview.cpp
src/kitemviews/kitemlistview.h
src/kitemviews/kitemmodelbase.h
src/tests/kfileitemmodeltest.cpp
src/tests/kitemlistselectionmanagertest.cpp

index 7874d30253368994db5896d8ba7b69b0d121a847..0d6f60336376741936a51d73c6662aaf82a182af 100644 (file)
@@ -732,6 +732,8 @@ void KFileItemModel::resortAllItems()
         return;
     }
 
+    const KFileItemList oldSortedItems = m_sortedItems;
+
     KFileItemList sortedItems = m_sortedItems;
     m_sortedItems.clear();
     m_items.clear();
@@ -748,12 +750,28 @@ void KFileItemModel::resortAllItems()
         ++index;
     }
 
+    bool emitItemsMoved = false;
+    QList<int> movedToIndexes;
+    movedToIndexes.reserve(sortedItems.count());
+    for (int i = 0; i < itemCount; i++) {
+        const int newIndex = m_items.value(oldSortedItems.at(i).url());
+        movedToIndexes.append(newIndex);
+        if (!emitItemsMoved && newIndex != i) {
+            emitItemsMoved = true;
+        }
+    }
+
+    if (emitItemsMoved) {
+        // TODO:
+        // * Implement KItemListView::slotItemsMoved() (which should call KItemListSelectionManager::itemsMoved())
+        // * Do not emit itemsRemoved()/itemsInserted() here.
+        emit itemsMoved(KItemRange(0, itemCount), movedToIndexes);
+    }
     emit itemsInserted(KItemRangeList() << KItemRange(0, itemCount));
 }
 
 void KFileItemModel::removeExpandedItems()
 {
-
     KFileItemList expandedItems;
 
     const int maxIndex = m_data.count() - 1;
index 63306a3f26440093479790aad3a43e8f81d9dfef..131ee46e6278a4d7598d5bfbfd9bd75f82bdd4eb 100644 (file)
@@ -239,6 +239,7 @@ void KItemListSelectionManager::itemsInserted(const KItemRangeList& itemRanges)
     if (!m_selectedItems.isEmpty()) {
         const QSet<int> previous = m_selectedItems;
         m_selectedItems.clear();
+        m_selectedItems.reserve(previous.count());
         QSetIterator<int> it(previous);
         while (it.hasNext()) {
             const int index = it.next();
@@ -307,6 +308,7 @@ void KItemListSelectionManager::itemsRemoved(const KItemRangeList& itemRanges)
     if (!m_selectedItems.isEmpty()) {
         const QSet<int> previous = m_selectedItems;
         m_selectedItems.clear();
+        m_selectedItems.reserve(previous.count());
         QSetIterator<int> it(previous);
         while (it.hasNext()) {
             int index = it.next();
@@ -338,4 +340,48 @@ void KItemListSelectionManager::itemsRemoved(const KItemRangeList& itemRanges)
     }
 }
 
+void KItemListSelectionManager::itemsMoved(const KItemRange& itemRange, const QList<int>& movedToIndexes)
+{
+    // Store the current selection (needed in the selectionChanged() signal)
+    const QSet<int> previousSelection = selectedItems();
+
+    // Update the current item
+    if (m_currentItem >= itemRange.index && m_currentItem < itemRange.index + itemRange.count) {
+        const int previousCurrentItem = m_currentItem;
+        const int newCurrentItem = movedToIndexes.at(previousCurrentItem - itemRange.index);
+
+        // Calling setCurrentItem would trigger the selectionChanged signal, but we want to
+        // emit it only once in this function -> change the current item manually and emit currentChanged
+        m_currentItem = newCurrentItem;
+        emit currentChanged(newCurrentItem, previousCurrentItem);
+    }
+
+    // Update the anchor item
+    if (m_anchorItem >= itemRange.index && m_anchorItem < itemRange.index + itemRange.count) {
+        m_anchorItem = movedToIndexes.at(m_anchorItem - itemRange.index);
+    }
+
+    // Update the selections
+    if (!m_selectedItems.isEmpty()) {
+        const QSet<int> previous = m_selectedItems;
+        m_selectedItems.clear();
+        m_selectedItems.reserve(previous.count());
+        QSetIterator<int> it(previous);
+        while (it.hasNext()) {
+            const int index = it.next();
+            if (index >= itemRange.index && index < itemRange.index + itemRange.count) {
+                m_selectedItems.insert(movedToIndexes.at(index - itemRange.index));
+            }
+            else {
+                m_selectedItems.insert(index);
+            }
+        }
+    }
+
+    const QSet<int> selection = selectedItems();
+    if (selection != previousSelection) {
+        emit selectionChanged(selection, previousSelection);
+    }
+}
+
 #include "kitemlistselectionmanager.moc"
index 8b3a121a6cbc1f7e29fc90de6106b9ad329a8f13..a8ef5ca2df62b163fb080833b6ea1722d6ee989b 100644 (file)
@@ -73,6 +73,7 @@ private:
     void setModel(KItemModelBase* model);
     void itemsInserted(const KItemRangeList& itemRanges);
     void itemsRemoved(const KItemRangeList& itemRanges);
+    void itemsMoved(const KItemRange& itemRange, const QList<int>& movedToIndexes);
 
 private:
     int m_currentItem;
index 988a45d409236c11135528ac330f039f6a5db7d3..63315b66583e6d6dca523bf6228e6e377e63bcc0 100644 (file)
@@ -762,6 +762,15 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
     }
 }
 
+void KItemListView::slotItemsMoved(const KItemRange& itemRange, const QList<int>& movedToIndexes)
+{
+    // TODO:
+    // * Implement KItemListView::slotItemsMoved() (which should call KItemListSelectionManager::itemsMoved())
+    // * Do not emit itemsRemoved()/itemsInserted() in KFileItemModel::resortAllItems()
+    Q_UNUSED(itemRange);
+    Q_UNUSED(movedToIndexes);
+}
+
 void KItemListView::slotItemsChanged(const KItemRangeList& itemRanges,
                                      const QSet<QByteArray>& roles)
 {
@@ -1031,6 +1040,8 @@ void KItemListView::setModel(KItemModelBase* model)
                    this,    SLOT(slotItemsInserted(KItemRangeList)));
         disconnect(m_model, SIGNAL(itemsRemoved(KItemRangeList)),
                    this,    SLOT(slotItemsRemoved(KItemRangeList)));
+        disconnect(m_model, SIGNAL(itemsMoved(KItemRangeList,QList<int>)),
+                   this,    SLOT(slotItemsMoved(KItemRangeList,QList<int>)));
     }
 
     m_model = model;
@@ -1044,6 +1055,8 @@ void KItemListView::setModel(KItemModelBase* model)
                 this,    SLOT(slotItemsInserted(KItemRangeList)));
         connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)),
                 this,    SLOT(slotItemsRemoved(KItemRangeList)));
+        connect(m_model, SIGNAL(itemsMoved(KItemRangeList,QList<int>)),
+                this,    SLOT(slotItemsMoved(KItemRangeList,QList<int>)));
     }
 
     onModelChanged(model, previous);
index 73b736bf7088ed7147024c10b393286e3a00ffff..e49dbe48d622f38a3389c9d800866293f899ceda 100644 (file)
@@ -246,6 +246,7 @@ protected:
 protected slots:
     virtual void slotItemsInserted(const KItemRangeList& itemRanges);
     virtual void slotItemsRemoved(const KItemRangeList& itemRanges);
+    virtual void slotItemsMoved(const KItemRange& itemRange, const QList<int>& movedToIndexes);
     virtual void slotItemsChanged(const KItemRangeList& itemRanges,
                                   const QSet<QByteArray>& roles);
 
index 742bc291551157144aa2defcaf4d2183e2836bf8..763a02efd6c456d864b5f35489cefd630cea2c07 100644 (file)
@@ -34,7 +34,7 @@ class QMimeData;
 
 struct KItemRange
 {
-    KItemRange(int index, int count);
+    KItemRange(int index = 0, int count = 0);
     int index;
     int count;
 
@@ -180,20 +180,15 @@ signals:
 
     /**
      * Is emitted if one ore more items get moved.
-     * @param itemRanges     Item-ranges that get moved to a new position.
-     * @param movedToIndexes New positions for each element of the item-ranges.
+     * @param itemRange      Item-range that gets moved to a new position.
+     * @param movedToIndexes New positions for each element of the item-range.
      *
      * For example if the model has 10 items and the items 0 and 1 get exchanged
      * with the items 5 and 6 then the parameters look like this:
-     * - itemRanges: Contains two ranges. The first has the index 0 and a count of
-     *               2 and the second as the index 5 and a count of 2.
-     * - movedToIndexes: Contains the four values 5, 6, 0, 1
-     *
-     * For the item-ranges it is assured that:
-     * - They don't overlap
-     * - The index of item-range n is smaller than the index of item-range n + 1.
+     * - itemRange: has the index 0 and a count of 7.
+     * - movedToIndexes: Contains the seven values 5, 6, 2, 3, 4, 0, 1
      */
-    void itemsMoved(const KItemRangeList& itemRanges, const QList<int> movedToIndexes);
+    void itemsMoved(const KItemRange& itemRange, const QList<int>& movedToIndexes);
 
     void itemsChanged(const KItemRangeList& itemRanges, const QSet<QByteArray>& roles);
 
index ea8c19c05ae2d7563f56410f9fdce5f9e781d340..f56ef90336829232307dae06ed5c8caa21405dca 100644 (file)
@@ -29,6 +29,7 @@ namespace {
 };
 
 Q_DECLARE_METATYPE(KItemRangeList)
+Q_DECLARE_METATYPE(QList<int>)
 
 class KFileItemModelTest : public QObject
 {
@@ -65,6 +66,7 @@ private:
 
 void KFileItemModelTest::init()
 {
+    qRegisterMetaType<KItemRange>("KItemRange");
     qRegisterMetaType<KItemRangeList>("KItemRangeList");
     qRegisterMetaType<KFileItemList>("KFileItemList");
 
@@ -355,23 +357,31 @@ void KFileItemModelTest::testSorting()
     //QVERIFY(!m_model->showHiddenFiles());
     QCOMPARE(itemsInModel(), QStringList() << "c" << "a" << "b" << "d" << "e");
 
+    QSignalSpy spyItemsMoved(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)));
+
     // Sort by Name, descending
     m_model->setSortOrder(Qt::DescendingOrder);
     QCOMPARE(m_model->sortRole(), QByteArray("name"));
     QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
     QCOMPARE(itemsInModel(), QStringList() << "c" << "e" << "d" << "b" << "a");
+    QCOMPARE(spyItemsMoved.count(), 1);
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 4 << 3 << 2 << 1);
 
-    // Sort by Date, decending
+    // Sort by Date, descending
     m_model->setSortRole("date");
     QCOMPARE(m_model->sortRole(), QByteArray("date"));
     QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
     QCOMPARE(itemsInModel(), QStringList() << "c" << "b" << "d" << "a" << "e");
+    QCOMPARE(spyItemsMoved.count(), 1);
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 4 << 2 << 1 << 3);
 
     // Sort by Date, ascending
     m_model->setSortOrder(Qt::AscendingOrder);
     QCOMPARE(m_model->sortRole(), QByteArray("date"));
     QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
     QCOMPARE(itemsInModel(), QStringList() << "c" << "e" << "a" << "d" << "b");
+    QCOMPARE(spyItemsMoved.count(), 1);
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 4 << 3 << 2 << 1);
 
     // Sort by Date, ascending, 'Sort Folders First' disabled
     m_model->setSortFoldersFirst(false);
@@ -379,20 +389,33 @@ void KFileItemModelTest::testSorting()
     QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
     QVERIFY(!m_model->sortFoldersFirst());
     QCOMPARE(itemsInModel(), QStringList() << "e" << "a" << "c" << "d" << "b");
+    QCOMPARE(spyItemsMoved.count(), 1);
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 0 << 1 << 3 << 4);
 
-    // Default: Sort by Name, ascending, 'Sort Folders First' disabled
+    // Sort by Name, ascending, 'Sort Folders First' disabled
     m_model->setSortRole("name");
     QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
     QVERIFY(!m_model->sortFoldersFirst());
     QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "d" << "e");
+    QCOMPARE(spyItemsMoved.count(), 1);
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 4 << 0 << 2 << 3 << 1);
 
-    // Sort by Size, ascending, 'Sort Folders First' enabled
+    // Sort by Size, ascending, 'Sort Folders First' disabled
     m_model->setSortRole("size");
+    QCOMPARE(m_model->sortRole(), QByteArray("size"));
+    QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
+    QVERIFY(!m_model->sortFoldersFirst());
+    QCOMPARE(itemsInModel(), QStringList() << "c" << "a" << "b" << "e" << "d");
+    QCOMPARE(spyItemsMoved.count(), 1);
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 1 << 2 << 0 << 4 << 3);
+
+    // In 'Sort by Size' mode, folders are always first -> changing 'Sort Folders First' does not resort the model
     m_model->setSortFoldersFirst(true);
     QCOMPARE(m_model->sortRole(), QByteArray("size"));
     QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
     QVERIFY(m_model->sortFoldersFirst());
     QCOMPARE(itemsInModel(), QStringList() << "c" << "a" << "b" << "e" << "d");
+    QCOMPARE(spyItemsMoved.count(), 0);
 
     // Sort by Size, descending, 'Sort Folders First' enabled
     m_model->setSortOrder(Qt::DescendingOrder);
@@ -400,8 +423,8 @@ void KFileItemModelTest::testSorting()
     QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
     QVERIFY(m_model->sortFoldersFirst());
     QCOMPARE(itemsInModel(), QStringList() << "c" << "d" << "e" << "b" << "a");
-
-    // TODO: How shall the sorting by size be done if 'Sort Folders First' is disabled?
+    QCOMPARE(spyItemsMoved.count(), 1);
+    QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 4 << 3 << 2 << 1);
 
     // TODO: Sort by other roles; show/hide hidden files
 }
index c27096177038a7d53c88be17e7792a5d2253910b..2a3601bb0a198dfeefcde7578b8191f351b55a7e 100644 (file)
@@ -297,14 +297,44 @@ namespace {
         NoChange,
         InsertItems,
         RemoveItems,
+        MoveItems,
         EndAnchoredSelection,
-        ToggleSelected
+        SetSelected
     };
 }
 
 Q_DECLARE_METATYPE(QSet<int>);
 Q_DECLARE_METATYPE(ChangeType);
+Q_DECLARE_METATYPE(KItemRange);
 Q_DECLARE_METATYPE(KItemRangeList);
+Q_DECLARE_METATYPE(KItemListSelectionManager::SelectionMode);
+Q_DECLARE_METATYPE(QList<int>);
+
+/**
+ * The following function provides a generic way to test the selection functionality.
+ *
+ * The test is data-driven and takes the following arguments:
+ * 
+ * \param initialSelection  The selection at the beginning.
+ * \param anchor            This item will be the anchor item.
+ * \param current           This item will be the current item.
+ * \param expectedSelection Expected selection after anchor and current are set.
+ * \param changeType        Type of the change that is done then:
+ *                          - NoChange
+ *                          - InsertItems -> data.at(0) provides the KItemRangeList. \sa KItemListSelectionManager::itemsInserted()
+ *                          - RemoveItems -> data.at(0) provides the KItemRangeList. \sa KItemListSelectionManager::itemsRemoved()
+ *                          - MoveItems   -> data.at(0) provides the KItemRange containing the original indices,
+ *                                           data.at(1) provides the list containing the new indices
+ *                                          \sa KItemListSelectionManager::itemsMoved(), KItemModelBase::itemsMoved()
+ *                          - EndAnchoredSelection
+ *                          - SetSelected -> data.at(0) provides the index where the selection process starts,
+ *                                           data.at(1) provides the number of indices to be selected,
+ *                                           data.at(2) provides the selection mode.
+ *                                          \sa KItemListSelectionManager::setSelected()
+ * \param data              A list of QVariants which will be cast to the arguments needed for the chosen ChangeType (see above).
+ * \param finalSelection    The expected final selection.
+ *
+ */
 
 void KItemListSelectionManagerTest::testChangeSelection_data()
 {
@@ -313,43 +343,68 @@ void KItemListSelectionManagerTest::testChangeSelection_data()
     QTest::addColumn<int>("current");
     QTest::addColumn<QSet<int> >("expectedSelection");
     QTest::addColumn<ChangeType>("changeType");
-    QTest::addColumn<KItemRangeList>("changedItems");
+    QTest::addColumn<QList<QVariant> >("data");
     QTest::addColumn<QSet<int> >("finalSelection");
 
     QTest::newRow("No change")
         << (QSet<int>() << 5 << 6)
         << 2 << 3
         << (QSet<int>() << 2 << 3 << 5 << 6)
-        << NoChange << KItemRangeList()
+        << NoChange
+        << QList<QVariant>()
         << (QSet<int>() << 2 << 3 << 5 << 6);
 
     QTest::newRow("Insert Items")
         << (QSet<int>() << 5 << 6)
         << 2 << 3
         << (QSet<int>() << 2 << 3 << 5 << 6)
-        << InsertItems << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 2) << KItemRange(10, 5))
+        << InsertItems
+        << (QList<QVariant>() << QVariant::fromValue(KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 2) << KItemRange(10, 5)))
         << (QSet<int>() << 3 << 4 << 8 << 9);
 
     QTest::newRow("Remove Items")
         << (QSet<int>() << 5 << 6)
         << 2 << 3
         << (QSet<int>() << 2 << 3 << 5 << 6)
-        << RemoveItems << (KItemRangeList() << KItemRange(1, 1) << KItemRange(3, 1) << KItemRange(10, 5))
+        << RemoveItems
+        << (QList<QVariant>() << QVariant::fromValue(KItemRangeList() << KItemRange(1, 1) << KItemRange(3, 1) << KItemRange(10, 5)))
         << (QSet<int>() << 1 << 2 << 3 << 4);
 
     QTest::newRow("Empty Anchored Selection")
         << QSet<int>()
         << 2 << 2
         << QSet<int>()
-        << EndAnchoredSelection << KItemRangeList()
+        << EndAnchoredSelection
+        << QList<QVariant>()
         << QSet<int>();
 
     QTest::newRow("Toggle selection")
         << (QSet<int>() << 1 << 3 << 4)
         << 6 << 8
         << (QSet<int>() << 1 << 3 << 4 << 6 << 7 << 8)
-        << ToggleSelected << (KItemRangeList() << KItemRange(0, 10))
+        << SetSelected
+        << (QList<QVariant>() << 0 << 10 << QVariant::fromValue(KItemListSelectionManager::Toggle))
         << (QSet<int>() << 0 << 2 << 5 << 9);
+
+    // Swap items 2, 3 and 4, 5
+    QTest::newRow("Move items")
+        << (QSet<int>() << 0 << 1 << 2 << 3)
+        << -1 << -1
+        << (QSet<int>() << 0 << 1 << 2 << 3)
+        << MoveItems
+        << (QList<QVariant>() << QVariant::fromValue(KItemRange(2, 4))
+                              << QVariant::fromValue(QList<int>() << 4 << 5 << 2 << 3))
+        << (QSet<int>() << 0 << 1 << 4 << 5);
+
+    // Revert sort order
+    QTest::newRow("Revert sort order")
+        << (QSet<int>() << 0 << 1)
+        << 3 << 4
+        << (QSet<int>() << 0 << 1 << 3 << 4)
+        << MoveItems
+        << (QList<QVariant>() << QVariant::fromValue(KItemRange(0, 10))
+                              << QVariant::fromValue(QList<int>() << 9 << 8 << 7 << 6 << 5 << 4 << 3 << 2 << 1 << 0))
+        << (QSet<int>() << 5 << 6 << 8 << 9);
 }
 
 void KItemListSelectionManagerTest::testChangeSelection()
@@ -357,10 +412,10 @@ void KItemListSelectionManagerTest::testChangeSelection()
     QFETCH(QSet<int>, initialSelection);
     QFETCH(int, anchor);
     QFETCH(int, current);
-    QFETCH(QSet<int> , expectedSelection);
+    QFETCH(QSet<int>, expectedSelection);
     QFETCH(ChangeType, changeType);
-    QFETCH(KItemRangeList, changedItems);
-    QFETCH(QSet<int> , finalSelection);
+    QFETCH(QList<QVariant>, data);
+    QFETCH(QSet<int>, finalSelection);
 
     QSignalSpy spySelectionChanged(m_selectionManager, SIGNAL(selectionChanged(QSet<int>,QSet<int>)));
 
@@ -406,19 +461,23 @@ void KItemListSelectionManagerTest::testChangeSelection()
     // Change the model by inserting or removing items.
     switch (changeType) {
     case InsertItems:
-        m_selectionManager->itemsInserted(changedItems);
+        m_selectionManager->itemsInserted(data.at(0).value<KItemRangeList>());
         break;
     case RemoveItems:
-        m_selectionManager->itemsRemoved(changedItems);
+        m_selectionManager->itemsRemoved(data.at(0).value<KItemRangeList>());
+        break;
+    case MoveItems:
+        m_selectionManager->itemsMoved(data.at(0).value<KItemRange>(),
+                                       data.at(1).value<QList<int> >());
         break;
     case EndAnchoredSelection:
         m_selectionManager->endAnchoredSelection();
         QVERIFY(!m_selectionManager->isAnchoredSelectionActive());
         break;
-    case ToggleSelected:
-        foreach(const KItemRange& range, changedItems) {
-            m_selectionManager->setSelected(range.index, range.count, KItemListSelectionManager::Toggle);
-        }
+    case SetSelected:
+        m_selectionManager->setSelected(data.at(0).value<int>(), // index
+                                        data.at(1).value<int>(), // count
+                                        data.at(2).value<KItemListSelectionManager::SelectionMode>());
         break;
     case NoChange:
         break;