return;
}
+ const KFileItemList oldSortedItems = m_sortedItems;
+
KFileItemList sortedItems = m_sortedItems;
m_sortedItems.clear();
m_items.clear();
++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;
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 (!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();
}
}
+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"
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;
}
}
+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)
{
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;
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);
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);
struct KItemRange
{
- KItemRange(int index, int count);
+ KItemRange(int index = 0, int count = 0);
int index;
int count;
/**
* 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);
};
Q_DECLARE_METATYPE(KItemRangeList)
+Q_DECLARE_METATYPE(QList<int>)
class KFileItemModelTest : public QObject
{
void KFileItemModelTest::init()
{
+ qRegisterMetaType<KItemRange>("KItemRange");
qRegisterMetaType<KItemRangeList>("KItemRangeList");
qRegisterMetaType<KFileItemList>("KFileItemList");
//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);
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);
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
}
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()
{
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()
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>)));
// 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;