qt5base-lts/tests/auto/qstandarditemmodel/tst_qstandarditemmodel.cpp
Qt by Nokia 38be0d1383 Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you
want to look at revision history older than this, please refer to the
Qt Git wiki for how to use Git history grafting. At the time of
writing, this wiki is located here:

http://qt.gitorious.org/qt/pages/GitIntroductionWithQt

If you have already performed the grafting and you don't see any
history beyond this commit, try running "git log" with the "--follow"
argument.

Branched from the monolithic repo, Qt master branch, at commit
896db169ea224deb96c59ce8af800d019de63f12
2011-04-27 12:05:43 +02:00

1667 lines
60 KiB
C++

/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, 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.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qstandarditemmodel.h>
#include <QTreeView>
#include <private/qtreeview_p.h>
//TESTED_CLASS=
//TESTED_FILES=
class tst_QStandardItemModel : public QObject
{
Q_OBJECT
public:
tst_QStandardItemModel();
virtual ~tst_QStandardItemModel();
enum ModelChanged {
RowsAboutToBeInserted,
RowsInserted,
RowsAboutToBeRemoved,
RowsRemoved,
ColumnsAboutToBeInserted,
ColumnsInserted,
ColumnsAboutToBeRemoved,
ColumnsRemoved
};
public slots:
void init();
void cleanup();
protected slots:
void checkAboutToBeRemoved();
void checkRemoved();
void updateRowAboutToBeRemoved();
void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last)
{ modelChanged(RowsAboutToBeInserted, parent, first, last); }
void rowsInserted(const QModelIndex &parent, int first, int last)
{ modelChanged(RowsInserted, parent, first, last); }
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
{ modelChanged(RowsAboutToBeRemoved, parent, first, last); }
void rowsRemoved(const QModelIndex &parent, int first, int last)
{ modelChanged(RowsRemoved, parent, first, last); }
void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last)
{ modelChanged(ColumnsAboutToBeInserted, parent, first, last); }
void columnsInserted(const QModelIndex &parent, int first, int last)
{ modelChanged(ColumnsInserted, parent, first, last); }
void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
{ modelChanged(ColumnsAboutToBeRemoved, parent, first, last); }
void columnsRemoved(const QModelIndex &parent, int first, int last)
{ modelChanged(ColumnsRemoved, parent, first, last); }
void modelChanged(ModelChanged change, const QModelIndex &parent, int first, int last);
private slots:
void insertRow_data();
void insertRow();
void insertRows();
void insertRowsItems();
void insertRowInHierarcy();
void insertColumn_data();
void insertColumn();
void insertColumns();
void removeRows();
void removeColumns();
void setHeaderData();
void persistentIndexes();
void removingPersistentIndexes();
void updatingPersistentIndexes();
void checkChildren();
void data();
void clear();
void sort_data();
void sort();
void sortRole_data();
void sortRole();
void findItems();
void getSetHeaderItem();
void indexFromItem();
void itemFromIndex();
void getSetItemPrototype();
void getSetItemData();
void setHeaderLabels_data();
void setHeaderLabels();
void itemDataChanged();
void takeHeaderItem();
void useCase1();
void useCase2();
void useCase3();
void rootItemFlags();
void treeDragAndDrop();
void removeRowsAndColumns();
private:
QAbstractItemModel *m_model;
QPersistentModelIndex persistent;
QVector<QModelIndex> rcParent;
QVector<int> rcFirst;
QVector<int> rcLast;
//return true if models have the same structure, and all child have the same text
bool compareModels(QStandardItemModel *model1, QStandardItemModel *model2);
//return true if models have the same structure, and all child have the same text
bool compareItems(QStandardItem *item1, QStandardItem *item2);
};
static const int defaultSize = 3;
Q_DECLARE_METATYPE(QModelIndex)
Q_DECLARE_METATYPE(QStandardItem*)
Q_DECLARE_METATYPE(Qt::Orientation)
Q_DECLARE_METATYPE(QVariantList)
tst_QStandardItemModel::tst_QStandardItemModel() : m_model(0), rcParent(8), rcFirst(8,0), rcLast(8,0)
{
}
tst_QStandardItemModel::~tst_QStandardItemModel()
{
}
/*
This test usually uses a model with a 3x3 table
---------------------------
| 0,0 | 0,1 | 0,2 |
---------------------------
| 1,0 | 1,1 | 1,2 |
---------------------------
| 2,0 | 2,1 | 2,2 |
---------------------------
*/
void tst_QStandardItemModel::init()
{
qRegisterMetaType<QModelIndex>("QModelIndex");
qRegisterMetaType<QStandardItem*>("QStandardItem*");
qRegisterMetaType<Qt::Orientation>("Qt::Orientation");
m_model = new QStandardItemModel(defaultSize, defaultSize);
connect(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
this, SLOT(rowsAboutToBeInserted(QModelIndex, int, int)));
connect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)),
this, SLOT(rowsInserted(QModelIndex, int, int)));
connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
this, SLOT(rowsAboutToBeRemoved(QModelIndex, int, int)));
connect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
this, SLOT(rowsRemoved(QModelIndex, int, int)));
connect(m_model, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)),
this, SLOT(columnsAboutToBeInserted(QModelIndex, int, int)));
connect(m_model, SIGNAL(columnsInserted(QModelIndex, int, int)),
this, SLOT(columnsInserted(QModelIndex, int, int)));
connect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)),
this, SLOT(columnsAboutToBeRemoved(QModelIndex, int, int)));
connect(m_model, SIGNAL(columnsRemoved(QModelIndex, int, int)),
this, SLOT(columnsRemoved(QModelIndex, int, int)));
rcFirst.fill(-1);
rcLast.fill(-1);
}
void tst_QStandardItemModel::cleanup()
{
disconnect(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
this, SLOT(rowsAboutToBeInserted(QModelIndex, int, int)));
disconnect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)),
this, SLOT(rowsInserted(QModelIndex, int, int)));
disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
this, SLOT(rowsAboutToBeRemoved(QModelIndex, int, int)));
disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
this, SLOT(rowsRemoved(QModelIndex, int, int)));
disconnect(m_model, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)),
this, SLOT(columnsAboutToBeInserted(QModelIndex, int, int)));
disconnect(m_model, SIGNAL(columnsInserted(QModelIndex, int, int)),
this, SLOT(columnsInserted(QModelIndex, int, int)));
disconnect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)),
this, SLOT(columnsAboutToBeRemoved(QModelIndex, int, int)));
disconnect(m_model, SIGNAL(columnsRemoved(QModelIndex, int, int)),
this, SLOT(columnsRemoved(QModelIndex, int, int)));
delete m_model;
m_model = 0;
}
void tst_QStandardItemModel::insertRow_data()
{
QTest::addColumn<int>("insertRow");
QTest::addColumn<int>("expectedRow");
QTest::newRow("Insert less then 0") << -1 << 0;
QTest::newRow("Insert at 0") << 0 << 0;
QTest::newRow("Insert beyond count") << defaultSize+1 << defaultSize;
QTest::newRow("Insert at count") << defaultSize << defaultSize;
QTest::newRow("Insert in the middle") << 1 << 1;
}
void tst_QStandardItemModel::insertRow()
{
QFETCH(int, insertRow);
QFETCH(int, expectedRow);
QIcon icon;
// default all initial items to DisplayRole: "initalitem"
for (int r=0; r < m_model->rowCount(); ++r) {
for (int c=0; c < m_model->columnCount(); ++c) {
m_model->setData(m_model->index(r,c), "initialitem", Qt::DisplayRole);
}
}
// check that inserts changes rowCount
QCOMPARE(m_model->rowCount(), defaultSize);
m_model->insertRow(insertRow);
if (insertRow >= 0 && insertRow <= defaultSize) {
QCOMPARE(m_model->rowCount(), defaultSize + 1);
// check that signals were emitted with correct info
QCOMPARE(rcFirst[RowsAboutToBeInserted], expectedRow);
QCOMPARE(rcLast[RowsAboutToBeInserted], expectedRow);
QCOMPARE(rcFirst[RowsInserted], expectedRow);
QCOMPARE(rcLast[RowsInserted], expectedRow);
//check that the inserted item has different DisplayRole than initial items
QVERIFY(m_model->data(m_model->index(expectedRow, 0), Qt::DisplayRole).toString() != "initialitem");
} else {
// We inserted something outside the bounds, do nothing
QCOMPARE(m_model->rowCount(), defaultSize);
QCOMPARE(rcFirst[RowsAboutToBeInserted], -1);
QCOMPARE(rcLast[RowsAboutToBeInserted], -1);
QCOMPARE(rcFirst[RowsInserted], -1);
QCOMPARE(rcLast[RowsInserted], -1);
}
}
void tst_QStandardItemModel::insertRows()
{
int rowCount = m_model->rowCount();
QCOMPARE(rowCount, defaultSize);
// insert custom header label
QString headerLabel = "custom";
m_model->setHeaderData(0, Qt::Vertical, headerLabel);
// insert one row
m_model->insertRows(0, 1);
QCOMPARE(m_model->rowCount(), rowCount + 1);
rowCount = m_model->rowCount();
// check header data has moved
QCOMPARE(m_model->headerData(1, Qt::Vertical).toString(), headerLabel);
// insert two rows
m_model->insertRows(0, 2);
QCOMPARE(m_model->rowCount(), rowCount + 2);
// check header data has moved
QCOMPARE(m_model->headerData(3, Qt::Vertical).toString(), headerLabel);
}
void tst_QStandardItemModel::insertRowsItems()
{
int rowCount = m_model->rowCount();
QList<QStandardItem *> items;
QStandardItemModel *m = qobject_cast<QStandardItemModel*>(m_model);
QStandardItem *hiddenRoot = m->invisibleRootItem();
for (int i = 0; i < 3; ++i)
items.append(new QStandardItem(QString("%1").arg(i + 10)));
hiddenRoot->appendRows(items);
QCOMPARE(m_model->rowCount(), rowCount + 3);
QCOMPARE(m_model->index(rowCount + 0, 0).data().toInt(), 10);
QCOMPARE(m_model->index(rowCount + 1, 0).data().toInt(), 11);
QCOMPARE(m_model->index(rowCount + 2, 0).data().toInt(), 12);
for (int i = rowCount; i < rowCount + 3; ++i) {
QVERIFY(m->item(i));
QCOMPARE(static_cast<QAbstractItemModel *>(m->item(i)->model()), m_model);
}
}
void tst_QStandardItemModel::insertRowInHierarcy()
{
QVERIFY(m_model->insertRows(0, 1, QModelIndex()));
QVERIFY(m_model->insertColumns(0, 1, QModelIndex()));
QVERIFY(m_model->hasIndex(0, 0, QModelIndex()));
QModelIndex parent = m_model->index(0, 0, QModelIndex());
QVERIFY(parent.isValid());
QVERIFY(m_model->insertRows(0, 1, parent));
QVERIFY(m_model->insertColumns(0, 1, parent));
QVERIFY(m_model->hasIndex(0, 0, parent));
QModelIndex child = m_model->index(0, 0, parent);
QVERIFY(child.isValid());
}
void tst_QStandardItemModel::insertColumn_data()
{
QTest::addColumn<int>("insertColumn");
QTest::addColumn<int>("expectedColumn");
QTest::newRow("Insert less then 0") << -1 << 0;
QTest::newRow("Insert at 0") << 0 << 0;
QTest::newRow("Insert beyond count") << defaultSize+1 << defaultSize;
QTest::newRow("Insert at count") << defaultSize << defaultSize;
QTest::newRow("Insert in the middle") << 1 << 1;
}
void tst_QStandardItemModel::insertColumn()
{
QFETCH(int, insertColumn);
QFETCH(int, expectedColumn);
// default all initial items to DisplayRole: "initalitem"
for (int r=0; r < m_model->rowCount(); ++r) {
for (int c=0; c < m_model->columnCount(); ++c) {
m_model->setData(m_model->index(r,c), "initialitem", Qt::DisplayRole);
}
}
// check that inserts changes columnCount
QCOMPARE(m_model->columnCount(), defaultSize);
m_model->insertColumn(insertColumn);
if (insertColumn >= 0 && insertColumn <= defaultSize) {
QCOMPARE(m_model->columnCount(), defaultSize + 1);
// check that signals were emitted with correct info
QCOMPARE(rcFirst[ColumnsAboutToBeInserted], expectedColumn);
QCOMPARE(rcLast[ColumnsAboutToBeInserted], expectedColumn);
QCOMPARE(rcFirst[ColumnsInserted], expectedColumn);
QCOMPARE(rcLast[ColumnsInserted], expectedColumn);
//check that the inserted item has different DisplayRole than initial items
QVERIFY(m_model->data(m_model->index(0, expectedColumn), Qt::DisplayRole).toString() != "initialitem");
} else {
// We inserted something outside the bounds, do nothing
QCOMPARE(m_model->columnCount(), defaultSize);
QCOMPARE(rcFirst[ColumnsAboutToBeInserted], -1);
QCOMPARE(rcLast[ColumnsAboutToBeInserted], -1);
QCOMPARE(rcFirst[ColumnsInserted], -1);
QCOMPARE(rcLast[ColumnsInserted], -1);
}
}
void tst_QStandardItemModel::insertColumns()
{
int columnCount = m_model->columnCount();
QCOMPARE(columnCount, defaultSize);
// insert custom header label
QString headerLabel = "custom";
m_model->setHeaderData(0, Qt::Horizontal, headerLabel);
// insert one column
m_model->insertColumns(0, 1);
QCOMPARE(m_model->columnCount(), columnCount + 1);
columnCount = m_model->columnCount();
// check header data has moved
QCOMPARE(m_model->headerData(1, Qt::Horizontal).toString(), headerLabel);
// insert two columns
m_model->insertColumns(0, 2);
QCOMPARE(m_model->columnCount(), columnCount + 2);
// check header data has moved
QCOMPARE(m_model->headerData(3, Qt::Horizontal).toString(), headerLabel);
}
void tst_QStandardItemModel::removeRows()
{
int rowCount = m_model->rowCount();
QCOMPARE(rowCount, defaultSize);
// insert custom header label
QString headerLabel = "custom";
m_model->setHeaderData(rowCount - 1, Qt::Vertical, headerLabel);
// remove one row
m_model->removeRows(0, 1);
QCOMPARE(m_model->rowCount(), rowCount - 1);
rowCount = m_model->rowCount();
// check header data has moved
QCOMPARE(m_model->headerData(rowCount - 1, Qt::Vertical).toString(), headerLabel);
// remove two rows
m_model->removeRows(0, 2);
QCOMPARE(m_model->rowCount(), rowCount - 2);
}
void tst_QStandardItemModel::removeColumns()
{
int columnCount = m_model->columnCount();
QCOMPARE(columnCount, defaultSize);
// insert custom header label
QString headerLabel = "custom";
m_model->setHeaderData(columnCount - 1, Qt::Horizontal, headerLabel);
// remove one column
m_model->removeColumns(0, 1);
QCOMPARE(m_model->columnCount(), columnCount - 1);
columnCount = m_model->columnCount();
// check header data has moved
QCOMPARE(m_model->headerData(columnCount - 1, Qt::Horizontal).toString(), headerLabel);
// remove two columns
m_model->removeColumns(0, 2);
QCOMPARE(m_model->columnCount(), columnCount - 2);
}
void tst_QStandardItemModel::setHeaderData()
{
for (int x = 0; x < 2; ++x) {
bool vertical = (x == 0);
int count = vertical ? m_model->rowCount() : m_model->columnCount();
QCOMPARE(count, defaultSize);
Qt::Orientation orient = vertical ? Qt::Vertical : Qt::Horizontal;
// check default values are ok
for (int i = 0; i < count; ++i)
QCOMPARE(m_model->headerData(i, orient).toString(), QString::number(i + 1));
QSignalSpy headerDataChangedSpy(
m_model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)));
QSignalSpy dataChangedSpy(
m_model, SIGNAL(dataChanged(QModelIndex, QModelIndex)));
// insert custom values and check
for (int i = 0; i < count; ++i) {
QString customString = QString("custom") + QString::number(i);
QCOMPARE(m_model->setHeaderData(i, orient, customString), true);
QCOMPARE(headerDataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.count(), 0);
QVariantList args = headerDataChangedSpy.takeFirst();
QCOMPARE(qvariant_cast<Qt::Orientation>(args.at(0)), orient);
QCOMPARE(args.at(1).toInt(), i);
QCOMPARE(args.at(2).toInt(), i);
QCOMPARE(m_model->headerData(i, orient).toString(), customString);
QCOMPARE(m_model->setHeaderData(i, orient, customString), true);
QCOMPARE(headerDataChangedSpy.count(), 0);
QCOMPARE(dataChangedSpy.count(), 0);
}
//check read from invalid sections
QVERIFY(!m_model->headerData(count, orient).isValid());
QVERIFY(!m_model->headerData(-1, orient).isValid());
//check write to invalid section
QCOMPARE(m_model->setHeaderData(count, orient, "foo"), false);
QCOMPARE(m_model->setHeaderData(-1, orient, "foo"), false);
QVERIFY(!m_model->headerData(count, orient).isValid());
QVERIFY(!m_model->headerData(-1, orient).isValid());
}
}
void tst_QStandardItemModel::persistentIndexes()
{
QCOMPARE(m_model->rowCount(), defaultSize);
QCOMPARE(m_model->columnCount(), defaultSize);
// create a persisten index at 0,0
QPersistentModelIndex persistentIndex(m_model->index(0, 0));
// verify it is ok and at the correct spot
QVERIFY(persistentIndex.isValid());
QCOMPARE(persistentIndex.row(), 0);
QCOMPARE(persistentIndex.column(), 0);
// insert row and check that the persisten index has moved
QVERIFY(m_model->insertRow(0));
QVERIFY(persistentIndex.isValid());
QCOMPARE(persistentIndex.row(), 1);
QCOMPARE(persistentIndex.column(), 0);
// insert row after the persisten index and see that it stays the same
QVERIFY(m_model->insertRow(m_model->rowCount()));
QVERIFY(persistentIndex.isValid());
QCOMPARE(persistentIndex.row(), 1);
QCOMPARE(persistentIndex.column(), 0);
// insert column and check that the persisten index has moved
QVERIFY(m_model->insertColumn(0));
QVERIFY(persistentIndex.isValid());
QCOMPARE(persistentIndex.row(), 1);
QCOMPARE(persistentIndex.column(), 1);
// insert column after the persisten index and see that it stays the same
QVERIFY(m_model->insertColumn(m_model->columnCount()));
QVERIFY(persistentIndex.isValid());
QCOMPARE(persistentIndex.row(), 1);
QCOMPARE(persistentIndex.column(), 1);
// removes a row beyond the persistent index and see it stays the same
QVERIFY(m_model->removeRow(m_model->rowCount() - 1));
QVERIFY(persistentIndex.isValid());
QCOMPARE(persistentIndex.row(), 1);
QCOMPARE(persistentIndex.column(), 1);
// removes a column beyond the persistent index and see it stays the same
QVERIFY(m_model->removeColumn(m_model->columnCount() - 1));
QVERIFY(persistentIndex.isValid());
QCOMPARE(persistentIndex.row(), 1);
QCOMPARE(persistentIndex.column(), 1);
// removes a row before the persistent index and see it moves the same
QVERIFY(m_model->removeRow(0));
QVERIFY(persistentIndex.isValid());
QCOMPARE(persistentIndex.row(), 0);
QCOMPARE(persistentIndex.column(), 1);
// removes a column before the persistent index and see it moves the same
QVERIFY(m_model->removeColumn(0));
QVERIFY(persistentIndex.isValid());
QCOMPARE(persistentIndex.row(), 0);
QCOMPARE(persistentIndex.column(), 0);
// remove the row where the persistent index is, and see that it becomes invalid
QVERIFY(m_model->removeRow(0));
QVERIFY(!persistentIndex.isValid());
// remove the row where the persistent index is, and see that it becomes invalid
persistentIndex = m_model->index(0, 0);
QVERIFY(persistentIndex.isValid());
QVERIFY(m_model->removeColumn(0));
QVERIFY(!persistentIndex.isValid());
}
void tst_QStandardItemModel::checkAboutToBeRemoved()
{
QVERIFY(persistent.isValid());
}
void tst_QStandardItemModel::checkRemoved()
{
QVERIFY(!persistent.isValid());
}
void tst_QStandardItemModel::removingPersistentIndexes()
{
// add 10 rows and columns to model to make it big enough
QVERIFY(m_model->insertRows(0, 10));
QVERIFY(m_model->insertColumns(0, 10));
QObject::connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(checkAboutToBeRemoved()));
QObject::connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(checkRemoved()));
QObject::connect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(checkAboutToBeRemoved()));
QObject::connect(m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(checkRemoved()));
// test removeRow
// add child table 3x3 to parent index(0, 0)
QVERIFY(m_model->insertRows(0, 3, m_model->index(0, 0)));
QVERIFY(m_model->insertColumns(0, 3, m_model->index(0, 0)));
// set child to persistent and delete parent row
persistent = m_model->index(0, 0, m_model->index(0, 0));
QVERIFY(persistent.isValid());
QVERIFY(m_model->removeRow(0));
// set persistent to index(0, 0) and remove that row
persistent = m_model->index(0, 0);
QVERIFY(persistent.isValid());
QVERIFY(m_model->removeRow(0));
// test removeColumn
// add child table 3x3 to parent index (0, 0)
QVERIFY(m_model->insertRows(0, 3, m_model->index(0, 0)));
QVERIFY(m_model->insertColumns(0, 3, m_model->index(0, 0)));
// set child to persistent and delete parent column
persistent = m_model->index(0, 0, m_model->index(0, 0));
QVERIFY(persistent.isValid());
QVERIFY(m_model->removeColumn(0));
// set persistent to index(0, 0) and remove that column
persistent = m_model->index(0, 0);
QVERIFY(persistent.isValid());
QVERIFY(m_model->removeColumn(0));
QObject::disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(checkAboutToBeRemoved()));
QObject::disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(checkRemoved()));
QObject::disconnect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(checkAboutToBeRemoved()));
QObject::disconnect(m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(checkRemoved()));
}
void tst_QStandardItemModel::updateRowAboutToBeRemoved()
{
QModelIndex idx = m_model->index(0, 0);
QVERIFY(idx.isValid());
persistent = idx;
}
void tst_QStandardItemModel::updatingPersistentIndexes()
{
QObject::connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(updateRowAboutToBeRemoved()));
persistent = m_model->index(1, 0);
QVERIFY(persistent.isValid());
QVERIFY(m_model->removeRow(1));
QVERIFY(persistent.isValid());
QPersistentModelIndex tmp = m_model->index(0, 0);
QCOMPARE(persistent, tmp);
QObject::disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(updateRowAboutToBeRemoved()));
}
void tst_QStandardItemModel::modelChanged(ModelChanged change, const QModelIndex &parent,
int first, int last)
{
rcParent[change] = parent;
rcFirst[change] = first;
rcLast[change] = last;
}
void tst_QStandardItemModel::checkChildren()
{
QStandardItemModel model(0, 0);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.columnCount(), 0);
QVERIFY(!model.hasChildren());
QVERIFY(model.insertRows(0, 1));
QVERIFY(!model.hasChildren());
QCOMPARE(model.rowCount(), 1);
QCOMPARE(model.columnCount(), 0);
QVERIFY(model.insertColumns(0, 1));
QVERIFY(model.hasChildren());
QCOMPARE(model.rowCount(), 1);
QCOMPARE(model.columnCount(), 1);
QModelIndex idx = model.index(0, 0);
QVERIFY(!model.hasChildren(idx));
QCOMPARE(model.rowCount(idx), 0);
QCOMPARE(model.columnCount(idx), 0);
QVERIFY(model.insertRows(0, 1, idx));
QVERIFY(!model.hasChildren(idx));
QCOMPARE(model.rowCount(idx), 1);
QCOMPARE(model.columnCount(idx), 0);
QVERIFY(model.insertColumns(0, 1, idx));
QVERIFY(model.hasChildren(idx));
QCOMPARE(model.rowCount(idx), 1);
QCOMPARE(model.columnCount(idx), 1);
QModelIndex idx2 = model.index(0, 0, idx);
QVERIFY(!model.hasChildren(idx2));
QCOMPARE(model.rowCount(idx2), 0);
QCOMPARE(model.columnCount(idx2), 0);
QVERIFY(model.removeRows(0, 1, idx));
QVERIFY(model.hasChildren());
QCOMPARE(model.rowCount(), 1);
QCOMPARE(model.columnCount(), 1);
QVERIFY(!model.hasChildren(idx));
QCOMPARE(model.rowCount(idx), 0);
QCOMPARE(model.columnCount(idx), 1);
QVERIFY(model.removeRows(0, 1));
QVERIFY(!model.hasChildren());
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.columnCount(), 1);
}
void tst_QStandardItemModel::data()
{
// bad args
m_model->setData(QModelIndex(), "bla", Qt::DisplayRole);
QIcon icon;
for (int r=0; r < m_model->rowCount(); ++r) {
for (int c=0; c < m_model->columnCount(); ++c) {
m_model->setData(m_model->index(r,c), "initialitem", Qt::DisplayRole);
m_model->setData(m_model->index(r,c), "tooltip", Qt::ToolTipRole);
m_model->setData(m_model->index(r,c), icon, Qt::DecorationRole);
}
}
QVERIFY(m_model->data(m_model->index(0, 0), Qt::DisplayRole).toString() == "initialitem");
QVERIFY(m_model->data(m_model->index(0, 0), Qt::ToolTipRole).toString() == "tooltip");
}
void tst_QStandardItemModel::clear()
{
QStandardItemModel model;
model.insertColumns(0, 10);
model.insertRows(0, 10);
QCOMPARE(model.columnCount(), 10);
QCOMPARE(model.rowCount(), 10);
QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
QSignalSpy layoutChangedSpy(&model, SIGNAL(layoutChanged()));
QSignalSpy rowsRemovedSpy(&model, SIGNAL(rowsRemoved(QModelIndex, int, int)));
model.clear();
QCOMPARE(modelResetSpy.count(), 1);
QCOMPARE(layoutChangedSpy.count(), 0);
QCOMPARE(rowsRemovedSpy.count(), 0);
QCOMPARE(model.index(0, 0), QModelIndex());
QCOMPARE(model.columnCount(), 0);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.hasChildren(), false);
}
void tst_QStandardItemModel::sort_data()
{
QTest::addColumn<int>("sortOrder");
QTest::addColumn<QStringList>("initial");
QTest::addColumn<QStringList>("expected");
QTest::newRow("flat descending") << static_cast<int>(Qt::DescendingOrder)
<< (QStringList()
<< "delta"
<< "yankee"
<< "bravo"
<< "lima"
<< "charlie"
<< "juliet"
<< "tango"
<< "hotel"
<< "uniform"
<< "alpha"
<< "echo"
<< "golf"
<< "quebec"
<< "foxtrot"
<< "india"
<< "romeo"
<< "november"
<< "oskar"
<< "zulu"
<< "kilo"
<< "whiskey"
<< "mike"
<< "papa"
<< "sierra"
<< "xray"
<< "viktor")
<< (QStringList()
<< "zulu"
<< "yankee"
<< "xray"
<< "whiskey"
<< "viktor"
<< "uniform"
<< "tango"
<< "sierra"
<< "romeo"
<< "quebec"
<< "papa"
<< "oskar"
<< "november"
<< "mike"
<< "lima"
<< "kilo"
<< "juliet"
<< "india"
<< "hotel"
<< "golf"
<< "foxtrot"
<< "echo"
<< "delta"
<< "charlie"
<< "bravo"
<< "alpha");
QTest::newRow("flat ascending") << static_cast<int>(Qt::AscendingOrder)
<< (QStringList()
<< "delta"
<< "yankee"
<< "bravo"
<< "lima"
<< "charlie"
<< "juliet"
<< "tango"
<< "hotel"
<< "uniform"
<< "alpha"
<< "echo"
<< "golf"
<< "quebec"
<< "foxtrot"
<< "india"
<< "romeo"
<< "november"
<< "oskar"
<< "zulu"
<< "kilo"
<< "whiskey"
<< "mike"
<< "papa"
<< "sierra"
<< "xray"
<< "viktor")
<< (QStringList()
<< "alpha"
<< "bravo"
<< "charlie"
<< "delta"
<< "echo"
<< "foxtrot"
<< "golf"
<< "hotel"
<< "india"
<< "juliet"
<< "kilo"
<< "lima"
<< "mike"
<< "november"
<< "oskar"
<< "papa"
<< "quebec"
<< "romeo"
<< "sierra"
<< "tango"
<< "uniform"
<< "viktor"
<< "whiskey"
<< "xray"
<< "yankee"
<< "zulu");
QStringList list;
for (int i=1000; i < 2000; ++i)
list.append(QString("Number: %1").arg(i));
QTest::newRow("large set ascending") << static_cast<int>(Qt::AscendingOrder) << list << list;
}
void tst_QStandardItemModel::sort()
{
QFETCH(int, sortOrder);
QFETCH(QStringList, initial);
QFETCH(QStringList, expected);
// prepare model
QStandardItemModel model;
QVERIFY(model.insertRows(0, initial.count(), QModelIndex()));
QCOMPARE(model.rowCount(QModelIndex()), initial.count());
model.insertColumns(0, 1, QModelIndex());
QCOMPARE(model.columnCount(QModelIndex()), 1);
for (int row = 0; row < model.rowCount(QModelIndex()); ++row) {
QModelIndex index = model.index(row, 0, QModelIndex());
model.setData(index, initial.at(row), Qt::DisplayRole);
}
QSignalSpy layoutAboutToBeChangedSpy(
&model, SIGNAL(layoutAboutToBeChanged()));
QSignalSpy layoutChangedSpy(
&model, SIGNAL(layoutChanged()));
// sort
model.sort(0, static_cast<Qt::SortOrder>(sortOrder));
QCOMPARE(layoutAboutToBeChangedSpy.count(), 1);
QCOMPARE(layoutChangedSpy.count(), 1);
// make sure the model is sorted
for (int row = 0; row < model.rowCount(QModelIndex()); ++row) {
QModelIndex index = model.index(row, 0, QModelIndex());
QCOMPARE(model.data(index, Qt::DisplayRole).toString(), expected.at(row));
}
}
void tst_QStandardItemModel::sortRole_data()
{
QTest::addColumn<QStringList>("initialText");
QTest::addColumn<QVariantList>("initialData");
QTest::addColumn<int>("sortRole");
QTest::addColumn<int>("sortOrder");
QTest::addColumn<QStringList>("expectedText");
QTest::addColumn<QVariantList>("expectedData");
QTest::newRow("sort ascending with Qt::DisplayRole")
<< (QStringList() << "b" << "a" << "c")
<< (QVariantList() << 2 << 3 << 1)
<< static_cast<int>(Qt::DisplayRole)
<< static_cast<int>(Qt::AscendingOrder)
<< (QStringList() << "a" << "b" << "c")
<< (QVariantList() << 3 << 2 << 1);
QTest::newRow("sort ascending with Qt::UserRole")
<< (QStringList() << "a" << "b" << "c")
<< (QVariantList() << 3 << 2 << 1)
<< static_cast<int>(Qt::UserRole)
<< static_cast<int>(Qt::AscendingOrder)
<< (QStringList() << "c" << "b" << "a")
<< (QVariantList() << 1 << 2 << 3);
}
void tst_QStandardItemModel::sortRole()
{
QFETCH(QStringList, initialText);
QFETCH(QVariantList, initialData);
QFETCH(int, sortRole);
QFETCH(int, sortOrder);
QFETCH(QStringList, expectedText);
QFETCH(QVariantList, expectedData);
QStandardItemModel model;
for (int i = 0; i < initialText.count(); ++i) {
QStandardItem *item = new QStandardItem;
item->setText(initialText.at(i));
item->setData(initialData.at(i), Qt::UserRole);
model.appendRow(item);
}
model.setSortRole(sortRole);
model.sort(0, static_cast<Qt::SortOrder>(sortOrder));
for (int i = 0; i < expectedText.count(); ++i) {
QStandardItem *item = model.item(i);
QCOMPARE(item->text(), expectedText.at(i));
QCOMPARE(item->data(Qt::UserRole), expectedData.at(i));
}
}
void tst_QStandardItemModel::findItems()
{
QStandardItemModel model;
model.appendRow(new QStandardItem(QLatin1String("foo")));
model.appendRow(new QStandardItem(QLatin1String("bar")));
model.item(1)->appendRow(new QStandardItem(QLatin1String("foo")));
QList<QStandardItem*> matches;
matches = model.findItems(QLatin1String("foo"), Qt::MatchExactly|Qt::MatchRecursive, 0);
QCOMPARE(matches.count(), 2);
matches = model.findItems(QLatin1String("foo"), Qt::MatchExactly, 0);
QCOMPARE(matches.count(), 1);
matches = model.findItems(QLatin1String("food"), Qt::MatchExactly|Qt::MatchRecursive, 0);
QCOMPARE(matches.count(), 0);
matches = model.findItems(QLatin1String("foo"), Qt::MatchExactly|Qt::MatchRecursive, -1);
QCOMPARE(matches.count(), 0);
matches = model.findItems(QLatin1String("foo"), Qt::MatchExactly|Qt::MatchRecursive, 1);
QCOMPARE(matches.count(), 0);
}
void tst_QStandardItemModel::getSetHeaderItem()
{
QStandardItemModel model;
QCOMPARE(model.horizontalHeaderItem(0), static_cast<QStandardItem*>(0));
QStandardItem *hheader = new QStandardItem();
model.setHorizontalHeaderItem(0, hheader);
QCOMPARE(model.columnCount(), 1);
QCOMPARE(model.horizontalHeaderItem(0), hheader);
QCOMPARE(hheader->model(), &model);
model.setHorizontalHeaderItem(0, 0);
QCOMPARE(model.horizontalHeaderItem(0), static_cast<QStandardItem*>(0));
QCOMPARE(model.verticalHeaderItem(0), static_cast<QStandardItem*>(0));
QStandardItem *vheader = new QStandardItem();
model.setVerticalHeaderItem(0, vheader);
QCOMPARE(model.rowCount(), 1);
QCOMPARE(model.verticalHeaderItem(0), vheader);
QCOMPARE(vheader->model(), &model);
model.setVerticalHeaderItem(0, 0);
QCOMPARE(model.verticalHeaderItem(0), static_cast<QStandardItem*>(0));
}
void tst_QStandardItemModel::indexFromItem()
{
QStandardItemModel model;
QCOMPARE(model.indexFromItem(model.invisibleRootItem()), QModelIndex());
QStandardItem *item = new QStandardItem;
model.setItem(10, 20, item);
QCOMPARE(item->model(), &model);
QModelIndex itemIndex = model.indexFromItem(item);
QVERIFY(itemIndex.isValid());
QCOMPARE(itemIndex.row(), 10);
QCOMPARE(itemIndex.column(), 20);
QCOMPARE(itemIndex.parent(), QModelIndex());
QCOMPARE(itemIndex.model(), (const QAbstractItemModel*)(&model));
QStandardItem *child = new QStandardItem;
item->setChild(4, 2, child);
QModelIndex childIndex = model.indexFromItem(child);
QVERIFY(childIndex.isValid());
QCOMPARE(childIndex.row(), 4);
QCOMPARE(childIndex.column(), 2);
QCOMPARE(childIndex.parent(), itemIndex);
QStandardItem *dummy = new QStandardItem;
QModelIndex noSuchIndex = model.indexFromItem(dummy);
QVERIFY(!noSuchIndex.isValid());
delete dummy;
noSuchIndex = model.indexFromItem(0);
QVERIFY(!noSuchIndex.isValid());
}
void tst_QStandardItemModel::itemFromIndex()
{
QStandardItemModel model;
QCOMPARE(model.itemFromIndex(QModelIndex()), (QStandardItem*)0);
QStandardItem *item = new QStandardItem;
model.setItem(10, 20, item);
QModelIndex itemIndex = model.index(10, 20, QModelIndex());
QVERIFY(itemIndex.isValid());
QCOMPARE(model.itemFromIndex(itemIndex), item);
QStandardItem *child = new QStandardItem;
item->setChild(4, 2, child);
QModelIndex childIndex = model.index(4, 2, itemIndex);
QVERIFY(childIndex.isValid());
QCOMPARE(model.itemFromIndex(childIndex), child);
QModelIndex noSuchIndex = model.index(99, 99, itemIndex);
QVERIFY(!noSuchIndex.isValid());
}
class CustomItem : public QStandardItem
{
public:
CustomItem() : QStandardItem() { }
~CustomItem() { }
int type() const {
return UserType;
}
QStandardItem *clone() const {
return new CustomItem;
}
};
void tst_QStandardItemModel::getSetItemPrototype()
{
QStandardItemModel model;
QCOMPARE(model.itemPrototype(), static_cast<const QStandardItem*>(0));
const CustomItem *proto = new CustomItem;
model.setItemPrototype(proto);
QCOMPARE(model.itemPrototype(), (const QStandardItem*)proto);
model.setRowCount(1);
model.setColumnCount(1);
QModelIndex index = model.index(0, 0, QModelIndex());
model.setData(index, "foo");
QStandardItem *item = model.itemFromIndex(index);
QVERIFY(item != 0);
QCOMPARE(item->type(), static_cast<int>(QStandardItem::UserType));
model.setItemPrototype(0);
QCOMPARE(model.itemPrototype(), static_cast<const QStandardItem*>(0));
}
void tst_QStandardItemModel::getSetItemData()
{
QMap<int, QVariant> roles;
QLatin1String text("text");
roles.insert(Qt::DisplayRole, text);
QLatin1String statusTip("statusTip");
roles.insert(Qt::StatusTipRole, statusTip);
QLatin1String toolTip("toolTip");
roles.insert(Qt::ToolTipRole, toolTip);
QLatin1String whatsThis("whatsThis");
roles.insert(Qt::WhatsThisRole, whatsThis);
QSize sizeHint(64, 48);
roles.insert(Qt::SizeHintRole, sizeHint);
QFont font;
roles.insert(Qt::FontRole, font);
Qt::Alignment textAlignment(Qt::AlignLeft|Qt::AlignVCenter);
roles.insert(Qt::TextAlignmentRole, int(textAlignment));
QColor backgroundColor(Qt::blue);
roles.insert(Qt::BackgroundRole, backgroundColor);
QColor textColor(Qt::green);
roles.insert(Qt::TextColorRole, textColor);
Qt::CheckState checkState(Qt::PartiallyChecked);
roles.insert(Qt::CheckStateRole, int(checkState));
QLatin1String accessibleText("accessibleText");
roles.insert(Qt::AccessibleTextRole, accessibleText);
QLatin1String accessibleDescription("accessibleDescription");
roles.insert(Qt::AccessibleDescriptionRole, accessibleDescription);
QStandardItemModel model;
model.insertRows(0, 1);
model.insertColumns(0, 1);
QModelIndex idx = model.index(0, 0, QModelIndex());
QSignalSpy modelDataChangedSpy(
&model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
QVERIFY(model.setItemData(idx, roles));
QCOMPARE(modelDataChangedSpy.count(), 1);
QVERIFY(model.setItemData(idx, roles));
QCOMPARE(modelDataChangedSpy.count(), 1); //it was already changed once
QCOMPARE(model.itemData(idx), roles);
}
void tst_QStandardItemModel::setHeaderLabels_data()
{
QTest::addColumn<int>("rows");
QTest::addColumn<int>("columns");
QTest::addColumn<int>("orientation");
QTest::addColumn<QStringList>("labels");
QTest::addColumn<QStringList>("expectedLabels");
QTest::newRow("horizontal labels")
<< 1
<< 4
<< int(Qt::Horizontal)
<< (QStringList() << "a" << "b" << "c" << "d")
<< (QStringList() << "a" << "b" << "c" << "d");
QTest::newRow("vertical labels")
<< 4
<< 1
<< int(Qt::Vertical)
<< (QStringList() << "a" << "b" << "c" << "d")
<< (QStringList() << "a" << "b" << "c" << "d");
QTest::newRow("too few (horizontal)")
<< 1
<< 4
<< int(Qt::Horizontal)
<< (QStringList() << "a" << "b")
<< (QStringList() << "a" << "b" << "3" << "4");
QTest::newRow("too few (vertical)")
<< 4
<< 1
<< int(Qt::Vertical)
<< (QStringList() << "a" << "b")
<< (QStringList() << "a" << "b" << "3" << "4");
QTest::newRow("too many (horizontal)")
<< 1
<< 2
<< int(Qt::Horizontal)
<< (QStringList() << "a" << "b" << "c" << "d")
<< (QStringList() << "a" << "b" << "c" << "d");
QTest::newRow("too many (vertical)")
<< 2
<< 1
<< int(Qt::Vertical)
<< (QStringList() << "a" << "b" << "c" << "d")
<< (QStringList() << "a" << "b" << "c" << "d");
}
void tst_QStandardItemModel::setHeaderLabels()
{
QFETCH(int, rows);
QFETCH(int, columns);
QFETCH(int, orientation);
QFETCH(QStringList, labels);
QFETCH(QStringList, expectedLabels);
QStandardItemModel model(rows, columns);
QSignalSpy columnsInsertedSpy(
&model, SIGNAL(columnsInserted(QModelIndex,int,int)));
QSignalSpy rowsInsertedSpy(
&model, SIGNAL(rowsInserted(QModelIndex,int,int)));
if (orientation == Qt::Horizontal)
model.setHorizontalHeaderLabels(labels);
else
model.setVerticalHeaderLabels(labels);
for (int i = 0; i < expectedLabels.count(); ++i)
QCOMPARE(model.headerData(i, Qt::Orientation(orientation)).toString(), expectedLabels.at(i));
QCOMPARE(columnsInsertedSpy.count(),
(orientation == Qt::Vertical) ? 0 : labels.count() > columns);
QCOMPARE(rowsInsertedSpy.count(),
(orientation == Qt::Horizontal) ? 0 : labels.count() > rows);
}
void tst_QStandardItemModel::itemDataChanged()
{
QStandardItemModel model(6, 4);
QStandardItem item;
QSignalSpy dataChangedSpy(
&model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)));
QSignalSpy itemChangedSpy(
&model, SIGNAL(itemChanged(QStandardItem *)));
model.setItem(0, &item);
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(itemChangedSpy.count(), 1);
QModelIndex index = model.indexFromItem(&item);
QList<QVariant> args;
args = dataChangedSpy.takeFirst();
QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), index);
QCOMPARE(qvariant_cast<QModelIndex>(args.at(1)), index);
args = itemChangedSpy.takeFirst();
QCOMPARE(qvariant_cast<QStandardItem*>(args.at(0)), &item);
item.setData(QLatin1String("foo"), Qt::DisplayRole);
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(itemChangedSpy.count(), 1);
args = dataChangedSpy.takeFirst();
QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), index);
QCOMPARE(qvariant_cast<QModelIndex>(args.at(1)), index);
args = itemChangedSpy.takeFirst();
QCOMPARE(qvariant_cast<QStandardItem*>(args.at(0)), &item);
item.setData(item.data(Qt::DisplayRole), Qt::DisplayRole);
QCOMPARE(dataChangedSpy.count(), 0);
QCOMPARE(itemChangedSpy.count(), 0);
item.setFlags(Qt::ItemIsEnabled);
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(itemChangedSpy.count(), 1);
args = dataChangedSpy.takeFirst();
QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), index);
QCOMPARE(qvariant_cast<QModelIndex>(args.at(1)), index);
args = itemChangedSpy.takeFirst();
QCOMPARE(qvariant_cast<QStandardItem*>(args.at(0)), &item);
item.setFlags(item.flags());
QCOMPARE(dataChangedSpy.count(), 0);
QCOMPARE(itemChangedSpy.count(), 0);
}
void tst_QStandardItemModel::takeHeaderItem()
{
QStandardItemModel model;
// set header items
QStandardItem *hheader = new QStandardItem();
model.setHorizontalHeaderItem(0, hheader);
QStandardItem *vheader = new QStandardItem();
model.setVerticalHeaderItem(0, vheader);
// take header items
QCOMPARE(model.takeHorizontalHeaderItem(0), hheader);
QCOMPARE(model.takeVerticalHeaderItem(0), vheader);
QCOMPARE(hheader->model(), static_cast<QStandardItemModel*>(0));
QCOMPARE(vheader->model(), static_cast<QStandardItemModel*>(0));
QCOMPARE(model.takeHorizontalHeaderItem(0), static_cast<QStandardItem*>(0));
QCOMPARE(model.takeVerticalHeaderItem(0), static_cast<QStandardItem*>(0));
delete hheader;
delete vheader;
}
void tst_QStandardItemModel::useCase1()
{
const int rows = 5;
const int columns = 8;
QStandardItemModel model(rows, columns);
for (int i = 0; i < model.rowCount(); ++i) {
for (int j = 0; j < model.columnCount(); ++j) {
QCOMPARE(model.item(i, j), static_cast<QStandardItem*>(0));
QStandardItem *item = new QStandardItem();
model.setItem(i, j, item);
QCOMPARE(item->row(), i);
QCOMPARE(item->column(), j);
QCOMPARE(item->model(), &model);
QModelIndex index = model.indexFromItem(item);
QCOMPARE(index, model.index(i, j, QModelIndex()));
QStandardItem *sameItem = model.itemFromIndex(index);
QCOMPARE(sameItem, item);
}
}
}
static void createChildren(QStandardItemModel *model, QStandardItem *parent, int level)
{
if (level > 4)
return;
for (int i = 0; i < 4; ++i) {
QCOMPARE(parent->rowCount(), i);
parent->appendRow(QList<QStandardItem*>());
for (int j = 0; j < parent->columnCount(); ++j) {
QStandardItem *item = new QStandardItem();
parent->setChild(i, j, item);
QCOMPARE(item->row(), i);
QCOMPARE(item->column(), j);
QModelIndex parentIndex = model->indexFromItem(parent);
QModelIndex index = model->indexFromItem(item);
QCOMPARE(index, model->index(i, j, parentIndex));
QStandardItem *theItem = model->itemFromIndex(index);
QCOMPARE(theItem, item);
QStandardItem *theParent = model->itemFromIndex(parentIndex);
QCOMPARE(theParent, (level == 0) ? (QStandardItem*)0 : parent);
}
{
QStandardItem *item = parent->child(i);
item->setColumnCount(parent->columnCount());
createChildren(model, item, level + 1);
}
}
}
void tst_QStandardItemModel::useCase2()
{
QStandardItemModel model;
model.setColumnCount(2);
createChildren(&model, model.invisibleRootItem(), 0);
}
void tst_QStandardItemModel::useCase3()
{
// create the tree structure first
QStandardItem *childItem = 0;
for (int i = 0; i < 100; ++i) {
QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
if (childItem)
item->appendRow(childItem);
childItem = item;
}
// add to model as last step
QStandardItemModel model;
model.appendRow(childItem);
// make sure each item has the correct model and parent
QStandardItem *parentItem = 0;
while (childItem) {
QCOMPARE(childItem->model(), &model);
QCOMPARE(childItem->parent(), parentItem);
parentItem = childItem;
childItem = childItem->child(0);
}
// take the item, make sure model is set to 0, but that parents are the same
childItem = model.takeItem(0);
{
parentItem = 0;
QStandardItem *item = childItem;
while (item) {
QCOMPARE(item->model(), static_cast<QStandardItemModel*>(0));
QCOMPARE(item->parent(), parentItem);
parentItem = item;
item = item->child(0);
}
}
delete childItem;
}
void tst_QStandardItemModel::rootItemFlags()
{
QStandardItemModel model(6, 4);
QCOMPARE(model.invisibleRootItem()->flags() , model.flags(QModelIndex()));
QCOMPARE(model.invisibleRootItem()->flags() , Qt::ItemIsDropEnabled);
Qt::ItemFlags f = Qt::ItemIsDropEnabled | Qt::ItemIsEnabled;
model.invisibleRootItem()->setFlags(f);
QCOMPARE(model.invisibleRootItem()->flags() , f);
QCOMPARE(model.invisibleRootItem()->flags() , model.flags(QModelIndex()));
#ifndef QT_NO_DRAGANDDROP
model.invisibleRootItem()->setDropEnabled(false);
#endif
QCOMPARE(model.invisibleRootItem()->flags() , Qt::ItemIsEnabled);
QCOMPARE(model.invisibleRootItem()->flags() , model.flags(QModelIndex()));
}
bool tst_QStandardItemModel::compareModels(QStandardItemModel *model1, QStandardItemModel *model2)
{
return compareItems(model1->invisibleRootItem(), model2->invisibleRootItem());
}
bool tst_QStandardItemModel::compareItems(QStandardItem *item1, QStandardItem *item2)
{
if (!item1 && !item2)
return true;
if (!item1 || !item2)
return false;
if (item1->text() != item2->text()){
qDebug() << item1->text() << item2->text();
return false;
}
if (item1->rowCount() != item2->rowCount()) {
// qDebug() << "RowCount" << item1->text() << item1->rowCount() << item2->rowCount();
return false;
}
if (item1->columnCount() != item2->columnCount()) {
// qDebug() << "ColumnCount" << item1->text() << item1->columnCount() << item2->columnCount();
return false;
}
for (int row = 0; row < item1->columnCount(); row++)
for (int col = 0; col < item1->columnCount(); col++) {
if (!compareItems(item1->child(row, col), item2->child(row, col)))
return false;
}
return true;
}
static QStandardItem *itemFromText(QStandardItem *parent, const QString &text)
{
QStandardItem *item = 0;
for(int i = 0; i < parent->columnCount(); i++)
for(int j = 0; j < parent->rowCount(); j++) {
QStandardItem *child = parent->child(j, i);
if(!child)
continue;
if (child->text() == text) {
if (item) {
return 0;
}
item = child;
}
QStandardItem *candidate = itemFromText(child, text);
if(candidate) {
if (item) {
return 0;
}
item = candidate;
}
}
return item;
}
#ifdef QT_BUILD_INTERNAL
static QModelIndex indexFromText(QStandardItemModel *model, const QString &text)
{
QStandardItem *item = itemFromText(model->invisibleRootItem(), text);
/*QVERIFY(item);*/
return model->indexFromItem(item);
}
struct FriendlyTreeView : public QTreeView
{
friend class tst_QStandardItemModel;
Q_DECLARE_PRIVATE(QTreeView)
};
#endif
void tst_QStandardItemModel::treeDragAndDrop()
{
#ifdef QT_BUILD_INTERNAL
const int nRow = 5;
const int nCol = 3;
QStandardItemModel model;
QStandardItemModel checkModel;
for (int i = 0; i < nRow; ++i) {
QList<QStandardItem *> colItems1;
for (int c = 0 ; c < nCol; c ++)
colItems1 << new QStandardItem(QString("item %1 - %0").arg(c).arg(i));
model.appendRow(colItems1);
for (int j = 0; j < nRow; ++j) {
QList<QStandardItem *> colItems2;
for (int c = 0 ; c < nCol; c ++)
colItems2 << new QStandardItem(QString("item %1/%2 - %0").arg(c).arg(i).arg(j));
colItems1.at(0)->appendRow(colItems2);
for (int k = 0; k < nRow; ++k) {
QList<QStandardItem *> colItems3;
for (int c = 0 ; c < nCol; c ++)
colItems3 << new QStandardItem(QString("item %1/%2/%3 - %0").arg(c).arg(i).arg(j).arg(k));
colItems2.at(0)->appendRow(colItems3);
}
}
}
for (int i = 0; i < nRow; ++i) {
QList<QStandardItem *> colItems1;
for (int c = 0 ; c < nCol; c ++)
colItems1 << new QStandardItem(QString("item %1 - %0").arg(c).arg(i));
checkModel.appendRow(colItems1);
for (int j = 0; j < nRow; ++j) {
QList<QStandardItem *> colItems2;
for (int c = 0 ; c < nCol; c ++)
colItems2 << new QStandardItem(QString("item %1/%2 - %0").arg(c).arg(i).arg(j));
colItems1.at(0)->appendRow(colItems2);
for (int k = 0; k < nRow; ++k) {
QList<QStandardItem *> colItems3;
for (int c = 0 ; c < nCol; c ++)
colItems3 << new QStandardItem(QString("item %1/%2/%3 - %0").arg(c).arg(i).arg(j).arg(k));
colItems2.at(0)->appendRow(colItems3);
}
}
}
QVERIFY(compareModels(&model, &checkModel));
FriendlyTreeView view;
view.setModel(&model);
view.expandAll();
view.show();
#ifndef QT_NO_DRAGANDDROP
view.setDragDropMode(QAbstractItemView::InternalMove);
#endif
view.setSelectionMode(QAbstractItemView::ExtendedSelection);
QItemSelectionModel *selection = view.selectionModel();
//
// step1 drag "item 1" and "item 2" into "item 4"
//
{
selection->clear();
selection->select(QItemSelection(indexFromText(&model, QString("item 1 - 0")),
indexFromText(&model, QString("item 1 - %0").arg(nCol-1))), QItemSelectionModel::Select);
selection->select(QItemSelection(indexFromText(&model, QString("item 2 - 0")),
indexFromText(&model, QString("item 2 - %0").arg(nCol-1))), QItemSelectionModel::Select);
//code based from QAbstractItemView::startDrag and QAbstractItemView::dropEvent
QModelIndexList indexes = view.selectedIndexes();
QMimeData *data = model.mimeData(indexes);
if(model.dropMimeData(data, Qt::MoveAction, 0, 0, indexFromText(&model, "item 4 - 0")))
view.d_func()->clearOrRemove();
delete data;
QVERIFY(!compareModels(&model, &checkModel)); //the model must be different at this point
QStandardItem *item4 = itemFromText(checkModel.invisibleRootItem(), "item 4 - 0");
item4->insertRow(0, checkModel.takeRow(1));
item4->insertRow(1, checkModel.takeRow(1));
QVERIFY(compareModels(&model, &checkModel));
}
//
// step2 drag "item 3" and "item 3/0" into "item 4"
//
{
selection->clear();
selection->select(QItemSelection(indexFromText(&model, QString("item 3 - 0")),
indexFromText(&model, QString("item 3 - %0").arg(nCol-1))), QItemSelectionModel::Select);
selection->select(QItemSelection(indexFromText(&model, QString("item 3/0 - 0")),
indexFromText(&model, QString("item 3/0 - %0").arg(nCol-1))), QItemSelectionModel::Select);
//code based from QAbstractItemView::startDrag and QAbstractItemView::dropEvent
QModelIndexList indexes = view.selectedIndexes();
QMimeData *data = model.mimeData(indexes);
if(model.dropMimeData(data, Qt::MoveAction, 0, 0, indexFromText(&model, "item 4 - 0")))
view.d_func()->clearOrRemove();
delete data;
QVERIFY(!compareModels(&model, &checkModel)); //the model must be different at this point
QStandardItem *item4 = itemFromText(checkModel.invisibleRootItem(), "item 4 - 0");
item4->insertRow(0, checkModel.takeRow(1));
QVERIFY(compareModels(&model, &checkModel));
}
//
// step2 drag "item 3" and "item 3/0/2" into "item 0/2"
// ( remember "item 3" is now the first child of "item 4")
//
{
selection->clear();
selection->select(QItemSelection(indexFromText(&model, QString("item 3 - 0")),
indexFromText(&model, QString("item 3 - %0").arg(nCol-1))), QItemSelectionModel::Select);
selection->select(QItemSelection(indexFromText(&model, QString("item 3/0/2 - 0")),
indexFromText(&model, QString("item 3/0/2 - %0").arg(nCol-1))), QItemSelectionModel::Select);
//code based from QAbstractItemView::startDrag and QAbstractItemView::dropEvent
QModelIndexList indexes = view.selectedIndexes();
QMimeData *data = model.mimeData(indexes);
if(model.dropMimeData(data, Qt::MoveAction, 0, 0, indexFromText(&model, "item 0/2 - 0")))
view.d_func()->clearOrRemove();
delete data;
QVERIFY(!compareModels(&model, &checkModel)); //the model must be different at this point
QStandardItem *item02 = itemFromText(checkModel.invisibleRootItem(), "item 0/2 - 0");
QStandardItem *item4 = itemFromText(checkModel.invisibleRootItem(), "item 4 - 0");
item02->insertRow(0, item4->takeRow(0));
QVERIFY(compareModels(&model, &checkModel));
}
#endif
}
void tst_QStandardItemModel::removeRowsAndColumns()
{
#define VERIFY_MODEL \
for (int c = 0; c < col_list.count(); c++) \
for (int r = 0; r < row_list.count(); r++) \
QCOMPARE(model.item(r,c)->text() , row_list[r] + "x" + col_list[c]);
QVector<QString> row_list = QString("1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20").split(',').toVector();
QVector<QString> col_list = row_list;
QStandardItemModel model;
for (int c = 0; c < col_list.count(); c++)
for (int r = 0; r < row_list.count(); r++)
model.setItem(r, c, new QStandardItem(row_list[r] + "x" + col_list[c]));
VERIFY_MODEL
row_list.remove(3);
model.removeRow(3);
VERIFY_MODEL
col_list.remove(5);
model.removeColumn(5);
VERIFY_MODEL
row_list.remove(2, 5);
model.removeRows(2, 5);
VERIFY_MODEL
col_list.remove(1, 6);
model.removeColumns(1, 6);
VERIFY_MODEL
QList<QStandardItem *> row_taken = model.takeRow(6);
QCOMPARE(row_taken.count(), col_list.count());
for (int c = 0; c < col_list.count(); c++)
QCOMPARE(row_taken[c]->text() , row_list[6] + "x" + col_list[c]);
row_list.remove(6);
VERIFY_MODEL
QList<QStandardItem *> col_taken = model.takeColumn(10);
QCOMPARE(col_taken.count(), row_list.count());
for (int r = 0; r < row_list.count(); r++)
QCOMPARE(col_taken[r]->text() , row_list[r] + "x" + col_list[10]);
col_list.remove(10);
VERIFY_MODEL
}
QTEST_MAIN(tst_QStandardItemModel)
#include "tst_qstandarditemmodel.moc"