summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp')
-rw-r--r--tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp3942
1 files changed, 3942 insertions, 0 deletions
diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp
new file mode 100644
index 0000000000..d6cdf00218
--- /dev/null
+++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp
@@ -0,0 +1,3942 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qabstractitemview.h>
+#include <QtTest/QtTest>
+#include <QtGui/QtGui>
+#include <QtWidgets/QtWidgets>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+Q_DECLARE_METATYPE(QModelIndex)
+#ifndef QT_NO_DRAGANDDROP
+Q_DECLARE_METATYPE(QAbstractItemView::DragDropMode)
+#endif
+Q_DECLARE_METATYPE(QAbstractItemView::EditTriggers)
+Q_DECLARE_METATYPE(QAbstractItemView::EditTrigger)
+
+static void initStandardTreeModel(QStandardItemModel *model)
+{
+ QStandardItem *item;
+ item = new QStandardItem(QLatin1String("Row 1 Item"));
+ model->insertRow(0, item);
+
+ item = new QStandardItem(QLatin1String("Row 2 Item"));
+ item->setCheckable(true);
+ model->insertRow(1, item);
+
+ QStandardItem *childItem = new QStandardItem(QLatin1String("Row 2 Child Item"));
+ item->setChild(0, childItem);
+
+ item = new QStandardItem(QLatin1String("Row 3 Item"));
+ item->setIcon(QIcon());
+ model->insertRow(2, item);
+}
+
+struct PublicView : public QTreeView
+{
+ inline void executeDelayedItemsLayout()
+ { QTreeView::executeDelayedItemsLayout(); }
+
+ enum PublicCursorAction {
+ MoveUp = QAbstractItemView::MoveUp,
+ MoveDown = QAbstractItemView::MoveDown,
+ MoveLeft = QAbstractItemView::MoveLeft,
+ MoveRight = QAbstractItemView::MoveRight,
+ MoveHome = QAbstractItemView::MoveHome,
+ MoveEnd = QAbstractItemView::MoveEnd,
+ MovePageUp = QAbstractItemView::MovePageUp,
+ MovePageDown = QAbstractItemView::MovePageDown,
+ MoveNext = QAbstractItemView::MoveNext,
+ MovePrevious = QAbstractItemView::MovePrevious
+ };
+
+ inline QModelIndex moveCursor(PublicCursorAction ca, Qt::KeyboardModifiers kbm)
+ { return QTreeView::moveCursor((CursorAction)ca, kbm); }
+
+ inline void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
+ {
+ QTreeView::setSelection(rect, command);
+ }
+ inline int state()
+ {
+ return QTreeView::state();
+ }
+
+ inline int rowHeight(QModelIndex idx) { return QTreeView::rowHeight(idx); }
+ inline int indexRowSizeHint(const QModelIndex &index) const { return QTreeView::indexRowSizeHint(index); }
+
+ inline QModelIndexList selectedIndexes() const { return QTreeView::selectedIndexes(); }
+
+ inline QStyleOptionViewItem viewOptions() const { return QTreeView::viewOptions(); }
+ inline int sizeHintForColumn(int column) const { return QTreeView::sizeHintForColumn(column); }
+};
+
+class tst_QTreeView : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QTreeView();
+ virtual ~tst_QTreeView();
+
+
+public slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void init();
+ void cleanup();
+
+ void selectionOrderTest();
+
+private slots:
+ void getSetCheck();
+
+ // one test per QTreeView property
+ void construction();
+ void alternatingRowColors();
+ void currentIndex_data();
+ void currentIndex();
+#ifndef QT_NO_DRAGANDDROP
+ void dragDropMode_data();
+ void dragDropMode();
+ void dragDropModeFromDragEnabledAndAcceptDrops_data();
+ void dragDropModeFromDragEnabledAndAcceptDrops();
+ void dragDropOverwriteMode();
+#endif
+ void editTriggers_data();
+ void editTriggers();
+ void hasAutoScroll();
+ void horizontalScrollMode();
+ void iconSize();
+ void indexAt();
+ void indexWidget();
+ void itemDelegate();
+ void itemDelegateForColumnOrRow();
+ void keyboardSearch();
+ void setModel();
+ void openPersistentEditor();
+ void rootIndex();
+
+ // specialized tests below
+ void setHeader();
+ void columnHidden();
+ void rowHidden();
+ void noDelegate();
+ void noModel();
+ void emptyModel();
+ void removeRows();
+ void removeCols();
+ void expandAndCollapse_data();
+ void expandAndCollapse();
+ void expandAndCollapseAll();
+ void expandWithNoChildren();
+ void keyboardNavigation();
+ void headerSections();
+ void moveCursor_data();
+ void moveCursor();
+ void setSelection_data();
+ void setSelection();
+ void extendedSelection_data();
+ void extendedSelection();
+ void indexAbove();
+ void indexBelow();
+ void clicked();
+ void mouseDoubleClick();
+ void rowsAboutToBeRemoved();
+ void headerSections_unhideSection();
+ void columnAt();
+ void scrollTo();
+ void rowsAboutToBeRemoved_move();
+ void resizeColumnToContents();
+ void insertAfterSelect();
+ void removeAfterSelect();
+ void hiddenItems();
+ void spanningItems();
+ void rowSizeHint();
+ void setSortingEnabled();
+ void headerHidden();
+
+ void selection();
+ void removeAndInsertExpandedCol0();
+ void selectionWithHiddenItems();
+ void selectAll();
+
+ void disabledButCheckable();
+ void sortByColumn_data();
+ void sortByColumn();
+
+ void evilModel_data();
+ void evilModel();
+
+ void indexRowSizeHint();
+ void addRowsWhileSectionsAreHidden();
+ void filterProxyModelCrash();
+ void styleOptionViewItem();
+ void keyboardNavigationWithDisabled();
+
+ // task-specific tests:
+ void task174627_moveLeftToRoot();
+ void task171902_expandWith1stColHidden();
+ void task203696_hidingColumnsAndRowsn();
+ void task211293_removeRootIndex();
+ void task216717_updateChildren();
+ void task220298_selectColumns();
+ void task224091_appendColumns();
+ void task225539_deleteModel();
+ void task230123_setItemsExpandable();
+ void task202039_closePersistentEditor();
+ void task238873_avoidAutoReopening();
+ void task244304_clickOnDecoration();
+ void task246536_scrollbarsNotWorking();
+ void task250683_wrongSectionSize();
+ void task239271_addRowsWithFirstColumnHidden();
+ void task254234_proxySort();
+ void task248022_changeSelection();
+ void task245654_changeModelAndExpandAll();
+ void doubleClickedWithSpans();
+ void taskQTBUG_6450_selectAllWith1stColumnHidden();
+ void taskQTBUG_9216_setSizeAndUniformRowHeightsWrongRepaint();
+ void taskQTBUG_11466_keyboardNavigationRegression();
+ void taskQTBUG_13567_removeLastItemRegression();
+};
+
+class QtTestModel: public QAbstractItemModel
+{
+public:
+ QtTestModel(QObject *parent = 0): QAbstractItemModel(parent),
+ fetched(false), rows(0), cols(0), levels(INT_MAX), wrongIndex(false) { init(); }
+
+ QtTestModel(int _rows, int _cols, QObject *parent = 0): QAbstractItemModel(parent),
+ fetched(false), rows(_rows), cols(_cols), levels(INT_MAX), wrongIndex(false) { init(); }
+
+ void init() {
+ decorationsEnabled = false;
+ }
+
+ inline qint32 level(const QModelIndex &index) const {
+ return index.isValid() ? qint32(index.internalId()) : qint32(-1);
+ }
+
+ bool canFetchMore(const QModelIndex &) const {
+ return !fetched;
+ }
+
+ void fetchMore(const QModelIndex &) {
+ fetched = true;
+ }
+
+ bool hasChildren(const QModelIndex &parent = QModelIndex()) const {
+ bool hasFetched = fetched;
+ fetched = true;
+ bool r = QAbstractItemModel::hasChildren(parent);
+ fetched = hasFetched;
+ return r;
+ }
+
+ int rowCount(const QModelIndex& parent = QModelIndex()) const {
+ if (!fetched)
+ qFatal("%s: rowCount should not be called before fetching", Q_FUNC_INFO);
+ if ((parent.column() > 0) || (level(parent) > levels))
+ return 0;
+ return rows;
+ }
+ int columnCount(const QModelIndex& parent = QModelIndex()) const {
+ if ((parent.column() > 0) || (level(parent) > levels))
+ return 0;
+ return cols;
+ }
+
+ bool isEditable(const QModelIndex &index) const {
+ if (index.isValid())
+ return true;
+ return false;
+ }
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const
+ {
+ if (row < 0 || column < 0 || (level(parent) > levels) || column >= cols || row >= rows) {
+ return QModelIndex();
+ }
+ QModelIndex i = createIndex(row, column, level(parent) + 1);
+ parentHash[i] = parent;
+ return i;
+ }
+
+ QModelIndex parent(const QModelIndex &index) const
+ {
+ if (!parentHash.contains(index))
+ return QModelIndex();
+ return parentHash[index];
+ }
+
+ QVariant data(const QModelIndex &idx, int role) const
+ {
+ if (!idx.isValid())
+ return QVariant();
+
+ if (role == Qt::DisplayRole) {
+ if (idx.row() < 0 || idx.column() < 0 || idx.column() >= cols || idx.row() >= rows) {
+ wrongIndex = true;
+ qWarning("Invalid modelIndex [%d,%d,%p]", idx.row(), idx.column(),
+ idx.internalPointer());
+ }
+ if (idx.row() & 1)
+ return QString("[%1,%2,%3] - this item is extra wide").arg(idx.row()).arg(idx.column()).arg(level(idx));
+ return QString("[%1,%2,%3]").arg(idx.row()).arg(idx.column()).arg(level(idx));
+ }
+ if (decorationsEnabled && role == Qt::DecorationRole) {
+ QPixmap pm(16,16);
+ pm.fill(QColor::fromHsv((idx.column() % 16)*8 + 64, 254, (idx.row() % 16)*8 + 32));
+ return pm;
+ }
+ return QVariant();
+ }
+
+ void removeLastRow()
+ {
+ beginRemoveRows(QModelIndex(), rows - 1, rows - 1);
+ --rows;
+ endRemoveRows();
+ }
+
+ void removeAllRows()
+ {
+ beginRemoveRows(QModelIndex(), 0, rows - 1);
+ rows = 0;
+ endRemoveRows();
+ }
+
+ void removeLastColumn()
+ {
+ beginRemoveColumns(QModelIndex(), cols - 1, cols - 1);
+ --cols;
+ endRemoveColumns();
+ }
+
+ void removeAllColumns()
+ {
+ beginRemoveColumns(QModelIndex(), 0, cols - 1);
+ cols = 0;
+ endRemoveColumns();
+ }
+
+ void insertNewRow()
+ {
+ beginInsertRows(QModelIndex(), rows - 1, rows - 1);
+ ++rows;
+ endInsertRows();
+ }
+
+ void setDecorationsEnabled(bool enable)
+ {
+ decorationsEnabled = enable;
+ }
+
+ mutable bool fetched;
+ bool decorationsEnabled;
+ int rows, cols;
+ int levels;
+ mutable bool wrongIndex;
+ mutable QMap<QModelIndex,QModelIndex> parentHash;
+};
+
+tst_QTreeView::tst_QTreeView()
+{
+}
+
+tst_QTreeView::~tst_QTreeView()
+{
+}
+
+void tst_QTreeView::initTestCase()
+{
+#ifdef Q_OS_WINCE //disable magic for WindowsCE
+ qApp->setAutoMaximizeThreshold(-1);
+#endif
+ qRegisterMetaType<QModelIndex>("QModelIndex");
+}
+
+void tst_QTreeView::cleanupTestCase()
+{
+}
+
+void tst_QTreeView::init()
+{
+}
+
+void tst_QTreeView::cleanup()
+{
+}
+
+// Testing get/set functions
+void tst_QTreeView::getSetCheck()
+{
+ QTreeView obj1;
+
+ // int QTreeView::indentation()
+ // void QTreeView::setIndentation(int)
+ QCOMPARE(obj1.indentation(), 20);
+ obj1.setIndentation(0);
+ QCOMPARE(obj1.indentation(), 0);
+ obj1.setIndentation(INT_MIN);
+ QCOMPARE(obj1.indentation(), INT_MIN);
+ obj1.setIndentation(INT_MAX);
+ QCOMPARE(obj1.indentation(), INT_MAX);
+
+ // bool QTreeView::rootIsDecorated()
+ // void QTreeView::setRootIsDecorated(bool)
+ QCOMPARE(obj1.rootIsDecorated(), true);
+ obj1.setRootIsDecorated(false);
+ QCOMPARE(obj1.rootIsDecorated(), false);
+ obj1.setRootIsDecorated(true);
+ QCOMPARE(obj1.rootIsDecorated(), true);
+
+ // bool QTreeView::uniformRowHeights()
+ // void QTreeView::setUniformRowHeights(bool)
+ QCOMPARE(obj1.uniformRowHeights(), false);
+ obj1.setUniformRowHeights(false);
+ QCOMPARE(obj1.uniformRowHeights(), false);
+ obj1.setUniformRowHeights(true);
+ QCOMPARE(obj1.uniformRowHeights(), true);
+
+ // bool QTreeView::itemsExpandable()
+ // void QTreeView::setItemsExpandable(bool)
+ QCOMPARE(obj1.itemsExpandable(), true);
+ obj1.setItemsExpandable(false);
+ QCOMPARE(obj1.itemsExpandable(), false);
+ obj1.setItemsExpandable(true);
+ QCOMPARE(obj1.itemsExpandable(), true);
+
+ // bool QTreeView::allColumnsShowFocus
+ // void QTreeView::setAllColumnsShowFocus
+ QCOMPARE(obj1.allColumnsShowFocus(), false);
+ obj1.setAllColumnsShowFocus(false);
+ QCOMPARE(obj1.allColumnsShowFocus(), false);
+ obj1.setAllColumnsShowFocus(true);
+ QCOMPARE(obj1.allColumnsShowFocus(), true);
+
+ // bool QTreeView::isAnimated
+ // void QTreeView::setAnimated
+ QCOMPARE(obj1.isAnimated(), false);
+ obj1.setAnimated(false);
+ QCOMPARE(obj1.isAnimated(), false);
+ obj1.setAnimated(true);
+ QCOMPARE(obj1.isAnimated(), true);
+
+ // bool QTreeView::setSortingEnabled
+ // void QTreeView::isSortingEnabled
+ QCOMPARE(obj1.isSortingEnabled(), false);
+ obj1.setSortingEnabled(false);
+ QCOMPARE(obj1.isSortingEnabled(), false);
+ obj1.setSortingEnabled(true);
+ QCOMPARE(obj1.isSortingEnabled(), true);
+}
+
+void tst_QTreeView::construction()
+{
+ QTreeView view;
+
+ // QAbstractItemView properties
+ QVERIFY(!view.alternatingRowColors());
+ QCOMPARE(view.currentIndex(), QModelIndex());
+#ifndef QT_NO_DRAGANDDROP
+ QCOMPARE(view.dragDropMode(), QAbstractItemView::NoDragDrop);
+ QVERIFY(!view.dragDropOverwriteMode());
+ QVERIFY(!view.dragEnabled());
+#endif
+ QCOMPARE(view.editTriggers(), QAbstractItemView::EditKeyPressed | QAbstractItemView::DoubleClicked);
+ QVERIFY(view.hasAutoScroll());
+ QCOMPARE(view.horizontalScrollMode(), QAbstractItemView::ScrollPerPixel);
+ QCOMPARE(view.iconSize(), QSize());
+ QCOMPARE(view.indexAt(QPoint()), QModelIndex());
+ QVERIFY(!view.indexWidget(QModelIndex()));
+ QVERIFY(qobject_cast<QStyledItemDelegate *>(view.itemDelegate()));
+ QVERIFY(!view.itemDelegateForColumn(-1));
+ QVERIFY(!view.itemDelegateForColumn(0));
+ QVERIFY(!view.itemDelegateForColumn(1));
+ QVERIFY(!view.itemDelegateForRow(-1));
+ QVERIFY(!view.itemDelegateForRow(0));
+ QVERIFY(!view.itemDelegateForRow(1));
+ QVERIFY(!view.model());
+ QCOMPARE(view.rootIndex(), QModelIndex());
+ QCOMPARE(view.selectionBehavior(), QAbstractItemView::SelectRows);
+ QCOMPARE(view.selectionMode(), QAbstractItemView::SingleSelection);
+ QVERIFY(!view.selectionModel());
+#ifndef QT_NO_DRAGANDDROP
+ QVERIFY(view.showDropIndicator());
+#endif
+ QCOMPARE(view.QAbstractItemView::sizeHintForColumn(-1), -1); // <- protected in QTreeView
+ QCOMPARE(view.QAbstractItemView::sizeHintForColumn(0), -1); // <- protected in QTreeView
+ QCOMPARE(view.QAbstractItemView::sizeHintForColumn(1), -1); // <- protected in QTreeView
+ QCOMPARE(view.sizeHintForIndex(QModelIndex()), QSize());
+ QCOMPARE(view.sizeHintForRow(-1), -1);
+ QCOMPARE(view.sizeHintForRow(0), -1);
+ QCOMPARE(view.sizeHintForRow(1), -1);
+ QVERIFY(!view.tabKeyNavigation());
+ QCOMPARE(view.textElideMode(), Qt::ElideRight);
+ QCOMPARE(view.verticalScrollMode(), QAbstractItemView::ScrollPerItem);
+ QCOMPARE(view.visualRect(QModelIndex()), QRect());
+
+ // QTreeView properties
+ QVERIFY(!view.allColumnsShowFocus());
+ QCOMPARE(view.autoExpandDelay(), -1);
+ QCOMPARE(view.columnAt(-1), -1);
+ QCOMPARE(view.columnAt(0), -1);
+ QCOMPARE(view.columnAt(1), -1);
+ QCOMPARE(view.columnViewportPosition(-1), -1);
+ QCOMPARE(view.columnViewportPosition(0), -1);
+ QCOMPARE(view.columnViewportPosition(1), -1);
+ QCOMPARE(view.columnWidth(-1), 0);
+ QCOMPARE(view.columnWidth(0), 0);
+ QCOMPARE(view.columnWidth(1), 0);
+ QVERIFY(view.header());
+ QCOMPARE(view.indentation(), 20);
+ QCOMPARE(view.indexAbove(QModelIndex()), QModelIndex());
+ QCOMPARE(view.indexBelow(QModelIndex()), QModelIndex());
+ QVERIFY(!view.isAnimated());
+ QVERIFY(!view.isColumnHidden(-1));
+ QVERIFY(!view.isColumnHidden(0));
+ QVERIFY(!view.isColumnHidden(1));
+ QVERIFY(!view.isExpanded(QModelIndex()));
+ QVERIFY(!view.isRowHidden(-1, QModelIndex()));
+ QVERIFY(!view.isRowHidden(0, QModelIndex()));
+ QVERIFY(!view.isRowHidden(1, QModelIndex()));
+ QVERIFY(!view.isFirstColumnSpanned(-1, QModelIndex()));
+ QVERIFY(!view.isFirstColumnSpanned(0, QModelIndex()));
+ QVERIFY(!view.isFirstColumnSpanned(1, QModelIndex()));
+ QVERIFY(!view.isSortingEnabled());
+ QVERIFY(view.itemsExpandable());
+ QVERIFY(view.rootIsDecorated());
+ QVERIFY(!view.uniformRowHeights());
+ QCOMPARE(view.visualRect(QModelIndex()), QRect());
+ QVERIFY(!view.wordWrap());
+}
+
+void tst_QTreeView::alternatingRowColors()
+{
+ QTreeView view;
+ QVERIFY(!view.alternatingRowColors());
+ view.setAlternatingRowColors(true);
+ QVERIFY(view.alternatingRowColors());
+ view.setAlternatingRowColors(false);
+ QVERIFY(!view.alternatingRowColors());
+
+ // ### Test visual effect.
+}
+
+void tst_QTreeView::currentIndex_data()
+{
+ QTest::addColumn<int>("row");
+ QTest::addColumn<int>("column");
+ QTest::addColumn<int>("indexRow");
+ QTest::addColumn<int>("indexColumn");
+ QTest::addColumn<int>("parentIndexRow");
+ QTest::addColumn<int>("parentIndexColumn");
+
+ QTest::newRow("-1, -1") << -1 << -1 << -1 << -1 << -1 << -1;
+ QTest::newRow("-1, 0") << -1 << 0 << -1 << -1 << -1 << -1;
+ QTest::newRow("0, -1") << 0 << -1 << -1 << -1 << -1 << -1;
+ QTest::newRow("0, 0") << 0 << 0 << 0 << 0 << -1 << -1;
+ QTest::newRow("0, 1") << 0 << 0 << 0 << 0 << -1 << -1;
+ QTest::newRow("1, 0") << 1 << 0 << 1 << 0 << -1 << -1;
+ QTest::newRow("1, 1") << 1 << 1 << -1 << -1 << -1 << -1;
+ QTest::newRow("2, 0") << 2 << 0 << 2 << 0 << -1 << -1;
+ QTest::newRow("2, 1") << 2 << 1 << -1 << -1 << -1 << -1;
+ QTest::newRow("3, -1") << 3 << -1 << -1 << -1 << -1 << -1;
+ QTest::newRow("3, 0") << 3 << 0 << -1 << -1 << -1 << -1;
+ QTest::newRow("3, 1") << 3 << 1 << -1 << -1 << -1 << -1;
+}
+
+void tst_QTreeView::currentIndex()
+{
+ QFETCH(int, row);
+ QFETCH(int, column);
+ QFETCH(int, indexRow);
+ QFETCH(int, indexColumn);
+ QFETCH(int, parentIndexRow);
+ QFETCH(int, parentIndexColumn);
+
+ QTreeView view;
+ QStandardItemModel treeModel;
+ initStandardTreeModel(&treeModel);
+ view.setModel(&treeModel);
+
+ QCOMPARE(view.currentIndex(), QModelIndex());
+ view.setCurrentIndex(view.model()->index(row, column));
+ QCOMPARE(view.currentIndex().row(), indexRow);
+ QCOMPARE(view.currentIndex().column(), indexColumn);
+ QCOMPARE(view.currentIndex().parent().row(), parentIndexRow);
+ QCOMPARE(view.currentIndex().parent().column(), parentIndexColumn);
+
+ // ### Test child and grandChild indexes.
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+void tst_QTreeView::dragDropMode_data()
+{
+ QTest::addColumn<QAbstractItemView::DragDropMode>("dragDropMode");
+ QTest::addColumn<bool>("acceptDrops");
+ QTest::addColumn<bool>("dragEnabled");
+ QTest::newRow("NoDragDrop") << QAbstractItemView::NoDragDrop << false << false;
+ QTest::newRow("DragOnly") << QAbstractItemView::DragOnly << false << true;
+ QTest::newRow("DropOnly") << QAbstractItemView::DropOnly << true << false;
+ QTest::newRow("DragDrop") << QAbstractItemView::DragDrop << true << true;
+ QTest::newRow("InternalMove") << QAbstractItemView::InternalMove << true << true;
+}
+
+void tst_QTreeView::dragDropMode()
+{
+ QFETCH(QAbstractItemView::DragDropMode, dragDropMode);
+ QFETCH(bool, acceptDrops);
+ QFETCH(bool, dragEnabled);
+
+ QTreeView view;
+ QCOMPARE(view.dragDropMode(), QAbstractItemView::NoDragDrop);
+ QVERIFY(!view.acceptDrops());
+ QVERIFY(!view.dragEnabled());
+
+ view.setDragDropMode(dragDropMode);
+ QCOMPARE(view.dragDropMode(), dragDropMode);
+ QCOMPARE(view.acceptDrops(), acceptDrops);
+ QCOMPARE(view.dragEnabled(), dragEnabled);
+
+ // ### Test effects of this mode
+}
+
+void tst_QTreeView::dragDropModeFromDragEnabledAndAcceptDrops_data()
+{
+ QTest::addColumn<bool>("dragEnabled");
+ QTest::addColumn<bool>("acceptDrops");
+ QTest::addColumn<QAbstractItemView::DragDropMode>("dragDropMode");
+ QTest::addColumn<QAbstractItemView::DragDropMode>("setBehavior");
+
+ QTest::newRow("NoDragDrop -1") << false << false << QAbstractItemView::NoDragDrop << QAbstractItemView::DragDropMode(-1);
+ QTest::newRow("NoDragDrop 0") << false << false << QAbstractItemView::NoDragDrop << QAbstractItemView::NoDragDrop;
+ QTest::newRow("NoDragDrop 1") << false << false << QAbstractItemView::NoDragDrop << QAbstractItemView::DragOnly;
+ QTest::newRow("NoDragDrop 2") << false << false << QAbstractItemView::NoDragDrop << QAbstractItemView::DropOnly;
+ QTest::newRow("NoDragDrop 3") << false << false << QAbstractItemView::NoDragDrop << QAbstractItemView::DragDrop;
+ QTest::newRow("NoDragDrop 4") << false << false << QAbstractItemView::NoDragDrop << QAbstractItemView::InternalMove;
+ QTest::newRow("DragOnly -1") << true << false << QAbstractItemView::DragOnly << QAbstractItemView::DragDropMode(-1);
+ QTest::newRow("DragOnly 0") << true << false << QAbstractItemView::DragOnly << QAbstractItemView::NoDragDrop;
+ QTest::newRow("DragOnly 1") << true << false << QAbstractItemView::DragOnly << QAbstractItemView::DragOnly;
+ QTest::newRow("DragOnly 2") << true << false << QAbstractItemView::DragOnly << QAbstractItemView::DropOnly;
+ QTest::newRow("DragOnly 3") << true << false << QAbstractItemView::DragOnly << QAbstractItemView::DragDrop;
+ QTest::newRow("DragOnly 4") << true << false << QAbstractItemView::DragOnly << QAbstractItemView::InternalMove;
+ QTest::newRow("DropOnly -1") << false << true << QAbstractItemView::DropOnly << QAbstractItemView::DragDropMode(-1);
+ QTest::newRow("DropOnly 0") << false << true << QAbstractItemView::DropOnly << QAbstractItemView::NoDragDrop;
+ QTest::newRow("DropOnly 1") << false << true << QAbstractItemView::DropOnly << QAbstractItemView::DragOnly;
+ QTest::newRow("DropOnly 2") << false << true << QAbstractItemView::DropOnly << QAbstractItemView::DropOnly;
+ QTest::newRow("DropOnly 3") << false << true << QAbstractItemView::DropOnly << QAbstractItemView::DragDrop;
+ QTest::newRow("DropOnly 4") << false << true << QAbstractItemView::DropOnly << QAbstractItemView::InternalMove;
+ QTest::newRow("DragDrop -1") << true << true << QAbstractItemView::DragDrop << QAbstractItemView::DragDropMode(-1);
+ QTest::newRow("DragDrop 0") << true << true << QAbstractItemView::DragDrop << QAbstractItemView::DragDropMode(-1);
+ QTest::newRow("DragDrop 1") << true << true << QAbstractItemView::DragDrop << QAbstractItemView::NoDragDrop;
+ QTest::newRow("DragDrop 2") << true << true << QAbstractItemView::DragDrop << QAbstractItemView::DragOnly;
+ QTest::newRow("DragDrop 3") << true << true << QAbstractItemView::DragDrop << QAbstractItemView::DropOnly;
+ QTest::newRow("DragDrop 4") << true << true << QAbstractItemView::DragDrop << QAbstractItemView::DragDrop;
+ QTest::newRow("DragDrop 5") << true << true << QAbstractItemView::InternalMove << QAbstractItemView::InternalMove;
+}
+
+void tst_QTreeView::dragDropModeFromDragEnabledAndAcceptDrops()
+{
+ QFETCH(bool, acceptDrops);
+ QFETCH(bool, dragEnabled);
+ QFETCH(QAbstractItemView::DragDropMode, dragDropMode);
+ QFETCH(QAbstractItemView::DragDropMode, setBehavior);
+
+ QTreeView view;
+ QCOMPARE(view.dragDropMode(), QAbstractItemView::NoDragDrop);
+
+ if (setBehavior != QAbstractItemView::DragDropMode(-1))
+ view.setDragDropMode(setBehavior);
+
+ view.setAcceptDrops(acceptDrops);
+ view.setDragEnabled(dragEnabled);
+ QCOMPARE(view.dragDropMode(), dragDropMode);
+
+ // ### Test effects of this mode
+}
+
+void tst_QTreeView::dragDropOverwriteMode()
+{
+ QTreeView view;
+ QVERIFY(!view.dragDropOverwriteMode());
+ view.setDragDropOverwriteMode(true);
+ QVERIFY(view.dragDropOverwriteMode());
+ view.setDragDropOverwriteMode(false);
+ QVERIFY(!view.dragDropOverwriteMode());
+
+ // ### This property changes the behavior of dropIndicatorPosition(),
+ // which is protected and called only from within QListWidget and
+ // QTableWidget, from their reimplementations of dropMimeData(). Hard to
+ // test.
+}
+#endif
+
+void tst_QTreeView::editTriggers_data()
+{
+ QTest::addColumn<QAbstractItemView::EditTriggers>("editTriggers");
+ QTest::addColumn<QAbstractItemView::EditTrigger>("triggeredTrigger");
+ QTest::addColumn<bool>("editorOpened");
+
+ // NoEditTriggers
+ QTest::newRow("NoEditTriggers 0") << QAbstractItemView::EditTriggers(QAbstractItemView::NoEditTriggers)
+ << QAbstractItemView::NoEditTriggers << false;
+ QTest::newRow("NoEditTriggers 1") << QAbstractItemView::EditTriggers(QAbstractItemView::NoEditTriggers)
+ << QAbstractItemView::CurrentChanged << false;
+ QTest::newRow("NoEditTriggers 2") << QAbstractItemView::EditTriggers(QAbstractItemView::NoEditTriggers)
+ << QAbstractItemView::DoubleClicked << false;
+ QTest::newRow("NoEditTriggers 3") << QAbstractItemView::EditTriggers(QAbstractItemView::NoEditTriggers)
+ << QAbstractItemView::SelectedClicked << false;
+ QTest::newRow("NoEditTriggers 4") << QAbstractItemView::EditTriggers(QAbstractItemView::NoEditTriggers)
+ << QAbstractItemView::EditKeyPressed << false;
+
+ // CurrentChanged
+ QTest::newRow("CurrentChanged 0") << QAbstractItemView::EditTriggers(QAbstractItemView::CurrentChanged)
+ << QAbstractItemView::NoEditTriggers << false;
+ QTest::newRow("CurrentChanged 1") << QAbstractItemView::EditTriggers(QAbstractItemView::CurrentChanged)
+ << QAbstractItemView::CurrentChanged << true;
+ QTest::newRow("CurrentChanged 2") << QAbstractItemView::EditTriggers(QAbstractItemView::CurrentChanged)
+ << QAbstractItemView::DoubleClicked << false;
+ QTest::newRow("CurrentChanged 3") << QAbstractItemView::EditTriggers(QAbstractItemView::CurrentChanged)
+ << QAbstractItemView::SelectedClicked << false;
+ QTest::newRow("CurrentChanged 4") << QAbstractItemView::EditTriggers(QAbstractItemView::CurrentChanged)
+ << QAbstractItemView::EditKeyPressed << false;
+
+ // DoubleClicked
+ QTest::newRow("DoubleClicked 0") << QAbstractItemView::EditTriggers(QAbstractItemView::DoubleClicked)
+ << QAbstractItemView::NoEditTriggers << false;
+ QTest::newRow("DoubleClicked 1") << QAbstractItemView::EditTriggers(QAbstractItemView::DoubleClicked)
+ << QAbstractItemView::CurrentChanged << false;
+ QTest::newRow("DoubleClicked 2") << QAbstractItemView::EditTriggers(QAbstractItemView::DoubleClicked)
+ << QAbstractItemView::DoubleClicked << true;
+ QTest::newRow("DoubleClicked 3") << QAbstractItemView::EditTriggers(QAbstractItemView::DoubleClicked)
+ << QAbstractItemView::SelectedClicked << false;
+ QTest::newRow("DoubleClicked 4") << QAbstractItemView::EditTriggers(QAbstractItemView::DoubleClicked)
+ << QAbstractItemView::EditKeyPressed << false;
+
+ // SelectedClicked
+ QTest::newRow("SelectedClicked 0") << QAbstractItemView::EditTriggers(QAbstractItemView::SelectedClicked)
+ << QAbstractItemView::NoEditTriggers << false;
+ QTest::newRow("SelectedClicked 1") << QAbstractItemView::EditTriggers(QAbstractItemView::SelectedClicked)
+ << QAbstractItemView::CurrentChanged << false;
+ QTest::newRow("SelectedClicked 2") << QAbstractItemView::EditTriggers(QAbstractItemView::SelectedClicked)
+ << QAbstractItemView::DoubleClicked << false;
+ QTest::newRow("SelectedClicked 3") << QAbstractItemView::EditTriggers(QAbstractItemView::SelectedClicked)
+ << QAbstractItemView::SelectedClicked << true;
+ QTest::newRow("SelectedClicked 4") << QAbstractItemView::EditTriggers(QAbstractItemView::SelectedClicked)
+ << QAbstractItemView::EditKeyPressed << false;
+
+ // EditKeyPressed
+ QTest::newRow("EditKeyPressed 0") << QAbstractItemView::EditTriggers(QAbstractItemView::EditKeyPressed)
+ << QAbstractItemView::NoEditTriggers << false;
+ QTest::newRow("EditKeyPressed 1") << QAbstractItemView::EditTriggers(QAbstractItemView::EditKeyPressed)
+ << QAbstractItemView::CurrentChanged << false;
+ QTest::newRow("EditKeyPressed 2") << QAbstractItemView::EditTriggers(QAbstractItemView::EditKeyPressed)
+ << QAbstractItemView::DoubleClicked << false;
+ QTest::newRow("EditKeyPressed 3") << QAbstractItemView::EditTriggers(QAbstractItemView::EditKeyPressed)
+ << QAbstractItemView::SelectedClicked << false;
+ QTest::newRow("EditKeyPressed 4") << QAbstractItemView::EditTriggers(QAbstractItemView::EditKeyPressed)
+ << QAbstractItemView::EditKeyPressed << true;
+}
+
+void tst_QTreeView::editTriggers()
+{
+ QFETCH(QAbstractItemView::EditTriggers, editTriggers);
+ QFETCH(QAbstractItemView::EditTrigger, triggeredTrigger);
+ QFETCH(bool, editorOpened);
+
+ QTreeView view;
+ QStandardItemModel treeModel;
+ initStandardTreeModel(&treeModel);
+ view.setModel(&treeModel);
+ view.show();
+
+ QCOMPARE(view.editTriggers(), QAbstractItemView::EditKeyPressed | QAbstractItemView::DoubleClicked);
+
+ // Initialize the first index
+ view.setCurrentIndex(view.model()->index(0, 0));
+
+ // Verify that we don't have any editor initially
+ QVERIFY(!qFindChild<QLineEdit *>(&view, QString()));
+
+ // Set the triggers
+ view.setEditTriggers(editTriggers);
+
+ // Interact with the view
+ switch (triggeredTrigger) {
+ case QAbstractItemView::NoEditTriggers:
+ // Do nothing, the editor shouldn't be there
+ break;
+ case QAbstractItemView::CurrentChanged:
+ // Change the index to open an editor
+ view.setCurrentIndex(view.model()->index(1, 0));
+ break;
+ case QAbstractItemView::DoubleClicked:
+ // Doubleclick the center of the current cell
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, 0,
+ view.visualRect(view.model()->index(0, 0)).center());
+ QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0,
+ view.visualRect(view.model()->index(0, 0)).center());
+ break;
+ case QAbstractItemView::SelectedClicked:
+ // Click the center of the current cell
+ view.selectionModel()->select(view.model()->index(0, 0), QItemSelectionModel::Select);
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, 0,
+ view.visualRect(view.model()->index(0, 0)).center());
+ QTest::qWait(int(QApplication::doubleClickInterval() * 1.5));
+ break;
+ case QAbstractItemView::EditKeyPressed:
+ view.setFocus();
+#ifdef Q_OS_MAC
+ // Mac OS X uses Enter for editing
+ QTest::keyPress(&view, Qt::Key_Enter);
+#else
+ // All other platforms use F2
+ QTest::keyPress(&view, Qt::Key_F2);
+#endif
+ break;
+ default:
+ break;
+ }
+
+ // Check if we got an editor
+ QTRY_COMPARE(qFindChild<QLineEdit *>(&view, QString()) != 0, editorOpened);
+}
+
+void tst_QTreeView::hasAutoScroll()
+{
+ QTreeView view;
+ QVERIFY(view.hasAutoScroll());
+ view.setAutoScroll(false);
+ QVERIFY(!view.hasAutoScroll());
+ view.setAutoScroll(true);
+ QVERIFY(view.hasAutoScroll());
+}
+
+void tst_QTreeView::horizontalScrollMode()
+{
+ QStandardItemModel model;
+ for (int i = 0; i < 100; ++i) {
+ model.appendRow(QList<QStandardItem *>()
+ << new QStandardItem("An item that has very long text and should"
+ " cause the horizontal scroll bar to appear.")
+ << new QStandardItem("An item that has very long text and should"
+ " cause the horizontal scroll bar to appear."));
+ }
+
+ QTreeView view;
+ view.setModel(&model);
+ view.setFixedSize(100, 100);
+ view.header()->resizeSection(0, 200);
+ view.show();
+
+ QCOMPARE(view.horizontalScrollMode(), QAbstractItemView::ScrollPerPixel);
+ QCOMPARE(view.horizontalScrollBar()->minimum(), 0);
+ QVERIFY(view.horizontalScrollBar()->maximum() > 2);
+
+ view.setHorizontalScrollMode(QAbstractItemView::ScrollPerItem);
+ QCOMPARE(view.horizontalScrollMode(), QAbstractItemView::ScrollPerItem);
+ QCOMPARE(view.horizontalScrollBar()->minimum(), 0);
+ QCOMPARE(view.horizontalScrollBar()->maximum(), 1);
+
+ view.setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
+ QCOMPARE(view.horizontalScrollMode(), QAbstractItemView::ScrollPerPixel);
+ QCOMPARE(view.horizontalScrollBar()->minimum(), 0);
+ QVERIFY(view.horizontalScrollBar()->maximum() > 2);
+}
+
+class RepaintTreeView : public QTreeView
+{
+public:
+ RepaintTreeView() : repainted(false) { }
+ bool repainted;
+
+protected:
+ void paintEvent(QPaintEvent *event)
+ { repainted = true; QTreeView::paintEvent(event); }
+};
+
+void tst_QTreeView::iconSize()
+{
+ RepaintTreeView view;
+ QCOMPARE(view.iconSize(), QSize());
+
+ QStandardItemModel treeModel;
+ initStandardTreeModel(&treeModel);
+ view.setModel(&treeModel);
+ QCOMPARE(view.iconSize(), QSize());
+ QVERIFY(!view.repainted);
+
+ view.show();
+ view.update();
+ QTest::qWaitForWindowShown(&view);
+ QTRY_VERIFY(view.repainted);
+ QCOMPARE(view.iconSize(), QSize());
+
+ view.repainted = false;
+ view.setIconSize(QSize());
+ QTRY_VERIFY(!view.repainted);
+ QCOMPARE(view.iconSize(), QSize());
+
+ view.setIconSize(QSize(10, 10));
+ QTRY_VERIFY(view.repainted);
+ QCOMPARE(view.iconSize(), QSize(10, 10));
+
+ view.repainted = false;
+ view.setIconSize(QSize(10000, 10000));
+ QTRY_VERIFY(view.repainted);
+ QCOMPARE(view.iconSize(), QSize(10000, 10000));
+}
+
+void tst_QTreeView::indexAt()
+{
+ QtTestModel model;
+ model.rows = model.cols = 5;
+
+ QTreeView view;
+ QCOMPARE(view.indexAt(QPoint()), QModelIndex());
+ view.setModel(&model);
+ QVERIFY(view.indexAt(QPoint()) != QModelIndex());
+
+ QSize itemSize = view.visualRect(model.index(0, 0)).size();
+ for (int i = 0; i < model.rowCount(); ++i) {
+ QPoint pos(itemSize.width() / 2, (i * itemSize.height()) + (itemSize.height() / 2));
+ QModelIndex index = view.indexAt(pos);
+ QCOMPARE(index, model.index(i, 0));
+ }
+
+ /*
+ // ### this is wrong; the widget _will_ affect the item size
+ for (int j = 0; j < model.rowCount(); ++j)
+ view.setIndexWidget(model.index(j, 0), new QPushButton);
+ */
+
+ for (int k = 0; k < model.rowCount(); ++k) {
+ QPoint pos(itemSize.width() / 2, (k * itemSize.height()) + (itemSize.height() / 2));
+ QModelIndex index = view.indexAt(pos);
+ QCOMPARE(index, model.index(k, 0));
+ }
+
+ view.show();
+ view.resize(600, 600);
+ view.header()->setStretchLastSection(false);
+ QCOMPARE(view.indexAt(QPoint(550, 20)), QModelIndex());
+}
+
+void tst_QTreeView::indexWidget()
+{
+ QTreeView view;
+ QStandardItemModel treeModel;
+ initStandardTreeModel(&treeModel);
+ view.setModel(&treeModel);
+
+ QModelIndex index = view.model()->index(0, 0);
+
+ QVERIFY(!view.indexWidget(QModelIndex()));
+ QVERIFY(!view.indexWidget(index));
+
+ QString text = QLatin1String("TestLabel");
+
+ QWidget *label = new QLabel(text);
+ view.setIndexWidget(QModelIndex(), label);
+ QVERIFY(!view.indexWidget(QModelIndex()));
+ QVERIFY(!label->parent());
+ view.setIndexWidget(index, label);
+ QCOMPARE(view.indexWidget(index), label);
+ QCOMPARE(label->parentWidget(), view.viewport());
+
+
+ QTextEdit *widget = new QTextEdit(text);
+ widget->setFixedSize(200,100);
+ view.setIndexWidget(index, widget);
+ QCOMPARE(view.indexWidget(index), static_cast<QWidget *>(widget));
+
+ QCOMPARE(widget->parentWidget(), view.viewport());
+ QCOMPARE(widget->geometry(), view.visualRect(index).intersected(widget->geometry()));
+ QCOMPARE(widget->toPlainText(), text);
+
+ //now let's try to do that later when the widget is already shown
+ view.show();
+ index = view.model()->index(1, 0);
+ QVERIFY(!view.indexWidget(index));
+
+ widget = new QTextEdit(text);
+ widget->setFixedSize(200,100);
+ view.setIndexWidget(index, widget);
+ QCOMPARE(view.indexWidget(index), static_cast<QWidget *>(widget));
+
+ QCOMPARE(widget->parentWidget(), view.viewport());
+ QCOMPARE(widget->geometry(), view.visualRect(index).intersect(widget->geometry()));
+ QCOMPARE(widget->toPlainText(), text);
+}
+
+void tst_QTreeView::itemDelegate()
+{
+ QPointer<QAbstractItemDelegate> oldDelegate;
+ QPointer<QItemDelegate> otherItemDelegate;
+
+ {
+ QTreeView view;
+ QVERIFY(qobject_cast<QStyledItemDelegate *>(view.itemDelegate()));
+ QPointer<QAbstractItemDelegate> oldDelegate = view.itemDelegate();
+
+ otherItemDelegate = new QItemDelegate;
+ view.setItemDelegate(otherItemDelegate);
+ QVERIFY(!otherItemDelegate->parent());
+ QVERIFY(oldDelegate);
+
+ QCOMPARE(view.itemDelegate(), (QAbstractItemDelegate *)otherItemDelegate);
+
+ view.setItemDelegate(0);
+ QVERIFY(!view.itemDelegate()); // <- view does its own drawing?
+ QVERIFY(otherItemDelegate);
+ }
+
+ // This is strange. Why doesn't setItemDelegate() reparent the delegate?
+ QVERIFY(!oldDelegate);
+ QVERIFY(otherItemDelegate);
+
+ delete otherItemDelegate;
+}
+
+void tst_QTreeView::itemDelegateForColumnOrRow()
+{
+ QTreeView view;
+ QAbstractItemDelegate *defaultDelegate = view.itemDelegate();
+ QVERIFY(defaultDelegate);
+
+ QVERIFY(!view.itemDelegateForRow(0));
+ QVERIFY(!view.itemDelegateForColumn(0));
+ QCOMPARE(view.itemDelegate(QModelIndex()), defaultDelegate);
+
+ QStandardItemModel model;
+ for (int i = 0; i < 100; ++i) {
+ model.appendRow(QList<QStandardItem *>()
+ << new QStandardItem("An item that has very long text and should"
+ " cause the horizontal scroll bar to appear.")
+ << new QStandardItem("An item that has very long text and should"
+ " cause the horizontal scroll bar to appear.")
+ << new QStandardItem("An item that has very long text and should"
+ " cause the horizontal scroll bar to appear."));
+ }
+ view.setModel(&model);
+
+ QVERIFY(!view.itemDelegateForRow(0));
+ QVERIFY(!view.itemDelegateForColumn(0));
+ QCOMPARE(view.itemDelegate(QModelIndex()), defaultDelegate);
+ QCOMPARE(view.itemDelegate(view.model()->index(0, 0)), defaultDelegate);
+
+ QPointer<QAbstractItemDelegate> rowDelegate = new QItemDelegate;
+ view.setItemDelegateForRow(0, rowDelegate);
+ QVERIFY(!rowDelegate->parent());
+ QCOMPARE(view.itemDelegateForRow(0), (QAbstractItemDelegate *)rowDelegate);
+ QCOMPARE(view.itemDelegate(view.model()->index(0, 0)), (QAbstractItemDelegate *)rowDelegate);
+ QCOMPARE(view.itemDelegate(view.model()->index(0, 1)), (QAbstractItemDelegate *)rowDelegate);
+ QCOMPARE(view.itemDelegate(view.model()->index(1, 0)), defaultDelegate);
+ QCOMPARE(view.itemDelegate(view.model()->index(1, 1)), defaultDelegate);
+
+ QPointer<QAbstractItemDelegate> columnDelegate = new QItemDelegate;
+ view.setItemDelegateForColumn(1, columnDelegate);
+ QVERIFY(!columnDelegate->parent());
+ QCOMPARE(view.itemDelegateForColumn(1), (QAbstractItemDelegate *)columnDelegate);
+ QCOMPARE(view.itemDelegate(view.model()->index(0, 0)), (QAbstractItemDelegate *)rowDelegate);
+ QCOMPARE(view.itemDelegate(view.model()->index(0, 1)), (QAbstractItemDelegate *)rowDelegate); // row wins
+ QCOMPARE(view.itemDelegate(view.model()->index(1, 0)), defaultDelegate);
+ QCOMPARE(view.itemDelegate(view.model()->index(1, 1)), (QAbstractItemDelegate *)columnDelegate);
+
+ view.setItemDelegateForRow(0, 0);
+ QVERIFY(!view.itemDelegateForRow(0));
+ QVERIFY(rowDelegate); // <- wasn't deleted
+
+ view.setItemDelegateForColumn(1, 0);
+ QVERIFY(!view.itemDelegateForColumn(1));
+ QVERIFY(columnDelegate); // <- wasn't deleted
+
+ delete rowDelegate;
+ delete columnDelegate;
+}
+
+void tst_QTreeView::keyboardSearch()
+{
+ QTreeView view;
+ QStandardItemModel model;
+ model.appendRow(new QStandardItem("Andreas"));
+ model.appendRow(new QStandardItem("Baldrian"));
+ model.appendRow(new QStandardItem("Cecilie"));
+ view.setModel(&model);
+ view.show();
+
+ // Nothing is selected
+ QVERIFY(!view.selectionModel()->hasSelection());
+ QVERIFY(!view.selectionModel()->isSelected(model.index(0, 0)));
+
+ // First item is selected
+ view.keyboardSearch(QLatin1String("A"));
+ QTRY_VERIFY(view.selectionModel()->isSelected(model.index(0, 0)));
+
+ // First item is still selected
+ view.keyboardSearch(QLatin1String("n"));
+ QVERIFY(view.selectionModel()->isSelected(model.index(0, 0)));
+
+ // No "AnB" item - keep the same selection.
+ view.keyboardSearch(QLatin1String("B"));
+ QVERIFY(view.selectionModel()->isSelected(model.index(0, 0)));
+
+ // Wait a bit.
+ QTest::qWait(QApplication::keyboardInputInterval() * 2);
+
+ // The item that starts with B is selected.
+ view.keyboardSearch(QLatin1String("B"));
+ QVERIFY(view.selectionModel()->isSelected(model.index(1, 0)));
+}
+
+void tst_QTreeView::setModel()
+{
+ QTreeView view;
+ view.show();
+ QCOMPARE(view.model(), (QAbstractItemModel*)0);
+ QCOMPARE(view.selectionModel(), (QItemSelectionModel*)0);
+ QCOMPARE(view.header()->model(), (QAbstractItemModel*)0);
+ QCOMPARE(view.header()->selectionModel(), (QItemSelectionModel*)0);
+
+ for (int x = 0; x < 2; ++x) {
+ QtTestModel *model = new QtTestModel(10, 8);
+ QAbstractItemModel *oldModel = view.model();
+ QSignalSpy modelDestroyedSpy(oldModel ? oldModel : model, SIGNAL(destroyed()));
+ // set the same model twice
+ for (int i = 0; i < 2; ++i) {
+ QItemSelectionModel *oldSelectionModel = view.selectionModel();
+ QItemSelectionModel *dummy = new QItemSelectionModel(model);
+ QSignalSpy selectionModelDestroyedSpy(
+ oldSelectionModel ? oldSelectionModel : dummy, SIGNAL(destroyed()));
+ view.setModel(model);
+// QCOMPARE(selectionModelDestroyedSpy.count(), (x == 0 || i == 1) ? 0 : 1);
+ QCOMPARE(view.model(), (QAbstractItemModel *)model);
+ QCOMPARE(view.header()->model(), (QAbstractItemModel *)model);
+ QCOMPARE(view.selectionModel() != oldSelectionModel, (i == 0));
+ }
+ QTRY_COMPARE(modelDestroyedSpy.count(), 0);
+
+ view.setModel(0);
+ QCOMPARE(view.model(), (QAbstractItemModel*)0);
+ // ### shouldn't selectionModel also be 0 now?
+// QCOMPARE(view.selectionModel(), (QItemSelectionModel*)0);
+ delete model;
+ }
+}
+
+void tst_QTreeView::openPersistentEditor()
+{
+ QTreeView view;
+ QStandardItemModel treeModel;
+ initStandardTreeModel(&treeModel);
+ view.setModel(&treeModel);
+ view.show();
+
+ QVERIFY(!qFindChild<QLineEdit *>(view.viewport()));
+ view.openPersistentEditor(view.model()->index(0, 0));
+ QVERIFY(qFindChild<QLineEdit *>(view.viewport()));
+
+ view.closePersistentEditor(view.model()->index(0, 0));
+ QVERIFY(!qFindChild<QLineEdit *>(view.viewport())->isVisible());
+
+ qApp->sendPostedEvents(0, QEvent::DeferredDelete);
+ QVERIFY(!qFindChild<QLineEdit *>(view.viewport()));
+}
+
+void tst_QTreeView::rootIndex()
+{
+ QTreeView view;
+ QCOMPARE(view.rootIndex(), QModelIndex());
+ QStandardItemModel treeModel;
+ initStandardTreeModel(&treeModel);
+ view.setModel(&treeModel);
+ QCOMPARE(view.rootIndex(), QModelIndex());
+
+ view.setRootIndex(view.model()->index(1, 0));
+
+ QCOMPARE(view.model()->data(view.model()->index(0, view.header()->visualIndex(0), view.rootIndex()), Qt::DisplayRole)
+ .toString(), QString("Row 2 Child Item"));
+}
+
+void tst_QTreeView::setHeader()
+{
+ QTreeView view;
+ QVERIFY(view.header() != 0);
+ QCOMPARE(view.header()->orientation(), Qt::Horizontal);
+ QCOMPARE(view.header()->parent(), (QObject *)&view);
+ for (int x = 0; x < 2; ++x) {
+ QSignalSpy destroyedSpy(view.header(), SIGNAL(destroyed()));
+ Qt::Orientation orient = x ? Qt::Vertical : Qt::Horizontal;
+ QHeaderView *head = new QHeaderView(orient);
+ view.setHeader(head);
+ QCOMPARE(destroyedSpy.count(), 1);
+ QCOMPARE(head->parent(), (QObject *)&view);
+ QCOMPARE(view.header(), head);
+ view.setHeader(head);
+ QCOMPARE(view.header(), head);
+ // Itemviews in Qt < 4.2 have asserts for this. Qt >= 4.2 should handle this gracefully
+ view.setHeader((QHeaderView *)0);
+ QCOMPARE(view.header(), head);
+ }
+}
+
+void tst_QTreeView::columnHidden()
+{
+ QTreeView view;
+ QtTestModel model(10, 8);
+ view.setModel(&model);
+ view.show();
+ for (int c = 0; c < model.columnCount(); ++c)
+ QCOMPARE(view.isColumnHidden(c), false);
+ // hide even columns
+ for (int c = 0; c < model.columnCount(); c+=2)
+ view.setColumnHidden(c, true);
+ for (int c = 0; c < model.columnCount(); ++c)
+ QCOMPARE(view.isColumnHidden(c), (c & 1) == 0);
+ view.update();
+ // hide odd columns too
+ for (int c = 1; c < model.columnCount(); c+=2)
+ view.setColumnHidden(c, true);
+ for (int c = 0; c < model.columnCount(); ++c)
+ QCOMPARE(view.isColumnHidden(c), true);
+ view.update();
+}
+
+void tst_QTreeView::rowHidden()
+{
+ QtTestModel model(4, 6);
+ model.levels = 3;
+ QTreeView view;
+ view.resize(500,500);
+ view.setModel(&model);
+ view.show();
+
+ QCOMPARE(view.isRowHidden(-1, QModelIndex()), false);
+ QCOMPARE(view.isRowHidden(999999, QModelIndex()), false);
+ view.setRowHidden(-1, QModelIndex(), true);
+ view.setRowHidden(999999, QModelIndex(), true);
+ QCOMPARE(view.isRowHidden(-1, QModelIndex()), false);
+ QCOMPARE(view.isRowHidden(999999, QModelIndex()), false);
+
+ view.setRowHidden(0, QModelIndex(), true);
+ QCOMPARE(view.isRowHidden(0, QModelIndex()), true);
+ view.setRowHidden(0, QModelIndex(), false);
+ QCOMPARE(view.isRowHidden(0, QModelIndex()), false);
+
+ QStack<QModelIndex> parents;
+ parents.push(QModelIndex());
+ while (!parents.isEmpty()) {
+ QModelIndex p = parents.pop();
+ if (model.canFetchMore(p))
+ model.fetchMore(p);
+ int rows = model.rowCount(p);
+ // hide all
+ for (int r = 0; r < rows; ++r) {
+ view.setRowHidden(r, p, true);
+ QCOMPARE(view.isRowHidden(r, p), true);
+ }
+ // hide none
+ for (int r = 0; r < rows; ++r) {
+ view.setRowHidden(r, p, false);
+ QCOMPARE(view.isRowHidden(r, p), false);
+ }
+ // hide only even rows
+ for (int r = 0; r < rows; ++r) {
+ bool hide = (r & 1) == 0;
+ view.setRowHidden(r, p, hide);
+ QCOMPARE(view.isRowHidden(r, p), hide);
+ }
+ for (int r = 0; r < rows; ++r)
+ parents.push(model.index(r, 0, p));
+ }
+
+ parents.push(QModelIndex());
+ while (!parents.isEmpty()) {
+ QModelIndex p = parents.pop();
+ // all even rows should still be hidden
+ for (int r = 0; r < model.rowCount(p); ++r)
+ QCOMPARE(view.isRowHidden(r, p), (r & 1) == 0);
+ if (model.rowCount(p) > 0) {
+ for (int r = 0; r < model.rowCount(p); ++r)
+ parents.push(model.index(r, 0, p));
+ }
+ }
+}
+
+void tst_QTreeView::noDelegate()
+{
+ QtTestModel model(10, 7);
+ QTreeView view;
+ view.setModel(&model);
+ view.setItemDelegate(0);
+ QCOMPARE(view.itemDelegate(), (QAbstractItemDelegate *)0);
+}
+
+void tst_QTreeView::noModel()
+{
+ QTreeView view;
+ view.show();
+ view.setRowHidden(0, QModelIndex(), true);
+}
+
+void tst_QTreeView::emptyModel()
+{
+ QtTestModel model;
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+ QVERIFY(!model.wrongIndex);
+}
+
+void tst_QTreeView::removeRows()
+{
+ QtTestModel model(7, 10);
+
+ QTreeView view;
+
+ view.setModel(&model);
+ view.show();
+
+ model.removeLastRow();
+ QVERIFY(!model.wrongIndex);
+
+ model.removeAllRows();
+ QVERIFY(!model.wrongIndex);
+}
+
+void tst_QTreeView::removeCols()
+{
+ QtTestModel model(5, 8);
+
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+ model.fetched = true;
+ model.removeLastColumn();
+ QVERIFY(!model.wrongIndex);
+ QCOMPARE(view.header()->count(), model.cols);
+
+ model.removeAllColumns();
+ QVERIFY(!model.wrongIndex);
+ QCOMPARE(view.header()->count(), model.cols);
+}
+
+void tst_QTreeView::expandAndCollapse_data()
+{
+ QTest::addColumn<bool>("animationEnabled");
+ QTest::newRow("normal") << false;
+ QTest::newRow("animated") << true;
+
+}
+
+void tst_QTreeView::expandAndCollapse()
+{
+ QFETCH(bool, animationEnabled);
+
+ QtTestModel model(10, 9);
+
+ QTreeView view;
+ view.setUniformRowHeights(true);
+ view.setModel(&model);
+ view.setAnimated(animationEnabled);
+ view.show();
+
+ QModelIndex a = model.index(0, 0, QModelIndex());
+ QModelIndex b = model.index(0, 0, a);
+
+ QSignalSpy expandedSpy(&view, SIGNAL(expanded(const QModelIndex&)));
+ QSignalSpy collapsedSpy(&view, SIGNAL(collapsed(const QModelIndex&)));
+ QVariantList args;
+
+ for (int y = 0; y < 2; ++y) {
+ view.setVisible(y == 0);
+ for (int x = 0; x < 2; ++x) {
+ view.setItemsExpandable(x == 0);
+
+ // Test bad args
+ view.expand(QModelIndex());
+ QCOMPARE(view.isExpanded(QModelIndex()), false);
+ view.collapse(QModelIndex());
+ QCOMPARE(expandedSpy.count(), 0);
+ QCOMPARE(collapsedSpy.count(), 0);
+
+ // expand a first level item
+ QVERIFY(!view.isExpanded(a));
+ view.expand(a);
+ QVERIFY(view.isExpanded(a));
+ QCOMPARE(expandedSpy.count(), 1);
+ QCOMPARE(collapsedSpy.count(), 0);
+ args = expandedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a);
+
+ view.expand(a);
+ QVERIFY(view.isExpanded(a));
+ QCOMPARE(expandedSpy.count(), 0);
+ QCOMPARE(collapsedSpy.count(), 0);
+
+ // expand a second level item
+ QVERIFY(!view.isExpanded(b));
+ view.expand(b);
+ QVERIFY(view.isExpanded(a));
+ QVERIFY(view.isExpanded(b));
+ QCOMPARE(expandedSpy.count(), 1);
+ QCOMPARE(collapsedSpy.count(), 0);
+ args = expandedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), b);
+
+ view.expand(b);
+ QVERIFY(view.isExpanded(b));
+ QCOMPARE(expandedSpy.count(), 0);
+ QCOMPARE(collapsedSpy.count(), 0);
+
+ // collapse the first level item
+ view.collapse(a);
+ QVERIFY(!view.isExpanded(a));
+ QVERIFY(view.isExpanded(b));
+ QCOMPARE(expandedSpy.count(), 0);
+ QCOMPARE(collapsedSpy.count(), 1);
+ args = collapsedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a);
+
+ view.collapse(a);
+ QVERIFY(!view.isExpanded(a));
+ QCOMPARE(expandedSpy.count(), 0);
+ QCOMPARE(collapsedSpy.count(), 0);
+
+ // expand the first level item again
+ view.expand(a);
+ QVERIFY(view.isExpanded(a));
+ QVERIFY(view.isExpanded(b));
+ QCOMPARE(expandedSpy.count(), 1);
+ QCOMPARE(collapsedSpy.count(), 0);
+ args = expandedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a);
+
+ // collapse the second level item
+ view.collapse(b);
+ QVERIFY(view.isExpanded(a));
+ QVERIFY(!view.isExpanded(b));
+ QCOMPARE(expandedSpy.count(), 0);
+ QCOMPARE(collapsedSpy.count(), 1);
+ args = collapsedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), b);
+
+ // collapse the first level item
+ view.collapse(a);
+ QVERIFY(!view.isExpanded(a));
+ QVERIFY(!view.isExpanded(b));
+ QCOMPARE(expandedSpy.count(), 0);
+ QCOMPARE(collapsedSpy.count(), 1);
+ args = collapsedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a);
+
+ // expand and remove row
+ QPersistentModelIndex c = model.index(9, 0, b);
+ view.expand(a);
+ view.expand(b);
+ model.removeLastRow(); // remove c
+ QVERIFY(view.isExpanded(a));
+ QVERIFY(view.isExpanded(b));
+ QVERIFY(!view.isExpanded(c));
+ QCOMPARE(expandedSpy.count(), 2);
+ QCOMPARE(collapsedSpy.count(), 0);
+ args = expandedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a);
+ args = expandedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), b);
+
+ view.collapse(a);
+ view.collapse(b);
+ QVERIFY(!view.isExpanded(a));
+ QVERIFY(!view.isExpanded(b));
+ QVERIFY(!view.isExpanded(c));
+ QCOMPARE(expandedSpy.count(), 0);
+ QCOMPARE(collapsedSpy.count(), 2);
+ args = collapsedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a);
+ args = collapsedSpy.takeFirst();
+ QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), b);
+ }
+ }
+}
+
+void tst_QTreeView::expandAndCollapseAll()
+{
+ QtTestModel model(3, 2);
+ model.levels = 2;
+ QTreeView view;
+ view.setUniformRowHeights(true);
+ view.setModel(&model);
+
+ QSignalSpy expandedSpy(&view, SIGNAL(expanded(const QModelIndex&)));
+ QSignalSpy collapsedSpy(&view, SIGNAL(collapsed(const QModelIndex&)));
+
+ view.expandAll();
+ view.show();
+
+ QCOMPARE(collapsedSpy.count(), 0);
+
+ QStack<QModelIndex> parents;
+ parents.push(QModelIndex());
+ int count = 0;
+ while (!parents.isEmpty()) {
+ QModelIndex p = parents.pop();
+ int rows = model.rowCount(p);
+ for (int r = 0; r < rows; ++r)
+ QVERIFY(view.isExpanded(model.index(r, 0, p)));
+ count += rows;
+ for (int r = 0; r < rows; ++r)
+ parents.push(model.index(r, 0, p));
+ }
+// ### why is expanded() signal not emitted?
+// QCOMPARE(expandedSpy.count(), count);
+
+ view.collapseAll();
+
+ QCOMPARE(expandedSpy.count(), 0);
+
+ parents.push(QModelIndex());
+ count = 0;
+ while (!parents.isEmpty()) {
+ QModelIndex p = parents.pop();
+ int rows = model.rowCount(p);
+ for (int r = 0; r < rows; ++r)
+ QVERIFY(!view.isExpanded(model.index(r, 0, p)));
+ count += rows;
+ for (int r = 0; r < rows; ++r)
+ parents.push(model.index(r, 0, p));
+ }
+// ### why is collapsed() signal not emitted?
+// QCOMPARE(collapsedSpy.count(), count);
+}
+
+void tst_QTreeView::expandWithNoChildren()
+{
+ QTreeView tree;
+ QStandardItemModel model(1,1);
+ tree.setModel(&model);
+ tree.setAnimated(true);
+ tree.doItemsLayout();
+ //this test should not output warnings
+ tree.expand(model.index(0,0));
+}
+
+
+
+void tst_QTreeView::keyboardNavigation()
+{
+ const int rows = 10;
+ const int columns = 7;
+
+ QtTestModel model(rows, columns);
+
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+
+ QVector<Qt::Key> keymoves;
+ keymoves << Qt::Key_Down << Qt::Key_Right << Qt::Key_Right << Qt::Key_Right
+ << Qt::Key_Down << Qt::Key_Down << Qt::Key_Down << Qt::Key_Down
+ << Qt::Key_Right << Qt::Key_Right << Qt::Key_Right
+ << Qt::Key_Left << Qt::Key_Up << Qt::Key_Left << Qt::Key_Left
+ << Qt::Key_Up << Qt::Key_Down << Qt::Key_Up << Qt::Key_Up
+ << Qt::Key_Up << Qt::Key_Up << Qt::Key_Up << Qt::Key_Up
+ << Qt::Key_Left << Qt::Key_Left << Qt::Key_Up << Qt::Key_Down;
+
+ int row = 0;
+ int column = 0;
+ QModelIndex index = model.index(row, column, QModelIndex());
+ view.setCurrentIndex(index);
+ QCOMPARE(view.currentIndex(), index);
+
+ for (int i = 0; i < keymoves.size(); ++i) {
+ Qt::Key key = keymoves.at(i);
+ QTest::keyClick(&view, key);
+
+ switch (key) {
+ case Qt::Key_Up:
+ if (row > 0) {
+ index = index.sibling(row - 1, column);
+ row -= 1;
+ } else if (index.parent() != QModelIndex()) {
+ index = index.parent();
+ row = index.row();
+ }
+ break;
+ case Qt::Key_Down:
+ if (view.isExpanded(index)) {
+ row = 0;
+ index = index.child(row, column);
+ } else {
+ row = qMin(rows - 1, row + 1);
+ index = index.sibling(row, column);
+ }
+ break;
+ case Qt::Key_Left: {
+ QScrollBar *b = view.horizontalScrollBar();
+ if (b->value() == b->minimum())
+ QVERIFY(!view.isExpanded(index));
+ // windows style right will walk to the parent
+ if (view.currentIndex() != index) {
+ QCOMPARE(view.currentIndex(), index.parent());
+ index = view.currentIndex();
+ row = index.row();
+ column = index.column();
+ }
+ break;
+ }
+ case Qt::Key_Right:
+ QVERIFY(view.isExpanded(index));
+ // windows style right will walk to the first child
+ if (view.currentIndex() != index) {
+ QCOMPARE(view.currentIndex().parent(), index);
+ row = view.currentIndex().row();
+ column = view.currentIndex().column();
+ index = view.currentIndex();
+ }
+ break;
+ default:
+ QVERIFY(false);
+ }
+
+ QCOMPARE(view.currentIndex().row(), row);
+ QCOMPARE(view.currentIndex().column(), column);
+ QCOMPARE(view.currentIndex(), index);
+ }
+}
+
+class Dmodel : public QtTestModel
+{
+public:
+ Dmodel() : QtTestModel(10, 10){}
+
+ int columnCount(const QModelIndex &parent) const
+ {
+ if (parent.row() == 5)
+ return 1;
+ return QtTestModel::columnCount(parent);
+ }
+};
+
+void tst_QTreeView::headerSections()
+{
+ Dmodel model;
+
+ QTreeView view;
+ QHeaderView *header = view.header();
+
+ view.setModel(&model);
+ QModelIndex index = model.index(5, 0);
+ view.setRootIndex(index);
+ QCOMPARE(model.columnCount(QModelIndex()), 10);
+ QCOMPARE(model.columnCount(index), 1);
+ QCOMPARE(header->count(), model.columnCount(index));
+}
+
+void tst_QTreeView::moveCursor_data()
+{
+ QTest::addColumn<bool>("uniformRowHeights");
+ QTest::addColumn<bool>("scrollPerPixel");
+ QTest::newRow("uniformRowHeights = false, scrollPerPixel = false")
+ << false << false;
+ QTest::newRow("uniformRowHeights = true, scrollPerPixel = false")
+ << true << false;
+ QTest::newRow("uniformRowHeights = false, scrollPerPixel = true")
+ << false << true;
+ QTest::newRow("uniformRowHeights = true, scrollPerPixel = true")
+ << true << true;
+}
+
+void tst_QTreeView::moveCursor()
+{
+ QFETCH(bool, uniformRowHeights);
+ QFETCH(bool, scrollPerPixel);
+ QtTestModel model(8, 6);
+
+ PublicView view;
+ view.setUniformRowHeights(uniformRowHeights);
+ view.setModel(&model);
+ view.setRowHidden(0, QModelIndex(), true);
+ view.setVerticalScrollMode(scrollPerPixel ?
+ QAbstractItemView::ScrollPerPixel :
+ QAbstractItemView::ScrollPerItem);
+ QVERIFY(view.isRowHidden(0, QModelIndex()));
+ view.setColumnHidden(0, true);
+ QVERIFY(view.isColumnHidden(0));
+ view.show();
+ qApp->setActiveWindow(&view);
+
+ //here the first visible index should be selected
+ //because the view got the focus
+ QModelIndex expected = model.index(1, 1, QModelIndex());
+ QCOMPARE(view.currentIndex(), expected);
+
+ //then pressing down should go to the next line
+ QModelIndex actual = view.moveCursor(PublicView::MoveDown, Qt::NoModifier);
+ expected = model.index(2, 1, QModelIndex());
+ QCOMPARE(actual, expected);
+
+ view.setRowHidden(0, QModelIndex(), false);
+ view.setColumnHidden(0, false);
+
+ // PageUp was broken with uniform row heights turned on
+ view.setCurrentIndex(model.index(1, 0));
+ actual = view.moveCursor(PublicView::MovePageUp, Qt::NoModifier);
+ expected = model.index(0, 0, QModelIndex());
+ QCOMPARE(actual, expected);
+
+ //let's try another column
+ view.setCurrentIndex(model.index(1, 1));
+ view.setSelectionBehavior(QAbstractItemView::SelectItems);
+ QTest::keyClick(view.viewport(), Qt::Key_Up);
+ expected = model.index(0, 1, QModelIndex());
+ QCOMPARE(view.currentIndex(), expected);
+ QTest::keyClick(view.viewport(), Qt::Key_Down);
+ expected = model.index(1, 1, QModelIndex());
+ QCOMPARE(view.currentIndex(), expected);
+ QTest::keyClick(view.viewport(), Qt::Key_Up);
+ expected = model.index(0, 1, QModelIndex());
+ QCOMPARE(view.currentIndex(), expected);
+ view.setColumnHidden(0, true);
+ QTest::keyClick(view.viewport(), Qt::Key_Left);
+ expected = model.index(0, 1, QModelIndex()); //it shouldn't have changed
+ QCOMPARE(view.currentIndex(), expected);
+ view.setColumnHidden(0, false);
+ QTest::keyClick(view.viewport(), Qt::Key_Left);
+ expected = model.index(0, 0, QModelIndex());
+ QCOMPARE(view.currentIndex(), expected);
+}
+
+class TestDelegate : public QItemDelegate
+{
+public:
+ TestDelegate(QObject *parent) : QItemDelegate(parent) {}
+ QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const { return QSize(200, 50); }
+};
+
+typedef QList<QPoint> PointList;
+Q_DECLARE_METATYPE(PointList)
+
+void tst_QTreeView::setSelection_data()
+{
+ QTest::addColumn<QRect>("selectionRect");
+ QTest::addColumn<int>("selectionMode");
+ QTest::addColumn<int>("selectionCommand");
+ QTest::addColumn<PointList>("expectedItems");
+ QTest::addColumn<int>("verticalOffset");
+
+ QTest::newRow("(0,0,50,20),rows") << QRect(0,0,50,20)
+ << int(QAbstractItemView::SingleSelection)
+ << int(QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows)
+ << (PointList()
+ << QPoint(0,0) << QPoint(1,0) << QPoint(2,0) << QPoint(3,0) << QPoint(4,0)
+ )
+ << 0;
+
+ QTest::newRow("(0,0,50,90),rows") << QRect(0,0,50,90)
+ << int(QAbstractItemView::ExtendedSelection)
+ << int(QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows)
+ << (PointList()
+ << QPoint(0,0) << QPoint(1,0) << QPoint(2,0) << QPoint(3,0) << QPoint(4,0)
+ << QPoint(0,1) << QPoint(1,1) << QPoint(2,1) << QPoint(3,1) << QPoint(4,1)
+ )
+ << 0;
+
+ QTest::newRow("(50,0,0,90),rows,invalid rect") << QRect(QPoint(50, 0), QPoint(0, 90))
+ << int(QAbstractItemView::ExtendedSelection)
+ << int(QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows)
+ << (PointList()
+ << QPoint(0,0) << QPoint(1,0) << QPoint(2,0) << QPoint(3,0) << QPoint(4,0)
+ << QPoint(0,1) << QPoint(1,1) << QPoint(2,1) << QPoint(3,1) << QPoint(4,1)
+ )
+ << 0;
+
+ QTest::newRow("(0,-20,20,50),rows") << QRect(0,-20,20,50)
+ << int(QAbstractItemView::ExtendedSelection)
+ << int(QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows)
+ << (PointList()
+ << QPoint(0,0) << QPoint(1,0) << QPoint(2,0) << QPoint(3,0) << QPoint(4,0)
+ << QPoint(0,1) << QPoint(1,1) << QPoint(2,1) << QPoint(3,1) << QPoint(4,1)
+ )
+ << 1;
+ QTest::newRow("(0,-50,20,90),rows") << QRect(0,-50,20,90)
+ << int(QAbstractItemView::ExtendedSelection)
+ << int(QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows)
+ << (PointList()
+ << QPoint(0,0) << QPoint(1,0) << QPoint(2,0) << QPoint(3,0) << QPoint(4,0)
+ << QPoint(0,1) << QPoint(1,1) << QPoint(2,1) << QPoint(3,1) << QPoint(4,1)
+ )
+ << 1;
+
+}
+
+void tst_QTreeView::setSelection()
+{
+ QFETCH(QRect, selectionRect);
+ QFETCH(int, selectionMode);
+ QFETCH(int, selectionCommand);
+ QFETCH(PointList, expectedItems);
+ QFETCH(int, verticalOffset);
+
+ QtTestModel model(10, 5);
+ model.levels = 1;
+ model.setDecorationsEnabled(true);
+ PublicView view;
+ view.resize(400, 300);
+ view.show();
+ view.setRootIsDecorated(false);
+ view.setItemDelegate(new TestDelegate(&view));
+ view.setSelectionMode(QAbstractItemView::SelectionMode(selectionMode));
+ view.setModel(&model);
+ view.setUniformRowHeights(true);
+ view.setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
+ view.scrollTo(model.index(verticalOffset, 0), QAbstractItemView::PositionAtTop);
+ view.setSelection(selectionRect, QItemSelectionModel::SelectionFlags(selectionCommand));
+ QItemSelectionModel *selectionModel = view.selectionModel();
+ QVERIFY(selectionModel);
+
+ QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
+ QCOMPARE(selectedIndexes.count(), expectedItems.count());
+ for (int i = 0; i < selectedIndexes.count(); ++i) {
+ QModelIndex idx = selectedIndexes.at(i);
+ QVERIFY(expectedItems.contains(QPoint(idx.column(), idx.row())));
+ }
+}
+
+void tst_QTreeView::indexAbove()
+{
+ QtTestModel model(6, 7);
+ model.levels = 2;
+ QTreeView view;
+
+ QCOMPARE(view.indexAbove(QModelIndex()), QModelIndex());
+ view.setModel(&model);
+ QCOMPARE(view.indexAbove(QModelIndex()), QModelIndex());
+
+ QStack<QModelIndex> parents;
+ parents.push(QModelIndex());
+ while (!parents.isEmpty()) {
+ QModelIndex p = parents.pop();
+ if (model.canFetchMore(p))
+ model.fetchMore(p);
+ int rows = model.rowCount(p);
+ for (int r = rows - 1; r > 0; --r) {
+ QModelIndex idx = model.index(r, 0, p);
+ QModelIndex expected = model.index(r - 1, 0, p);
+ QCOMPARE(view.indexAbove(idx), expected);
+ }
+ // hide even rows
+ for (int r = 0; r < rows; r+=2)
+ view.setRowHidden(r, p, true);
+ for (int r = rows - 1; r > 0; r-=2) {
+ QModelIndex idx = model.index(r, 0, p);
+ QModelIndex expected = model.index(r - 2, 0, p);
+ QCOMPARE(view.indexAbove(idx), expected);
+ }
+// for (int r = 0; r < rows; ++r)
+// parents.push(model.index(r, 0, p));
+ }
+}
+
+void tst_QTreeView::indexBelow()
+{
+ QtTestModel model(2, 1);
+
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+
+ QModelIndex i = model.index(0, 0, view.rootIndex());
+ QVERIFY(i.isValid());
+ QCOMPARE(i.row(), 0);
+
+ i = view.indexBelow(i);
+ QVERIFY(i.isValid());
+ QCOMPARE(i.row(), 1);
+ i = view.indexBelow(i);
+ QVERIFY(!i.isValid());
+}
+
+void tst_QTreeView::clicked()
+{
+ QtTestModel model(10, 2);
+
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+
+ QModelIndex firstIndex = model.index(0, 0, QModelIndex());
+ QVERIFY(firstIndex.isValid());
+ int itemHeight = view.visualRect(firstIndex).height();
+ int itemOffset = view.visualRect(firstIndex).width() / 2;
+ view.resize(200, itemHeight * (model.rows + 2));
+
+ for (int i = 0; i < model.rowCount(); ++i) {
+ QPoint p(itemOffset, 1 + itemHeight * i);
+ QModelIndex index = view.indexAt(p);
+ if (!index.isValid())
+ continue;
+ QSignalSpy spy(&view, SIGNAL(clicked(const QModelIndex&)));
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
+ QTRY_COMPARE(spy.count(), 1);
+ }
+}
+
+void tst_QTreeView::mouseDoubleClick()
+{
+ // Test double clicks outside the viewport.
+ // (Should be a no-op and should not expand any item.)
+
+ QStandardItemModel model(20, 2);
+ for (int i = 0; i < model.rowCount(); i++) {
+ QModelIndex index = model.index(i, 0, QModelIndex());
+ model.insertRows(0, 20, index);
+ model.insertColumns(0,2,index);
+ for (int i1 = 0; i1 < model.rowCount(index); i1++) {
+ QModelIndex index2 = model.index(i1, 0, index);
+ }
+ }
+
+ QTreeView view;
+ view.setModel(&model);
+
+ // make sure the viewport height is smaller than the contents height.
+ view.resize(200,200);
+ view.move(0,0);
+ view.show();
+ QModelIndex index = model.index(0, 0, QModelIndex());
+ view.setCurrentIndex(index);
+
+ view.setExpanded(model.index(0,0, QModelIndex()), true);
+ view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+
+ // Make sure all items are collapsed
+ for (int i = 0; i < model.rowCount(QModelIndex()); i++) {
+ view.setExpanded(model.index(i,0, QModelIndex()), false);
+ }
+
+ int maximum = view.verticalScrollBar()->maximum();
+
+ // Doubleclick in the bottom right corner, in the unused area between the vertical and horizontal scrollbar.
+ int vsw = view.verticalScrollBar()->width();
+ int hsh = view.horizontalScrollBar()->height();
+ QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, QPoint(view.width() - vsw + 1, view.height() - hsh + 1));
+ // Should not have expanded, thus maximum() should have the same value.
+ QCOMPARE(maximum, view.verticalScrollBar()->maximum());
+
+ view.setExpandsOnDoubleClick(false);
+ QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, view.visualRect(index).center());
+ QVERIFY(!view.isExpanded(index));
+}
+
+void tst_QTreeView::rowsAboutToBeRemoved()
+{
+ QStandardItemModel model(3, 1);
+ for (int i = 0; i < model.rowCount(); i++) {
+ QModelIndex index = model.index(i, 0, QModelIndex());
+ model.setData(index, QString("%1").arg(i));
+ model.insertRows(0, 4, index);
+ model.insertColumns(0,1,index);
+ for (int i1 = 0; i1 < model.rowCount(index); i1++) {
+ QModelIndex index2 = model.index(i1, 0, index);
+ model.setData(index2, QString("%1%2").arg(i).arg(i1));
+ }
+ }
+
+ PublicView view;
+ view.setModel(&model);
+ view.show();
+ QModelIndex index = model.index(0,0, QModelIndex());
+ view.setCurrentIndex(index);
+ view.setExpanded(model.index(0,0, QModelIndex()), true);
+
+ for (int i = 0; i < model.rowCount(QModelIndex()); i++) {
+ view.setExpanded(model.index(i,0, QModelIndex()), true);
+ }
+
+ QSignalSpy spy1(&model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
+
+ model.removeRows(1,1);
+ QCOMPARE(view.state(), 0);
+ // Should not be 5 (or any other number for that sake :)
+ QCOMPARE(spy1.count(), 1);
+
+}
+
+void tst_QTreeView::headerSections_unhideSection()
+{
+ QtTestModel model(10, 7);
+
+ QTreeView view;
+
+ view.setModel(&model);
+ view.show();
+ int size = view.header()->sectionSize(0);
+ view.setColumnHidden(0, true);
+
+ // should go back to old size
+ view.setColumnHidden(0, false);
+ QCOMPARE(size, view.header()->sectionSize(0));
+}
+
+void tst_QTreeView::columnAt()
+{
+ QtTestModel model;
+ model.rows = model.cols = 10;
+ QTreeView view;
+ view.resize(500,500);
+ view.setModel(&model);
+
+ QCOMPARE(view.columnAt(0), 0);
+ // really this is testing the header... so not much more should be needed if the header is working...
+}
+
+void tst_QTreeView::scrollTo()
+{
+#define CHECK_VISIBLE(ROW,COL) QVERIFY(QRect(QPoint(),view.viewport()->size()).contains(\
+ view.visualRect(model.index((ROW),(COL),QModelIndex()))))
+
+ QtTestModel model;
+ model.rows = model.cols = 100;
+ QTreeView view;
+ view.setUniformRowHeights(true);
+ view.scrollTo(QModelIndex(), QTreeView::PositionAtTop);
+ view.setModel(&model);
+
+ // ### check the scrollbar values an make sure it actually scrolls to the item
+ // ### check for bot item based and pixel based scrolling
+ // ### create a data function for this test
+
+ view.scrollTo(QModelIndex());
+ view.scrollTo(model.index(0,0,QModelIndex()));
+ view.scrollTo(model.index(0,0,QModelIndex()), QTreeView::PositionAtTop);
+ view.scrollTo(model.index(0,0,QModelIndex()), QTreeView::PositionAtBottom);
+
+ //
+
+ view.show();
+ view.setVerticalScrollMode(QAbstractItemView::ScrollPerItem); //some styles change that in Polish
+
+ view.resize(300, 200);
+ //view.verticalScrollBar()->setValue(0);
+
+ view.scrollTo(model.index(0,0,QModelIndex()));
+ CHECK_VISIBLE(0,0);
+ QCOMPARE(view.verticalScrollBar()->value(), 0);
+
+ view.header()->resizeSection(0, 5); // now we only see the branches
+ view.scrollTo(model.index(5, 0, QModelIndex()), QTreeView::PositionAtTop);
+ QCOMPARE(view.verticalScrollBar()->value(), 5);
+
+ view.scrollTo(model.index(60, 60, QModelIndex()));
+
+ CHECK_VISIBLE(60,60);
+ view.scrollTo(model.index(60, 30, QModelIndex()));
+ CHECK_VISIBLE(60,30);
+ view.scrollTo(model.index(30, 30, QModelIndex()));
+ CHECK_VISIBLE(30,30);
+
+ // TODO force it to move to the left and then the right
+}
+
+void tst_QTreeView::rowsAboutToBeRemoved_move()
+{
+ QStandardItemModel model(3,1);
+ QTreeView view;
+ view.setModel(&model);
+ QModelIndex indexThatWantsToLiveButWillDieDieITellYou;
+ QModelIndex parent = model.index(2, 0 );
+ view.expand(parent);
+ for (int i = 0; i < 6; ++i) {
+ model.insertRows(0, 1, parent);
+ model.insertColumns(0, 1, parent);
+ QModelIndex index = model.index(0, 0, parent);
+ view.expand(index);
+ if ( i == 3 )
+ indexThatWantsToLiveButWillDieDieITellYou = index;
+ model.setData(index, i);
+ parent = index;
+ }
+ view.resize(600,800);
+ view.show();
+ view.doItemsLayout();
+ static_cast<PublicView *>(&view)->executeDelayedItemsLayout();
+ parent = indexThatWantsToLiveButWillDieDieITellYou.parent();
+ QCOMPARE(view.isExpanded(indexThatWantsToLiveButWillDieDieITellYou), true);
+ QCOMPARE(parent.isValid(), true);
+ QCOMPARE(parent.parent().isValid(), true);
+ view.expand(parent);
+ QCOMPARE(view.isExpanded(parent), true);
+ QCOMPARE(view.isExpanded(indexThatWantsToLiveButWillDieDieITellYou), true);
+ model.removeRow(0, indexThatWantsToLiveButWillDieDieITellYou);
+ QCOMPARE(view.isExpanded(parent), true);
+ QCOMPARE(view.isExpanded(indexThatWantsToLiveButWillDieDieITellYou), true);
+}
+
+void tst_QTreeView::resizeColumnToContents()
+{
+ QStandardItemModel model(50,2);
+ for (int r = 0; r < model.rowCount(); ++r) {
+ for (int c = 0; c < model.columnCount(); ++c) {
+ QModelIndex idx = model.index(r, c);
+ model.setData(idx, QString::fromAscii("%1,%2").arg(r).arg(c) );
+ model.insertColumns(0, 2, idx);
+ model.insertRows(0, 6, idx);
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 2 ; ++j) {
+ model.setData(model.index(i, j, idx), QString::fromAscii("child%1%2").arg(i).arg(j));
+ }
+ }
+ }
+ }
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+ qApp->processEvents(); //must have this, or else it will not scroll
+ view.scrollToBottom();
+ view.resizeColumnToContents(0);
+ int oldColumnSize = view.header()->sectionSize(0);
+ view.setRootIndex(model.index(0, 0));
+ view.resizeColumnToContents(0); //Earlier, this gave an assert
+ QVERIFY(view.header()->sectionSize(0) > oldColumnSize);
+}
+
+void tst_QTreeView::insertAfterSelect()
+{
+ QtTestModel model;
+ model.rows = model.cols = 10;
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+ QModelIndex firstIndex = model.index(0, 0, QModelIndex());
+ QVERIFY(firstIndex.isValid());
+ int itemOffset = view.visualRect(firstIndex).width() / 2;
+ QPoint p(itemOffset, 1);
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
+ QVERIFY(view.selectionModel()->isSelected(firstIndex));
+ model.insertNewRow();
+ QVERIFY(view.selectionModel()->isSelected(firstIndex)); // Should still be selected
+}
+
+void tst_QTreeView::removeAfterSelect()
+{
+ QtTestModel model;
+ model.rows = model.cols = 10;
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+ QModelIndex firstIndex = model.index(0, 0, QModelIndex());
+ QVERIFY(firstIndex.isValid());
+ int itemOffset = view.visualRect(firstIndex).width() / 2;
+ QPoint p(itemOffset, 1);
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
+ QVERIFY(view.selectionModel()->isSelected(firstIndex));
+ model.removeLastRow();
+ QVERIFY(view.selectionModel()->isSelected(firstIndex)); // Should still be selected
+}
+
+void tst_QTreeView::hiddenItems()
+{
+ QtTestModel model;
+ model.rows = model.cols = 10;
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+
+ QModelIndex firstIndex = model.index(1, 0, QModelIndex());
+ QVERIFY(firstIndex.isValid());
+ if (model.canFetchMore(firstIndex))
+ model.fetchMore(firstIndex);
+ for (int i=0; i < model.rowCount(firstIndex); i++)
+ view.setRowHidden(i , firstIndex, true );
+
+ int itemOffset = view.visualRect(firstIndex).width() / 2;
+ int itemHeight = view.visualRect(firstIndex).height();
+ QPoint p(itemOffset, itemHeight + 1);
+ view.setExpanded(firstIndex, false);
+ QTest::mouseDClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
+ QCOMPARE(view.isExpanded(firstIndex), false);
+
+ p.setX( 5 );
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
+ QCOMPARE(view.isExpanded(firstIndex), false);
+}
+
+void tst_QTreeView::spanningItems()
+{
+ QtTestModel model;
+ model.rows = model.cols = 10;
+ PublicView view;
+ view.setModel(&model);
+ view.show();
+
+ int itemWidth = view.header()->sectionSize(0);
+ int itemHeight = view.visualRect(model.index(0, 0, QModelIndex())).height();
+
+ // every second row is spanning
+ for (int i = 1; i < model.rowCount(QModelIndex()); i += 2)
+ view.setFirstColumnSpanned(i , QModelIndex(), true);
+
+ // non-spanning item
+ QPoint p(itemWidth / 2, itemHeight / 2); // column 0, row 0
+ view.setCurrentIndex(QModelIndex());
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
+ QCOMPARE(view.currentIndex(), model.index(0, 0, QModelIndex()));
+ QCOMPARE(view.header()->sectionSize(0) - view.indentation(),
+ view.visualRect(model.index(0, 0, QModelIndex())).width());
+
+ // spanning item
+ p.setX(itemWidth + (itemWidth / 2)); // column 1
+ p.setY(itemHeight + (itemHeight / 2)); // row 1
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
+ QCOMPARE(view.currentIndex(), model.index(1, 0, QModelIndex()));
+ QCOMPARE(view.header()->length() - view.indentation(),
+ view.visualRect(model.index(1, 0, QModelIndex())).width());
+
+ // size hint
+ // every second row is un-spanned
+ QStyleOptionViewItem option = view.viewOptions();
+ int w = view.header()->sectionSizeHint(0);
+ for (int i = 0; i < model.rowCount(QModelIndex()); ++i) {
+ if (!view.isFirstColumnSpanned(i, QModelIndex())) {
+ QModelIndex index = model.index(i, 0, QModelIndex());
+ w = qMax(w, view.itemDelegate(index)->sizeHint(option, index).width() + view.indentation());
+ }
+ }
+ QCOMPARE(view.sizeHintForColumn(0), w);
+}
+
+void tst_QTreeView::selectionOrderTest()
+{
+ QVERIFY(((QItemSelectionModel*)sender())->currentIndex().row() != -1);
+}
+
+void tst_QTreeView::selection()
+{
+ QTreeView treeView;
+ QStandardItemModel m(10, 2);
+ for (int i = 0;i < 10; ++i)
+ m.setData(m.index(i, 0), i);
+ treeView.setModel(&m);
+
+ treeView.setSelectionBehavior(QAbstractItemView::SelectRows);
+ treeView.setSelectionMode(QAbstractItemView::ExtendedSelection);
+
+ connect(treeView.selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
+ this, SLOT(selectionOrderTest()));
+
+ treeView.show();
+
+ QTest::mousePress(treeView.viewport(), Qt::LeftButton, 0, treeView.visualRect(m.index(1, 0)).center());
+ QTest::keyPress(treeView.viewport(), Qt::Key_Down);
+}
+
+//From task 151686 QTreeView ExtendedSelection selects hidden rows
+void tst_QTreeView::selectionWithHiddenItems()
+{
+ QStandardItemModel model;
+ for (int i = 0; i < model.rowCount(); ++i)
+ model.setData(model.index(i,0), QString("row %1").arg(i));
+
+ QStandardItem item0("row 0");
+ QStandardItem item1("row 1");
+ QStandardItem item2("row 2");
+ QStandardItem item3("row 3");
+ model.appendColumn( QList<QStandardItem*>() << &item0 << &item1 << &item2 << &item3);
+
+ QStandardItem child("child");
+ item1.appendRow( &child);
+
+ QTreeView view;
+ view.setModel(&model);
+ view.setSelectionMode(QAbstractItemView::ExtendedSelection);
+ view.show();
+ qApp->processEvents();
+
+ //child should not be selected as it is hidden (its parent is not expanded)
+ view.selectAll();
+ QCOMPARE(view.selectionModel()->selection().count(), 1); //one range
+ QCOMPARE(view.selectionModel()->selectedRows().count(), 4);
+ view.expandAll();
+ QVERIFY(view.isExpanded(item1.index()));
+ QCOMPARE(view.selectionModel()->selection().count(), 1);
+ QCOMPARE(view.selectionModel()->selectedRows().count(), 4);
+ QVERIFY( !view.selectionModel()->isSelected(model.indexFromItem(&child)));
+ view.clearSelection();
+ QVERIFY(view.isExpanded(item1.index()));
+
+ //child should be selected as it is visible (its parent is expanded)
+ view.selectAll();
+ QCOMPARE(view.selectionModel()->selection().count(), 2);
+ QCOMPARE(view.selectionModel()->selectedRows().count(), 5); //everything is selected
+ view.clearSelection();
+
+ //we hide the node with a child (there should then be 3 items selected in 2 ranges)
+ view.setRowHidden(1, QModelIndex(), true);
+ QVERIFY(view.isExpanded(item1.index()));
+ qApp->processEvents();
+ view.selectAll();
+ QCOMPARE(view.selectionModel()->selection().count(), 2);
+ QCOMPARE(view.selectionModel()->selectedRows().count(), 3);
+ QVERIFY( !view.selectionModel()->isSelected(model.indexFromItem(&item1)));
+ QVERIFY( !view.selectionModel()->isSelected(model.indexFromItem(&child)));
+
+ view.setRowHidden(1, QModelIndex(), false);
+ QVERIFY(view.isExpanded(item1.index()));
+ view.clearSelection();
+
+ //we hide a node without children (there should then be 4 items selected in 3 ranges)
+ view.setRowHidden(2, QModelIndex(), true);
+ qApp->processEvents();
+ QVERIFY(view.isExpanded(item1.index()));
+ view.selectAll();
+ QVERIFY(view.isExpanded(item1.index()));
+ QCOMPARE(view.selectionModel()->selection().count(), 3);
+ QCOMPARE(view.selectionModel()->selectedRows().count(), 4);
+ QVERIFY( !view.selectionModel()->isSelected(model.indexFromItem(&item2)));
+ view.setRowHidden(2, QModelIndex(), false);
+ QVERIFY(view.isExpanded(item1.index()));
+ view.clearSelection();
+}
+
+void tst_QTreeView::selectAll()
+{
+ QStandardItemModel model(4,4);
+ PublicView view2;
+ view2.setModel(&model);
+ view2.setSelectionMode(QAbstractItemView::ExtendedSelection);
+ view2.selectAll(); // Should work with an empty model
+ //everything should be selected since we are in ExtendedSelection mode
+ QCOMPARE(view2.selectedIndexes().count(), model.rowCount() * model.columnCount());
+
+ for (int i = 0; i < model.rowCount(); ++i)
+ model.setData(model.index(i,0), QString("row %1").arg(i));
+ PublicView view;
+ view.setModel(&model);
+ int selectedCount = view.selectedIndexes().count();
+ view.selectAll();
+ QCOMPARE(view.selectedIndexes().count(), selectedCount);
+}
+
+void tst_QTreeView::extendedSelection_data()
+{
+ QTest::addColumn<QPoint>("mousePressPos");
+ QTest::addColumn<int>("selectedCount");
+
+ QTest::newRow("select") << QPoint(10, 10) << 2;
+ QTest::newRow("unselect") << QPoint(10, 150) << 0;
+}
+
+void tst_QTreeView::extendedSelection()
+{
+ QFETCH(QPoint, mousePressPos);
+ QFETCH(int, selectedCount);
+
+ QStandardItemModel model(5, 2);
+ QWidget topLevel;
+ QTreeView view(&topLevel);
+ view.resize(qMax(mousePressPos.x() * 2, 200), qMax(mousePressPos.y() * 2, 200));
+ view.setModel(&model);
+ view.setSelectionMode(QAbstractItemView::ExtendedSelection);
+ topLevel.show();
+ QTest::mousePress(view.viewport(), Qt::LeftButton, 0, mousePressPos);
+ QCOMPARE(view.selectionModel()->selectedIndexes().count(), selectedCount);
+}
+
+void tst_QTreeView::rowSizeHint()
+{
+ //tests whether the correct visible columns are taken into account when
+ //calculating the height of a line
+ QStandardItemModel model(1,3);
+ model.setData( model.index(0,0), QSize(20,40), Qt::SizeHintRole);
+ model.setData( model.index(0,1), QSize(20,10), Qt::SizeHintRole);
+ model.setData( model.index(0,2), QSize(20,10), Qt::SizeHintRole);
+ QTreeView view;
+ view.setModel(&model);
+
+ view.header()->moveSection(1, 0); //the 2nd column goes to the 1st place
+
+ view.show();
+
+ //it must be 40 since the tallest item that defines the height of a line
+ QCOMPARE( view.visualRect(model.index(0,0)).height(), 40);
+ QCOMPARE( view.visualRect(model.index(0,1)).height(), 40);
+ QCOMPARE( view.visualRect(model.index(0,2)).height(), 40);
+}
+
+
+//From task 155449 (QTreeWidget has a large width for the first section when sorting
+//is turned on before items are added)
+void tst_QTreeView::setSortingEnabled()
+{
+ //1st the treeview is a top-level
+ {
+ QTreeView view;
+ QStandardItemModel model(1,1);
+ view.setModel(&model);
+ const int size = view.header()->sectionSize(0);
+ view.setSortingEnabled(true);
+ model.setColumnCount(3);
+ //we test that changing the column count doesn't change the 1st column size
+ QCOMPARE(view.header()->sectionSize(0), size);
+ }
+
+ //then it is no more a top-level
+ {
+ QMainWindow win;
+ QTreeView view;
+ QStandardItemModel model(1,1);
+ view.setModel(&model);
+ win.setCentralWidget(&view);
+ const int size = view.header()->sectionSize(0);
+ view.setSortingEnabled(true);
+ model.setColumnCount(3);
+ //we test that changing the column count doesn't change the 1st column size
+ QCOMPARE(view.header()->sectionSize(0), size);
+ }
+}
+
+void tst_QTreeView::headerHidden()
+{
+ QTreeView view;
+ QStandardItemModel model;
+ view.setModel(&model);
+ QCOMPARE(view.isHeaderHidden(), false);
+ QCOMPARE(view.header()->isHidden(), false);
+ view.setHeaderHidden(true);
+ QCOMPARE(view.isHeaderHidden(), true);
+ QCOMPARE(view.header()->isHidden(), true);
+}
+
+// From Task 145199 (crash when column 0 having at least one expanded item is removed and then
+// inserted). The test passes simply if it doesn't crash, hence there are no calls
+// to QCOMPARE() or QVERIFY().
+void tst_QTreeView::removeAndInsertExpandedCol0()
+{
+ QTreeView view;
+ QStandardItemModel model;
+ view.setModel(&model);
+
+ model.setColumnCount(1);
+
+ QStandardItem *item0 = new QStandardItem(QString("item 0"));
+ model.invisibleRootItem()->appendRow(item0);
+ view.expand(item0->index());
+ QStandardItem *item1 = new QStandardItem(QString("item 1"));
+ item0->appendRow(item1);
+
+ model.removeColumns(0, 1);
+ model.insertColumns(0, 1);
+
+ view.show();
+ qApp->processEvents();
+}
+
+void tst_QTreeView::disabledButCheckable()
+{
+ QTreeView view;
+ QStandardItemModel model;
+ QStandardItem *item;
+ item = new QStandardItem(QLatin1String("Row 1 Item"));
+ model.insertRow(0, item);
+
+ item = new QStandardItem(QLatin1String("Row 2 Item"));
+ item->setCheckable(true);
+ item->setEnabled(false);
+ model.insertRow(1, item);
+
+ view.setModel(&model);
+ view.setCurrentIndex(model.index(1,0));
+ QCOMPARE(item->checkState(), Qt::Unchecked);
+ view.show();
+
+ QTest::keyClick(&view, Qt::Key_Space);
+ QCOMPARE(item->checkState(), Qt::Unchecked);
+}
+
+void tst_QTreeView::sortByColumn_data()
+{
+ QTest::addColumn<bool>("sortingEnabled");
+ QTest::newRow("sorting enabled") << true;
+ QTest::newRow("sorting disabled") << false;
+}
+
+// Checks sorting and that sortByColumn also sets the sortIndicator
+void tst_QTreeView::sortByColumn()
+{
+ QFETCH(bool, sortingEnabled);
+ QTreeView view;
+ QStandardItemModel model(4,2);
+ model.setItem(0,0,new QStandardItem("b"));
+ model.setItem(1,0,new QStandardItem("d"));
+ model.setItem(2,0,new QStandardItem("c"));
+ model.setItem(3,0,new QStandardItem("a"));
+ model.setItem(0,1,new QStandardItem("e"));
+ model.setItem(1,1,new QStandardItem("g"));
+ model.setItem(2,1,new QStandardItem("h"));
+ model.setItem(3,1,new QStandardItem("f"));
+
+ view.setSortingEnabled(sortingEnabled);
+ view.setModel(&model);
+ view.sortByColumn(1);
+ QCOMPARE(view.header()->sortIndicatorSection(), 1);
+ QCOMPARE(view.model()->data(view.model()->index(0,1)).toString(), QString::fromLatin1("h"));
+ QCOMPARE(view.model()->data(view.model()->index(1,1)).toString(), QString::fromLatin1("g"));
+ view.sortByColumn(0, Qt::AscendingOrder);
+ QCOMPARE(view.header()->sortIndicatorSection(), 0);
+ QCOMPARE(view.model()->data(view.model()->index(0,0)).toString(), QString::fromLatin1("a"));
+ QCOMPARE(view.model()->data(view.model()->index(1,0)).toString(), QString::fromLatin1("b"));
+}
+
+/*
+ This is a model that every time kill() is called it will completely change
+ all of its nodes for new nodes. It then qFatal's if you later use a dead node.
+ */
+class EvilModel: public QAbstractItemModel
+{
+
+public:
+ class Node {
+ public:
+ Node(Node *p = 0, int level = 0) : parent(p), isDead(false) {
+ populate(level);
+ }
+ ~Node()
+ {
+ qDeleteAll(children.begin(), children.end());
+ qDeleteAll(deadChildren.begin(), deadChildren.end());
+ }
+
+ void populate(int level = 0) {
+ if (level < 4)
+ for (int i = 0; i < 5; ++i)
+ children.append(new Node(this, level + 1));
+ }
+ void kill() {
+ for (int i = children.count() -1; i >= 0; --i) {
+ children.at(i)->kill();
+ if (parent == 0) {
+ deadChildren.append(children.at(i));
+ children.removeAt(i);
+ }
+ }
+ if (parent == 0) {
+ if (!children.isEmpty())
+ qFatal("%s: children should be empty when parent is null", Q_FUNC_INFO);
+ populate();
+ } else {
+ isDead = true;
+ }
+ }
+
+ QList<Node*> children;
+ QList<Node*> deadChildren;
+ Node *parent;
+ bool isDead;
+ };
+
+ Node *root;
+
+ EvilModel(QObject *parent = 0): QAbstractItemModel(parent), root(new Node)
+ {
+ }
+ ~EvilModel()
+ {
+ delete root;
+ }
+
+ void change()
+ {
+ emit layoutAboutToBeChanged();
+ QModelIndexList oldList = persistentIndexList();
+ QList<QStack<int> > oldListPath;
+ for (int i = 0; i < oldList.count(); ++i) {
+ QModelIndex idx = oldList.at(i);
+ QStack<int> path;
+ while (idx.isValid()) {
+ path.push(idx.row());
+ idx = idx.parent();
+ }
+ oldListPath.append(path);
+ }
+ root->kill();
+
+ QModelIndexList newList;
+ for (int i = 0; i < oldListPath.count(); ++i) {
+ QStack<int> path = oldListPath[i];
+ QModelIndex idx;
+ while(!path.isEmpty()) {
+ idx = index(path.pop(), 0, idx);
+ }
+ newList.append(idx);
+ }
+
+ changePersistentIndexList(oldList, newList);
+ emit layoutChanged();
+ }
+
+ int rowCount(const QModelIndex& parent = QModelIndex()) const {
+ Node *parentNode = root;
+ if (parent.isValid()) {
+ parentNode = static_cast<Node*>(parent.internalPointer());
+ if (parentNode->isDead)
+ qFatal("%s: parentNode is dead!", Q_FUNC_INFO);
+ }
+ return parentNode->children.count();
+ }
+ int columnCount(const QModelIndex& parent = QModelIndex()) const {
+ if (parent.column() > 0)
+ return 0;
+ return 1;
+ }
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const
+ {
+ Node *grandparentNode = static_cast<Node*>(parent.internalPointer());
+ Node *parentNode = root;
+ if (parent.isValid()) {
+ if (grandparentNode->isDead)
+ qFatal("%s: grandparentNode is dead!", Q_FUNC_INFO);
+ parentNode = grandparentNode->children[parent.row()];
+ if (parentNode->isDead)
+ qFatal("%s: grandparentNode is dead!", Q_FUNC_INFO);
+ }
+ return createIndex(row, column, parentNode);
+ }
+
+ QModelIndex parent(const QModelIndex &index) const
+ {
+ Node *parent = static_cast<Node*>(index.internalPointer());
+ Node *grandparent = parent->parent;
+ if (!grandparent)
+ return QModelIndex();
+ return createIndex(grandparent->children.indexOf(parent), 0, grandparent);
+ }
+
+ QVariant data(const QModelIndex &idx, int role) const
+ {
+ if (idx.isValid() && role == Qt::DisplayRole) {
+ Node *parentNode = root;
+ if (idx.isValid()) {
+ parentNode = static_cast<Node*>(idx.internalPointer());
+ if (parentNode->isDead)
+ qFatal("%s: grandparentNode is dead!", Q_FUNC_INFO);
+ }
+ return QString("[%1,%2,%3]").arg(idx.row()).arg(idx.column())
+ .arg(parentNode->isDead ? "dead" : "alive");
+ }
+ return QVariant();
+ }
+};
+
+void tst_QTreeView::evilModel_data()
+{
+ QTest::addColumn<bool>("visible");
+ QTest::newRow("visible") << false;
+}
+
+void tst_QTreeView::evilModel()
+{
+ QFETCH(bool, visible);
+ // init
+ PublicView view;
+ EvilModel model;
+ view.setModel(&model);
+ view.setVisible(visible);
+ QPersistentModelIndex firstLevel = model.index(0, 0);
+ QPersistentModelIndex secondLevel = model.index(1, 0, firstLevel);
+ QPersistentModelIndex thirdLevel = model.index(2, 0, secondLevel);
+ view.setExpanded(firstLevel, true);
+ view.setExpanded(secondLevel, true);
+ view.setExpanded(thirdLevel, true);
+ model.change();
+
+ // tests
+ view.setRowHidden(0, firstLevel, true);
+ model.change();
+
+ return;
+ view.setFirstColumnSpanned(1, QModelIndex(), true);
+ model.change();
+
+ view.expand(secondLevel);
+ model.change();
+
+ view.collapse(secondLevel);
+ model.change();
+
+ view.isExpanded(secondLevel);
+ view.setCurrentIndex(firstLevel);
+ model.change();
+
+ view.keyboardSearch("foo");
+ model.change();
+
+ view.visualRect(secondLevel);
+ model.change();
+
+ view.scrollTo(thirdLevel);
+ model.change();
+
+ view.repaint();
+ model.change();
+
+ QTest::mouseDClick(view.viewport(), Qt::LeftButton);
+ model.change();
+
+ view.indexAt(QPoint(10, 10));
+ model.change();
+
+ view.indexAbove(model.index(2, 0));
+ model.change();
+
+ view.indexBelow(model.index(1, 0));
+ model.change();
+
+ QRect rect(0, 0, 10, 10);
+ view.setSelection(rect, QItemSelectionModel::Select);
+ model.change();
+
+ view.moveCursor(PublicView::MoveDown, Qt::NoModifier);
+ model.change();
+
+ view.resizeColumnToContents(1);
+ model.change();
+
+ view.QAbstractItemView::sizeHintForColumn(1);
+ model.change();
+
+ view.rowHeight(secondLevel);
+ model.change();
+
+ view.setRootIsDecorated(true);
+ model.change();
+
+ view.setItemsExpandable(false);
+ model.change();
+
+ view.columnViewportPosition(0);
+ model.change();
+
+ view.columnWidth(0);
+ model.change();
+
+ view.setColumnWidth(0, 30);
+ model.change();
+
+ view.columnAt(15);
+ model.change();
+
+ view.isColumnHidden(1);
+ model.change();
+
+ view.setColumnHidden(2, true);
+ model.change();
+
+ view.isHeaderHidden();
+ model.change();
+
+ view.setHeaderHidden(true);
+ model.change();
+
+ view.isRowHidden(2, secondLevel);
+ model.change();
+
+ view.setRowHidden(3, secondLevel, true);
+ model.change();
+
+ view.isFirstColumnSpanned(3, thirdLevel);
+ model.change();
+
+ view.setSortingEnabled(true);
+ model.change();
+
+ view.isSortingEnabled();
+ model.change();
+
+ view.setAnimated(true);
+ model.change();
+
+ view.isAnimated();
+ model.change();
+
+ view.setAllColumnsShowFocus(true);
+ model.change();
+
+ view.allColumnsShowFocus();
+ model.change();
+
+ view.doItemsLayout();
+ model.change();
+
+ view.reset();
+ model.change();
+
+ view.sortByColumn(1, Qt::AscendingOrder);
+ model.change();
+
+ view.dataChanged(secondLevel, secondLevel);
+ model.change();
+
+ view.hideColumn(1);
+ model.change();
+
+ view.showColumn(1);
+ model.change();
+
+ view.resizeColumnToContents(1);
+ model.change();
+
+ view.sortByColumn(1);
+ model.change();
+
+ view.selectAll();
+ model.change();
+
+ view.expandAll();
+ model.change();
+
+ view.collapseAll();
+ model.change();
+
+ view.expandToDepth(3);
+ model.change();
+
+ view.setRootIndex(secondLevel);
+}
+
+void tst_QTreeView::indexRowSizeHint()
+{
+ QStandardItemModel model(10, 1);
+ PublicView view;
+
+ view.setModel(&model);
+
+ QModelIndex index = model.index(5, 0);
+ QPushButton *w = new QPushButton("Test");
+ view.setIndexWidget(index, w);
+
+ view.show();
+
+ QCOMPARE(view.indexRowSizeHint(index), w->sizeHint().height());
+}
+
+void tst_QTreeView::filterProxyModelCrash()
+{
+ QStandardItemModel model;
+ QList<QStandardItem *> items;
+ for (int i = 0; i < 100; i++)
+ items << new QStandardItem(QString::fromLatin1("item %1").arg(i));
+ model.appendColumn(items);
+
+ QSortFilterProxyModel proxy;
+ proxy.setSourceModel(&model);
+
+ QTreeView view;
+ view.setModel(&proxy);
+ view.show();
+ QTest::qWait(30);
+ proxy.invalidate();
+ view.verticalScrollBar()->setValue(15);
+ QTest::qWait(20);
+
+ proxy.invalidate();
+ view.repaint(); //used to crash
+}
+
+void tst_QTreeView::styleOptionViewItem()
+{
+ class MyDelegate : public QStyledItemDelegate
+ {
+ public:
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
+ {
+ QVERIFY(qstyleoption_cast<const QStyleOptionViewItemV4 *>(&option));
+ QStyleOptionViewItemV4 opt(option);
+ initStyleOption(&opt, index);
+
+ QVERIFY(!opt.text.isEmpty());
+ QCOMPARE(opt.index, index);
+ //qDebug() << index << opt.text;
+
+ if (allCollapsed)
+ QCOMPARE(!(opt.features & QStyleOptionViewItemV2::Alternate), !(index.row() % 2));
+ QCOMPARE(!(opt.features & QStyleOptionViewItemV2::HasCheckIndicator), !opt.text.contains("Checkable"));
+
+ if (opt.text.contains("Beginning"))
+ QCOMPARE(opt.viewItemPosition, QStyleOptionViewItemV4::Beginning);
+
+ if (opt.text.contains("Middle"))
+ QCOMPARE(opt.viewItemPosition, QStyleOptionViewItemV4::Middle);
+
+ if (opt.text.contains("End"))
+ QCOMPARE(opt.viewItemPosition, QStyleOptionViewItemV4::End);
+
+ if (opt.text.contains("OnlyOne"))
+ QCOMPARE(opt.viewItemPosition, QStyleOptionViewItemV4::OnlyOne);
+
+ if (opt.text.contains("Checked"))
+ QCOMPARE(opt.checkState, Qt::Checked);
+ else
+ QCOMPARE(opt.checkState, Qt::Unchecked);
+
+ QCOMPARE(!(opt.state & QStyle::State_Children) , !opt.text.contains("HasChildren"));
+ QCOMPARE(!!(opt.state & QStyle::State_Sibling) , !opt.text.contains("Last"));
+
+ QVERIFY(!opt.text.contains("Assert"));
+
+ QStyledItemDelegate::paint(painter, option, index);
+ count++;
+ }
+ mutable int count;
+ bool allCollapsed;
+ };
+
+ QTreeView view;
+ QStandardItemModel model;
+ view.setModel(&model);
+ MyDelegate delegate;
+ view.setItemDelegate(&delegate);
+ model.appendRow(QList<QStandardItem*>()
+ << new QStandardItem("Beginning") << new QStandardItem("Middle") << new QStandardItem("Middle") << new QStandardItem("End") );
+ QStandardItem *par1 = new QStandardItem("Beginning HasChildren");
+ model.appendRow(QList<QStandardItem*>()
+ << par1 << new QStandardItem("Middle HasChildren") << new QStandardItem("Middle HasChildren") << new QStandardItem("End HasChildren") );
+ model.appendRow(QList<QStandardItem*>()
+ << new QStandardItem("OnlyOne") << new QStandardItem("Assert") << new QStandardItem("Assert") << new QStandardItem("Assert") );
+ QStandardItem *checkable = new QStandardItem("Checkable");
+ checkable->setCheckable(true);
+ QStandardItem *checked = new QStandardItem("Checkable Checked");
+ checkable->setCheckable(true);
+ checked->setCheckState(Qt::Checked);
+ model.appendRow(QList<QStandardItem*>()
+ << new QStandardItem("Beginning") << checkable << checked << new QStandardItem("End") );
+ model.appendRow(QList<QStandardItem*>()
+ << new QStandardItem("Beginning Last") << new QStandardItem("Middle Last") << new QStandardItem("Middle Last") << new QStandardItem("End Last") );
+
+ par1->appendRow(QList<QStandardItem*>()
+ << new QStandardItem("Beginning") << new QStandardItem("Middle") << new QStandardItem("Middle") << new QStandardItem("End") );
+ QStandardItem *par2 = new QStandardItem("Beginning HasChildren");
+ par1->appendRow(QList<QStandardItem*>()
+ << par2 << new QStandardItem("Middle HasChildren") << new QStandardItem("Middle HasChildren") << new QStandardItem("End HasChildren") );
+ par2->appendRow(QList<QStandardItem*>()
+ << new QStandardItem("Beginning Last") << new QStandardItem("Middle Last") << new QStandardItem("Middle Last") << new QStandardItem("End Last") );
+
+ QStandardItem *par3 = new QStandardItem("Beginning Last");
+ par1->appendRow(QList<QStandardItem*>()
+ << par3 << new QStandardItem("Middle Last") << new QStandardItem("Middle Last") << new QStandardItem("End Last") );
+ par3->appendRow(QList<QStandardItem*>()
+ << new QStandardItem("Assert") << new QStandardItem("Assert") << new QStandardItem("Assert") << new QStandardItem("Asser") );
+ view.setRowHidden(0, par3->index(), true);
+ par1->appendRow(QList<QStandardItem*>()
+ << new QStandardItem("Assert") << new QStandardItem("Assert") << new QStandardItem("Assert") << new QStandardItem("Asser") );
+ view.setRowHidden(3, par1->index(), true);
+
+
+ view.setFirstColumnSpanned(2, QModelIndex(), true);
+ view.setAlternatingRowColors(true);
+
+ delegate.count = 0;
+ delegate.allCollapsed = true;
+ view.showMaximized();
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 13);
+ delegate.count = 0;
+ delegate.allCollapsed = false;
+ view.expandAll();
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 13);
+ delegate.count = 0;
+ view.collapse(par2->index());
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 4);
+
+
+ //test dynamic models
+ {
+ delegate.count = 0;
+ QStandardItemModel model2;
+ QStandardItem *item0 = new QStandardItem("OnlyOne Last");
+ model2.appendRow(QList<QStandardItem*>() << item0);
+ view.setModel(&model2);
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 1);
+ QApplication::processEvents();
+
+ QStandardItem *item00 = new QStandardItem("OnlyOne Last");
+ item0->appendRow(QList<QStandardItem*>() << item00);
+ item0->setText("OnlyOne Last HasChildren");
+ QApplication::processEvents();
+ delegate.count = 0;
+ view.expandAll();
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 2);
+ QApplication::processEvents();
+
+ QStandardItem *item1 = new QStandardItem("OnlyOne Last");
+ delegate.count = 0;
+ item0->setText("OnlyOne HasChildren");
+ model2.appendRow(QList<QStandardItem*>() << item1);
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 3);
+ QApplication::processEvents();
+
+ QStandardItem *item01 = new QStandardItem("OnlyOne Last");
+ delegate.count = 0;
+ item00->setText("OnlyOne");
+ item0->appendRow(QList<QStandardItem*>() << item01);
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 4);
+ QApplication::processEvents();
+
+ QStandardItem *item000 = new QStandardItem("OnlyOne Last");
+ delegate.count = 0;
+ item00->setText("OnlyOne HasChildren");
+ item00->appendRow(QList<QStandardItem*>() << item000);
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 5);
+ QApplication::processEvents();
+
+ delegate.count = 0;
+ item0->removeRow(0);
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 3);
+ QApplication::processEvents();
+
+ item00 = new QStandardItem("OnlyOne");
+ item0->insertRow(0, QList<QStandardItem*>() << item00);
+ QApplication::processEvents();
+ delegate.count = 0;
+ view.expandAll();
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 4);
+ QApplication::processEvents();
+
+ delegate.count = 0;
+ item0->removeRow(1);
+ item00->setText("OnlyOne Last");
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 3);
+ QApplication::processEvents();
+
+ delegate.count = 0;
+ item0->removeRow(0);
+ item0->setText("OnlyOne");
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 2);
+ QApplication::processEvents();
+
+ //with hidden items
+ item0->setText("OnlyOne HasChildren");
+ item00 = new QStandardItem("OnlyOne");
+ item0->appendRow(QList<QStandardItem*>() << item00);
+ item01 = new QStandardItem("Assert");
+ item0->appendRow(QList<QStandardItem*>() << item01);
+ view.setRowHidden(1, item0->index(), true);
+ view.expandAll();
+ QStandardItem *item02 = new QStandardItem("OnlyOne Last");
+ item0->appendRow(QList<QStandardItem*>() << item02);
+ delegate.count = 0;
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 4);
+ QApplication::processEvents();
+
+ item0->removeRow(2);
+ item00->setText("OnlyOne Last");
+ delegate.count = 0;
+ QApplication::processEvents();
+ QTRY_VERIFY(delegate.count >= 3);
+ QApplication::processEvents();
+
+ item00->setText("OnlyOne");
+ item0->insertRow(2, new QStandardItem("OnlyOne Last"));
+ view.collapse(item0->index());
+ item0->removeRow(0);
+ delegate.count = 0;
+ QTRY_VERIFY(delegate.count >= 2);
+ QApplication::processEvents();
+
+ item0->removeRow(1);
+ item0->setText("OnlyOne");
+ delegate.count = 0;
+ QTRY_VERIFY(delegate.count >= 2);
+ QApplication::processEvents();
+ }
+}
+
+class task174627_TreeView : public QTreeView
+{
+ Q_OBJECT
+protected slots:
+ void currentChanged(const QModelIndex &current, const QModelIndex &)
+ { emit currentChanged(current); }
+signals:
+ void currentChanged(const QModelIndex &);
+};
+
+void tst_QTreeView::task174627_moveLeftToRoot()
+{
+ QStandardItemModel model;
+ QStandardItem *item1 = new QStandardItem(QString("item 1"));
+ model.invisibleRootItem()->appendRow(item1);
+ QStandardItem *item2 = new QStandardItem(QString("item 2"));
+ item1->appendRow(item2);
+
+ task174627_TreeView view;
+ view.setModel(&model);
+ view.setRootIndex(item1->index());
+ view.setCurrentIndex(item2->index());
+
+ QSignalSpy spy(&view, SIGNAL(currentChanged(QModelIndex)));
+ QTest::keyClick(&view, Qt::Key_Left);
+ QCOMPARE(spy.count(), 0);
+}
+
+void tst_QTreeView::task171902_expandWith1stColHidden()
+{
+ //the task was: if the first column of a treeview is hidden, the expanded state is not correctly restored
+ QStandardItemModel model;
+ QStandardItem root("root"), root2("root"),
+ subitem("subitem"), subitem2("subitem"),
+ subsubitem("subsubitem"), subsubitem2("subsubitem");
+
+ model.appendRow( QList<QStandardItem *>() << &root << &root2);
+ root.appendRow( QList<QStandardItem *>() << &subitem << &subitem2);
+ subitem.appendRow( QList<QStandardItem *>() << &subsubitem << &subsubitem2);
+
+ QTreeView view;
+ view.setModel(&model);
+
+ view.setColumnHidden(0, true);
+ view.expandAll();
+ view.collapse(root.index());
+ view.expand(root.index());
+
+ QCOMPARE(view.isExpanded(root.index()), true);
+ QCOMPARE(view.isExpanded(subitem.index()), true);
+
+}
+
+void tst_QTreeView::task203696_hidingColumnsAndRowsn()
+{
+ QTreeView view;
+ QStandardItemModel *model = new QStandardItemModel(0, 3, &view);
+ for (int i = 0; i < 3; ++i)
+ {
+ model->insertRow(model->rowCount());
+ for (int j = 0; j < model->columnCount(); ++j)
+ model->setData(model->index(i, j), QString("row %1 col %2").arg(i).arg(j));
+ }
+ view.setModel(model);
+ view.show();
+ view.setColumnHidden(0, true);
+ view.setRowHidden(0, QModelIndex(), true);
+ QCOMPARE(view.indexAt(QPoint(0, 0)), model->index(1, 1));
+}
+
+
+void tst_QTreeView::addRowsWhileSectionsAreHidden()
+{
+ QTreeView view;
+ for (int pass = 1; pass <= 2; ++pass) {
+ QStandardItemModel *model = new QStandardItemModel(6, pass, &view);
+ view.setModel(model);
+ view.show();
+
+ int i;
+ for (i = 0; i < 3; ++i)
+ {
+ model->insertRow(model->rowCount());
+ for (int j = 0; j < model->columnCount(); ++j) {
+ model->setData(model->index(i, j), QString("row %1 col %2").arg(i).arg(j));
+ }
+ }
+ int col;
+ for (col = 0; col < pass; ++col)
+ view.setColumnHidden(col, true);
+ for (i = 3; i < 6; ++i)
+ {
+ model->insertRow(model->rowCount());
+ for (int j = 0; j < model->columnCount(); ++j) {
+ model->setData(model->index(i, j), QString("row %1 col %2").arg(i).arg(j));
+ }
+ }
+ for (col = 0; col < pass; ++col)
+ view.setColumnHidden(col, false);
+ QTest::qWait(250);
+
+ for (i = 0; i < 6; ++i) {
+ QRect rect = view.visualRect(model->index(i, 0));
+ QCOMPARE(rect.isValid(), true);
+ }
+
+ delete model;
+ }
+}
+
+void tst_QTreeView::task216717_updateChildren()
+{
+ class Tree : public QTreeWidget {
+ protected:
+ void paintEvent(QPaintEvent *e)
+ {
+ QTreeWidget::paintEvent(e);
+ refreshed=true;
+ }
+ public:
+ bool refreshed;
+ } tree;
+ tree.show();
+ QTest::qWaitForWindowShown(&tree);
+ tree.refreshed = false;
+ QTreeWidgetItem *parent = new QTreeWidgetItem(QStringList() << "parent");
+ tree.addTopLevelItem(parent);
+ QTest::qWait(10);
+ QTRY_VERIFY(tree.refreshed);
+ tree.refreshed = false;
+ parent->addChild(new QTreeWidgetItem(QStringList() << "child"));
+ QTest::qWait(10);
+ QTRY_VERIFY(tree.refreshed);
+
+}
+
+void tst_QTreeView::task220298_selectColumns()
+{
+ //this is a very simple 3x3 model where the internalId of the index are different for each cell
+ class Model : public QAbstractTableModel
+ { public:
+ virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const
+ { return parent.isValid() ? 0 : 3; }
+ virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const
+ { return parent.isValid() ? 0 : 3; }
+
+ virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const
+ {
+ if(role == Qt::DisplayRole)
+ return QVariant(QString("%1-%2").arg(index.column()).arg(index.row()));
+ return QVariant();
+ }
+
+ virtual QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const
+ {
+ return hasIndex(row, column, parent) ? createIndex(row, column, column*10+row) : QModelIndex();
+ }
+ };
+
+ class TreeView : public QTreeView { public: QModelIndexList selectedIndexes () const { return QTreeView::selectedIndexes(); } } view;
+ Model model;
+ view.setModel(&model);
+ view.show();
+ QTest::qWait(50);
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, 0,
+ view.visualRect(view.model()->index(1, 1)).center());
+ QTest::qWait(50);
+ QVERIFY(view.selectedIndexes().contains(view.model()->index(1, 2)));
+ QVERIFY(view.selectedIndexes().contains(view.model()->index(1, 1)));
+ QVERIFY(view.selectedIndexes().contains(view.model()->index(1, 0)));
+}
+
+
+void tst_QTreeView::task224091_appendColumns()
+{
+ QStandardItemModel *model = new QStandardItemModel();
+ QWidget* topLevel= new QWidget;
+ QTreeView *treeView = new QTreeView(topLevel);
+ treeView->setModel(model);
+ topLevel->show();
+ treeView->resize(50,50);
+
+ QTest::qWaitForWindowShown(treeView);
+ qApp->processEvents();
+
+ QList<QStandardItem *> projlist;
+ for (int k = 0; k < 10; ++k)
+ projlist.append(new QStandardItem(QString("Top Level %0").arg(k)));
+ model->appendColumn(projlist);
+ model->invisibleRootItem()->appendRow(new QStandardItem("end"));
+
+ QTest::qWait(50);
+ qApp->processEvents();
+
+ QTRY_VERIFY(treeView->verticalScrollBar()->isVisible());
+
+ delete topLevel;
+ delete model;
+}
+
+void tst_QTreeView::task211293_removeRootIndex()
+{
+ QTreeView view;
+ QStandardItemModel model;
+ QStandardItem *A1 = new QStandardItem("A1");
+ QStandardItem *B11 = new QStandardItem("B1.1");
+ QStandardItem *C111 = new QStandardItem("C1.1.1");
+ QStandardItem *C112 = new QStandardItem("C1.1.2");
+ QStandardItem *C113 = new QStandardItem("C1.1.3");
+ QStandardItem *D1131 = new QStandardItem("D1.1.3.1");
+ QStandardItem *E11311 = new QStandardItem("E1.1.3.1.1");
+ QStandardItem *E11312 = new QStandardItem("E1.1.3.1.2");
+ QStandardItem *E11313 = new QStandardItem("E1.1.3.1.3");
+ QStandardItem *E11314 = new QStandardItem("E1.1.3.1.4");
+ QStandardItem *D1132 = new QStandardItem("D1.1.3.2");
+ QStandardItem *E11321 = new QStandardItem("E1.1.3.2.1");
+ D1132->appendRow(E11321);
+ D1131->appendRow(E11311);
+ D1131->appendRow(E11312);
+ D1131->appendRow(E11313);
+ D1131->appendRow(E11314);
+ C113->appendRow(D1131);
+ C113->appendRow(D1132);
+ B11->appendRow(C111);
+ B11->appendRow(C112);
+ B11->appendRow(C113);
+ A1->appendRow(B11);
+ model.appendRow(A1);
+ view.setModel(&model);
+ view.setRootIndex(model.indexFromItem(B11));
+ view.setExpanded(model.indexFromItem(B11), true);
+ view.setCurrentIndex(model.indexFromItem(E11314));
+ view.setExpanded(model.indexFromItem(E11314), true);
+ view.show();
+ qApp->processEvents();
+ model.removeRows(0, 1);
+ qApp->processEvents();
+}
+
+void tst_QTreeView::task225539_deleteModel()
+{
+ QTreeView treeView;
+ treeView.show();
+ QStandardItemModel *model = new QStandardItemModel(&treeView);
+
+ QStandardItem* parentItem = model->invisibleRootItem();
+ QStandardItem* item = new QStandardItem(QString("item"));
+ parentItem->appendRow(item);
+
+ treeView.setModel(model);
+
+ QCOMPARE(item->index(), treeView.indexAt(QPoint()));
+
+ delete model;
+
+ QVERIFY(!treeView.indexAt(QPoint()).isValid());
+}
+
+void tst_QTreeView::task230123_setItemsExpandable()
+{
+ //let's check that we prevent the expansion inside a treeview
+ //when the property is set.
+ QTreeWidget tree;
+
+ QTreeWidgetItem root;
+ QTreeWidgetItem child;
+ root.addChild(&child);
+ tree.addTopLevelItem(&root);
+
+ tree.setCurrentItem(&root);
+
+ tree.setItemsExpandable(false);
+
+ QTest::keyClick(&tree, Qt::Key_Plus);
+ QVERIFY(!root.isExpanded());
+
+ QTest::keyClick(&tree, Qt::Key_Right);
+ QVERIFY(!root.isExpanded());
+
+ tree.setItemsExpandable(true);
+
+ QTest::keyClick(&tree, Qt::Key_Plus);
+ QVERIFY(root.isExpanded());
+
+ QTest::keyClick(&tree, Qt::Key_Minus);
+ QVERIFY(!root.isExpanded());
+
+ QTest::keyClick(&tree, Qt::Key_Right);
+ QVERIFY(root.isExpanded());
+
+ QTest::keyClick(&tree, Qt::Key_Left);
+ QVERIFY(!root.isExpanded());
+
+ QTest::keyClick(&tree, Qt::Key_Right);
+ QVERIFY(root.isExpanded());
+
+ const bool navToChild = tree.style()->styleHint(QStyle::SH_ItemView_ArrowKeysNavigateIntoChildren, 0, &tree);
+ QTest::keyClick(&tree, Qt::Key_Right);
+ QCOMPARE(tree.currentItem(), navToChild ? &child : &root);
+
+ QTest::keyClick(&tree, Qt::Key_Right);
+ //it should not be expanded: it has no leaf
+ QCOMPARE(child.isExpanded(), false);
+
+ QTest::keyClick(&tree, Qt::Key_Left);
+ QCOMPARE(tree.currentItem(), &root);
+
+ QTest::keyClick(&tree, Qt::Key_Left);
+ QVERIFY(!root.isExpanded());
+
+
+}
+
+void tst_QTreeView::task202039_closePersistentEditor()
+{
+ QStandardItemModel model(1,1);
+ QTreeView view;
+ view.setModel(&model);
+
+ QModelIndex current = model.index(0,0);
+ QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.visualRect(current).center());
+ QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, view.visualRect(current).center());
+ QCOMPARE(view.currentIndex(), current);
+ QVERIFY(view.indexWidget(current));
+
+ view.closePersistentEditor(current);
+ QVERIFY(view.indexWidget(current) == 0);
+
+ //here was the bug: closing the persistent editor would not reset the state
+ //and it was impossible to go into editinon again
+ QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.visualRect(current).center());
+ QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, view.visualRect(current).center());
+ QCOMPARE(view.currentIndex(), current);
+ QVERIFY(view.indexWidget(current));
+}
+
+void tst_QTreeView::task238873_avoidAutoReopening()
+{
+ QStandardItemModel model;
+
+ QStandardItem item0("row 0");
+ QStandardItem item1("row 1");
+ QStandardItem item2("row 2");
+ QStandardItem item3("row 3");
+ model.appendColumn( QList<QStandardItem*>() << &item0 << &item1 << &item2 << &item3);
+
+ QStandardItem child("child");
+ item1.appendRow( &child);
+
+ QTreeView view;
+ view.setModel(&model);
+ view.show();
+ view.expandAll();
+ QTest::qWait(100);
+
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.visualRect(child.index()).center());
+ QTest::qWait(20);
+ QCOMPARE(view.currentIndex(), child.index());
+
+ view.setExpanded(item1.index(), false);
+
+ QTest::qWait(500); //enough to trigger the delayedAutoScroll timer
+ QVERIFY(!view.isExpanded(item1.index()));
+}
+
+void tst_QTreeView::task244304_clickOnDecoration()
+{
+ QTreeView view;
+ QStandardItemModel model;
+ QStandardItem item0("row 0");
+ QStandardItem item00("row 0");
+ item0.appendRow(&item00);
+ QStandardItem item1("row 1");
+ model.appendColumn(QList<QStandardItem*>() << &item0 << &item1);
+ view.setModel(&model);
+
+ QVERIFY(!view.currentIndex().isValid());
+ QRect rect = view.visualRect(item0.index());
+ //we click on the decoration
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, rect.topLeft()+QPoint(-rect.left()/2,rect.height()/2));
+ QVERIFY(!view.currentIndex().isValid());
+ QVERIFY(view.isExpanded(item0.index()));
+
+ rect = view.visualRect(item1.index());
+ //the item has no decoration, it should get selected
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, rect.topLeft()+QPoint(-rect.left()/2,rect.height()/2));
+ QCOMPARE(view.currentIndex(), item1.index());
+}
+
+void tst_QTreeView::task246536_scrollbarsNotWorking()
+{
+ struct MyObject : public QObject
+ {
+ MyObject() : count(0)
+ {
+ }
+
+ bool eventFilter(QObject*, QEvent *e)
+ {
+ if (e->type() == QEvent::Paint)
+ count++;
+
+ return false;
+ }
+
+ int count;
+ };
+ QTreeView tree;
+ MyObject o;
+ tree.viewport()->installEventFilter(&o);
+ QStandardItemModel model;
+ tree.setModel(&model);
+ tree.show();
+ QTest::qWaitForWindowShown(&tree);
+ QList<QStandardItem *> items;
+ for(int i=0; i<100; ++i){
+ items << new QStandardItem(QString::fromLatin1("item %1").arg(i));
+ }
+ model.invisibleRootItem()->appendColumn(items);
+ QTest::qWait(100);
+ o.count = 0;
+ tree.verticalScrollBar()->setValue(50);
+ QTest::qWait(100);
+ QTRY_VERIFY(o.count > 0);
+}
+
+void tst_QTreeView::task250683_wrongSectionSize()
+{
+ QDirModel model;
+ QTreeView treeView;
+ treeView.header()->setResizeMode(QHeaderView::ResizeToContents);
+ treeView.setModel(&model);
+ treeView.setColumnHidden(2, true);
+ treeView.setColumnHidden(3, true);
+
+ treeView.show();
+ QTest::qWait(100);
+
+ QCOMPARE(treeView.header()->sectionSize(0) + treeView.header()->sectionSize(1), treeView.viewport()->width());
+}
+
+void tst_QTreeView::task239271_addRowsWithFirstColumnHidden()
+{
+ class MyDelegate : public QStyledItemDelegate
+ {
+ public:
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
+ {
+ paintedIndexes << index;
+ QStyledItemDelegate::paint(painter, option, index);
+ }
+
+ mutable QSet<QModelIndex> paintedIndexes;
+ };
+
+ QTreeView view;
+ QStandardItemModel model;
+ view.setModel(&model);
+ MyDelegate delegate;
+ view.setItemDelegate(&delegate);
+ QStandardItem root0("root0"), root1("root1");
+ model.invisibleRootItem()->appendRow(QList<QStandardItem*>() << &root0 << &root1);
+ QStandardItem sub0("sub0"), sub00("sub00");
+ root0.appendRow(QList<QStandardItem*>() << &sub0 << &sub00);
+ view.expand(root0.index());
+
+ view.hideColumn(0);
+ view.show();
+ QTest::qWaitForWindowShown(&view);
+ delegate.paintedIndexes.clear();
+ QStandardItem sub1("sub1"), sub11("sub11");
+ root0.appendRow(QList<QStandardItem*>() << &sub1 << &sub11);
+
+ QTest::qWait(20);
+ //items in the 2nd column should have been painted
+ QTRY_VERIFY(!delegate.paintedIndexes.isEmpty());
+ QVERIFY(delegate.paintedIndexes.contains(sub00.index()));
+ QVERIFY(delegate.paintedIndexes.contains(sub11.index()));
+}
+
+void tst_QTreeView::task254234_proxySort()
+{
+ //based on tst_QTreeView::sortByColumn
+ // it used not to work when setting the source of a proxy after enabling sorting
+ QTreeView view;
+ QStandardItemModel model(4,2);
+ model.setItem(0,0,new QStandardItem("b"));
+ model.setItem(1,0,new QStandardItem("d"));
+ model.setItem(2,0,new QStandardItem("c"));
+ model.setItem(3,0,new QStandardItem("a"));
+ model.setItem(0,1,new QStandardItem("e"));
+ model.setItem(1,1,new QStandardItem("g"));
+ model.setItem(2,1,new QStandardItem("h"));
+ model.setItem(3,1,new QStandardItem("f"));
+
+ view.sortByColumn(1);
+ view.setSortingEnabled(true);
+
+ QSortFilterProxyModel proxy;
+ proxy.setDynamicSortFilter(true);
+ view.setModel(&proxy);
+ proxy.setSourceModel(&model);
+ QCOMPARE(view.header()->sortIndicatorSection(), 1);
+ QCOMPARE(view.model()->data(view.model()->index(0,1)).toString(), QString::fromLatin1("h"));
+ QCOMPARE(view.model()->data(view.model()->index(1,1)).toString(), QString::fromLatin1("g"));
+}
+
+class TreeView : public QTreeView
+{
+ Q_OBJECT
+public slots:
+ void handleSelectionChanged()
+ {
+ //let's select the last item
+ QModelIndex idx = model()->index(0, 0);
+ selectionModel()->select(QItemSelection(idx, idx), QItemSelectionModel::Select);
+ disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(handleSelectionChanged()));
+ }
+};
+
+void tst_QTreeView::task248022_changeSelection()
+{
+ //we check that changing the selection between the mouse press and the mouse release
+ //works correctly
+ TreeView view;
+ QStringList list = QStringList() << "1" << "2";
+ QStringListModel model(list);
+ view.setSelectionMode(QAbstractItemView::ExtendedSelection);
+ view.setModel(&model);
+ view.connect(view.selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(handleSelectionChanged()));
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.visualRect(model.index(1)).center());
+ QCOMPARE(view.selectionModel()->selectedIndexes().count(), list.count());
+}
+
+void tst_QTreeView::task245654_changeModelAndExpandAll()
+{
+ QTreeView view;
+ QStandardItemModel *model = new QStandardItemModel;
+ QStandardItem *top = new QStandardItem("top");
+ QStandardItem *sub = new QStandardItem("sub");
+ top->appendRow(sub);
+ model->appendRow(top);
+ view.setModel(model);
+ view.expandAll();
+ QApplication::processEvents();
+ QVERIFY(view.isExpanded(top->index()));
+
+ //now let's try to delete the model
+ //then repopulate and expand again
+ delete model;
+ model = new QStandardItemModel;
+ top = new QStandardItem("top");
+ sub = new QStandardItem("sub");
+ top->appendRow(sub);
+ model->appendRow(top);
+ view.setModel(model);
+ view.expandAll();
+ QApplication::processEvents();
+ QVERIFY(view.isExpanded(top->index()));
+
+}
+
+void tst_QTreeView::doubleClickedWithSpans()
+{
+ QTreeView view;
+ QStandardItemModel model(1, 2);
+ view.setModel(&model);
+ view.setFirstColumnSpanned(0, QModelIndex(), true);
+ view.show();
+ QApplication::setActiveWindow(&view);
+ QTest::qWaitForWindowShown(&view);
+ QTRY_VERIFY(view.isActiveWindow());
+
+ QPoint p(10, 10);
+ QCOMPARE(view.indexAt(p), model.index(0, 0));
+ QSignalSpy spy(&view, SIGNAL(doubleClicked(QModelIndex)));
+ QTest::mousePress(view.viewport(), Qt::LeftButton, 0, p);
+ QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, p);
+ QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, p);
+ QCOMPARE(spy.count(), 1);
+
+ //let's click on the 2nd column
+ p.setX(p.x() + view.header()->sectionSize(0));
+ QCOMPARE(view.indexAt(p), model.index(0, 0));
+
+ //end the previous edition
+ QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, p);
+ QTest::qWait(150);
+ QTest::mousePress(view.viewport(), Qt::LeftButton, 0, p);
+ QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, p);
+ QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, p);
+ QTRY_COMPARE(spy.count(), 2);
+}
+
+void tst_QTreeView::taskQTBUG_6450_selectAllWith1stColumnHidden()
+{
+ QTreeWidget tree;
+ tree.setSelectionMode(QAbstractItemView::MultiSelection);
+ tree.setColumnCount(2);
+ QList<QTreeWidgetItem *> items;
+ const int nrRows = 10;
+ for (int i = 0; i < nrRows; ++i) {
+ items.append(new QTreeWidgetItem((QTreeWidget*)0, QStringList(QString("item: %1").arg(i))));
+ items.last()->setText(1, QString("is an item"));
+ }
+ tree.insertTopLevelItems(0, items);
+
+ tree.hideColumn(0);
+ tree.selectAll();
+
+ QVERIFY(tree.selectionModel()->hasSelection());
+ for (int i = 0; i < nrRows; ++i)
+ QVERIFY(tree.selectionModel()->isRowSelected(i, QModelIndex()));
+}
+
+class TreeViewQTBUG_9216 : public QTreeView
+{
+ Q_OBJECT
+public:
+ void paintEvent(QPaintEvent *event)
+ {
+ if (doCompare)
+ QCOMPARE(event->rect(), viewport()->rect());
+ QTreeView::paintEvent(event);
+ painted++;
+ }
+ int painted;
+ bool doCompare;
+};
+
+void tst_QTreeView::taskQTBUG_9216_setSizeAndUniformRowHeightsWrongRepaint()
+{
+ QStandardItemModel model(10, 10, this);
+ for (int row = 0; row < 10; row++)
+ for (int col = 0; col < 10; col++)
+ model.setItem(row, col, new QStandardItem(QString("row %0, col %1").arg(row).arg(col)));
+ TreeViewQTBUG_9216 view;
+ view.setUniformRowHeights(true);
+ view.setModel(&model);
+ view.painted = 0;
+ view.doCompare = false;
+ view.show();
+ QTest::qWaitForWindowShown(&view);
+ QTRY_VERIFY(view.painted > 0);
+
+ QTest::qWait(100); // This one is needed to make the test fail before the patch.
+ view.painted = 0;
+ view.doCompare = true;
+ model.setData(model.index(0, 0), QVariant(QSize(50, 50)), Qt::SizeHintRole);
+ QTest::qWait(100);
+ QTRY_VERIFY(view.painted > 0);
+}
+
+void tst_QTreeView::keyboardNavigationWithDisabled()
+{
+ QWidget topLevel;
+ QTreeView view(&topLevel);
+ QStandardItemModel model(90, 0);
+ for (int i = 0; i < 90; i ++) {
+ model.setItem(i, new QStandardItem(QString::number(i)));
+ model.item(i)->setEnabled(i%6 == 0);
+ }
+ view.setModel(&model);
+
+ view.resize(200, view.visualRect(model.index(0,0)).height()*10);
+ topLevel.show();
+ QApplication::setActiveWindow(&topLevel);
+ QTest::qWaitForWindowShown(&topLevel);
+ QTRY_VERIFY(topLevel.isActiveWindow());
+
+ view.setCurrentIndex(model.index(1, 0));
+ QTest::keyClick(view.viewport(), Qt::Key_Up);
+ QCOMPARE(view.currentIndex(), model.index(0, 0));
+ QTest::keyClick(view.viewport(), Qt::Key_Down);
+ QCOMPARE(view.currentIndex(), model.index(6, 0));
+ QTest::keyClick(view.viewport(), Qt::Key_PageDown);
+ QCOMPARE(view.currentIndex(), model.index(18, 0));
+ QTest::keyClick(view.viewport(), Qt::Key_Down);
+ QCOMPARE(view.currentIndex(), model.index(24, 0));
+ QTest::keyClick(view.viewport(), Qt::Key_PageUp);
+ QCOMPARE(view.currentIndex(), model.index(12, 0));
+ QTest::keyClick(view.viewport(), Qt::Key_Up);
+ QCOMPARE(view.currentIndex(), model.index(6, 0));
+}
+
+class Model_11466 : public QAbstractItemModel
+{
+ Q_OBJECT
+public:
+ Model_11466(QObject * /* parent */) :
+ m_block(false)
+ {
+ // set up the model to have two top level items and a few others
+ m_selectionModel = new QItemSelectionModel(this, this); // owned by this
+
+ connect(m_selectionModel, SIGNAL(currentChanged(const QModelIndex &,const QModelIndex &)),
+ this, SLOT(slotCurrentChanged(const QModelIndex &,const QModelIndex &)));
+ };
+
+ int rowCount(const QModelIndex &parent) const
+ {
+ if (parent.isValid())
+ return (parent.internalId() == 0) ? 4 : 0;
+ return 2; // two top level items
+ }
+
+ int columnCount(const QModelIndex & /* parent */) const
+ {
+ return 2;
+ }
+
+ QVariant data(const QModelIndex &index, int role) const
+ {
+ if (role == Qt::DisplayRole && index.isValid()) {
+ qint64 parentRowPlusOne = index.internalId();
+ QString str;
+ QTextStream stream(&str);
+ if (parentRowPlusOne > 0)
+ stream << parentRowPlusOne << " -> " << index.row() << " : " << index.column();
+ else
+ stream << index.row() << " : " << index.column();
+ return QVariant(str);
+ }
+ return QVariant();
+ }
+
+ QModelIndex parent(const QModelIndex &index) const
+ {
+ if (index.isValid()) {
+ qint64 parentRowPlusOne = index.internalId();
+ if (parentRowPlusOne > 0) {
+ int row = static_cast<int>(parentRowPlusOne - 1);
+ return createIndex(row, 0, (quint32)0);
+ }
+ }
+ return QModelIndex();
+ }
+
+ void bindView(QTreeView *view)
+ {
+ // sets the view to this model with a shared selection model
+ QItemSelectionModel *oldModel = view->selectionModel();
+ if (oldModel != m_selectionModel)
+ delete oldModel;
+ view->setModel(this); // this creates a new selection model for the view, but we dont want it either ...
+ oldModel = view->selectionModel();
+ view->setSelectionModel(m_selectionModel);
+ delete oldModel;
+ }
+
+ QModelIndex index(int row, int column, const QModelIndex &parent) const
+ {
+ return createIndex(row, column, parent.isValid() ? (quint32)(parent.row() + 1) : (quint32)0);
+ }
+
+public slots:
+ void slotCurrentChanged(const QModelIndex &current,const QModelIndex &)
+ {
+ if (m_block)
+ return;
+
+ if (current.isValid()) {
+ int selectedRow = current.row();
+ quint32 parentRowPlusOne = static_cast<quint32>(current.internalId());
+
+ for (int i = 0; i < 2; ++i) {
+ // announce the removal of all non top level items
+ beginRemoveRows(createIndex(i, 0, 0), 0, 3);
+ // nothing to actually do for the removal
+ endRemoveRows();
+
+ // put them back in again
+ beginInsertRows(createIndex(i, 0, 0), 0, 3);
+ // nothing to actually do for the insertion
+ endInsertRows();
+ }
+ // reselect the current item ...
+ QModelIndex selectedIndex = createIndex(selectedRow, 0, parentRowPlusOne);
+
+ m_block = true; // recursion block
+ m_selectionModel->select(selectedIndex, QItemSelectionModel::ClearAndSelect|QItemSelectionModel::Current|QItemSelectionModel::Rows);
+ m_selectionModel->setCurrentIndex(selectedIndex, QItemSelectionModel::NoUpdate);
+ m_block = false;
+ } else {
+ m_selectionModel->clear();
+ }
+ }
+
+private:
+ bool m_block;
+ QItemSelectionModel *m_selectionModel;
+};
+
+void tst_QTreeView::taskQTBUG_11466_keyboardNavigationRegression()
+{
+ QTreeView treeView;
+ treeView.setSelectionBehavior(QAbstractItemView::SelectRows);
+ treeView.setSelectionMode(QAbstractItemView::SingleSelection);
+ Model_11466 model(&treeView);
+ model.bindView(&treeView);
+ treeView.expandAll();
+ treeView.show();
+ QTest::qWaitForWindowShown(&treeView);
+
+ QTest::keyPress(treeView.viewport(), Qt::Key_Down);
+ QTest::qWait(10);
+ QTRY_COMPARE(treeView.currentIndex(), treeView.selectionModel()->selection().indexes().first());
+}
+
+void tst_QTreeView::taskQTBUG_13567_removeLastItemRegression()
+{
+ QtTestModel model(200, 1);
+
+ QTreeView view;
+ view.setSelectionMode(QAbstractItemView::ExtendedSelection);
+ view.setModel(&model);
+ view.show();
+ QTest::qWaitForWindowShown(&view);
+
+ view.scrollToBottom();
+ QTest::qWait(10);
+ CHECK_VISIBLE(199, 0);
+
+ view.setCurrentIndex(model.index(199, 0));
+ model.removeLastRow();
+ QTest::qWait(10);
+ QCOMPARE(view.currentIndex(), model.index(198, 0));
+ CHECK_VISIBLE(198, 0);
+}
+
+QTEST_MAIN(tst_QTreeView)
+#include "tst_qtreeview.moc"