1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44
45 #include <qstandarditemmodel.h>
46 #include <QTreeView>
47 #include <private/qtreeview_p.h>
48
49 //TESTED_CLASS=
50 //TESTED_FILES=
51
52 class tst_QStandardItemModel : public QObject
53 {
54 Q_OBJECT
55
56 public:
57 tst_QStandardItemModel();
58 virtual ~tst_QStandardItemModel();
59
60 enum ModelChanged {
61 RowsAboutToBeInserted,
62 RowsInserted,
63 RowsAboutToBeRemoved,
64 RowsRemoved,
65 ColumnsAboutToBeInserted,
66 ColumnsInserted,
67 ColumnsAboutToBeRemoved,
68 ColumnsRemoved
69 };
70
71 public slots:
72 void init();
73 void cleanup();
74
75 protected slots:
76 void checkAboutToBeRemoved();
77 void checkRemoved();
78 void updateRowAboutToBeRemoved();
79
rowsAboutToBeInserted(const QModelIndex & parent,int first,int last)80 void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last)
81 { modelChanged(RowsAboutToBeInserted, parent, first, last); }
rowsInserted(const QModelIndex & parent,int first,int last)82 void rowsInserted(const QModelIndex &parent, int first, int last)
83 { modelChanged(RowsInserted, parent, first, last); }
rowsAboutToBeRemoved(const QModelIndex & parent,int first,int last)84 void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
85 { modelChanged(RowsAboutToBeRemoved, parent, first, last); }
rowsRemoved(const QModelIndex & parent,int first,int last)86 void rowsRemoved(const QModelIndex &parent, int first, int last)
87 { modelChanged(RowsRemoved, parent, first, last); }
columnsAboutToBeInserted(const QModelIndex & parent,int first,int last)88 void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last)
89 { modelChanged(ColumnsAboutToBeInserted, parent, first, last); }
columnsInserted(const QModelIndex & parent,int first,int last)90 void columnsInserted(const QModelIndex &parent, int first, int last)
91 { modelChanged(ColumnsInserted, parent, first, last); }
columnsAboutToBeRemoved(const QModelIndex & parent,int first,int last)92 void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
93 { modelChanged(ColumnsAboutToBeRemoved, parent, first, last); }
columnsRemoved(const QModelIndex & parent,int first,int last)94 void columnsRemoved(const QModelIndex &parent, int first, int last)
95 { modelChanged(ColumnsRemoved, parent, first, last); }
96
97 void modelChanged(ModelChanged change, const QModelIndex &parent, int first, int last);
98
99 private slots:
100 void insertRow_data();
101 void insertRow();
102 void insertRows();
103 void insertRowsItems();
104 void insertRowInHierarcy();
105 void insertColumn_data();
106 void insertColumn();
107 void insertColumns();
108 void removeRows();
109 void removeColumns();
110 void setHeaderData();
111 void persistentIndexes();
112 void removingPersistentIndexes();
113 void updatingPersistentIndexes();
114
115 void checkChildren();
116 void data();
117 void clear();
118 void sort_data();
119 void sort();
120 void sortRole_data();
121 void sortRole();
122 void findItems();
123 void getSetHeaderItem();
124 void indexFromItem();
125 void itemFromIndex();
126 void getSetItemPrototype();
127 void getSetItemData();
128 void setHeaderLabels_data();
129 void setHeaderLabels();
130 void itemDataChanged();
131 void takeHeaderItem();
132 void useCase1();
133 void useCase2();
134 void useCase3();
135
136 void rootItemFlags();
137 void treeDragAndDrop();
138 void removeRowsAndColumns();
139
140 private:
141 QAbstractItemModel *m_model;
142 QPersistentModelIndex persistent;
143 QVector<QModelIndex> rcParent;
144 QVector<int> rcFirst;
145 QVector<int> rcLast;
146
147 //return true if models have the same structure, and all child have the same text
148 bool compareModels(QStandardItemModel *model1, QStandardItemModel *model2);
149 //return true if models have the same structure, and all child have the same text
150 bool compareItems(QStandardItem *item1, QStandardItem *item2);
151 };
152
153 static const int defaultSize = 3;
154
155 Q_DECLARE_METATYPE(QModelIndex)
Q_DECLARE_METATYPE(QStandardItem *)156 Q_DECLARE_METATYPE(QStandardItem*)
157 Q_DECLARE_METATYPE(Qt::Orientation)
158 Q_DECLARE_METATYPE(QVariantList)
159
160 tst_QStandardItemModel::tst_QStandardItemModel() : m_model(0), rcParent(8), rcFirst(8,0), rcLast(8,0)
161 {
162 }
163
~tst_QStandardItemModel()164 tst_QStandardItemModel::~tst_QStandardItemModel()
165 {
166 }
167
168 /*
169 This test usually uses a model with a 3x3 table
170 ---------------------------
171 | 0,0 | 0,1 | 0,2 |
172 ---------------------------
173 | 1,0 | 1,1 | 1,2 |
174 ---------------------------
175 | 2,0 | 2,1 | 2,2 |
176 ---------------------------
177 */
init()178 void tst_QStandardItemModel::init()
179 {
180 qRegisterMetaType<QModelIndex>("QModelIndex");
181 qRegisterMetaType<QStandardItem*>("QStandardItem*");
182 qRegisterMetaType<Qt::Orientation>("Qt::Orientation");
183
184 m_model = new QStandardItemModel(defaultSize, defaultSize);
185 connect(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
186 this, SLOT(rowsAboutToBeInserted(QModelIndex, int, int)));
187 connect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)),
188 this, SLOT(rowsInserted(QModelIndex, int, int)));
189 connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
190 this, SLOT(rowsAboutToBeRemoved(QModelIndex, int, int)));
191 connect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
192 this, SLOT(rowsRemoved(QModelIndex, int, int)));
193
194 connect(m_model, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)),
195 this, SLOT(columnsAboutToBeInserted(QModelIndex, int, int)));
196 connect(m_model, SIGNAL(columnsInserted(QModelIndex, int, int)),
197 this, SLOT(columnsInserted(QModelIndex, int, int)));
198 connect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)),
199 this, SLOT(columnsAboutToBeRemoved(QModelIndex, int, int)));
200 connect(m_model, SIGNAL(columnsRemoved(QModelIndex, int, int)),
201 this, SLOT(columnsRemoved(QModelIndex, int, int)));
202
203 rcFirst.fill(-1);
204 rcLast.fill(-1);
205 }
206
cleanup()207 void tst_QStandardItemModel::cleanup()
208 {
209 disconnect(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)),
210 this, SLOT(rowsAboutToBeInserted(QModelIndex, int, int)));
211 disconnect(m_model, SIGNAL(rowsInserted(QModelIndex, int, int)),
212 this, SLOT(rowsInserted(QModelIndex, int, int)));
213 disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
214 this, SLOT(rowsAboutToBeRemoved(QModelIndex, int, int)));
215 disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
216 this, SLOT(rowsRemoved(QModelIndex, int, int)));
217
218 disconnect(m_model, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)),
219 this, SLOT(columnsAboutToBeInserted(QModelIndex, int, int)));
220 disconnect(m_model, SIGNAL(columnsInserted(QModelIndex, int, int)),
221 this, SLOT(columnsInserted(QModelIndex, int, int)));
222 disconnect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)),
223 this, SLOT(columnsAboutToBeRemoved(QModelIndex, int, int)));
224 disconnect(m_model, SIGNAL(columnsRemoved(QModelIndex, int, int)),
225 this, SLOT(columnsRemoved(QModelIndex, int, int)));
226 delete m_model;
227 m_model = 0;
228 }
229
insertRow_data()230 void tst_QStandardItemModel::insertRow_data()
231 {
232 QTest::addColumn<int>("insertRow");
233 QTest::addColumn<int>("expectedRow");
234
235 QTest::newRow("Insert less then 0") << -1 << 0;
236 QTest::newRow("Insert at 0") << 0 << 0;
237 QTest::newRow("Insert beyond count") << defaultSize+1 << defaultSize;
238 QTest::newRow("Insert at count") << defaultSize << defaultSize;
239 QTest::newRow("Insert in the middle") << 1 << 1;
240 }
241
insertRow()242 void tst_QStandardItemModel::insertRow()
243 {
244 QFETCH(int, insertRow);
245 QFETCH(int, expectedRow);
246
247 QIcon icon;
248 // default all initial items to DisplayRole: "initalitem"
249 for (int r=0; r < m_model->rowCount(); ++r) {
250 for (int c=0; c < m_model->columnCount(); ++c) {
251 m_model->setData(m_model->index(r,c), "initialitem", Qt::DisplayRole);
252 }
253 }
254
255 // check that inserts changes rowCount
256 QCOMPARE(m_model->rowCount(), defaultSize);
257 m_model->insertRow(insertRow);
258 if (insertRow >= 0 && insertRow <= defaultSize) {
259 QCOMPARE(m_model->rowCount(), defaultSize + 1);
260
261 // check that signals were emitted with correct info
262 QCOMPARE(rcFirst[RowsAboutToBeInserted], expectedRow);
263 QCOMPARE(rcLast[RowsAboutToBeInserted], expectedRow);
264 QCOMPARE(rcFirst[RowsInserted], expectedRow);
265 QCOMPARE(rcLast[RowsInserted], expectedRow);
266
267 //check that the inserted item has different DisplayRole than initial items
268 QVERIFY(m_model->data(m_model->index(expectedRow, 0), Qt::DisplayRole).toString() != "initialitem");
269 } else {
270 // We inserted something outside the bounds, do nothing
271 QCOMPARE(m_model->rowCount(), defaultSize);
272 QCOMPARE(rcFirst[RowsAboutToBeInserted], -1);
273 QCOMPARE(rcLast[RowsAboutToBeInserted], -1);
274 QCOMPARE(rcFirst[RowsInserted], -1);
275 QCOMPARE(rcLast[RowsInserted], -1);
276 }
277 }
278
insertRows()279 void tst_QStandardItemModel::insertRows()
280 {
281 int rowCount = m_model->rowCount();
282 QCOMPARE(rowCount, defaultSize);
283
284 // insert custom header label
285 QString headerLabel = "custom";
286 m_model->setHeaderData(0, Qt::Vertical, headerLabel);
287
288 // insert one row
289 m_model->insertRows(0, 1);
290 QCOMPARE(m_model->rowCount(), rowCount + 1);
291 rowCount = m_model->rowCount();
292
293 // check header data has moved
294 QCOMPARE(m_model->headerData(1, Qt::Vertical).toString(), headerLabel);
295
296 // insert two rows
297 m_model->insertRows(0, 2);
298 QCOMPARE(m_model->rowCount(), rowCount + 2);
299
300 // check header data has moved
301 QCOMPARE(m_model->headerData(3, Qt::Vertical).toString(), headerLabel);
302 }
303
insertRowsItems()304 void tst_QStandardItemModel::insertRowsItems()
305 {
306 int rowCount = m_model->rowCount();
307
308 QList<QStandardItem *> items;
309 QStandardItemModel *m = qobject_cast<QStandardItemModel*>(m_model);
310 QStandardItem *hiddenRoot = m->invisibleRootItem();
311 for (int i = 0; i < 3; ++i)
312 items.append(new QStandardItem(QString("%1").arg(i + 10)));
313 hiddenRoot->appendRows(items);
314 QCOMPARE(m_model->rowCount(), rowCount + 3);
315 QCOMPARE(m_model->index(rowCount + 0, 0).data().toInt(), 10);
316 QCOMPARE(m_model->index(rowCount + 1, 0).data().toInt(), 11);
317 QCOMPARE(m_model->index(rowCount + 2, 0).data().toInt(), 12);
318 for (int i = rowCount; i < rowCount + 3; ++i) {
319 QVERIFY(m->item(i));
320 QCOMPARE(static_cast<QAbstractItemModel *>(m->item(i)->model()), m_model);
321 }
322 }
323
insertRowInHierarcy()324 void tst_QStandardItemModel::insertRowInHierarcy()
325 {
326 QVERIFY(m_model->insertRows(0, 1, QModelIndex()));
327 QVERIFY(m_model->insertColumns(0, 1, QModelIndex()));
328 QVERIFY(m_model->hasIndex(0, 0, QModelIndex()));
329
330 QModelIndex parent = m_model->index(0, 0, QModelIndex());
331 QVERIFY(parent.isValid());
332
333 QVERIFY(m_model->insertRows(0, 1, parent));
334 QVERIFY(m_model->insertColumns(0, 1, parent));
335 QVERIFY(m_model->hasIndex(0, 0, parent));
336
337 QModelIndex child = m_model->index(0, 0, parent);
338 QVERIFY(child.isValid());
339 }
340
insertColumn_data()341 void tst_QStandardItemModel::insertColumn_data()
342 {
343 QTest::addColumn<int>("insertColumn");
344 QTest::addColumn<int>("expectedColumn");
345
346 QTest::newRow("Insert less then 0") << -1 << 0;
347 QTest::newRow("Insert at 0") << 0 << 0;
348 QTest::newRow("Insert beyond count") << defaultSize+1 << defaultSize;
349 QTest::newRow("Insert at count") << defaultSize << defaultSize;
350 QTest::newRow("Insert in the middle") << 1 << 1;
351 }
352
insertColumn()353 void tst_QStandardItemModel::insertColumn()
354 {
355 QFETCH(int, insertColumn);
356 QFETCH(int, expectedColumn);
357
358 // default all initial items to DisplayRole: "initalitem"
359 for (int r=0; r < m_model->rowCount(); ++r) {
360 for (int c=0; c < m_model->columnCount(); ++c) {
361 m_model->setData(m_model->index(r,c), "initialitem", Qt::DisplayRole);
362 }
363 }
364
365 // check that inserts changes columnCount
366 QCOMPARE(m_model->columnCount(), defaultSize);
367 m_model->insertColumn(insertColumn);
368 if (insertColumn >= 0 && insertColumn <= defaultSize) {
369 QCOMPARE(m_model->columnCount(), defaultSize + 1);
370 // check that signals were emitted with correct info
371 QCOMPARE(rcFirst[ColumnsAboutToBeInserted], expectedColumn);
372 QCOMPARE(rcLast[ColumnsAboutToBeInserted], expectedColumn);
373 QCOMPARE(rcFirst[ColumnsInserted], expectedColumn);
374 QCOMPARE(rcLast[ColumnsInserted], expectedColumn);
375
376 //check that the inserted item has different DisplayRole than initial items
377 QVERIFY(m_model->data(m_model->index(0, expectedColumn), Qt::DisplayRole).toString() != "initialitem");
378 } else {
379 // We inserted something outside the bounds, do nothing
380 QCOMPARE(m_model->columnCount(), defaultSize);
381 QCOMPARE(rcFirst[ColumnsAboutToBeInserted], -1);
382 QCOMPARE(rcLast[ColumnsAboutToBeInserted], -1);
383 QCOMPARE(rcFirst[ColumnsInserted], -1);
384 QCOMPARE(rcLast[ColumnsInserted], -1);
385 }
386
387 }
388
insertColumns()389 void tst_QStandardItemModel::insertColumns()
390 {
391 int columnCount = m_model->columnCount();
392 QCOMPARE(columnCount, defaultSize);
393
394 // insert custom header label
395 QString headerLabel = "custom";
396 m_model->setHeaderData(0, Qt::Horizontal, headerLabel);
397
398 // insert one column
399 m_model->insertColumns(0, 1);
400 QCOMPARE(m_model->columnCount(), columnCount + 1);
401 columnCount = m_model->columnCount();
402
403 // check header data has moved
404 QCOMPARE(m_model->headerData(1, Qt::Horizontal).toString(), headerLabel);
405
406 // insert two columns
407 m_model->insertColumns(0, 2);
408 QCOMPARE(m_model->columnCount(), columnCount + 2);
409
410 // check header data has moved
411 QCOMPARE(m_model->headerData(3, Qt::Horizontal).toString(), headerLabel);
412 }
413
removeRows()414 void tst_QStandardItemModel::removeRows()
415 {
416 int rowCount = m_model->rowCount();
417 QCOMPARE(rowCount, defaultSize);
418
419 // insert custom header label
420 QString headerLabel = "custom";
421 m_model->setHeaderData(rowCount - 1, Qt::Vertical, headerLabel);
422
423 // remove one row
424 m_model->removeRows(0, 1);
425 QCOMPARE(m_model->rowCount(), rowCount - 1);
426 rowCount = m_model->rowCount();
427
428 // check header data has moved
429 QCOMPARE(m_model->headerData(rowCount - 1, Qt::Vertical).toString(), headerLabel);
430
431 // remove two rows
432 m_model->removeRows(0, 2);
433 QCOMPARE(m_model->rowCount(), rowCount - 2);
434 }
435
removeColumns()436 void tst_QStandardItemModel::removeColumns()
437 {
438 int columnCount = m_model->columnCount();
439 QCOMPARE(columnCount, defaultSize);
440
441 // insert custom header label
442 QString headerLabel = "custom";
443 m_model->setHeaderData(columnCount - 1, Qt::Horizontal, headerLabel);
444
445 // remove one column
446 m_model->removeColumns(0, 1);
447 QCOMPARE(m_model->columnCount(), columnCount - 1);
448 columnCount = m_model->columnCount();
449
450 // check header data has moved
451 QCOMPARE(m_model->headerData(columnCount - 1, Qt::Horizontal).toString(), headerLabel);
452
453 // remove two columns
454 m_model->removeColumns(0, 2);
455 QCOMPARE(m_model->columnCount(), columnCount - 2);
456 }
457
458
setHeaderData()459 void tst_QStandardItemModel::setHeaderData()
460 {
461 for (int x = 0; x < 2; ++x) {
462 bool vertical = (x == 0);
463 int count = vertical ? m_model->rowCount() : m_model->columnCount();
464 QCOMPARE(count, defaultSize);
465 Qt::Orientation orient = vertical ? Qt::Vertical : Qt::Horizontal;
466
467 // check default values are ok
468 for (int i = 0; i < count; ++i)
469 QCOMPARE(m_model->headerData(i, orient).toString(), QString::number(i + 1));
470
471 QSignalSpy headerDataChangedSpy(
472 m_model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)));
473 QSignalSpy dataChangedSpy(
474 m_model, SIGNAL(dataChanged(QModelIndex, QModelIndex)));
475 // insert custom values and check
476 for (int i = 0; i < count; ++i) {
477 QString customString = QString("custom") + QString::number(i);
478 QCOMPARE(m_model->setHeaderData(i, orient, customString), true);
479 QCOMPARE(headerDataChangedSpy.count(), 1);
480 QCOMPARE(dataChangedSpy.count(), 0);
481 QVariantList args = headerDataChangedSpy.takeFirst();
482 QCOMPARE(qvariant_cast<Qt::Orientation>(args.at(0)), orient);
483 QCOMPARE(args.at(1).toInt(), i);
484 QCOMPARE(args.at(2).toInt(), i);
485 QCOMPARE(m_model->headerData(i, orient).toString(), customString);
486 QCOMPARE(m_model->setHeaderData(i, orient, customString), true);
487 QCOMPARE(headerDataChangedSpy.count(), 0);
488 QCOMPARE(dataChangedSpy.count(), 0);
489 }
490
491 //check read from invalid sections
492 QVERIFY(!m_model->headerData(count, orient).isValid());
493 QVERIFY(!m_model->headerData(-1, orient).isValid());
494 //check write to invalid section
495 QCOMPARE(m_model->setHeaderData(count, orient, "foo"), false);
496 QCOMPARE(m_model->setHeaderData(-1, orient, "foo"), false);
497 QVERIFY(!m_model->headerData(count, orient).isValid());
498 QVERIFY(!m_model->headerData(-1, orient).isValid());
499 }
500 }
501
persistentIndexes()502 void tst_QStandardItemModel::persistentIndexes()
503 {
504 QCOMPARE(m_model->rowCount(), defaultSize);
505 QCOMPARE(m_model->columnCount(), defaultSize);
506
507 // create a persisten index at 0,0
508 QPersistentModelIndex persistentIndex(m_model->index(0, 0));
509
510 // verify it is ok and at the correct spot
511 QVERIFY(persistentIndex.isValid());
512 QCOMPARE(persistentIndex.row(), 0);
513 QCOMPARE(persistentIndex.column(), 0);
514
515 // insert row and check that the persisten index has moved
516 QVERIFY(m_model->insertRow(0));
517 QVERIFY(persistentIndex.isValid());
518 QCOMPARE(persistentIndex.row(), 1);
519 QCOMPARE(persistentIndex.column(), 0);
520
521 // insert row after the persisten index and see that it stays the same
522 QVERIFY(m_model->insertRow(m_model->rowCount()));
523 QVERIFY(persistentIndex.isValid());
524 QCOMPARE(persistentIndex.row(), 1);
525 QCOMPARE(persistentIndex.column(), 0);
526
527 // insert column and check that the persisten index has moved
528 QVERIFY(m_model->insertColumn(0));
529 QVERIFY(persistentIndex.isValid());
530 QCOMPARE(persistentIndex.row(), 1);
531 QCOMPARE(persistentIndex.column(), 1);
532
533 // insert column after the persisten index and see that it stays the same
534 QVERIFY(m_model->insertColumn(m_model->columnCount()));
535 QVERIFY(persistentIndex.isValid());
536 QCOMPARE(persistentIndex.row(), 1);
537 QCOMPARE(persistentIndex.column(), 1);
538
539 // removes a row beyond the persistent index and see it stays the same
540 QVERIFY(m_model->removeRow(m_model->rowCount() - 1));
541 QVERIFY(persistentIndex.isValid());
542 QCOMPARE(persistentIndex.row(), 1);
543 QCOMPARE(persistentIndex.column(), 1);
544
545 // removes a column beyond the persistent index and see it stays the same
546 QVERIFY(m_model->removeColumn(m_model->columnCount() - 1));
547 QVERIFY(persistentIndex.isValid());
548 QCOMPARE(persistentIndex.row(), 1);
549 QCOMPARE(persistentIndex.column(), 1);
550
551 // removes a row before the persistent index and see it moves the same
552 QVERIFY(m_model->removeRow(0));
553 QVERIFY(persistentIndex.isValid());
554 QCOMPARE(persistentIndex.row(), 0);
555 QCOMPARE(persistentIndex.column(), 1);
556
557 // removes a column before the persistent index and see it moves the same
558 QVERIFY(m_model->removeColumn(0));
559 QVERIFY(persistentIndex.isValid());
560 QCOMPARE(persistentIndex.row(), 0);
561 QCOMPARE(persistentIndex.column(), 0);
562
563 // remove the row where the persistent index is, and see that it becomes invalid
564 QVERIFY(m_model->removeRow(0));
565 QVERIFY(!persistentIndex.isValid());
566
567 // remove the row where the persistent index is, and see that it becomes invalid
568 persistentIndex = m_model->index(0, 0);
569 QVERIFY(persistentIndex.isValid());
570 QVERIFY(m_model->removeColumn(0));
571 QVERIFY(!persistentIndex.isValid());
572 }
573
checkAboutToBeRemoved()574 void tst_QStandardItemModel::checkAboutToBeRemoved()
575 {
576 QVERIFY(persistent.isValid());
577 }
578
checkRemoved()579 void tst_QStandardItemModel::checkRemoved()
580 {
581 QVERIFY(!persistent.isValid());
582 }
583
removingPersistentIndexes()584 void tst_QStandardItemModel::removingPersistentIndexes()
585 {
586 // add 10 rows and columns to model to make it big enough
587 QVERIFY(m_model->insertRows(0, 10));
588 QVERIFY(m_model->insertColumns(0, 10));
589
590 QObject::connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
591 this, SLOT(checkAboutToBeRemoved()));
592 QObject::connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
593 this, SLOT(checkRemoved()));
594 QObject::connect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
595 this, SLOT(checkAboutToBeRemoved()));
596 QObject::connect(m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
597 this, SLOT(checkRemoved()));
598
599
600 // test removeRow
601 // add child table 3x3 to parent index(0, 0)
602 QVERIFY(m_model->insertRows(0, 3, m_model->index(0, 0)));
603 QVERIFY(m_model->insertColumns(0, 3, m_model->index(0, 0)));
604
605 // set child to persistent and delete parent row
606 persistent = m_model->index(0, 0, m_model->index(0, 0));
607 QVERIFY(persistent.isValid());
608 QVERIFY(m_model->removeRow(0));
609
610 // set persistent to index(0, 0) and remove that row
611 persistent = m_model->index(0, 0);
612 QVERIFY(persistent.isValid());
613 QVERIFY(m_model->removeRow(0));
614
615
616 // test removeColumn
617 // add child table 3x3 to parent index (0, 0)
618 QVERIFY(m_model->insertRows(0, 3, m_model->index(0, 0)));
619 QVERIFY(m_model->insertColumns(0, 3, m_model->index(0, 0)));
620
621 // set child to persistent and delete parent column
622 persistent = m_model->index(0, 0, m_model->index(0, 0));
623 QVERIFY(persistent.isValid());
624 QVERIFY(m_model->removeColumn(0));
625
626 // set persistent to index(0, 0) and remove that column
627 persistent = m_model->index(0, 0);
628 QVERIFY(persistent.isValid());
629 QVERIFY(m_model->removeColumn(0));
630
631
632 QObject::disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
633 this, SLOT(checkAboutToBeRemoved()));
634 QObject::disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
635 this, SLOT(checkRemoved()));
636 QObject::disconnect(m_model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
637 this, SLOT(checkAboutToBeRemoved()));
638 QObject::disconnect(m_model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
639 this, SLOT(checkRemoved()));
640 }
641
updateRowAboutToBeRemoved()642 void tst_QStandardItemModel::updateRowAboutToBeRemoved()
643 {
644 QModelIndex idx = m_model->index(0, 0);
645 QVERIFY(idx.isValid());
646 persistent = idx;
647 }
648
updatingPersistentIndexes()649 void tst_QStandardItemModel::updatingPersistentIndexes()
650 {
651 QObject::connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
652 this, SLOT(updateRowAboutToBeRemoved()));
653
654 persistent = m_model->index(1, 0);
655 QVERIFY(persistent.isValid());
656 QVERIFY(m_model->removeRow(1));
657 QVERIFY(persistent.isValid());
658 QPersistentModelIndex tmp = m_model->index(0, 0);
659 QCOMPARE(persistent, tmp);
660
661 QObject::disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
662 this, SLOT(updateRowAboutToBeRemoved()));
663 }
664
modelChanged(ModelChanged change,const QModelIndex & parent,int first,int last)665 void tst_QStandardItemModel::modelChanged(ModelChanged change, const QModelIndex &parent,
666 int first, int last)
667 {
668 rcParent[change] = parent;
669 rcFirst[change] = first;
670 rcLast[change] = last;
671 }
672
673
checkChildren()674 void tst_QStandardItemModel::checkChildren()
675 {
676 QStandardItemModel model(0, 0);
677 QCOMPARE(model.rowCount(), 0);
678 QCOMPARE(model.columnCount(), 0);
679 QVERIFY(!model.hasChildren());
680
681 QVERIFY(model.insertRows(0, 1));
682 QVERIFY(!model.hasChildren());
683 QCOMPARE(model.rowCount(), 1);
684 QCOMPARE(model.columnCount(), 0);
685
686 QVERIFY(model.insertColumns(0, 1));
687 QVERIFY(model.hasChildren());
688 QCOMPARE(model.rowCount(), 1);
689 QCOMPARE(model.columnCount(), 1);
690
691 QModelIndex idx = model.index(0, 0);
692 QVERIFY(!model.hasChildren(idx));
693 QCOMPARE(model.rowCount(idx), 0);
694 QCOMPARE(model.columnCount(idx), 0);
695
696 QVERIFY(model.insertRows(0, 1, idx));
697 QVERIFY(!model.hasChildren(idx));
698 QCOMPARE(model.rowCount(idx), 1);
699 QCOMPARE(model.columnCount(idx), 0);
700
701 QVERIFY(model.insertColumns(0, 1, idx));
702 QVERIFY(model.hasChildren(idx));
703 QCOMPARE(model.rowCount(idx), 1);
704 QCOMPARE(model.columnCount(idx), 1);
705
706 QModelIndex idx2 = model.index(0, 0, idx);
707 QVERIFY(!model.hasChildren(idx2));
708 QCOMPARE(model.rowCount(idx2), 0);
709 QCOMPARE(model.columnCount(idx2), 0);
710
711 QVERIFY(model.removeRows(0, 1, idx));
712 QVERIFY(model.hasChildren());
713 QCOMPARE(model.rowCount(), 1);
714 QCOMPARE(model.columnCount(), 1);
715 QVERIFY(!model.hasChildren(idx));
716 QCOMPARE(model.rowCount(idx), 0);
717 QCOMPARE(model.columnCount(idx), 1);
718
719 QVERIFY(model.removeRows(0, 1));
720 QVERIFY(!model.hasChildren());
721 QCOMPARE(model.rowCount(), 0);
722 QCOMPARE(model.columnCount(), 1);
723 }
724
data()725 void tst_QStandardItemModel::data()
726 {
727 // bad args
728 m_model->setData(QModelIndex(), "bla", Qt::DisplayRole);
729
730 QIcon icon;
731 for (int r=0; r < m_model->rowCount(); ++r) {
732 for (int c=0; c < m_model->columnCount(); ++c) {
733 m_model->setData(m_model->index(r,c), "initialitem", Qt::DisplayRole);
734 m_model->setData(m_model->index(r,c), "tooltip", Qt::ToolTipRole);
735 m_model->setData(m_model->index(r,c), icon, Qt::DecorationRole);
736 }
737 }
738
739 QVERIFY(m_model->data(m_model->index(0, 0), Qt::DisplayRole).toString() == "initialitem");
740 QVERIFY(m_model->data(m_model->index(0, 0), Qt::ToolTipRole).toString() == "tooltip");
741
742 }
743
clear()744 void tst_QStandardItemModel::clear()
745 {
746 QStandardItemModel model;
747 model.insertColumns(0, 10);
748 model.insertRows(0, 10);
749 QCOMPARE(model.columnCount(), 10);
750 QCOMPARE(model.rowCount(), 10);
751
752 QSignalSpy modelResetSpy(&model, SIGNAL(modelReset()));
753 QSignalSpy layoutChangedSpy(&model, SIGNAL(layoutChanged()));
754 QSignalSpy rowsRemovedSpy(&model, SIGNAL(rowsRemoved(QModelIndex, int, int)));
755 model.clear();
756
757 QCOMPARE(modelResetSpy.count(), 1);
758 QCOMPARE(layoutChangedSpy.count(), 0);
759 QCOMPARE(rowsRemovedSpy.count(), 0);
760 QCOMPARE(model.index(0, 0), QModelIndex());
761 QCOMPARE(model.columnCount(), 0);
762 QCOMPARE(model.rowCount(), 0);
763 QCOMPARE(model.hasChildren(), false);
764 }
765
sort_data()766 void tst_QStandardItemModel::sort_data()
767 {
768 QTest::addColumn<int>("sortOrder");
769 QTest::addColumn<QStringList>("initial");
770 QTest::addColumn<QStringList>("expected");
771
772 QTest::newRow("flat descending") << static_cast<int>(Qt::DescendingOrder)
773 << (QStringList()
774 << "delta"
775 << "yankee"
776 << "bravo"
777 << "lima"
778 << "charlie"
779 << "juliet"
780 << "tango"
781 << "hotel"
782 << "uniform"
783 << "alpha"
784 << "echo"
785 << "golf"
786 << "quebec"
787 << "foxtrot"
788 << "india"
789 << "romeo"
790 << "november"
791 << "oskar"
792 << "zulu"
793 << "kilo"
794 << "whiskey"
795 << "mike"
796 << "papa"
797 << "sierra"
798 << "xray"
799 << "viktor")
800 << (QStringList()
801 << "zulu"
802 << "yankee"
803 << "xray"
804 << "whiskey"
805 << "viktor"
806 << "uniform"
807 << "tango"
808 << "sierra"
809 << "romeo"
810 << "quebec"
811 << "papa"
812 << "oskar"
813 << "november"
814 << "mike"
815 << "lima"
816 << "kilo"
817 << "juliet"
818 << "india"
819 << "hotel"
820 << "golf"
821 << "foxtrot"
822 << "echo"
823 << "delta"
824 << "charlie"
825 << "bravo"
826 << "alpha");
827 QTest::newRow("flat ascending") << static_cast<int>(Qt::AscendingOrder)
828 << (QStringList()
829 << "delta"
830 << "yankee"
831 << "bravo"
832 << "lima"
833 << "charlie"
834 << "juliet"
835 << "tango"
836 << "hotel"
837 << "uniform"
838 << "alpha"
839 << "echo"
840 << "golf"
841 << "quebec"
842 << "foxtrot"
843 << "india"
844 << "romeo"
845 << "november"
846 << "oskar"
847 << "zulu"
848 << "kilo"
849 << "whiskey"
850 << "mike"
851 << "papa"
852 << "sierra"
853 << "xray"
854 << "viktor")
855 << (QStringList()
856 << "alpha"
857 << "bravo"
858 << "charlie"
859 << "delta"
860 << "echo"
861 << "foxtrot"
862 << "golf"
863 << "hotel"
864 << "india"
865 << "juliet"
866 << "kilo"
867 << "lima"
868 << "mike"
869 << "november"
870 << "oskar"
871 << "papa"
872 << "quebec"
873 << "romeo"
874 << "sierra"
875 << "tango"
876 << "uniform"
877 << "viktor"
878 << "whiskey"
879 << "xray"
880 << "yankee"
881 << "zulu");
882 QStringList list;
883 for (int i=1000; i < 2000; ++i)
884 list.append(QString("Number: %1").arg(i));
885 QTest::newRow("large set ascending") << static_cast<int>(Qt::AscendingOrder) << list << list;
886 }
887
sort()888 void tst_QStandardItemModel::sort()
889 {
890 QFETCH(int, sortOrder);
891 QFETCH(QStringList, initial);
892 QFETCH(QStringList, expected);
893 // prepare model
894 QStandardItemModel model;
895 QVERIFY(model.insertRows(0, initial.count(), QModelIndex()));
896 QCOMPARE(model.rowCount(QModelIndex()), initial.count());
897 model.insertColumns(0, 1, QModelIndex());
898 QCOMPARE(model.columnCount(QModelIndex()), 1);
899 for (int row = 0; row < model.rowCount(QModelIndex()); ++row) {
900 QModelIndex index = model.index(row, 0, QModelIndex());
901 model.setData(index, initial.at(row), Qt::DisplayRole);
902 }
903
904 QSignalSpy layoutAboutToBeChangedSpy(
905 &model, SIGNAL(layoutAboutToBeChanged()));
906 QSignalSpy layoutChangedSpy(
907 &model, SIGNAL(layoutChanged()));
908
909 // sort
910 model.sort(0, static_cast<Qt::SortOrder>(sortOrder));
911
912 QCOMPARE(layoutAboutToBeChangedSpy.count(), 1);
913 QCOMPARE(layoutChangedSpy.count(), 1);
914
915 // make sure the model is sorted
916 for (int row = 0; row < model.rowCount(QModelIndex()); ++row) {
917 QModelIndex index = model.index(row, 0, QModelIndex());
918 QCOMPARE(model.data(index, Qt::DisplayRole).toString(), expected.at(row));
919 }
920 }
921
sortRole_data()922 void tst_QStandardItemModel::sortRole_data()
923 {
924 QTest::addColumn<QStringList>("initialText");
925 QTest::addColumn<QVariantList>("initialData");
926 QTest::addColumn<int>("sortRole");
927 QTest::addColumn<int>("sortOrder");
928 QTest::addColumn<QStringList>("expectedText");
929 QTest::addColumn<QVariantList>("expectedData");
930
931 QTest::newRow("sort ascending with Qt::DisplayRole")
932 << (QStringList() << "b" << "a" << "c")
933 << (QVariantList() << 2 << 3 << 1)
934 << static_cast<int>(Qt::DisplayRole)
935 << static_cast<int>(Qt::AscendingOrder)
936 << (QStringList() << "a" << "b" << "c")
937 << (QVariantList() << 3 << 2 << 1);
938 QTest::newRow("sort ascending with Qt::UserRole")
939 << (QStringList() << "a" << "b" << "c")
940 << (QVariantList() << 3 << 2 << 1)
941 << static_cast<int>(Qt::UserRole)
942 << static_cast<int>(Qt::AscendingOrder)
943 << (QStringList() << "c" << "b" << "a")
944 << (QVariantList() << 1 << 2 << 3);
945 }
946
sortRole()947 void tst_QStandardItemModel::sortRole()
948 {
949 QFETCH(QStringList, initialText);
950 QFETCH(QVariantList, initialData);
951 QFETCH(int, sortRole);
952 QFETCH(int, sortOrder);
953 QFETCH(QStringList, expectedText);
954 QFETCH(QVariantList, expectedData);
955
956 QStandardItemModel model;
957 for (int i = 0; i < initialText.count(); ++i) {
958 QStandardItem *item = new QStandardItem;
959 item->setText(initialText.at(i));
960 item->setData(initialData.at(i), Qt::UserRole);
961 model.appendRow(item);
962 }
963 model.setSortRole(sortRole);
964 model.sort(0, static_cast<Qt::SortOrder>(sortOrder));
965 for (int i = 0; i < expectedText.count(); ++i) {
966 QStandardItem *item = model.item(i);
967 QCOMPARE(item->text(), expectedText.at(i));
968 QCOMPARE(item->data(Qt::UserRole), expectedData.at(i));
969 }
970 }
971
findItems()972 void tst_QStandardItemModel::findItems()
973 {
974 QStandardItemModel model;
975 model.appendRow(new QStandardItem(QLatin1String("foo")));
976 model.appendRow(new QStandardItem(QLatin1String("bar")));
977 model.item(1)->appendRow(new QStandardItem(QLatin1String("foo")));
978 QList<QStandardItem*> matches;
979 matches = model.findItems(QLatin1String("foo"), Qt::MatchExactly|Qt::MatchRecursive, 0);
980 QCOMPARE(matches.count(), 2);
981 matches = model.findItems(QLatin1String("foo"), Qt::MatchExactly, 0);
982 QCOMPARE(matches.count(), 1);
983 matches = model.findItems(QLatin1String("food"), Qt::MatchExactly|Qt::MatchRecursive, 0);
984 QCOMPARE(matches.count(), 0);
985 matches = model.findItems(QLatin1String("foo"), Qt::MatchExactly|Qt::MatchRecursive, -1);
986 QCOMPARE(matches.count(), 0);
987 matches = model.findItems(QLatin1String("foo"), Qt::MatchExactly|Qt::MatchRecursive, 1);
988 QCOMPARE(matches.count(), 0);
989 }
990
getSetHeaderItem()991 void tst_QStandardItemModel::getSetHeaderItem()
992 {
993 QStandardItemModel model;
994
995 QCOMPARE(model.horizontalHeaderItem(0), static_cast<QStandardItem*>(0));
996 QStandardItem *hheader = new QStandardItem();
997 model.setHorizontalHeaderItem(0, hheader);
998 QCOMPARE(model.columnCount(), 1);
999 QCOMPARE(model.horizontalHeaderItem(0), hheader);
1000 QCOMPARE(hheader->model(), &model);
1001 model.setHorizontalHeaderItem(0, 0);
1002 QCOMPARE(model.horizontalHeaderItem(0), static_cast<QStandardItem*>(0));
1003
1004 QCOMPARE(model.verticalHeaderItem(0), static_cast<QStandardItem*>(0));
1005 QStandardItem *vheader = new QStandardItem();
1006 model.setVerticalHeaderItem(0, vheader);
1007 QCOMPARE(model.rowCount(), 1);
1008 QCOMPARE(model.verticalHeaderItem(0), vheader);
1009 QCOMPARE(vheader->model(), &model);
1010 model.setVerticalHeaderItem(0, 0);
1011 QCOMPARE(model.verticalHeaderItem(0), static_cast<QStandardItem*>(0));
1012 }
1013
indexFromItem()1014 void tst_QStandardItemModel::indexFromItem()
1015 {
1016 QStandardItemModel model;
1017
1018 QCOMPARE(model.indexFromItem(model.invisibleRootItem()), QModelIndex());
1019
1020 QStandardItem *item = new QStandardItem;
1021 model.setItem(10, 20, item);
1022 QCOMPARE(item->model(), &model);
1023 QModelIndex itemIndex = model.indexFromItem(item);
1024 QVERIFY(itemIndex.isValid());
1025 QCOMPARE(itemIndex.row(), 10);
1026 QCOMPARE(itemIndex.column(), 20);
1027 QCOMPARE(itemIndex.parent(), QModelIndex());
1028 QCOMPARE(itemIndex.model(), (const QAbstractItemModel*)(&model));
1029
1030 QStandardItem *child = new QStandardItem;
1031 item->setChild(4, 2, child);
1032 QModelIndex childIndex = model.indexFromItem(child);
1033 QVERIFY(childIndex.isValid());
1034 QCOMPARE(childIndex.row(), 4);
1035 QCOMPARE(childIndex.column(), 2);
1036 QCOMPARE(childIndex.parent(), itemIndex);
1037
1038 QStandardItem *dummy = new QStandardItem;
1039 QModelIndex noSuchIndex = model.indexFromItem(dummy);
1040 QVERIFY(!noSuchIndex.isValid());
1041 delete dummy;
1042
1043 noSuchIndex = model.indexFromItem(0);
1044 QVERIFY(!noSuchIndex.isValid());
1045 }
1046
itemFromIndex()1047 void tst_QStandardItemModel::itemFromIndex()
1048 {
1049 QStandardItemModel model;
1050
1051 QCOMPARE(model.itemFromIndex(QModelIndex()), (QStandardItem*)0);
1052
1053 QStandardItem *item = new QStandardItem;
1054 model.setItem(10, 20, item);
1055 QModelIndex itemIndex = model.index(10, 20, QModelIndex());
1056 QVERIFY(itemIndex.isValid());
1057 QCOMPARE(model.itemFromIndex(itemIndex), item);
1058
1059 QStandardItem *child = new QStandardItem;
1060 item->setChild(4, 2, child);
1061 QModelIndex childIndex = model.index(4, 2, itemIndex);
1062 QVERIFY(childIndex.isValid());
1063 QCOMPARE(model.itemFromIndex(childIndex), child);
1064
1065 QModelIndex noSuchIndex = model.index(99, 99, itemIndex);
1066 QVERIFY(!noSuchIndex.isValid());
1067 }
1068
1069 class CustomItem : public QStandardItem
1070 {
1071 public:
CustomItem()1072 CustomItem() : QStandardItem() { }
~CustomItem()1073 ~CustomItem() { }
type() const1074 int type() const {
1075 return UserType;
1076 }
clone() const1077 QStandardItem *clone() const {
1078 return new CustomItem;
1079 }
1080 };
1081
getSetItemPrototype()1082 void tst_QStandardItemModel::getSetItemPrototype()
1083 {
1084 QStandardItemModel model;
1085 QCOMPARE(model.itemPrototype(), static_cast<const QStandardItem*>(0));
1086
1087 const CustomItem *proto = new CustomItem;
1088 model.setItemPrototype(proto);
1089 QCOMPARE(model.itemPrototype(), (const QStandardItem*)proto);
1090
1091 model.setRowCount(1);
1092 model.setColumnCount(1);
1093 QModelIndex index = model.index(0, 0, QModelIndex());
1094 model.setData(index, "foo");
1095 QStandardItem *item = model.itemFromIndex(index);
1096 QVERIFY(item != 0);
1097 QCOMPARE(item->type(), static_cast<int>(QStandardItem::UserType));
1098
1099 model.setItemPrototype(0);
1100 QCOMPARE(model.itemPrototype(), static_cast<const QStandardItem*>(0));
1101 }
1102
getSetItemData()1103 void tst_QStandardItemModel::getSetItemData()
1104 {
1105 QMap<int, QVariant> roles;
1106 QLatin1String text("text");
1107 roles.insert(Qt::DisplayRole, text);
1108 QLatin1String statusTip("statusTip");
1109 roles.insert(Qt::StatusTipRole, statusTip);
1110 QLatin1String toolTip("toolTip");
1111 roles.insert(Qt::ToolTipRole, toolTip);
1112 QLatin1String whatsThis("whatsThis");
1113 roles.insert(Qt::WhatsThisRole, whatsThis);
1114 QSize sizeHint(64, 48);
1115 roles.insert(Qt::SizeHintRole, sizeHint);
1116 QFont font;
1117 roles.insert(Qt::FontRole, font);
1118 Qt::Alignment textAlignment(Qt::AlignLeft|Qt::AlignVCenter);
1119 roles.insert(Qt::TextAlignmentRole, int(textAlignment));
1120 QColor backgroundColor(Qt::blue);
1121 roles.insert(Qt::BackgroundRole, backgroundColor);
1122 QColor textColor(Qt::green);
1123 roles.insert(Qt::TextColorRole, textColor);
1124 Qt::CheckState checkState(Qt::PartiallyChecked);
1125 roles.insert(Qt::CheckStateRole, int(checkState));
1126 QLatin1String accessibleText("accessibleText");
1127 roles.insert(Qt::AccessibleTextRole, accessibleText);
1128 QLatin1String accessibleDescription("accessibleDescription");
1129 roles.insert(Qt::AccessibleDescriptionRole, accessibleDescription);
1130
1131 QStandardItemModel model;
1132 model.insertRows(0, 1);
1133 model.insertColumns(0, 1);
1134 QModelIndex idx = model.index(0, 0, QModelIndex());
1135
1136 QSignalSpy modelDataChangedSpy(
1137 &model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)));
1138 QVERIFY(model.setItemData(idx, roles));
1139 QCOMPARE(modelDataChangedSpy.count(), 1);
1140 QVERIFY(model.setItemData(idx, roles));
1141 QCOMPARE(modelDataChangedSpy.count(), 1); //it was already changed once
1142 QCOMPARE(model.itemData(idx), roles);
1143 }
1144
setHeaderLabels_data()1145 void tst_QStandardItemModel::setHeaderLabels_data()
1146 {
1147 QTest::addColumn<int>("rows");
1148 QTest::addColumn<int>("columns");
1149 QTest::addColumn<int>("orientation");
1150 QTest::addColumn<QStringList>("labels");
1151 QTest::addColumn<QStringList>("expectedLabels");
1152
1153 QTest::newRow("horizontal labels")
1154 << 1
1155 << 4
1156 << int(Qt::Horizontal)
1157 << (QStringList() << "a" << "b" << "c" << "d")
1158 << (QStringList() << "a" << "b" << "c" << "d");
1159 QTest::newRow("vertical labels")
1160 << 4
1161 << 1
1162 << int(Qt::Vertical)
1163 << (QStringList() << "a" << "b" << "c" << "d")
1164 << (QStringList() << "a" << "b" << "c" << "d");
1165 QTest::newRow("too few (horizontal)")
1166 << 1
1167 << 4
1168 << int(Qt::Horizontal)
1169 << (QStringList() << "a" << "b")
1170 << (QStringList() << "a" << "b" << "3" << "4");
1171 QTest::newRow("too few (vertical)")
1172 << 4
1173 << 1
1174 << int(Qt::Vertical)
1175 << (QStringList() << "a" << "b")
1176 << (QStringList() << "a" << "b" << "3" << "4");
1177 QTest::newRow("too many (horizontal)")
1178 << 1
1179 << 2
1180 << int(Qt::Horizontal)
1181 << (QStringList() << "a" << "b" << "c" << "d")
1182 << (QStringList() << "a" << "b" << "c" << "d");
1183 QTest::newRow("too many (vertical)")
1184 << 2
1185 << 1
1186 << int(Qt::Vertical)
1187 << (QStringList() << "a" << "b" << "c" << "d")
1188 << (QStringList() << "a" << "b" << "c" << "d");
1189 }
1190
setHeaderLabels()1191 void tst_QStandardItemModel::setHeaderLabels()
1192 {
1193 QFETCH(int, rows);
1194 QFETCH(int, columns);
1195 QFETCH(int, orientation);
1196 QFETCH(QStringList, labels);
1197 QFETCH(QStringList, expectedLabels);
1198 QStandardItemModel model(rows, columns);
1199 QSignalSpy columnsInsertedSpy(
1200 &model, SIGNAL(columnsInserted(QModelIndex,int,int)));
1201 QSignalSpy rowsInsertedSpy(
1202 &model, SIGNAL(rowsInserted(QModelIndex,int,int)));
1203 if (orientation == Qt::Horizontal)
1204 model.setHorizontalHeaderLabels(labels);
1205 else
1206 model.setVerticalHeaderLabels(labels);
1207 for (int i = 0; i < expectedLabels.count(); ++i)
1208 QCOMPARE(model.headerData(i, Qt::Orientation(orientation)).toString(), expectedLabels.at(i));
1209 QCOMPARE(columnsInsertedSpy.count(),
1210 (orientation == Qt::Vertical) ? 0 : labels.count() > columns);
1211 QCOMPARE(rowsInsertedSpy.count(),
1212 (orientation == Qt::Horizontal) ? 0 : labels.count() > rows);
1213 }
1214
itemDataChanged()1215 void tst_QStandardItemModel::itemDataChanged()
1216 {
1217 QStandardItemModel model(6, 4);
1218 QStandardItem item;
1219 QSignalSpy dataChangedSpy(
1220 &model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)));
1221 QSignalSpy itemChangedSpy(
1222 &model, SIGNAL(itemChanged(QStandardItem *)));
1223
1224 model.setItem(0, &item);
1225 QCOMPARE(dataChangedSpy.count(), 1);
1226 QCOMPARE(itemChangedSpy.count(), 1);
1227 QModelIndex index = model.indexFromItem(&item);
1228 QList<QVariant> args;
1229 args = dataChangedSpy.takeFirst();
1230 QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), index);
1231 QCOMPARE(qvariant_cast<QModelIndex>(args.at(1)), index);
1232 args = itemChangedSpy.takeFirst();
1233 QCOMPARE(qvariant_cast<QStandardItem*>(args.at(0)), &item);
1234
1235 item.setData(QLatin1String("foo"), Qt::DisplayRole);
1236 QCOMPARE(dataChangedSpy.count(), 1);
1237 QCOMPARE(itemChangedSpy.count(), 1);
1238 args = dataChangedSpy.takeFirst();
1239 QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), index);
1240 QCOMPARE(qvariant_cast<QModelIndex>(args.at(1)), index);
1241 args = itemChangedSpy.takeFirst();
1242 QCOMPARE(qvariant_cast<QStandardItem*>(args.at(0)), &item);
1243
1244 item.setData(item.data(Qt::DisplayRole), Qt::DisplayRole);
1245 QCOMPARE(dataChangedSpy.count(), 0);
1246 QCOMPARE(itemChangedSpy.count(), 0);
1247
1248 item.setFlags(Qt::ItemIsEnabled);
1249 QCOMPARE(dataChangedSpy.count(), 1);
1250 QCOMPARE(itemChangedSpy.count(), 1);
1251 args = dataChangedSpy.takeFirst();
1252 QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), index);
1253 QCOMPARE(qvariant_cast<QModelIndex>(args.at(1)), index);
1254 args = itemChangedSpy.takeFirst();
1255 QCOMPARE(qvariant_cast<QStandardItem*>(args.at(0)), &item);
1256
1257 item.setFlags(item.flags());
1258 QCOMPARE(dataChangedSpy.count(), 0);
1259 QCOMPARE(itemChangedSpy.count(), 0);
1260 }
1261
takeHeaderItem()1262 void tst_QStandardItemModel::takeHeaderItem()
1263 {
1264 QStandardItemModel model;
1265 // set header items
1266 QStandardItem *hheader = new QStandardItem();
1267 model.setHorizontalHeaderItem(0, hheader);
1268 QStandardItem *vheader = new QStandardItem();
1269 model.setVerticalHeaderItem(0, vheader);
1270 // take header items
1271 QCOMPARE(model.takeHorizontalHeaderItem(0), hheader);
1272 QCOMPARE(model.takeVerticalHeaderItem(0), vheader);
1273 QCOMPARE(hheader->model(), static_cast<QStandardItemModel*>(0));
1274 QCOMPARE(vheader->model(), static_cast<QStandardItemModel*>(0));
1275 QCOMPARE(model.takeHorizontalHeaderItem(0), static_cast<QStandardItem*>(0));
1276 QCOMPARE(model.takeVerticalHeaderItem(0), static_cast<QStandardItem*>(0));
1277 delete hheader;
1278 delete vheader;
1279 }
1280
useCase1()1281 void tst_QStandardItemModel::useCase1()
1282 {
1283 const int rows = 5;
1284 const int columns = 8;
1285 QStandardItemModel model(rows, columns);
1286 for (int i = 0; i < model.rowCount(); ++i) {
1287 for (int j = 0; j < model.columnCount(); ++j) {
1288 QCOMPARE(model.item(i, j), static_cast<QStandardItem*>(0));
1289
1290 QStandardItem *item = new QStandardItem();
1291 model.setItem(i, j, item);
1292 QCOMPARE(item->row(), i);
1293 QCOMPARE(item->column(), j);
1294 QCOMPARE(item->model(), &model);
1295
1296 QModelIndex index = model.indexFromItem(item);
1297 QCOMPARE(index, model.index(i, j, QModelIndex()));
1298 QStandardItem *sameItem = model.itemFromIndex(index);
1299 QCOMPARE(sameItem, item);
1300 }
1301 }
1302 }
1303
createChildren(QStandardItemModel * model,QStandardItem * parent,int level)1304 static void createChildren(QStandardItemModel *model, QStandardItem *parent, int level)
1305 {
1306 if (level > 4)
1307 return;
1308 for (int i = 0; i < 4; ++i) {
1309 QCOMPARE(parent->rowCount(), i);
1310 parent->appendRow(QList<QStandardItem*>());
1311 for (int j = 0; j < parent->columnCount(); ++j) {
1312 QStandardItem *item = new QStandardItem();
1313 parent->setChild(i, j, item);
1314 QCOMPARE(item->row(), i);
1315 QCOMPARE(item->column(), j);
1316
1317 QModelIndex parentIndex = model->indexFromItem(parent);
1318 QModelIndex index = model->indexFromItem(item);
1319 QCOMPARE(index, model->index(i, j, parentIndex));
1320 QStandardItem *theItem = model->itemFromIndex(index);
1321 QCOMPARE(theItem, item);
1322 QStandardItem *theParent = model->itemFromIndex(parentIndex);
1323 QCOMPARE(theParent, (level == 0) ? (QStandardItem*)0 : parent);
1324 }
1325
1326 {
1327 QStandardItem *item = parent->child(i);
1328 item->setColumnCount(parent->columnCount());
1329 createChildren(model, item, level + 1);
1330 }
1331 }
1332 }
1333
useCase2()1334 void tst_QStandardItemModel::useCase2()
1335 {
1336 QStandardItemModel model;
1337 model.setColumnCount(2);
1338 createChildren(&model, model.invisibleRootItem(), 0);
1339 }
1340
useCase3()1341 void tst_QStandardItemModel::useCase3()
1342 {
1343 // create the tree structure first
1344 QStandardItem *childItem = 0;
1345 for (int i = 0; i < 100; ++i) {
1346 QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
1347 if (childItem)
1348 item->appendRow(childItem);
1349 childItem = item;
1350 }
1351
1352 // add to model as last step
1353 QStandardItemModel model;
1354 model.appendRow(childItem);
1355
1356 // make sure each item has the correct model and parent
1357 QStandardItem *parentItem = 0;
1358 while (childItem) {
1359 QCOMPARE(childItem->model(), &model);
1360 QCOMPARE(childItem->parent(), parentItem);
1361 parentItem = childItem;
1362 childItem = childItem->child(0);
1363 }
1364
1365 // take the item, make sure model is set to 0, but that parents are the same
1366 childItem = model.takeItem(0);
1367 {
1368 parentItem = 0;
1369 QStandardItem *item = childItem;
1370 while (item) {
1371 QCOMPARE(item->model(), static_cast<QStandardItemModel*>(0));
1372 QCOMPARE(item->parent(), parentItem);
1373 parentItem = item;
1374 item = item->child(0);
1375 }
1376 }
1377 delete childItem;
1378 }
1379
rootItemFlags()1380 void tst_QStandardItemModel::rootItemFlags()
1381 {
1382 QStandardItemModel model(6, 4);
1383 QCOMPARE(model.invisibleRootItem()->flags() , model.flags(QModelIndex()));
1384 QCOMPARE(model.invisibleRootItem()->flags() , Qt::ItemIsDropEnabled);
1385
1386 Qt::ItemFlags f = Qt::ItemIsDropEnabled | Qt::ItemIsEnabled;
1387 model.invisibleRootItem()->setFlags(f);
1388 QCOMPARE(model.invisibleRootItem()->flags() , f);
1389 QCOMPARE(model.invisibleRootItem()->flags() , model.flags(QModelIndex()));
1390
1391 #ifndef QT_NO_DRAGANDDROP
1392 model.invisibleRootItem()->setDropEnabled(false);
1393 #endif
1394 QCOMPARE(model.invisibleRootItem()->flags() , Qt::ItemIsEnabled);
1395 QCOMPARE(model.invisibleRootItem()->flags() , model.flags(QModelIndex()));
1396 }
1397
compareModels(QStandardItemModel * model1,QStandardItemModel * model2)1398 bool tst_QStandardItemModel::compareModels(QStandardItemModel *model1, QStandardItemModel *model2)
1399 {
1400 return compareItems(model1->invisibleRootItem(), model2->invisibleRootItem());
1401 }
1402
compareItems(QStandardItem * item1,QStandardItem * item2)1403 bool tst_QStandardItemModel::compareItems(QStandardItem *item1, QStandardItem *item2)
1404 {
1405 if (!item1 && !item2)
1406 return true;
1407 if (!item1 || !item2)
1408 return false;
1409 if (item1->text() != item2->text()){
1410 qDebug() << item1->text() << item2->text();
1411 return false;
1412 }
1413 if (item1->rowCount() != item2->rowCount()) {
1414 // qDebug() << "RowCount" << item1->text() << item1->rowCount() << item2->rowCount();
1415 return false;
1416 }
1417 if (item1->columnCount() != item2->columnCount()) {
1418 // qDebug() << "ColumnCount" << item1->text() << item1->columnCount() << item2->columnCount();
1419 return false;
1420 }
1421 for (int row = 0; row < item1->columnCount(); row++)
1422 for (int col = 0; col < item1->columnCount(); col++) {
1423
1424 if (!compareItems(item1->child(row, col), item2->child(row, col)))
1425 return false;
1426 }
1427 return true;
1428 }
1429
itemFromText(QStandardItem * parent,const QString & text)1430 static QStandardItem *itemFromText(QStandardItem *parent, const QString &text)
1431 {
1432 QStandardItem *item = 0;
1433 for(int i = 0; i < parent->columnCount(); i++)
1434 for(int j = 0; j < parent->rowCount(); j++) {
1435
1436 QStandardItem *child = parent->child(j, i);
1437
1438 if(!child)
1439 continue;
1440
1441 if (child->text() == text) {
1442 if (item) {
1443 return 0;
1444 }
1445 item = child;
1446 }
1447
1448 QStandardItem *candidate = itemFromText(child, text);
1449 if(candidate) {
1450 if (item) {
1451 return 0;
1452 }
1453 item = candidate;
1454 }
1455 }
1456 return item;
1457 }
1458
1459 #ifdef QT_BUILD_INTERNAL
indexFromText(QStandardItemModel * model,const QString & text)1460 static QModelIndex indexFromText(QStandardItemModel *model, const QString &text)
1461 {
1462 QStandardItem *item = itemFromText(model->invisibleRootItem(), text);
1463 /*QVERIFY(item);*/
1464 return model->indexFromItem(item);
1465 }
1466
1467
1468 struct FriendlyTreeView : public QTreeView
1469 {
1470 friend class tst_QStandardItemModel;
1471 Q_DECLARE_PRIVATE(QTreeView)
1472 };
1473 #endif
1474
treeDragAndDrop()1475 void tst_QStandardItemModel::treeDragAndDrop()
1476 {
1477 #ifdef QT_BUILD_INTERNAL
1478 const int nRow = 5;
1479 const int nCol = 3;
1480
1481 QStandardItemModel model;
1482 QStandardItemModel checkModel;
1483
1484 for (int i = 0; i < nRow; ++i) {
1485 QList<QStandardItem *> colItems1;
1486 for (int c = 0 ; c < nCol; c ++)
1487 colItems1 << new QStandardItem(QString("item %1 - %0").arg(c).arg(i));
1488 model.appendRow(colItems1);
1489
1490 for (int j = 0; j < nRow; ++j) {
1491 QList<QStandardItem *> colItems2;
1492 for (int c = 0 ; c < nCol; c ++)
1493 colItems2 << new QStandardItem(QString("item %1/%2 - %0").arg(c).arg(i).arg(j));
1494 colItems1.at(0)->appendRow(colItems2);
1495
1496 for (int k = 0; k < nRow; ++k) {
1497 QList<QStandardItem *> colItems3;
1498 for (int c = 0 ; c < nCol; c ++)
1499 colItems3 << new QStandardItem(QString("item %1/%2/%3 - %0").arg(c).arg(i).arg(j).arg(k));
1500 colItems2.at(0)->appendRow(colItems3);
1501 }
1502 }
1503 }
1504
1505 for (int i = 0; i < nRow; ++i) {
1506 QList<QStandardItem *> colItems1;
1507 for (int c = 0 ; c < nCol; c ++)
1508 colItems1 << new QStandardItem(QString("item %1 - %0").arg(c).arg(i));
1509 checkModel.appendRow(colItems1);
1510
1511 for (int j = 0; j < nRow; ++j) {
1512 QList<QStandardItem *> colItems2;
1513 for (int c = 0 ; c < nCol; c ++)
1514 colItems2 << new QStandardItem(QString("item %1/%2 - %0").arg(c).arg(i).arg(j));
1515 colItems1.at(0)->appendRow(colItems2);
1516
1517 for (int k = 0; k < nRow; ++k) {
1518 QList<QStandardItem *> colItems3;
1519 for (int c = 0 ; c < nCol; c ++)
1520 colItems3 << new QStandardItem(QString("item %1/%2/%3 - %0").arg(c).arg(i).arg(j).arg(k));
1521 colItems2.at(0)->appendRow(colItems3);
1522 }
1523 }
1524 }
1525
1526 QVERIFY(compareModels(&model, &checkModel));
1527
1528 FriendlyTreeView view;
1529 view.setModel(&model);
1530 view.expandAll();
1531 view.show();
1532 #ifndef QT_NO_DRAGANDDROP
1533 view.setDragDropMode(QAbstractItemView::InternalMove);
1534 #endif
1535 view.setSelectionMode(QAbstractItemView::ExtendedSelection);
1536
1537 QItemSelectionModel *selection = view.selectionModel();
1538
1539 //
1540 // step1 drag "item 1" and "item 2" into "item 4"
1541 //
1542 {
1543 selection->clear();
1544 selection->select(QItemSelection(indexFromText(&model, QString("item 1 - 0")),
1545 indexFromText(&model, QString("item 1 - %0").arg(nCol-1))), QItemSelectionModel::Select);
1546
1547 selection->select(QItemSelection(indexFromText(&model, QString("item 2 - 0")),
1548 indexFromText(&model, QString("item 2 - %0").arg(nCol-1))), QItemSelectionModel::Select);
1549
1550 //code based from QAbstractItemView::startDrag and QAbstractItemView::dropEvent
1551 QModelIndexList indexes = view.selectedIndexes();
1552 QMimeData *data = model.mimeData(indexes);
1553 if(model.dropMimeData(data, Qt::MoveAction, 0, 0, indexFromText(&model, "item 4 - 0")))
1554 view.d_func()->clearOrRemove();
1555 delete data;
1556
1557 QVERIFY(!compareModels(&model, &checkModel)); //the model must be different at this point
1558 QStandardItem *item4 = itemFromText(checkModel.invisibleRootItem(), "item 4 - 0");
1559 item4->insertRow(0, checkModel.takeRow(1));
1560 item4->insertRow(1, checkModel.takeRow(1));
1561 QVERIFY(compareModels(&model, &checkModel));
1562 }
1563
1564 //
1565 // step2 drag "item 3" and "item 3/0" into "item 4"
1566 //
1567 {
1568 selection->clear();
1569 selection->select(QItemSelection(indexFromText(&model, QString("item 3 - 0")),
1570 indexFromText(&model, QString("item 3 - %0").arg(nCol-1))), QItemSelectionModel::Select);
1571
1572 selection->select(QItemSelection(indexFromText(&model, QString("item 3/0 - 0")),
1573 indexFromText(&model, QString("item 3/0 - %0").arg(nCol-1))), QItemSelectionModel::Select);
1574
1575 //code based from QAbstractItemView::startDrag and QAbstractItemView::dropEvent
1576 QModelIndexList indexes = view.selectedIndexes();
1577 QMimeData *data = model.mimeData(indexes);
1578 if(model.dropMimeData(data, Qt::MoveAction, 0, 0, indexFromText(&model, "item 4 - 0")))
1579 view.d_func()->clearOrRemove();
1580 delete data;
1581
1582 QVERIFY(!compareModels(&model, &checkModel)); //the model must be different at this point
1583 QStandardItem *item4 = itemFromText(checkModel.invisibleRootItem(), "item 4 - 0");
1584 item4->insertRow(0, checkModel.takeRow(1));
1585
1586 QVERIFY(compareModels(&model, &checkModel));
1587 }
1588
1589 //
1590 // step2 drag "item 3" and "item 3/0/2" into "item 0/2"
1591 // ( remember "item 3" is now the first child of "item 4")
1592 //
1593 {
1594 selection->clear();
1595 selection->select(QItemSelection(indexFromText(&model, QString("item 3 - 0")),
1596 indexFromText(&model, QString("item 3 - %0").arg(nCol-1))), QItemSelectionModel::Select);
1597
1598 selection->select(QItemSelection(indexFromText(&model, QString("item 3/0/2 - 0")),
1599 indexFromText(&model, QString("item 3/0/2 - %0").arg(nCol-1))), QItemSelectionModel::Select);
1600
1601 //code based from QAbstractItemView::startDrag and QAbstractItemView::dropEvent
1602 QModelIndexList indexes = view.selectedIndexes();
1603 QMimeData *data = model.mimeData(indexes);
1604 if(model.dropMimeData(data, Qt::MoveAction, 0, 0, indexFromText(&model, "item 0/2 - 0")))
1605 view.d_func()->clearOrRemove();
1606 delete data;
1607
1608 QVERIFY(!compareModels(&model, &checkModel)); //the model must be different at this point
1609 QStandardItem *item02 = itemFromText(checkModel.invisibleRootItem(), "item 0/2 - 0");
1610 QStandardItem *item4 = itemFromText(checkModel.invisibleRootItem(), "item 4 - 0");
1611 item02->insertRow(0, item4->takeRow(0));
1612
1613 QVERIFY(compareModels(&model, &checkModel));
1614 }
1615 #endif
1616 }
1617
removeRowsAndColumns()1618 void tst_QStandardItemModel::removeRowsAndColumns()
1619 {
1620 #define VERIFY_MODEL \
1621 for (int c = 0; c < col_list.count(); c++) \
1622 for (int r = 0; r < row_list.count(); r++) \
1623 QCOMPARE(model.item(r,c)->text() , row_list[r] + "x" + col_list[c]);
1624
1625 QVector<QString> row_list = QString("1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20").split(',').toVector();
1626 QVector<QString> col_list = row_list;
1627 QStandardItemModel model;
1628 for (int c = 0; c < col_list.count(); c++)
1629 for (int r = 0; r < row_list.count(); r++)
1630 model.setItem(r, c, new QStandardItem(row_list[r] + "x" + col_list[c]));
1631 VERIFY_MODEL
1632
1633 row_list.remove(3);
1634 model.removeRow(3);
1635 VERIFY_MODEL
1636
1637 col_list.remove(5);
1638 model.removeColumn(5);
1639 VERIFY_MODEL
1640
1641 row_list.remove(2, 5);
1642 model.removeRows(2, 5);
1643 VERIFY_MODEL
1644
1645 col_list.remove(1, 6);
1646 model.removeColumns(1, 6);
1647 VERIFY_MODEL
1648
1649 QList<QStandardItem *> row_taken = model.takeRow(6);
1650 QCOMPARE(row_taken.count(), col_list.count());
1651 for (int c = 0; c < col_list.count(); c++)
1652 QCOMPARE(row_taken[c]->text() , row_list[6] + "x" + col_list[c]);
1653 row_list.remove(6);
1654 VERIFY_MODEL
1655
1656 QList<QStandardItem *> col_taken = model.takeColumn(10);
1657 QCOMPARE(col_taken.count(), row_list.count());
1658 for (int r = 0; r < row_list.count(); r++)
1659 QCOMPARE(col_taken[r]->text() , row_list[r] + "x" + col_list[10]);
1660 col_list.remove(10);
1661 VERIFY_MODEL
1662 }
1663
1664
1665 QTEST_MAIN(tst_QStandardItemModel)
1666 #include "tst_qstandarditemmodel.moc"
1667