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
30#include <QApplication>
31#include <QHeaderView>
32#include <QLineEdit>
33#include <QScrollBar>
34#include <QSignalSpy>
35#include <QStyledItemDelegate>
36#include <QTreeWidget>
37#include <QTreeWidgetItemIterator>
38#include <QTest>
39
40class tst_QTreeWidget : public QObject
41{
42 Q_OBJECT
43
44public:
45 tst_QTreeWidget() = default;
46
47public slots:
48 void initTestCase();
49 void cleanupTestCase();
50 void init();
51 void cleanup();
52
53private slots:
54 void getSetCheck();
55 void addTopLevelItem();
56 void currentItem_data();
57 void currentItem();
58 void editItem_data();
59 void editItem();
60 void takeItem_data();
61 void takeItem();
62 void removeChild_data();
63 void removeChild();
64 void setItemHidden();
65 void setItemHidden2();
66 void selectedItems_data();
67 void selectedItems();
68 void itemAssignment();
69 void clone_data();
70 void clone();
71 void expand_data();
72 void expand();
73 void checkState_data();
74 void checkState();
75 void findItems_data();
76 void findItems();
77 void findItemsInColumn();
78 void sortItems_data();
79 void sortItems();
80 void deleteItems_data();
81 void deleteItems();
82 void itemAboveOrBelow();
83 void itemStreaming_data();
84 void itemStreaming();
85 void insertTopLevelItems_data();
86 void insertTopLevelItems();
87 void keyboardNavigation();
88 void keyboardNavigationWithHidden();
89 void scrollToItem();
90 void setSortingEnabled();
91 void match();
92 void columnCount();
93 void setHeaderLabels();
94 void setHeaderItem();
95 void itemWidget_data();
96 void itemWidget();
97 void insertItemsWithSorting_data();
98 void insertItemsWithSorting();
99 void insertExpandedItemsWithSorting_data();
100 void insertExpandedItemsWithSorting();
101 void changeDataWithSorting_data();
102 void changeDataWithSorting();
103 void changeDataWithStableSorting_data();
104 void changeDataWithStableSorting();
105 void sizeHint_data();
106 void sizeHint();
107
108 void sortedIndexOfChild_data();
109 void sortedIndexOfChild();
110 void defaultRowSizes();
111
112 void task191552_rtl();
113 void task203673_selection();
114 void rootItemFlags();
115 void task218661_setHeaderData();
116 void task245280_sortChildren();
117 void task253109_itemHeight();
118
119 void nonEditableTristate();
120
121 // QTreeWidgetItem
122 void itemOperatorLessThan();
123 void addChild();
124 void setData();
125 void enableDisable();
126
127 void expandAndCallapse();
128 void itemData();
129 void setDisabled();
130 void setSpanned();
131 void removeSelectedItem();
132 void removeCurrentItem();
133 void removeCurrentItem_task186451();
134 void randomExpand();
135 void crashTest();
136 void sortAndSelect();
137
138 void task206367_duplication();
139 void selectionOrder();
140
141 void setSelectionModel();
142 void task217309();
143 void setCurrentItemExpandsParent();
144 void task239150_editorWidth();
145 void setTextUpdate();
146 void taskQTBUG2844_visualItemRect();
147 void setChildIndicatorPolicy();
148
149 void taskQTBUG_34717_collapseAtBottom();
150 void task20345_sortChildren();
151 void getMimeDataWithInvalidItem();
152 void testVisualItemRect();
153 void reparentHiddenItem();
154 void persistentChildIndex();
155#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
156 void clearItemData();
157#endif
158
159public slots:
160 void itemSelectionChanged();
161 void emitDataChanged();
162
163public:
164 class PublicTreeWidget : public QTreeWidget
165 {
166 public:
167 using QTreeWidget::indexFromItem;
168 using QTreeWidget::mimeData;
169 using QTreeWidget::sizeHintForColumn;
170 void deleteCurrent() { delete currentItem(); }
171 };
172
173 class PublicTreeItem : public QTreeWidgetItem
174 {
175 public:
176 using QTreeWidgetItem::QTreeWidgetItem;
177 using QTreeWidgetItem::emitDataChanged;
178 };
179
180private:
181 PublicTreeWidget *testWidget = nullptr;
182};
183
184// Testing get/set functions
185void tst_QTreeWidget::getSetCheck()
186{
187 QTreeWidget obj1;
188 // int QTreeWidget::columnCount()
189 // void QTreeWidget::setColumnCount(int)
190 obj1.setColumnCount(0);
191 QCOMPARE(obj1.columnCount(), 0);
192
193 obj1.setColumnCount(std::numeric_limits<int>::min());
194 QCOMPARE(obj1.columnCount(), 0);
195
196 //obj1.setColumnCount(INT_MAX);
197 //QCOMPARE(obj1.columnCount(), INT_MAX);
198 // Since setColumnCount allocates memory, there is no way this will succeed
199
200 obj1.setColumnCount(100);
201 QCOMPARE(obj1.columnCount(), 100);
202
203 // QTreeWidgetItem * QTreeWidget::headerItem()
204 // void QTreeWidget::setHeaderItem(QTreeWidgetItem *)
205 QTreeWidgetItem *var2 = new QTreeWidgetItem();
206 obj1.setHeaderItem(var2);
207 QCOMPARE(obj1.headerItem(), var2);
208
209 obj1.setHeaderItem(nullptr);
210// QCOMPARE(obj1.headerItem(), nullptr);
211
212 // QTreeWidgetItem * QTreeWidget::currentItem()
213 // void QTreeWidget::setCurrentItem(QTreeWidgetItem *)
214 QTreeWidgetItem *var3 = new QTreeWidgetItem(&obj1);
215 obj1.setCurrentItem(var3);
216 QCOMPARE(obj1.currentItem(), var3);
217
218 obj1.setCurrentItem(nullptr);
219 QCOMPARE(obj1.currentItem(), nullptr);
220}
221
222using IntList = QVector<int>;
223using ListIntList = QVector<IntList>;
224using PersistentModelIndexVec = QVector<QPersistentModelIndex>;
225using TreeItem = QTreeWidgetItem;
226using TreeItemList = QVector<TreeItem*>;
227
228Q_DECLARE_METATYPE(Qt::Orientation)
229Q_DECLARE_METATYPE(QTreeWidgetItem*)
230Q_DECLARE_METATYPE(TreeItemList)
231
232void tst_QTreeWidget::initTestCase()
233{
234 qMetaTypeId<Qt::Orientation>();
235 qRegisterMetaType<QTreeWidgetItem*>(typeName: "QTreeWidgetItem*");
236 qRegisterMetaType<QList<QPersistentModelIndex>>(typeName: "QList<QPersistentModelIndex>");
237 qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>(typeName: "QAbstractItemModel::LayoutChangeHint");
238
239 testWidget = new PublicTreeWidget();
240 testWidget->show();
241 QVERIFY(QTest::qWaitForWindowExposed(testWidget));
242}
243
244void tst_QTreeWidget::cleanupTestCase()
245{
246 testWidget->hide();
247 delete testWidget;
248}
249
250void tst_QTreeWidget::init()
251{
252 testWidget->clear();
253 testWidget->setColumnCount(2);
254}
255
256void tst_QTreeWidget::cleanup()
257{
258}
259
260TreeItem *operator<<(TreeItem *parent, const TreeItemList &children)
261{
262 for (TreeItem *child : children)
263 parent->addChild(child);
264 return parent;
265}
266
267static void populate(QTreeWidget *widget, const TreeItemList &topLevelItems,
268 TreeItem *headerItem = nullptr)
269{
270 widget->clear();
271 widget->setHeaderItem(headerItem);
272 for (TreeItem *item : topLevelItems)
273 widget->addTopLevelItem(item);
274}
275
276void tst_QTreeWidget::addTopLevelItem()
277{
278 QTreeWidget tree;
279 QCOMPARE(tree.topLevelItemCount(), 0);
280
281 // try to add 0
282 tree.addTopLevelItem(item: nullptr);
283 QCOMPARE(tree.topLevelItemCount(), 0);
284 QCOMPARE(tree.indexOfTopLevelItem(nullptr), -1);
285
286 // add one at a time
287 QList<TreeItem *> tops;
288 for (int i = 0; i < 10; ++i) {
289 TreeItem *ti = new TreeItem();
290 QCOMPARE(tree.indexOfTopLevelItem(ti), -1);
291 tree.addTopLevelItem(item: ti);
292 QCOMPARE(tree.topLevelItemCount(), i+1);
293 QCOMPARE(tree.topLevelItem(i), ti);
294 QCOMPARE(tree.topLevelItem(-1), nullptr);
295 QCOMPARE(tree.indexOfTopLevelItem(ti), i);
296 QCOMPARE(ti->parent(), nullptr);
297 tree.addTopLevelItem(item: ti);
298 QCOMPARE(tree.topLevelItemCount(), i+1);
299 tops.append(t: ti);
300 }
301
302 // delete one at a time
303 while (!tops.isEmpty()) {
304 TreeItem *ti = tops.takeFirst();
305 delete ti;
306 QCOMPARE(tree.topLevelItemCount(), tops.count());
307 for (int i = 0; i < tops.count(); ++i)
308 QCOMPARE(tree.topLevelItem(i), tops.at(i));
309 }
310
311 // add none
312 {
313 int count = tree.topLevelItemCount();
314 tree.addTopLevelItems(items: tops);
315 QCOMPARE(tree.topLevelItemCount(), count);
316 }
317
318 // add many at a time
319 {
320 const int count = 10;
321 for (int i = 0; i < 100; i += count) {
322 tops.clear();
323 for (int j = 0; j < count; ++j)
324 tops << new TreeItem(QStringList(QString::number(j)));
325 tree.addTopLevelItems(items: tops);
326 QCOMPARE(tree.topLevelItemCount(), count + i);
327 for (int j = 0; j < count; ++j)
328 QCOMPARE(tree.topLevelItem(i+j), tops.at(j));
329
330 tree.addTopLevelItems(items: tops);
331 QCOMPARE(tree.topLevelItemCount(), count + i);
332 }
333 }
334
335 // insert
336 {
337 tops.clear();
338 for (int i = 0; i < 10; ++i)
339 tops << new TreeItem();
340 int count = tree.topLevelItemCount();
341 tree.insertTopLevelItems(index: count, items: tops);
342 QCOMPARE(tree.topLevelItemCount(), count + 10);
343 }
344
345 // invalid insert
346 {
347 tops.clear();
348 for (int i = 0; i < 10; ++i)
349 tops << new TreeItem();
350 int count = tree.topLevelItemCount();
351 tree.insertTopLevelItems(index: 100000, items: tops); // should be a no-op
352 QCOMPARE(tree.topLevelItemCount(), count);
353 }
354}
355
356void tst_QTreeWidget::currentItem_data()
357{
358 QTest::addColumn<TreeItemList>(name: "topLevelItems");
359
360 QTest::newRow(dataTag: "only top-level items, 2 columns")
361 << (TreeItemList()
362 << new TreeItem({"a", "b"})
363 << new TreeItem({"c", "d"}));
364 TreeItemList lst;
365 lst << (new TreeItem({"a", "b"})
366 << (TreeItemList()
367 << new TreeItem({"c", "d"})
368 << new TreeItem({"c", "d"})
369 )
370 )
371 << (new TreeItem({"e", "f"})
372 << (TreeItemList()
373 << new TreeItem({"g", "h"})
374 << new TreeItem({"g", "h"})
375 )
376 );
377 QTest::newRow(dataTag: "hierarchy, 2 columns") << lst;
378}
379
380void tst_QTreeWidget::currentItem()
381{
382 QFETCH(TreeItemList, topLevelItems);
383
384 QTreeWidget tree;
385 tree.show();
386 populate(widget: &tree, topLevelItems, headerItem: new TreeItem({"1", "2"}));
387 QTreeWidgetItem *previous = nullptr;
388 for (int x = 0; x < 2; ++x) {
389 tree.setSelectionBehavior(x ? QAbstractItemView::SelectItems
390 : QAbstractItemView::SelectRows);
391 QSignalSpy currentItemChangedSpy(
392 &tree, &QTreeWidget::currentItemChanged);
393 QSignalSpy itemSelectionChangedSpy(
394 &tree, &QTreeWidget::itemSelectionChanged);
395
396 QTreeWidgetItemIterator it(&tree);
397 // do all items
398 while (QTreeWidgetItem *item = (*it++)) {
399 tree.setCurrentItem(item);
400 QCOMPARE(tree.currentItem(), item);
401
402 QCOMPARE(currentItemChangedSpy.count(), 1);
403 QVariantList args = currentItemChangedSpy.takeFirst();
404 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
405 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(1)), previous);
406
407 QCOMPARE(itemSelectionChangedSpy.count(), 1);
408 itemSelectionChangedSpy.clear();
409
410 previous = item;
411 // do all columns
412 for (int col = 0; col < item->columnCount(); ++col) {
413 tree.setCurrentItem(item, column: col);
414 QCOMPARE(tree.currentItem(), item);
415 QCOMPARE(tree.currentColumn(), col);
416
417 if (!currentItemChangedSpy.isEmpty()) {
418 // ### we get a currentItemChanged() when what really
419 // changed was just currentColumn(). Should it be like this?
420 QCOMPARE(currentItemChangedSpy.count(), 1);
421 QVariantList args = currentItemChangedSpy.takeFirst();
422 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
423 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(1)), item);
424 if (tree.selectionBehavior() == QAbstractItemView::SelectItems) {
425 QCOMPARE(itemSelectionChangedSpy.count(), 1);
426 itemSelectionChangedSpy.clear();
427 } else {
428 QCOMPARE(itemSelectionChangedSpy.count(), 0);
429 }
430 }
431 }
432 }
433 }
434
435 // can't set the headerItem to be the current item
436 tree.setCurrentItem(tree.headerItem());
437 QCOMPARE(tree.currentItem(), nullptr);
438}
439
440void tst_QTreeWidget::editItem_data()
441{
442 QTest::addColumn<TreeItemList>(name: "topLevelItems");
443
444 {
445 TreeItemList list;
446 for (int i = 0; i < 10; i++) {
447 TreeItem *item = new TreeItem(QStringList() << "col1" << "col2");
448 if ((i & 1) == 0)
449 item->setFlags(item->flags() | Qt::ItemIsEditable);
450 else
451 item->setFlags(item->flags() & ~Qt::ItemIsEditable);
452 list << item;
453 }
454 QTest::newRow(dataTag: "2 columns, only even items editable")
455 << list;
456 }
457}
458
459void tst_QTreeWidget::editItem()
460{
461 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
462 QSKIP("Wayland: This fails. Figure out why.");
463
464 QFETCH(TreeItemList, topLevelItems);
465
466 QTreeWidget tree;
467 populate(widget: &tree, topLevelItems, headerItem: new TreeItem(QStringList() << "1" << "2"));
468 tree.show();
469 QVERIFY(QTest::qWaitForWindowActive(&tree));
470
471 QSignalSpy itemChangedSpy(&tree, &QTreeWidget::itemChanged);
472
473 QTreeWidgetItemIterator it(&tree);
474 while (QTreeWidgetItem *item = (*it++)) {
475 for (int col = 0; col < item->columnCount(); ++col) {
476 if (!(item->flags() & Qt::ItemIsEditable))
477 QTest::ignoreMessage(type: QtWarningMsg, message: "edit: editing failed");
478 tree.editItem(item, column: col);
479 QCoreApplication::processEvents();
480 QCoreApplication::processEvents();
481 QLineEdit *editor = tree.findChild<QLineEdit*>();
482 if (editor) {
483 QVERIFY(item->flags() & Qt::ItemIsEditable);
484 QCOMPARE(editor->selectedText(), editor->text());
485 QTest::keyClick(widget: editor, key: Qt::Key_A);
486 QTest::keyClick(widget: editor, key: Qt::Key_Enter);
487 QCoreApplication::processEvents();
488 QCOMPARE(itemChangedSpy.count(), 1);
489 QVariantList args = itemChangedSpy.takeFirst();
490 QCOMPARE(qvariant_cast<QTreeWidgetItem *>(args.at(0)), item);
491 QCOMPARE(qvariant_cast<int>(args.at(1)), col);
492 } else {
493 QVERIFY(!(item->flags() & Qt::ItemIsEditable));
494 }
495 }
496 }
497}
498
499void tst_QTreeWidget::takeItem_data()
500{
501 QTest::addColumn<int>(name: "index");
502 QTest::addColumn<bool>(name: "topLevel");
503 QTest::addColumn<bool>(name: "outOfBounds");
504
505 QTest::newRow(dataTag: "First, topLevel") << 0 << true << false;
506 QTest::newRow(dataTag: "Last, topLevel") << 2 << true << false;
507 QTest::newRow(dataTag: "Middle, topLevel") << 1 << true << false;
508 QTest::newRow(dataTag: "Out of bounds, toplevel, (index: -1)") << -1 << true << true;
509 QTest::newRow(dataTag: "Out of bounds, toplevel, (index: 3)") << 3 << true << true;
510
511 QTest::newRow(dataTag: "First, child of topLevel") << 0 << false << false;
512 QTest::newRow(dataTag: "Last, child of topLevel") << 2 << false << false;
513 QTest::newRow(dataTag: "Middle, child of topLevel") << 1 << false << false;
514 QTest::newRow(dataTag: "Out of bounds, child of toplevel, (index: -1)") << -1 << false << true;
515 QTest::newRow(dataTag: "Out of bounds, child of toplevel, (index: 3)") << 3 << false << true;
516}
517
518void tst_QTreeWidget::takeItem()
519{
520 QFETCH(int, index);
521 QFETCH(bool, topLevel);
522 QFETCH(bool, outOfBounds);
523
524 for (int i = 0; i < 3; ++i) {
525 QTreeWidgetItem *top = new QTreeWidgetItem(testWidget);
526 top->setText(column: 0, QStringLiteral("top") + QString::number(i));
527 for (int j = 0; j < 3; ++j) {
528 QTreeWidgetItem *child = new QTreeWidgetItem(top);
529 child->setText(column: 0, QStringLiteral("child") + QString::number(j));
530 }
531 }
532
533 QCOMPARE(testWidget->topLevelItemCount(), 3);
534 QCOMPARE(testWidget->topLevelItem(0)->childCount(), 3);
535
536 if (topLevel) {
537 int count = testWidget->topLevelItemCount();
538 QTreeWidgetItem *item = testWidget->takeTopLevelItem(index);
539 if (outOfBounds) {
540 QCOMPARE(item, nullptr);
541 QCOMPARE(count, testWidget->topLevelItemCount());
542 } else {
543 QCOMPARE(item->text(0), QStringLiteral("top") + QString::number(index));
544 QCOMPARE(count-1, testWidget->topLevelItemCount());
545 delete item;
546 }
547 } else {
548 int count = testWidget->topLevelItem(index: 0)->childCount();
549 QTreeWidgetItem *item = testWidget->topLevelItem(index: 0)->takeChild(index);
550 if (outOfBounds) {
551 QCOMPARE(item, nullptr);
552 QCOMPARE(count, testWidget->topLevelItem(0)->childCount());
553 } else {
554 QCOMPARE(item->text(0), QStringLiteral("child") + QString::number(index));
555 QCOMPARE(count-1, testWidget->topLevelItem(0)->childCount());
556 delete item;
557 }
558 }
559}
560
561void tst_QTreeWidget::removeChild_data()
562{
563 QTest::addColumn<int>(name: "childCount");
564 QTest::addColumn<int>(name: "removeAt");
565
566 QTest::newRow(dataTag: "10 remove 3") << 10 << 3;
567}
568
569void tst_QTreeWidget::removeChild()
570{
571 QFETCH(int, childCount);
572 QFETCH(int, removeAt);
573
574 const QScopedPointer<QTreeWidgetItem> root(new QTreeWidgetItem);
575 for (int i = 0; i < childCount; ++i)
576 new QTreeWidgetItem(root.data(), QStringList(QString::number(i)));
577
578 QCOMPARE(root->childCount(), childCount);
579 for (int j = 0; j < childCount; ++j)
580 QCOMPARE(root->child(j)->text(0), QString::number(j));
581
582 const QScopedPointer<QTreeWidgetItem> remove(root->child(index: removeAt));
583 root->removeChild(child: remove.data());
584
585 QCOMPARE(root->childCount(), childCount - 1);
586 for (int k = 0; k < childCount; ++k) {
587 if (k == removeAt)
588 QCOMPARE(remove->text(0), QString::number(k));
589 else if (k < removeAt)
590 QCOMPARE(root->child(k)->text(0), QString::number(k));
591 else if (k > removeAt)
592 QCOMPARE(root->child(k - 1)->text(0), QString::number(k));
593 }
594}
595
596void tst_QTreeWidget::setItemHidden()
597{
598 QTreeWidgetItem *parent = new QTreeWidgetItem(testWidget);
599 parent->setText(column: 0, atext: "parent");
600 QTreeWidgetItem *child = new QTreeWidgetItem(parent);
601 child->setText(column: 0, atext: "child");
602 QVERIFY(child->parent());
603
604 testWidget->expandItem(item: parent);
605 testWidget->scrollToItem(item: child);
606
607 QVERIFY(testWidget->visualItemRect(parent).isValid()
608 && testWidget->viewport()->rect().intersects(testWidget->visualItemRect(parent)));
609 QVERIFY(testWidget->visualItemRect(child).isValid()
610 && testWidget->viewport()->rect().intersects(testWidget->visualItemRect(child)));
611
612 QVERIFY(!parent->isHidden());
613 QVERIFY(!child->isHidden());
614
615 parent->setHidden(true);
616
617 QVERIFY(!(testWidget->visualItemRect(parent).isValid()
618 && testWidget->viewport()->rect().intersects(testWidget->visualItemRect(parent))));
619 QVERIFY(!(testWidget->visualItemRect(child).isValid()
620 && testWidget->viewport()->rect().intersects(testWidget->visualItemRect(child))));
621
622 QVERIFY(parent->isHidden());
623 QVERIFY(!child->isHidden());
624
625 // From task 78670 (This caused an core dump)
626 // Check if we can set an item visible if it already is visible.
627 parent->setHidden(false);
628 parent->setHidden(false);
629 QVERIFY(!parent->isHidden());
630
631
632 // hide, hide and then unhide.
633 parent->setHidden(true);
634 parent->setHidden(true);
635 parent->setHidden(false);
636 QVERIFY(!parent->isHidden());
637}
638
639
640void tst_QTreeWidget::setItemHidden2()
641{
642 // From Task 78587
643 const QStringList hl({"ID", "Desc"});
644 testWidget->setColumnCount(hl.count());
645 testWidget->setHeaderLabels(hl);
646 testWidget->setSortingEnabled(true);
647
648 QTreeWidgetItem *top = new QTreeWidgetItem(testWidget);
649 top->setText(column: 0, atext: "ItemList");
650 for (int i = 1; i <= 4; i++) {
651 auto leaf = new QTreeWidgetItem(top);
652 leaf->setText(column: 0, atext: QString::number(i));
653 leaf->setText(column: 1, QStringLiteral("Item %1").arg(a: i));
654 }
655
656 if (testWidget->topLevelItemCount() > 0) {
657 top = testWidget->topLevelItem(index: 0);
658 top->setExpanded(true);
659 }
660
661 if (testWidget->topLevelItemCount() > 0) {
662 top = testWidget->topLevelItem(index: 0);
663 for (int i = 0; i < top->childCount(); i++) {
664 auto leaf = top->child(index: i);
665 if (leaf->text(column: 0).toInt() % 2 == 0) {
666 if (!leaf->isHidden())
667 leaf->setHidden(true);
668 }
669 }
670 }
671}
672
673
674void tst_QTreeWidget::selectedItems_data()
675{
676 QTest::addColumn<int>(name: "topLevel");
677 QTest::addColumn<int>(name: "children");
678 QTest::addColumn<bool>(name: "closeTopLevel");
679 QTest::addColumn<ListIntList>(name: "selectedItems");
680 QTest::addColumn<ListIntList>(name: "hiddenItems");
681 QTest::addColumn<ListIntList>(name: "expectedItems");
682
683 ListIntList selectedItems;
684 ListIntList hiddenItems;
685 ListIntList expectedItems;
686
687 selectedItems.clear(); hiddenItems.clear(); expectedItems.clear();
688 selectedItems
689 << (IntList()
690 << 0);
691 expectedItems
692 << (IntList() << 0);
693 QTest::newRow(dataTag: "2 top with 2 children, closed, top0 selected, no hidden")
694 << 2 << 2 << true << selectedItems << hiddenItems << expectedItems;
695
696 selectedItems.clear(); hiddenItems.clear(); expectedItems.clear();
697 selectedItems
698 << (IntList()
699 << 0 << 0);
700 expectedItems
701 << (IntList() << 0 << 0);
702 QTest::newRow(dataTag: "2 top with 2 children, closed, top0child0 selected, no hidden")
703 << 2 << 2 << true << selectedItems << hiddenItems << expectedItems;
704
705 selectedItems.clear(); hiddenItems.clear(); expectedItems.clear();
706 selectedItems
707 << (IntList()
708 << 0 << 0);
709 expectedItems
710 << (IntList()
711 << 0 << 0);
712 QTest::newRow(dataTag: "2 top with 2 children, open, top0child0 selected, no hidden")
713 << 2 << 2 << false << selectedItems << hiddenItems << expectedItems;
714
715 selectedItems.clear(); hiddenItems.clear(); expectedItems.clear();
716 selectedItems << (IntList() << 0);
717 hiddenItems << (IntList() << 0);
718 QTest::newRow(dataTag: "2 top with 2 children, closed, top0 selected, top0 hidden")
719 << 2 << 2 << true << selectedItems << hiddenItems << expectedItems;
720
721 selectedItems.clear(); hiddenItems.clear(); expectedItems.clear();
722 selectedItems << (IntList() << 0 << 0);
723 hiddenItems << (IntList() << 0);
724 expectedItems << (IntList() << 0 << 0);
725 QTest::newRow(dataTag: "2 top with 2 children, closed, top0child0 selected, top0 hidden")
726 << 2 << 2 << true << selectedItems << hiddenItems << expectedItems;
727
728 selectedItems.clear(); hiddenItems.clear(); expectedItems.clear();
729 selectedItems
730 << (IntList() << 0)
731 << (IntList() << 0 << 0)
732 << (IntList() << 0 << 1)
733 << (IntList() << 1)
734 << (IntList() << 1 << 0)
735 << (IntList() << 1 << 1);
736 expectedItems
737 << (IntList() << 0)
738 << (IntList() << 0 << 0)
739 << (IntList() << 0 << 1)
740 << (IntList() << 1)
741 << (IntList() << 1 << 0)
742 << (IntList() << 1 << 1);
743 QTest::newRow(dataTag: "2 top with 2 children, closed, all selected, no hidden")
744 << 2 << 2 << true << selectedItems << hiddenItems << expectedItems;
745
746
747 selectedItems.clear(); hiddenItems.clear(); expectedItems.clear();
748 selectedItems
749 << (IntList() << 0)
750 << (IntList() << 0 << 0)
751 << (IntList() << 0 << 1)
752 << (IntList() << 1)
753 << (IntList() << 1 << 0)
754 << (IntList() << 1 << 1);
755 hiddenItems
756 << (IntList() << 0);
757 expectedItems
758 //<< (IntList() << 0)
759 << (IntList() << 0 << 0)
760 << (IntList() << 0 << 1)
761 << (IntList() << 1)
762 << (IntList() << 1 << 0)
763 << (IntList() << 1 << 1);
764 QTest::newRow(dataTag: "2 top with 2 children, closed, all selected, top0 hidden")
765 << 2 << 2 << true << selectedItems << hiddenItems << expectedItems;
766
767 selectedItems.clear(); hiddenItems.clear(); expectedItems.clear();
768 selectedItems
769 << (IntList() << 0)
770 << (IntList() << 0 << 0)
771 << (IntList() << 0 << 1)
772 << (IntList() << 1)
773 << (IntList() << 1 << 0)
774 << (IntList() << 1 << 1);
775 hiddenItems
776 << (IntList() << 0 << 1)
777 << (IntList() << 1);
778 expectedItems
779 << (IntList() << 0)
780 << (IntList() << 0 << 0)
781 //<< (IntList() << 0 << 1)
782 //<< (IntList() << 1)
783 << (IntList() << 1 << 0)
784 << (IntList() << 1 << 1);
785
786 QTest::newRow(dataTag: "2 top with 2 children, closed, all selected, top0child1 and top1")
787 << 2 << 2 << true << selectedItems << hiddenItems << expectedItems;
788
789}
790
791void tst_QTreeWidget::selectedItems()
792{
793 QFETCH(int, topLevel);
794 QFETCH(int, children);
795 QFETCH(bool, closeTopLevel);
796 QFETCH(const ListIntList, selectedItems);
797 QFETCH(const ListIntList, hiddenItems);
798 QFETCH(const ListIntList, expectedItems);
799
800 // create items
801 for (int t = 0; t < topLevel; ++t) {
802 QTreeWidgetItem *top = new QTreeWidgetItem(testWidget);
803 const QString topS = QLatin1String("top") + QString::number(t);
804 top->setText(column: 0, atext: topS);
805 for (int c = 0; c < children; ++c) {
806 QTreeWidgetItem *child = new QTreeWidgetItem(top);
807 child->setText(column: 0, atext: topS + QLatin1String("child") + QString::number(c));
808 }
809 }
810
811 // set selected
812 for (const auto &itemPath : selectedItems) {
813 QTreeWidgetItem *item = nullptr;
814 for (int index : itemPath) {
815 if (!item)
816 item = testWidget->topLevelItem(index);
817 else
818 item = item->child(index);
819 }
820 item->setSelected(true);
821 }
822
823 // hide rows
824 for (const auto &itemPath : hiddenItems) {
825 QTreeWidgetItem *item = nullptr;
826 for (int index : itemPath) {
827 if (!item)
828 item = testWidget->topLevelItem(index);
829 else
830 item = item->child(index);
831 }
832 item->setHidden(true);
833 }
834
835 // open/close toplevel
836 for (int i = 0; i < testWidget->topLevelItemCount(); ++i) {
837 if (closeTopLevel)
838 testWidget->collapseItem(item: testWidget->topLevelItem(index: i));
839 else
840 testWidget->expandItem(item: testWidget->topLevelItem(index: i));
841 }
842
843 // check selectedItems
844 const auto sel = testWidget->selectedItems();
845 QCOMPARE(sel.count(), expectedItems.count());
846 for (const auto &itemPath : expectedItems) {
847 QTreeWidgetItem *item = nullptr;
848 for (int index : itemPath) {
849 if (!item)
850 item = testWidget->topLevelItem(index);
851 else
852 item = item->child(index);
853 }
854 if (item)
855 QVERIFY(sel.contains(item));
856 }
857
858 // compare isSelected
859 for (int t = 0; t < testWidget->topLevelItemCount(); ++t) {
860 QTreeWidgetItem *top = testWidget->topLevelItem(index: t);
861 if (top->isSelected() && !top->isHidden())
862 QVERIFY(sel.contains(top));
863 for (int c = 0; c < top->childCount(); ++c) {
864 QTreeWidgetItem *child = top->child(index: c);
865 if (child->isSelected() && !child->isHidden())
866 QVERIFY(sel.contains(child));
867 }
868 }
869
870#if QT_DEPRECATED_SINCE(5, 13)
871QT_WARNING_PUSH
872QT_WARNING_DISABLE_DEPRECATED
873 // Possible to select null without crashing?
874 testWidget->setItemSelected(item: nullptr, select: true);
875 QVERIFY(!testWidget->isItemSelected(nullptr));
876QT_WARNING_POP
877#endif
878
879 // unselect
880 for (const auto &itemPath : selectedItems) {
881 QTreeWidgetItem *item = nullptr;
882 for (int index : itemPath) {
883 if (!item)
884 item = testWidget->topLevelItem(index);
885 else
886 item = item->child(index);
887 }
888 item->setSelected(false);
889 }
890 QCOMPARE(testWidget->selectedItems().count(), 0);
891}
892
893void tst_QTreeWidget::itemAssignment()
894{
895 // create item with children and parent but not insert in the view
896 QTreeWidgetItem grandParent;
897 QTreeWidgetItem *parent = new QTreeWidgetItem(&grandParent);
898 parent->setText(column: 0, atext: "foo");
899 parent->setText(column: 1, atext: "bar");
900 for (int i = 0; i < 5; ++i) {
901 QTreeWidgetItem *child = new QTreeWidgetItem(parent);
902 child->setText(column: 0, atext: "bingo");
903 child->setText(column: 1, atext: "bango");
904 }
905 QCOMPARE(parent->parent(), &grandParent);
906 QVERIFY(!parent->treeWidget());
907 QCOMPARE(parent->columnCount(), 2);
908 QCOMPARE(parent->text(0), QString("foo"));
909 QCOMPARE(parent->childCount(), 5);
910 QCOMPARE(parent->child(0)->parent(), parent);
911
912 // create item which is inserted in the widget
913 QTreeWidgetItem item(testWidget);
914 item.setText(column: 0, atext: "baz");
915 QVERIFY(!item.parent());
916 QCOMPARE(item.treeWidget(), testWidget);
917 QCOMPARE(item.columnCount(), 1);
918 QCOMPARE(item.text(0), QString("baz"));
919 QCOMPARE(item.childCount(), 0);
920
921 // assign and test
922 *parent = item;
923 QCOMPARE(parent->parent(), &grandParent);
924 QVERIFY(!parent->treeWidget());
925 QCOMPARE(parent->columnCount(), 1);
926 QCOMPARE(parent->text(0), QString("baz"));
927 QCOMPARE(parent->childCount(), 5);
928 QCOMPARE(parent->child(0)->parent(), parent);
929}
930
931void tst_QTreeWidget::clone_data()
932{
933 QTest::addColumn<int>(name: "column");
934 QTest::addColumn<int>(name: "topLevelIndex");
935 QTest::addColumn<int>(name: "childIndex");
936 QTest::addColumn<QStringList>(name: "topLevelText");
937 QTest::addColumn<QStringList>(name: "childText");
938 QTest::addColumn<bool>(name: "cloneChild");
939
940 QTest::newRow(dataTag: "clone parent with child") << 0 << 0 << 0
941 << (QStringList() << "some text")
942 << (QStringList() << "more text")
943 << false;
944
945 QTest::newRow(dataTag: "clone child") << 0 << 0 << 0
946 << (QStringList() << "some text")
947 << (QStringList() << "more text")
948 << true;
949}
950
951void tst_QTreeWidget::clone()
952{
953 QFETCH(int, column);
954 QFETCH(int, topLevelIndex);
955 QFETCH(int, childIndex);
956 QFETCH(const QStringList, topLevelText);
957 QFETCH(const QStringList, childText);
958 QFETCH(bool, cloneChild);
959
960 for (const QString &tl : topLevelText) {
961 QTreeWidgetItem *item = new QTreeWidgetItem(testWidget);
962 item->setText(column, atext: tl);
963 for (const QString &cl : childText) {
964 QTreeWidgetItem *child = new QTreeWidgetItem(item);
965 child->setText(column, atext: cl);
966 }
967 }
968
969 QTreeWidgetItem *original = testWidget->topLevelItem(index: topLevelIndex);
970 QTreeWidgetItem *copy = original->clone();
971 QCOMPARE(copy->text(column), original->text(column));
972 QCOMPARE(copy->childCount(), original->childCount());
973 QVERIFY(!copy->parent());
974 QVERIFY(!copy->treeWidget());
975
976 QTreeWidgetItem *originalChild = original->child(index: childIndex);
977 QTreeWidgetItem *copiedChild = cloneChild ? originalChild->clone() : copy->child(index: childIndex);
978 QVERIFY(copiedChild != originalChild);
979 QCOMPARE(copiedChild->text(column), originalChild->text(column));
980 QCOMPARE(copiedChild->childCount(), originalChild->childCount());
981 QCOMPARE(copiedChild->parent(), cloneChild ? nullptr : copy);
982 QVERIFY(!copiedChild->treeWidget());
983 if (cloneChild)
984 delete copiedChild;
985 delete copy;
986}
987
988void tst_QTreeWidget::expand_data()
989{
990 QTest::addColumn<int>(name: "topLevelIndex");
991 QTest::addColumn<int>(name: "topLevelCount");
992 QTest::addColumn<int>(name: "childIndex");
993 QTest::addColumn<int>(name: "childCount");
994
995 QTest::newRow(dataTag: "the only test data for now") << 0 << 1 << 0 << 1;
996}
997
998void tst_QTreeWidget::expand()
999{
1000 QFETCH(int, topLevelIndex);
1001 QFETCH(int, topLevelCount);
1002 QFETCH(int, childIndex);
1003 QFETCH(int, childCount);
1004
1005 for (int i = 0; i < topLevelCount; ++i) {
1006 QTreeWidgetItem *item = new QTreeWidgetItem(testWidget);
1007 for (int j = 0; j < childCount; ++j)
1008 new QTreeWidgetItem(item);
1009 }
1010
1011 QTreeWidgetItem *topLevelItem = testWidget->topLevelItem(index: topLevelIndex);
1012 QTreeWidgetItem *childItem = topLevelItem->child(index: childIndex);
1013
1014 QVERIFY(!topLevelItem->isExpanded());
1015 topLevelItem->setExpanded(true);
1016 QVERIFY(topLevelItem->isExpanded());
1017
1018 QVERIFY(!childItem->isExpanded());
1019 childItem->setExpanded(true);
1020 QVERIFY(childItem->isExpanded());
1021
1022 QVERIFY(topLevelItem->isExpanded());
1023 topLevelItem->setExpanded(false);
1024 QVERIFY(!topLevelItem->isExpanded());
1025
1026 QVERIFY(childItem->isExpanded());
1027 childItem->setExpanded(false);
1028 QVERIFY(!childItem->isExpanded());
1029}
1030
1031void tst_QTreeWidget::checkState_data()
1032{
1033}
1034
1035void tst_QTreeWidget::checkState()
1036{
1037 QTreeWidgetItem *item = new QTreeWidgetItem(testWidget);
1038 item->setCheckState(column: 0, state: Qt::Unchecked);
1039 QTreeWidgetItem *firstChild = new QTreeWidgetItem(item);
1040 firstChild->setCheckState(column: 0, state: Qt::Unchecked);
1041 QTreeWidgetItem *seccondChild = new QTreeWidgetItem(item);
1042 seccondChild->setCheckState(column: 0, state: Qt::Unchecked);
1043
1044 QCOMPARE(item->checkState(0), Qt::Unchecked);
1045 QCOMPARE(firstChild->checkState(0), Qt::Unchecked);
1046 QCOMPARE(seccondChild->checkState(0), Qt::Unchecked);
1047
1048 firstChild->setCheckState(column: 0, state: Qt::Checked);
1049 QCOMPARE(item->checkState(0), Qt::Unchecked);
1050 QCOMPARE(firstChild->checkState(0), Qt::Checked);
1051 QCOMPARE(seccondChild->checkState(0), Qt::Unchecked);
1052
1053 item->setFlags(item->flags()|Qt::ItemIsAutoTristate);
1054 QCOMPARE(item->checkState(0), Qt::PartiallyChecked);
1055 QCOMPARE(firstChild->checkState(0), Qt::Checked);
1056 QCOMPARE(seccondChild->checkState(0), Qt::Unchecked);
1057
1058 seccondChild->setCheckState(column: 0, state: Qt::Checked);
1059 QCOMPARE(item->checkState(0), Qt::Checked);
1060 QCOMPARE(firstChild->checkState(0), Qt::Checked);
1061 QCOMPARE(seccondChild->checkState(0), Qt::Checked);
1062
1063 firstChild->setCheckState(column: 0, state: Qt::Unchecked);
1064 seccondChild->setCheckState(column: 0, state: Qt::Unchecked);
1065 QCOMPARE(item->checkState(0), Qt::Unchecked);
1066 QCOMPARE(firstChild->checkState(0), Qt::Unchecked);
1067 QCOMPARE(seccondChild->checkState(0), Qt::Unchecked);
1068
1069 // Can't force the state to PartiallyChecked; state comes from children
1070 item->setCheckState(column: 0, state: Qt::PartiallyChecked);
1071 QCOMPARE(item->checkState(0), Qt::Unchecked);
1072 QCOMPARE(firstChild->checkState(0), Qt::Unchecked);
1073 QCOMPARE(seccondChild->checkState(0), Qt::Unchecked);
1074}
1075
1076void tst_QTreeWidget::findItems_data()
1077{
1078 QTest::addColumn<int>(name: "column");
1079 QTest::addColumn<QStringList>(name: "topLevelText");
1080 QTest::addColumn<QStringList>(name: "childText");
1081 QTest::addColumn<QString>(name: "pattern");
1082 QTest::addColumn<int>(name: "resultCount");
1083 QTest::addColumn<QStringList>(name: "resultText");
1084
1085 QTest::newRow(dataTag: "find in toplevel")
1086 << 0
1087 << (QStringList() << "This is a text" << "This is another" << "This is the one")
1088 << (QStringList() << "A child" << "This is not the one" << "And yet another child")
1089 << "This is the one"
1090 << 1
1091 << (QStringList() << "This is the one");
1092
1093 QTest::newRow(dataTag: "find child")
1094 << 0
1095 << (QStringList() << "This is a text" << "This is another" << "This is the one")
1096 << (QStringList() << "A child" << "This is not the one" << "And yet another child")
1097 << "A child"
1098 << 3 // once for each branch
1099 << (QStringList() << "A child");
1100
1101}
1102
1103void tst_QTreeWidget::findItems()
1104{
1105 QFETCH(int, column);
1106 QFETCH(const QStringList, topLevelText);
1107 QFETCH(const QStringList, childText);
1108 QFETCH(QString, pattern);
1109 QFETCH(int, resultCount);
1110 QFETCH(const QStringList, resultText);
1111
1112 for (const QString &tl : topLevelText) {
1113 QTreeWidgetItem *item = new QTreeWidgetItem(testWidget);
1114 item->setText(column, atext: tl);
1115 for (const QString &cl : childText) {
1116 QTreeWidgetItem *child = new QTreeWidgetItem(item);
1117 child->setText(column, atext: cl);
1118 }
1119 }
1120
1121 QList<QTreeWidgetItem*> result = testWidget->findItems(text: pattern,
1122 flags: Qt::MatchExactly|Qt::MatchRecursive);
1123 QCOMPARE(result.count(), resultCount);
1124
1125 for (int k = 0; k < result.count() && k < resultText.count(); ++k)
1126 QCOMPARE(result.at(k)->text(column), resultText.at(k));
1127}
1128
1129void tst_QTreeWidget::findItemsInColumn()
1130{
1131 // Create 5 root items.
1132 for (int i = 0; i < 5; i++)
1133 new QTreeWidgetItem(testWidget, QStringList() << QString::number(i));
1134
1135 // Create a child with two columns for each root item.
1136 for (int i = 0; i < 5; i++) {
1137 QTreeWidgetItem * const parent = testWidget->topLevelItem(index: i);
1138 new QTreeWidgetItem(parent, QStringList() << QString::number(i * 10) << QString::number(i * 100));
1139 }
1140
1141 // Recursively search column one for 400.
1142 QList<QTreeWidgetItem*> items = testWidget->findItems(text: "400", flags: Qt::MatchExactly|Qt::MatchRecursive, column: 1);
1143 QCOMPARE(items.count(), 1);
1144}
1145
1146void tst_QTreeWidget::sortItems_data()
1147{
1148 QTest::addColumn<int>(name: "column");
1149 QTest::addColumn<Qt::SortOrder>(name: "order");
1150 QTest::addColumn<QStringList>(name: "topLevelText");
1151 QTest::addColumn<QStringList>(name: "childText");
1152 QTest::addColumn<QStringList>(name: "topLevelResult");
1153 QTest::addColumn<QStringList>(name: "childResult");
1154 QTest::addColumn<IntList>(name: "expectedTopRows");
1155 QTest::addColumn<IntList>(name: "expectedChildRows");
1156
1157 QTest::newRow(dataTag: "ascending order")
1158 << 0
1159 << Qt::AscendingOrder
1160 << (QStringList() << "c" << "d" << "a" << "b")
1161 << (QStringList() << "e" << "h" << "g" << "f")
1162 << (QStringList() << "a" << "b" << "c" << "d")
1163 << (QStringList() << "e" << "f" << "g" << "h")
1164 << (IntList() << 2 << 3 << 0 << 1)
1165 << (IntList() << 0 << 3 << 2 << 1);
1166
1167 QTest::newRow(dataTag: "descending order")
1168 << 0
1169 << Qt::DescendingOrder
1170 << (QStringList() << "c" << "d" << "a" << "b")
1171 << (QStringList() << "e" << "h" << "g" << "f")
1172 << (QStringList() << "d" << "c" << "b" << "a")
1173 << (QStringList() << "h" << "g" << "f" << "e")
1174 << (IntList() << 1 << 0 << 3 << 2)
1175 << (IntList() << 3 << 0 << 1 << 2);
1176}
1177
1178void tst_QTreeWidget::sortItems()
1179{
1180 QFETCH(int, column);
1181 QFETCH(Qt::SortOrder, order);
1182 QFETCH(QStringList, topLevelText);
1183 QFETCH(QStringList, childText);
1184 QFETCH(QStringList, topLevelResult);
1185 QFETCH(QStringList, childResult);
1186 QFETCH(IntList, expectedTopRows);
1187 QFETCH(IntList, expectedChildRows);
1188 testWidget->setSortingEnabled(false);
1189
1190 for (const QString &tl : topLevelText) {
1191 QTreeWidgetItem *item = new QTreeWidgetItem(testWidget);
1192 item->setText(column, atext: tl);
1193 for (const QString &cl : childText) {
1194 QTreeWidgetItem *child = new QTreeWidgetItem(item);
1195 child->setText(column, atext: cl);
1196 }
1197 }
1198
1199 QAbstractItemModel *model = testWidget->model();
1200 PersistentModelIndexVec tops;
1201 for (int r = 0; r < model->rowCount(parent: QModelIndex()); ++r)
1202 tops.push_back(t: model->index(row: r, column: 0, parent: QModelIndex()));
1203 PersistentModelIndexVec children;
1204 for (int s = 0; s < model->rowCount(parent: tops.constFirst()); ++s)
1205 children.push_back(t: model->index(row: s, column: 0, parent: tops.constFirst()));
1206
1207 testWidget->sortItems(column, order);
1208 QCOMPARE(testWidget->sortColumn(), column);
1209
1210 for (int k = 0; k < topLevelResult.count(); ++k) {
1211 QTreeWidgetItem *item = testWidget->topLevelItem(index: k);
1212 QCOMPARE(item->text(column), topLevelResult.at(k));
1213 for (int l = 0; l < childResult.count(); ++l)
1214 QCOMPARE(item->child(l)->text(column), childResult.at(l));
1215 }
1216
1217 for (int m = 0; m < tops.count(); ++m)
1218 QCOMPARE(tops.at(m).row(), expectedTopRows.at(m));
1219 for (int n = 0; n < children.count(); ++n)
1220 QCOMPARE(children.at(n).row(), expectedChildRows.at(n));
1221}
1222
1223void tst_QTreeWidget::deleteItems_data()
1224{
1225 QTest::addColumn<int>(name: "topLevelCount");
1226 QTest::addColumn<int>(name: "childCount");
1227 QTest::addColumn<int>(name: "grandChildCount");
1228
1229 QTest::addColumn<int>(name: "deleteTopLevelCount");
1230 QTest::addColumn<int>(name: "deleteChildCount");
1231 QTest::addColumn<int>(name: "deleteGrandChildCount");
1232
1233 QTest::addColumn<int>(name: "expectedTopLevelCount");
1234 QTest::addColumn<int>(name: "expectedChildCount");
1235 QTest::addColumn<int>(name: "expectedGrandChildCount");
1236
1237 QTest::addColumn<int>(name: "persistentRow");
1238 QTest::addColumn<int>(name: "persistentColumn");
1239 QTest::addColumn<bool>(name: "persistentIsValid");
1240
1241 QTest::newRow(dataTag: "start with 10, delete 1")
1242 << 10 << 10 << 10
1243 << 1 << 1 << 1
1244 << 9 << 9 << 9
1245 << 0 << 0 << false;
1246 QTest::newRow(dataTag: "start with 10, delete 5")
1247 << 10 << 10 << 10
1248 << 5 << 5 << 5
1249 << 5 << 5 << 5
1250 << 0 << 0 << false;
1251 QTest::newRow(dataTag: "mixed")
1252 << 10 << 13 << 7
1253 << 3 << 7 << 4
1254 << 7 << 6 << 3
1255 << 0 << 0 << false;
1256 QTest::newRow(dataTag: "all")
1257 << 10 << 10 << 10
1258 << 10 << 10 << 10
1259 << 0 << 0 << 0
1260 << 0 << 0 << false;
1261}
1262
1263void tst_QTreeWidget::deleteItems()
1264{
1265 QFETCH(int, topLevelCount);
1266 QFETCH(int, childCount);
1267 QFETCH(int, grandChildCount);
1268
1269 QFETCH(int, deleteTopLevelCount);
1270 QFETCH(int, deleteChildCount);
1271 QFETCH(int, deleteGrandChildCount);
1272
1273 QFETCH(int, expectedTopLevelCount);
1274 QFETCH(int, expectedChildCount);
1275 QFETCH(int, expectedGrandChildCount);
1276
1277 QFETCH(int, persistentRow);
1278 QFETCH(int, persistentColumn);
1279 QFETCH(bool, persistentIsValid);
1280
1281 for (int i = 0; i < topLevelCount; ++i) {
1282 QTreeWidgetItem *top = new QTreeWidgetItem(testWidget);
1283 for (int j = 0; j < childCount; ++j) {
1284 QTreeWidgetItem *child = new QTreeWidgetItem(top);
1285 for (int k = 0; k < grandChildCount; ++k) {
1286 new QTreeWidgetItem(child);
1287 }
1288 }
1289 }
1290
1291 QPersistentModelIndex persistent = testWidget->model()->index(row: persistentRow,
1292 column: persistentColumn);
1293 QVERIFY(persistent.isValid());
1294
1295 QTreeWidgetItem *top = testWidget->topLevelItem(index: 0);
1296 QTreeWidgetItem *child = top->child(index: 0);
1297
1298 for (int n = 0; n < deleteGrandChildCount; ++n)
1299 delete child->child(index: 0);
1300 QCOMPARE(child->childCount(), expectedGrandChildCount);
1301
1302 for (int m = 0; m < deleteChildCount; ++m)
1303 delete top->child(index: 0);
1304 QCOMPARE(top->childCount(), expectedChildCount);
1305
1306 for (int l = 0; l < deleteTopLevelCount; ++l)
1307 delete testWidget->topLevelItem(index: 0);
1308 QCOMPARE(testWidget->topLevelItemCount(), expectedTopLevelCount);
1309
1310 QCOMPARE(persistent.isValid(), persistentIsValid);
1311}
1312
1313
1314void tst_QTreeWidget::itemAboveOrBelow()
1315{
1316 QTreeWidget tw;
1317 tw.setColumnCount(1);
1318 QTreeWidgetItem *twi = new QTreeWidgetItem(&tw, QStringList() << "Test");
1319 QTreeWidgetItem *twi2 = new QTreeWidgetItem(&tw, QStringList() << "Test 2");
1320 QTreeWidgetItem *twi3 = new QTreeWidgetItem(&tw, QStringList() << "Test 3");
1321 tw.show();
1322 QCOMPARE(tw.itemAbove(twi2), twi);
1323 QCOMPARE(tw.itemBelow(twi2), twi3);
1324}
1325
1326void tst_QTreeWidget::itemStreaming_data()
1327{
1328 QTest::addColumn<QString>(name: "text");
1329 QTest::addColumn<QString>(name: "toolTip");
1330 QTest::addColumn<int>(name: "column");
1331
1332 QTest::newRow(dataTag: "Data") << "item text" << "tool tip text" << 0;
1333}
1334
1335void tst_QTreeWidget::itemStreaming()
1336{
1337 QFETCH(QString, text);
1338 QFETCH(QString, toolTip);
1339 QFETCH(int, column);
1340
1341 QTreeWidgetItem item(testWidget);
1342 QCOMPARE(item.text(column), QString());
1343 QCOMPARE(item.toolTip(column), QString());
1344
1345 item.setText(column, atext: text);
1346 item.setToolTip(column, atoolTip: toolTip);
1347 QCOMPARE(item.text(column), text);
1348 QCOMPARE(item.toolTip(column), toolTip);
1349
1350 QByteArray buffer;
1351 QDataStream out(&buffer, QIODevice::WriteOnly);
1352 out << item;
1353
1354 QTreeWidgetItem item2(testWidget);
1355 QCOMPARE(item2.text(column), QString());
1356 QCOMPARE(item2.toolTip(column), QString());
1357
1358 QVERIFY(!buffer.isEmpty());
1359
1360 QDataStream in(&buffer, QIODevice::ReadOnly);
1361 in >> item2;
1362 QCOMPARE(item2.text(column), text);
1363 QCOMPARE(item2.toolTip(column), toolTip);
1364}
1365
1366void tst_QTreeWidget::insertTopLevelItems_data()
1367{
1368 QTest::addColumn<QStringList>(name: "initialText");
1369 QTest::addColumn<QStringList>(name: "insertText");
1370 QTest::addColumn<int>(name: "insertTopLevelIndex");
1371 QTest::addColumn<int>(name: "expectedTopLevelIndex");
1372 QTest::addColumn<int>(name: "insertChildIndex");
1373 QTest::addColumn<int>(name: "expectedChildIndex");
1374
1375 const QStringList initial{ "foo", "bar" };
1376 const QStringList insert{ "baz" };
1377
1378 QTest::newRow(dataTag: "Insert at count") << initial << insert
1379 << initial.count() << initial.count()
1380 << initial.count() << initial.count();
1381 QTest::newRow(dataTag: "Insert in the middle") << initial << insert
1382 << (initial.count() / 2) << (initial.count() / 2)
1383 << (initial.count() / 2) << (initial.count() / 2);
1384 QTest::newRow(dataTag: "Insert less than 0") << initial << insert
1385 << -1 << -1
1386 << -1 << -1;
1387 QTest::newRow(dataTag: "Insert beyond count") << initial << insert
1388 << initial.count() + 1 << -1
1389 << initial.count() + 1 << -1;
1390}
1391
1392void tst_QTreeWidget::insertTopLevelItems()
1393{
1394 QFETCH(QStringList, initialText);
1395 QFETCH(QStringList, insertText);
1396 QFETCH(int, insertTopLevelIndex);
1397 QFETCH(int, expectedTopLevelIndex);
1398 QFETCH(int, insertChildIndex);
1399 QFETCH(int, expectedChildIndex);
1400 testWidget->setSortingEnabled(false);
1401
1402 { // insert the initial items
1403 QCOMPARE(testWidget->topLevelItemCount(), 0);
1404 for (int i = 0; i < initialText.count(); ++i) {
1405 QTreeWidgetItem *top = new QTreeWidgetItem(QStringList(initialText.at(i)));
1406 testWidget->addTopLevelItem(item: top);
1407 QCOMPARE(testWidget->indexOfTopLevelItem(top), i);
1408 }
1409 QCOMPARE(testWidget->topLevelItemCount(), initialText.count());
1410 }
1411
1412 { // test adding children
1413 QTreeWidgetItem *topLevel = testWidget->topLevelItem(index: 0);
1414 for (int i = 0; i < initialText.count(); ++i)
1415 topLevel->addChild(child: new QTreeWidgetItem(QStringList(initialText.at(i))));
1416 QCOMPARE(topLevel->childCount(), initialText.count());
1417 }
1418
1419 { // test adding more top level items
1420 QTreeWidgetItem *topsy = new QTreeWidgetItem(QStringList(insertText.at(i: 0)));
1421 testWidget->insertTopLevelItem(index: insertTopLevelIndex, item: topsy);
1422 if (expectedTopLevelIndex == -1) {
1423 QCOMPARE(testWidget->topLevelItemCount(), initialText.count());
1424 delete topsy;
1425 } else {
1426 QTreeWidgetItem *item = testWidget->topLevelItem(index: expectedTopLevelIndex);
1427 QVERIFY(item != nullptr);
1428 QCOMPARE(item->text(0), insertText.at(0));
1429 QCOMPARE(testWidget->indexOfTopLevelItem(item), expectedTopLevelIndex);
1430 }
1431 }
1432
1433 { // test adding more children
1434 QTreeWidgetItem *topLevel = testWidget->topLevelItem(index: 0);
1435 QVERIFY(topLevel != nullptr);
1436 QTreeWidgetItem *child = new QTreeWidgetItem(QStringList(insertText.at(i: 0)));
1437 topLevel->insertChild(index: insertChildIndex, child);
1438 if (expectedChildIndex == -1) {
1439 QCOMPARE(topLevel->childCount(), initialText.count());
1440 delete child;
1441 } else {
1442 QTreeWidgetItem *item = topLevel->child(index: expectedChildIndex);
1443 QVERIFY(item != nullptr);
1444 QCOMPARE(item->text(0), insertText.at(0));
1445 }
1446 }
1447}
1448
1449static void fillTreeWidget(QTreeWidgetItem *parent, int rows)
1450{
1451 const int columns = parent->treeWidget()->columnCount();
1452 for (int r = 0; r < rows; ++r) {
1453 const QString prefix = QLatin1String("[r:") + QString::number(r) + QLatin1String(",c:");
1454 QTreeWidgetItem *w = new QTreeWidgetItem(parent);
1455 for (int c = 0; c < columns; ++c)
1456 w->setText(column: c, atext: prefix + QString::number(c) + QLatin1Char(']'));
1457 fillTreeWidget(parent: w, rows: rows - r - 1);
1458 }
1459}
1460
1461static void fillTreeWidget(QTreeWidget *tree, int rows)
1462{
1463 for (int r = 0; r < rows; ++r) {
1464 QTreeWidgetItem *w = new QTreeWidgetItem();
1465 const QString prefix = QLatin1String("[r:") + QString::number(r) + QLatin1String(",c:");
1466 for (int c = 0; c < tree->columnCount(); ++c)
1467 w->setText(column: c, atext: prefix + QString::number(c) + QLatin1Char(']'));
1468 tree->insertTopLevelItem(index: r, item: w);
1469 fillTreeWidget(parent: w, rows: rows - r - 1);
1470 }
1471}
1472
1473void tst_QTreeWidget::keyboardNavigation()
1474{
1475 int rows = 8;
1476
1477 fillTreeWidget(tree: testWidget, rows);
1478
1479 const QVector<Qt::Key> keymoves {
1480 Qt::Key_Down, Qt::Key_Right, Qt::Key_Left,
1481 Qt::Key_Down, Qt::Key_Down, Qt::Key_Down, Qt::Key_Down,
1482 Qt::Key_Right,
1483 Qt::Key_Up, Qt::Key_Left, Qt::Key_Left,
1484 Qt::Key_Up, Qt::Key_Down, Qt::Key_Up, Qt::Key_Up,
1485 Qt::Key_Up, Qt::Key_Up, Qt::Key_Up, Qt::Key_Up,
1486 Qt::Key_Down, Qt::Key_Right, Qt::Key_Down, Qt::Key_Down,
1487 Qt::Key_Down, Qt::Key_Right, Qt::Key_Down, Qt::Key_Down,
1488 Qt::Key_Left, Qt::Key_Left, Qt::Key_Up, Qt::Key_Down,
1489 Qt::Key_Up, Qt::Key_Up, Qt::Key_Up, Qt::Key_Left,
1490 Qt::Key_Down, Qt::Key_Right, Qt::Key_Right, Qt::Key_Right,
1491 Qt::Key_Left, Qt::Key_Left, Qt::Key_Right, Qt::Key_Left
1492 };
1493
1494 int row = 0;
1495 QTreeWidgetItem *item = testWidget->topLevelItem(index: 0);
1496 testWidget->setCurrentItem(item);
1497 QCOMPARE(testWidget->currentItem(), item);
1498 QCoreApplication::processEvents();
1499
1500 QScrollBar *scrollBar = testWidget->horizontalScrollBar();
1501 for (const Qt::Key key : keymoves) {
1502 int valueBeforeClick = scrollBar->value();
1503 const bool checkScroll = (valueBeforeClick >= scrollBar->singleStep());
1504 QTest::keyClick(widget: testWidget, key);
1505 QCoreApplication::processEvents();
1506
1507 switch (key) {
1508 case Qt::Key_Up:
1509 if (row > 0) {
1510 if (item->parent())
1511 item = item->parent()->child(index: row - 1);
1512 else
1513 item = testWidget->topLevelItem(index: row - 1);
1514 row -= 1;
1515 } else if (item->parent()) {
1516 item = item->parent();
1517 row = item->parent() ? item->parent()->indexOfChild(achild: item) : testWidget->indexOfTopLevelItem(item);
1518 }
1519 break;
1520 case Qt::Key_Down:
1521 if (item->isExpanded()) {
1522 row = 0;
1523 item = item->child(index: row);
1524 } else {
1525 row = qMin(a: rows - 1, b: row + 1);
1526 if (item->parent())
1527 item = item->parent()->child(index: row);
1528 else
1529 item = testWidget->topLevelItem(index: row);
1530 }
1531 break;
1532 case Qt::Key_Left:
1533 if (checkScroll) {
1534 QVERIFY(item->isExpanded());
1535 QCOMPARE(scrollBar->value(), valueBeforeClick - scrollBar->singleStep());
1536 }
1537 // windows style right will walk to the parent
1538 if (testWidget->currentItem() != item) {
1539 QCOMPARE(testWidget->currentItem(), item->parent());
1540 item = testWidget->currentItem();
1541 row = item->parent() ? item->parent()->indexOfChild(achild: item) : testWidget->indexOfTopLevelItem(item);;
1542 }
1543 break;
1544 case Qt::Key_Right:
1545 if (checkScroll)
1546 QCOMPARE(scrollBar->value(), valueBeforeClick + scrollBar->singleStep());
1547 // windows style right will walk to the first child
1548 if (testWidget->currentItem() != item) {
1549 QCOMPARE(testWidget->currentItem()->parent(), item);
1550 row = item->indexOfChild(achild: testWidget->currentItem());
1551 item = testWidget->currentItem();
1552 QCOMPARE(row, 0);
1553 }
1554 break;
1555 default:
1556 QVERIFY(false);
1557 }
1558
1559 QTreeWidgetItem *current = testWidget->currentItem();
1560 QCOMPARE(current->text(0), QLatin1String("[r:") + QString::number(row) + QLatin1String(",c:0]"));
1561 if (current->parent())
1562 QCOMPARE(current->parent()->indexOfChild(current), row);
1563 else
1564 QCOMPARE(testWidget->indexOfTopLevelItem(current), row);
1565 }
1566}
1567
1568void tst_QTreeWidget::keyboardNavigationWithHidden()
1569{
1570 QTreeWidget tw;
1571 for (int i = 0; i < 1000; ++i)
1572 tw.addTopLevelItem(item: new QTreeWidgetItem({QString::number(i), QStringLiteral("second col")}));
1573 // QTBUG-34832 - when first/last item is hidden,
1574 // Key_PageUp/Down/Home/End will not work as expected.
1575 tw.topLevelItem(index: 0)->setHidden(true);
1576 tw.topLevelItem(index: tw.model()->rowCount() - 1)->setHidden(true);
1577 // PageUp
1578 tw.setCurrentIndex(tw.model()->index(row: 2, column: 0));
1579 QCOMPARE(tw.currentIndex(), tw.model()->index(2, 0));
1580 QTest::keyClick(widget: tw.viewport(), key: Qt::Key_PageUp);
1581 QCOMPARE(tw.currentIndex(), tw.model()->index(1, 0));
1582 // PageDown
1583 tw.setCurrentIndex(tw.model()->index(row: tw.model()->rowCount() - 3, column: 0));
1584 QCOMPARE(tw.currentIndex(), tw.model()->index(tw.model()->rowCount() - 3, 0));
1585 QTest::keyClick(widget: tw.viewport(), key: Qt::Key_PageDown);
1586 QCOMPARE(tw.currentIndex(), tw.model()->index(tw.model()->rowCount() - 2, 0));
1587 // Key_Home
1588 QTest::keyClick(widget: tw.viewport(), key: Qt::Key_Home);
1589 QCOMPARE(tw.currentIndex(), tw.model()->index(1, 0));
1590 // Key_End
1591 QTest::keyClick(widget: tw.viewport(), key: Qt::Key_End);
1592 QCOMPARE(tw.currentIndex(), tw.model()->index(tw.model()->rowCount() - 2, 0));
1593}
1594
1595void tst_QTreeWidget::scrollToItem()
1596{
1597 // Check if all parent nodes of the item found are expanded.
1598 // Reported in task #78761
1599 QTreeWidgetItem *search = nullptr;
1600 for (int i = 0; i < 2; ++i) {
1601 QTreeWidgetItem *bar = new QTreeWidgetItem(testWidget);
1602 bar->setText(column: 0, atext: QString::number(i));
1603
1604 for (int j = 0; j < 2; ++j) {
1605 QTreeWidgetItem *foo = new QTreeWidgetItem(bar);
1606 foo->setText(column: 0, atext: bar->text(column: 0) + QString::number(j));
1607
1608 for (int k = 0; k < 2; ++k) {
1609 search = new QTreeWidgetItem(foo);
1610 search->setText(column: 0, atext: foo->text(column: 0) + QString::number(k));
1611 }
1612 }
1613 }
1614
1615 testWidget->setHeaderLabels(QStringList() << "foo");
1616 testWidget->scrollToItem(item: search);
1617 QCOMPARE(search->text(0), QLatin1String("111"));
1618
1619 QTreeWidgetItem *par = search->parent();
1620 QVERIFY(par->isExpanded());
1621 par = par->parent();
1622 QVERIFY(par->isExpanded());
1623}
1624
1625// From task #85413
1626void tst_QTreeWidget::setSortingEnabled()
1627{
1628 const QStringList hl{ "ID" };
1629 testWidget->setColumnCount(hl.count());
1630 testWidget->setHeaderLabels(hl);
1631
1632 QTreeWidgetItem *item1 = new QTreeWidgetItem(testWidget);
1633 QTreeWidgetItem *item2 = new QTreeWidgetItem(testWidget);
1634
1635 testWidget->setSortingEnabled(true);
1636 QCOMPARE(testWidget->isSortingEnabled(), true);
1637 QCOMPARE(testWidget->isSortingEnabled(), testWidget->header()->isSortIndicatorShown());
1638 QCOMPARE(testWidget->topLevelItem(0), item1);
1639 QCOMPARE(testWidget->topLevelItem(1), item2);
1640
1641 // Make sure we do it twice
1642 testWidget->setSortingEnabled(true);
1643 QCOMPARE(testWidget->isSortingEnabled(), true);
1644 QCOMPARE(testWidget->isSortingEnabled(), testWidget->header()->isSortIndicatorShown());
1645
1646 testWidget->setSortingEnabled(false);
1647 QCOMPARE(testWidget->isSortingEnabled(), false);
1648 QCOMPARE(testWidget->isSortingEnabled(), testWidget->header()->isSortIndicatorShown());
1649
1650 testWidget->setSortingEnabled(false);
1651 QCOMPARE(testWidget->isSortingEnabled(), false);
1652 QCOMPARE(testWidget->isSortingEnabled(), testWidget->header()->isSortIndicatorShown());
1653
1654 // And back again so that we make sure that we test the transition from false to true
1655 testWidget->setSortingEnabled(true);
1656 QCOMPARE(testWidget->isSortingEnabled(), true);
1657 QCOMPARE(testWidget->isSortingEnabled(), testWidget->header()->isSortIndicatorShown());
1658
1659 testWidget->setSortingEnabled(true);
1660 QCOMPARE(testWidget->isSortingEnabled(), true);
1661 QCOMPARE(testWidget->isSortingEnabled(), testWidget->header()->isSortIndicatorShown());
1662
1663 testWidget->setSortingEnabled(false);
1664}
1665
1666void tst_QTreeWidget::addChild()
1667{
1668 QTreeWidget tree;
1669 for (int x = 0; x < 2; ++x) {
1670 QTreeWidget *view = x ? &tree : static_cast<QTreeWidget*>(nullptr);
1671 QTreeWidgetItem *item = new QTreeWidgetItem(view);
1672 QCOMPARE(item->childCount(), 0);
1673
1674 // try to add 0
1675 item->addChild(child: nullptr);
1676 QCOMPARE(item->childCount(), 0);
1677 QCOMPARE(item->indexOfChild(nullptr), -1);
1678
1679 // add one at a time
1680 QList<QTreeWidgetItem*> children;
1681 for (int i = 0; i < 10; ++i) {
1682 QTreeWidgetItem *child = new QTreeWidgetItem();
1683 item->addChild(child);
1684 QCOMPARE(item->childCount(), i+1);
1685 QCOMPARE(item->child(i), child);
1686 QCOMPARE(item->indexOfChild(child), i);
1687 QCOMPARE(child->parent(), item);
1688 QCOMPARE(child->treeWidget(), view);
1689 item->addChild(child);
1690 QCOMPARE(item->childCount(), i+1);
1691 children.append(t: child);
1692 }
1693
1694 // take them all
1695 QList<QTreeWidgetItem*> taken = item->takeChildren();
1696 QCOMPARE(taken, children);
1697 QCOMPARE(item->childCount(), 0);
1698 for (int i = 0; i < taken.count(); ++i) {
1699 QCOMPARE(taken.at(i)->parent(), nullptr);
1700 QCOMPARE(taken.at(i)->treeWidget(), nullptr);
1701 item->addChild(child: taken.at(i)); // re-add
1702 }
1703
1704 // delete one at a time
1705 while (!children.isEmpty()) {
1706 QTreeWidgetItem *ti = children.takeFirst();
1707 delete ti;
1708 QCOMPARE(item->childCount(), children.count());
1709 for (int i = 0; i < children.count(); ++i)
1710 QCOMPARE(item->child(i), children.at(i));
1711 }
1712
1713 // add none
1714 {
1715 int count = item->childCount();
1716 item->addChildren(children: QList<QTreeWidgetItem*>());
1717 QCOMPARE(item->childCount(), count);
1718 }
1719
1720 // add many at a time
1721 const int count = 10;
1722 for (int i = 0; i < 100; i += count) {
1723 QList<QTreeWidgetItem*> list;
1724 for (int j = 0; j < count; ++j)
1725 list << new QTreeWidgetItem(QStringList(QString::number(j)));
1726 item->addChildren(children: list);
1727 QCOMPARE(item->childCount(), count + i);
1728 for (int j = 0; j < count; ++j) {
1729 QCOMPARE(item->child(i+j), list.at(j));
1730 QCOMPARE(item->child(i+j)->parent(), item);
1731 }
1732
1733 item->addChildren(children: list);
1734 QCOMPARE(item->childCount(), count + i);
1735 }
1736
1737 if (!view)
1738 delete item;
1739 }
1740}
1741
1742void tst_QTreeWidget::setData()
1743{
1744 {
1745 QTreeWidgetItem *headerItem = new QTreeWidgetItem();
1746 headerItem->setText(column: 0, atext: "Item1");
1747 testWidget->setHeaderItem(headerItem);
1748
1749 QSignalSpy headerDataChangedSpy(
1750 testWidget->model(), &QAbstractItemModel::headerDataChanged);
1751 QSignalSpy dataChangedSpy(
1752 testWidget->model(), &QAbstractItemModel::dataChanged);
1753 QSignalSpy itemChangedSpy(
1754 testWidget, &QTreeWidget::itemChanged);
1755 headerItem->setText(column: 0, atext: "test");
1756 QCOMPARE(dataChangedSpy.count(), 0);
1757 QCOMPARE(headerDataChangedSpy.count(), 1);
1758 QCOMPARE(itemChangedSpy.count(), 0); // no itemChanged() signal for header item
1759
1760 headerItem->setData(column: -1, role: -1, value: QVariant());
1761 }
1762
1763 {
1764 QSignalSpy itemChangedSpy(
1765 testWidget, &QTreeWidget::itemChanged);
1766 QTreeWidgetItem *item = new QTreeWidgetItem();
1767 testWidget->addTopLevelItem(item);
1768 for (int x = 0; x < 2; ++x) {
1769 for (int i = 1; i <= 2; ++i) {
1770 for (int j = 0; j < 5; ++j) {
1771 QVariantList args;
1772 const QString iS = QString::number(i);
1773 const QString text = QLatin1String("text ") + iS;
1774 item->setText(column: j, atext: text);
1775 QCOMPARE(item->text(j), text);
1776 QCOMPARE(itemChangedSpy.count(), 1);
1777 args = itemChangedSpy.takeFirst();
1778 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1779 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1780 item->setText(column: j, atext: text);
1781 QCOMPARE(itemChangedSpy.count(), 0);
1782
1783 QPixmap pixmap(32, 32);
1784 pixmap.fill(fillColor: (i == 1) ? Qt::red : Qt::green);
1785 QIcon icon(pixmap);
1786 item->setIcon(column: j, aicon: icon);
1787 QCOMPARE(item->icon(j), icon);
1788 QCOMPARE(itemChangedSpy.count(), 1);
1789 args = itemChangedSpy.takeFirst();
1790 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1791 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1792 item->setIcon(column: j, aicon: icon);
1793 QCOMPARE(itemChangedSpy.count(), 0);
1794
1795 const QString toolTip = QLatin1String("toolTip ") + iS;
1796 item->setToolTip(column: j, atoolTip: toolTip);
1797 QCOMPARE(item->toolTip(j), toolTip);
1798 QCOMPARE(itemChangedSpy.count(), 1);
1799 args = itemChangedSpy.takeFirst();
1800 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1801 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1802 item->setToolTip(column: j, atoolTip: toolTip);
1803 QCOMPARE(itemChangedSpy.count(), 0);
1804
1805 const QString statusTip = QLatin1String("statusTip ") + iS;
1806 item->setStatusTip(column: j, astatusTip: statusTip);
1807 QCOMPARE(item->statusTip(j), statusTip);
1808 QCOMPARE(itemChangedSpy.count(), 1);
1809 args = itemChangedSpy.takeFirst();
1810 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1811 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1812 item->setStatusTip(column: j, astatusTip: statusTip);
1813 QCOMPARE(itemChangedSpy.count(), 0);
1814
1815 const QString whatsThis = QLatin1String("whatsThis ") + iS;
1816 item->setWhatsThis(column: j, awhatsThis: whatsThis);
1817 QCOMPARE(item->whatsThis(j), whatsThis);
1818 QCOMPARE(itemChangedSpy.count(), 1);
1819 args = itemChangedSpy.takeFirst();
1820 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1821 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1822 item->setWhatsThis(column: j, awhatsThis: whatsThis);
1823 QCOMPARE(itemChangedSpy.count(), 0);
1824
1825 QSize sizeHint(64*i, 48*i);
1826 item->setSizeHint(column: j, size: sizeHint);
1827 QCOMPARE(item->sizeHint(j), sizeHint);
1828 QCOMPARE(itemChangedSpy.count(), 1);
1829 args = itemChangedSpy.takeFirst();
1830 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1831 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1832 item->setSizeHint(column: j, size: sizeHint);
1833 QCOMPARE(itemChangedSpy.count(), 0);
1834
1835 QFont font;
1836 item->setFont(column: j, afont: font);
1837 QCOMPARE(item->font(j), font);
1838 QCOMPARE(itemChangedSpy.count(), 1);
1839 args = itemChangedSpy.takeFirst();
1840 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1841 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1842 item->setFont(column: j, afont: font);
1843 QCOMPARE(itemChangedSpy.count(), 0);
1844
1845 Qt::Alignment textAlignment((i == 1)
1846 ? Qt::AlignLeft|Qt::AlignVCenter
1847 : Qt::AlignRight);
1848 item->setTextAlignment(column: j, alignment: textAlignment);
1849 QCOMPARE(item->textAlignment(j), int(textAlignment));
1850 QCOMPARE(itemChangedSpy.count(), 1);
1851 args = itemChangedSpy.takeFirst();
1852 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1853 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1854 item->setTextAlignment(column: j, alignment: textAlignment);
1855 QCOMPARE(itemChangedSpy.count(), 0);
1856
1857 QColor backgroundColor((i == 1) ? Qt::blue : Qt::yellow);
1858 item->setBackground(column: j, brush: backgroundColor);
1859 QCOMPARE(item->background(j).color(), backgroundColor);
1860 QCOMPARE(itemChangedSpy.count(), 1);
1861 args = itemChangedSpy.takeFirst();
1862 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1863 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1864 item->setBackground(column: j, brush: backgroundColor);
1865 QCOMPARE(itemChangedSpy.count(), 0);
1866
1867 const QColor foregroundColor((i == 1) ? Qt::green : Qt::cyan);
1868 item->setForeground(column: j, brush: foregroundColor);
1869 QCOMPARE(item->foreground(j), foregroundColor);
1870 QCOMPARE(itemChangedSpy.count(), 1);
1871 args = itemChangedSpy.takeFirst();
1872 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1873 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1874 item->setForeground(column: j, brush: foregroundColor);
1875 QCOMPARE(itemChangedSpy.count(), 0);
1876
1877 Qt::CheckState checkState((i == 1) ? Qt::PartiallyChecked : Qt::Checked);
1878 item->setCheckState(column: j, state: checkState);
1879 QCOMPARE(item->checkState(j), checkState);
1880 QCOMPARE(itemChangedSpy.count(), 1);
1881 args = itemChangedSpy.takeFirst();
1882 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1883 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1884 item->setCheckState(column: j, state: checkState);
1885 QCOMPARE(itemChangedSpy.count(), 0);
1886
1887 QCOMPARE(item->text(j), text);
1888 QCOMPARE(item->icon(j), icon);
1889 QCOMPARE(item->toolTip(j), toolTip);
1890 QCOMPARE(item->statusTip(j), statusTip);
1891 QCOMPARE(item->whatsThis(j), whatsThis);
1892 QCOMPARE(item->sizeHint(j), sizeHint);
1893 QCOMPARE(item->font(j), font);
1894 QCOMPARE(item->textAlignment(j), int(textAlignment));
1895 QCOMPARE(item->background(j).color(), backgroundColor);
1896 QCOMPARE(item->foreground(j), foregroundColor);
1897 QCOMPARE(item->checkState(j), checkState);
1898
1899 QCOMPARE(qvariant_cast<QString>(item->data(j, Qt::DisplayRole)), text);
1900 QCOMPARE(qvariant_cast<QIcon>(item->data(j, Qt::DecorationRole)), icon);
1901 QCOMPARE(qvariant_cast<QString>(item->data(j, Qt::ToolTipRole)), toolTip);
1902 QCOMPARE(qvariant_cast<QString>(item->data(j, Qt::StatusTipRole)), statusTip);
1903 QCOMPARE(qvariant_cast<QString>(item->data(j, Qt::WhatsThisRole)), whatsThis);
1904 QCOMPARE(qvariant_cast<QSize>(item->data(j, Qt::SizeHintRole)), sizeHint);
1905 QCOMPARE(qvariant_cast<QFont>(item->data(j, Qt::FontRole)), font);
1906 QCOMPARE(qvariant_cast<int>(item->data(j, Qt::TextAlignmentRole)), int(textAlignment));
1907 QCOMPARE(qvariant_cast<QBrush>(item->data(j, Qt::BackgroundRole)), QBrush(backgroundColor));
1908 QCOMPARE(qvariant_cast<QColor>(item->data(j, Qt::ForegroundRole)), foregroundColor);
1909 QCOMPARE(qvariant_cast<int>(item->data(j, Qt::CheckStateRole)), int(checkState));
1910
1911 item->setBackground(column: j, brush: pixmap);
1912 QCOMPARE(item->background(j).texture(), pixmap);
1913 QCOMPARE(qvariant_cast<QBrush>(item->data(j, Qt::BackgroundRole)).texture(), pixmap);
1914 args = itemChangedSpy.takeFirst();
1915 QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item);
1916 QCOMPARE(qvariant_cast<int>(args.at(1)), j);
1917 item->setBackground(column: j, brush: pixmap);
1918 QCOMPARE(itemChangedSpy.count(), 0);
1919
1920 item->setData(column: j, role: Qt::DisplayRole, value: QVariant());
1921 item->setData(column: j, role: Qt::DecorationRole, value: QVariant());
1922 item->setData(column: j, role: Qt::ToolTipRole, value: QVariant());
1923 item->setData(column: j, role: Qt::StatusTipRole, value: QVariant());
1924 item->setData(column: j, role: Qt::WhatsThisRole, value: QVariant());
1925 item->setData(column: j, role: Qt::SizeHintRole, value: QVariant());
1926 item->setData(column: j, role: Qt::FontRole, value: QVariant());
1927 item->setData(column: j, role: Qt::TextAlignmentRole, value: QVariant());
1928 item->setData(column: j, role: Qt::BackgroundRole, value: QVariant());
1929 item->setData(column: j, role: Qt::ForegroundRole, value: QVariant());
1930 item->setData(column: j, role: Qt::CheckStateRole, value: QVariant());
1931 QCOMPARE(itemChangedSpy.count(), 11);
1932 itemChangedSpy.clear();
1933
1934 QCOMPARE(item->data(j, Qt::DisplayRole).toString(), QString());
1935 QCOMPARE(item->data(j, Qt::DecorationRole), QVariant());
1936 QCOMPARE(item->data(j, Qt::ToolTipRole), QVariant());
1937 QCOMPARE(item->data(j, Qt::StatusTipRole), QVariant());
1938 QCOMPARE(item->data(j, Qt::WhatsThisRole), QVariant());
1939 QCOMPARE(item->data(j, Qt::SizeHintRole), QVariant());
1940 QCOMPARE(item->data(j, Qt::FontRole), QVariant());
1941 QCOMPARE(item->data(j, Qt::TextAlignmentRole), QVariant());
1942 QCOMPARE(item->data(j, Qt::BackgroundRole), QVariant());
1943 QCOMPARE(item->data(j, Qt::ForegroundRole), QVariant());
1944 QCOMPARE(item->data(j, Qt::CheckStateRole), QVariant());
1945 }
1946 }
1947 }
1948
1949 // ### add more data types here
1950
1951 item->setData(column: 0, role: Qt::DisplayRole, value: 5);
1952 QCOMPARE(item->data(0, Qt::DisplayRole).type(), QVariant::Int);
1953
1954 item->setData(column: 0, role: Qt::DisplayRole, value: "test");
1955 QCOMPARE(item->data(0, Qt::DisplayRole).type(), QVariant::String);
1956
1957 item->setData(column: 0, role: Qt::DisplayRole, value: 0.4);
1958 QCOMPARE(item->data(0, Qt::DisplayRole).type(), QVariant::Double);
1959
1960 delete item;
1961 }
1962}
1963
1964class QTreeWidgetDataChanged : public QTreeWidget
1965{
1966 Q_OBJECT
1967public:
1968 using QTreeWidget::QTreeWidget;
1969
1970 void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) override
1971 {
1972 QTreeWidget::dataChanged(topLeft, bottomRight, roles);
1973 currentRoles = roles;
1974 }
1975 QVector<int> currentRoles;
1976};
1977
1978void tst_QTreeWidget::itemData()
1979{
1980 QTreeWidgetDataChanged widget;
1981 QTreeWidgetItem item(&widget);
1982 widget.setColumnCount(2);
1983 item.setFlags(item.flags() | Qt::ItemIsEditable);
1984 item.setData(column: 0, role: Qt::DisplayRole, value: QString("0"));
1985 QCOMPARE(widget.currentRoles, QVector<int>({Qt::DisplayRole, Qt::EditRole}));
1986 item.setData(column: 0, role: Qt::CheckStateRole, value: Qt::PartiallyChecked);
1987 QCOMPARE(widget.currentRoles, QVector<int>{Qt::CheckStateRole});
1988 for (int i = 0; i < 4; ++i) {
1989 item.setData(column: 0, role: Qt::UserRole + i, value: QString::number(i + 1));
1990 QCOMPARE(widget.currentRoles, QVector<int>{Qt::UserRole + i});
1991 }
1992 QMap<int, QVariant> flags = widget.model()->itemData(index: widget.model()->index(row: 0, column: 0));
1993 QCOMPARE(flags.count(), 6);
1994 for (int i = 0; i < 4; ++i)
1995 QCOMPARE(flags[Qt::UserRole + i].toString(), QString::number(i + 1));
1996 flags = widget.model()->itemData(index: widget.model()->index(row: 0, column: 1));
1997 QCOMPARE(flags.count(), 0);
1998
1999 item.setBackground(column: 0, brush: QBrush(Qt::red));
2000 item.setForeground(column: 0, brush: QBrush(Qt::green));
2001 item.setSizeHint(column: 0, size: QSize(10, 10));
2002 QCOMPARE(item.data(0, Qt::BackgroundRole), QVariant(QBrush(Qt::red)));
2003 QCOMPARE(item.data(0, Qt::ForegroundRole), QVariant(QBrush(Qt::green)));
2004 QCOMPARE(item.data(0, Qt::SizeHintRole), QVariant(QSize(10, 10)));
2005 // an empty brush should result in a QVariant()
2006 item.setBackground(column: 0, brush: QBrush());
2007 item.setForeground(column: 0, brush: QBrush());
2008 item.setSizeHint(column: 0, size: QSize());
2009 QCOMPARE(item.data(0, Qt::BackgroundRole), QVariant());
2010 QCOMPARE(item.data(0, Qt::ForegroundRole), QVariant());
2011 QCOMPARE(item.data(0, Qt::SizeHintRole), QVariant());
2012}
2013
2014void tst_QTreeWidget::enableDisable()
2015{
2016 const QScopedPointer<QTreeWidgetItem> itm(new QTreeWidgetItem);
2017 for (int i = 0; i < 10; ++i)
2018 new QTreeWidgetItem(itm.data());
2019
2020 // make sure all items are enabled
2021 QVERIFY(itm->flags() & Qt::ItemIsEnabled);
2022 for (int j = 0; j < itm->childCount(); ++j)
2023 QVERIFY(itm->child(j)->flags() & Qt::ItemIsEnabled);
2024
2025 // disable root and make sure they are all disabled
2026 itm->setFlags(itm->flags() & ~Qt::ItemIsEnabled);
2027 QVERIFY(!(itm->flags() & Qt::ItemIsEnabled));
2028 for (int k = 0; k < itm->childCount(); ++k)
2029 QVERIFY(!(itm->child(k)->flags() & Qt::ItemIsEnabled));
2030
2031 // disable a child and make sure they are all still disabled
2032 itm->child(index: 5)->setFlags(itm->child(index: 5)->flags() & ~Qt::ItemIsEnabled);
2033 QVERIFY(!(itm->flags() & Qt::ItemIsEnabled));
2034 for (int l = 0; l < itm->childCount(); ++l)
2035 QVERIFY(!(itm->child(l)->flags() & Qt::ItemIsEnabled));
2036
2037 // enable root and make sure all items except one are enabled
2038 itm->setFlags(itm->flags() | Qt::ItemIsEnabled);
2039 QVERIFY(itm->flags() & Qt::ItemIsEnabled);
2040 for (int m = 0; m < itm->childCount(); ++m)
2041 if (m == 5)
2042 QVERIFY(!(itm->child(m)->flags() & Qt::ItemIsEnabled));
2043 else
2044 QVERIFY(itm->child(m)->flags() & Qt::ItemIsEnabled);
2045}
2046
2047void tst_QTreeWidget::match()
2048{
2049 QTreeWidget tree;
2050 QModelIndexList list = tree.model()->match(start: QModelIndex(), role: Qt::DisplayRole, value: QString());
2051
2052 QVERIFY(list.isEmpty());
2053}
2054
2055void tst_QTreeWidget::columnCount()
2056{
2057 int columnCountBefore = testWidget->columnCount();
2058 testWidget->setColumnCount(-1);
2059 QCOMPARE(testWidget->columnCount(), columnCountBefore);
2060}
2061
2062void tst_QTreeWidget::setHeaderLabels()
2063{
2064 QStringList list = QString("a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z").split(sep: QLatin1Char(','));
2065 testWidget->setHeaderLabels(list);
2066 QCOMPARE(testWidget->header()->count(), list.count());
2067}
2068
2069void tst_QTreeWidget::setHeaderItem()
2070{
2071 testWidget->setHeaderItem(nullptr);
2072 QTreeWidgetItem *headerItem = new QTreeWidgetItem();
2073
2074 testWidget->setColumnCount(0);
2075 QCOMPARE(testWidget->header()->count(), 0);
2076 QCOMPARE(testWidget->columnCount(), 0);
2077
2078 headerItem->setText(column: 0, atext: "0");
2079 headerItem->setText(column: 1, atext: "1");
2080 testWidget->setHeaderItem(headerItem);
2081 QCOMPARE(testWidget->headerItem(), headerItem);
2082 QCOMPARE(headerItem->treeWidget(), static_cast<QTreeWidget *>(testWidget));
2083
2084 QCOMPARE(testWidget->header()->count(), 2);
2085 QCOMPARE(testWidget->columnCount(), 2);
2086
2087 headerItem->setText(column: 2, atext: "2");
2088 QCOMPARE(testWidget->header()->count(), 3);
2089 QCOMPARE(testWidget->columnCount(), 3);
2090
2091 delete headerItem;
2092 testWidget->setColumnCount(3);
2093 testWidget->setColumnCount(5);
2094 QCOMPARE(testWidget->model()->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(), QString("1"));
2095 QCOMPARE(testWidget->model()->headerData(1, Qt::Horizontal, Qt::DisplayRole).toString(), QString("2"));
2096 QCOMPARE(testWidget->model()->headerData(2, Qt::Horizontal, Qt::DisplayRole).toString(), QString("3"));
2097 QCOMPARE(testWidget->model()->headerData(3, Qt::Horizontal, Qt::DisplayRole).toString(), QString("4"));
2098 QCOMPARE(testWidget->model()->headerData(4, Qt::Horizontal, Qt::DisplayRole).toString(), QString("5"));
2099
2100 headerItem = new QTreeWidgetItem();
2101 testWidget->setHeaderItem(headerItem);
2102 testWidget->model()->insertColumns(column: 0, count: 5, parent: QModelIndex());
2103 QCOMPARE(testWidget->model()->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString(), QString("1"));
2104 QCOMPARE(testWidget->model()->headerData(1, Qt::Horizontal, Qt::DisplayRole).toString(), QString("2"));
2105 QCOMPARE(testWidget->model()->headerData(2, Qt::Horizontal, Qt::DisplayRole).toString(), QString("3"));
2106 QCOMPARE(testWidget->model()->headerData(3, Qt::Horizontal, Qt::DisplayRole).toString(), QString("4"));
2107 QCOMPARE(testWidget->model()->headerData(4, Qt::Horizontal, Qt::DisplayRole).toString(), QString("5"));
2108}
2109
2110void tst_QTreeWidget::itemWidget_data()
2111{
2112 editItem_data();
2113}
2114
2115void tst_QTreeWidget::itemWidget()
2116{
2117 QFETCH(TreeItemList, topLevelItems);
2118
2119 QTreeWidget tree;
2120 populate(widget: &tree, topLevelItems, headerItem: new TreeItem({"1", "2"}));
2121 tree.show();
2122
2123 for (int x = 0; x < 2; ++x) {
2124 QTreeWidgetItemIterator it(&tree);
2125 while (QTreeWidgetItem *item = (*it++)) {
2126 for (int col = 0; col < item->columnCount(); ++col) {
2127 if (x == 0) {
2128 QCOMPARE(tree.itemWidget(item, col), nullptr);
2129 QWidget *editor = new QLineEdit();
2130 tree.setItemWidget(item, column: col, widget: editor);
2131 QCOMPARE(tree.itemWidget(item, col), editor);
2132 tree.removeItemWidget(item, column: col);
2133 QCOMPARE(tree.itemWidget(item, col), nullptr);
2134 } else {
2135 // ### should you really be able to open a persistent
2136 // editor for an item that isn't editable??
2137 tree.openPersistentEditor(item, column: col);
2138 QWidget *editor = tree.findChild<QLineEdit*>();
2139 QVERIFY(editor != nullptr);
2140 tree.closePersistentEditor(item, column: col);
2141 }
2142 }
2143 }
2144 }
2145}
2146
2147void tst_QTreeWidget::insertItemsWithSorting_data()
2148{
2149 QTest::addColumn<Qt::SortOrder>(name: "sortOrder");
2150 QTest::addColumn<QStringList>(name: "initialItems");
2151 QTest::addColumn<QStringList>(name: "insertItems");
2152 QTest::addColumn<QStringList>(name: "expectedItems");
2153 QTest::addColumn<IntList>(name: "expectedRows");
2154
2155 QTest::newRow(dataTag: "() + (a) = (a)")
2156 << Qt::AscendingOrder
2157 << QStringList()
2158 << (QStringList() << "a")
2159 << (QStringList() << "a")
2160 << IntList();
2161 QTest::newRow(dataTag: "() + (c, b, a) = (a, b, c)")
2162 << Qt::AscendingOrder
2163 << QStringList()
2164 << (QStringList() << "c" << "b" << "a")
2165 << (QStringList() << "a" << "b" << "c")
2166 << IntList();
2167 QTest::newRow(dataTag: "() + (a, b, c) = (c, b, a)")
2168 << Qt::DescendingOrder
2169 << QStringList()
2170 << (QStringList() << "a" << "b" << "c")
2171 << (QStringList() << "c" << "b" << "a")
2172 << IntList();
2173 QTest::newRow(dataTag: "(a) + (b) = (a, b)")
2174 << Qt::AscendingOrder
2175 << QStringList("a")
2176 << (QStringList() << "b")
2177 << (QStringList() << "a" << "b")
2178 << (IntList() << 0);
2179 QTest::newRow(dataTag: "(a) + (b) = (b, a)")
2180 << Qt::DescendingOrder
2181 << QStringList("a")
2182 << (QStringList() << "b")
2183 << (QStringList() << "b" << "a")
2184 << (IntList() << 1);
2185 QTest::newRow(dataTag: "(a, c, b) + (d) = (a, b, c, d)")
2186 << Qt::AscendingOrder
2187 << (QStringList() << "a" << "c" << "b")
2188 << (QStringList() << "d")
2189 << (QStringList() << "a" << "b" << "c" << "d")
2190 << (IntList() << 0 << 1 << 2);
2191 QTest::newRow(dataTag: "(b, c, a) + (d) = (d, c, b, a)")
2192 << Qt::DescendingOrder
2193 << (QStringList() << "b" << "c" << "a")
2194 << (QStringList() << "d")
2195 << (QStringList() << "d" << "c" << "b" << "a")
2196 << (IntList() << 1 << 2 << 3);
2197 {
2198 IntList ascendingRows;
2199 IntList reverseRows;
2200 QStringList ascendingItems;
2201 QStringList reverseItems;
2202 for (char i = 'a'; i <= 'z'; ++i) {
2203 ascendingItems << QString(1, QLatin1Char(i));
2204 reverseItems << QString(1, QLatin1Char('z' - i + 'a'));
2205 ascendingRows << i - 'a';
2206 reverseRows << 'z' - i + 'a';
2207 }
2208 QTest::newRow(dataTag: "() + (sorted items) = (sorted items)")
2209 << Qt::AscendingOrder
2210 << QStringList()
2211 << ascendingItems
2212 << ascendingItems
2213 << IntList();
2214 QTest::newRow(dataTag: "(sorted items) + () = (sorted items)")
2215 << Qt::AscendingOrder
2216 << ascendingItems
2217 << QStringList()
2218 << ascendingItems
2219 << ascendingRows;
2220 QTest::newRow(dataTag: "() + (ascending items) = (reverse items)")
2221 << Qt::DescendingOrder
2222 << QStringList()
2223 << ascendingItems
2224 << reverseItems
2225 << IntList();
2226 QTest::newRow(dataTag: "(reverse items) + () = (ascending items)")
2227 << Qt::AscendingOrder
2228 << reverseItems
2229 << QStringList()
2230 << ascendingItems
2231 << ascendingRows;
2232 QTest::newRow(dataTag: "(reverse items) + () = (reverse items)")
2233 << Qt::DescendingOrder
2234 << reverseItems
2235 << QStringList()
2236 << reverseItems
2237 << ascendingRows;
2238 }
2239}
2240
2241void tst_QTreeWidget::insertItemsWithSorting()
2242{
2243 QFETCH(Qt::SortOrder, sortOrder);
2244 QFETCH(const QStringList, initialItems);
2245 QFETCH(const QStringList, insertItems);
2246 QFETCH(const QStringList, expectedItems);
2247 QFETCH(IntList, expectedRows);
2248
2249 for (int method = 0; method < 5; ++method) {
2250 QTreeWidget w;
2251 w.setSortingEnabled(true);
2252 w.sortItems(column: 0, order: sortOrder);
2253 for (const QString &initialItem : initialItems)
2254 w.addTopLevelItem(item: new QTreeWidgetItem({initialItem}));
2255
2256 QAbstractItemModel *model = w.model();
2257 PersistentModelIndexVec persistent;
2258 for (int j = 0; j < model->rowCount(parent: QModelIndex()); ++j)
2259 persistent << model->index(row: j, column: 0, parent: QModelIndex());
2260
2261 switch (method) {
2262 case 0:
2263 // insert using item constructor
2264 for (const QString &txt : insertItems)
2265 new QTreeWidgetItem(&w, { txt });
2266 break;
2267 case 1:
2268 {
2269 // insert using insertTopLevelItems()
2270 QList<QTreeWidgetItem*> lst;
2271 for (const QString &txt : insertItems)
2272 lst << new QTreeWidgetItem({ txt });
2273 w.insertTopLevelItems(index: 0, items: lst);
2274 break;
2275 }
2276 case 2:
2277 // insert using insertTopLevelItem()
2278 for (const QString &txt : insertItems)
2279 w.insertTopLevelItem(index: 0, item: new QTreeWidgetItem({ txt }));
2280 break;
2281 case 3:
2282 {
2283 // insert using addTopLevelItems()
2284 QList<QTreeWidgetItem*> lst;
2285 for (const QString &txt : insertItems)
2286 lst << new QTreeWidgetItem({ txt });
2287 w.addTopLevelItems(items: lst);
2288 break;
2289 }
2290 case 4:
2291 // insert using addTopLevelItem()
2292 for (const QString &txt : insertItems)
2293 w.addTopLevelItem(item: new QTreeWidgetItem({ txt }));
2294 break;
2295 }
2296 QCOMPARE(w.topLevelItemCount(), expectedItems.count());
2297 for (int i = 0; i < w.topLevelItemCount(); ++i)
2298 QCOMPARE(w.topLevelItem(i)->text(0), expectedItems.at(i));
2299
2300 for (int k = 0; k < persistent.count(); ++k)
2301 QCOMPARE(persistent.at(k).row(), expectedRows.at(k));
2302 }
2303}
2304
2305void tst_QTreeWidget::insertExpandedItemsWithSorting_data()
2306{
2307 QTest::addColumn<QStringList>(name: "parentTexts");
2308 QTest::addColumn<QStringList>(name: "childTexts");
2309 QTest::addColumn<QStringList>(name: "parentResult");
2310 QTest::addColumn<QStringList>(name: "childResult");
2311 QTest::newRow(dataTag: "test 1")
2312 << (QStringList() << "c" << "d" << "a" << "b")
2313 << (QStringList() << "e" << "h" << "g" << "f")
2314 << (QStringList() << "d" << "c" << "b" << "a")
2315 << (QStringList() << "h" << "g" << "f" << "e");
2316}
2317
2318// From Task 134978
2319void tst_QTreeWidget::insertExpandedItemsWithSorting()
2320{
2321 QFETCH(const QStringList, parentTexts);
2322 QFETCH(const QStringList, childTexts);
2323 QFETCH(const QStringList, parentResult);
2324 QFETCH(const QStringList, childResult);
2325
2326 // create a tree with autosorting enabled
2327 PublicTreeWidget tree;
2328 tree.setSortingEnabled(true);
2329
2330 // insert expanded items in unsorted order
2331 QVector<QTreeWidgetItem *> items;
2332 for (const QString &text : parentTexts) {
2333 QTreeWidgetItem *parent = new QTreeWidgetItem(&tree, {text});
2334 parent->setExpanded(true);
2335 QVERIFY(parent->isExpanded());
2336 items << parent;
2337 for (const QString &text : childTexts) {
2338 QTreeWidgetItem *child = new QTreeWidgetItem(parent, {text});
2339 items << child;
2340 }
2341 QCOMPARE(parent->childCount(), childTexts.count());
2342 QVERIFY(parent->isExpanded());
2343 }
2344 QCOMPARE(tree.model()->rowCount(), parentTexts.count());
2345
2346 // verify that the items are still expanded
2347 for (const QTreeWidgetItem *item : qAsConst(t&: items)) {
2348 if (item->childCount() > 0)
2349 QVERIFY(item->isExpanded());
2350 QModelIndex idx = tree.indexFromItem(item);
2351 QVERIFY(idx.isValid());
2352 //QRect rect = tree.visualRect(idx);
2353 //QVERIFY(rect.isValid());
2354 // ### it is not guarantied that the index is in the viewport
2355 }
2356
2357 // verify that the tree is sorted
2358 QAbstractItemModel *model = tree.model();
2359 PersistentModelIndexVec parents;
2360 for (int i = 0; i < model->rowCount(parent: QModelIndex()); ++i)
2361 parents.push_back(t: model->index(row: i, column: 0, parent: QModelIndex()));
2362 PersistentModelIndexVec children;
2363 for (int i = 0; i < model->rowCount(parent: parents.constFirst()); ++i)
2364 children.push_back(t: model->index(row: i, column: 0, parent: parents.constFirst()));
2365 for (int i = 0; i < parentResult.count(); ++i) {
2366 QTreeWidgetItem *item = tree.topLevelItem(index: i);
2367 QCOMPARE(item->text(0), parentResult.at(i));
2368 for (int j = 0; j < childResult.count(); ++j)
2369 QCOMPARE(item->child(j)->text(0), childResult.at(j));
2370 }
2371}
2372
2373void tst_QTreeWidget::changeDataWithSorting_data()
2374{
2375 QTest::addColumn<Qt::SortOrder>(name: "sortOrder");
2376 QTest::addColumn<QStringList>(name: "initialItems");
2377 QTest::addColumn<int>(name: "itemIndex");
2378 QTest::addColumn<QString>(name: "newValue");
2379 QTest::addColumn<QStringList>(name: "expectedItems");
2380 QTest::addColumn<IntList>(name: "expectedRows");
2381 QTest::addColumn<bool>(name: "reorderingExpected");
2382
2383 QTest::newRow(dataTag: "change a to b in (a)")
2384 << Qt::AscendingOrder
2385 << (QStringList() << "a")
2386 << 0 << "b"
2387 << (QStringList() << "b")
2388 << (IntList() << 0)
2389 << false;
2390 QTest::newRow(dataTag: "change a to b in (a, c)")
2391 << Qt::AscendingOrder
2392 << (QStringList() << "a" << "c")
2393 << 0 << "b"
2394 << (QStringList() << "b" << "c")
2395 << (IntList() << 0 << 1)
2396 << false;
2397 QTest::newRow(dataTag: "change a to c in (a, b)")
2398 << Qt::AscendingOrder
2399 << (QStringList() << "a" << "b")
2400 << 0 << "c"
2401 << (QStringList() << "b" << "c")
2402 << (IntList() << 1 << 0)
2403 << true;
2404 QTest::newRow(dataTag: "change c to a in (c, b)")
2405 << Qt::DescendingOrder
2406 << (QStringList() << "c" << "b")
2407 << 0 << "a"
2408 << (QStringList() << "b" << "a")
2409 << (IntList() << 1 << 0)
2410 << true;
2411 QTest::newRow(dataTag: "change e to i in (a, c, e, g)")
2412 << Qt::AscendingOrder
2413 << (QStringList() << "a" << "c" << "e" << "g")
2414 << 2 << "i"
2415 << (QStringList() << "a" << "c" << "g" << "i")
2416 << (IntList() << 0 << 1 << 3 << 2)
2417 << true;
2418 QTest::newRow(dataTag: "change e to a in (c, e, g, i)")
2419 << Qt::AscendingOrder
2420 << (QStringList() << "c" << "e" << "g" << "i")
2421 << 1 << "a"
2422 << (QStringList() << "a" << "c" << "g" << "i")
2423 << (IntList() << 1 << 0 << 2 << 3)
2424 << true;
2425 QTest::newRow(dataTag: "change e to f in (c, e, g, i)")
2426 << Qt::AscendingOrder
2427 << (QStringList() << "c" << "e" << "g" << "i")
2428 << 1 << "f"
2429 << (QStringList() << "c" << "f" << "g" << "i")
2430 << (IntList() << 0 << 1 << 2 << 3)
2431 << false;
2432}
2433
2434void tst_QTreeWidget::changeDataWithSorting()
2435{
2436 QFETCH(Qt::SortOrder, sortOrder);
2437 QFETCH(const QStringList, initialItems);
2438 QFETCH(int, itemIndex);
2439 QFETCH(const QString, newValue);
2440 QFETCH(const QStringList, expectedItems);
2441 QFETCH(const IntList, expectedRows);
2442 QFETCH(bool, reorderingExpected);
2443
2444 QTreeWidget w;
2445 w.setSortingEnabled(true);
2446 w.sortItems(column: 0, order: sortOrder);
2447 for (const QString &str : initialItems)
2448 w.addTopLevelItem(item: new QTreeWidgetItem({ str }));
2449
2450 QAbstractItemModel *model = w.model();
2451 PersistentModelIndexVec persistent;
2452 for (int j = 0; j < model->rowCount(parent: QModelIndex()); ++j)
2453 persistent << model->index(row: j, column: 0, parent: QModelIndex());
2454
2455 QSignalSpy dataChangedSpy(model, &QAbstractItemModel::dataChanged);
2456 QSignalSpy layoutChangedSpy(model, &QAbstractItemModel::layoutChanged);
2457
2458 QTreeWidgetItem *item = w.topLevelItem(index: itemIndex);
2459 item->setText(column: 0, atext: newValue);
2460 for (int i = 0; i < expectedItems.count(); ++i) {
2461 QCOMPARE(w.topLevelItem(i)->text(0), expectedItems.at(i));
2462 for (const QPersistentModelIndex &p : qAsConst(t&: persistent)) {
2463 if (p.row() == i) // the same toplevel row
2464 QCOMPARE(p.internalPointer(), static_cast<void *>(w.topLevelItem(i)));
2465 }
2466 }
2467
2468 for (int k = 0; k < persistent.count(); ++k)
2469 QCOMPARE(persistent.at(k).row(), expectedRows.at(k));
2470
2471 QCOMPARE(dataChangedSpy.count(), 1);
2472 QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0);
2473}
2474
2475void tst_QTreeWidget::changeDataWithStableSorting_data()
2476{
2477 QTest::addColumn<Qt::SortOrder>(name: "sortOrder");
2478 QTest::addColumn<QStringList>(name: "initialItems");
2479 QTest::addColumn<int>(name: "itemIndex");
2480 QTest::addColumn<QString>(name: "newValue");
2481 QTest::addColumn<QStringList>(name: "expectedItems");
2482 QTest::addColumn<IntList>(name: "expectedRows");
2483 QTest::addColumn<bool>(name: "reorderingExpected");
2484 QTest::addColumn<bool>(name: "forceChange");
2485
2486 QTest::newRow(dataTag: "change a to c in (a, c, c, c, e)")
2487 << Qt::AscendingOrder
2488 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2489 << 0 << "c"
2490 << (QStringList() << "c" << "c" << "c" << "c" << "e")
2491 << (IntList() << 0 << 1 << 2 << 3 << 4)
2492 << false
2493 << false;
2494 QTest::newRow(dataTag: "change e to c in (a, c, c, c, e)")
2495 << Qt::AscendingOrder
2496 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2497 << 4 << "c"
2498 << (QStringList() << "a" << "c" << "c" << "c" << "c")
2499 << (IntList() << 0 << 1 << 2 << 3 << 4)
2500 << false
2501 << false;
2502 QTest::newRow(dataTag: "change 1st c to c in (a, c, c, c, e)")
2503 << Qt::AscendingOrder
2504 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2505 << 1 << "c"
2506 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2507 << (IntList() << 0 << 1 << 2 << 3 << 4)
2508 << false
2509 << true;
2510 QTest::newRow(dataTag: "change 2nd c to c in (a, c, c, c, e)")
2511 << Qt::AscendingOrder
2512 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2513 << 2 << "c"
2514 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2515 << (IntList() << 0 << 1 << 2 << 3 << 4)
2516 << false
2517 << true;
2518 QTest::newRow(dataTag: "change 3rd c to c in (a, c, c, c, e)")
2519 << Qt::AscendingOrder
2520 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2521 << 3 << "c"
2522 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2523 << (IntList() << 0 << 1 << 2 << 3 << 4)
2524 << false
2525 << true;
2526 QTest::newRow(dataTag: "change 1st c to c in (e, c, c, c, a)")
2527 << Qt::DescendingOrder
2528 << (QStringList() << "e" << "c" << "c" << "c" << "a")
2529 << 1 << "c"
2530 << (QStringList() << "e" << "c" << "c" << "c" << "a")
2531 << (IntList() << 0 << 1 << 2 << 3 << 4)
2532 << false
2533 << true;
2534 QTest::newRow(dataTag: "change 2nd c to c in (e, c, c, c, a)")
2535 << Qt::DescendingOrder
2536 << (QStringList() << "e" << "c" << "c" << "c" << "a")
2537 << 2 << "c"
2538 << (QStringList() << "e" << "c" << "c" << "c" << "a")
2539 << (IntList() << 0 << 1 << 2 << 3 << 4)
2540 << false
2541 << true;
2542 QTest::newRow(dataTag: "change 3rd c to c in (e, c, c, c, a)")
2543 << Qt::DescendingOrder
2544 << (QStringList() << "e" << "c" << "c" << "c" << "a")
2545 << 3 << "c"
2546 << (QStringList() << "e" << "c" << "c" << "c" << "a")
2547 << (IntList() << 0 << 1 << 2 << 3 << 4)
2548 << false
2549 << true;
2550 QTest::newRow(dataTag: "change 1st c to b in (a, c, c, c, e)")
2551 << Qt::AscendingOrder
2552 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2553 << 1 << "b"
2554 << (QStringList() << "a" << "b" << "c" << "c" << "e")
2555 << (IntList() << 0 << 1 << 2 << 3 << 4)
2556 << false
2557 << false;
2558 QTest::newRow(dataTag: "change 2nd c to b in (a, c, c, c, e)")
2559 << Qt::AscendingOrder
2560 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2561 << 2 << "b"
2562 << (QStringList() << "a" << "b" << "c" << "c" << "e")
2563 << (IntList() << 0 << 2 << 1 << 3 << 4)
2564 << true
2565 << false;
2566 QTest::newRow(dataTag: "change 3rd c to b in (a, c, c, c, e)")
2567 << Qt::AscendingOrder
2568 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2569 << 3 << "b"
2570 << (QStringList() << "a" << "b" << "c" << "c" << "e")
2571 << (IntList() << 0 << 2 << 3 << 1 << 4)
2572 << true
2573 << false;
2574 QTest::newRow(dataTag: "change 1st c to d in (a, c, c, c, e)")
2575 << Qt::AscendingOrder
2576 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2577 << 1 << "d"
2578 << (QStringList() << "a" << "c" << "c" << "d" << "e")
2579 << (IntList() << 0 << 3 << 1 << 2 << 4)
2580 << true
2581 << false;
2582 QTest::newRow(dataTag: "change 2nd c to d in (a, c, c, c, e)")
2583 << Qt::AscendingOrder
2584 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2585 << 2 << "d"
2586 << (QStringList() << "a" << "c" << "c" << "d" << "e")
2587 << (IntList() << 0 << 1 << 3 << 2 << 4)
2588 << true
2589 << false;
2590 QTest::newRow(dataTag: "change 3rd c to d in (a, c, c, c, e)")
2591 << Qt::AscendingOrder
2592 << (QStringList() << "a" << "c" << "c" << "c" << "e")
2593 << 3 << "d"
2594 << (QStringList() << "a" << "c" << "c" << "d" << "e")
2595 << (IntList() << 0 << 1 << 2 << 3 << 4)
2596 << false
2597 << false;
2598}
2599
2600void tst_QTreeWidget::changeDataWithStableSorting()
2601{
2602 QFETCH(Qt::SortOrder, sortOrder);
2603 QFETCH(const QStringList, initialItems);
2604 QFETCH(int, itemIndex);
2605 QFETCH(const QString, newValue);
2606 QFETCH(const QStringList, expectedItems);
2607 QFETCH(const IntList, expectedRows);
2608 QFETCH(bool, reorderingExpected);
2609 QFETCH(bool, forceChange);
2610
2611 QTreeWidget w;
2612 w.setSortingEnabled(true);
2613 w.sortItems(column: 0, order: sortOrder);
2614 for (const QString &str : initialItems)
2615 w.addTopLevelItem(item: new PublicTreeItem({ str }));
2616
2617 QAbstractItemModel *model = w.model();
2618 PersistentModelIndexVec persistent;
2619 for (int j = 0; j < model->rowCount(parent: QModelIndex()); ++j)
2620 persistent << model->index(row: j, column: 0, parent: QModelIndex());
2621
2622 QSignalSpy dataChangedSpy(model, &QAbstractItemModel::dataChanged);
2623 QSignalSpy layoutChangedSpy(model, &QAbstractItemModel::layoutChanged);
2624
2625 auto *item = static_cast<PublicTreeItem *>(w.topLevelItem(index: itemIndex));
2626 item->setText(column: 0, atext: newValue);
2627 if (forceChange)
2628 item->emitDataChanged();
2629 for (int i = 0; i < expectedItems.count(); ++i) {
2630 QCOMPARE(w.topLevelItem(i)->text(0), expectedItems.at(i));
2631 for (const QPersistentModelIndex &p : qAsConst(t&: persistent)) {
2632 if (p.row() == i) // the same toplevel row
2633 QCOMPARE(p.internalPointer(), static_cast<void *>(w.topLevelItem(i)));
2634 }
2635 }
2636
2637 for (int k = 0; k < persistent.count(); ++k)
2638 QCOMPARE(persistent.at(k).row(), expectedRows.at(k));
2639
2640 QCOMPARE(dataChangedSpy.count(), 1);
2641 QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0);
2642}
2643
2644void tst_QTreeWidget::sizeHint_data()
2645{
2646 QTest::addColumn<Qt::ScrollBarPolicy>(name: "scrollBarPolicy");
2647 QTest::addColumn<QSize>(name: "viewSize");
2648 QTest::newRow(dataTag: "ScrollBarAlwaysOn") << Qt::ScrollBarAlwaysOn << QSize();
2649 QTest::newRow(dataTag: "ScrollBarAlwaysOff") << Qt::ScrollBarAlwaysOff << QSize();
2650 // make sure the scrollbars are shown by resizing the view to 40x40
2651 QTest::newRow(dataTag: "ScrollBarAsNeeded (40x40)") << Qt::ScrollBarAsNeeded << QSize(40, 40);
2652 QTest::newRow(dataTag: "ScrollBarAsNeeded (1000x1000)") << Qt::ScrollBarAsNeeded << QSize(1000, 1000);
2653}
2654
2655void tst_QTreeWidget::sizeHint()
2656{
2657 QFETCH(Qt::ScrollBarPolicy, scrollBarPolicy);
2658 QFETCH(QSize, viewSize);
2659
2660 QTreeWidget view;
2661 view.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
2662 view.setVerticalScrollBarPolicy(scrollBarPolicy);
2663 view.setHorizontalScrollBarPolicy(scrollBarPolicy);
2664 view.setColumnCount(2);
2665 for (int i = 0 ; i < view.columnCount(); ++i)
2666 view.addTopLevelItem(item: new QTreeWidgetItem(QStringList{"foo","bar"}));
2667
2668 view.show();
2669 QVERIFY(QTest::qWaitForWindowExposed(&view));
2670
2671 if (viewSize.isValid()) {
2672 view.resize(viewSize);
2673 view.setColumnWidth(column: 0, width: 100);
2674 QTRY_COMPARE(view.size(), viewSize);
2675 }
2676
2677 auto sizeHint = view.sizeHint();
2678 view.hide();
2679 QCOMPARE(view.sizeHint(), sizeHint);
2680
2681 view.header()->hide();
2682 view.show();
2683 sizeHint = view.sizeHint();
2684 view.hide();
2685 QCOMPARE(view.sizeHint(), sizeHint);
2686}
2687
2688void tst_QTreeWidget::itemOperatorLessThan()
2689{
2690 QTreeWidget tw;
2691 tw.setColumnCount(2);
2692 {
2693 QTreeWidgetItem item1(&tw);
2694 QTreeWidgetItem item2(&tw);
2695 QCOMPARE(item1 < item2, false);
2696 item1.setText(column: 1, atext: "a");
2697 item2.setText(column: 1, atext: "b");
2698 QCOMPARE(item1 < item2, false);
2699 item1.setText(column: 0, atext: "a");
2700 item2.setText(column: 0, atext: "b");
2701 QCOMPARE(item1 < item2, true);
2702 tw.sortItems(column: 1, order: Qt::AscendingOrder);
2703 item1.setText(column: 0, atext: "b");
2704 item2.setText(column: 0, atext: "a");
2705 QCOMPARE(item1 < item2, true);
2706 tw.sortItems(column: 0, order: Qt::AscendingOrder);
2707 item1.setData(column: 0, role: Qt::DisplayRole, value: 11);
2708 item2.setData(column: 0, role: Qt::DisplayRole, value: 2);
2709 QCOMPARE(item1 < item2, false);
2710 }
2711}
2712
2713void tst_QTreeWidget::sortedIndexOfChild_data()
2714{
2715 QTest::addColumn<Qt::SortOrder>(name: "sortOrder");
2716 QTest::addColumn<QStringList>(name: "itemTexts");
2717 QTest::addColumn<IntList>(name: "expectedIndexes");
2718
2719 QTest::newRow(dataTag: "three ascending")
2720 << Qt::AscendingOrder
2721 << (QStringList{"A", "B", "C"})
2722 << (IntList{0, 1, 2});
2723
2724
2725 QTest::newRow(dataTag: "three descending")
2726 << Qt::DescendingOrder
2727 << (QStringList{"A", "B", "C"})
2728 << (IntList{2, 1, 0});
2729}
2730
2731void tst_QTreeWidget::sortedIndexOfChild()
2732{
2733 QFETCH(Qt::SortOrder, sortOrder);
2734 QFETCH(const QStringList, itemTexts);
2735 QFETCH(const IntList, expectedIndexes);
2736
2737 QTreeWidget tw;
2738 QVector<QTreeWidgetItem *> itms;
2739 auto *top = new QTreeWidgetItem(&tw, {"top"});
2740
2741 for (const QString &str : itemTexts)
2742 itms << new QTreeWidgetItem(top, {str});
2743
2744 tw.sortItems(column: 0, order: sortOrder);
2745 tw.expandAll();
2746
2747 QCOMPARE(itms.count(), expectedIndexes.count());
2748 for (int j = 0; j < expectedIndexes.count(); ++j)
2749 QCOMPARE(top->indexOfChild(itms.at(j)), expectedIndexes.at(j));
2750}
2751
2752void tst_QTreeWidget::expandAndCallapse()
2753{
2754 QTreeWidget tw;
2755 QTreeWidgetItem *top = new QTreeWidgetItem(&tw, QStringList() << "top");
2756 QTreeWidgetItem *p = nullptr;
2757 for (int i = 0; i < 10; ++i) {
2758 p = new QTreeWidgetItem(top, QStringList(QString::number(i)));
2759 for (int j = 0; j < 10; ++j)
2760 new QTreeWidgetItem(p, QStringList(QString::number(j)));
2761 }
2762 QSignalSpy spy0(&tw, &QTreeWidget::itemExpanded);
2763 QSignalSpy spy1(&tw, &QTreeWidget::itemCollapsed);
2764
2765
2766 tw.expandItem(item: p);
2767 tw.collapseItem(item: p);
2768 tw.expandItem(item: p);
2769 tw.expandItem(item: top);
2770 tw.collapseItem(item: top);
2771 tw.collapseItem(item: top);
2772
2773 QCOMPARE(spy0.count(), 3);
2774 QCOMPARE(spy1.count(), 2);
2775}
2776
2777void tst_QTreeWidget::setDisabled()
2778{
2779 QTreeWidget w;
2780 QTreeWidgetItem *i1 = new QTreeWidgetItem();
2781 QTreeWidgetItem *i2 = new QTreeWidgetItem(i1);
2782 QTreeWidgetItem *i3 = new QTreeWidgetItem(i1);
2783
2784 QTreeWidgetItem *top = new QTreeWidgetItem(&w);
2785 top->setDisabled(true);
2786 top->addChild(child: i1);
2787 QCOMPARE(i1->isDisabled(), true);
2788 QCOMPARE(i2->isDisabled(), true);
2789 QCOMPARE(i3->isDisabled(), true);
2790
2791 i1 = top->takeChild(index: 0);
2792 QCOMPARE(i1->isDisabled(), false);
2793 QCOMPARE(i2->isDisabled(), false);
2794 QCOMPARE(i3->isDisabled(), false);
2795
2796 top->addChild(child: i1);
2797 QCOMPARE(i1->isDisabled(), true);
2798 QCOMPARE(i2->isDisabled(), true);
2799 QCOMPARE(i3->isDisabled(), true);
2800
2801 top->setDisabled(false);
2802 QCOMPARE(i1->isDisabled(), false);
2803 QCOMPARE(i2->isDisabled(), false);
2804 QCOMPARE(i3->isDisabled(), false);
2805
2806
2807
2808 QList<QTreeWidgetItem*> children;
2809 children.append(t: new QTreeWidgetItem());
2810 children.append(t: new QTreeWidgetItem());
2811 children.append(t: new QTreeWidgetItem());
2812 {
2813 const QScopedPointer<QTreeWidgetItem> taken(top->takeChild(index: 0));
2814 QCOMPARE(taken.data(), i1);
2815 }
2816
2817 top->addChildren(children);
2818 QCOMPARE(top->child(0)->isDisabled(), false);
2819 QCOMPARE(top->child(1)->isDisabled(), false);
2820 QCOMPARE(top->child(1)->isDisabled(), false);
2821
2822 top->setDisabled(true);
2823 QCOMPARE(top->child(0)->isDisabled(), true);
2824 QCOMPARE(top->child(1)->isDisabled(), true);
2825 QCOMPARE(top->child(1)->isDisabled(), true);
2826
2827 struct Deleter {
2828 QList<QTreeWidgetItem *> items;
2829 explicit Deleter(QList<QTreeWidgetItem *> items) : items(std::move(items)) {}
2830 ~Deleter() { qDeleteAll(c: items); }
2831 };
2832
2833 const Deleter takenChildren(top->takeChildren());
2834 QCOMPARE(takenChildren.items[0]->isDisabled(), false);
2835 QCOMPARE(takenChildren.items[1]->isDisabled(), false);
2836 QCOMPARE(takenChildren.items[1]->isDisabled(), false);
2837}
2838
2839void tst_QTreeWidget::setSpanned()
2840{
2841 QTreeWidget w;
2842 QTreeWidgetItem *i1 = new QTreeWidgetItem();
2843 QScopedPointer<QTreeWidgetItem> i2(new QTreeWidgetItem());
2844
2845 QTreeWidgetItem *top = new QTreeWidgetItem(&w);
2846 top->addChild(child: i1);
2847
2848 top->setFirstColumnSpanned(true);
2849 QCOMPARE(top->isFirstColumnSpanned(), true);
2850 QCOMPARE(i1->isFirstColumnSpanned(), false);
2851 QCOMPARE(i2->isFirstColumnSpanned(), false);
2852
2853 top->setFirstColumnSpanned(false);
2854 i1->setFirstColumnSpanned(true);
2855 i2->setFirstColumnSpanned(true);
2856 QCOMPARE(top->isFirstColumnSpanned(), false);
2857 QCOMPARE(i1->isFirstColumnSpanned(), true);
2858 QCOMPARE(i2->isFirstColumnSpanned(), false);
2859}
2860
2861void tst_QTreeWidget::removeSelectedItem()
2862{
2863 const QScopedPointer <QTreeWidget> w(new QTreeWidget);
2864 w->setSortingEnabled(true);
2865
2866 QTreeWidgetItem *first = new QTreeWidgetItem();
2867 first->setText(column: 0, atext: QLatin1String("D"));
2868 w->addTopLevelItem(item: first);
2869
2870 QTreeWidgetItem *itm = new QTreeWidgetItem();
2871 itm->setText(column: 0, atext: QLatin1String("D"));
2872 w->addTopLevelItem(item: itm);
2873
2874 itm = new QTreeWidgetItem();
2875 itm->setText(column: 0, atext: QLatin1String("C"));
2876 w->addTopLevelItem(item: itm);
2877 itm->setSelected(true);
2878
2879 itm = new QTreeWidgetItem();
2880 itm->setText(column: 0, atext: QLatin1String("A"));
2881 w->addTopLevelItem(item: itm);
2882
2883 //w->show();
2884
2885 QItemSelectionModel *selModel = w->selectionModel();
2886 QCOMPARE(selModel->hasSelection(), true);
2887 QCOMPARE(selModel->selectedRows().count(), 1);
2888
2889 const QScopedPointer<QTreeWidgetItem> taken(w->takeTopLevelItem(index: 2));
2890 QCOMPARE(taken->text(0), QLatin1String("C"));
2891
2892 QCOMPARE(selModel->hasSelection(), false);
2893 QCOMPARE(selModel->selectedRows().count(), 0);
2894 QItemSelection sel = selModel->selection();
2895 QCOMPARE(selModel->isSelected(w->model()->index(0,0)), false);
2896}
2897
2898void tst_QTreeWidget::removeCurrentItem()
2899{
2900 PublicTreeWidget widget;
2901 connect(sender: widget.selectionModel(),
2902 signal: &QItemSelectionModel::currentChanged,
2903 receiver: &widget, slot: &PublicTreeWidget::clear);
2904 QTreeWidgetItem *item = new QTreeWidgetItem(&widget);
2905 widget.setCurrentItem(item);
2906 widget.deleteCurrent();
2907}
2908
2909void tst_QTreeWidget::removeCurrentItem_task186451()
2910{
2911 PublicTreeWidget widget;
2912 QTreeWidgetItem *item = new QTreeWidgetItem(&widget, {"1"});
2913 QTreeWidgetItem *item2 = new QTreeWidgetItem(&widget, {"2"});
2914 widget.setCurrentItem(item);
2915 widget.deleteCurrent();
2916
2917 QVERIFY(item2->isSelected());
2918 QCOMPARE(item2, widget.currentItem());
2919}
2920
2921void tst_QTreeWidget::randomExpand()
2922{
2923 QTreeWidget tree;
2924 QTreeWidgetItem *item1 = new QTreeWidgetItem(&tree);
2925 QTreeWidgetItem *item3 = new QTreeWidgetItem(&tree, item1);
2926 new QTreeWidgetItem(item1);
2927 new QTreeWidgetItem(item3);
2928
2929 tree.expandAll();
2930
2931 /*
2932 item1
2933 \- item2
2934 item3
2935 \- item4
2936 */
2937
2938 for (int i = 0; i < 100; i++) {
2939 auto newItem1 = new QTreeWidgetItem(&tree, item1);
2940 newItem1->setExpanded(true);
2941 QCOMPARE(newItem1->isExpanded(), true);
2942
2943 QTreeWidgetItem *x = new QTreeWidgetItem();
2944 QCOMPARE(newItem1->isExpanded(), true);
2945 newItem1->addChild(child: x);
2946
2947 QCOMPARE(newItem1->isExpanded(), true);
2948 }
2949}
2950
2951void tst_QTreeWidget::crashTest()
2952{
2953 QTreeWidget tree;
2954 tree.setColumnCount(1);
2955 tree.show();
2956
2957 QTreeWidgetItem *item1 = new QTreeWidgetItem(&tree);
2958 item1->setText(column: 0, atext: "item1");
2959 item1->setExpanded(true);
2960 QTreeWidgetItem *item2 = new QTreeWidgetItem(item1);
2961 item2->setText(column: 0, atext: "item2");
2962
2963 QTreeWidgetItem *item3 = new QTreeWidgetItem(&tree, item1);
2964 item3->setText(column: 0, atext: "item3");
2965 item3->setExpanded(true);
2966 QTreeWidgetItem *item4 = new QTreeWidgetItem(item3);
2967 item4->setText(column: 0, atext: "item4");
2968
2969 QTreeWidgetItem *item5 = new QTreeWidgetItem(&tree, item3);
2970 item5->setText(column: 0, atext: "item5");
2971 item5->setExpanded(true);
2972 QTreeWidgetItem *item6 = new QTreeWidgetItem(item5);
2973 item6->setText(column: 0, atext: "item6");
2974
2975 for (int i = 0; i < 1000; i++) {
2976 QTreeWidgetItem *newItem1 = new QTreeWidgetItem(&tree, item1);
2977 newItem1->setText(column: 0, atext: "newItem");
2978 QTreeWidgetItem *newItem2 = new QTreeWidgetItem(newItem1);
2979 newItem2->setText(column: 0, atext: "subItem1");
2980 QTreeWidgetItem *newItem3 = new QTreeWidgetItem(newItem1, newItem2);
2981 newItem3->setText(column: 0, atext: "subItem2");
2982 delete item3;
2983 item3 = newItem1;
2984 }
2985 QCoreApplication::processEvents();
2986}
2987
2988class CrashWidget : public QTreeWidget
2989{
2990public:
2991 CrashWidget(QWidget *parent = nullptr) : QTreeWidget(parent)
2992 {
2993 setSortingEnabled(true);
2994 timerId = startTimer(interval: 10);
2995 }
2996 int i = 0;
2997protected:
2998 void timerEvent(QTimerEvent * event) override
2999 {
3000 if (event->timerId() == timerId) {
3001 auto newItem = new QTreeWidgetItem({QString::number(i++)});
3002 m_list.append(t: newItem);
3003 insertTopLevelItem(index: 0, item: newItem);
3004 while (m_list.count() > 10)
3005 delete m_list.takeFirst();
3006 }
3007 QTreeWidget::timerEvent(event);
3008 }
3009private:
3010 int timerId;
3011 QVector<QTreeWidgetItem*> m_list;
3012};
3013
3014void tst_QTreeWidget::sortAndSelect()
3015{
3016 CrashWidget w;
3017 w.resize(w: 1, h: 1);
3018 w.show();
3019 while (w.i < 100) {
3020 QApplication::processEvents();
3021 if (w.i & 16) {
3022 QPoint pt = w.viewport()->rect().center();
3023 QTest::mouseClick(widget: w.viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: pt);
3024 }
3025 }
3026 QVERIFY(true);
3027}
3028
3029void tst_QTreeWidget::defaultRowSizes()
3030{
3031 const QScopedPointer<QTreeWidget> tw(new QTreeWidget);
3032 tw->setIconSize(QSize(50, 50));
3033 tw->setColumnCount(6);
3034 for (int i = 0; i < 10; ++i) {
3035 auto it = new QTreeWidgetItem(tw.data());
3036 for (int j = 0; j < tw->columnCount() - 1; ++j)
3037 it->setText(column: j, atext: "This is a test");
3038 auto sp = static_cast<QStyle::StandardPixmap>(i + QStyle::SP_TitleBarMenuButton);
3039 QPixmap icon = tw->style()->standardPixmap(standardPixmap: sp);
3040
3041 if (icon.isNull())
3042 QSKIP("No pixmap found on current style, skipping this test.");
3043 it->setIcon(column: tw->columnCount() - 1,
3044 aicon: icon.scaled(s: tw->iconSize()));
3045 }
3046 tw->resize(w: 100,h: 100);
3047 tw->show();
3048 QApplication::processEvents();
3049
3050 QRect visualRect = tw->visualItemRect(item: tw->topLevelItem(index: 0));
3051 QVERIFY(visualRect.height() >= 50);
3052}
3053
3054void tst_QTreeWidget::task191552_rtl()
3055{
3056 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3057 QSKIP("Wayland: This fails. Figure out why.");
3058
3059 Qt::LayoutDirection oldDir = QGuiApplication::layoutDirection();
3060 QGuiApplication::setLayoutDirection(Qt::RightToLeft);
3061
3062 QTreeWidget tw;
3063 tw.setColumnCount(1);
3064 QTreeWidgetItem *item = new QTreeWidgetItem(&tw);
3065 item->setText(column: 0, atext: "item 1");
3066 item->setCheckState(column: 0, state: Qt::Checked);
3067 QCOMPARE(item->checkState(0), Qt::Checked);
3068 tw.show();
3069 QVERIFY(QTest::qWaitForWindowActive(&tw));
3070 QStyleOptionViewItem opt;
3071 opt.initFrom(w: &tw);
3072 opt.rect = tw.visualItemRect(item);
3073 // mimic QStyledItemDelegate::initStyleOption logic
3074 opt.features = QStyleOptionViewItem::HasDisplay | QStyleOptionViewItem::HasCheckIndicator;
3075 opt.checkState = Qt::Checked;
3076 opt.widget = &tw;
3077 const QRect checkRect = tw.style()->subElementRect(subElement: QStyle::SE_ItemViewItemCheckIndicator, option: &opt, widget: &tw);
3078 QTest::mouseClick(widget: tw.viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos: checkRect.center());
3079 QCOMPARE(item->checkState(0), Qt::Unchecked);
3080
3081 QGuiApplication::setLayoutDirection(oldDir);
3082}
3083
3084void tst_QTreeWidget::task203673_selection()
3085{
3086 //we try to change the selection by rightclick + ctrl
3087 //it should do anything when using ExtendedSelection
3088
3089 QTreeWidget tw;
3090 tw.setColumnCount(1);
3091 QTreeWidgetItem *item1 = new QTreeWidgetItem(&tw);
3092 item1->setText(column: 0, atext: "item 1");
3093 tw.setSelectionMode(QTreeView::ExtendedSelection);
3094
3095 QPoint center = tw.visualItemRect(item: item1).center();
3096 QCOMPARE(item1->isSelected(), false);
3097
3098 QTest::mouseClick(widget: tw.viewport(), button: Qt::RightButton, stateKey: Qt::ControlModifier, pos: center);
3099 QCOMPARE(item1->isSelected(), false);
3100
3101 QTest::mouseClick(widget: tw.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: center);
3102 QCOMPARE(item1->isSelected(), true);
3103
3104 QTest::mouseClick(widget: tw.viewport(), button: Qt::RightButton, stateKey: Qt::ControlModifier, pos: center);
3105 QCOMPARE(item1->isSelected(), true); //it shouldn't change
3106
3107 QTest::mouseClick(widget: tw.viewport(), button: Qt::LeftButton, stateKey: Qt::ControlModifier, pos: center);
3108 QCOMPARE(item1->isSelected(), false);
3109}
3110
3111
3112void tst_QTreeWidget::rootItemFlags()
3113{
3114 QTreeWidget tw;
3115 tw.setColumnCount(1);
3116 QTreeWidgetItem *item = new QTreeWidgetItem(&tw);
3117 item->setText(column: 0, atext: "item 1");
3118
3119 QVERIFY(tw.invisibleRootItem()->flags() & Qt::ItemIsDropEnabled);
3120
3121 tw.invisibleRootItem()->setFlags(tw.invisibleRootItem()->flags() & ~Qt::ItemIsDropEnabled);
3122
3123 QVERIFY(!(tw.invisibleRootItem()->flags() & Qt::ItemIsDropEnabled));
3124}
3125
3126void tst_QTreeWidget::task218661_setHeaderData()
3127{
3128 //We check that setting header data out of bounds returns false
3129 //and doesn't increase the size of the model
3130 QTreeWidget tw;
3131 tw.setColumnCount(1);
3132 QCOMPARE(tw.columnCount(), 1);
3133
3134 QCOMPARE(tw.model()->setHeaderData(99999, Qt::Horizontal, QVariant()), false);
3135
3136 QCOMPARE(tw.columnCount(), 1);
3137}
3138
3139void tst_QTreeWidget::task245280_sortChildren()
3140{
3141 QTreeWidget tw;
3142 tw.setColumnCount(2);
3143
3144 QTreeWidgetItem top(&tw);
3145 top.setText(column: 0,atext: "Col 0");
3146 top.setText(column: 1,atext: "Col 1");
3147 QTreeWidgetItem item1(&top);
3148 item1.setText(column: 0,atext: "X");
3149 item1.setText(column: 1,atext: "0");
3150 QTreeWidgetItem item2(&top);
3151 item2.setText(column: 0,atext: "A");
3152 item2.setText(column: 1,atext: "4");
3153 QTreeWidgetItem item3(&top);
3154 item3.setText(column: 0,atext: "E");
3155 item3.setText(column: 1,atext: "1");
3156 QTreeWidgetItem item4(&top);
3157 item4.setText(column: 0,atext: "Z");
3158 item4.setText(column: 1,atext: "3");
3159 QTreeWidgetItem item5(&top);
3160 item5.setText(column: 0,atext: "U");
3161 item5.setText(column: 1,atext: "2");
3162 tw.expandAll();
3163 tw.show();
3164 top.sortChildren(column: 1,order: Qt::AscendingOrder);
3165
3166 for (int i = 0; i < top.childCount(); ++i)
3167 QCOMPARE(top.child(i)->text(1), QString::number(i));
3168}
3169
3170void tst_QTreeWidget::task253109_itemHeight()
3171{
3172 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3173 QSKIP("Wayland: This fails. Figure out why.");
3174
3175 QTreeWidget treeWidget;
3176 treeWidget.setColumnCount(1);
3177 treeWidget.show();
3178 QVERIFY(QTest::qWaitForWindowActive(&treeWidget));
3179
3180 QTreeWidgetItem item(&treeWidget);
3181 class MyWidget : public QWidget
3182 {
3183 QSize sizeHint() const override { return QSize(200, 100); }
3184 } w;
3185 treeWidget.setItemWidget(item: &item, column: 0, widget: &w);
3186
3187 QTRY_COMPARE(w.geometry(), treeWidget.visualItemRect(&item));
3188}
3189
3190void tst_QTreeWidget::task206367_duplication()
3191{
3192 QWidget topLevel;
3193 // Explicitly set the font size because it is dpi dependent on some platforms
3194 QFont font;
3195 font.setPixelSize(40);
3196 topLevel.setFont(font);
3197 QTreeWidget treeWidget(&topLevel);
3198 topLevel.show();
3199 treeWidget.resize(w: 200, h: 200);
3200 treeWidget.setHeaderHidden(true);
3201
3202 treeWidget.setSortingEnabled(true);
3203 QTreeWidgetItem* rootItem = new QTreeWidgetItem(&treeWidget, QStringList("root"));
3204 for (int nFile = 0; nFile < 2; nFile++ ) {
3205 QTreeWidgetItem* itemFile = new QTreeWidgetItem(rootItem, {QString::number(nFile)});
3206 for (int nRecord = 0; nRecord < 2; nRecord++)
3207 new QTreeWidgetItem(itemFile, {QString::number(nRecord)});
3208 itemFile->setExpanded(true);
3209 }
3210 rootItem->setExpanded(true);
3211
3212 //there should be enough room for 2x2 items. If there is a scrollbar, it means the items are duplicated
3213 QTRY_VERIFY(!treeWidget.verticalScrollBar()->isVisible());
3214}
3215
3216void tst_QTreeWidget::itemSelectionChanged()
3217{
3218 QVERIFY(testWidget);
3219 if (testWidget->topLevelItem(index: 0))
3220 QVERIFY(testWidget->topLevelItem(0)->isSelected());
3221}
3222
3223void tst_QTreeWidget::selectionOrder()
3224{
3225 testWidget->setColumnCount(1);
3226 QList<QTreeWidgetItem *> items;
3227 for (int i = 0; i < 10; ++i) {
3228 items.append(t: new QTreeWidgetItem(static_cast<QTreeWidget *>(nullptr),
3229 {QStringLiteral("item: %1").arg(a: i)}));
3230 }
3231 testWidget->insertTopLevelItems(index: 0, items);
3232
3233 QModelIndex idx = testWidget->indexFromItem(item: items.at(i: 0));
3234 connect(sender: testWidget, signal: &QTreeWidget::itemSelectionChanged,
3235 receiver: this, slot: &tst_QTreeWidget::itemSelectionChanged);
3236 testWidget->selectionModel()->select(index: idx, command: QItemSelectionModel::SelectCurrent);
3237 disconnect(sender: testWidget, signal: &QTreeWidget::itemSelectionChanged,
3238 receiver: this, slot: &tst_QTreeWidget::itemSelectionChanged);
3239}
3240
3241void tst_QTreeWidget::setSelectionModel()
3242{
3243 QTreeWidget tree;
3244 for(int i = 0; i < 3; ++i)
3245 new QTreeWidgetItem(&tree, QStringList(QString::number(i)));
3246 QItemSelectionModel selection(tree.model());
3247 selection.select(index: tree.model()->index(row: 1, column: 0), command: QItemSelectionModel::Select);
3248 tree.setSelectionModel(&selection);
3249 QCOMPARE(tree.topLevelItem(1)->isSelected(), true);
3250}
3251
3252void tst_QTreeWidget::task217309()
3253{
3254 QTreeWidgetItem item;
3255 item.setFlags(item.flags() | Qt::ItemIsAutoTristate);
3256 QTreeWidgetItem subitem1;
3257 subitem1.setFlags(subitem1.flags() | Qt::ItemIsAutoTristate);
3258 QTreeWidgetItem subitem2;
3259 subitem2.setFlags(subitem2.flags() | Qt::ItemIsAutoTristate);
3260 item.addChild(child: &subitem1);
3261 item.addChild(child: &subitem2);
3262 subitem1.setCheckState(column: 0, state: Qt::Checked);
3263 subitem2.setCheckState(column: 0, state: Qt::Unchecked);
3264
3265 QVERIFY(item.data(0, Qt::CheckStateRole) == Qt::PartiallyChecked);
3266
3267 subitem2.setCheckState(column: 0, state: Qt::PartiallyChecked);
3268 QVERIFY(item.data(0, Qt::CheckStateRole) == Qt::PartiallyChecked);
3269
3270 subitem2.setCheckState(column: 0, state: Qt::Checked);
3271 QVERIFY(item.data(0, Qt::CheckStateRole) == Qt::Checked);
3272}
3273
3274void tst_QTreeWidget::nonEditableTristate()
3275{
3276 // A tree with checkable items, the parent is tristate
3277 QTreeWidget tree;
3278 QTreeWidgetItem *item = new QTreeWidgetItem;
3279 tree.insertTopLevelItem(index: 0, item);
3280 item->setFlags(item->flags() | Qt::ItemIsAutoTristate);
3281 item->setCheckState(column: 0, state: Qt::Unchecked);
3282 QTreeWidgetItem *subitem1 = new QTreeWidgetItem(item);
3283 subitem1->setCheckState(column: 0, state: Qt::Unchecked);
3284 QTreeWidgetItem *subitem2 = new QTreeWidgetItem(item);
3285 subitem2->setCheckState(column: 0, state: Qt::Unchecked);
3286 QCOMPARE(int(item->checkState(0)), int(Qt::Unchecked));
3287 tree.show();
3288 QVERIFY(QTest::qWaitForWindowExposed(&tree));
3289
3290 // Test clicking on the parent item, it should become Checked (not PartiallyChecked)
3291 QStyleOptionViewItem option;
3292 option.rect = tree.visualRect(index: tree.model()->index(row: 0, column: 0));
3293 option.state |= QStyle::State_Enabled;
3294 option.features |= QStyleOptionViewItem::HasCheckIndicator | QStyleOptionViewItem::HasDisplay;
3295 option.checkState = item->checkState(column: 0);
3296
3297 auto appStyle = QApplication::style();
3298 const int checkMargin = appStyle->pixelMetric(
3299 metric: QStyle::PM_FocusFrameHMargin, option: nullptr, widget: nullptr) + 1;
3300 QPoint pos = appStyle->subElementRect(
3301 subElement: QStyle::SE_ItemViewItemCheckIndicator, option: &option, widget: nullptr).center();
3302 pos.rx() += checkMargin;
3303 QTest::mouseClick(widget: tree.viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos);
3304 QCOMPARE(item->checkState(0), Qt::Checked);
3305
3306 // Click again, it should become Unchecked.
3307 QTest::mouseClick(widget: tree.viewport(), button: Qt::LeftButton, stateKey: Qt::NoModifier, pos);
3308 QCOMPARE(item->checkState(0), Qt::Unchecked);
3309}
3310
3311void tst_QTreeWidget::emitDataChanged()
3312{
3313 QTreeWidget tree;
3314 QSignalSpy spy(&tree, &QTreeWidget::itemChanged);
3315 auto item = new PublicTreeItem;
3316 tree.insertTopLevelItem(index: 0, item);
3317 item->emitDataChanged();
3318 QCOMPARE(spy.count(), 1);
3319}
3320
3321void tst_QTreeWidget::setCurrentItemExpandsParent()
3322{
3323 QTreeWidget w;
3324 w.setColumnCount(1);
3325 QTreeWidgetItem *i1 = new QTreeWidgetItem(&w, {"parent"});
3326 QTreeWidgetItem *i2 = new QTreeWidgetItem(i1, {"child"});
3327 QVERIFY(!i2->isExpanded());
3328 QVERIFY(!w.currentItem());
3329 w.setCurrentItem(i2);
3330 QVERIFY(!i2->isExpanded());
3331 QCOMPARE(w.currentItem(), i2);
3332}
3333
3334void tst_QTreeWidget::task239150_editorWidth()
3335{
3336 //we check that an item with no text will get an editor with a correct size
3337 QTreeWidget tree;
3338
3339 QStyleOptionFrame opt;
3340 opt.init(w: &tree);
3341 const int minWidth = tree.style()->sizeFromContents(ct: QStyle::CT_LineEdit, opt: &opt, contentsSize: QSize(0, 0).
3342 expandedTo(otherSize: QApplication::globalStrut()), w: nullptr).width();
3343
3344 {
3345 QTreeWidgetItem item;
3346 item.setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled );
3347 tree.addTopLevelItem(item: &item);
3348 QVERIFY(tree.itemWidget(&item, 0) == nullptr);
3349 tree.editItem(item: &item);
3350 QVERIFY(tree.itemWidget(&item, 0));
3351 QVERIFY(tree.itemWidget(&item, 0)->width() >= minWidth);
3352 }
3353
3354 //now let's test it with an item with a lot of text
3355 {
3356 QTreeWidgetItem item;
3357 item.setText(column: 0, atext: "foooooooooooooooooooooooo");
3358 item.setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled );
3359 tree.addTopLevelItem(item: &item);
3360 QVERIFY(tree.itemWidget(&item, 0) == nullptr);
3361 tree.editItem(item: &item);
3362 QVERIFY(tree.itemWidget(&item, 0));
3363 QVERIFY(tree.itemWidget(&item, 0)->width() >= minWidth + tree.fontMetrics().horizontalAdvance(item.text(0)));
3364 }
3365}
3366
3367
3368
3369void tst_QTreeWidget::setTextUpdate()
3370{
3371 QTreeWidget treeWidget;
3372 treeWidget.setColumnCount(2);
3373
3374 class MyItemDelegate : public QStyledItemDelegate
3375 {
3376 public:
3377 using QStyledItemDelegate::QStyledItemDelegate;
3378 void paint(QPainter *painter, const QStyleOptionViewItem &option,
3379 const QModelIndex &index) const override
3380 {
3381 numPaints++;
3382 QStyledItemDelegate::paint(painter, option, index);
3383 }
3384
3385 mutable int numPaints = 0;
3386 } delegate;
3387
3388 treeWidget.setItemDelegate(&delegate);
3389 treeWidget.show();
3390 QVERIFY(QTest::qWaitForWindowExposed(&treeWidget));
3391 QTreeWidgetItem *item = new QTreeWidgetItem({ "variable1", "0" });
3392 treeWidget.insertTopLevelItem(index: 0, item);
3393 QTRY_VERIFY(delegate.numPaints > 0);
3394 delegate.numPaints = 0;
3395
3396 item->setText(column: 1, atext: "42");
3397 QTRY_VERIFY(delegate.numPaints > 0);
3398}
3399
3400void tst_QTreeWidget::taskQTBUG2844_visualItemRect()
3401{
3402 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3403 QSKIP("Wayland: This fails. Figure out why.");
3404
3405 PublicTreeWidget tree;
3406 tree.resize(w: 150, h: 100);
3407 tree.setColumnCount(3);
3408 QTreeWidgetItem item(&tree);
3409
3410 QRect rectCol0 = tree.visualRect(index: tree.indexFromItem(item: &item, column: 0));
3411 QRect rectCol1 = tree.visualRect(index: tree.indexFromItem(item: &item, column: 1));
3412 QRect rectCol2 = tree.visualRect(index: tree.indexFromItem(item: &item, column: 2));
3413
3414 QCOMPARE(tree.visualItemRect(&item), rectCol0 | rectCol2);
3415 tree.setColumnHidden(column: 2, hide: true);
3416 QCOMPARE(tree.visualItemRect(&item), rectCol0 | rectCol1);
3417}
3418
3419void tst_QTreeWidget::setChildIndicatorPolicy()
3420{
3421 QTreeWidget treeWidget;
3422 treeWidget.setColumnCount(1);
3423
3424 class MyItemDelegate : public QStyledItemDelegate
3425 {
3426 public:
3427 using QStyledItemDelegate::QStyledItemDelegate;
3428 void paint(QPainter *painter,
3429 const QStyleOptionViewItem &option,
3430 const QModelIndex &index) const override
3431 {
3432 numPaints++;
3433 QCOMPARE(!(option.state & QStyle::State_Children), !expectChildren);
3434 QStyledItemDelegate::paint(painter, option, index);
3435 }
3436 mutable int numPaints = 0;
3437 bool expectChildren = false;
3438 } delegate;
3439
3440 treeWidget.setItemDelegate(&delegate);
3441 treeWidget.show();
3442 QVERIFY(QTest::qWaitForWindowExposed(&treeWidget));
3443 QCoreApplication::processEvents(); // Process all queued paint events
3444
3445 QTreeWidgetItem *item = new QTreeWidgetItem(QStringList("Hello"));
3446 treeWidget.insertTopLevelItem(index: 0, item);
3447 QTRY_VERIFY(delegate.numPaints > 0);
3448
3449 delegate.numPaints = 0;
3450 delegate.expectChildren = true;
3451 item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
3452 QTRY_COMPARE(delegate.numPaints, 1);
3453
3454 delegate.numPaints = 0;
3455 delegate.expectChildren = false;
3456 item->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
3457 QTRY_COMPARE(delegate.numPaints, 1);
3458
3459 delegate.numPaints = 0;
3460 delegate.expectChildren = true;
3461 new QTreeWidgetItem(item);
3462 QTRY_COMPARE(delegate.numPaints, 1);
3463
3464 delegate.numPaints = 0;
3465 delegate.expectChildren = false;
3466 item->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator);
3467 QTRY_COMPARE(delegate.numPaints, 1);
3468}
3469
3470// From QTBUG_34717 (QTreeWidget crashes when scrolling to the end
3471// of an expanded tree, then collapse all)
3472// The test passes simply if it doesn't crash.
3473void tst_QTreeWidget::taskQTBUG_34717_collapseAtBottom()
3474{
3475 PublicTreeWidget treeWidget;
3476 treeWidget.header()->setSectionResizeMode(QHeaderView::ResizeToContents);
3477 treeWidget.setColumnCount(2);
3478 QTreeWidgetItem *mainItem = new QTreeWidgetItem(&treeWidget, { "Root" });
3479 for (int i = 0; i < 200; ++i) {
3480 QTreeWidgetItem *item = new QTreeWidgetItem(mainItem, { "Item" });
3481 new QTreeWidgetItem(item, { "Child", "1" });
3482 new QTreeWidgetItem(item, { "Child", "2" });
3483 new QTreeWidgetItem(item, { "Child", "3" });
3484 }
3485 treeWidget.show();
3486 treeWidget.expandAll();
3487 treeWidget.scrollToBottom();
3488 treeWidget.collapseAll();
3489
3490 treeWidget.setAnimated(true);
3491 treeWidget.expandAll();
3492 treeWidget.scrollToBottom();
3493 mainItem->setExpanded(false);
3494
3495 QVERIFY(treeWidget.sizeHintForColumn(1) >= 0);
3496}
3497
3498void tst_QTreeWidget::task20345_sortChildren()
3499{
3500 if (!QGuiApplication::platformName().compare(other: QLatin1String("wayland"), cs: Qt::CaseInsensitive)
3501 || !QGuiApplication::platformName().compare(other: QLatin1String("winrt"), cs: Qt::CaseInsensitive))
3502 QSKIP("Wayland/WinRT: This causes a crash triggered by setVisible(false)");
3503
3504 // This test case is considered successful if it is executed (no crash in sorting)
3505 QTreeWidget tw;
3506 tw.setColumnCount(3);
3507 tw.headerItem()->setText(column: 0, atext: "Col 0");
3508 tw.headerItem()->setText(column: 1, atext: "Col 1");
3509 tw.header()->setSortIndicator(logicalIndex: 0, order: Qt::AscendingOrder);
3510 tw.setSortingEnabled(true);
3511 tw.show();
3512
3513 auto rootItem = new QTreeWidgetItem(&tw, QStringList("a"));
3514 auto childItem = new QTreeWidgetItem(rootItem);
3515 childItem->setText(column: 1, atext: "3");
3516 childItem = new QTreeWidgetItem(rootItem);
3517 childItem->setText(column: 1, atext: "1");
3518 childItem = new QTreeWidgetItem(rootItem);
3519 childItem->setText(column: 1, atext: "2");
3520
3521 tw.setCurrentItem(tw.topLevelItem(index: 0));
3522
3523 QTreeWidgetItem *curItem = tw.currentItem();
3524 int childCount = curItem->childCount() + 1;
3525
3526 QTreeWidgetItem *newItem = new QTreeWidgetItem(curItem);
3527 newItem->setText(column: 1, atext: QString::number(childCount));
3528 rootItem->sortChildren(column: 1, order: Qt::AscendingOrder);
3529 QVERIFY(1);
3530}
3531
3532void tst_QTreeWidget::getMimeDataWithInvalidItem()
3533{
3534 PublicTreeWidget w;
3535 QTest::ignoreMessage(type: QtWarningMsg, message: "QTreeWidget::mimeData: Null-item passed");
3536 QMimeData *md = w.mimeData(items: QList<QTreeWidgetItem*>() << nullptr);
3537 QVERIFY(!md);
3538}
3539
3540// visualItemRect returned a wrong rect when the columns were moved
3541// (-> logical index != visual index). see QTBUG-28733
3542void tst_QTreeWidget::testVisualItemRect()
3543{
3544 if (QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive))
3545 QSKIP("Wayland: This fails. Figure out why.");
3546
3547 QTreeWidget tw;
3548 tw.setColumnCount(2);
3549 QTreeWidgetItem *item = new QTreeWidgetItem(&tw);
3550 item->setText(column: 0, atext: "text 0");
3551 item->setText(column: 1, atext: "text 1");
3552
3553 static const int sectionSize = 30;
3554 tw.header()->setStretchLastSection(false);
3555 tw.header()->setMinimumSectionSize(sectionSize);
3556 tw.header()->resizeSection(logicalIndex: 0, size: sectionSize);
3557 tw.header()->resizeSection(logicalIndex: 1, size: sectionSize);
3558 tw.setRootIsDecorated(false);
3559 tw.show();
3560 QVERIFY(QTest::qWaitForWindowExposed(&tw));
3561
3562 QRect r = tw.visualItemRect(item);
3563 QCOMPARE(r.width(), sectionSize * 2); // 2 columns
3564 tw.header()->moveSection(from: 1, to: 0);
3565 r = tw.visualItemRect(item);
3566 QCOMPARE(r.width(), sectionSize * 2); // 2 columns
3567 tw.hideColumn(column: 0);
3568 r = tw.visualItemRect(item);
3569 QCOMPARE(r.width(), sectionSize);
3570}
3571
3572void tst_QTreeWidget::reparentHiddenItem()
3573{
3574 QTreeWidgetItem *parent = new QTreeWidgetItem(testWidget);
3575 parent->setText(column: 0, atext: "parent");
3576 QTreeWidgetItem *otherParent = new QTreeWidgetItem(testWidget);
3577 otherParent->setText(column: 0, atext: "other parent");
3578 QTreeWidgetItem *child = new QTreeWidgetItem(parent);
3579 child->setText(column: 0, atext: "child");
3580 QTreeWidgetItem *grandChild = new QTreeWidgetItem(child);
3581 grandChild->setText(column: 0, atext: "grandchild");
3582 QVERIFY(child->parent());
3583 QVERIFY(grandChild->parent());
3584
3585 testWidget->expandItem(item: parent);
3586 testWidget->expandItem(item: otherParent);
3587 testWidget->expandItem(item: child);
3588
3589 QVERIFY(!parent->isHidden());
3590 QVERIFY(!child->isHidden());
3591 QVERIFY(!grandChild->isHidden());
3592
3593 grandChild->setHidden(true);
3594
3595 QVERIFY(grandChild->isHidden());
3596 parent->removeChild(child);
3597 otherParent->addChild(child);
3598 QVERIFY(grandChild->isHidden());
3599}
3600
3601void tst_QTreeWidget::persistentChildIndex() // QTBUG-90030
3602{
3603 QTreeWidget tree;
3604 QTreeWidgetItem *toplevel = new QTreeWidgetItem(QStringList{QStringLiteral("toplevel")});
3605 tree.addTopLevelItem(item: toplevel);
3606 QModelIndex firstIndex = tree.model()->index(row: 0, column: 0);
3607 QTreeWidgetItem *child1 = new QTreeWidgetItem(QStringList{QStringLiteral("child1")});
3608 QTreeWidgetItem *child2 = new QTreeWidgetItem(QStringList{QStringLiteral("child2")});
3609 toplevel->addChildren(children: {child1, child2});
3610 QPersistentModelIndex persistentIdx = tree.model()->index(row: 1, column: 0, parent: firstIndex);
3611 QCOMPARE(persistentIdx.data().toString(), QStringLiteral("child2"));
3612 tree.model()->removeRows(row: 0, count: 1, parent: firstIndex);
3613 QCOMPARE(persistentIdx.data().toString(), QStringLiteral("child2"));
3614}
3615
3616#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
3617void tst_QTreeWidget::clearItemData()
3618{
3619 QTreeWidget tree;
3620 QAbstractItemModel* model = tree.model();
3621 QVERIFY(model->insertColumn(0));
3622 QVERIFY(model->insertRow(0));
3623 const QModelIndex parentIdx = model->index(0, 0);
3624 QVERIFY(model->insertColumn(0, parentIdx));
3625 QVERIFY(model->insertRow(0, parentIdx));
3626 const QModelIndex childIdx = model->index(0, 0, parentIdx);
3627 model->setData(parentIdx, QStringLiteral("parent"));
3628 model->setData(parentIdx, QStringLiteral("parent"), Qt::UserRole);
3629 model->setData(childIdx, QStringLiteral("child"));
3630 QSignalSpy dataChangeSpy(model, &QAbstractItemModel::dataChanged);
3631 QVERIFY(dataChangeSpy.isValid());
3632 QVERIFY(!model->clearItemData(QModelIndex()));
3633 QCOMPARE(dataChangeSpy.size(), 0);
3634 QVERIFY(model->clearItemData(parentIdx));
3635 QVERIFY(!model->data(parentIdx).isValid());
3636 QVERIFY(!model->data(parentIdx, Qt::UserRole).isValid());
3637 QCOMPARE(dataChangeSpy.size(), 1);
3638 QList<QVariant> dataChangeArgs = dataChangeSpy.takeFirst();
3639 QCOMPARE(dataChangeArgs.at(0).value<QModelIndex>(), parentIdx);
3640 QCOMPARE(dataChangeArgs.at(1).value<QModelIndex>(), parentIdx);
3641 QVERIFY(dataChangeArgs.at(2).value<QVector<int>>().isEmpty());
3642 QVERIFY(model->clearItemData(parentIdx));
3643 QCOMPARE(dataChangeSpy.size(), 0);
3644 QVERIFY(model->clearItemData(childIdx));
3645 QVERIFY(!model->data(childIdx).isValid());
3646 QCOMPARE(dataChangeSpy.size(), 1);
3647 dataChangeArgs = dataChangeSpy.takeFirst();
3648 QCOMPARE(dataChangeArgs.at(0).value<QModelIndex>(), childIdx);
3649 QCOMPARE(dataChangeArgs.at(1).value<QModelIndex>(), childIdx);
3650 QVERIFY(dataChangeArgs.at(2).value<QVector<int>>().isEmpty());
3651}
3652#endif
3653
3654QTEST_MAIN(tst_QTreeWidget)
3655#include "tst_qtreewidget.moc"
3656

source code of qtbase/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp