qt5base-lts/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp
Jason McDonald 5635823e17 Remove "All rights reserved" line from license headers.
As in the past, to avoid rewriting various autotests that contain
line-number information, an extra blank line has been inserted at the
end of the license text to ensure that this commit does not change the
total number of lines in the license header.

Change-Id: I311e001373776812699d6efc045b5f742890c689
Reviewed-by: Rohan McGovern <rohan.mcgovern@nokia.com>
2012-01-30 03:54:59 +01:00

3961 lines
135 KiB
C++

/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** 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>
#include <private/qabstractitemview_p.h>
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); }
QAbstractItemViewPrivate* aiv_priv() { return static_cast<QAbstractItemViewPrivate*>(d_ptr.data()); }
};
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
#ifdef Q_OS_MAC
QEXPECT_FAIL("EditKeyPressed 4", "QTBUG-23696", Continue);
#endif
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
{
static QString posToString(QStyleOptionViewItemV4::ViewItemPosition pos) {
static const char* s_pos[] = { "Invalid", "Beginning", "Middle", "End", "OnlyOne" };
return s_pos[pos];
}
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(posToString(opt.viewItemPosition), posToString(QStyleOptionViewItemV4::Beginning));
if (opt.text.contains("Middle"))
QCOMPARE(posToString(opt.viewItemPosition), posToString(QStyleOptionViewItemV4::Middle));
if (opt.text.contains("End"))
QCOMPARE(posToString(opt.viewItemPosition), posToString(QStyleOptionViewItemV4::End));
if (opt.text.contains("OnlyOne"))
QCOMPARE(posToString(opt.viewItemPosition), posToString(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;
};
PublicView view;
QStandardItemModel model;
view.setModel(&model);
MyDelegate delegate;
view.setItemDelegate(&delegate);
model.appendRow(QList<QStandardItem*>()
<< new QStandardItem("Beginning") << new QStandardItem("Hidden") << new QStandardItem("Middle") << new QStandardItem("Middle") << new QStandardItem("End") );
QStandardItem *par1 = new QStandardItem("Beginning HasChildren");
model.appendRow(QList<QStandardItem*>()
<< par1 << new QStandardItem("Hidden") << new QStandardItem("Middle HasChildren") << new QStandardItem("Middle HasChildren") << new QStandardItem("End HasChildren") );
model.appendRow(QList<QStandardItem*>()
<< new QStandardItem("OnlyOne") << new QStandardItem("Hidden") << new QStandardItem("Assert") << new QStandardItem("Assert") << new QStandardItem("Assert") );
QStandardItem *checkable = new QStandardItem("Checkable");
checkable->setCheckable(true);
QStandardItem *checked = new QStandardItem("Checkable Checked");
checked->setCheckable(true);
checked->setCheckState(Qt::Checked);
model.appendRow(QList<QStandardItem*>()
<< new QStandardItem("Beginning") << new QStandardItem("Hidden") << checkable << checked << new QStandardItem("End") );
model.appendRow(QList<QStandardItem*>()
<< new QStandardItem("Beginning Last") << new QStandardItem("Hidden") << new QStandardItem("Middle Last") << new QStandardItem("Middle Last") << new QStandardItem("End Last") );
par1->appendRow(QList<QStandardItem*>()
<< new QStandardItem("Beginning") << new QStandardItem("Hidden") << new QStandardItem("Middle") << new QStandardItem("Middle") << new QStandardItem("End") );
QStandardItem *par2 = new QStandardItem("Beginning HasChildren");
par1->appendRow(QList<QStandardItem*>()
<< par2 << new QStandardItem("Hidden") << new QStandardItem("Middle HasChildren") << new QStandardItem("Middle HasChildren") << new QStandardItem("End HasChildren") );
par2->appendRow(QList<QStandardItem*>()
<< new QStandardItem("Beginning Last") << new QStandardItem("Hidden") << 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("Hidden") << new QStandardItem("Middle Last") << new QStandardItem("Middle Last") << new QStandardItem("End Last") );
par3->appendRow(QList<QStandardItem*>()
<< new QStandardItem("Assert") << new QStandardItem("Hidden") << new QStandardItem("Assert") << new QStandardItem("Assert") << new QStandardItem("Asser") );
view.setRowHidden(0, par3->index(), true);
par1->appendRow(QList<QStandardItem*>()
<< new QStandardItem("Assert") << new QStandardItem("Hidden") << new QStandardItem("Assert") << new QStandardItem("Assert") << new QStandardItem("Asser") );
view.setRowHidden(3, par1->index(), true);
view.setColumnHidden(1, true);
const int visibleColumns = 4;
const int modelColumns = 5;
view.header()->swapSections(2, 3);
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 that the rendering of drag pixmap sets the correct options too (QTBUG-15834)
#ifdef QT_BUILD_INTERNAL
delegate.count = 0;
QItemSelection sel(model.index(0,0), model.index(0,modelColumns-1));
QRect rect;
view.aiv_priv()->renderToPixmap(sel.indexes(), &rect);
QTRY_VERIFY(delegate.count == visibleColumns);
#endif
//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 don't 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"