From 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 23 Mar 2009 10:34:13 +0100 Subject: Long live Qt! --- tests/auto/qstandarditemmodel/.gitignore | 1 + .../auto/qstandarditemmodel/qstandarditemmodel.pro | 4 + .../qstandarditemmodel/tst_qstandarditemmodel.cpp | 1623 ++++++++++++++++++++ 3 files changed, 1628 insertions(+) create mode 100644 tests/auto/qstandarditemmodel/.gitignore create mode 100644 tests/auto/qstandarditemmodel/qstandarditemmodel.pro create mode 100644 tests/auto/qstandarditemmodel/tst_qstandarditemmodel.cpp (limited to 'tests/auto/qstandarditemmodel') diff --git a/tests/auto/qstandarditemmodel/.gitignore b/tests/auto/qstandarditemmodel/.gitignore new file mode 100644 index 0000000000..c218efce58 --- /dev/null +++ b/tests/auto/qstandarditemmodel/.gitignore @@ -0,0 +1 @@ +tst_qstandarditemmodel diff --git a/tests/auto/qstandarditemmodel/qstandarditemmodel.pro b/tests/auto/qstandarditemmodel/qstandarditemmodel.pro new file mode 100644 index 0000000000..cc622e2e74 --- /dev/null +++ b/tests/auto/qstandarditemmodel/qstandarditemmodel.pro @@ -0,0 +1,4 @@ +load(qttest_p4) +SOURCES += tst_qstandarditemmodel.cpp + + diff --git a/tests/auto/qstandarditemmodel/tst_qstandarditemmodel.cpp b/tests/auto/qstandarditemmodel/tst_qstandarditemmodel.cpp new file mode 100644 index 0000000000..a3322ab713 --- /dev/null +++ b/tests/auto/qstandarditemmodel/tst_qstandarditemmodel.cpp @@ -0,0 +1,1623 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include + +//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(); +#if QT_VERSION >= 0x040200 + 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(); +#endif + + void rootItemFlags(); + void treeDragAndDrop(); + +private: + QAbstractItemModel *m_model; + QPersistentModelIndex persistent; + QVector rcParent; + QVector rcFirst; + QVector 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) +#if QT_VERSION >= 0x040200 +Q_DECLARE_METATYPE(QStandardItem*) +#endif +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"); +#if QT_VERSION >= 0x040200 + qRegisterMetaType("QStandardItem*"); +#endif + qRegisterMetaType("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("insertRow"); + QTest::addColumn("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 items; + QStandardItemModel *m = qobject_cast(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(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("insertColumn"); + QTest::addColumn("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(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); +} + +#if QT_VERSION >= 0x040200 +void tst_QStandardItemModel::sort_data() +{ + QTest::addColumn("sortOrder"); + QTest::addColumn("initial"); + QTest::addColumn("expected"); + + QTest::newRow("flat descending") << static_cast(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(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(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(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("initialText"); + QTest::addColumn("initialData"); + QTest::addColumn("sortRole"); + QTest::addColumn("sortOrder"); + QTest::addColumn("expectedText"); + QTest::addColumn("expectedData"); + + QTest::newRow("sort ascending with Qt::DisplayRole") + << (QStringList() << "b" << "a" << "c") + << (QVariantList() << 2 << 3 << 1) + << static_cast(Qt::DisplayRole) + << static_cast(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(Qt::UserRole) + << static_cast(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(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 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(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(0)); + + QCOMPARE(model.verticalHeaderItem(0), static_cast(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(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(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(QStandardItem::UserType)); + + model.setItemPrototype(0); + QCOMPARE(model.itemPrototype(), static_cast(0)); +} + +void tst_QStandardItemModel::getSetItemData() +{ + QMap 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("rows"); + QTest::addColumn("columns"); + QTest::addColumn("orientation"); + QTest::addColumn("labels"); + QTest::addColumn("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 args; + args = dataChangedSpy.takeFirst(); + QCOMPARE(qvariant_cast(args.at(0)), index); + QCOMPARE(qvariant_cast(args.at(1)), index); + args = itemChangedSpy.takeFirst(); + QCOMPARE(qvariant_cast(args.at(0)), &item); + + item.setData(QLatin1String("foo"), Qt::DisplayRole); + QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.count(), 1); + args = dataChangedSpy.takeFirst(); + QCOMPARE(qvariant_cast(args.at(0)), index); + QCOMPARE(qvariant_cast(args.at(1)), index); + args = itemChangedSpy.takeFirst(); + QCOMPARE(qvariant_cast(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(args.at(0)), index); + QCOMPARE(qvariant_cast(args.at(1)), index); + args = itemChangedSpy.takeFirst(); + QCOMPARE(qvariant_cast(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(0)); + QCOMPARE(vheader->model(), static_cast(0)); + QCOMPARE(model.takeHorizontalHeaderItem(0), static_cast(0)); + QCOMPARE(model.takeVerticalHeaderItem(0), static_cast(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(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()); + 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(0)); + QCOMPARE(item->parent(), parentItem); + parentItem = item; + item = item->child(0); + } + } + delete childItem; +} + +#endif // QT_VERSION >= 0x040200 + +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())); + + model.invisibleRootItem()->setDropEnabled(false); + 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; +} + +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) +}; + +void tst_QStandardItemModel::treeDragAndDrop() +{ + const int nRow = 5; + const int nCol = 3; + + QStandardItemModel model; + QStandardItemModel checkModel; + + for (int i = 0; i < nRow; ++i) { + QList 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 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 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 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 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 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(); + view.setDragDropMode(QAbstractItemView::InternalMove); + 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)); + } +} + + +QTEST_MAIN(tst_QStandardItemModel) +#include "tst_qstandarditemmodel.moc" -- cgit v1.2.3 From e0be99974d5d66f548543df9b5b919c8e81ade62 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 10 Jun 2009 18:42:35 +0200 Subject: Invalid QPersistentIndexes after QStandardItem::takeRow We need the parent of each potential QPersistentModelIndex in order to cleanup when removing the rows. They need not to change in order QSortFilterProxyModel maping to be still valid. takeRow must not change the internal data before calling beginRemoveRow. Same thing for takeColumn Task-number: 255652 Reviewed-by: Thierry Reviewed-by: Leo --- .../qstandarditemmodel/tst_qstandarditemmodel.cpp | 49 +++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'tests/auto/qstandarditemmodel') diff --git a/tests/auto/qstandarditemmodel/tst_qstandarditemmodel.cpp b/tests/auto/qstandarditemmodel/tst_qstandarditemmodel.cpp index 84bda92ee3..8067554ae8 100644 --- a/tests/auto/qstandarditemmodel/tst_qstandarditemmodel.cpp +++ b/tests/auto/qstandarditemmodel/tst_qstandarditemmodel.cpp @@ -135,6 +135,7 @@ private slots: void rootItemFlags(); void treeDragAndDrop(); + void removeRowsAndColumns(); private: QAbstractItemModel *m_model; @@ -1403,7 +1404,7 @@ bool tst_QStandardItemModel::compareItems(QStandardItem *item1, QStandardItem *i return true; if (!item1 || !item2) return false; - if (item1->text() != item2->text()){ + if (item1->text() != item2->text()){ qDebug() << item1->text() << item2->text(); return false; } @@ -1606,6 +1607,52 @@ void tst_QStandardItemModel::treeDragAndDrop() } } +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 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 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 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 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" -- cgit v1.2.3