#include <qtestmouse.h>
#include <qtestkeyboard.h>
-#include <KDirLister>
#include "kitemviews/kitemlistcontainer.h"
#include "kitemviews/kfileitemlistview.h"
#include "kitemviews/kfileitemmodel.h"
#include "kitemviews/kitemlistcontroller.h"
#include "kitemviews/kitemlistselectionmanager.h"
-#include "kitemviews/kitemlistviewlayouter_p.h"
+#include "kitemviews/private/kitemlistviewlayouter.h"
#include "testdir.h"
+#include <KConfigGroup>
+#include <KGlobalSettings>
+
+#include <QGraphicsSceneMouseEvent>
+
namespace {
const int DefaultTimeout = 2000;
};
-Q_DECLARE_METATYPE(KFileItemListView::Layout);
+Q_DECLARE_METATYPE(KFileItemListView::ItemLayout);
Q_DECLARE_METATYPE(Qt::Orientation);
Q_DECLARE_METATYPE(KItemListController::SelectionBehavior);
+Q_DECLARE_METATYPE(QSet<int>);
class KItemListControllerTest : public QObject
{
void testKeyboardNavigation_data();
void testKeyboardNavigation();
+ void testMouseClickActivation();
private:
/**
KItemListController* m_controller;
KItemListSelectionManager* m_selectionManager;
KFileItemModel* m_model;
- KDirLister* m_dirLister;
TestDir* m_testDir;
KItemListContainer* m_container;
};
*/
void KItemListControllerTest::initTestCase()
{
+ qRegisterMetaType<QSet<int> >("QSet<int>");
+
m_testDir = new TestDir();
- m_dirLister = new KDirLister();
- m_model = new KFileItemModel(m_dirLister);
- m_container = new KItemListContainer();
+ m_model = new KFileItemModel();
+ m_view = new KFileItemListView();
+ m_controller = new KItemListController(m_model, m_view, this);
+ m_container = new KItemListContainer(m_controller);
m_controller = m_container->controller();
m_controller->setSelectionBehavior(KItemListController::MultiSelection);
m_selectionManager = m_controller->selectionManager();
- m_view = new KFileItemListView();
- m_controller->setView(m_view);
- m_controller->setModel(m_model);
-
QStringList files;
files
<< "a1" << "a2" << "a3"
<< "e1" << "e2" << "e3" << "e4" << "e5" << "e6" << "e7";
m_testDir->createFiles(files);
- m_dirLister->openUrl(m_testDir->url());
- QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout));
m_container->show();
QTest::qWaitForWindowShown(m_container);
void KItemListControllerTest::cleanupTestCase()
{
- delete m_view;
- m_view = 0;
-
delete m_container;
m_container = 0;
- m_controller = 0;
-
- delete m_model;
- m_model = 0;
-
- delete m_dirLister;
- m_dirLister = 0;
delete m_testDir;
m_testDir = 0;
/**
* \class ViewState is a small helper struct that represents a certain state
- * of the view, including the current item and the selected items.
+ * of the view, including the current item, the selected items in MultiSelection
+ * mode (in the other modes, the selection is either empty or equal to the
+ * current item), and the information whether items were activated by the last
+ * key press.
*/
struct ViewState {
- ViewState(int current, QSet<int> selection) :
+ ViewState(int current, const QSet<int> selection, bool activated = false) :
m_current(current),
- m_selection(selection)
+ m_selection(selection),
+ m_activated(activated)
{}
int m_current;
QSet<int> m_selection;
+ bool m_activated;
};
// We have to define a typedef for the pair in order to make the test compile.
*/
void KItemListControllerTest::testKeyboardNavigation_data()
{
- QTest::addColumn<KFileItemListView::Layout>("layout");
+ QTest::addColumn<KFileItemListView::ItemLayout>("layout");
QTest::addColumn<Qt::Orientation>("scrollOrientation");
QTest::addColumn<int>("columnCount");
QTest::addColumn<KItemListController::SelectionBehavior>("selectionBehavior");
QTest::addColumn<bool>("groupingEnabled");
QTest::addColumn<QList<QPair<KeyPress, ViewState> > >("testList");
- QList<KFileItemListView::Layout> layoutList;
- QHash<KFileItemListView::Layout, QString> layoutNames;
+ QList<KFileItemListView::ItemLayout> layoutList;
+ QHash<KFileItemListView::ItemLayout, QString> layoutNames;
layoutList.append(KFileItemListView::IconsLayout);
layoutNames[KFileItemListView::IconsLayout] = "Icons";
layoutList.append(KFileItemListView::CompactLayout);
groupingEnabledList.append(true);
groupingEnabledNames[true] = "grouping enabled";
- foreach (KFileItemListView::Layout layout, layoutList) {
+ foreach (const KFileItemListView::ItemLayout& layout, layoutList) {
// The following settings depend on the layout.
// Note that 'columns' are actually 'rows' in
// Compact layout.
}
foreach (int columnCount, columnCountList) {
- foreach (KItemListController::SelectionBehavior selectionBehavior, selectionBehaviorList) {
- foreach (bool groupingEnabled, groupingEnabledList) {
+ foreach (const KItemListController::SelectionBehavior& selectionBehavior, selectionBehaviorList) {
+ foreach (bool groupingEnabled, groupingEnabledList) { // krazy:exclude=foreach
QList<QPair<KeyPress, ViewState> > testList;
// First, key presses which should have the same effect
// for any layout and any number of columns.
testList
<< qMakePair(KeyPress(nextItemKey), ViewState(1, QSet<int>() << 1))
+ << qMakePair(KeyPress(Qt::Key_Return), ViewState(1, QSet<int>() << 1, true))
+ << qMakePair(KeyPress(Qt::Key_Enter), ViewState(1, QSet<int>() << 1, true))
<< qMakePair(KeyPress(nextItemKey), ViewState(2, QSet<int>() << 2))
<< qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, QSet<int>() << 2 << 3))
+ << qMakePair(KeyPress(Qt::Key_Return), ViewState(3, QSet<int>() << 2 << 3, true))
<< qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(2, QSet<int>() << 2))
<< qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, QSet<int>() << 2 << 3))
<< qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, QSet<int>() << 2 << 3))
+ << qMakePair(KeyPress(Qt::Key_Return), ViewState(4, QSet<int>() << 2 << 3, true))
<< qMakePair(KeyPress(previousItemKey), ViewState(3, QSet<int>() << 3))
<< qMakePair(KeyPress(Qt::Key_Home, Qt::ShiftModifier), ViewState(0, QSet<int>() << 0 << 1 << 2 << 3))
<< qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(1, QSet<int>() << 0 << 1 << 2 << 3))
<< qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, QSet<int>() << 0 << 1 << 2 << 3))
<< qMakePair(KeyPress(Qt::Key_End), ViewState(19, QSet<int>() << 19))
<< qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(18, QSet<int>() << 18 << 19))
- << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, QSet<int>() << 0));
+ << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, QSet<int>() << 0))
+ << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, QSet<int>()))
+ << qMakePair(KeyPress(Qt::Key_Enter), ViewState(0, QSet<int>(), true))
+ << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, QSet<int>() << 0));
// Next, we test combinations of key presses which only work for a
// particular number of columns and either enabled or disabled grouping.
*/
void KItemListControllerTest::testKeyboardNavigation()
{
- QFETCH(KFileItemListView::Layout, layout);
+ QFETCH(KFileItemListView::ItemLayout, layout);
QFETCH(Qt::Orientation, scrollOrientation);
QFETCH(int, columnCount);
QFETCH(KItemListController::SelectionBehavior, selectionBehavior);
adjustGeometryForColumnCount(columnCount);
QCOMPARE(m_view->m_layouter->m_columnCount, columnCount);
+ QSignalSpy spySingleItemActivated(m_controller, SIGNAL(itemActivated(int)));
+ QSignalSpy spyMultipleItemsActivated(m_controller, SIGNAL(itemsActivated(QSet<int>)));
+
while (!testList.isEmpty()) {
const QPair<KeyPress, ViewState> test = testList.takeFirst();
const Qt::Key key = test.first.m_key;
const Qt::KeyboardModifiers modifier = test.first.m_modifier;
const int current = test.second.m_current;
const QSet<int> selection = test.second.m_selection;
+ const bool activated = test.second.m_activated;
QTest::keyClick(m_container, key, modifier);
+
QCOMPARE(m_selectionManager->currentItem(), current);
switch (selectionBehavior) {
case KItemListController::NoSelection: QVERIFY(m_selectionManager->selectedItems().isEmpty()); break;
case KItemListController::SingleSelection: QCOMPARE(m_selectionManager->selectedItems(), QSet<int>() << current); break;
case KItemListController::MultiSelection: QCOMPARE(m_selectionManager->selectedItems(), selection); break;
}
+
+ if (activated) {
+ switch (selectionBehavior) {
+ case KItemListController::MultiSelection:
+ if (!selection.isEmpty()) {
+ // The selected items should be activated.
+ if (selection.count() == 1) {
+ QVERIFY(!spySingleItemActivated.isEmpty());
+ QCOMPARE(qvariant_cast<int>(spySingleItemActivated.takeFirst().at(0)), selection.toList().at(0));
+ QVERIFY(spyMultipleItemsActivated.isEmpty());
+ } else {
+ QVERIFY(spySingleItemActivated.isEmpty());
+ QVERIFY(!spyMultipleItemsActivated.isEmpty());
+ QCOMPARE(qvariant_cast<QSet<int> >(spyMultipleItemsActivated.takeFirst().at(0)), selection);
+ }
+ break;
+ }
+ // No items are selected. Therefore, the current item should be activated.
+ // This is handled by falling through to the NoSelection/SingleSelection case.
+ case KItemListController::NoSelection:
+ case KItemListController::SingleSelection:
+ // In NoSelection and SingleSelection mode, the current item should be activated.
+ QVERIFY(!spySingleItemActivated.isEmpty());
+ QCOMPARE(qvariant_cast<int>(spySingleItemActivated.takeFirst().at(0)), current);
+ QVERIFY(spyMultipleItemsActivated.isEmpty());
+ break;
+ }
+ }
+ }
+}
+
+void KItemListControllerTest::testMouseClickActivation()
+{
+ m_view->setItemLayout(KFileItemListView::IconsLayout);
+
+ // Make sure that we have a large window, such that
+ // the items are visible and clickable.
+ adjustGeometryForColumnCount(5);
+
+ // Make sure that the first item is visible in the view.
+ m_view->setScrollOffset(0);
+ QCOMPARE(m_view->firstVisibleIndex(), 0);
+
+ const QPointF pos = m_view->itemContextRect(0).center();
+
+ // Save the "single click" setting.
+ const bool restoreKGlobalSettingsSingleClick = KGlobalSettings::singleClick();
+
+ KConfig config("kcminputrc");
+ KConfigGroup group = config.group("KDE");
+
+ QGraphicsSceneMouseEvent mousePressEvent(QEvent::GraphicsSceneMousePress);
+ mousePressEvent.setPos(pos);
+ mousePressEvent.setButton(Qt::LeftButton);
+ mousePressEvent.setButtons(Qt::LeftButton);
+
+ QGraphicsSceneMouseEvent mouseReleaseEvent(QEvent::GraphicsSceneMouseRelease);
+ mouseReleaseEvent.setPos(pos);
+ mouseReleaseEvent.setButton(Qt::LeftButton);
+ mouseReleaseEvent.setButtons(Qt::NoButton);
+
+ QSignalSpy spyItemActivated(m_controller, SIGNAL(itemActivated(int)));
+
+ // Default setting: single click activation.
+ group.writeEntry("SingleClick", true, KConfig::Persistent|KConfig::Global);
+ config.sync();
+ KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE);
+
+ int iterations = 0;
+ const int maxIterations = 20;
+ while (!KGlobalSettings::singleClick() && iterations < maxIterations) {
+ QTest::qWait(50);
+ ++iterations;
+ }
+
+ if (!KGlobalSettings::singleClick()) {
+ // TODO: Try to find a way to make sure that changing the global setting works.
+ QSKIP("Failed to change the KGlobalSettings::singleClick() setting!", SkipSingle);
+ }
+
+ m_view->event(&mousePressEvent);
+ m_view->event(&mouseReleaseEvent);
+ QCOMPARE(spyItemActivated.count(), 1);
+ spyItemActivated.clear();
+
+ // Set the global setting to "double click activation".
+ group.writeEntry("SingleClick", false, KConfig::Persistent|KConfig::Global);
+ config.sync();
+ KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE);
+
+ iterations = 0;
+ while (KGlobalSettings::singleClick() && iterations < maxIterations) {
+ QTest::qWait(50);
+ ++iterations;
+ }
+
+ if (KGlobalSettings::singleClick()) {
+ // TODO: Try to find a way to make sure that changing the global setting works.
+ QSKIP("Failed to change the KGlobalSettings::singleClick() setting!", SkipSingle);
+ }
+
+ m_view->event(&mousePressEvent);
+ m_view->event(&mouseReleaseEvent);
+ QCOMPARE(spyItemActivated.count(), 0);
+ spyItemActivated.clear();
+
+ // Enforce single click activation in the controller.
+ m_controller->setSingleClickActivationEnforced(true);
+ m_view->event(&mousePressEvent);
+ m_view->event(&mouseReleaseEvent);
+ QCOMPARE(spyItemActivated.count(), 1);
+ spyItemActivated.clear();
+
+ // Do not enforce single click activation in the controller.
+ m_controller->setSingleClickActivationEnforced(false);
+ m_view->event(&mousePressEvent);
+ m_view->event(&mouseReleaseEvent);
+ QCOMPARE(spyItemActivated.count(), 0);
+ spyItemActivated.clear();
+
+ // Set the global setting back to "single click activation".
+ group.writeEntry("SingleClick", true, KConfig::Persistent|KConfig::Global);
+ config.sync();
+ KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE);
+
+ iterations = 0;
+ while (!KGlobalSettings::singleClick() && iterations < maxIterations) {
+ QTest::qWait(50);
+ ++iterations;
+ }
+
+ if (!KGlobalSettings::singleClick()) {
+ // TODO: Try to find a way to make sure that changing the global setting works.
+ QSKIP("Failed to change the KGlobalSettings::singleClick() setting!", SkipSingle);
+ }
+
+ m_view->event(&mousePressEvent);
+ m_view->event(&mouseReleaseEvent);
+ QCOMPARE(spyItemActivated.count(), 1);
+ spyItemActivated.clear();
+
+ // Enforce single click activation in the controller.
+ m_controller->setSingleClickActivationEnforced(true);
+ m_view->event(&mousePressEvent);
+ m_view->event(&mouseReleaseEvent);
+ QCOMPARE(spyItemActivated.count(), 1);
+ spyItemActivated.clear();
+
+ // Restore previous settings.
+ m_controller->setSingleClickActivationEnforced(true);
+ group.writeEntry("SingleClick", restoreKGlobalSettingsSingleClick, KConfig::Persistent|KConfig::Global);
+ config.sync();
+ KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_MOUSE);
+
+ iterations = 0;
+ while (KGlobalSettings::singleClick() != restoreKGlobalSettingsSingleClick && iterations < maxIterations) {
+ QTest::qWait(50);
+ ++iterations;
+ }
+
+ if (KGlobalSettings::singleClick() != restoreKGlobalSettingsSingleClick) {
+ // TODO: Try to find a way to make sure that changing the global setting works.
+ QSKIP("Failed to change the KGlobalSettings::singleClick() setting!", SkipSingle);
}
}