summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/itemmodels
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/itemmodels')
-rw-r--r--tests/auto/corelib/itemmodels/itemmodels.pro2
-rw-r--r--tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro5
-rw-r--r--tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp823
-rw-r--r--tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp4
-rw-r--r--tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp4
-rw-r--r--tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp176
-rw-r--r--tests/auto/corelib/itemmodels/qtransposeproxymodel/qtransposeproxymodel.pro6
-rw-r--r--tests/auto/corelib/itemmodels/qtransposeproxymodel/tst_qtransposeproxymodel.cpp915
8 files changed, 1931 insertions, 4 deletions
diff --git a/tests/auto/corelib/itemmodels/itemmodels.pro b/tests/auto/corelib/itemmodels/itemmodels.pro
index cca350ad43..ffbda6ec40 100644
--- a/tests/auto/corelib/itemmodels/itemmodels.pro
+++ b/tests/auto/corelib/itemmodels/itemmodels.pro
@@ -5,9 +5,11 @@ SUBDIRS = qstringlistmodel
qtHaveModule(gui): SUBDIRS += \
qabstractitemmodel \
qabstractproxymodel \
+ qconcatenatetablesproxymodel \
qidentityproxymodel \
qitemselectionmodel \
qsortfilterproxymodel_recursive \
+ qtransposeproxymodel \
qtHaveModule(widgets) {
SUBDIRS += \
diff --git a/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro
new file mode 100644
index 0000000000..ee4ea28b5b
--- /dev/null
+++ b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+TARGET = tst_qconcatenatetablesproxymodel
+QT = core gui testlib
+
+SOURCES = tst_qconcatenatetablesproxymodel.cpp
diff --git a/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp
new file mode 100644
index 0000000000..40617c1f7d
--- /dev/null
+++ b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp
@@ -0,0 +1,823 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QSignalSpy>
+#include <QSortFilterProxyModel>
+#include <QTest>
+#include <QStandardItemModel>
+#include <QIdentityProxyModel>
+#include <QItemSelectionModel>
+#include <QMimeData>
+#include <QStringListModel>
+#include <QAbstractItemModelTester>
+
+#include <qconcatenatetablesproxymodel.h>
+
+Q_DECLARE_METATYPE(QModelIndex)
+
+// Extracts a full row from a model as a string
+// Works best if every cell contains only one character
+static QString extractRowTexts(QAbstractItemModel *model, int row, const QModelIndex &parent = QModelIndex())
+{
+ QString result;
+ const int colCount = model->columnCount();
+ for (int col = 0; col < colCount; ++col) {
+ const QString txt = model->index(row, col, parent).data().toString();
+ result += txt.isEmpty() ? QStringLiteral(" ") : txt;
+ }
+ return result;
+}
+
+// Extracts a full column from a model as a string
+// Works best if every cell contains only one character
+static QString extractColumnTexts(QAbstractItemModel *model, int column, const QModelIndex &parent = QModelIndex())
+{
+ QString result;
+ const int rowCount = model->rowCount();
+ for (int row = 0; row < rowCount; ++row) {
+ const QString txt = model->index(row, column, parent).data().toString();
+ result += txt.isEmpty() ? QStringLiteral(" ") : txt;
+ }
+ return result;
+}
+
+static QString rowSpyToText(const QSignalSpy &spy)
+{
+ if (!spy.isValid())
+ return QStringLiteral("THE SIGNALSPY IS INVALID!");
+ QString str;
+ for (int i = 0; i < spy.count(); ++i) {
+ str += spy.at(i).at(1).toString() + QLatin1Char(',') + spy.at(i).at(2).toString();
+ if (i + 1 < spy.count())
+ str += QLatin1Char(';');
+ }
+ return str;
+}
+
+class tst_QConcatenateTablesProxyModel : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void init();
+ void shouldAggregateTwoModelsCorrectly();
+ void shouldAggregateThenRemoveTwoEmptyModelsCorrectly();
+ void shouldAggregateTwoEmptyModelsWhichThenGetFilled();
+ void shouldHandleDataChanged();
+ void shouldHandleSetData();
+ void shouldHandleSetItemData();
+ void shouldHandleRowInsertionAndRemoval();
+ void shouldAggregateAnotherModelThenRemoveModels();
+ void shouldUseSmallestColumnCount();
+ void shouldIncreaseColumnCountWhenRemovingFirstModel();
+ void shouldHandleColumnInsertionAndRemoval();
+ void shouldPropagateLayoutChanged();
+ void shouldReactToModelReset();
+ void shouldUpdateColumnsOnModelReset();
+ void shouldPropagateDropOnItem_data();
+ void shouldPropagateDropOnItem();
+ void shouldPropagateDropBetweenItems();
+ void shouldPropagateDropBetweenItemsAtModelBoundary();
+ void shouldPropagateDropAfterLastRow_data();
+ void shouldPropagateDropAfterLastRow();
+
+private:
+ QStandardItemModel mod;
+ QStandardItemModel mod2;
+ QStandardItemModel mod3;
+};
+
+void tst_QConcatenateTablesProxyModel::init()
+{
+ // Prepare some source models to use later on
+ mod.clear();
+ mod.appendRow({ new QStandardItem(QStringLiteral("A")), new QStandardItem(QStringLiteral("B")), new QStandardItem(QStringLiteral("C")) });
+ mod.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3"));
+ mod.setVerticalHeaderLabels(QStringList() << QStringLiteral("One"));
+
+ mod2.clear();
+ mod2.appendRow({ new QStandardItem(QStringLiteral("D")), new QStandardItem(QStringLiteral("E")), new QStandardItem(QStringLiteral("F")) });
+ mod2.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3"));
+ mod2.setVerticalHeaderLabels(QStringList() << QStringLiteral("Two"));
+
+ mod3.clear();
+ mod3.appendRow({ new QStandardItem(QStringLiteral("1")), new QStandardItem(QStringLiteral("2")), new QStandardItem(QStringLiteral("3")) });
+ mod3.appendRow({ new QStandardItem(QStringLiteral("4")), new QStandardItem(QStringLiteral("5")), new QStandardItem(QStringLiteral("6")) });
+}
+
+void tst_QConcatenateTablesProxyModel::shouldAggregateTwoModelsCorrectly()
+{
+ // Given a combining proxy
+ QConcatenateTablesProxyModel pm;
+
+ // When adding two source models
+ pm.addSourceModel(&mod);
+ pm.addSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+
+ // Then the proxy should show 2 rows
+ QCOMPARE(pm.rowCount(), 2);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF"));
+
+ // ... and correct headers
+ QCOMPARE(pm.headerData(0, Qt::Horizontal).toString(), QStringLiteral("H1"));
+ QCOMPARE(pm.headerData(1, Qt::Horizontal).toString(), QStringLiteral("H2"));
+ QCOMPARE(pm.headerData(2, Qt::Horizontal).toString(), QStringLiteral("H3"));
+ QCOMPARE(pm.headerData(0, Qt::Vertical).toString(), QStringLiteral("One"));
+ QCOMPARE(pm.headerData(1, Qt::Vertical).toString(), QStringLiteral("Two"));
+
+ QVERIFY(!pm.canFetchMore(QModelIndex()));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldAggregateThenRemoveTwoEmptyModelsCorrectly()
+{
+ // Given a combining proxy
+ QConcatenateTablesProxyModel pm;
+
+ // When adding two empty models
+ QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
+ QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int)));
+ QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
+ QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int)));
+ QIdentityProxyModel i1, i2;
+ pm.addSourceModel(&i1);
+ pm.addSourceModel(&i2);
+
+ // Then the proxy should still be empty (and no signals emitted)
+ QCOMPARE(pm.rowCount(), 0);
+ QCOMPARE(pm.columnCount(), 0);
+ QCOMPARE(rowATBISpy.count(), 0);
+ QCOMPARE(rowInsertedSpy.count(), 0);
+
+ // When removing the empty models
+ pm.removeSourceModel(&i1);
+ pm.removeSourceModel(&i2);
+
+ // Then the proxy should still be empty (and no signals emitted)
+ QCOMPARE(pm.rowCount(), 0);
+ QCOMPARE(pm.columnCount(), 0);
+ QCOMPARE(rowATBRSpy.count(), 0);
+ QCOMPARE(rowRemovedSpy.count(), 0);
+}
+
+void tst_QConcatenateTablesProxyModel::shouldAggregateTwoEmptyModelsWhichThenGetFilled()
+{
+ // Given a combining proxy with two empty models
+ QConcatenateTablesProxyModel pm;
+ QIdentityProxyModel i1, i2;
+ pm.addSourceModel(&i1);
+ pm.addSourceModel(&i2);
+
+ // When filling them afterwards
+ i1.setSourceModel(&mod);
+ i2.setSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+
+ // Then the proxy should show 2 rows
+ QCOMPARE(pm.rowCount(), 2);
+ QCOMPARE(pm.columnCount(), 3);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF"));
+
+ // ... and correct headers
+ QCOMPARE(pm.headerData(0, Qt::Horizontal).toString(), QStringLiteral("H1"));
+ QCOMPARE(pm.headerData(1, Qt::Horizontal).toString(), QStringLiteral("H2"));
+ QCOMPARE(pm.headerData(2, Qt::Horizontal).toString(), QStringLiteral("H3"));
+ QCOMPARE(pm.headerData(0, Qt::Vertical).toString(), QStringLiteral("One"));
+ QCOMPARE(pm.headerData(1, Qt::Vertical).toString(), QStringLiteral("Two"));
+
+ QVERIFY(!pm.canFetchMore(QModelIndex()));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldHandleDataChanged()
+{
+ // Given two models combined
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ pm.addSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+ QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
+
+ // When a cell in a source model changes
+ mod.item(0, 0)->setData("a", Qt::EditRole);
+
+ // Then the change should be notified to the proxy
+ QCOMPARE(dataChangedSpy.count(), 1);
+ QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0));
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBC"));
+
+ // Same test with the other model
+ mod2.item(0, 2)->setData("f", Qt::EditRole);
+
+ QCOMPARE(dataChangedSpy.count(), 2);
+ QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEf"));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldHandleSetData()
+{
+ // Given two models combined
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ pm.addSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+ QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
+
+ // When changing a cell using setData
+ pm.setData(pm.index(0, 0), "a");
+
+ // Then the change should be notified to the proxy
+ QCOMPARE(dataChangedSpy.count(), 1);
+ QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0));
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBC"));
+
+ // Same test with the other model
+ pm.setData(pm.index(1, 2), "f");
+
+ QCOMPARE(dataChangedSpy.count(), 2);
+ QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEf"));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldHandleSetItemData()
+{
+ // Given two models combined
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ pm.addSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+ QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex)));
+
+ // When changing a cell using setData
+ pm.setItemData(pm.index(0, 0), QMap<int, QVariant>{ std::make_pair<int, QVariant>(Qt::DisplayRole, QStringLiteral("X")),
+ std::make_pair<int, QVariant>(Qt::UserRole, 88) });
+
+ // Then the change should be notified to the proxy
+ QCOMPARE(dataChangedSpy.count(), 1);
+ QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0));
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("XBC"));
+ QCOMPARE(pm.index(0, 0).data(Qt::UserRole).toInt(), 88);
+
+ // Same test with the other model
+ pm.setItemData(pm.index(1, 2), QMap<int, QVariant>{ std::make_pair<int, QVariant>(Qt::DisplayRole, QStringLiteral("Y")),
+ std::make_pair<int, QVariant>(Qt::UserRole, 89) });
+
+ QCOMPARE(dataChangedSpy.count(), 2);
+ QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEY"));
+ QCOMPARE(pm.index(1, 2).data(Qt::UserRole).toInt(), 89);
+}
+
+void tst_QConcatenateTablesProxyModel::shouldHandleRowInsertionAndRemoval()
+{
+ // Given two models combined
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ pm.addSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+ QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
+ QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int)));
+
+ // When a source model inserts a new row
+ QList<QStandardItem *> row;
+ row.append(new QStandardItem(QStringLiteral("1")));
+ row.append(new QStandardItem(QStringLiteral("2")));
+ row.append(new QStandardItem(QStringLiteral("3")));
+ mod2.insertRow(0, row);
+
+ // Then the proxy should notify its users and show changes
+ QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("1,1"));
+ QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("1,1"));
+ QCOMPARE(pm.rowCount(), 3);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF"));
+
+ // When removing that row
+ QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
+ QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int)));
+ mod2.removeRow(0);
+
+ // Then the proxy should notify its users and show changes
+ QCOMPARE(rowATBRSpy.count(), 1);
+ QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1);
+ QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1);
+ QCOMPARE(rowRemovedSpy.count(), 1);
+ QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1);
+ QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1);
+ QCOMPARE(pm.rowCount(), 2);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF"));
+
+ // When removing the last row from mod2
+ rowATBRSpy.clear();
+ rowRemovedSpy.clear();
+ mod2.removeRow(0);
+
+ // Then the proxy should notify its users and show changes
+ QCOMPARE(rowATBRSpy.count(), 1);
+ QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1);
+ QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1);
+ QCOMPARE(rowRemovedSpy.count(), 1);
+ QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1);
+ QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1);
+ QCOMPARE(pm.rowCount(), 1);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldAggregateAnotherModelThenRemoveModels()
+{
+ // Given two models combined, and a third model
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ pm.addSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+
+ QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
+ QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int)));
+
+ // When adding the new source model
+ pm.addSourceModel(&mod3);
+
+ // Then the proxy should notify its users about the two rows inserted
+ QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("2,3"));
+ QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("2,3"));
+ QCOMPARE(pm.rowCount(), 4);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("456"));
+
+ // When removing that source model again
+ QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
+ QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int)));
+ pm.removeSourceModel(&mod3);
+
+ // Then the proxy should notify its users about the row removed
+ QCOMPARE(rowATBRSpy.count(), 1);
+ QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 2);
+ QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 3);
+ QCOMPARE(rowRemovedSpy.count(), 1);
+ QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 2);
+ QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 3);
+ QCOMPARE(pm.rowCount(), 2);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF"));
+
+ // When removing model 2
+ rowATBRSpy.clear();
+ rowRemovedSpy.clear();
+ pm.removeSourceModel(&mod2);
+ QCOMPARE(rowATBRSpy.count(), 1);
+ QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1);
+ QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1);
+ QCOMPARE(rowRemovedSpy.count(), 1);
+ QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1);
+ QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1);
+ QCOMPARE(pm.rowCount(), 1);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+
+ // When removing model 1
+ rowATBRSpy.clear();
+ rowRemovedSpy.clear();
+ pm.removeSourceModel(&mod);
+ QCOMPARE(rowATBRSpy.count(), 1);
+ QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 0);
+ QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 0);
+ QCOMPARE(rowRemovedSpy.count(), 1);
+ QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 0);
+ QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 0);
+ QCOMPARE(pm.rowCount(), 0);
+}
+
+void tst_QConcatenateTablesProxyModel::shouldUseSmallestColumnCount()
+{
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ pm.addSourceModel(&mod2);
+ mod2.setColumnCount(1);
+ pm.addSourceModel(&mod3);
+ QAbstractItemModelTester modelTest(&pm, this);
+
+ QCOMPARE(pm.rowCount(), 4);
+ QCOMPARE(pm.columnCount(), 1);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("A"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("D"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("1"));
+ QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("4"));
+
+ const QModelIndex indexA = pm.mapFromSource(mod.index(0, 0));
+ QVERIFY(indexA.isValid());
+ QCOMPARE(indexA, pm.index(0, 0));
+
+ const QModelIndex indexB = pm.mapFromSource(mod.index(0, 1));
+ QVERIFY(!indexB.isValid());
+
+ const QModelIndex indexD = pm.mapFromSource(mod2.index(0, 0));
+ QVERIFY(indexD.isValid());
+ QCOMPARE(indexD, pm.index(1, 0));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldIncreaseColumnCountWhenRemovingFirstModel()
+{
+ // Given a model with 2 columns and one with 3 columns
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ QAbstractItemModelTester modelTest(&pm, this);
+ mod.setColumnCount(2);
+ pm.addSourceModel(&mod2);
+ QCOMPARE(pm.rowCount(), 2);
+ QCOMPARE(pm.columnCount(), 2);
+
+ QSignalSpy colATBISpy(&pm, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)));
+ QSignalSpy colInsertedSpy(&pm, SIGNAL(columnsInserted(QModelIndex,int,int)));
+ QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
+ QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int)));
+
+ // When removing the first source model
+ pm.removeSourceModel(&mod);
+
+ // Then the proxy should notify its users about the row removed, and the column added
+ QCOMPARE(pm.rowCount(), 1);
+ QCOMPARE(pm.columnCount(), 3);
+ QCOMPARE(rowSpyToText(rowATBRSpy), QStringLiteral("0,0"));
+ QCOMPARE(rowSpyToText(rowRemovedSpy), QStringLiteral("0,0"));
+ QCOMPARE(rowSpyToText(colATBISpy), QStringLiteral("2,2"));
+ QCOMPARE(rowSpyToText(colInsertedSpy), QStringLiteral("2,2"));
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("DEF"));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldHandleColumnInsertionAndRemoval()
+{
+ // Given two models combined, one with 2 columns and one with 3
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ QAbstractItemModelTester modelTest(&pm, this);
+ mod.setColumnCount(2);
+ pm.addSourceModel(&mod2);
+ QSignalSpy colATBISpy(&pm, SIGNAL(columnsAboutToBeInserted(QModelIndex,int,int)));
+ QSignalSpy colInsertedSpy(&pm, SIGNAL(columnsInserted(QModelIndex,int,int)));
+ QSignalSpy colATBRSpy(&pm, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)));
+ QSignalSpy colRemovedSpy(&pm, SIGNAL(columnsRemoved(QModelIndex,int,int)));
+
+ // When the first source model inserts a new column
+ QCOMPARE(mod.columnCount(), 2);
+ mod.setColumnCount(3);
+
+ // Then the proxy should notify its users and show changes
+ QCOMPARE(rowSpyToText(colATBISpy), QStringLiteral("2,2"));
+ QCOMPARE(rowSpyToText(colInsertedSpy), QStringLiteral("2,2"));
+ QCOMPARE(pm.rowCount(), 2);
+ QCOMPARE(pm.columnCount(), 3);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("AB "));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF"));
+
+ // And when removing two columns
+ mod.setColumnCount(1);
+
+ // Then the proxy should notify its users and show changes
+ QCOMPARE(rowSpyToText(colATBRSpy), QStringLiteral("1,2"));
+ QCOMPARE(rowSpyToText(colRemovedSpy), QStringLiteral("1,2"));
+ QCOMPARE(pm.rowCount(), 2);
+ QCOMPARE(pm.columnCount(), 1);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("A"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("D"));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldPropagateLayoutChanged()
+{
+ // Given two source models, the second one being a QSFPM
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ QAbstractItemModelTester modelTest(&pm, this);
+
+ QSortFilterProxyModel qsfpm;
+ qsfpm.setSourceModel(&mod3);
+ pm.addSourceModel(&qsfpm);
+
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("456"));
+
+ // And a selection (row 1)
+ QItemSelectionModel selection(&pm);
+ selection.select(pm.index(1, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows);
+ const QModelIndexList lst = selection.selectedIndexes();
+ QCOMPARE(lst.count(), 3);
+ for (int col = 0; col < lst.count(); ++col) {
+ QCOMPARE(lst.at(col).row(), 1);
+ QCOMPARE(lst.at(col).column(), col);
+ }
+
+ QSignalSpy layoutATBCSpy(&pm, SIGNAL(layoutAboutToBeChanged()));
+ QSignalSpy layoutChangedSpy(&pm, SIGNAL(layoutChanged()));
+
+ // When changing the sorting in the QSFPM
+ qsfpm.sort(0, Qt::DescendingOrder);
+
+ // Then the proxy should emit the layoutChanged signals, and show re-sorted data
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("123"));
+ QCOMPARE(layoutATBCSpy.count(), 1);
+ QCOMPARE(layoutChangedSpy.count(), 1);
+
+ // And the selection should be updated accordingly (it became row 2)
+ const QModelIndexList lstAfter = selection.selectedIndexes();
+ QCOMPARE(lstAfter.count(), 3);
+ for (int col = 0; col < lstAfter.count(); ++col) {
+ QCOMPARE(lstAfter.at(col).row(), 2);
+ QCOMPARE(lstAfter.at(col).column(), col);
+ }
+}
+
+void tst_QConcatenateTablesProxyModel::shouldReactToModelReset()
+{
+ // Given two source models, the second one being a QSFPM
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod);
+ QAbstractItemModelTester modelTest(&pm, this);
+
+ QSortFilterProxyModel qsfpm;
+ qsfpm.setSourceModel(&mod3);
+ pm.addSourceModel(&qsfpm);
+
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("456"));
+ QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
+ QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int)));
+ QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
+ QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int)));
+ QSignalSpy colATBRSpy(&pm, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)));
+ QSignalSpy colRemovedSpy(&pm, SIGNAL(columnsRemoved(QModelIndex,int,int)));
+ QSignalSpy modelATBResetSpy(&pm, SIGNAL(modelAboutToBeReset()));
+ QSignalSpy modelResetSpy(&pm, SIGNAL(modelReset()));
+
+ // When changing the source model of the QSFPM
+ qsfpm.setSourceModel(&mod2);
+
+ // Then the proxy should emit the reset signals, and show the new data
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF"));
+ QCOMPARE(rowATBRSpy.count(), 0);
+ QCOMPARE(rowRemovedSpy.count(), 0);
+ QCOMPARE(rowATBISpy.count(), 0);
+ QCOMPARE(rowInsertedSpy.count(), 0);
+ QCOMPARE(colATBRSpy.count(), 0);
+ QCOMPARE(colRemovedSpy.count(), 0);
+ QCOMPARE(modelATBResetSpy.count(), 1);
+ QCOMPARE(modelResetSpy.count(), 1);
+}
+
+void tst_QConcatenateTablesProxyModel::shouldUpdateColumnsOnModelReset()
+{
+ // Given two source models, the first one being a QSFPM
+ QConcatenateTablesProxyModel pm;
+
+ QSortFilterProxyModel qsfpm;
+ qsfpm.setSourceModel(&mod3);
+ pm.addSourceModel(&qsfpm);
+ pm.addSourceModel(&mod);
+ QAbstractItemModelTester modelTest(&pm, this);
+
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("ABC"));
+
+ // ... and a model with only 2 columns
+ QStandardItemModel mod2Columns;
+ mod2Columns.appendRow({ new QStandardItem(QStringLiteral("W")), new QStandardItem(QStringLiteral("X")) });
+
+ QSignalSpy rowATBRSpy(&pm, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
+ QSignalSpy rowRemovedSpy(&pm, SIGNAL(rowsRemoved(QModelIndex,int,int)));
+ QSignalSpy rowATBISpy(&pm, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
+ QSignalSpy rowInsertedSpy(&pm, SIGNAL(rowsInserted(QModelIndex,int,int)));
+ QSignalSpy colATBRSpy(&pm, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)));
+ QSignalSpy colRemovedSpy(&pm, SIGNAL(columnsRemoved(QModelIndex,int,int)));
+ QSignalSpy modelATBResetSpy(&pm, SIGNAL(modelAboutToBeReset()));
+ QSignalSpy modelResetSpy(&pm, SIGNAL(modelReset()));
+
+ // When changing the source model of the QSFPM
+ qsfpm.setSourceModel(&mod2Columns);
+
+ // Then the proxy should reset, and show the new data
+ QCOMPARE(modelATBResetSpy.count(), 1);
+ QCOMPARE(modelResetSpy.count(), 1);
+ QCOMPARE(rowATBRSpy.count(), 0);
+ QCOMPARE(rowRemovedSpy.count(), 0);
+ QCOMPARE(rowATBISpy.count(), 0);
+ QCOMPARE(rowInsertedSpy.count(), 0);
+ QCOMPARE(colATBRSpy.count(), 0);
+ QCOMPARE(colRemovedSpy.count(), 0);
+
+ QCOMPARE(pm.rowCount(), 2);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("WX"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("AB"));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldPropagateDropOnItem_data()
+{
+ QTest::addColumn<int>("sourceRow");
+ QTest::addColumn<int>("destRow");
+ QTest::addColumn<QString>("expectedResult");
+
+ QTest::newRow("0-3") << 0 << 3 << QStringLiteral("ABCA");
+ QTest::newRow("1-2") << 1 << 2 << QStringLiteral("ABBD");
+ QTest::newRow("2-1") << 2 << 1 << QStringLiteral("ACCD");
+ QTest::newRow("3-0") << 3 << 0 << QStringLiteral("DBCD");
+
+}
+
+void tst_QConcatenateTablesProxyModel::shouldPropagateDropOnItem()
+{
+ // Given two source models who handle drops
+
+ // Note: QStandardItemModel handles drop onto items by inserting child rows,
+ // which is good for QTreeView but not for QTableView or QConcatenateTablesProxyModel.
+ // So we use QStringListModel here instead.
+ QConcatenateTablesProxyModel pm;
+ QStringListModel model1({QStringLiteral("A"), QStringLiteral("B")});
+ QStringListModel model2({QStringLiteral("C"), QStringLiteral("D")});
+ pm.addSourceModel(&model1);
+ pm.addSourceModel(&model2);
+ QAbstractItemModelTester modelTest(&pm, this);
+ QCOMPARE(extractColumnTexts(&pm, 0), QStringLiteral("ABCD"));
+
+ // When dragging one item
+ QFETCH(int, sourceRow);
+ QMimeData* mimeData = pm.mimeData({pm.index(sourceRow, 0)});
+ QVERIFY(mimeData);
+
+ // and dropping onto another item
+ QFETCH(int, destRow);
+ QVERIFY(pm.canDropMimeData(mimeData, Qt::CopyAction, -1, -1, pm.index(destRow, 0)));
+ QVERIFY(pm.dropMimeData(mimeData, Qt::CopyAction, -1, -1, pm.index(destRow, 0)));
+ delete mimeData;
+
+ // Then the result should be as expected
+ QFETCH(QString, expectedResult);
+ QCOMPARE(extractColumnTexts(&pm, 0), expectedResult);
+}
+
+void tst_QConcatenateTablesProxyModel::shouldPropagateDropBetweenItems()
+{
+ // Given two models combined
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod3);
+ pm.addSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+ QCOMPARE(pm.rowCount(), 3);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF"));
+
+ // When dragging the last row
+ QModelIndexList indexes;
+ indexes.reserve(pm.columnCount());
+ for (int col = 0; col < pm.columnCount(); ++col) {
+ indexes.append(pm.index(2, col));
+ }
+ QMimeData* mimeData = pm.mimeData(indexes);
+ QVERIFY(mimeData);
+
+ // and dropping it before row 1
+ const int destRow = 1;
+ QVERIFY(pm.canDropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex()));
+ QVERIFY(pm.dropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex()));
+ delete mimeData;
+
+ // Then a new row should be inserted
+ QCOMPARE(pm.rowCount(), 4);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("456"));
+ QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("DEF"));
+}
+
+void tst_QConcatenateTablesProxyModel::shouldPropagateDropBetweenItemsAtModelBoundary()
+{
+ // Given two models combined
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod3);
+ pm.addSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+ QCOMPARE(pm.rowCount(), 3);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF"));
+
+ // When dragging the first row
+ QModelIndexList indexes;
+ indexes.reserve(pm.columnCount());
+ for (int col = 0; col < pm.columnCount(); ++col) {
+ indexes.append(pm.index(0, col));
+ }
+ QMimeData* mimeData = pm.mimeData(indexes);
+ QVERIFY(mimeData);
+
+ // and dropping it before row 2
+ const int destRow = 2;
+ QVERIFY(pm.canDropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex()));
+ QVERIFY(pm.dropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex()));
+ delete mimeData;
+
+ // Then a new row should be inserted
+ QCOMPARE(pm.rowCount(), 4);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("DEF"));
+
+ // and it should be part of the second model
+ QCOMPARE(mod2.rowCount(), 2);
+}
+
+void tst_QConcatenateTablesProxyModel::shouldPropagateDropAfterLastRow_data()
+{
+ QTest::addColumn<int>("destRow");
+
+ // Dropping after the last row is documented to be done with destRow == -1.
+ QTest::newRow("-1") << -1;
+ // However, sometimes QTreeView calls dropMimeData with destRow == rowCount...
+ // Not sure if that's a bug or not, but let's support it in the model, just in case.
+ QTest::newRow("3") << 3;
+}
+
+void tst_QConcatenateTablesProxyModel::shouldPropagateDropAfterLastRow()
+{
+ QFETCH(int, destRow);
+
+ // Given two models combined
+ QConcatenateTablesProxyModel pm;
+ pm.addSourceModel(&mod3);
+ pm.addSourceModel(&mod2);
+ QAbstractItemModelTester modelTest(&pm, this);
+ QCOMPARE(pm.rowCount(), 3);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF"));
+
+ // When dragging the second row
+ QModelIndexList indexes;
+ indexes.reserve(pm.columnCount());
+ for (int col = 0; col < pm.columnCount(); ++col) {
+ indexes.append(pm.index(1, col));
+ }
+ QMimeData* mimeData = pm.mimeData(indexes);
+ QVERIFY(mimeData);
+
+ // and dropping it after the last row
+ QVERIFY(pm.canDropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex()));
+ QVERIFY(pm.dropMimeData(mimeData, Qt::CopyAction, destRow, 0, QModelIndex()));
+ delete mimeData;
+
+ // Then a new row should be inserted at the end
+ QCOMPARE(pm.rowCount(), 4);
+ QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("123"));
+ QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456"));
+ QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("DEF"));
+ QCOMPARE(extractRowTexts(&pm, 3), QStringLiteral("456"));
+
+}
+
+QTEST_GUILESS_MAIN(tst_QConcatenateTablesProxyModel)
+
+#include "tst_qconcatenatetablesproxymodel.moc"
diff --git a/tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp b/tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp
index 6ea7a38137..dbc7173028 100644
--- a/tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp
+++ b/tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp
@@ -251,7 +251,7 @@ QModelIndex ModelsToTest::populateTestArea(QAbstractItemModel *model)
QString val = xval + QString::number(y) + QString::number(i);
QModelIndex index = model->index(x, y, parent);
model->setData(index, val);
- model->setData(index, blue, Qt::TextColorRole);
+ model->setData(index, blue, Qt::ForegroundRole);
}
}
*/
@@ -276,7 +276,7 @@ QModelIndex ModelsToTest::populateTestArea(QAbstractItemModel *model)
QString val = xval + QString::number(y) + QString::number(i);
QModelIndex index = realModel->index(x, y, parent);
realModel->setData(index, val);
- realModel->setData(index, blue, Qt::TextColorRole);
+ realModel->setData(index, blue, Qt::ForegroundRole);
}
}
*/
diff --git a/tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp b/tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp
index 7cd220e684..da13b9f33f 100644
--- a/tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp
+++ b/tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp
@@ -576,12 +576,12 @@ void tst_QItemModel::data()
alignment == Qt::AlignJustify);
}
- QVariant colorVariant = currentModel->data(currentModel->index(0,0), Qt::BackgroundColorRole);
+ QVariant colorVariant = currentModel->data(currentModel->index(0,0), Qt::BackgroundRole);
if (colorVariant.isValid()) {
QVERIFY(colorVariant.canConvert<QColor>());
}
- colorVariant = currentModel->data(currentModel->index(0,0), Qt::TextColorRole);
+ colorVariant = currentModel->data(currentModel->index(0,0), Qt::ForegroundRole);
if (colorVariant.isValid()) {
QVERIFY(colorVariant.canConvert<QColor>());
}
diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp b/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp
index 1b40e77648..0b8686560c 100644
--- a/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp
+++ b/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp
@@ -84,8 +84,116 @@ private slots:
void setData_emits_on_change_only();
void supportedDragDropActions();
+
+ void moveRows_data();
+ void moveRows();
+ void moveRowsInvalid_data();
+ void moveRowsInvalid();
+
+ void itemData();
+ void setItemData();
};
+void tst_QStringListModel::moveRowsInvalid_data()
+{
+ QTest::addColumn<QStringListModel*>("baseModel");
+ QTest::addColumn<QModelIndex>("startParent");
+ QTest::addColumn<int>("startRow");
+ QTest::addColumn<int>("count");
+ QTest::addColumn<QModelIndex>("destinationParent");
+ QTest::addColumn<int>("destination");
+
+ QStringListModel* tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("destination_equal_source") << tempModel << QModelIndex() << 0 << 1 << QModelIndex() << 1;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("count_equal_0") << tempModel << QModelIndex() << 0 << 0 << QModelIndex() << 2;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("move_child") << tempModel << tempModel->index(0, 0) << 0 << 1 << QModelIndex() << 2;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("move_to_child") << tempModel << QModelIndex() << 0 << 1 << tempModel->index(0, 0) << 2;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("negative_count") << tempModel << QModelIndex() << 0 << -1 << QModelIndex() << 2;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("negative_source_row") << tempModel << QModelIndex() << -1 << 1 << QModelIndex() << 2;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("negative_destination_row") << tempModel << QModelIndex() << 0 << 1 << QModelIndex() << -1;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("source_row_equal_rowCount") << tempModel << QModelIndex() << tempModel->rowCount() << 1 << QModelIndex() << 1;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("destination_row_greater_rowCount") << tempModel << QModelIndex() << 0 << 1 << QModelIndex() << tempModel->rowCount() + 1;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("move_row_within_source_range") << tempModel << QModelIndex() << 0 << 3 << QModelIndex() << 2;
+ tempModel = new QStringListModel(QStringList{"A", "B", "C", "D", "E", "F"}, this);
+ QTest::addRow("destination_row_before_0") << tempModel << QModelIndex() << 1 << 1 << QModelIndex() << 0;
+}
+
+void tst_QStringListModel::moveRowsInvalid()
+{
+ QFETCH(QStringListModel* const, baseModel);
+ QFETCH(const QModelIndex, startParent);
+ QFETCH(const int, startRow);
+ QFETCH(const int, count);
+ QFETCH(const QModelIndex, destinationParent);
+ QFETCH(const int, destination);
+
+ QSignalSpy rowMovedSpy(baseModel, &QAbstractItemModel::rowsMoved);
+ QSignalSpy rowAboutMovedSpy(baseModel, &QAbstractItemModel::rowsAboutToBeMoved);
+ QVERIFY(rowMovedSpy.isValid());
+ QVERIFY(rowAboutMovedSpy.isValid());
+ QVERIFY(!baseModel->moveRows(startParent, startRow, count, destinationParent, destination));
+ QCOMPARE(rowMovedSpy.size(), 0);
+ QCOMPARE(rowAboutMovedSpy.size(), 0);
+ delete baseModel;
+}
+
+void tst_QStringListModel::moveRows_data()
+{
+ QTest::addColumn<int>("startRow");
+ QTest::addColumn<int>("count");
+ QTest::addColumn<int>("destination");
+ QTest::addColumn<QStringList>("expected");
+
+ QTest::newRow("1_Item_from_top_to_middle") << 0 << 1 << 3 << QStringList{"B", "C", "A", "D", "E", "F"};
+ QTest::newRow("1_Item_from_top_to_bottom") << 0 << 1 << 6 << QStringList{"B", "C", "D", "E", "F", "A"};
+ QTest::newRow("1_Item_from_middle_to_top") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"};
+ QTest::newRow("1_Item_from_bottom_to_middle") << 5 << 1 << 3 << QStringList{"A", "B", "F", "C", "D", "E"};
+ QTest::newRow("1_Item_from_bottom to_top") << 5 << 1 << 1 << QStringList{"F", "A", "B", "C", "D", "E"};
+ QTest::newRow("1_Item_from_middle_to_bottom") << 2 << 1 << 6 << QStringList{"A", "B", "D", "E", "F", "C"};
+ QTest::newRow("1_Item_from_middle_to_middle_before") << 2 << 1 << 1 << QStringList{"C", "A", "B", "D", "E", "F"};
+ QTest::newRow("1_Item_from_middle_to_middle_after") << 2 << 1 << 4 << QStringList{"A", "B", "D", "C", "E", "F"};
+
+ QTest::newRow("2_Items_from_top_to_middle") << 0 << 2 << 3 << QStringList{"C", "A", "B", "D", "E", "F"};
+ QTest::newRow("2_Items_from_top_to_bottom") << 0 << 2 << 6 << QStringList{"C", "D", "E", "F", "A", "B"};
+ QTest::newRow("2_Items_from_middle_to_top") << 2 << 2 << 1 << QStringList{"C", "D", "A", "B", "E", "F"};
+ QTest::newRow("2_Items_from_bottom_to_middle") << 4 << 2 << 3 << QStringList{"A", "B", "E", "F", "C", "D"};
+ QTest::newRow("2_Items_from_bottom_to_top") << 4 << 2 << 1 << QStringList{"E", "F", "A", "B", "C", "D"};
+ QTest::newRow("2_Items_from_middle_to_bottom") << 2 << 2 << 6 << QStringList{"A", "B", "E", "F", "C", "D"};
+ QTest::newRow("2_Items_from_middle_to_middle_before") << 3 << 2 << 2 << QStringList{"A", "D", "E", "B", "C", "F"};
+ QTest::newRow("2_Items_from_middle_to_middle_after") << 1 << 2 << 5 << QStringList{"A", "D", "E", "B", "C", "F"};
+}
+
+void tst_QStringListModel::moveRows()
+{
+ QFETCH(const int, startRow);
+ QFETCH(const int, count);
+ QFETCH(const int, destination);
+ QFETCH(const QStringList, expected);
+ QStringListModel baseModel(QStringList{"A", "B", "C", "D", "E", "F"});
+ QSignalSpy rowMovedSpy(&baseModel, &QAbstractItemModel::rowsMoved);
+ QSignalSpy rowAboutMovedSpy(&baseModel, &QAbstractItemModel::rowsAboutToBeMoved);
+ QVERIFY(baseModel.moveRows(QModelIndex(), startRow, count, QModelIndex(), destination));
+ QCOMPARE(baseModel.stringList(), expected);
+ QCOMPARE(rowMovedSpy.size(), 1);
+ QCOMPARE(rowAboutMovedSpy.size(), 1);
+ for (const QList<QVariant> &signalArgs : {rowMovedSpy.first(), rowAboutMovedSpy.first()}){
+ QVERIFY(!signalArgs.at(0).value<QModelIndex>().isValid());
+ QCOMPARE(signalArgs.at(1).toInt(), startRow);
+ QCOMPARE(signalArgs.at(2).toInt(), startRow + count - 1);
+ QVERIFY(!signalArgs.at(3).value<QModelIndex>().isValid());
+ QCOMPARE(signalArgs.at(4).toInt(), destination);
+ }
+}
+
void tst_QStringListModel::rowsAboutToBeRemoved_rowsRemoved_data()
{
QTest::addColumn<QStringList>("input");
@@ -248,6 +356,74 @@ void tst_QStringListModel::setData_emits_both_roles()
expected);
}
+void tst_QStringListModel::itemData()
+{
+ QStringListModel testModel{ QStringList {
+ QStringLiteral("One"),
+ QStringLiteral("Two"),
+ QStringLiteral("Three"),
+ QStringLiteral("Four"),
+ QStringLiteral("Five")
+ }};
+ QMap<int, QVariant> compareMap;
+ QCOMPARE(testModel.itemData(QModelIndex()), compareMap);
+ compareMap.insert(Qt::DisplayRole, QStringLiteral("Two"));
+ compareMap.insert(Qt::EditRole, QStringLiteral("Two"));
+ QCOMPARE(testModel.itemData(testModel.index(1, 0)), compareMap);
+}
+
+void tst_QStringListModel::setItemData()
+{
+ QStringListModel testModel{ QStringList {
+ QStringLiteral("One"),
+ QStringLiteral("Two"),
+ QStringLiteral("Three"),
+ QStringLiteral("Four"),
+ QStringLiteral("Five")
+ }};
+ QSignalSpy dataChangedSpy(&testModel, &QAbstractItemModel::dataChanged);
+ QModelIndex changeIndex = testModel.index(1, 0);
+ const QVector<int> changeRoles{Qt::DisplayRole, Qt::EditRole};
+ const QString changedString("Changed");
+ QMap<int, QVariant> newItemData{std::make_pair<int>(Qt::DisplayRole, changedString)};
+ // invalid index does nothing and returns false
+ QVERIFY(!testModel.setItemData(QModelIndex(), newItemData));
+ // valid data is set, return value is true and dataChanged is emitted once
+ QVERIFY(testModel.setItemData(changeIndex, newItemData));
+ QCOMPARE(changeIndex.data(Qt::DisplayRole).toString(), changedString);
+ QCOMPARE(changeIndex.data(Qt::EditRole).toString(), changedString);
+ QCOMPARE(dataChangedSpy.size(), 1);
+ QVariantList dataChangedArguments = dataChangedSpy.takeFirst();
+ QCOMPARE(dataChangedArguments.at(0).value<QModelIndex>(), changeIndex);
+ QCOMPARE(dataChangedArguments.at(1).value<QModelIndex>(), changeIndex);
+ QCOMPARE(dataChangedArguments.at(2).value<QVector<int> >(), changeRoles);
+ // Unsupported roles do nothing return false
+ newItemData.clear();
+ newItemData.insert(Qt::UserRole, changedString);
+ QVERIFY(!testModel.setItemData(changeIndex, newItemData));
+ QCOMPARE(dataChangedSpy.size(), 0);
+ // If some but not all the roles are supported it returns false and does nothing
+ newItemData.insert(Qt::EditRole, changedString);
+ changeIndex = testModel.index(2, 0);
+ QVERIFY(!testModel.setItemData(changeIndex, newItemData));
+ QCOMPARE(changeIndex.data(Qt::DisplayRole).toString(), QStringLiteral("Three"));
+ QCOMPARE(changeIndex.data(Qt::EditRole).toString(), QStringLiteral("Three"));
+ QCOMPARE(dataChangedSpy.size(), 0);
+ // Qt::EditRole and Qt::DisplayRole are both set, Qt::EditRole takes precedence
+ newItemData.clear();
+ newItemData.insert(Qt::EditRole, changedString);
+ newItemData.insert(Qt::DisplayRole, QStringLiteral("Ignored"));
+ changeIndex = testModel.index(3, 0);
+ QVERIFY(testModel.setItemData(changeIndex, newItemData));
+ QCOMPARE(changeIndex.data(Qt::DisplayRole).toString(), changedString);
+ QCOMPARE(changeIndex.data(Qt::EditRole).toString(), changedString);
+ QCOMPARE(dataChangedSpy.size(), 1);
+ dataChangedArguments = dataChangedSpy.takeFirst();
+ QCOMPARE(dataChangedArguments.at(0).value<QModelIndex>(), changeIndex);
+ QCOMPARE(dataChangedArguments.at(1).value<QModelIndex>(), changeIndex);
+ QCOMPARE(dataChangedArguments.at(2).value<QVector<int> >(), changeRoles);
+}
+
void tst_QStringListModel::setData_emits_on_change_only()
{
QStringListModel model(QStringList{QStringLiteral("one"), QStringLiteral("two")});
diff --git a/tests/auto/corelib/itemmodels/qtransposeproxymodel/qtransposeproxymodel.pro b/tests/auto/corelib/itemmodels/qtransposeproxymodel/qtransposeproxymodel.pro
new file mode 100644
index 0000000000..3834add115
--- /dev/null
+++ b/tests/auto/corelib/itemmodels/qtransposeproxymodel/qtransposeproxymodel.pro
@@ -0,0 +1,6 @@
+CONFIG += testcase
+TARGET = tst_qtransposeproxymodel
+QT = core gui testlib
+
+SOURCES = tst_qtransposeproxymodel.cpp
+
diff --git a/tests/auto/corelib/itemmodels/qtransposeproxymodel/tst_qtransposeproxymodel.cpp b/tests/auto/corelib/itemmodels/qtransposeproxymodel/tst_qtransposeproxymodel.cpp
new file mode 100644
index 0000000000..a30ac46571
--- /dev/null
+++ b/tests/auto/corelib/itemmodels/qtransposeproxymodel/tst_qtransposeproxymodel.cpp
@@ -0,0 +1,915 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QTest>
+#include <QSignalSpy>
+#include <QStandardItemModel>
+#include <QStringListModel>
+#include <QAbstractItemModelTester>
+#include <random>
+
+#include <qtransposeproxymodel.h>
+
+class tst_QTransposeProxyModel : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void index();
+ void data();
+ void setData_data();
+ void setData();
+ void parent();
+ void mapToSource();
+ void mapFromSource();
+ void basicTest_data();
+ void basicTest();
+ void sort();
+ void insertRowBase_data();
+ void insertRowBase();
+ void insertColumnBase_data();
+ void insertColumnBase();
+ void insertColumnProxy_data();
+ void insertColumnProxy();
+ void insertRowProxy_data();
+ void insertRowProxy();
+ void removeRowBase_data();
+ void removeRowBase();
+ void removeColumnBase_data();
+ void removeColumnBase();
+ void removeColumnProxy_data();
+ void removeColumnProxy();
+ void removeRowProxy_data();
+ void removeRowProxy();
+ void headerData();
+ void setHeaderData();
+ void span();
+ void itemData();
+ void setItemData();
+ void moveRowsBase();
+ void moveColumnsProxy();
+private:
+ void testTransposed(
+ const QAbstractItemModel *const baseModel,
+ const QAbstractItemModel *const transposed,
+ const QModelIndex &baseParent = QModelIndex(),
+ const QModelIndex &transposedParent = QModelIndex()
+ );
+ QAbstractItemModel *createListModel(QObject *parent);
+ QAbstractItemModel *createTableModel(QObject *parent);
+ QAbstractItemModel *createTreeModel(QObject *parent);
+};
+
+QAbstractItemModel *tst_QTransposeProxyModel::createListModel(QObject *parent)
+{
+ QStringList sequence;
+ sequence.reserve(10);
+ for (int i = 0; i < 10; ++i)
+ sequence.append(QString::number(i));
+ return new QStringListModel(sequence, parent);
+}
+
+QAbstractItemModel *tst_QTransposeProxyModel::createTableModel(QObject *parent)
+{
+ QAbstractItemModel *model = new QStandardItemModel(parent);
+ model->insertRows(0, 5);
+ model->insertColumns(0, 4);
+ for (int i = 0; i < model->rowCount(); ++i) {
+ for (int j = 0; j < model->columnCount(); ++j) {
+ model->setData(model->index(i, j), QStringLiteral("%1,%2").arg(i).arg(j), Qt::EditRole);
+ model->setData(model->index(i, j), i, Qt::UserRole);
+ model->setData(model->index(i, j), j, Qt::UserRole + 1);
+ }
+ }
+ return model;
+}
+
+QAbstractItemModel *tst_QTransposeProxyModel::createTreeModel(QObject *parent)
+{
+ QAbstractItemModel *model = new QStandardItemModel(parent);
+ model->insertRows(0, 5);
+ model->insertColumns(0, 4);
+ for (int i = 0; i < model->rowCount(); ++i) {
+ for (int j = 0; j < model->columnCount(); ++j) {
+ const QModelIndex parIdx = model->index(i, j);
+ model->setData(parIdx, QStringLiteral("%1,%2").arg(i).arg(j), Qt::EditRole);
+ model->setData(parIdx, i, Qt::UserRole);
+ model->setData(parIdx, j, Qt::UserRole + 1);
+ model->insertRows(0, 3, parIdx);
+ model->insertColumns(0, 2, parIdx);
+ for (int h = 0; h < model->rowCount(parIdx); ++h) {
+ for (int k = 0; k < model->columnCount(parIdx); ++k) {
+ const QModelIndex childIdx = model->index(h, k, parIdx);
+ model->setData(childIdx, QStringLiteral("%1,%2,%3,%4").arg(i).arg(j).arg(h).arg(k), Qt::EditRole);
+ model->setData(childIdx, i, Qt::UserRole);
+ model->setData(childIdx, j, Qt::UserRole + 1);
+ model->setData(childIdx, h, Qt::UserRole + 2);
+ model->setData(childIdx, k, Qt::UserRole + 3);
+ }
+ }
+ }
+ }
+ return model;
+}
+
+void tst_QTransposeProxyModel::testTransposed(
+ const QAbstractItemModel *const baseModel,
+ const QAbstractItemModel *const transposed,
+ const QModelIndex &baseParent,
+ const QModelIndex &transposedParent
+)
+{
+ QCOMPARE(transposed->hasChildren(transposedParent), baseModel->hasChildren(baseParent));
+ QCOMPARE(transposed->columnCount(transposedParent), baseModel->rowCount(baseParent));
+ QCOMPARE(transposed->rowCount(transposedParent), baseModel->columnCount(baseParent));
+ for (int i = 0, maxRow = baseModel->rowCount(baseParent); i < maxRow; ++i) {
+ for (int j = 0, maxCol = baseModel->columnCount(baseParent); j < maxCol; ++j) {
+ const QModelIndex baseIdx = baseModel->index(i, j, baseParent);
+ const QModelIndex transIdx = transposed->index(j, i, transposedParent);
+ QCOMPARE(transIdx.data(), baseIdx.data());
+ QCOMPARE(transIdx.data(Qt::UserRole), baseIdx.data(Qt::UserRole));
+ QCOMPARE(transIdx.data(Qt::UserRole + 1), baseIdx.data(Qt::UserRole + 1));
+ QCOMPARE(transIdx.data(Qt::UserRole + 2), baseIdx.data(Qt::UserRole + 2));
+ QCOMPARE(transIdx.data(Qt::UserRole + 3), baseIdx.data(Qt::UserRole + 3));
+ if (baseModel->hasChildren(baseIdx)) {
+ testTransposed(baseModel, transposed, baseIdx, transIdx);
+ }
+ }
+ }
+}
+
+void tst_QTransposeProxyModel::initTestCase()
+{
+ qRegisterMetaType<QList<QPersistentModelIndex> >();
+ qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>();
+}
+
+void tst_QTransposeProxyModel::index()
+{
+ QAbstractItemModel *model = createTreeModel(this);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ QVERIFY(!proxy.index(0, -1).isValid());
+ QVERIFY(!proxy.index(0, -1).isValid());
+ QVERIFY(!proxy.index(-1, -1).isValid());
+ QVERIFY(!proxy.index(0, proxy.columnCount()).isValid());
+ QVERIFY(!proxy.index(proxy.rowCount(), 0).isValid());
+ QVERIFY(!proxy.index(proxy.rowCount(), proxy.columnCount()).isValid());
+ QModelIndex tempIdx = proxy.index(0, 1);
+ QVERIFY(tempIdx.isValid());
+ QCOMPARE(tempIdx.row(), 0);
+ QCOMPARE(tempIdx.column(), 1);
+ tempIdx = proxy.index(0, 1, tempIdx);
+ QVERIFY(tempIdx.isValid());
+ QCOMPARE(tempIdx.row(), 0);
+ QCOMPARE(tempIdx.column(), 1);
+ delete model;
+}
+
+void tst_QTransposeProxyModel::data()
+{
+ QStringListModel model{QStringList{"A", "B"}};
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(&model);
+ QCOMPARE(proxy.index(0, 1).data().toString(), QStringLiteral("B"));
+}
+
+void tst_QTransposeProxyModel::parent()
+{
+ QAbstractItemModel *model = createTreeModel(this);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ const QModelIndex parentIdx = proxy.index(0, 0);
+ const QModelIndex childIdx = proxy.index(0, 0, parentIdx);
+ QVERIFY(parentIdx.isValid());
+ QVERIFY(childIdx.isValid());
+ QCOMPARE(childIdx.parent(), parentIdx);
+ delete model;
+}
+
+void tst_QTransposeProxyModel::mapToSource()
+{
+ QAbstractItemModel *model = createTreeModel(this);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ QVERIFY(!proxy.mapToSource(QModelIndex()).isValid());
+ QCOMPARE(proxy.mapToSource(proxy.index(0, 0)), model->index(0, 0));
+ QCOMPARE(proxy.mapToSource(proxy.index(1, 0)), model->index(0, 1));
+ QCOMPARE(proxy.mapToSource(proxy.index(0, 1)), model->index(1, 0));
+ const QModelIndex proxyParent = proxy.index(1, 0);
+ const QModelIndex sourceParent = model->index(0, 1);
+ QCOMPARE(proxy.mapToSource(proxy.index(0, 0, proxyParent)), model->index(0, 0, sourceParent));
+ QCOMPARE(proxy.mapToSource(proxy.index(1, 0, proxyParent)), model->index(0, 1, sourceParent));
+ QCOMPARE(proxy.mapToSource(proxy.index(0, 1, proxyParent)), model->index(1, 0, sourceParent));
+ delete model;
+}
+
+void tst_QTransposeProxyModel::mapFromSource()
+{
+ QAbstractItemModel *model = createTreeModel(this);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ QVERIFY(!proxy.mapFromSource(QModelIndex()).isValid());
+ QCOMPARE(proxy.mapFromSource(model->index(0, 0)), proxy.index(0, 0));
+ QCOMPARE(proxy.mapFromSource(model->index(0, 1)), proxy.index(1, 0));
+ QCOMPARE(proxy.mapFromSource(model->index(1, 0)), proxy.index(0, 1));
+ const QModelIndex proxyParent = proxy.index(1, 0);
+ const QModelIndex sourceParent = model->index(0, 1);
+ QCOMPARE(proxy.mapToSource(proxy.index(0, 0, proxyParent)), model->index(0, 0, sourceParent));
+ QCOMPARE(proxy.mapFromSource(model->index(1, 0, sourceParent)), proxy.index(0, 1, proxyParent));
+ QCOMPARE(proxy.mapFromSource(model->index(0, 1, sourceParent)), proxy.index(1, 0, proxyParent));
+ delete model;
+}
+
+void tst_QTransposeProxyModel::basicTest_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::newRow("List") << createListModel(this);
+ QTest::newRow("Table") << createTableModel(this);
+ QTest::newRow("Tree") << createTreeModel(this);
+}
+
+void tst_QTransposeProxyModel::basicTest()
+{
+ QFETCH(QAbstractItemModel *, model);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ testTransposed(model, &proxy);
+ delete model;
+}
+
+void tst_QTransposeProxyModel::sort()
+{
+ QStringList sequence;
+ sequence.reserve(100);
+ for (int i = 0; i < 100; ++i)
+ sequence.append(QStringLiteral("%1").arg(i, 3, 10, QLatin1Char('0')));
+ std::shuffle(sequence.begin(), sequence.end(), std::mt19937(88));
+ const QString firstItemBeforeSort = sequence.first();
+ QStringListModel baseModel(sequence);
+ QTransposeProxyModel proxyModel;
+ new QAbstractItemModelTester(&proxyModel, &proxyModel);
+ proxyModel.setSourceModel(&baseModel);
+ QSignalSpy layoutChangedSpy(&proxyModel, &QAbstractItemModel::layoutChanged);
+ QVERIFY(layoutChangedSpy.isValid());
+ QSignalSpy layoutAboutToBeChangedSpy(&proxyModel, &QAbstractItemModel::layoutAboutToBeChanged);
+ QVERIFY(layoutAboutToBeChangedSpy.isValid());
+ QPersistentModelIndex firstIndexBeforeSort = proxyModel.index(0, 0);
+ baseModel.sort(0, Qt::AscendingOrder);
+ QCOMPARE(layoutChangedSpy.count(), 1);
+ QCOMPARE(layoutAboutToBeChangedSpy.count(), 1);
+ QCOMPARE(layoutChangedSpy.takeFirst().at(1).toInt(), int(QAbstractItemModel::HorizontalSortHint));
+ QCOMPARE(firstIndexBeforeSort.data().toString(), firstItemBeforeSort);
+ for (int i = 0; i < 100; ++i)
+ QCOMPARE(proxyModel.index(0, i).data().toInt(), i);
+}
+
+void tst_QTransposeProxyModel::removeColumnBase_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::addColumn<QModelIndex>("parent");
+ QTest::newRow("Table") << createTableModel(this) << QModelIndex();
+ QTest::newRow("Tree_Root_Item") << createTreeModel(this) << QModelIndex();
+ QAbstractItemModel *model = createTreeModel(this);
+ QTest::newRow("Tree_Child_Item") << model << model->index(0, 0);
+}
+
+void tst_QTransposeProxyModel::removeColumnBase()
+{
+ QFETCH(QAbstractItemModel * const, model);
+ QFETCH(const QModelIndex, parent);
+ QTransposeProxyModel proxy;
+ QSignalSpy rowRemoveSpy(&proxy, &QAbstractItemModel::rowsRemoved);
+ QVERIFY(rowRemoveSpy.isValid());
+ QSignalSpy rowAboutToBeRemoveSpy(&proxy, &QAbstractItemModel::rowsAboutToBeRemoved);
+ QVERIFY(rowAboutToBeRemoveSpy.isValid());
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ const int oldRowCount = proxy.rowCount(proxy.mapFromSource(parent));
+ const QVariant expectedNewVal = model->index(0, 2, parent).data();
+ QVERIFY(model->removeColumn(1, parent));
+ QCOMPARE(proxy.rowCount(proxy.mapFromSource(parent)), oldRowCount - 1);
+ QCOMPARE(proxy.index(1, 0, proxy.mapFromSource(parent)).data(), expectedNewVal);
+ QCOMPARE(rowRemoveSpy.count(), 1);
+ QCOMPARE(rowAboutToBeRemoveSpy.count(), 1);
+ for (const auto &spyArgs : {rowRemoveSpy.takeFirst(),
+ rowAboutToBeRemoveSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxy.mapFromSource(parent));
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ delete model;
+}
+
+void tst_QTransposeProxyModel::insertColumnBase_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::addColumn<QModelIndex>("parent");
+ QTest::newRow("Table") << createTableModel(this) << QModelIndex();
+ QTest::newRow("Tree_Root_Item") << createTreeModel(this) << QModelIndex();
+ QAbstractItemModel *model = createTreeModel(this);
+ QTest::newRow("Tree_Child_Item") << model << model->index(0, 0);
+}
+
+void tst_QTransposeProxyModel::insertColumnBase()
+{
+ QFETCH(QAbstractItemModel * const, model);
+ QFETCH(const QModelIndex, parent);
+ QTransposeProxyModel proxy;
+ QSignalSpy rowInsertSpy(&proxy, &QAbstractItemModel::rowsInserted);
+ QVERIFY(rowInsertSpy.isValid());
+ QSignalSpy rowAboutToBeInsertSpy(&proxy, &QAbstractItemModel::rowsAboutToBeInserted);
+ QVERIFY(rowAboutToBeInsertSpy.isValid());
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ const int oldRowCount = proxy.rowCount(proxy.mapFromSource(parent));
+ QVERIFY(model->insertColumn(1, parent));
+ QCOMPARE(proxy.rowCount(proxy.mapFromSource(parent)), oldRowCount + 1);
+ QVERIFY(!proxy.index(1, 0, proxy.mapFromSource(parent)).data().isValid());
+ QCOMPARE(rowInsertSpy.count(), 1);
+ QCOMPARE(rowAboutToBeInsertSpy.count(), 1);
+ for (const auto &spyArgs : {rowInsertSpy.takeFirst(),
+ rowAboutToBeInsertSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxy.mapFromSource(parent));
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ delete model;
+}
+
+void tst_QTransposeProxyModel::removeRowBase_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::addColumn<QModelIndex>("parent");
+ QTest::newRow("List") << createListModel(this) << QModelIndex();
+ QTest::newRow("Table") << createTableModel(this) << QModelIndex();
+ QTest::newRow("Tree_Root_Item") << createTreeModel(this) << QModelIndex();
+ QAbstractItemModel *model = createTreeModel(this);
+ QTest::newRow("Tree_Child_Item") << model << model->index(0, 0);
+}
+
+void tst_QTransposeProxyModel::removeRowBase()
+{
+ QFETCH(QAbstractItemModel * const, model);
+ QFETCH(const QModelIndex, parent);
+ QTransposeProxyModel proxy;
+ QSignalSpy columnsRemoveSpy(&proxy, &QAbstractItemModel::columnsRemoved);
+ QVERIFY(columnsRemoveSpy.isValid());
+ QSignalSpy columnsAboutToBeRemoveSpy(&proxy, &QAbstractItemModel::columnsAboutToBeRemoved);
+ QVERIFY(columnsAboutToBeRemoveSpy.isValid());
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ const int oldColCount = proxy.columnCount(proxy.mapFromSource(parent));
+ const QVariant expectedNewVal = model->index(2, 0, parent).data();
+ QVERIFY(model->removeRow(1, parent));
+ QCOMPARE(proxy.columnCount(proxy.mapFromSource(parent)), oldColCount - 1);
+ QCOMPARE(proxy.index(0, 1, proxy.mapFromSource(parent)).data(), expectedNewVal);
+ QCOMPARE(columnsRemoveSpy.count(), 1);
+ QCOMPARE(columnsAboutToBeRemoveSpy.count(), 1);
+ for (const auto &spyArgs : {columnsRemoveSpy.takeFirst(),
+ columnsAboutToBeRemoveSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxy.mapFromSource(parent));
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ delete model;
+}
+
+void tst_QTransposeProxyModel::insertRowBase_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::addColumn<QModelIndex>("parent");
+ QTest::newRow("List") << createListModel(this) << QModelIndex();
+ QTest::newRow("Table") << createTableModel(this) << QModelIndex();
+ QTest::newRow("Tree_Root_Item") << createTreeModel(this) << QModelIndex();
+ QAbstractItemModel *model = createTreeModel(this);
+ QTest::newRow("Tree_Child_Item") << model << model->index(0, 0);
+}
+
+void tst_QTransposeProxyModel::insertRowBase()
+{
+ QFETCH(QAbstractItemModel * const, model);
+ QFETCH(const QModelIndex, parent);
+ QTransposeProxyModel proxy;
+ QSignalSpy columnsInsertSpy(&proxy, &QAbstractItemModel::columnsInserted);
+ QVERIFY(columnsInsertSpy.isValid());
+ QSignalSpy columnsAboutToBeInsertSpy(&proxy, &QAbstractItemModel::columnsAboutToBeInserted);
+ QVERIFY(columnsAboutToBeInsertSpy.isValid());
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ const int oldColCount = proxy.columnCount(proxy.mapFromSource(parent));
+ QVERIFY(model->insertRow(1, parent));
+ QCOMPARE(proxy.columnCount(proxy.mapFromSource(parent)), oldColCount + 1);
+ QVERIFY(proxy.index(0, 1, proxy.mapFromSource(parent)).data().isNull());
+ QCOMPARE(columnsInsertSpy.count(), 1);
+ QCOMPARE(columnsAboutToBeInsertSpy.count(), 1);
+ for (const auto &spyArgs : {columnsInsertSpy.takeFirst(),
+ columnsAboutToBeInsertSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxy.mapFromSource(parent));
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ delete model;
+}
+
+void tst_QTransposeProxyModel::removeColumnProxy_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::addColumn<bool>("rootItem");
+ QTest::newRow("List") << createListModel(this) << true;
+ QTest::newRow("Table") << createTableModel(this) << true;
+ QTest::newRow("Tree_Root_Item") << createTreeModel(this) << true;
+ QTest::newRow("Tree_Child_Item") << createTreeModel(this) << false;
+}
+
+void tst_QTransposeProxyModel::removeColumnProxy()
+{
+ QFETCH(QAbstractItemModel *, model);
+ QFETCH(bool, rootItem);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ QSignalSpy columnsRemoveSpy(&proxy, &QAbstractItemModel::columnsRemoved);
+ QVERIFY(columnsRemoveSpy.isValid());
+ QSignalSpy columnsAboutToBeRemoveSpy(&proxy, &QAbstractItemModel::columnsAboutToBeRemoved);
+ QVERIFY(columnsAboutToBeRemoveSpy.isValid());
+ QSignalSpy rowsRemoveSpy(model, &QAbstractItemModel::rowsRemoved);
+ QVERIFY(rowsRemoveSpy.isValid());
+ QSignalSpy rowsAboutToBeRemoveSpy(model, &QAbstractItemModel::rowsAboutToBeRemoved);
+ QVERIFY(rowsAboutToBeRemoveSpy.isValid());
+ proxy.setSourceModel(model);
+ const QModelIndex proxyParent = rootItem ? QModelIndex() : proxy.index(0, 1);
+ const QModelIndex sourceParent = proxy.mapToSource(proxyParent);
+ const int oldColCount = proxy.columnCount(proxyParent);
+ const int oldRowCount = model->rowCount(sourceParent);
+ const QVariant expectedNewVal = proxy.index(0, 2, proxyParent).data();
+ QVERIFY(proxy.removeColumn(1, proxyParent));
+ QCOMPARE(proxy.columnCount(proxyParent), oldColCount - 1);
+ QCOMPARE(model->rowCount(sourceParent), oldRowCount - 1);
+ QCOMPARE(proxy.index(0, 1, proxyParent).data(), expectedNewVal);
+ QCOMPARE(model->index(1, 0, sourceParent).data(), expectedNewVal);
+ QCOMPARE(columnsRemoveSpy.count(), 1);
+ QCOMPARE(columnsAboutToBeRemoveSpy.count(), 1);
+ QCOMPARE(rowsRemoveSpy.count(), 1);
+ QCOMPARE(rowsAboutToBeRemoveSpy.count(), 1);
+ for (const auto &spyArgs : {columnsRemoveSpy.takeFirst(),
+ columnsAboutToBeRemoveSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxyParent);
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ for (const auto &spyArgs : {rowsRemoveSpy.takeFirst(),
+ rowsAboutToBeRemoveSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), sourceParent);
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ delete model;
+}
+
+void tst_QTransposeProxyModel::insertColumnProxy_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::addColumn<bool>("rootItem");
+ QTest::newRow("List") << createListModel(this) << true;
+ QTest::newRow("Table") << createTableModel(this) << true;
+ QTest::newRow("Tree_Root_Item") << createTreeModel(this) << true;
+ QTest::newRow("Tree_Child_Item") << createTreeModel(this) << false;
+}
+
+void tst_QTransposeProxyModel::insertColumnProxy()
+{
+ QFETCH(QAbstractItemModel *, model);
+ QFETCH(bool, rootItem);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ QSignalSpy columnsInsertSpy(&proxy, &QAbstractItemModel::columnsInserted);
+ QVERIFY(columnsInsertSpy.isValid());
+ QSignalSpy columnsAboutToBeInsertSpy(&proxy, &QAbstractItemModel::columnsAboutToBeInserted);
+ QVERIFY(columnsAboutToBeInsertSpy.isValid());
+ QSignalSpy rowsInsertSpy(model, &QAbstractItemModel::rowsInserted);
+ QVERIFY(rowsInsertSpy.isValid());
+ QSignalSpy rowsAboutToBeInsertSpy(model, &QAbstractItemModel::rowsAboutToBeInserted);
+ QVERIFY(rowsAboutToBeInsertSpy.isValid());
+ proxy.setSourceModel(model);
+ const QModelIndex proxyParent = rootItem ? QModelIndex() : proxy.index(0, 1);
+ const QModelIndex sourceParent = proxy.mapToSource(proxyParent);
+ const int oldColCount = proxy.columnCount(proxyParent);
+ const int oldRowCount = model->rowCount(sourceParent);
+ QVERIFY(proxy.insertColumn(1, proxyParent));
+ QCOMPARE(proxy.columnCount(proxyParent), oldColCount + 1);
+ QCOMPARE(model->rowCount(sourceParent), oldRowCount + 1);
+ QVERIFY(proxy.index(0, 1, proxyParent).data().isNull());
+ QVERIFY(model->index(1, 0, sourceParent).data().isNull());
+ QCOMPARE(columnsInsertSpy.count(), 1);
+ QCOMPARE(columnsAboutToBeInsertSpy.count(), 1);
+ QCOMPARE(rowsInsertSpy.count(), 1);
+ QCOMPARE(rowsAboutToBeInsertSpy.count(), 1);
+ for (const auto &spyArgs : {columnsInsertSpy.takeFirst(),
+ columnsAboutToBeInsertSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxyParent);
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ for (const auto &spyArgs : {rowsInsertSpy.takeFirst(),
+ rowsAboutToBeInsertSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), sourceParent);
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ delete model;
+}
+
+void tst_QTransposeProxyModel::removeRowProxy_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::addColumn<bool>("rootItem");
+ QTest::newRow("Table") << createTableModel(this) << true;
+ QTest::newRow("Tree_Root_Item") << createTreeModel(this) << true;
+ QTest::newRow("Tree_Child_Item") << createTreeModel(this) << false;
+}
+
+void tst_QTransposeProxyModel::removeRowProxy()
+{
+ QFETCH(QAbstractItemModel *, model);
+ QFETCH(bool, rootItem);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ QSignalSpy rowsRemoveSpy(&proxy, &QAbstractItemModel::rowsRemoved);
+ QVERIFY(rowsRemoveSpy.isValid());
+ QSignalSpy rowsAboutToBeRemoveSpy(&proxy, &QAbstractItemModel::rowsAboutToBeRemoved);
+ QVERIFY(rowsAboutToBeRemoveSpy.isValid());
+ QSignalSpy columnsRemoveSpy(model, &QAbstractItemModel::columnsRemoved);
+ QVERIFY(columnsRemoveSpy.isValid());
+ QSignalSpy columnsAboutToBeRemoveSpy(model, &QAbstractItemModel::columnsAboutToBeRemoved);
+ QVERIFY(columnsAboutToBeRemoveSpy.isValid());
+ proxy.setSourceModel(model);
+ const QModelIndex proxyParent = rootItem ? QModelIndex() : proxy.index(0, 1);
+ const QModelIndex sourceParent = proxy.mapToSource(proxyParent);
+ const int oldRowCount = proxy.rowCount(proxyParent);
+ const int oldColCount = model->columnCount(sourceParent);
+ const QVariant expectedNewVal = proxy.index(2, 0, proxyParent).data();
+ QVERIFY(proxy.removeRow(1, proxyParent));
+ QCOMPARE(proxy.rowCount(proxyParent), oldRowCount - 1);
+ QCOMPARE(model->columnCount(sourceParent), oldColCount - 1);
+ QCOMPARE(proxy.index(1, 0, proxyParent).data(), expectedNewVal);
+ QCOMPARE(model->index(0, 1, sourceParent).data(), expectedNewVal);
+ QCOMPARE(columnsRemoveSpy.count(), 1);
+ QCOMPARE(columnsAboutToBeRemoveSpy.count(), 1);
+ QCOMPARE(rowsRemoveSpy.count(), 1);
+ QCOMPARE(rowsAboutToBeRemoveSpy.count(), 1);
+ for (const auto &spyArgs : {columnsRemoveSpy.takeFirst(),
+ columnsAboutToBeRemoveSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), sourceParent);
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ for (const auto &spyArgs : {rowsRemoveSpy.takeFirst(),
+ rowsAboutToBeRemoveSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxyParent);
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ delete model;
+}
+
+void tst_QTransposeProxyModel::insertRowProxy_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::addColumn<bool>("rootItem");
+ QTest::newRow("Table") << createTableModel(this) << true;
+ QTest::newRow("Tree_Root_Item") << createTreeModel(this) << true;
+ QTest::newRow("Tree_Child_Item") << createTreeModel(this) << false;
+}
+
+void tst_QTransposeProxyModel::insertRowProxy()
+{
+ QFETCH(QAbstractItemModel *, model);
+ QFETCH(bool, rootItem);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ QSignalSpy rowsInsertSpy(&proxy, &QAbstractItemModel::rowsInserted);
+ QVERIFY(rowsInsertSpy.isValid());
+ QSignalSpy rowsAboutToBeInsertSpy(&proxy, &QAbstractItemModel::rowsAboutToBeInserted);
+ QVERIFY(rowsAboutToBeInsertSpy.isValid());
+ QSignalSpy columnsInsertSpy(model, &QAbstractItemModel::columnsInserted);
+ QVERIFY(columnsInsertSpy.isValid());
+ QSignalSpy columnsAboutToBeInsertSpy(model, &QAbstractItemModel::columnsAboutToBeInserted);
+ QVERIFY(columnsAboutToBeInsertSpy.isValid());
+ proxy.setSourceModel(model);
+ const QModelIndex proxyParent = rootItem ? QModelIndex() : proxy.index(0, 1);
+ const QModelIndex sourceParent = proxy.mapToSource(proxyParent);
+ const int oldRowCount = proxy.rowCount(proxyParent);
+ const int oldColCount = model->columnCount(sourceParent);
+ QVERIFY(proxy.insertRow(1, proxyParent));
+ QCOMPARE(proxy.rowCount(proxyParent), oldRowCount + 1);
+ QCOMPARE(model->columnCount(sourceParent), oldColCount + 1);
+ QVERIFY(proxy.index(1, 0, proxyParent).data().isNull());
+ QVERIFY(model->index(0, 1, sourceParent).data().isNull());
+ QCOMPARE(columnsInsertSpy.count(), 1);
+ QCOMPARE(columnsAboutToBeInsertSpy.count(), 1);
+ QCOMPARE(rowsInsertSpy.count(), 1);
+ QCOMPARE(rowsAboutToBeInsertSpy.count(), 1);
+ for (const auto &spyArgs : {columnsInsertSpy.takeFirst(),
+ columnsAboutToBeInsertSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), sourceParent);
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ for (const auto &spyArgs : {rowsInsertSpy.takeFirst(),
+ rowsAboutToBeInsertSpy.takeFirst()}) {
+ QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxyParent);
+ QCOMPARE(spyArgs.at(1).toInt(), 1);
+ QCOMPARE(spyArgs.at(2).toInt(), 1);
+ }
+ delete model;
+}
+
+void tst_QTransposeProxyModel::headerData()
+{
+ QStandardItemModel model;
+ model.insertRows(0, 3);
+ model.insertColumns(0, 5);
+ for (int i = 0; i < model.rowCount(); ++i)
+ model.setHeaderData(i, Qt::Horizontal, QChar('A' + i));
+ for (int i = 1; i <= model.columnCount(); ++i)
+ model.setHeaderData(i, Qt::Vertical, i);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(&model);
+ for (int i = 0; i < model.rowCount(); ++i)
+ QCOMPARE(model.headerData(i, Qt::Horizontal), proxy.headerData(i, Qt::Vertical));
+ for (int i = 0; i < model.columnCount(); ++i)
+ QCOMPARE(model.headerData(i, Qt::Vertical), proxy.headerData(i, Qt::Horizontal));
+}
+
+void tst_QTransposeProxyModel::setHeaderData()
+{
+ QStandardItemModel model;
+ model.insertRows(0, 3);
+ model.insertColumns(0, 5);
+ for (int i = 0; i < model.rowCount(); ++i)
+ model.setHeaderData(i, Qt::Horizontal, QChar('A' + i));
+ for (int i = 1; i <= model.columnCount(); ++i)
+ model.setHeaderData(i, Qt::Vertical, i);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(&model);
+ QVERIFY(proxy.setHeaderData(1, Qt::Horizontal, 99));
+ QCOMPARE(model.headerData(1, Qt::Vertical).toInt(), 99);
+ QVERIFY(proxy.setHeaderData(1, Qt::Vertical, QChar('Z')));
+ QCOMPARE(model.headerData(1, Qt::Horizontal).toChar(), QChar('Z'));
+}
+
+void tst_QTransposeProxyModel::span()
+{
+ class SpanModel : public QStandardItemModel
+ {
+ Q_DISABLE_COPY(SpanModel)
+ public:
+ SpanModel(int rows, int columns, QObject *parent = nullptr)
+ : QStandardItemModel(rows, columns, parent)
+ {}
+ QSize span(const QModelIndex &index) const override
+ {
+ Q_UNUSED(index)
+ return QSize(2, 1);
+ }
+ };
+ SpanModel model(3, 5);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(&model);
+ QCOMPARE(proxy.span(proxy.index(0, 0)), QSize(1, 2));
+}
+
+void tst_QTransposeProxyModel::itemData()
+{
+ QAbstractItemModel *model = createTreeModel(this);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ QMap<int, QVariant> itmData = proxy.itemData(proxy.index(0, 1));
+ QCOMPARE(itmData.value(Qt::DisplayRole).toString(), QStringLiteral("1,0"));
+ QCOMPARE(itmData.value(Qt::UserRole).toInt(), 1);
+ QCOMPARE(itmData.value(Qt::UserRole + 1).toInt(), 0);
+ itmData = proxy.itemData(proxy.index(1, 2, proxy.index(0, 1)));
+ QCOMPARE(itmData.value(Qt::DisplayRole).toString(), QStringLiteral("1,0,2,1"));
+ QCOMPARE(itmData.value(Qt::UserRole).toInt(), 1);
+ QCOMPARE(itmData.value(Qt::UserRole + 1).toInt(), 0);
+ QCOMPARE(itmData.value(Qt::UserRole + 2).toInt(), 2);
+ QCOMPARE(itmData.value(Qt::UserRole + 3).toInt(), 1);
+ QVERIFY(proxy.itemData(QModelIndex()).isEmpty());
+ delete model;
+}
+
+void tst_QTransposeProxyModel::setItemData()
+{
+ QAbstractItemModel *model = createTreeModel(this);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ QSignalSpy sourceDataChangeSpy(model, &QAbstractItemModel::dataChanged);
+ QVERIFY(sourceDataChangeSpy.isValid());
+ QSignalSpy proxyDataChangeSpy(&proxy, &QAbstractItemModel::dataChanged);
+ QVERIFY(proxyDataChangeSpy.isValid());
+ const QMap<int, QVariant> itmData = {
+ std::make_pair<int, QVariant>(Qt::DisplayRole, QStringLiteral("Test")),
+ std::make_pair<int, QVariant>(Qt::UserRole, 88),
+ std::make_pair<int, QVariant>(Qt::UserRole + 1, 99),
+ };
+ QModelIndex idx = proxy.index(0, 1);
+ QVERIFY(proxy.setItemData(idx, itmData));
+ QCOMPARE(idx.data(Qt::DisplayRole).toString(), QStringLiteral("Test"));
+ QCOMPARE(idx.data(Qt::UserRole).toInt(), 88);
+ QCOMPARE(idx.data(Qt::UserRole + 1).toInt(), 99);
+ QCOMPARE(sourceDataChangeSpy.size(), 1);
+ QCOMPARE(proxyDataChangeSpy.size(), 1);
+ auto signalData = proxyDataChangeSpy.takeFirst();
+ QCOMPARE(signalData.at(0).value<QModelIndex>(), idx);
+ QCOMPARE(signalData.at(1).value<QModelIndex>(), idx);
+ const QVector<int> expectedRoles{Qt::DisplayRole, Qt::UserRole, Qt::EditRole, Qt::UserRole + 1};
+ QVector<int> receivedRoles = signalData.at(2).value<QVector<int> >();
+ QCOMPARE(receivedRoles.size(), expectedRoles.size());
+ for (int role : expectedRoles)
+ QVERIFY(receivedRoles.contains(role));
+ signalData = sourceDataChangeSpy.takeFirst();
+ QCOMPARE(signalData.at(0).value<QModelIndex>(), proxy.mapToSource(idx));
+ QCOMPARE(signalData.at(1).value<QModelIndex>(), proxy.mapToSource(idx));
+ receivedRoles = signalData.at(2).value<QVector<int> >();
+ QCOMPARE(receivedRoles.size(), expectedRoles.size());
+ for (int role : expectedRoles)
+ QVERIFY(receivedRoles.contains(role));
+ idx = proxy.index(1, 2, proxy.index(0, 1));
+ QVERIFY(proxy.setItemData(idx, itmData));
+ QCOMPARE(idx.data(Qt::DisplayRole).toString(), QStringLiteral("Test"));
+ QCOMPARE(idx.data(Qt::UserRole).toInt(), 88);
+ QCOMPARE(idx.data(Qt::UserRole + 1).toInt(), 99);
+ QCOMPARE(idx.data(Qt::UserRole + 2).toInt(), 2);
+ QCOMPARE(idx.data(Qt::UserRole + 3).toInt(), 1);
+ QCOMPARE(sourceDataChangeSpy.size(), 1);
+ QCOMPARE(proxyDataChangeSpy.size(), 1);
+ signalData = proxyDataChangeSpy.takeFirst();
+ QCOMPARE(signalData.at(0).value<QModelIndex>(), idx);
+ QCOMPARE(signalData.at(1).value<QModelIndex>(), idx);
+ receivedRoles = signalData.at(2).value<QVector<int> >();
+ QCOMPARE(receivedRoles.size(), expectedRoles.size());
+ for (int role : expectedRoles)
+ QVERIFY(receivedRoles.contains(role));
+ signalData = sourceDataChangeSpy.takeFirst();
+ QCOMPARE(signalData.at(0).value<QModelIndex>(), proxy.mapToSource(idx));
+ QCOMPARE(signalData.at(1).value<QModelIndex>(), proxy.mapToSource(idx));
+ receivedRoles = signalData.at(2).value<QVector<int> >();
+ QCOMPARE(receivedRoles.size(), expectedRoles.size());
+ for (int role : expectedRoles)
+ QVERIFY(receivedRoles.contains(role));
+ QVERIFY(!proxy.setItemData(QModelIndex(), itmData));
+ delete model;
+}
+
+void tst_QTransposeProxyModel::moveRowsBase()
+{
+ QStringListModel model{QStringList{"A", "B", "C", "D"}};
+ QTransposeProxyModel proxy;
+ QSignalSpy columnsMoveSpy(&proxy, &QAbstractItemModel::columnsMoved);
+ QVERIFY(columnsMoveSpy.isValid());
+ QSignalSpy columnsAboutToBeMoveSpy(&proxy, &QAbstractItemModel::columnsAboutToBeMoved);
+ QVERIFY(columnsAboutToBeMoveSpy.isValid());
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(&model);
+ const QStringList expectedNewVal = {"B", "A", "C", "D"};
+ QVERIFY(model.moveRows(QModelIndex(), 0, 1, QModelIndex(), 2));
+ for (int i = 0; i < expectedNewVal.size(); ++i)
+ QCOMPARE(proxy.index(0, i).data(), expectedNewVal.at(i));
+ QCOMPARE(columnsMoveSpy.count(), 1);
+ QCOMPARE(columnsAboutToBeMoveSpy.count(), 1);
+ for (const auto &spyArgs : {columnsMoveSpy.takeFirst(),
+ columnsAboutToBeMoveSpy.takeFirst()}) {
+ QVERIFY(!spyArgs.at(0).value<QModelIndex>().isValid());
+ QCOMPARE(spyArgs.at(1).toInt(), 0);
+ QCOMPARE(spyArgs.at(2).toInt(), 0);
+ QVERIFY(!spyArgs.at(3).value<QModelIndex>().isValid());
+ QCOMPARE(spyArgs.at(4).toInt(), 2);
+ }
+}
+
+void tst_QTransposeProxyModel::moveColumnsProxy()
+{
+ QStringListModel model{QStringList{"A", "B", "C", "D"}};
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ QSignalSpy columnsMoveSpy(&proxy, &QAbstractItemModel::columnsMoved);
+ QVERIFY(columnsMoveSpy.isValid());
+ QSignalSpy columnsAboutToBeMoveSpy(&proxy, &QAbstractItemModel::columnsAboutToBeMoved);
+ QVERIFY(columnsAboutToBeMoveSpy.isValid());
+ QSignalSpy rowsMoveSpy(&model, &QAbstractItemModel::rowsMoved);
+ QVERIFY(rowsMoveSpy.isValid());
+ QSignalSpy rowsAboutToBeMoveSpy(&model, &QAbstractItemModel::rowsAboutToBeMoved);
+ QVERIFY(rowsAboutToBeMoveSpy.isValid());
+ proxy.setSourceModel(&model);
+ const QStringList expectedNewVal = {"B", "A", "C", "D"};
+ QVERIFY(proxy.moveColumns(QModelIndex(), 0, 1, QModelIndex(), 2));
+ for (int i = 0; i < expectedNewVal.size(); ++i)
+ QCOMPARE(proxy.index(0, i).data(), expectedNewVal.at(i));
+ for (int i = 0; i < expectedNewVal.size(); ++i)
+ QCOMPARE(model.index(i, 0).data(), expectedNewVal.at(i));
+ QCOMPARE(columnsMoveSpy.count(), 1);
+ QCOMPARE(columnsAboutToBeMoveSpy.count(), 1);
+ QCOMPARE(rowsMoveSpy.count(), 1);
+ QCOMPARE(rowsAboutToBeMoveSpy.count(), 1);
+ for (const auto &spyArgs : {columnsMoveSpy.takeFirst(),
+ columnsAboutToBeMoveSpy.takeFirst(),
+ rowsMoveSpy.takeFirst(),rowsAboutToBeMoveSpy.takeFirst()}) {
+ QVERIFY(!spyArgs.at(0).value<QModelIndex>().isValid());
+ QCOMPARE(spyArgs.at(1).toInt(), 0);
+ QCOMPARE(spyArgs.at(2).toInt(), 0);
+ QVERIFY(!spyArgs.at(3).value<QModelIndex>().isValid());
+ }
+}
+
+void tst_QTransposeProxyModel::setData_data()
+{
+ QTest::addColumn<QAbstractItemModel *>("model");
+ QTest::addColumn<bool>("rootItem");
+ QTest::addColumn<bool>("viaProxy");
+ QTest::newRow("List_via_Base") << createListModel(this) << true << false;
+ QTest::newRow("Table_via_Base") << createTableModel(this) << true << false;
+ QTest::newRow("Tree_via_Base_Root_Item") << createTreeModel(this) << true << false;
+ QTest::newRow("Tree_via_Base_Child_Item") << createTreeModel(this) << false << false;
+ QTest::newRow("List_via_Proxy") << createListModel(this) << true << true;
+ QTest::newRow("Table_via_Proxy") << createTableModel(this) << true << true;
+ QTest::newRow("Tree_via_Proxy_Root_Item") << createTreeModel(this) << true << true;
+ QTest::newRow("Tree_via_Proxy_Child_Item") << createTreeModel(this) << false << true;
+}
+
+void tst_QTransposeProxyModel::setData()
+{
+ QFETCH(QAbstractItemModel *, model);
+ QFETCH(bool, rootItem);
+ QFETCH(bool, viaProxy);
+ QTransposeProxyModel proxy;
+ new QAbstractItemModelTester(&proxy, &proxy);
+ proxy.setSourceModel(model);
+ QSignalSpy sourceDataChangeSpy(model, &QAbstractItemModel::dataChanged);
+ QVERIFY(sourceDataChangeSpy.isValid());
+ QSignalSpy proxyDataChangeSpy(&proxy, &QAbstractItemModel::dataChanged);
+ QVERIFY(proxyDataChangeSpy.isValid());
+ const QString testData = QStringLiteral("TestingSetData");
+ if (viaProxy) {
+ const QModelIndex parIdx = rootItem ? QModelIndex() : proxy.index(0, 1);
+ QVERIFY(proxy.setData(proxy.index(0, 1, parIdx), testData));
+ QCOMPARE(model->index(1, 0, proxy.mapToSource(parIdx)).data().toString(), testData);
+ } else {
+ const QModelIndex parIdx = rootItem ? QModelIndex() : model->index(1, 0);
+ QVERIFY(model->setData(model->index(1, 0, parIdx), testData));
+ QCOMPARE(proxy.index(0, 1, proxy.mapFromSource(parIdx)).data().toString(), testData);
+ }
+ QCOMPARE(sourceDataChangeSpy.size(), 1);
+ QCOMPARE(proxyDataChangeSpy.size(), 1);
+ delete model;
+}
+
+QTEST_GUILESS_MAIN(tst_QTransposeProxyModel)
+
+#include "tst_qtransposeproxymodel.moc"