1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QtCore/QCoreApplication>
31
32#include <QtCore/QSortFilterProxyModel>
33#include <QtCore/QStringListModel>
34#include <QtGui/QStandardItemModel>
35
36#include "dynamictreemodel.h"
37
38/*!
39 Note that this doesn't test models, but any functionality that QAbstractItemModel should provide
40 */
41class tst_QAbstractItemModel : public QObject
42{
43 Q_OBJECT
44
45public slots:
46 void init();
47 void cleanup();
48
49private slots:
50 void index();
51 void parent();
52 void hasChildren();
53 void data();
54 void headerData();
55 void itemData();
56 void itemFlags();
57 void match();
58 void dropMimeData_data();
59 void dropMimeData();
60 void canDropMimeData();
61 void changePersistentIndex();
62 void movePersistentIndex();
63
64 void insertRows();
65 void insertColumns();
66 void removeRows();
67 void removeColumns();
68 void moveRows();
69 void moveColumns();
70
71 void reset();
72
73 void complexChangesWithPersistent();
74
75 void testMoveSameParentUp_data();
76 void testMoveSameParentUp();
77
78 void testMoveSameParentDown_data();
79 void testMoveSameParentDown();
80
81 void testMoveToGrandParent_data();
82 void testMoveToGrandParent();
83
84 void testMoveToSibling_data();
85 void testMoveToSibling();
86
87 void testMoveToUncle_data();
88 void testMoveToUncle();
89
90 void testMoveToDescendants();
91
92 void testMoveWithinOwnRange_data();
93 void testMoveWithinOwnRange();
94
95 void testMoveThroughProxy();
96
97 void testReset();
98
99 void testDataChanged();
100
101 void testChildrenLayoutsChanged();
102
103 void testRoleNames();
104 void testDragActions();
105 void dragActionsFallsBackToDropActions();
106
107 void testFunctionPointerSignalConnection();
108
109 void checkIndex();
110
111private:
112 DynamicTreeModel *m_model;
113};
114
115/*!
116 Test model that impliments the pure vitual functions and anything else that is
117 needed.
118
119 It is a table implemented as a vector of vectors of strings.
120 */
121class QtTestModel: public QAbstractItemModel
122{
123public:
124 QtTestModel(int rows, int columns, QObject *parent = 0);
125 QtTestModel(const QVector<QVector<QString> > tbl, QObject *parent = 0);
126 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
127 QModelIndex parent(const QModelIndex &) const;
128 int rowCount(const QModelIndex &parent) const;
129 int columnCount(const QModelIndex &parent) const;
130 bool hasChildren(const QModelIndex &) const;
131 QVariant data(const QModelIndex &idx, int) const;
132 bool setData(const QModelIndex &idx, const QVariant &value, int);
133 bool insertRows(int row, int count, const QModelIndex &parent= QModelIndex());
134 bool insertColumns(int column, int count, const QModelIndex &parent= QModelIndex());
135 void setPersistent(const QModelIndex &from, const QModelIndex &to);
136 bool removeRows ( int row, int count, const QModelIndex & parent = QModelIndex() );
137 bool removeColumns( int column, int count, const QModelIndex & parent = QModelIndex());
138 bool moveRows (const QModelIndex &sourceParent, int sourceRow, int count,
139 const QModelIndex &destinationParent, int destinationChild);
140 bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count,
141 const QModelIndex &destinationParent, int destinationChild);
142 void reset();
143
144 bool canDropMimeData(const QMimeData *data, Qt::DropAction action,
145 int row, int column, const QModelIndex &parent) const;
146
147 int cCount, rCount;
148 mutable bool wrongIndex;
149 QVector<QVector<QString> > table;
150};
151
152Q_DECLARE_METATYPE(QAbstractItemModel::LayoutChangeHint);
153
154QtTestModel::QtTestModel(int rows, int columns, QObject *parent)
155 : QAbstractItemModel(parent), cCount(columns), rCount(rows), wrongIndex(false)
156{
157 table.resize(asize: rows);
158 for (int r = 0; r < rows; ++r) {
159 const QString prefix = QString::number(r) + QLatin1Char('/');
160 table[r].resize(asize: columns);
161 for (int c = 0; c < columns; ++c)
162 table[r][c] = prefix + QString::number(c);
163 }
164}
165
166QtTestModel::QtTestModel(const QVector<QVector<QString> > tbl, QObject *parent)
167 : QAbstractItemModel(parent), wrongIndex(false)
168{
169 table = tbl;
170 rCount = tbl.count();
171 cCount = tbl.at(i: 0).count();
172}
173
174QModelIndex QtTestModel::index(int row, int column, const QModelIndex &parent) const
175{
176 return hasIndex(row, column, parent) ? createIndex(arow: row, acolumn: column) : QModelIndex();
177}
178
179QModelIndex QtTestModel::parent(const QModelIndex &) const { return QModelIndex(); }
180int QtTestModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : rCount; }
181int QtTestModel::columnCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : cCount; }
182bool QtTestModel::hasChildren(const QModelIndex &) const { return false; }
183
184QVariant QtTestModel::data(const QModelIndex &idx, int) const
185{
186 if (idx.row() < 0 || idx.column() < 0 || idx.column() > cCount || idx.row() > rCount) {
187 wrongIndex = true;
188 qWarning(msg: "got invalid modelIndex %d/%d", idx.row(), idx.column());
189 return QVariant();
190 }
191 return table.at(i: idx.row()).at(i: idx.column());
192}
193
194bool QtTestModel::setData(const QModelIndex &idx, const QVariant &value, int)
195{
196 table[idx.row()][idx.column()] = value.toString();
197 return true;
198}
199
200bool QtTestModel::insertRows(int row, int count, const QModelIndex &parent)
201{
202 QAbstractItemModel::beginInsertRows(parent, first: row, last: row + count - 1);
203 int cc = columnCount(parent);
204 table.insert(i: row, n: count, t: QVector<QString>(cc));
205 rCount = table.count();
206 QAbstractItemModel::endInsertRows();
207 return true;
208}
209
210bool QtTestModel::insertColumns(int column, int count, const QModelIndex &parent)
211{
212 QAbstractItemModel::beginInsertColumns(parent, first: column, last: column + count - 1);
213 int rc = rowCount(parent);
214 for (int i = 0; i < rc; ++i)
215 table[i].insert(i: column, n: 1, t: "");
216 cCount = table.at(i: 0).count();
217 QAbstractItemModel::endInsertColumns();
218 return true;
219}
220
221void QtTestModel::setPersistent(const QModelIndex &from, const QModelIndex &to)
222{
223 changePersistentIndex(from, to);
224}
225
226bool QtTestModel::removeRows( int row, int count, const QModelIndex & parent)
227{
228 QAbstractItemModel::beginRemoveRows(parent, first: row, last: row + count - 1);
229
230 for (int r = row+count-1; r >= row; --r)
231 table.remove(i: r);
232 rCount = table.count();
233
234 QAbstractItemModel::endRemoveRows();
235 return true;
236}
237
238bool QtTestModel::removeColumns(int column, int count, const QModelIndex & parent)
239{
240 QAbstractItemModel::beginRemoveColumns(parent, first: column, last: column + count - 1);
241
242 for (int c = column+count-1; c > column; --c)
243 for (int r = 0; r < rCount; ++r)
244 table[r].remove(i: c);
245
246 cCount = table.at(i: 0).count();
247
248 QAbstractItemModel::endRemoveColumns();
249 return true;
250}
251
252bool QtTestModel::moveRows(const QModelIndex &sourceParent, int src, int cnt,
253 const QModelIndex &destinationParent, int dst)
254{
255 if (!QAbstractItemModel::beginMoveRows(sourceParent, sourceFirst: src, sourceLast: src + cnt - 1,
256 destinationParent, destinationRow: dst))
257 return false;
258
259 QVector<QString> buf;
260 if (dst < src) {
261 for (int i = 0; i < cnt; ++i) {
262 buf.swap(other&: table[src + i]);
263 table.remove(i: src + 1);
264 table.insert(i: dst, t: buf);
265 }
266 } else if (src < dst) {
267 for (int i = 0; i < cnt; ++i) {
268 buf.swap(other&: table[src]);
269 table.remove(i: src);
270 table.insert(i: dst + i, t: buf);
271 }
272 }
273
274 rCount = table.count();
275
276 QAbstractItemModel::endMoveRows();
277 return true;
278}
279
280bool QtTestModel::moveColumns(const QModelIndex &sourceParent, int src, int cnt,
281 const QModelIndex &destinationParent, int dst)
282{
283 if (!QAbstractItemModel::beginMoveColumns(sourceParent, sourceFirst: src, sourceLast: src + cnt - 1,
284 destinationParent, destinationColumn: dst))
285 return false;
286
287 for (int r = 0; r < rCount; ++r) {
288 QString buf;
289 if (dst < src) {
290 for (int i = 0; i < cnt; ++i) {
291 buf = table[r][src + i];
292 table[r].remove(i: src + 1);
293 table[r].insert(i: dst, t: buf);
294 }
295 } else if (src < dst) {
296 for (int i = 0; i < cnt; ++i) {
297 buf = table[r][src];
298 table[r].remove(i: src);
299 table[r].insert(i: dst + i, t: buf);
300 }
301 }
302 }
303
304 cCount = table.at(i: 0).count();
305
306 QAbstractItemModel::endMoveColumns();
307 return true;
308}
309
310void QtTestModel::reset()
311{
312 QAbstractItemModel::beginResetModel();
313 QAbstractItemModel::endResetModel();
314}
315
316bool QtTestModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
317 int row, int column, const QModelIndex &parent) const
318{
319 Q_UNUSED(data);
320 Q_UNUSED(action);
321
322 // For testing purposes, we impose some arbitrary rules on what may be dropped.
323 if (!parent.isValid() && row < 0 && column < 0) {
324 // a drop in emtpy space in the view is allowed.
325 // For example, in a filesystem view, a file may be dropped into empty space
326 // if it represents a writable directory.
327 return true;
328 }
329
330 // We then arbitrarily decide to only allow drops on odd rows.
331 // A filesystem view/model might be able to drop onto (writable) directories.
332 return row % 2 == 0;
333}
334
335void tst_QAbstractItemModel::init()
336{
337 m_model = new DynamicTreeModel(this);
338
339 ModelInsertCommand *insertCommand = new ModelInsertCommand(m_model, this);
340 insertCommand->setNumCols(4);
341 insertCommand->setStartRow(0);
342 insertCommand->setEndRow(9);
343 insertCommand->doCommand();
344
345 insertCommand = new ModelInsertCommand(m_model, this);
346 insertCommand->setAncestorRowNumbers(QList<int>() << 5);
347 insertCommand->setNumCols(4);
348 insertCommand->setStartRow(0);
349 insertCommand->setEndRow(9);
350 insertCommand->doCommand();
351
352 qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>();
353}
354
355void tst_QAbstractItemModel::cleanup()
356{
357 delete m_model;
358}
359
360/*
361 tests
362*/
363
364void tst_QAbstractItemModel::index()
365{
366 QtTestModel model(1, 1);
367 QModelIndex idx = model.index(row: 0, column: 0, parent: QModelIndex());
368 QVERIFY(idx.isValid());
369}
370
371void tst_QAbstractItemModel::parent()
372{
373 QtTestModel model(1, 1);
374 QModelIndex idx = model.index(row: 0, column: 0, parent: QModelIndex());
375 QModelIndex par = model.parent(idx);
376 QVERIFY(!par.isValid());
377}
378
379void tst_QAbstractItemModel::hasChildren()
380{
381 QtTestModel model(1, 1);
382 QModelIndex idx = model.index(row: 0, column: 0, parent: QModelIndex());
383 QVERIFY(!model.hasChildren(idx));
384}
385
386void tst_QAbstractItemModel::data()
387{
388 QtTestModel model(1, 1);
389 QModelIndex idx = model.index(row: 0, column: 0, parent: QModelIndex());
390 QVERIFY(idx.isValid());
391 QCOMPARE(model.data(idx, Qt::DisplayRole).toString(), QString("0/0"));
392
393 // Default does nothing
394 QCOMPARE(model.setHeaderData(0, Qt::Horizontal, QVariant(0), 0), false);
395}
396
397void tst_QAbstractItemModel::headerData()
398{
399 QtTestModel model(1, 1);
400 QCOMPARE(model.headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(),
401 QString("1"));
402
403 // Default text alignment for header must be invalid
404 QVERIFY( !model.headerData(0, Qt::Horizontal, Qt::TextAlignmentRole).isValid() );
405}
406
407void tst_QAbstractItemModel::itemData()
408{
409 QtTestModel model(1, 1);
410 QModelIndex idx = model.index(row: 0, column: 0, parent: QModelIndex());
411 QVERIFY(idx.isValid());
412 QMap<int, QVariant> dat = model.itemData(index: idx);
413 QCOMPARE(dat.count(Qt::DisplayRole), 1);
414 QCOMPARE(dat.value(Qt::DisplayRole).toString(), QString("0/0"));
415}
416
417void tst_QAbstractItemModel::itemFlags()
418{
419 QtTestModel model(1, 1);
420 QModelIndex idx = model.index(row: 0, column: 0, parent: QModelIndex());
421 QVERIFY(idx.isValid());
422 Qt::ItemFlags flags = model.flags(index: idx);
423 QCOMPARE(Qt::ItemIsSelectable|Qt::ItemIsEnabled, flags);
424}
425
426void tst_QAbstractItemModel::match()
427{
428 QtTestModel model(4, 1);
429 QModelIndex start = model.index(row: 0, column: 0, parent: QModelIndex());
430 QVERIFY(start.isValid());
431 QModelIndexList res = model.match(start, role: Qt::DisplayRole, value: QVariant("1"), hits: 3);
432 QCOMPARE(res.count(), 1);
433 QModelIndex idx = model.index(row: 1, column: 0, parent: QModelIndex());
434 bool areEqual = (idx == res.first());
435 QVERIFY(areEqual);
436
437 model.setData(idx: model.index(row: 0, column: 0, parent: QModelIndex()), value: "bat", Qt::DisplayRole);
438 model.setData(idx: model.index(row: 1, column: 0, parent: QModelIndex()), value: "cat", Qt::DisplayRole);
439 model.setData(idx: model.index(row: 2, column: 0, parent: QModelIndex()), value: "dog", Qt::DisplayRole);
440 model.setData(idx: model.index(row: 3, column: 0, parent: QModelIndex()), value: "boar", Qt::DisplayRole);
441
442 res = model.match(start, role: Qt::DisplayRole, value: QVariant("dog"), hits: -1, flags: Qt::MatchExactly);
443 QCOMPARE(res.count(), 1);
444 res = model.match(start, role: Qt::DisplayRole, value: QVariant("a"), hits: -1, flags: Qt::MatchContains);
445 QCOMPARE(res.count(), 3);
446 res = model.match(start, role: Qt::DisplayRole, value: QVariant("b"), hits: -1, flags: Qt::MatchStartsWith);
447 QCOMPARE(res.count(), 2);
448 res = model.match(start, role: Qt::DisplayRole, value: QVariant("t"), hits: -1, flags: Qt::MatchEndsWith);
449 QCOMPARE(res.count(), 2);
450 res = model.match(start, role: Qt::DisplayRole, value: QVariant("*a*"), hits: -1, flags: Qt::MatchWildcard);
451 QCOMPARE(res.count(), 3);
452 res = model.match(start, role: Qt::DisplayRole, value: QVariant(".*O.*"), hits: -1, flags: Qt::MatchRegExp);
453 QCOMPARE(res.count(), 2);
454 res = model.match(start, role: Qt::DisplayRole, value: QVariant(".*O.*"), hits: -1, flags: Qt::MatchRegExp | Qt::MatchCaseSensitive);
455 QCOMPARE(res.count(), 0);
456 res = model.match(start, role: Qt::DisplayRole, value: QVariant("BOAR"), hits: -1, flags: Qt::MatchFixedString);
457 QCOMPARE(res.count(), 1);
458 res = model.match(start, role: Qt::DisplayRole, value: QVariant("bat"), hits: -1,
459 flags: Qt::MatchFixedString | Qt::MatchCaseSensitive);
460 QCOMPARE(res.count(), 1);
461
462 res = model.match(start, role: Qt::DisplayRole, value: QVariant(".*O.*"), hits: -1,
463 flags: Qt::MatchRegularExpression);
464 QCOMPARE(res.count(), 2);
465 res = model.match(start, role: Qt::DisplayRole, value: QVariant(".*O.*"), hits: -1,
466 flags: Qt::MatchRegularExpression | Qt::MatchCaseSensitive);
467 QCOMPARE(res.count(), 0);
468
469 res = model.match(start, role: Qt::DisplayRole, value: QVariant(QRegularExpression(".*O.*")),
470 hits: -1, flags: Qt::MatchRegularExpression);
471 QCOMPARE(res.count(), 0);
472 res = model.match(start,
473 role: Qt::DisplayRole,
474 value: QVariant(QRegularExpression(".*O.*",
475 QRegularExpression::CaseInsensitiveOption)),
476 hits: -1,
477 flags: Qt::MatchRegularExpression);
478 QCOMPARE(res.count(), 2);
479
480 // Ensure that the case sensitivity is properly ignored when passing a
481 // QRegularExpression object.
482 res = model.match(start,
483 role: Qt::DisplayRole,
484 value: QVariant(QRegularExpression(".*O.*",
485 QRegularExpression::CaseInsensitiveOption)),
486 hits: -1,
487 flags: Qt::MatchRegularExpression | Qt::MatchCaseSensitive);
488 QCOMPARE(res.count(), 2);
489}
490
491typedef QPair<int, int> Position;
492typedef QVector<QPair<int, int> > Selection;
493typedef QVector<QVector<QString> > StringTable;
494typedef QVector<QString> StringTableRow;
495
496static StringTableRow qStringTableRow(const QString &s1, const QString &s2, const QString &s3)
497{
498 StringTableRow row;
499 row << s1 << s2 << s3;
500 return row;
501}
502
503#ifdef Q_CC_MSVC
504# define STRINGTABLE (StringTable())
505#else
506# define STRINGTABLE StringTable()
507#endif
508
509void tst_QAbstractItemModel::dropMimeData_data()
510{
511 QTest::addColumn<StringTable>(name: "src_table"); // drag source
512 QTest::addColumn<StringTable>(name: "dst_table"); // drop target
513 QTest::addColumn<Selection>(name: "selection"); // dragged items
514 QTest::addColumn<Position>(name: "dst_position"); // drop position
515 QTest::addColumn<StringTable>(name: "res_table"); // expected result
516
517 {
518 QTest::newRow(dataTag: "2x2 dropped at [0, 0]")
519 << (STRINGTABLE // source table
520 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
521 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
522 << (STRINGTABLE // destination table
523 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
524 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
525 << (Selection() // selection
526 << Position(0, 0) << Position(0, 1)
527 << Position(1, 0) << Position(1, 1))
528 << Position(0, 0) // drop position
529 << (STRINGTABLE // resulting table
530 << (qStringTableRow(s1: "A", s2: "B", s3: "" ))
531 << (qStringTableRow(s1: "D", s2: "E", s3: "" ))
532 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
533 << (qStringTableRow(s1: "3", s2: "4", s3: "5")));
534 }
535
536 {
537 QTest::newRow(dataTag: "2x2 dropped at [1, 0]")
538 << (STRINGTABLE // source table
539 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
540 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
541 << (STRINGTABLE // destination table
542 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
543 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
544 << (Selection() // selection
545 << Position(0, 0) << Position(0, 1)
546 << Position(1, 0) << Position(1, 1))
547 << Position(1, 0) // drop position
548 << (STRINGTABLE // resulting table
549 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
550 << (qStringTableRow(s1: "A", s2: "B", s3: "" ))
551 << (qStringTableRow(s1: "D", s2: "E", s3: "" ))
552 << (qStringTableRow(s1: "3", s2: "4", s3: "5")));
553 }
554
555 {
556 QTest::newRow(dataTag: "2x2 dropped at [3, 0]")
557 << (STRINGTABLE // source table
558 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
559 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
560 << (STRINGTABLE // destination table
561 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
562 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
563 << (Selection() // selection
564 << Position(0, 0) << Position(0, 1)
565 << Position(1, 0) << Position(1, 1))
566 << Position(3, 0) // drop position
567 << (STRINGTABLE // resulting table
568 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
569 << (qStringTableRow(s1: "3", s2: "4", s3: "5"))
570 << (qStringTableRow(s1: "A", s2: "B", s3: "" ))
571 << (qStringTableRow(s1: "D", s2: "E", s3: "" )));
572 }
573
574 {
575 QTest::newRow(dataTag: "2x2 dropped at [0, 1]")
576 << (STRINGTABLE // source table
577 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
578 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
579 << (STRINGTABLE // destination table
580 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
581 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
582 << (Selection() // selection
583 << Position(0, 0) << Position(0, 1)
584 << Position(1, 0) << Position(1, 1))
585 << Position(0, 1) // drop position
586 << (STRINGTABLE // resulting table
587 << (qStringTableRow(s1: "" , s2: "A", s3: "B"))
588 << (qStringTableRow(s1: "" , s2: "D", s3: "E"))
589 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
590 << (qStringTableRow(s1: "3", s2: "4", s3: "5")));
591 }
592
593 {
594 QTest::newRow(dataTag: "2x2 dropped at [0, 2] (line break)")
595 << (STRINGTABLE // source table
596 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
597 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
598 << (STRINGTABLE // destination table
599 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
600 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
601 << (Selection() // selection
602 << Position(0, 0) << Position(0, 1)
603 << Position(1, 0) << Position(1, 1))
604 << Position(0, 2) // drop position
605 << (STRINGTABLE // resulting table
606 << (qStringTableRow(s1: "" , s2: "" , s3: "A"))
607 << (qStringTableRow(s1: "" , s2: "" , s3: "D"))
608 << (qStringTableRow(s1: "" , s2: "" , s3: "B"))
609 << (qStringTableRow(s1: "" , s2: "" , s3: "E"))
610 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
611 << (qStringTableRow(s1: "3", s2: "4", s3: "5")));
612 }
613
614 {
615 QTest::newRow(dataTag: "2x2 dropped at [3, 2] (line break)")
616 << (STRINGTABLE // source table
617 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
618 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
619 << (STRINGTABLE // destination table
620 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
621 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
622 << (Selection() // selection
623 << Position(0, 0) << Position(0, 1)
624 << Position(1, 0) << Position(1, 1))
625 << Position(3, 2) // drop position
626 << (STRINGTABLE // resulting table
627 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
628 << (qStringTableRow(s1: "3", s2: "4", s3: "5"))
629 << (qStringTableRow(s1: "" , s2: "" , s3: "A"))
630 << (qStringTableRow(s1: "" , s2: "" , s3: "D"))
631 << (qStringTableRow(s1: "" , s2: "" , s3: "B"))
632 << (qStringTableRow(s1: "" , s2: "" , s3: "E")));
633 }
634
635 {
636 QTest::newRow(dataTag: "non-square dropped at [0, 0]")
637 << (STRINGTABLE // source table
638 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
639 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
640 << (STRINGTABLE // destination table
641 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
642 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
643 << (Selection() // selection
644 << Position(0, 0) << Position(0, 1)
645 << Position(1, 0))
646 << Position(0, 0) // drop position
647 << (STRINGTABLE // resulting table
648 << (qStringTableRow(s1: "A", s2: "B", s3: "" ))
649 << (qStringTableRow(s1: "D", s2: "" , s3: "" ))
650 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
651 << (qStringTableRow(s1: "3", s2: "4", s3: "5")));
652 }
653
654 {
655 QTest::newRow(dataTag: "non-square dropped at [0, 2]")
656 << (STRINGTABLE // source table
657 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
658 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
659 << (STRINGTABLE // destination table
660 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
661 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
662 << (Selection() // selection
663 << Position(0, 0) << Position(0, 1)
664 << Position(1, 0))
665 << Position(0, 2) // drop position
666 << (STRINGTABLE // resulting table
667 << (qStringTableRow(s1: "" , s2: "" , s3: "A"))
668 << (qStringTableRow(s1: "" , s2: "" , s3: "D"))
669 << (qStringTableRow(s1: "" , s2: "" , s3: "B"))
670 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
671 << (qStringTableRow(s1: "3", s2: "4", s3: "5")));
672 }
673
674 {
675 QTest::newRow(dataTag: "2x 1x2 dropped at [0, 0] (duplicates)")
676 << (STRINGTABLE // source table
677 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
678 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
679 << (STRINGTABLE // destination table
680 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
681 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
682 << (Selection() // selection; 2x the same row (to simulate selections in hierarchy)
683 << Position(0, 0) << Position(0, 1)
684 << Position(0, 0) << Position(0, 1))
685 << Position(0, 0) // drop position
686 << (STRINGTABLE // resulting table
687 << (qStringTableRow(s1: "A", s2: "B", s3: "" ))
688 << (qStringTableRow(s1: "A", s2: "" , s3: "" ))
689 << (qStringTableRow(s1: "" , s2: "B", s3: "" )) // ### FIXME: strange behavior, but rare case
690 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
691 << (qStringTableRow(s1: "3", s2: "4", s3: "5")));
692 }
693
694 {
695 QTest::newRow(dataTag: "2x 1x2 dropped at [3, 2] (duplicates)")
696 << (STRINGTABLE // source table
697 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
698 << (qStringTableRow(s1: "D", s2: "E", s3: "F")))
699 << (STRINGTABLE // destination table
700 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
701 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
702 << (Selection() // selection; 2x the same row (to simulate selections in hierarchy)
703 << Position(0, 0) << Position(0, 1)
704 << Position(0, 0) << Position(0, 1))
705 << Position(3, 2) // drop position
706 << (STRINGTABLE // resulting table
707 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
708 << (qStringTableRow(s1: "3", s2: "4", s3: "5"))
709 << (qStringTableRow(s1: "" , s2: "" , s3: "A"))
710 << (qStringTableRow(s1: "" , s2: "" , s3: "B"))
711 << (qStringTableRow(s1: "" , s2: "" , s3: "A"))
712 << (qStringTableRow(s1: "" , s2: "" , s3: "B")));
713 }
714 {
715 QTest::newRow(dataTag: "2x 1x2 dropped at [3, 2] (different rows)")
716 << (STRINGTABLE // source table
717 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
718 << (qStringTableRow(s1: "D", s2: "E", s3: "F"))
719 << (qStringTableRow(s1: "G", s2: "H", s3: "I")))
720 << (STRINGTABLE // destination table
721 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
722 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
723 << (Selection() // selection; 2x the same row (to simulate selections in hierarchy)
724 << Position(0, 0) << Position(0, 1)
725 << Position(2, 0) << Position(2, 1))
726 << Position(2, 1) // drop position
727 << (STRINGTABLE // resulting table
728 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
729 << (qStringTableRow(s1: "3", s2: "4", s3: "5"))
730 << (qStringTableRow(s1: "" , s2: "A" , s3: "B"))
731 << (qStringTableRow(s1: "" , s2: "G" , s3: "H")));
732 }
733
734 {
735 QTest::newRow(dataTag: "2x 1x2 dropped at [3, 2] (different rows, over the edge)")
736 << (STRINGTABLE // source table
737 << (qStringTableRow(s1: "A", s2: "B", s3: "C"))
738 << (qStringTableRow(s1: "D", s2: "E", s3: "F"))
739 << (qStringTableRow(s1: "G", s2: "H", s3: "I")))
740 << (STRINGTABLE // destination table
741 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
742 << (qStringTableRow(s1: "3", s2: "4", s3: "5")))
743 << (Selection() // selection; 2x the same row (to simulate selections in hierarchy)
744 << Position(0, 0) << Position(0, 1)
745 << Position(2, 0) << Position(2, 1))
746 << Position(3, 2) // drop position
747 << (STRINGTABLE // resulting table
748 << (qStringTableRow(s1: "0", s2: "1", s3: "2"))
749 << (qStringTableRow(s1: "3", s2: "4", s3: "5"))
750 << (qStringTableRow(s1: "" , s2: "" , s3: "A"))
751 << (qStringTableRow(s1: "" , s2: "" , s3: "G"))
752 << (qStringTableRow(s1: "" , s2: "" , s3: "B"))
753 << (qStringTableRow(s1: "" , s2: "" , s3: "H")));
754 }
755}
756
757void tst_QAbstractItemModel::dropMimeData()
758{
759 QFETCH(StringTable, src_table);
760 QFETCH(StringTable, dst_table);
761 QFETCH(Selection, selection);
762 QFETCH(Position, dst_position);
763 QFETCH(StringTable, res_table);
764
765 QtTestModel src(src_table);
766 QtTestModel dst(dst_table);
767 QtTestModel res(res_table);
768
769 // get the mimeData from the "selected" indexes
770 QModelIndexList selectedIndexes;
771 for (int i = 0; i < selection.count(); ++i)
772 selectedIndexes << src.index(row: selection.at(i).first, column: selection.at(i).second, parent: QModelIndex());
773 QMimeData *md = src.mimeData(indexes: selectedIndexes);
774 // do the drop
775 dst.dropMimeData(data: md, action: Qt::CopyAction, row: dst_position.first, column: dst_position.second, parent: QModelIndex());
776 delete md;
777
778 // compare to the expected results
779 QCOMPARE(dst.rowCount(QModelIndex()), res.rowCount(QModelIndex()));
780 QCOMPARE(dst.columnCount(QModelIndex()), res.columnCount(QModelIndex()));
781 for (int r = 0; r < dst.rowCount(parent: QModelIndex()); ++r) {
782 for (int c = 0; c < dst.columnCount(parent: QModelIndex()); ++c) {
783 QModelIndex dst_idx = dst.index(row: r, column: c, parent: QModelIndex());
784 QModelIndex res_idx = res.index(row: r, column: c, parent: QModelIndex());
785 QMap<int, QVariant> dst_data = dst.itemData(index: dst_idx);
786 QMap<int, QVariant> res_data = res.itemData(index: res_idx);
787 QCOMPARE(dst_data , res_data);
788 }
789 }
790}
791
792void tst_QAbstractItemModel::canDropMimeData()
793{
794 QtTestModel model(3, 3);
795
796 QVERIFY(model.canDropMimeData(0, Qt::CopyAction, -1, -1, QModelIndex()));
797 QVERIFY(model.canDropMimeData(0, Qt::CopyAction, 0, 0, QModelIndex()));
798 QVERIFY(!model.canDropMimeData(0, Qt::CopyAction, 1, 0, QModelIndex()));
799}
800
801void tst_QAbstractItemModel::changePersistentIndex()
802{
803 QtTestModel model(3, 3);
804 QModelIndex a = model.index(row: 1, column: 2, parent: QModelIndex());
805 QModelIndex b = model.index(row: 2, column: 1, parent: QModelIndex());
806 QPersistentModelIndex p(a);
807 QVERIFY(p == a);
808 model.setPersistent(from: a, to: b);
809 QVERIFY(p == b);
810}
811
812void tst_QAbstractItemModel::movePersistentIndex()
813{
814 QtTestModel model(3, 3);
815
816 QPersistentModelIndex a = model.index(row: 1, column: 1);
817 QVERIFY(a.isValid());
818 QCOMPARE(a.row(), 1);
819 QCOMPARE(a.column(), 1);
820
821 model.insertRow(arow: 0);
822 QCOMPARE(a.row(), 2);
823
824 model.insertRow(arow: 1);
825 QCOMPARE(a.row(), 3);
826
827 model.insertColumn(acolumn: 0);
828 QCOMPARE(a.column(), 2);
829}
830
831void tst_QAbstractItemModel::removeRows()
832{
833 QtTestModel model(10, 10);
834
835 QSignalSpy rowsAboutToBeRemovedSpy(&model, &QtTestModel::rowsAboutToBeRemoved);
836 QSignalSpy rowsRemovedSpy(&model, &QtTestModel::rowsRemoved);
837
838 QVERIFY(rowsAboutToBeRemovedSpy.isValid());
839 QVERIFY(rowsRemovedSpy.isValid());
840
841 QCOMPARE(model.removeRows(6, 4), true);
842 QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1);
843 QCOMPARE(rowsRemovedSpy.count(), 1);
844}
845
846void tst_QAbstractItemModel::removeColumns()
847{
848 QtTestModel model(10, 10);
849
850 QSignalSpy columnsAboutToBeRemovedSpy(&model, &QtTestModel::columnsAboutToBeRemoved);
851 QSignalSpy columnsRemovedSpy(&model, &QtTestModel::columnsRemoved);
852
853 QVERIFY(columnsAboutToBeRemovedSpy.isValid());
854 QVERIFY(columnsRemovedSpy.isValid());
855
856 QCOMPARE(model.removeColumns(6, 4), true);
857 QCOMPARE(columnsAboutToBeRemovedSpy.count(), 1);
858 QCOMPARE(columnsRemovedSpy.count(), 1);
859}
860
861void tst_QAbstractItemModel::insertRows()
862{
863 QtTestModel model(10, 10);
864
865 QSignalSpy rowsAboutToBeInsertedSpy(&model, &QtTestModel::rowsAboutToBeInserted);
866 QSignalSpy rowsInsertedSpy(&model, &QtTestModel::rowsInserted);
867
868 QVERIFY(rowsAboutToBeInsertedSpy.isValid());
869 QVERIFY(rowsInsertedSpy.isValid());
870
871 QCOMPARE(model.insertRows(6, 4), true);
872 QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
873 QCOMPARE(rowsInsertedSpy.count(), 1);
874}
875
876void tst_QAbstractItemModel::insertColumns()
877{
878 QtTestModel model(10, 10);
879
880 QSignalSpy columnsAboutToBeInsertedSpy(&model, &QtTestModel::columnsAboutToBeInserted);
881 QSignalSpy columnsInsertedSpy(&model, &QtTestModel::columnsInserted);
882
883 QVERIFY(columnsAboutToBeInsertedSpy.isValid());
884 QVERIFY(columnsInsertedSpy.isValid());
885
886 QCOMPARE(model.insertColumns(6, 4), true);
887 QCOMPARE(columnsAboutToBeInsertedSpy.count(), 1);
888 QCOMPARE(columnsInsertedSpy.count(), 1);
889}
890
891void tst_QAbstractItemModel::moveRows()
892{
893 QtTestModel model(10, 10);
894
895 QSignalSpy rowsAboutToBeMovedSpy(&model, &QtTestModel::rowsAboutToBeMoved);
896 QSignalSpy rowsMovedSpy(&model, &QtTestModel::rowsMoved);
897
898 QVERIFY(rowsAboutToBeMovedSpy.isValid());
899 QVERIFY(rowsMovedSpy.isValid());
900
901 QCOMPARE(model.moveRows(QModelIndex(), 6, 4, QModelIndex(), 1), true);
902 QCOMPARE(rowsAboutToBeMovedSpy.count(), 1);
903 QCOMPARE(rowsMovedSpy.count(), 1);
904}
905
906void tst_QAbstractItemModel::moveColumns()
907{
908 QtTestModel model(10, 10);
909
910 QSignalSpy columnsAboutToBeMovedSpy(&model, &QtTestModel::columnsAboutToBeMoved);
911 QSignalSpy columnsMovedSpy(&model, &QtTestModel::columnsMoved);
912
913 QVERIFY(columnsAboutToBeMovedSpy.isValid());
914 QVERIFY(columnsMovedSpy.isValid());
915
916 QCOMPARE(model.moveColumns(QModelIndex(), 6, 4, QModelIndex(), 1), true);
917 QCOMPARE(columnsAboutToBeMovedSpy.count(), 1);
918 QCOMPARE(columnsMovedSpy.count(), 1);
919
920 QCOMPARE(model.moveColumn(QModelIndex(), 4, QModelIndex(), 1), true);
921 QCOMPARE(columnsAboutToBeMovedSpy.count(), 2);
922 QCOMPARE(columnsMovedSpy.count(), 2);
923}
924
925void tst_QAbstractItemModel::reset()
926{
927 QtTestModel model(10, 10);
928
929 QSignalSpy resetSpy(&model, &QtTestModel::modelReset);
930 QVERIFY(resetSpy.isValid());
931 model.reset();
932 QCOMPARE(resetSpy.count(), 1);
933}
934
935void tst_QAbstractItemModel::complexChangesWithPersistent()
936{
937 QtTestModel model(10, 10);
938 QPersistentModelIndex a = model.index(row: 1, column: 1, parent: QModelIndex());
939 QPersistentModelIndex b = model.index(row: 9, column: 7, parent: QModelIndex());
940 QPersistentModelIndex c = model.index(row: 5, column: 6, parent: QModelIndex());
941 QPersistentModelIndex d = model.index(row: 3, column: 9, parent: QModelIndex());
942 QPersistentModelIndex e[10];
943 for (int i=0; i <10 ; i++) {
944 e[i] = model.index(row: 2, column: i , parent: QModelIndex());
945 }
946
947 QVERIFY(a == model.index(1, 1, QModelIndex()));
948 QVERIFY(b == model.index(9, 7, QModelIndex()));
949 QVERIFY(c == model.index(5, 6, QModelIndex()));
950 QVERIFY(d == model.index(3, 9, QModelIndex()));
951 for (int i=0; i <8 ; i++)
952 QVERIFY(e[i] == model.index(2, i , QModelIndex()));
953
954 //remove a bunch of columns
955 model.removeColumns(column: 2, count: 4);
956
957 QVERIFY(a == model.index(1, 1, QModelIndex()));
958 QVERIFY(b == model.index(9, 3, QModelIndex()));
959 QVERIFY(c == model.index(5, 2, QModelIndex()));
960 QVERIFY(d == model.index(3, 5, QModelIndex()));
961 for (int i=0; i <2 ; i++)
962 QVERIFY(e[i] == model.index(2, i , QModelIndex()));
963 for (int i=2; i <6 ; i++)
964 QVERIFY(!e[i].isValid());
965 for (int i=6; i <10 ; i++)
966 QVERIFY(e[i] == model.index(2, i-4 , QModelIndex()));
967
968 //move some indexes around
969 model.setPersistent(from: model.index(row: 1, column: 1 , parent: QModelIndex()), to: model.index(row: 9, column: 3 , parent: QModelIndex()));
970 model.setPersistent(from: model.index(row: 9, column: 3 , parent: QModelIndex()), to: model.index(row: 8, column: 4 , parent: QModelIndex()));
971
972 QVERIFY(a == model.index(9, 3, QModelIndex()));
973 QVERIFY(b == model.index(8, 4, QModelIndex()));
974 QVERIFY(c == model.index(5, 2, QModelIndex()));
975 QVERIFY(d == model.index(3, 5, QModelIndex()));
976 for (int i=0; i <2 ; i++)
977 QVERIFY(e[i] == model.index(2, i , QModelIndex()));
978 for (int i=2; i <6 ; i++)
979 QVERIFY(!e[i].isValid());
980 for (int i=6; i <10 ; i++)
981 QVERIFY(e[i] == model.index(2, i-4 , QModelIndex()));
982
983 //inserting a bunch of columns
984 model.insertColumns(column: 2, count: 2);
985 QVERIFY(a == model.index(9, 5, QModelIndex()));
986 QVERIFY(b == model.index(8, 6, QModelIndex()));
987 QVERIFY(c == model.index(5, 4, QModelIndex()));
988 QVERIFY(d == model.index(3, 7, QModelIndex()));
989 for (int i=0; i <2 ; i++)
990 QVERIFY(e[i] == model.index(2, i , QModelIndex()));
991 for (int i=2; i <6 ; i++)
992 QVERIFY(!e[i].isValid());
993 for (int i=6; i <10 ; i++)
994 QVERIFY(e[i] == model.index(2, i-2 , QModelIndex()));
995}
996
997void tst_QAbstractItemModel::testMoveSameParentDown_data()
998{
999 QTest::addColumn<int>(name: "startRow");
1000 QTest::addColumn<int>(name: "endRow");
1001 QTest::addColumn<int>(name: "destRow");
1002 // We can't put the actual parent index for the move in here because m_model is not defined until init() is run.
1003 QTest::addColumn<bool>(name: "topLevel");
1004
1005 // Move from the start to the middle
1006 QTest::newRow(dataTag: "move01") << 0 << 2 << 8 << true;
1007 // Move from the start to the end
1008 QTest::newRow(dataTag: "move02") << 0 << 2 << 10 << true;
1009 // Move from the middle to the middle
1010 QTest::newRow(dataTag: "move03") << 3 << 5 << 8 << true;
1011 // Move from the middle to the end
1012 QTest::newRow(dataTag: "move04") << 3 << 5 << 10 << true;
1013
1014 QTest::newRow(dataTag: "move05") << 0 << 2 << 8 << false;
1015 QTest::newRow(dataTag: "move06") << 0 << 2 << 10 << false;
1016 QTest::newRow(dataTag: "move07") << 3 << 5 << 8 << false;
1017 QTest::newRow(dataTag: "move08") << 3 << 5 << 10 << false;
1018}
1019
1020void tst_QAbstractItemModel::testMoveSameParentDown()
1021{
1022 QFETCH(int, startRow);
1023 QFETCH(int, endRow);
1024 QFETCH(int, destRow);
1025 QFETCH(bool, topLevel);
1026
1027 QModelIndex moveParent = topLevel ? QModelIndex() : m_model->index(row: 5, column: 0);
1028
1029 QList<QPersistentModelIndex> persistentList;
1030 QModelIndexList indexList;
1031
1032 for (int column = 0; column < m_model->columnCount(); ++column) {
1033 for (int row = 0; row < m_model->rowCount(); ++row) {
1034 QModelIndex idx = m_model->index(row, column);
1035 QVERIFY(idx.isValid());
1036 indexList << idx;
1037 persistentList << QPersistentModelIndex(idx);
1038 }
1039 }
1040
1041 QModelIndex parent = m_model->index(row: 5, column: 0);
1042 for (int column = 0; column < m_model->columnCount(); ++column) {
1043 for (int row = 0; row < m_model->rowCount(index: parent); ++row) {
1044 QModelIndex idx = m_model->index(row, column, parent);
1045 QVERIFY(idx.isValid());
1046 indexList << idx;
1047 persistentList << QPersistentModelIndex(idx);
1048 }
1049 }
1050
1051 QSignalSpy beforeSpy(m_model, &DynamicTreeModel::rowsAboutToBeMoved);
1052 QSignalSpy afterSpy(m_model, &DynamicTreeModel::rowsMoved);
1053
1054 QVERIFY(beforeSpy.isValid());
1055 QVERIFY(afterSpy.isValid());
1056
1057 ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
1058 moveCommand->setNumCols(4);
1059 if (!topLevel)
1060 moveCommand->setAncestorRowNumbers(QList<int>() << 5);
1061 moveCommand->setStartRow(startRow);
1062 moveCommand->setEndRow(endRow);
1063 moveCommand->setDestRow(destRow);
1064 if (!topLevel)
1065 moveCommand->setDestAncestors(QList<int>() << 5);
1066 moveCommand->doCommand();
1067
1068 QVariantList beforeSignal = beforeSpy.takeAt(i: 0);
1069 QVariantList afterSignal = afterSpy.takeAt(i: 0);
1070
1071 QCOMPARE(beforeSignal.size(), 5);
1072 QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), moveParent);
1073 QCOMPARE(beforeSignal.at(1).toInt(), startRow);
1074 QCOMPARE(beforeSignal.at(2).toInt(), endRow);
1075 QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), moveParent);
1076 QCOMPARE(beforeSignal.at(4).toInt(), destRow);
1077
1078 QCOMPARE(afterSignal.size(), 5);
1079 QCOMPARE(afterSignal.at(0).value<QModelIndex>(), moveParent);
1080 QCOMPARE(afterSignal.at(1).toInt(), startRow);
1081 QCOMPARE(afterSignal.at(2).toInt(), endRow);
1082 QCOMPARE(afterSignal.at(3).value<QModelIndex>(), moveParent);
1083 QCOMPARE(afterSignal.at(4).toInt(), destRow);
1084
1085 for (int i = 0; i < indexList.size(); i++) {
1086 QModelIndex idx = indexList.at(i);
1087 QModelIndex persistentIndex = persistentList.at(i);
1088 if (idx.parent() == moveParent) {
1089 int row = idx.row();
1090 if ( row >= startRow) {
1091 if (row <= endRow) {
1092 QCOMPARE(row + destRow - endRow - 1, persistentIndex.row());
1093 QCOMPARE(idx.column(), persistentIndex.column());
1094 QCOMPARE(idx.parent(), persistentIndex.parent());
1095 QCOMPARE(idx.model(), persistentIndex.model());
1096 } else if (row < destRow) {
1097 QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row());
1098 QCOMPARE(idx.column(), persistentIndex.column());
1099 QCOMPARE(idx.parent(), persistentIndex.parent());
1100 QCOMPARE(idx.model(), persistentIndex.model());
1101 } else {
1102 QCOMPARE(idx, persistentIndex);
1103 }
1104 } else {
1105 QCOMPARE(idx, persistentIndex);
1106 }
1107 } else {
1108 QCOMPARE(idx, persistentIndex);
1109 }
1110 }
1111}
1112
1113void tst_QAbstractItemModel::testMoveSameParentUp_data()
1114{
1115 QTest::addColumn<int>(name: "startRow");
1116 QTest::addColumn<int>(name: "endRow");
1117 QTest::addColumn<int>(name: "destRow");
1118 QTest::addColumn<bool>(name: "topLevel");
1119
1120 // Move from the middle to the start
1121 QTest::newRow(dataTag: "move01") << 5 << 7 << 0 << true;
1122 // Move from the end to the start
1123 QTest::newRow(dataTag: "move02") << 8 << 9 << 0 << true;
1124 // Move from the middle to the middle
1125 QTest::newRow(dataTag: "move03") << 5 << 7 << 2 << true;
1126 // Move from the end to the middle
1127 QTest::newRow(dataTag: "move04") << 8 << 9 << 5 << true;
1128
1129 QTest::newRow(dataTag: "move05") << 5 << 7 << 0 << false;
1130 QTest::newRow(dataTag: "move06") << 8 << 9 << 0 << false;
1131 QTest::newRow(dataTag: "move07") << 5 << 7 << 2 << false;
1132 QTest::newRow(dataTag: "move08") << 8 << 9 << 5 << false;
1133}
1134
1135void tst_QAbstractItemModel::testMoveSameParentUp()
1136{
1137 QFETCH(int, startRow);
1138 QFETCH(int, endRow);
1139 QFETCH(int, destRow);
1140 QFETCH(bool, topLevel);
1141
1142 QModelIndex moveParent = topLevel ? QModelIndex() : m_model->index(row: 5, column: 0);
1143
1144 QList<QPersistentModelIndex> persistentList;
1145 QModelIndexList indexList;
1146
1147 for (int column = 0; column < m_model->columnCount(); ++column) {
1148 for (int row = 0; row < m_model->rowCount(); ++row) {
1149 QModelIndex idx = m_model->index(row, column);
1150 QVERIFY(idx.isValid());
1151 indexList << idx;
1152 persistentList << QPersistentModelIndex(idx);
1153 }
1154 }
1155
1156 QModelIndex parent = m_model->index(row: 2, column: 0);
1157 for (int column = 0; column < m_model->columnCount(); ++column) {
1158 for (int row = 0; row < m_model->rowCount(index: parent); ++row) {
1159 QModelIndex idx = m_model->index(row, column, parent);
1160 QVERIFY(idx.isValid());
1161 indexList << idx;
1162 persistentList << QPersistentModelIndex(idx);
1163 }
1164 }
1165
1166 QSignalSpy beforeSpy(m_model, &DynamicTreeModel::rowsAboutToBeMoved);
1167 QSignalSpy afterSpy(m_model, &DynamicTreeModel::rowsMoved);
1168
1169 QVERIFY(beforeSpy.isValid());
1170 QVERIFY(afterSpy.isValid());
1171
1172 ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
1173 moveCommand->setNumCols(4);
1174 if (!topLevel)
1175 moveCommand->setAncestorRowNumbers(QList<int>() << 5);
1176 moveCommand->setStartRow(startRow);
1177 moveCommand->setEndRow(endRow);
1178 moveCommand->setDestRow(destRow);
1179 if (!topLevel)
1180 moveCommand->setDestAncestors(QList<int>() << 5);
1181 moveCommand->doCommand();
1182
1183 QVariantList beforeSignal = beforeSpy.takeAt(i: 0);
1184 QVariantList afterSignal = afterSpy.takeAt(i: 0);
1185
1186 QCOMPARE(beforeSignal.size(), 5);
1187 QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), moveParent);
1188 QCOMPARE(beforeSignal.at(1).toInt(), startRow);
1189 QCOMPARE(beforeSignal.at(2).toInt(), endRow);
1190 QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), moveParent);
1191 QCOMPARE(beforeSignal.at(4).toInt(), destRow);
1192
1193 QCOMPARE(afterSignal.size(), 5);
1194 QCOMPARE(afterSignal.at(0).value<QModelIndex>(), moveParent);
1195 QCOMPARE(afterSignal.at(1).toInt(), startRow);
1196 QCOMPARE(afterSignal.at(2).toInt(), endRow);
1197 QCOMPARE(afterSignal.at(3).value<QModelIndex>(), moveParent);
1198 QCOMPARE(afterSignal.at(4).toInt(), destRow);
1199
1200 for (int i = 0; i < indexList.size(); i++) {
1201 QModelIndex idx = indexList.at(i);
1202 QModelIndex persistentIndex = persistentList.at(i);
1203 if (idx.parent() == moveParent) {
1204 int row = idx.row();
1205 if ( row >= destRow) {
1206 if (row < startRow) {
1207 QCOMPARE(row + endRow - startRow + 1, persistentIndex.row());
1208 QCOMPARE(idx.column(), persistentIndex.column());
1209 QCOMPARE(idx.parent(), persistentIndex.parent());
1210 QCOMPARE(idx.model(), persistentIndex.model());
1211 } else if (row <= endRow) {
1212 QCOMPARE(row + destRow - startRow, persistentIndex.row());
1213 QCOMPARE(idx.column(), persistentIndex.column());
1214 QCOMPARE(idx.parent(), persistentIndex.parent());
1215 QCOMPARE(idx.model(), persistentIndex.model());
1216 } else {
1217 QCOMPARE(idx, persistentIndex);
1218 }
1219 } else {
1220 QCOMPARE(idx, persistentIndex);
1221 }
1222 } else {
1223 QCOMPARE(idx, persistentIndex);
1224 }
1225 }
1226}
1227
1228void tst_QAbstractItemModel::testMoveThroughProxy()
1229{
1230 QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
1231 proxy->setSourceModel(m_model);
1232
1233 QList<QPersistentModelIndex> persistentList;
1234
1235 persistentList.append(t: proxy->index(row: 0, column: 0));
1236 persistentList.append(t: proxy->index(row: 0, column: 0, parent: proxy->mapFromSource(sourceIndex: m_model->index(row: 5, column: 0))));
1237
1238 ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
1239 moveCommand->setNumCols(4);
1240 moveCommand->setAncestorRowNumbers(QList<int>() << 5);
1241 moveCommand->setStartRow(0);
1242 moveCommand->setEndRow(0);
1243 moveCommand->setDestRow(0);
1244 moveCommand->doCommand();
1245}
1246
1247void tst_QAbstractItemModel::testMoveToGrandParent_data()
1248{
1249 QTest::addColumn<int>(name: "startRow");
1250 QTest::addColumn<int>(name: "endRow");
1251 QTest::addColumn<int>(name: "destRow");
1252
1253 // Move from the start to the middle
1254 QTest::newRow(dataTag: "move01") << 0 << 2 << 8;
1255 // Move from the start to the end
1256 QTest::newRow(dataTag: "move02") << 0 << 2 << 10;
1257 // Move from the middle to the middle
1258 QTest::newRow(dataTag: "move03") << 3 << 5 << 8;
1259 // Move from the middle to the end
1260 QTest::newRow(dataTag: "move04") << 3 << 5 << 10;
1261
1262 // Move from the middle to the start
1263 QTest::newRow(dataTag: "move05") << 5 << 7 << 0;
1264 // Move from the end to the start
1265 QTest::newRow(dataTag: "move06") << 8 << 9 << 0;
1266 // Move from the middle to the middle
1267 QTest::newRow(dataTag: "move07") << 5 << 7 << 2;
1268 // Move from the end to the middle
1269 QTest::newRow(dataTag: "move08") << 8 << 9 << 5;
1270
1271 // Moving to the same row in a different parent doesn't confuse things.
1272 QTest::newRow(dataTag: "move09") << 8 << 8 << 8;
1273
1274 // Moving to the row of my parent and its neighbours doesn't confuse things
1275 QTest::newRow(dataTag: "move10") << 8 << 8 << 4;
1276 QTest::newRow(dataTag: "move11") << 8 << 8 << 5;
1277 QTest::newRow(dataTag: "move12") << 8 << 8 << 6;
1278
1279 // Moving everything from one parent to another
1280 QTest::newRow(dataTag: "move13") << 0 << 9 << 10;
1281 QTest::newRow(dataTag: "move14") << 0 << 9 << 0;
1282}
1283
1284void tst_QAbstractItemModel::testMoveToGrandParent()
1285{
1286 QFETCH(int, startRow);
1287 QFETCH(int, endRow);
1288 QFETCH(int, destRow);
1289
1290 QList<QPersistentModelIndex> persistentList;
1291 QModelIndexList indexList;
1292 QModelIndexList parentsList;
1293
1294 for (int column = 0; column < m_model->columnCount(); ++column) {
1295 for (int row = 0; row < m_model->rowCount(); ++row) {
1296 QModelIndex idx = m_model->index(row, column);
1297 QVERIFY(idx.isValid());
1298 indexList << idx;
1299 parentsList << idx.parent();
1300 persistentList << QPersistentModelIndex(idx);
1301 }
1302 }
1303
1304 QModelIndex sourceIndex = m_model->index(row: 5, column: 0);
1305 for (int column = 0; column < m_model->columnCount(); ++column) {
1306 for (int row = 0; row < m_model->rowCount(index: sourceIndex); ++row) {
1307 QModelIndex idx = m_model->index(row, column, parent: sourceIndex);
1308 QVERIFY(idx.isValid());
1309 indexList << idx;
1310 parentsList << idx.parent();
1311 persistentList << QPersistentModelIndex(idx);
1312 }
1313 }
1314
1315 QSignalSpy beforeSpy(m_model, &DynamicTreeModel::rowsAboutToBeMoved);
1316 QSignalSpy afterSpy(m_model, &DynamicTreeModel::rowsMoved);
1317
1318 QVERIFY(beforeSpy.isValid());
1319 QVERIFY(afterSpy.isValid());
1320
1321 QPersistentModelIndex persistentSource = sourceIndex;
1322
1323 ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
1324 moveCommand->setAncestorRowNumbers(QList<int>() << 5);
1325 moveCommand->setNumCols(4);
1326 moveCommand->setStartRow(startRow);
1327 moveCommand->setEndRow(endRow);
1328 moveCommand->setDestRow(destRow);
1329 moveCommand->doCommand();
1330
1331 QVariantList beforeSignal = beforeSpy.takeAt(i: 0);
1332 QVariantList afterSignal = afterSpy.takeAt(i: 0);
1333
1334 QCOMPARE(beforeSignal.size(), 5);
1335 QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), sourceIndex);
1336 QCOMPARE(beforeSignal.at(1).toInt(), startRow);
1337 QCOMPARE(beforeSignal.at(2).toInt(), endRow);
1338 QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), QModelIndex());
1339 QCOMPARE(beforeSignal.at(4).toInt(), destRow);
1340
1341 QCOMPARE(afterSignal.size(), 5);
1342 QCOMPARE(afterSignal.at(0).value<QModelIndex>(), static_cast<QModelIndex>(persistentSource));
1343 QCOMPARE(afterSignal.at(1).toInt(), startRow);
1344 QCOMPARE(afterSignal.at(2).toInt(), endRow);
1345 QCOMPARE(afterSignal.at(3).value<QModelIndex>(), QModelIndex());
1346 QCOMPARE(afterSignal.at(4).toInt(), destRow);
1347
1348 for (int i = 0; i < indexList.size(); i++) {
1349 QModelIndex idx = indexList.at(i);
1350 QModelIndex idxParent = parentsList.at(i);
1351 QModelIndex persistentIndex = persistentList.at(i);
1352 int row = idx.row();
1353 if (idxParent == QModelIndex()) {
1354 if (row >= destRow) {
1355 QCOMPARE(row + endRow - startRow + 1, persistentIndex.row());
1356 QCOMPARE(idx.column(), persistentIndex.column());
1357 QCOMPARE(idxParent, persistentIndex.parent());
1358 QCOMPARE(idx.model(), persistentIndex.model());
1359 } else {
1360 QCOMPARE(idx, persistentIndex);
1361 }
1362 } else {
1363 if (row < startRow) {
1364 QCOMPARE(idx, persistentIndex);
1365 } else if (row <= endRow) {
1366 QCOMPARE(row + destRow - startRow, persistentIndex.row());
1367 QCOMPARE(idx.column(), persistentIndex.column());
1368 QCOMPARE(QModelIndex(), persistentIndex.parent());
1369 QCOMPARE(idx.model(), persistentIndex.model());
1370 } else {
1371 QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row());
1372 QCOMPARE(idx.column(), persistentIndex.column());
1373
1374 if (idxParent.row() >= destRow) {
1375 QModelIndex adjustedParent;
1376 adjustedParent = idxParent.sibling(arow: idxParent.row() + endRow - startRow + 1, acolumn: idxParent.column());
1377 QCOMPARE(adjustedParent, persistentIndex.parent());
1378 } else {
1379 QCOMPARE(idxParent, persistentIndex.parent());
1380 }
1381 QCOMPARE(idx.model(), persistentIndex.model());
1382 }
1383 }
1384 }
1385}
1386
1387void tst_QAbstractItemModel::testMoveToSibling_data()
1388{
1389 QTest::addColumn<int>(name: "startRow");
1390 QTest::addColumn<int>(name: "endRow");
1391 QTest::addColumn<int>(name: "destRow");
1392
1393 // Move from the start to the middle
1394 QTest::newRow(dataTag: "move01") << 0 << 2 << 8;
1395 // Move from the start to the end
1396 QTest::newRow(dataTag: "move02") << 0 << 2 << 10;
1397 // Move from the middle to the middle
1398 QTest::newRow(dataTag: "move03") << 2 << 4 << 8;
1399 // Move from the middle to the end
1400 QTest::newRow(dataTag: "move04") << 2 << 4 << 10;
1401
1402 // Move from the middle to the start
1403 QTest::newRow(dataTag: "move05") << 8 << 8 << 0;
1404 // Move from the end to the start
1405 QTest::newRow(dataTag: "move06") << 8 << 9 << 0;
1406 // Move from the middle to the middle
1407 QTest::newRow(dataTag: "move07") << 6 << 8 << 2;
1408 // Move from the end to the middle
1409 QTest::newRow(dataTag: "move08") << 8 << 9 << 5;
1410
1411 // Moving to the same row in a different parent doesn't confuse things.
1412 QTest::newRow(dataTag: "move09") << 8 << 8 << 8;
1413
1414 // Moving to the row of my target and its neighbours doesn't confuse things
1415 QTest::newRow(dataTag: "move10") << 8 << 8 << 4;
1416 QTest::newRow(dataTag: "move11") << 8 << 8 << 5;
1417 QTest::newRow(dataTag: "move12") << 8 << 8 << 6;
1418
1419 // Move such that the destination parent no longer valid after the move.
1420 // The destination parent is always QMI(5, 0), but after this move the
1421 // row count is 5, so (5, 0) (used internally in QAIM) no longer refers to a valid index.
1422 QTest::newRow(dataTag: "move13") << 0 << 4 << 0;
1423}
1424
1425void tst_QAbstractItemModel::testMoveToSibling()
1426{
1427 QFETCH(int, startRow);
1428 QFETCH(int, endRow);
1429 QFETCH(int, destRow);
1430
1431 QList<QPersistentModelIndex> persistentList;
1432 QModelIndexList indexList;
1433 QModelIndexList parentsList;
1434
1435 const int column = 0;
1436
1437 for (int i = 0; i < m_model->rowCount(); ++i) {
1438 QModelIndex idx = m_model->index(row: i, column);
1439 QVERIFY(idx.isValid());
1440 indexList << idx;
1441 parentsList << idx.parent();
1442 persistentList << QPersistentModelIndex(idx);
1443 }
1444
1445 QModelIndex destIndex = m_model->index(row: 5, column: 0);
1446 QModelIndex sourceIndex;
1447 for (int i = 0; i < m_model->rowCount(index: destIndex); ++i) {
1448 QModelIndex idx = m_model->index(row: i, column, parent: destIndex);
1449 QVERIFY(idx.isValid());
1450 indexList << idx;
1451 parentsList << idx.parent();
1452 persistentList << QPersistentModelIndex(idx);
1453 }
1454
1455 QSignalSpy beforeSpy(m_model, &DynamicTreeModel::rowsAboutToBeMoved);
1456 QSignalSpy afterSpy(m_model, &DynamicTreeModel::rowsMoved);
1457
1458 QVERIFY(beforeSpy.isValid());
1459 QVERIFY(afterSpy.isValid());
1460
1461 QPersistentModelIndex persistentDest = destIndex;
1462
1463 ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
1464 moveCommand->setNumCols(4);
1465 moveCommand->setStartRow(startRow);
1466 moveCommand->setEndRow(endRow);
1467 moveCommand->setDestAncestors(QList<int>() << 5);
1468 moveCommand->setDestRow(destRow);
1469 moveCommand->doCommand();
1470
1471 QVariantList beforeSignal = beforeSpy.takeAt(i: 0);
1472 QVariantList afterSignal = afterSpy.takeAt(i: 0);
1473
1474 QCOMPARE(beforeSignal.size(), 5);
1475 QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), sourceIndex);
1476 QCOMPARE(beforeSignal.at(1).toInt(), startRow);
1477 QCOMPARE(beforeSignal.at(2).toInt(), endRow);
1478 QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), destIndex);
1479 QCOMPARE(beforeSignal.at(4).toInt(), destRow);
1480
1481 QCOMPARE(afterSignal.size(), 5);
1482 QCOMPARE(afterSignal.at(0).value<QModelIndex>(), sourceIndex);
1483 QCOMPARE(afterSignal.at(1).toInt(), startRow);
1484 QCOMPARE(afterSignal.at(2).toInt(), endRow);
1485 QCOMPARE(afterSignal.at(3).value<QModelIndex>(), static_cast<QModelIndex>(persistentDest));
1486 QCOMPARE(afterSignal.at(4).toInt(), destRow);
1487
1488 for (int i = 0; i < indexList.size(); i++) {
1489 QModelIndex idx = indexList.at(i);
1490 QModelIndex idxParent = parentsList.at(i);
1491 QModelIndex persistentIndex = persistentList.at(i);
1492
1493 QModelIndex adjustedDestination = destIndex.sibling(arow: destIndex.row() - (endRow - startRow + 1), acolumn: destIndex.column());
1494 int row = idx.row();
1495 if (idxParent == destIndex) {
1496 if (row >= destRow) {
1497 QCOMPARE(row + endRow - startRow + 1, persistentIndex.row());
1498 QCOMPARE(idx.column(), persistentIndex.column());
1499 if (idxParent.row() > startRow) {
1500 QCOMPARE(adjustedDestination, persistentIndex.parent());
1501 } else {
1502 QCOMPARE(destIndex, persistentIndex.parent());
1503 }
1504 QCOMPARE(idx.model(), persistentIndex.model());
1505 } else {
1506 QCOMPARE(idx, persistentIndex);
1507 }
1508 } else {
1509 if (row < startRow) {
1510 QCOMPARE(idx, persistentIndex);
1511 } else if (row <= endRow) {
1512 QCOMPARE(row + destRow - startRow, persistentIndex.row());
1513 QCOMPARE(idx.column(), persistentIndex.column());
1514 if (destIndex.row() > startRow) {
1515 QCOMPARE(adjustedDestination, persistentIndex.parent());
1516 } else {
1517 QCOMPARE(destIndex, persistentIndex.parent());
1518 }
1519
1520 QCOMPARE(idx.model(), persistentIndex.model());
1521 } else {
1522 QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row());
1523 QCOMPARE(idx.column(), persistentIndex.column());
1524 QCOMPARE(idxParent, persistentIndex.parent());
1525 QCOMPARE(idx.model(), persistentIndex.model());
1526 }
1527 }
1528 }
1529}
1530
1531void tst_QAbstractItemModel::testMoveToUncle_data()
1532{
1533 QTest::addColumn<int>(name: "startRow");
1534 QTest::addColumn<int>(name: "endRow");
1535 QTest::addColumn<int>(name: "destRow");
1536
1537 // Move from the start to the middle
1538 QTest::newRow(dataTag: "move01") << 0 << 2 << 8;
1539 // Move from the start to the end
1540 QTest::newRow(dataTag: "move02") << 0 << 2 << 10;
1541 // Move from the middle to the middle
1542 QTest::newRow(dataTag: "move03") << 3 << 5 << 8;
1543 // Move from the middle to the end
1544 QTest::newRow(dataTag: "move04") << 3 << 5 << 10;
1545
1546 // Move from the middle to the start
1547 QTest::newRow(dataTag: "move05") << 5 << 7 << 0;
1548 // Move from the end to the start
1549 QTest::newRow(dataTag: "move06") << 8 << 9 << 0;
1550 // Move from the middle to the middle
1551 QTest::newRow(dataTag: "move07") << 5 << 7 << 2;
1552 // Move from the end to the middle
1553 QTest::newRow(dataTag: "move08") << 8 << 9 << 5;
1554
1555 // Moving to the same row in a different parent doesn't confuse things.
1556 QTest::newRow(dataTag: "move09") << 8 << 8 << 8;
1557
1558 // Moving to the row of my parent and its neighbours doesn't confuse things
1559 QTest::newRow(dataTag: "move10") << 8 << 8 << 4;
1560 QTest::newRow(dataTag: "move11") << 8 << 8 << 5;
1561 QTest::newRow(dataTag: "move12") << 8 << 8 << 6;
1562
1563 // Moving everything from one parent to another
1564 QTest::newRow(dataTag: "move13") << 0 << 9 << 10;
1565}
1566
1567void tst_QAbstractItemModel::testMoveToUncle()
1568{
1569 // Need to have some extra rows available.
1570 ModelInsertCommand *insertCommand = new ModelInsertCommand(m_model, this);
1571 insertCommand->setAncestorRowNumbers(QList<int>() << 9);
1572 insertCommand->setNumCols(4);
1573 insertCommand->setStartRow(0);
1574 insertCommand->setEndRow(9);
1575 insertCommand->doCommand();
1576
1577 QFETCH(int, startRow);
1578 QFETCH(int, endRow);
1579 QFETCH(int, destRow);
1580
1581 QList<QPersistentModelIndex> persistentList;
1582 QModelIndexList indexList;
1583 QModelIndexList parentsList;
1584
1585 const int column = 0;
1586
1587 QModelIndex sourceIndex = m_model->index(row: 9, column: 0);
1588 for (int i = 0; i < m_model->rowCount(index: sourceIndex); ++i) {
1589 QModelIndex idx = m_model->index(row: i, column, parent: sourceIndex);
1590 QVERIFY(idx.isValid());
1591 indexList << idx;
1592 parentsList << idx.parent();
1593 persistentList << QPersistentModelIndex(idx);
1594 }
1595
1596 QModelIndex destIndex = m_model->index(row: 5, column: 0);
1597 for (int i = 0; i < m_model->rowCount(index: destIndex); ++i) {
1598 QModelIndex idx = m_model->index(row: i, column, parent: destIndex);
1599 QVERIFY(idx.isValid());
1600 indexList << idx;
1601 parentsList << idx.parent();
1602 persistentList << QPersistentModelIndex(idx);
1603 }
1604
1605 QSignalSpy beforeSpy(m_model, &DynamicTreeModel::rowsAboutToBeMoved);
1606 QSignalSpy afterSpy(m_model, &DynamicTreeModel::rowsMoved);
1607
1608 QVERIFY(beforeSpy.isValid());
1609 QVERIFY(afterSpy.isValid());
1610
1611 ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
1612 moveCommand->setAncestorRowNumbers(QList<int>() << 9);
1613 moveCommand->setNumCols(4);
1614 moveCommand->setStartRow(startRow);
1615 moveCommand->setEndRow(endRow);
1616 moveCommand->setDestAncestors(QList<int>() << 5);
1617 moveCommand->setDestRow(destRow);
1618 moveCommand->doCommand();
1619
1620 QVariantList beforeSignal = beforeSpy.takeAt(i: 0);
1621 QVariantList afterSignal = afterSpy.takeAt(i: 0);
1622
1623 QCOMPARE(beforeSignal.size(), 5);
1624 QCOMPARE(beforeSignal.at(0).value<QModelIndex>(), sourceIndex);
1625 QCOMPARE(beforeSignal.at(1).toInt(), startRow);
1626 QCOMPARE(beforeSignal.at(2).toInt(), endRow);
1627 QCOMPARE(beforeSignal.at(3).value<QModelIndex>(), destIndex);
1628 QCOMPARE(beforeSignal.at(4).toInt(), destRow);
1629
1630 QCOMPARE(afterSignal.size(), 5);
1631 QCOMPARE(afterSignal.at(0).value<QModelIndex>(), sourceIndex);
1632 QCOMPARE(afterSignal.at(1).toInt(), startRow);
1633 QCOMPARE(afterSignal.at(2).toInt(), endRow);
1634 QCOMPARE(afterSignal.at(3).value<QModelIndex>(), destIndex);
1635 QCOMPARE(afterSignal.at(4).toInt(), destRow);
1636
1637 for (int i = 0; i < indexList.size(); i++) {
1638 QModelIndex idx = indexList.at(i);
1639 QModelIndex idxParent = parentsList.at(i);
1640 QModelIndex persistentIndex = persistentList.at(i);
1641
1642 int row = idx.row();
1643 if (idxParent == destIndex) {
1644 if (row >= destRow) {
1645 QCOMPARE(row + endRow - startRow + 1, persistentIndex.row());
1646 QCOMPARE(idx.column(), persistentIndex.column());
1647 QCOMPARE(destIndex, persistentIndex.parent());
1648 QCOMPARE(idx.model(), persistentIndex.model());
1649 } else {
1650 QCOMPARE(idx, persistentIndex);
1651 }
1652 } else {
1653 if (row < startRow) {
1654 QCOMPARE(idx, persistentIndex);
1655 } else if (row <= endRow) {
1656 QCOMPARE(row + destRow - startRow, persistentIndex.row());
1657 QCOMPARE(idx.column(), persistentIndex.column());
1658 QCOMPARE(destIndex, persistentIndex.parent());
1659 QCOMPARE(idx.model(), persistentIndex.model());
1660 } else {
1661 QCOMPARE(row - (endRow - startRow + 1), persistentIndex.row());
1662 QCOMPARE(idx.column(), persistentIndex.column());
1663 QCOMPARE(idxParent, persistentIndex.parent());
1664 QCOMPARE(idx.model(), persistentIndex.model());
1665 }
1666 }
1667 }
1668}
1669
1670void tst_QAbstractItemModel::testMoveToDescendants()
1671{
1672 // Attempt to move a row to its ancestors depth rows deep.
1673 const int depth = 6;
1674
1675 // Need to have some extra rows available in a tree.
1676 QList<int> rows;
1677 ModelInsertCommand *insertCommand;
1678 for (int i = 0; i < depth; i++) {
1679 insertCommand = new ModelInsertCommand(m_model, this);
1680 insertCommand->setAncestorRowNumbers(rows);
1681 insertCommand->setNumCols(4);
1682 insertCommand->setStartRow(0);
1683 insertCommand->setEndRow(9);
1684 insertCommand->doCommand();
1685 rows << 9;
1686 }
1687
1688 QList<QPersistentModelIndex> persistentList;
1689 QModelIndexList indexList;
1690 QModelIndexList parentsList;
1691
1692 const int column = 0;
1693
1694 QModelIndex sourceIndex = m_model->index(row: 9, column: 0);
1695 for (int i = 0; i < m_model->rowCount(index: sourceIndex); ++i) {
1696 QModelIndex idx = m_model->index(row: i, column, parent: sourceIndex);
1697 QVERIFY(idx.isValid());
1698 indexList << idx;
1699 parentsList << idx.parent();
1700 persistentList << QPersistentModelIndex(idx);
1701 }
1702
1703 QModelIndex destIndex = m_model->index(row: 5, column: 0);
1704 for (int i = 0; i < m_model->rowCount(index: destIndex); ++i) {
1705 QModelIndex idx = m_model->index(row: i, column, parent: destIndex);
1706 QVERIFY(idx.isValid());
1707 indexList << idx;
1708 parentsList << idx.parent();
1709 persistentList << QPersistentModelIndex(idx);
1710 }
1711
1712 QSignalSpy beforeSpy(m_model, &DynamicTreeModel::rowsAboutToBeMoved);
1713 QSignalSpy afterSpy(m_model, &DynamicTreeModel::rowsMoved);
1714
1715 QVERIFY(beforeSpy.isValid());
1716 QVERIFY(afterSpy.isValid());
1717
1718 ModelMoveCommand *moveCommand;
1719 QList<int> ancestors;
1720 while (ancestors.size() < depth) {
1721 ancestors << 9;
1722 for (int row = 0; row <= 9; row++) {
1723 moveCommand = new ModelMoveCommand(m_model, this);
1724 moveCommand->setNumCols(4);
1725 moveCommand->setStartRow(9);
1726 moveCommand->setEndRow(9);
1727 moveCommand->setDestAncestors(ancestors);
1728 moveCommand->setDestRow(row);
1729 moveCommand->doCommand();
1730
1731 QCOMPARE(beforeSpy.size(), 0);
1732 QCOMPARE(afterSpy.size(), 0);
1733 }
1734 }
1735}
1736
1737void tst_QAbstractItemModel::testMoveWithinOwnRange_data()
1738{
1739 QTest::addColumn<int>(name: "startRow");
1740 QTest::addColumn<int>(name: "endRow");
1741 QTest::addColumn<int>(name: "destRow");
1742
1743 QTest::newRow(dataTag: "move01") << 0 << 0 << 0;
1744 QTest::newRow(dataTag: "move02") << 0 << 0 << 1;
1745 QTest::newRow(dataTag: "move03") << 0 << 5 << 0;
1746 QTest::newRow(dataTag: "move04") << 0 << 5 << 1;
1747 QTest::newRow(dataTag: "move05") << 0 << 5 << 2;
1748 QTest::newRow(dataTag: "move06") << 0 << 5 << 3;
1749 QTest::newRow(dataTag: "move07") << 0 << 5 << 4;
1750 QTest::newRow(dataTag: "move08") << 0 << 5 << 5;
1751 QTest::newRow(dataTag: "move09") << 0 << 5 << 6;
1752 QTest::newRow(dataTag: "move10") << 3 << 5 << 5;
1753 QTest::newRow(dataTag: "move11") << 3 << 5 << 6;
1754 QTest::newRow(dataTag: "move12") << 4 << 5 << 5;
1755 QTest::newRow(dataTag: "move13") << 4 << 5 << 6;
1756 QTest::newRow(dataTag: "move14") << 5 << 5 << 5;
1757 QTest::newRow(dataTag: "move15") << 5 << 5 << 6;
1758 QTest::newRow(dataTag: "move16") << 5 << 9 << 9;
1759 QTest::newRow(dataTag: "move17") << 5 << 9 << 10;
1760 QTest::newRow(dataTag: "move18") << 6 << 9 << 9;
1761 QTest::newRow(dataTag: "move19") << 6 << 9 << 10;
1762 QTest::newRow(dataTag: "move20") << 7 << 9 << 9;
1763 QTest::newRow(dataTag: "move21") << 7 << 9 << 10;
1764 QTest::newRow(dataTag: "move22") << 8 << 9 << 9;
1765 QTest::newRow(dataTag: "move23") << 8 << 9 << 10;
1766 QTest::newRow(dataTag: "move24") << 9 << 9 << 9;
1767 QTest::newRow(dataTag: "move25") << 0 << 9 << 10;
1768}
1769
1770void tst_QAbstractItemModel::testMoveWithinOwnRange()
1771{
1772 QFETCH(int, startRow);
1773 QFETCH(int, endRow);
1774 QFETCH(int, destRow);
1775
1776 QSignalSpy beforeSpy(m_model, &DynamicTreeModel::rowsAboutToBeMoved);
1777 QSignalSpy afterSpy(m_model, &DynamicTreeModel::rowsMoved);
1778
1779 QVERIFY(beforeSpy.isValid());
1780 QVERIFY(afterSpy.isValid());
1781
1782 ModelMoveCommand *moveCommand = new ModelMoveCommand(m_model, this);
1783 moveCommand->setNumCols(4);
1784 moveCommand->setStartRow(startRow);
1785 moveCommand->setEndRow(endRow);
1786 moveCommand->setDestRow(destRow);
1787 moveCommand->doCommand();
1788
1789 QCOMPARE(beforeSpy.size(), 0);
1790 QCOMPARE(afterSpy.size(), 0);
1791}
1792
1793class ListenerObject : public QObject
1794{
1795 Q_OBJECT
1796public:
1797 ListenerObject(QAbstractProxyModel *parent);
1798
1799protected:
1800 void fillIndexStores(const QModelIndex &parent);
1801
1802public slots:
1803 void slotAboutToBeReset();
1804 void slotReset();
1805
1806private:
1807 QAbstractProxyModel *m_model;
1808 QList<QPersistentModelIndex> m_persistentIndexes;
1809 QModelIndexList m_nonPersistentIndexes;
1810};
1811
1812
1813class ModelWithCustomRole : public QStringListModel
1814{
1815 Q_OBJECT
1816public:
1817 using QStringListModel::QStringListModel;
1818
1819 QHash<int, QByteArray> roleNames() const override
1820 {
1821 return {{Qt::UserRole + 1, QByteArrayLiteral("custom")}};
1822 }
1823};
1824
1825ListenerObject::ListenerObject(QAbstractProxyModel *parent)
1826 : QObject(parent), m_model(parent)
1827{
1828 connect(asender: m_model, SIGNAL(modelAboutToBeReset()), SLOT(slotAboutToBeReset()));
1829 connect(asender: m_model, SIGNAL(modelReset()), SLOT(slotReset()));
1830
1831 fillIndexStores(parent: QModelIndex());
1832}
1833
1834void ListenerObject::fillIndexStores(const QModelIndex &parent)
1835{
1836 const int column = 0;
1837 int row = 0;
1838 QModelIndex idx = m_model->index(row, column, parent);
1839 while (idx.isValid()) {
1840 m_persistentIndexes << QPersistentModelIndex(idx);
1841 m_nonPersistentIndexes << idx;
1842 if (m_model->hasChildren(parent: idx))
1843 fillIndexStores(parent: idx);
1844 ++row;
1845 idx = m_model->index(row, column, parent);
1846 }
1847}
1848
1849void ListenerObject::slotAboutToBeReset()
1850{
1851 // Nothing has been changed yet. All indexes should be the same.
1852 for (int i = 0; i < m_persistentIndexes.size(); ++i) {
1853 QModelIndex idx = m_persistentIndexes.at(i);
1854 QCOMPARE(idx, m_nonPersistentIndexes.at(i));
1855 QVERIFY(m_model->mapToSource(idx).isValid());
1856 }
1857}
1858
1859void ListenerObject::slotReset()
1860{
1861 for (const auto &idx : qAsConst(t&: m_persistentIndexes)) {
1862 QVERIFY(!idx.isValid());
1863 }
1864}
1865
1866void tst_QAbstractItemModel::testReset()
1867{
1868 QSignalSpy beforeResetSpy(m_model, &DynamicTreeModel::modelAboutToBeReset);
1869 QSignalSpy afterResetSpy(m_model, &DynamicTreeModel::modelReset);
1870
1871 QVERIFY(beforeResetSpy.isValid());
1872 QVERIFY(afterResetSpy.isValid());
1873
1874 QSortFilterProxyModel *nullProxy = new QSortFilterProxyModel(this);
1875 nullProxy->setSourceModel(m_model);
1876
1877 // Makes sure the model and proxy are in a consistent state. before and after reset.
1878 ListenerObject *listener = new ListenerObject(nullProxy);
1879
1880 ModelResetCommandFixed *resetCommand = new ModelResetCommandFixed(m_model, this);
1881
1882 resetCommand->setNumCols(4);
1883 resetCommand->setStartRow(0);
1884 resetCommand->setEndRow(0);
1885 resetCommand->setDestRow(0);
1886 resetCommand->setDestAncestors(QList<int>() << 5);
1887 resetCommand->doCommand();
1888
1889 // Verify that the correct signals were emitted
1890 QCOMPARE(beforeResetSpy.size(), 1);
1891 QCOMPARE(afterResetSpy.size(), 1);
1892
1893 // Verify that the move actually happened.
1894 QCOMPARE(m_model->rowCount(), 9);
1895 QModelIndex destIndex = m_model->index(row: 4, column: 0);
1896 QCOMPARE(m_model->rowCount(destIndex), 11);
1897
1898 // Delete it because its slots test things which are not true after this point.
1899 delete listener;
1900
1901 QSignalSpy proxyBeforeResetSpy(nullProxy, &QSortFilterProxyModel::modelAboutToBeReset);
1902 QSignalSpy proxyAfterResetSpy(nullProxy, &QSortFilterProxyModel::modelReset);
1903
1904 // Before setting it, it does not have custom roles.
1905 QCOMPARE(nullProxy->roleNames().value(Qt::UserRole + 1), QByteArray());
1906
1907 nullProxy->setSourceModel(new ModelWithCustomRole(this));
1908 QCOMPARE(proxyBeforeResetSpy.size(), 1);
1909 QCOMPARE(proxyAfterResetSpy.size(), 1);
1910
1911 QCOMPARE(nullProxy->roleNames().value(Qt::UserRole + 1), QByteArray("custom"));
1912
1913 nullProxy->setSourceModel(m_model);
1914 QCOMPARE(proxyBeforeResetSpy.size(), 2);
1915 QCOMPARE(proxyAfterResetSpy.size(), 2);
1916
1917 // After being reset the proxy must be queried again.
1918 QCOMPARE(nullProxy->roleNames().value(Qt::UserRole + 1), QByteArray());
1919}
1920
1921class CustomRoleModel : public QStringListModel
1922{
1923 Q_OBJECT
1924 Q_ENUMS(Roles)
1925public:
1926 enum Roles {
1927 Custom1 = Qt::UserRole + 1,
1928 Custom2,
1929 UserRole
1930 };
1931
1932 CustomRoleModel(QObject *parent = 0)
1933 : QStringListModel(QStringList() << "a" << "b" << "c", parent)
1934 {
1935 }
1936
1937 void emitSignals()
1938 {
1939 const QModelIndex top = index(row: 0, column: 0);
1940 const QModelIndex bottom = index(row: 2, column: 0);
1941
1942 emit dataChanged(topLeft: top, bottomRight: bottom);
1943 emit dataChanged(topLeft: top, bottomRight: bottom, roles: QVector<int>() << Qt::ToolTipRole);
1944 emit dataChanged(topLeft: top, bottomRight: bottom, roles: QVector<int>() << Qt::ToolTipRole << Custom1);
1945 }
1946};
1947
1948void tst_QAbstractItemModel::testDataChanged()
1949{
1950 CustomRoleModel model;
1951
1952 QSignalSpy withRoles(&model, &CustomRoleModel::dataChanged);
1953 QSignalSpy withoutRoles(&model, &CustomRoleModel::dataChanged);
1954
1955 QVERIFY(withRoles.isValid());
1956 QVERIFY(withoutRoles.isValid());
1957
1958 model.emitSignals();
1959
1960 QCOMPARE(withRoles.size(), withoutRoles.size());
1961 QCOMPARE(withRoles.size(), 3);
1962
1963 const QVariantList secondEmission = withRoles.at(i: 1);
1964 const QVariantList thirdEmission = withRoles.at(i: 2);
1965
1966 const QVector<int> secondRoles = secondEmission.at(i: 2).value<QVector<int> >();
1967 const QVector<int> thirdRoles = thirdEmission.at(i: 2).value<QVector<int> >();
1968
1969 QCOMPARE(secondRoles.size(), 1);
1970 QVERIFY(secondRoles.contains(Qt::ToolTipRole));
1971
1972 QCOMPARE(thirdRoles.size(), 2);
1973 QVERIFY(thirdRoles.contains(Qt::ToolTipRole));
1974 QVERIFY(thirdRoles.contains(CustomRoleModel::Custom1));
1975}
1976
1977Q_DECLARE_METATYPE(QList<QPersistentModelIndex>)
1978
1979class SignalArgumentChecker : public QObject
1980{
1981 Q_OBJECT
1982public:
1983 SignalArgumentChecker(const QModelIndex &p1, const QModelIndex &p2, QObject *parent = 0)
1984 : QObject(parent), m_p1(p1), m_p2(p2), m_p1Persistent(p1), m_p2Persistent(p2)
1985 {
1986 connect(asender: p1.model(), SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>)), SLOT(layoutAboutToBeChanged(QList<QPersistentModelIndex>)));
1987 connect(asender: p1.model(), SIGNAL(layoutChanged(QList<QPersistentModelIndex>)), SLOT(layoutChanged(QList<QPersistentModelIndex>)));
1988 }
1989
1990private slots:
1991 void layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents)
1992 {
1993 QCOMPARE(parents.size(), 2);
1994 QVERIFY(parents.first() != parents.at(1));
1995 QVERIFY(parents.contains(m_p1));
1996 QVERIFY(parents.contains(m_p2));
1997 }
1998
1999 void layoutChanged(const QList<QPersistentModelIndex> &parents)
2000 {
2001 QCOMPARE(parents.size(), 2);
2002 QVERIFY(parents.first() != parents.at(1));
2003 QVERIFY(parents.contains(m_p1Persistent));
2004 QVERIFY(parents.contains(m_p2Persistent));
2005 QVERIFY(!parents.contains(m_p2)); // Has changed
2006 }
2007
2008private:
2009 QModelIndex m_p1;
2010 QModelIndex m_p2;
2011 QPersistentModelIndex m_p1Persistent;
2012 QPersistentModelIndex m_p2Persistent;
2013};
2014
2015void tst_QAbstractItemModel::testChildrenLayoutsChanged()
2016{
2017 DynamicTreeModel model;
2018
2019 ModelInsertCommand *insertCommand = new ModelInsertCommand(&model, this);
2020 insertCommand->setStartRow(0);
2021 insertCommand->setEndRow(9);
2022 insertCommand->doCommand();
2023
2024 insertCommand = new ModelInsertCommand(&model, this);
2025 insertCommand->setAncestorRowNumbers(QList<int>() << 2);
2026 insertCommand->setStartRow(0);
2027 insertCommand->setEndRow(9);
2028 insertCommand->doCommand();
2029
2030 insertCommand = new ModelInsertCommand(&model, this);
2031 insertCommand->setAncestorRowNumbers(QList<int>() << 5);
2032 insertCommand->setStartRow(0);
2033 insertCommand->setEndRow(9);
2034 insertCommand->doCommand();
2035
2036 qRegisterMetaType<QList<QPersistentModelIndex> >();
2037
2038 {
2039 const QModelIndex p1 = model.index(row: 2, column: 0);
2040 const QModelIndex p2 = model.index(row: 5, column: 0);
2041
2042 const QPersistentModelIndex p1FirstPersistent = model.index(row: 0, column: 0, parent: p1);
2043 const QPersistentModelIndex p1LastPersistent = model.index(row: 9, column: 0, parent: p1);
2044 const QPersistentModelIndex p2FirstPersistent = model.index(row: 0, column: 0, parent: p2);
2045 const QPersistentModelIndex p2LastPersistent = model.index(row: 9, column: 0, parent: p2);
2046
2047 QVERIFY(p1.isValid());
2048 QVERIFY(p2.isValid());
2049
2050 QCOMPARE(model.rowCount(), 10);
2051 QCOMPARE(model.rowCount(p1), 10);
2052 QCOMPARE(model.rowCount(p2), 10);
2053
2054 QSignalSpy beforeSpy(&model, &DynamicTreeModel::layoutAboutToBeChanged);
2055 QSignalSpy afterSpy(&model, &DynamicTreeModel::layoutChanged);
2056
2057 QVERIFY(beforeSpy.isValid());
2058 QVERIFY(afterSpy.isValid());
2059
2060 ModelChangeChildrenLayoutsCommand *changeCommand = new ModelChangeChildrenLayoutsCommand(&model, this);
2061 changeCommand->setAncestorRowNumbers(QList<int>() << 2);
2062 changeCommand->setSecondAncestorRowNumbers(QList<int>() << 5);
2063 changeCommand->doCommand();
2064
2065 QCOMPARE(beforeSpy.size(), 1);
2066 QCOMPARE(afterSpy.size(), 1);
2067
2068 const QVariantList beforeSignal = beforeSpy.first();
2069 const QVariantList afterSignal = afterSpy.first();
2070 QCOMPARE(beforeSignal.size(), 2);
2071 QCOMPARE(afterSignal.size(), 2);
2072
2073 const QList<QPersistentModelIndex> beforeParents = beforeSignal.first().value<QList<QPersistentModelIndex> >();
2074 QCOMPARE(beforeParents.size(), 2);
2075 QVERIFY(beforeParents.first() != beforeParents.at(1));
2076 QVERIFY(beforeParents.contains(p1));
2077 QVERIFY(beforeParents.contains(p2));
2078
2079 const QList<QPersistentModelIndex> afterParents = afterSignal.first().value<QList<QPersistentModelIndex> >();
2080 QCOMPARE(afterParents.size(), 2);
2081 QVERIFY(afterParents.first() != afterParents.at(1));
2082 QVERIFY(afterParents.contains(p1));
2083 QVERIFY(afterParents.contains(p2));
2084
2085 // The first will be the last, and the lest will be the first.
2086 QCOMPARE(p1FirstPersistent.row(), 1);
2087 QCOMPARE(p1LastPersistent.row(), 0);
2088 QCOMPARE(p2FirstPersistent.row(), 9);
2089 QCOMPARE(p2LastPersistent.row(), 8);
2090 }
2091
2092 insertCommand = new ModelInsertCommand(&model, this);
2093 insertCommand->setAncestorRowNumbers(QList<int>() << 5 << 4);
2094 insertCommand->setStartRow(0);
2095 insertCommand->setEndRow(9);
2096 insertCommand->doCommand();
2097
2098 delete insertCommand;
2099
2100 // Even when p2 itself is moved around, signal emission remains correct for its children.
2101 {
2102 const QModelIndex p1 = model.index(row: 5, column: 0);
2103 const QModelIndex p2 = model.index(row: 4, column: 0, parent: p1);
2104
2105 QVERIFY(p1.isValid());
2106 QVERIFY(p2.isValid());
2107
2108 QCOMPARE(model.rowCount(), 10);
2109 QCOMPARE(model.rowCount(p1), 10);
2110 QCOMPARE(model.rowCount(p2), 10);
2111
2112 const QPersistentModelIndex p1Persistent = p1;
2113 const QPersistentModelIndex p2Persistent = p2;
2114
2115 const QPersistentModelIndex p1FirstPersistent = model.index(row: 0, column: 0, parent: p1);
2116 const QPersistentModelIndex p1LastPersistent = model.index(row: 9, column: 0, parent: p1);
2117 const QPersistentModelIndex p2FirstPersistent = model.index(row: 0, column: 0, parent: p2);
2118 const QPersistentModelIndex p2LastPersistent = model.index(row: 9, column: 0, parent: p2);
2119
2120 QSignalSpy beforeSpy(&model, &DynamicTreeModel::layoutAboutToBeChanged);
2121 QSignalSpy afterSpy(&model, &DynamicTreeModel::layoutChanged);
2122
2123 QVERIFY(beforeSpy.isValid());
2124 QVERIFY(afterSpy.isValid());
2125
2126 // Because the arguments in the signal are persistent, we need to check them for the aboutToBe
2127 // case at emission time - before they get updated.
2128 SignalArgumentChecker checker(p1, p2);
2129
2130 ModelChangeChildrenLayoutsCommand *changeCommand = new ModelChangeChildrenLayoutsCommand(&model, this);
2131 changeCommand->setAncestorRowNumbers(QList<int>() << 5);
2132 changeCommand->setSecondAncestorRowNumbers(QList<int>() << 5 << 4);
2133 changeCommand->doCommand();
2134
2135 // p2 has been moved.
2136 QCOMPARE(p2Persistent.row(), p2.row() + 1);
2137
2138 QCOMPARE(beforeSpy.size(), 1);
2139 QCOMPARE(afterSpy.size(), 1);
2140
2141 const QVariantList beforeSignal = beforeSpy.first();
2142 const QVariantList afterSignal = afterSpy.first();
2143 QCOMPARE(beforeSignal.size(), 2);
2144 QCOMPARE(afterSignal.size(), 2);
2145
2146 QCOMPARE(p1FirstPersistent.row(), 1);
2147 QCOMPARE(p1LastPersistent.row(), 0);
2148 QCOMPARE(p2FirstPersistent.row(), 9);
2149 QCOMPARE(p2LastPersistent.row(), 8);
2150 }
2151}
2152
2153class OverrideRoleNamesAndDragActions : public QStringListModel
2154{
2155 Q_OBJECT
2156public:
2157 OverrideRoleNamesAndDragActions(QObject *parent = 0)
2158 : QStringListModel(parent)
2159 {
2160
2161 }
2162
2163 QHash<int, QByteArray> roleNames() const
2164 {
2165 QHash<int, QByteArray> roles = QStringListModel::roleNames();
2166 roles.insert(akey: Qt::UserRole + 2, avalue: "custom");
2167 return roles;
2168 }
2169
2170 Qt::DropActions supportedDragActions() const
2171 {
2172 return QStringListModel::supportedDragActions() | Qt::MoveAction;
2173 }
2174};
2175
2176void tst_QAbstractItemModel::testRoleNames()
2177{
2178 QAbstractItemModel *model = new OverrideRoleNamesAndDragActions(this);
2179 QHash<int, QByteArray> roles = model->roleNames();
2180 QVERIFY(roles.contains(Qt::UserRole + 2));
2181 QVERIFY(roles.value(Qt::UserRole + 2) == "custom");
2182}
2183
2184void tst_QAbstractItemModel::testDragActions()
2185{
2186 QAbstractItemModel *model = new OverrideRoleNamesAndDragActions(this);
2187 const Qt::DropActions actions = model->supportedDragActions();
2188 QVERIFY(actions & Qt::CopyAction); // Present by default
2189 QVERIFY(actions & Qt::MoveAction);
2190}
2191
2192class OverrideDropActions : public QStringListModel
2193{
2194 Q_OBJECT
2195public:
2196 OverrideDropActions(QObject *parent = 0)
2197 : QStringListModel(parent)
2198 {
2199 }
2200 Qt::DropActions supportedDropActions() const override
2201 {
2202 return Qt::MoveAction;
2203 }
2204};
2205
2206void tst_QAbstractItemModel::dragActionsFallsBackToDropActions()
2207{
2208 QAbstractItemModel *model = new OverrideDropActions(this);
2209 QCOMPARE(model->supportedDragActions(), Qt::MoveAction);
2210 QCOMPARE(model->supportedDropActions(), Qt::MoveAction);
2211}
2212
2213class SignalConnectionTester : public QObject
2214{
2215 Q_OBJECT
2216public:
2217 SignalConnectionTester(QObject *parent = 0)
2218 : QObject(parent), testPassed(false)
2219 {
2220
2221 }
2222
2223public Q_SLOTS:
2224 void testSlot()
2225 {
2226 testPassed = true;
2227 }
2228 void testSlotWithParam_1(const QModelIndex &idx)
2229 {
2230 testPassed = !idx.isValid();
2231 }
2232 void testSlotWithParam_2(const QModelIndex &idx, int start)
2233 {
2234 testPassed = !idx.isValid() && start == 0;
2235 }
2236 void testSlotWithParam_3(const QModelIndex &idx, int start, int end)
2237 {
2238 testPassed = !idx.isValid() && start == 0 && end == 1;
2239 }
2240
2241public:
2242 bool testPassed;
2243};
2244
2245void tst_QAbstractItemModel::testFunctionPointerSignalConnection()
2246{
2247 QStringListModel model;
2248 {
2249 SignalConnectionTester tester;
2250 QObject::connect(sender: &model, signal: &QAbstractItemModel::rowsInserted, receiver: &tester, slot: &SignalConnectionTester::testSlot);
2251
2252 QVERIFY(!tester.testPassed);
2253
2254 model.insertRows(row: 0, count: 2);
2255
2256 QVERIFY(tester.testPassed);
2257 tester.testPassed = false;
2258 QMetaObject::invokeMethod(obj: &model, member: "rowsInserted", Q_ARG(QModelIndex, QModelIndex()), Q_ARG(int, 0), Q_ARG(int, 1));
2259 QVERIFY(tester.testPassed);
2260 }
2261 {
2262 SignalConnectionTester tester;
2263 QObject::connect(sender: &model, signal: &QAbstractItemModel::rowsInserted, receiver: &tester, slot: &SignalConnectionTester::testSlotWithParam_1);
2264
2265 QVERIFY(!tester.testPassed);
2266
2267 model.insertRows(row: 0, count: 2);
2268
2269 QVERIFY(tester.testPassed);
2270 tester.testPassed = false;
2271 QMetaObject::invokeMethod(obj: &model, member: "rowsInserted", Q_ARG(QModelIndex, QModelIndex()), Q_ARG(int, 0), Q_ARG(int, 1));
2272 QVERIFY(tester.testPassed);
2273 }
2274 {
2275 SignalConnectionTester tester;
2276 QObject::connect(sender: &model, signal: &QAbstractItemModel::rowsInserted, receiver: &tester, slot: &SignalConnectionTester::testSlotWithParam_2);
2277
2278 QVERIFY(!tester.testPassed);
2279
2280 model.insertRows(row: 0, count: 2);
2281
2282 QVERIFY(tester.testPassed);
2283 tester.testPassed = false;
2284 QMetaObject::invokeMethod(obj: &model, member: "rowsInserted", Q_ARG(QModelIndex, QModelIndex()), Q_ARG(int, 0), Q_ARG(int, 1));
2285 QVERIFY(tester.testPassed);
2286 }
2287 {
2288 SignalConnectionTester tester;
2289 QObject::connect(sender: &model, signal: &QAbstractItemModel::rowsInserted, receiver: &tester, slot: &SignalConnectionTester::testSlotWithParam_3);
2290
2291 QVERIFY(!tester.testPassed);
2292
2293 model.insertRows(row: 0, count: 2);
2294
2295 QVERIFY(tester.testPassed);
2296 tester.testPassed = false;
2297 QMetaObject::invokeMethod(obj: &model, member: "rowsInserted", Q_ARG(QModelIndex, QModelIndex()), Q_ARG(int, 0), Q_ARG(int, 1));
2298 QVERIFY(tester.testPassed);
2299 }
2300 {
2301 SignalConnectionTester tester;
2302 QObject::connect(sender: &model, SIGNAL(rowsInserted(QModelIndex,int,int)), receiver: &tester, SLOT(testSlot()));
2303
2304 QVERIFY(!tester.testPassed);
2305
2306 model.insertRows(row: 0, count: 2);
2307
2308 QVERIFY(tester.testPassed);
2309 tester.testPassed = false;
2310 QMetaObject::invokeMethod(obj: &model, member: "rowsInserted", Q_ARG(QModelIndex, QModelIndex()), Q_ARG(int, 0), Q_ARG(int, 1));
2311 QVERIFY(tester.testPassed);
2312 }
2313 // Intentionally does not compile.
2314// model.rowsInserted(QModelIndex(), 0, 0);
2315}
2316
2317void tst_QAbstractItemModel::checkIndex()
2318{
2319 const QRegularExpression ignorePattern("^Index QModelIndex");
2320
2321 // checkIndex is QAbstractItemModel API; using QStandardItem as an easy
2322 // way to build a tree model
2323 QStandardItemModel model;
2324 QStandardItem *topLevel = new QStandardItem("topLevel");
2325 model.appendRow(aitem: topLevel);
2326
2327 topLevel->appendRow(aitem: new QStandardItem("child1"));
2328 topLevel->appendRow(aitem: new QStandardItem("child2"));
2329
2330 QVERIFY(model.checkIndex(QModelIndex()));
2331 QVERIFY(model.checkIndex(QModelIndex(), QAbstractItemModel::CheckIndexOption::DoNotUseParent));
2332 QVERIFY(model.checkIndex(QModelIndex(), QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
2333 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2334 QVERIFY(!model.checkIndex(QModelIndex(), QAbstractItemModel::CheckIndexOption::IndexIsValid));
2335
2336 QModelIndex topLevelIndex = model.index(row: 0, column: 0);
2337 QVERIFY(topLevelIndex.isValid());
2338 QVERIFY(model.checkIndex(topLevelIndex));
2339 QVERIFY(model.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::DoNotUseParent));
2340 QVERIFY(model.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
2341 QVERIFY(model.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid));
2342
2343 QModelIndex childIndex = model.index(row: 0, column: 0, parent: topLevelIndex);
2344 QVERIFY(childIndex.isValid());
2345 QVERIFY(model.checkIndex(childIndex));
2346 QVERIFY(model.checkIndex(childIndex, QAbstractItemModel::CheckIndexOption::DoNotUseParent));
2347 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2348 QVERIFY(!model.checkIndex(childIndex, QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
2349 QVERIFY(model.checkIndex(childIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid));
2350
2351 childIndex = model.index(row: 1, column: 0, parent: topLevelIndex);
2352 QVERIFY(childIndex.isValid());
2353 QVERIFY(model.checkIndex(childIndex));
2354 QVERIFY(model.checkIndex(childIndex, QAbstractItemModel::CheckIndexOption::DoNotUseParent));
2355 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2356 QVERIFY(!model.checkIndex(childIndex, QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
2357 QVERIFY(model.checkIndex(childIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid));
2358
2359 topLevel->removeRow(row: 1);
2360 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2361 QVERIFY(!model.checkIndex(childIndex));
2362 QVERIFY(model.checkIndex(childIndex, QAbstractItemModel::CheckIndexOption::DoNotUseParent));
2363 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2364 QVERIFY(!model.checkIndex(childIndex, QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
2365 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2366 QVERIFY(!model.checkIndex(childIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid));
2367
2368 QStandardItemModel model2;
2369 model2.appendRow(aitem: new QStandardItem("otherTopLevel"));
2370 topLevelIndex = model2.index(row: 0, column: 0);
2371 QVERIFY(topLevelIndex.isValid());
2372 QVERIFY(model2.checkIndex(topLevelIndex));
2373 QVERIFY(model2.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::DoNotUseParent));
2374 QVERIFY(model2.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
2375 QVERIFY(model2.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid));
2376
2377 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2378 QVERIFY(!model.checkIndex(topLevelIndex));
2379 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2380 QVERIFY(!model.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::DoNotUseParent));
2381 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2382 QVERIFY(!model.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::ParentIsInvalid));
2383 QTest::ignoreMessage(type: QtWarningMsg, messagePattern: ignorePattern);
2384 QVERIFY(!model.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid));
2385}
2386
2387QTEST_MAIN(tst_QAbstractItemModel)
2388#include "tst_qabstractitemmodel.moc"
2389

source code of qtbase/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp