+/**
+ * This test checks that updating the selection after key presses works as expected.
+ * Qt does not handle this internally if the first letter of an item is pressed, which
+ * is why DolphinTreeView has some custom code for this. The test verifies that this
+ * works without unwanted side effects.
+ *
+ * The test uses the class TreeViewWithDeleteShortcut which deletes the selected items
+ * when Shift-Delete is pressed. This is needed to test the fix for bug 259656 (see below).
+ */
+
+class TreeViewWithDeleteShortcut : public DolphinTreeView {
+
+ Q_OBJECT
+
+public:
+
+ TreeViewWithDeleteShortcut(QWidget* parent = 0) : DolphinTreeView(parent) {
+ // To test the fix for bug 259656, we need a delete shortcut.
+ KAction* deleteAction = new KAction(this);
+ deleteAction->setShortcut(Qt::SHIFT | Qt::Key_Delete);
+ addAction(deleteAction);
+ connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteSelectedItems()));
+ };
+
+ ~TreeViewWithDeleteShortcut() {};
+
+public slots:
+
+ void deleteSelectedItems() {
+ // We have to delete the items one by one and update the list of selected items after
+ // each step because every removal will invalidate the model indexes in the list.
+ QModelIndexList selectedItems = selectionModel()->selectedIndexes();
+ while (!selectedItems.isEmpty()) {
+ const QModelIndex index = selectedItems.takeFirst();
+ model()->removeRow(index.row());
+ selectedItems = selectionModel()->selectedIndexes();
+ }
+ }
+};
+
+void DolphinTreeViewTest::testKeyboardNavigationSelectionUpdate() {
+ QStringList items;
+ items << "a" << "b" << "c" << "d" << "e";
+ QStringListModel model(items);
+
+ QModelIndex index[5];
+ for (int i = 0; i < 5; i++) {
+ index[i] = model.index(i, 0);
+ }
+
+ TreeViewWithDeleteShortcut view;
+ view.setModel(&model);
+ view.setSelectionMode(QAbstractItemView::ExtendedSelection);
+ view.resize(400, 400);
+ view.show();
+ QTest::qWaitForWindowShown(&view);
+
+ view.clearSelection();
+ QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
+
+ /**
+ * Check that basic keyboard navigation with arrow keys works.
+ */
+
+ view.setCurrentIndex(index[0]);
+ verifyCurrentItemAndSelection(view, index[0]);
+
+ // Go down -> item 1 ("b") should be selected
+ kDebug() << "Down";
+ QTest::keyClick(view.viewport(), Qt::Key_Down);
+ verifyCurrentItemAndSelection(view, index[1]);
+
+ // Go down -> item 2 ("c") should be selected
+ kDebug() << "Down";
+ QTest::keyClick(view.viewport(), Qt::Key_Down);
+ verifyCurrentItemAndSelection(view, index[2]);
+
+ // Ctrl-Up -> item 2 ("c") remains selected
+ kDebug() << "Ctrl-Up";
+ QTest::keyClick(view.viewport(), Qt::Key_Up, Qt::ControlModifier);
+ verifyCurrentItemAndSelection(view, index[1], index[2]);
+
+ // Go up -> item 0 ("a") should be selected
+ kDebug() << "Up";
+ QTest::keyClick(view.viewport(), Qt::Key_Up);
+ verifyCurrentItemAndSelection(view, index[0]);
+
+ // Shift-Down -> items 0 and 1 ("a" and "b") should be selected
+ kDebug() << "Shift-Down";
+ QTest::keyClick(view.viewport(), Qt::Key_Down, Qt::ShiftModifier);
+ QModelIndexList expectedSelection;
+ expectedSelection << index[0] << index[1];
+ verifyCurrentItemAndSelection(view, index[1], expectedSelection);
+
+ /**
+ * When the first letter of a file name is pressed, this file becomes the current item
+ * and gets selected. If the user then Shift-clicks another item, it is expected that
+ * all items between these two items get selected. Before the bug
+ *
+ * https://bugs.kde.org/show_bug.cgi?id=201459
+ *
+ * was fixed, this was not the case: the starting point for the Shift-selection was not
+ * updated if an item was selected by pressing the first letter of the file name.
+ */
+
+ view.clearSelection();
+ QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
+
+ // Control-click item 0 ("a")
+ kDebug() << "Ctrl-click on \"a\"";
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.visualRect(index[0]).center());
+ verifyCurrentItemAndSelection(view, index[0]);
+
+ // Press "c", such that item 2 ("c") should be the current one.
+ kDebug() << "Press \"c\"";
+ QTest::keyClick(view.viewport(), Qt::Key_C);
+ verifyCurrentItemAndSelection(view, index[2]);
+
+ // Now Shift-Click the last item ("e"). We expect that 3 items ("c", "d", "e") are selected.
+ kDebug() << "Shift-click on \"e\"";
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(index[4]).center());
+ expectedSelection.clear();
+ expectedSelection << index[2] << index[3] << index[4];
+ verifyCurrentItemAndSelection(view, index[4], expectedSelection);
+
+ /**
+ * Starting a drag&drop operation should not clear the selection, see
+ *
+ * https://bugs.kde.org/show_bug.cgi?id=158649
+ */
+
+ view.clearSelection();
+ QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
+
+ // Click item 0 ("a")
+ kDebug() << "Click on \"a\"";
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[0]).center());
+ verifyCurrentItemAndSelection(view, index[0]);
+
+ // Shift-Down -> "a" and "b" should be selected
+ kDebug() << "Shift-Down";
+ QTest::keyClick(view.viewport(), Qt::Key_Down, Qt::ShiftModifier);
+ expectedSelection.clear();
+ expectedSelection << index[0] << index[1];
+ verifyCurrentItemAndSelection(view, index[1], expectedSelection);
+
+ // Press mouse button on item 0 ("a"), but do not release it. Check that the selection is unchanged
+ kDebug() << "Mouse press on \"a\"";
+ QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[0]).center());
+ verifyCurrentItemAndSelection(view, index[0], expectedSelection);
+
+ // Move mouse to item 1 ("b"), check that selection is unchanged
+ kDebug() << "Move mouse to \"b\"";
+ QMouseEvent moveEvent(QEvent::MouseMove, view.visualRect(index[1]).center(), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
+ bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent);
+ QVERIFY(moveEventReceived);
+ verifyCurrentItemAndSelection(view, index[0], expectedSelection);
+
+ // Release mouse button on item 1 ("b"), check that selection is unchanged
+ kDebug() << "Mouse release on \"b\"";
+ QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[1]).center());
+ verifyCurrentItemAndSelection(view, index[0], expectedSelection);
+
+ /**
+ * Keeping Shift+Delete pressed for some time should delete only one item, see
+ *
+ * https://bugs.kde.org/show_bug.cgi?id=259656
+ */
+
+ view.clearSelection();
+ QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
+
+ // Click item 0 ("a")
+ kDebug() << "Click on \"a\"";
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[0]).center());
+ verifyCurrentItemAndSelection(view, index[0]);
+
+ // Press Shift-Delete and keep the keys pressed for some time
+ kDebug() << "Press Shift-Delete";
+ QTest::keyPress(view.viewport(), Qt::Key_Delete, Qt::ShiftModifier);
+ QTest::qWait(200);
+ QTest::keyRelease(view.viewport(), Qt::Key_Delete, Qt::ShiftModifier);
+
+ // Verify that only one item has been deleted
+ QCOMPARE(view.model()->rowCount(), 4);
+}
+