File indexing completed on 2024-12-01 03:41:34
0001 /* 0002 SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0003 SPDX-FileContributor: David Faure <david.faure@kdab.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include <QAbstractItemModelTester> 0009 #include <QDebug> 0010 #include <QSignalSpy> 0011 #include <QStandardItemModel> 0012 #include <QTest> 0013 0014 #include "test_model_helpers.h" 0015 #include <krearrangecolumnsproxymodel.h> 0016 using namespace TestModelHelpers; 0017 0018 Q_DECLARE_METATYPE(QModelIndex) 0019 0020 class tst_KRearrangeColumnsProxyModel : public QObject 0021 { 0022 Q_OBJECT 0023 0024 private Q_SLOTS: 0025 0026 void initTestCase() 0027 { 0028 qRegisterMetaType<QModelIndex>(); 0029 } 0030 0031 void init() 0032 { 0033 // Prepare the source model to use later on 0034 mod.clear(); 0035 mod.appendRow(makeStandardItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") << QStringLiteral("D") 0036 << QStringLiteral("E"))); 0037 mod.item(0, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("m") << QStringLiteral("n") << QStringLiteral("o") << QStringLiteral("p") 0038 << QStringLiteral("-"))); 0039 mod.item(0, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("q") << QStringLiteral("r") << QStringLiteral("s") << QStringLiteral("t") 0040 << QStringLiteral("-"))); 0041 mod.appendRow(makeStandardItems(QStringList() << QStringLiteral("E") << QStringLiteral("F") << QStringLiteral("G") << QStringLiteral("H") 0042 << QStringLiteral("I"))); 0043 mod.item(1, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("x") << QStringLiteral("y") << QStringLiteral("z") << QStringLiteral(".") 0044 << QStringLiteral("-"))); 0045 mod.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3") << QStringLiteral("H4") 0046 << QStringLiteral("H5")); 0047 0048 QCOMPARE(extractRowTexts(&mod, 0), QStringLiteral("ABCDE")); 0049 QCOMPARE(extractRowTexts(&mod, 0, mod.index(0, 0)), QStringLiteral("mnop-")); 0050 QCOMPARE(extractRowTexts(&mod, 1, mod.index(0, 0)), QStringLiteral("qrst-")); 0051 QCOMPARE(extractRowTexts(&mod, 1), QStringLiteral("EFGHI")); 0052 QCOMPARE(extractRowTexts(&mod, 0, mod.index(1, 0)), QStringLiteral("xyz.-")); 0053 QCOMPARE(extractHorizontalHeaderTexts(&mod), QStringLiteral("H1H2H3H4H5")); 0054 0055 // test code to see the model 0056 // showModel(&mod); 0057 } 0058 0059 void shouldShowNothingIfNoSourceModel() 0060 { 0061 // Given a rearrange-columns proxy with no source model 0062 KRearrangeColumnsProxyModel pm; 0063 0064 // Then the proxy should show nothing (no columns selected) 0065 QCOMPARE(pm.rowCount(), 0); 0066 QCOMPARE(pm.columnCount(), 0); 0067 QVERIFY(!pm.index(0, 0).isValid()); 0068 } 0069 0070 void shouldShowNothingIfNoColumnSelection() 0071 { 0072 // Given a rearrange-columns proxy 0073 KRearrangeColumnsProxyModel pm; 0074 0075 // When setting it to a source model 0076 pm.setSourceModel(&mod); 0077 0078 // Then the proxy should show nothing (no columns selected) 0079 QCOMPARE(pm.rowCount(), mod.rowCount()); 0080 QCOMPARE(pm.columnCount(), 0); 0081 QVERIFY(!pm.hasChildren()); 0082 } 0083 0084 void shouldMapColumns() 0085 { 0086 // Given a rearrange-columns proxy 0087 KRearrangeColumnsProxyModel pm; 0088 pm.setSourceColumns(QList<int>() << 3 << 1 << 0); 0089 0090 // When using that proxy on top of an empty source model 0091 QStandardItemModel sourceModel; 0092 sourceModel.setColumnCount(4); 0093 pm.setSourceModel(&sourceModel); 0094 0095 // Then the mapping methods should work 0096 QCOMPARE(pm.proxyColumnForSourceColumn(0), 2); 0097 QCOMPARE(pm.proxyColumnForSourceColumn(1), 1); 0098 QCOMPARE(pm.proxyColumnForSourceColumn(2), -1); 0099 QCOMPARE(pm.proxyColumnForSourceColumn(3), 0); 0100 QCOMPARE(pm.sourceColumnForProxyColumn(0), 3); 0101 QCOMPARE(pm.sourceColumnForProxyColumn(1), 1); 0102 QCOMPARE(pm.sourceColumnForProxyColumn(2), 0); 0103 0104 // And mapFromSource should return invalid for unmapped cells 0105 QVERIFY(!pm.mapFromSource(sourceModel.index(0, 2)).isValid()); 0106 } 0107 0108 void shouldShowNothingIfNoRows() 0109 { 0110 // Given a rearrange-columns proxy 0111 KRearrangeColumnsProxyModel pm; 0112 pm.setSourceColumns(QList<int>() << 2 << 3 << 1 << 0); 0113 0114 // When using that proxy on top of an empty source model 0115 QStandardItemModel sourceModel; 0116 sourceModel.setColumnCount(4); 0117 pm.setSourceModel(&sourceModel); 0118 0119 // Then the proxy should show nothing 0120 QCOMPARE(pm.rowCount(), 0); 0121 QCOMPARE(pm.columnCount(), 4); 0122 QCOMPARE(pm.index(0, 0), pm.index(0, 0)); // like QAbstractItemView::setModel does in a Q_ASSERT_X 0123 } 0124 0125 void shouldRearrangeColumns() 0126 { 0127 // Given a rearrange-columns proxy 0128 KRearrangeColumnsProxyModel pm; 0129 new QAbstractItemModelTester(&pm, &pm); 0130 0131 // When setting it to a source model, with columns rearranged 0132 setup(pm); 0133 0134 // Then the proxy should show columns reordered 0135 QCOMPARE(pm.rowCount(), 2); 0136 0137 // (verify that the mapFromSource(mapToSource(x)) == x roundtrip works) 0138 for (int row = 0; row < pm.rowCount(); ++row) { 0139 for (int col = 0; col < pm.columnCount(); ++col) { 0140 // qDebug() << "row" << row << "col" << col; 0141 QCOMPARE(pm.mapFromSource(pm.mapToSource(pm.index(row, col))), pm.index(row, col)); 0142 } 0143 } 0144 QCOMPARE(indexRowCol(pm.index(0, 0)), QStringLiteral("0,0")); 0145 0146 QCOMPARE(pm.rowCount(pm.index(0, 0)), 2); 0147 QCOMPARE(pm.index(0, 0).parent(), QModelIndex()); 0148 0149 QCOMPARE(pm.mapToSource(pm.index(0, 0)).column(), 2); // column 0 points to C 0150 QCOMPARE(pm.mapToSource(pm.index(0, 1)).column(), 3); // column 1 points to D 0151 0152 QCOMPARE(pm.sibling(0, 1, pm.index(0, 0)).column(), 1); 0153 QCOMPARE(pm.sibling(0, 0, pm.index(0, 1)).column(), 0); 0154 0155 QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("CDBA")); 0156 QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QStringLiteral("opnm")); 0157 QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QStringLiteral("strq")); 0158 QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("GHFE")); 0159 QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QStringLiteral("z.yx")); 0160 QCOMPARE(extractHorizontalHeaderTexts(&pm), QStringLiteral("H3H4H2H1")); 0161 0162 // Verify tree structure of proxy 0163 const QModelIndex secondParent = pm.index(1, 0); 0164 QVERIFY(!secondParent.parent().isValid()); 0165 QCOMPARE(indexToText(pm.index(0, 0, secondParent).parent()), indexToText(secondParent)); 0166 QCOMPARE(indexToText(pm.index(0, 3, secondParent).parent()), indexToText(secondParent)); 0167 0168 QVERIFY(!pm.canFetchMore(QModelIndex())); 0169 } 0170 0171 void shouldHandleDataChanged() 0172 { 0173 // Given a rearrange-columns proxy 0174 KRearrangeColumnsProxyModel pm; 0175 setup(pm); 0176 0177 QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex, QModelIndex))); 0178 0179 // When a cell in a source model changes 0180 mod.item(0, 2)->setData("c", Qt::EditRole); 0181 mod.item(0, 3)->setData("d", Qt::EditRole); 0182 0183 // Then the change should be notified to the proxy 0184 QCOMPARE(dataChangedSpy.count(), 2); 0185 QCOMPARE(indexToText(dataChangedSpy.at(0).at(0).toModelIndex()), indexToText(pm.index(0, 0))); 0186 QCOMPARE(indexToText(dataChangedSpy.at(1).at(0).toModelIndex()), indexToText(pm.index(0, 1))); 0187 QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("cdBA")); 0188 } 0189 0190 void shouldHandleDataChangedInChild() 0191 { 0192 // Given a rearrange-columns proxy 0193 KRearrangeColumnsProxyModel pm; 0194 setup(pm); 0195 0196 QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex, QModelIndex))); 0197 0198 // When a cell in a source model changes 0199 mod.item(1, 0)->child(0, 3)->setData(",", Qt::EditRole); 0200 0201 // Then the change should be notified to the proxy 0202 QCOMPARE(dataChangedSpy.count(), 1); 0203 QCOMPARE(indexToText(dataChangedSpy.at(0).at(0).toModelIndex()), indexToText(pm.index(0, 1, pm.index(1, 0)))); 0204 QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QStringLiteral("z,yx")); 0205 } 0206 0207 void shouldSupportSetData() 0208 { 0209 // Given a rearrange-columns proxy 0210 KRearrangeColumnsProxyModel pm; 0211 setup(pm); 0212 0213 QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex, QModelIndex))); 0214 0215 // When changing data via the proxy 0216 const QModelIndex idx = pm.index(0, 2); 0217 QCOMPARE(idx.data().toString(), QStringLiteral("B")); 0218 pm.setData(idx, QStringLiteral("Z")); 0219 QCOMPARE(idx.data().toString(), QStringLiteral("Z")); 0220 QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("CDZA")); 0221 QCOMPARE(extractRowTexts(&mod, 0), QStringLiteral("AZCDE")); 0222 } 0223 0224 private: 0225 // setup proxy 0226 void setup(KRearrangeColumnsProxyModel &pm) 0227 { 0228 pm.setSourceColumns(QList<int>() << 2 << 3 << 1 << 0); 0229 pm.setSourceModel(&mod); 0230 pm.sort(0); // don't forget this! 0231 } 0232 0233 static QString indexRowCol(const QModelIndex &index) 0234 { 0235 if (!index.isValid()) { 0236 return QStringLiteral("invalid"); 0237 } 0238 return QString::number(index.row()) + "," + QString::number(index.column()); 0239 } 0240 0241 static QString indexToText(const QModelIndex &index) 0242 { 0243 if (!index.isValid()) { 0244 return QStringLiteral("invalid"); 0245 } 0246 return QString::number(index.row()) + "," + QString::number(index.column()) + "," 0247 + QString::number(reinterpret_cast<qulonglong>(index.internalPointer()), 16) + " in " 0248 + QString::number(reinterpret_cast<qulonglong>(index.model()), 16); 0249 } 0250 0251 QStandardItemModel mod; 0252 }; 0253 0254 QTEST_MAIN(tst_KRearrangeColumnsProxyModel) 0255 0256 #include "krearrangecolumnsproxymodeltest.moc"