1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44 #include <qstandarditemmodel.h>
45 #include <qitemdelegate.h>
46 #include <qcolumnview.h>
47 #include "../../../src/gui/itemviews/qcolumnviewgrip_p.h"
48 #ifndef Q_OS_SYMBIAN
49 #include "../../../src/gui/dialogs/qfilesystemmodel_p.h"
50 #endif
51 #include <qdirmodel.h>
52 #include <qstringlistmodel.h>
53 #include <qdebug.h>
54 #include <qitemdelegate.h>
55 #include <qscrollbar.h>
56 #include <private/qcolumnview_p.h>
57 #include "../../shared/util.h"
58
59 //TESTED_CLASS=
60 //TESTED_FILES=
61
62 #define ANIMATION_DELAY 300
63
64 class tst_QColumnView : public QObject {
65 Q_OBJECT
66
67 public:
68 tst_QColumnView();
69 virtual ~tst_QColumnView();
70
71 public Q_SLOTS:
72 void init();
73 void cleanup();
74
75 private slots:
76 void rootIndex();
77 void grips();
78 void isIndexHidden();
79 void indexAt();
80 void scrollContentsBy_data();
81 void scrollContentsBy();
82 void scrollTo_data();
83 void scrollTo();
84 void moveCursor_data();
85 void moveCursor();
86 void selectAll();
87 void clicked();
88 void selectedColumns();
89 void setSelection();
90 void setSelectionModel();
91 void visualRegionForSelection();
92
93 void dynamicModelChanges();
94
95 // grip
96 void moveGrip_basic();
97 void moveGrip_data();
98 void moveGrip();
99 void doubleClick();
100 void gripMoved();
101
102 void preview();
103 void swapPreview();
104 void sizes();
105 void rowDelegate();
106 void resize();
107 void changeSameColumn();
108 void parentCurrentIndex_data();
109 void parentCurrentIndex();
110 void pullRug_data();
111 void pullRug();
112
113 protected slots:
114 void setPreviewWidget();
115 };
116
117 class TreeModel : public QStandardItemModel
118 {
119 public:
TreeModel()120 TreeModel()
121 {
122 for (int j = 0; j < 10; ++j) {
123 QStandardItem *parentItem = invisibleRootItem();
124 for (int i = 0; i < 10; ++i) {
125 QStandardItem *item = new QStandardItem(QString("item %0").arg(i));
126 parentItem->appendRow(item);
127 QStandardItem *item2 = new QStandardItem(QString("item %0").arg(i));
128 parentItem->appendRow(item2);
129 item2->appendRow(new QStandardItem(QString("item %0").arg(i)));
130 parentItem->appendRow(new QStandardItem(QString("file %0").arg(i)));
131 parentItem = item;
132 }
133 }
134 }
135
firstLevel()136 inline QModelIndex firstLevel() { return index(0, 0, QModelIndex()); }
secondLevel()137 inline QModelIndex secondLevel() { return index(0, 0, firstLevel()); }
thirdLevel()138 inline QModelIndex thirdLevel() { return index(0, 0, secondLevel()); }
139 };
140
141 class ColumnView : public QColumnView {
142
143 public:
ColumnView(QWidget * parent=0)144 ColumnView(QWidget *parent = 0) : QColumnView(parent){}
145
146 QList<QPointer<QAbstractItemView> > createdColumns;
ScrollContentsBy(int x,int y)147 void ScrollContentsBy(int x, int y) {scrollContentsBy(x,y); }
HorizontalOffset() const148 int HorizontalOffset() const { return horizontalOffset(); }
emitClicked()149 void emitClicked() { emit clicked(QModelIndex()); }
150
151 enum PublicCursorAction {
152 MoveUp = QAbstractItemView::MoveUp,
153 MoveDown = QAbstractItemView::MoveDown,
154 MoveLeft = QAbstractItemView::MoveLeft,
155 MoveRight = QAbstractItemView::MoveRight,
156 MoveHome = QAbstractItemView::MoveHome,
157 MoveEnd = QAbstractItemView::MoveEnd,
158 MovePageUp = QAbstractItemView::MovePageUp,
159 MovePageDown = QAbstractItemView::MovePageDown,
160 MoveNext = QAbstractItemView::MoveNext,
161 MovePrevious = QAbstractItemView::MovePrevious
162 };
163
MoveCursor(PublicCursorAction ca,Qt::KeyboardModifiers kbm)164 inline QModelIndex MoveCursor(PublicCursorAction ca, Qt::KeyboardModifiers kbm)
165 { return QColumnView::moveCursor((CursorAction)ca, kbm); }
IsIndexHidden(const QModelIndex & index) const166 bool IsIndexHidden(const QModelIndex&index) const
167 { return isIndexHidden(index); }
168
setSelection(const QRect & rect,QItemSelectionModel::SelectionFlags command)169 void setSelection(const QRect & rect, QItemSelectionModel::SelectionFlags command )
170 {
171 QColumnView::setSelection(rect, command);
172 }
173
visualRegionForSelection(QItemSelection selection)174 QRegion visualRegionForSelection(QItemSelection selection){
175 return QColumnView::visualRegionForSelection(selection);
176 }
177 protected:
createColumn(const QModelIndex & index)178 QAbstractItemView *createColumn(const QModelIndex &index) {
179 QAbstractItemView *view = QColumnView::createColumn(index);
180 QPointer<QAbstractItemView> savedView = view;
181 createdColumns.append(savedView);
182 return view;
183 }
184
185 };
186
tst_QColumnView()187 tst_QColumnView::tst_QColumnView()
188 {
189 }
190
~tst_QColumnView()191 tst_QColumnView::~tst_QColumnView()
192 {
193 }
194
init()195 void tst_QColumnView::init()
196 {
197 qApp->setLayoutDirection(Qt::LeftToRight);
198 #ifdef Q_OS_WINCE //disable magic for WindowsCE
199 qApp->setAutoMaximizeThreshold(-1);
200 #endif
201 }
202
cleanup()203 void tst_QColumnView::cleanup()
204 {
205 }
206
rootIndex()207 void tst_QColumnView::rootIndex()
208 {
209 ColumnView view;
210 // no model
211 view.setRootIndex(QModelIndex());
212
213 TreeModel model;
214 view.setModel(&model);
215
216 // A top level index
217 QModelIndex drive = model.firstLevel();
218 QVERIFY(view.visualRect(drive).isValid());
219 view.setRootIndex(QModelIndex());
220 QCOMPARE(view.HorizontalOffset(), 0);
221 QCOMPARE(view.rootIndex(), QModelIndex());
222 QVERIFY(view.visualRect(drive).isValid());
223
224 // A item under the rootIndex exists
225 QModelIndex home = model.thirdLevel();
226 QModelIndex homeFile = model.index(0, 0, home);
227 int i = 0;
228 while (i < model.rowCount(home) - 1 && !model.hasChildren(homeFile))
229 homeFile = model.index(++i, 0, home);
230 view.setRootIndex(home);
231 QCOMPARE(view.HorizontalOffset(), 0);
232 QCOMPARE(view.rootIndex(), home);
233 QVERIFY(!view.visualRect(drive).isValid());
234 QVERIFY(!view.visualRect(home).isValid());
235 if (homeFile.isValid())
236 QVERIFY(view.visualRect(homeFile).isValid());
237
238 // set root when there already is one and everything should still be ok
239 view.setRootIndex(home);
240 view.setCurrentIndex(homeFile);
241 view.scrollTo(model.index(0,0, homeFile));
242 QCOMPARE(view.HorizontalOffset(), 0);
243 QCOMPARE(view.rootIndex(), home);
244 QVERIFY(!view.visualRect(drive).isValid());
245 QVERIFY(!view.visualRect(home).isValid());
246 if (homeFile.isValid())
247 QVERIFY(view.visualRect(homeFile).isValid());
248
249 //
250 homeFile = model.thirdLevel();
251 home = homeFile.parent();
252 view.setRootIndex(home);
253 view.setCurrentIndex(homeFile);
254 view.show();
255 i = 0;
256 QModelIndex two = model.index(0, 0, homeFile);
257 while (i < model.rowCount(homeFile) - 1 && !model.hasChildren(two))
258 two = model.index(++i, 0, homeFile);
259 qApp->processEvents();
260 QTest::qWait(ANIMATION_DELAY);
261 view.setCurrentIndex(two);
262 view.scrollTo(two);
263 QTest::qWait(ANIMATION_DELAY);
264 qApp->processEvents();
265 QVERIFY(two.isValid());
266 QVERIFY(view.HorizontalOffset() != 0);
267
268 view.setRootIndex(homeFile);
269 QCOMPARE(view.HorizontalOffset(), 0);
270 }
271
grips()272 void tst_QColumnView::grips()
273 {
274 QColumnView view;
275 QDirModel model;
276 view.setModel(&model);
277 QCOMPARE(view.resizeGripsVisible(), true);
278
279 view.setResizeGripsVisible(true);
280 QCOMPARE(view.resizeGripsVisible(), true);
281
282 {
283 const QObjectList list = view.viewport()->children();
284 for (int i = 0 ; i < list.count(); ++i) {
285 if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(list.at(i)))
286 QVERIFY(view->cornerWidget() != 0);
287 }
288 }
289 view.setResizeGripsVisible(false);
290 QCOMPARE(view.resizeGripsVisible(), false);
291
292 {
293 const QObjectList list = view.viewport()->children();
294 for (int i = 0 ; i < list.count(); ++i) {
295 if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(list.at(i))) {
296 if (view->isVisible())
297 QVERIFY(view->cornerWidget() == 0);
298 }
299 }
300 }
301
302 view.setResizeGripsVisible(true);
303 QCOMPARE(view.resizeGripsVisible(), true);
304 }
305
isIndexHidden()306 void tst_QColumnView::isIndexHidden()
307 {
308 ColumnView view;
309 QModelIndex idx;
310 QCOMPARE(view.IsIndexHidden(idx), false);
311 QDirModel model;
312 view.setModel(&model);
313 QCOMPARE(view.IsIndexHidden(idx), false);
314 }
315
indexAt()316 void tst_QColumnView::indexAt()
317 {
318 QColumnView view;
319 QCOMPARE(view.indexAt(QPoint(0,0)), QModelIndex());
320 QDirModel model;
321 view.setModel(&model);
322
323 QModelIndex home = model.index(QDir::homePath());
324 QModelIndex homeFile = model.index(0, 0, home);
325 if (!homeFile.isValid())
326 return;
327 view.setRootIndex(home);
328 QRect rect = view.visualRect(QModelIndex());
329 QVERIFY(!rect.isValid());
330 rect = view.visualRect(homeFile);
331 QVERIFY(rect.isValid());
332
333 QModelIndex child;
334 for (int i = 0; i < model.rowCount(home); ++i) {
335 child = model.index(i, 0, home);
336 rect = view.visualRect(child);
337 QVERIFY(rect.isValid());
338 if (i > 0)
339 QVERIFY(rect.top() > 0);
340 QCOMPARE(view.indexAt(rect.center()), child);
341
342 view.selectionModel()->select(child, QItemSelectionModel::SelectCurrent);
343 view.setCurrentIndex(child);
344 qApp->processEvents();
345 QTest::qWait(200);
346
347 // test that the second row doesn't start at 0
348 if (model.rowCount(child) > 0) {
349 child = model.index(0, 0, child);
350 QVERIFY(child.isValid());
351 rect = view.visualRect(child);
352 QVERIFY(rect.isValid());
353 QVERIFY(rect.left() > 0);
354 QCOMPARE(view.indexAt(rect.center()), child);
355 break;
356 }
357 }
358 }
359
scrollContentsBy_data()360 void tst_QColumnView::scrollContentsBy_data()
361 {
362 QTest::addColumn<bool>("reverse");
363 QTest::newRow("normal") << false;
364 QTest::newRow("reverse") << true;
365 }
366
scrollContentsBy()367 void tst_QColumnView::scrollContentsBy()
368 {
369 QFETCH(bool, reverse);
370 if (reverse)
371 qApp->setLayoutDirection(Qt::RightToLeft);
372 ColumnView view;
373 view.ScrollContentsBy(-1, -1);
374 view.ScrollContentsBy(0, 0);
375
376 TreeModel model;
377 view.setModel(&model);
378 view.ScrollContentsBy(0, 0);
379
380 QModelIndex home = model.thirdLevel();
381 view.setCurrentIndex(home);
382 QTest::qWait(ANIMATION_DELAY);
383 view.ScrollContentsBy(0, 0);
384 }
385
scrollTo_data()386 void tst_QColumnView::scrollTo_data()
387 {
388 QTest::addColumn<bool>("reverse");
389 QTest::addColumn<bool>("giveFocus");
390 /// ### add test later for giveFocus == true
391 QTest::newRow("normal") << false << false;
392 QTest::newRow("reverse") << true << false;
393 }
394
scrollTo()395 void tst_QColumnView::scrollTo()
396 {
397 QFETCH(bool, reverse);
398 QFETCH(bool, giveFocus);
399 if (reverse)
400 qApp->setLayoutDirection(Qt::RightToLeft);
401 QWidget topLevel;
402 ColumnView view(&topLevel);
403 view.resize(200, 200);
404 topLevel.show();
405 view.scrollTo(QModelIndex(), QAbstractItemView::EnsureVisible);
406 QCOMPARE(view.HorizontalOffset(), 0);
407
408 TreeModel model;
409 view.setModel(&model);
410 view.scrollTo(QModelIndex(), QAbstractItemView::EnsureVisible);
411
412 QModelIndex home;
413 home = model.index(0, 0, home);
414 home = model.index(0, 0, home);
415 home = model.index(0, 0, home);
416 view.scrollTo(home, QAbstractItemView::EnsureVisible);
417 QModelIndex homeFile = model.index(0, 0, home);
418 view.setRootIndex(home);
419
420 QModelIndex index = model.index(0, 0, home);
421 view.scrollTo(index, QAbstractItemView::EnsureVisible);
422 QCOMPARE(view.HorizontalOffset(), 0);
423
424 // Embedded requires that at least one widget have focus
425 QWidget w;
426 w.show();
427
428 if (giveFocus)
429 view.setFocus(Qt::OtherFocusReason);
430 else
431 view.clearFocus();
432
433 qApp->processEvents();
434 QTRY_COMPARE(view.hasFocus(), giveFocus);
435 // scroll to the right
436 int level = 0;
437 int last = view.HorizontalOffset();
438 while(model.hasChildren(index) && level < 5) {
439 view.setCurrentIndex(index);
440 QTest::qWait(ANIMATION_DELAY);
441 view.scrollTo(index, QAbstractItemView::EnsureVisible);
442 QTest::qWait(ANIMATION_DELAY);
443 qApp->processEvents();
444 index = model.index(0, 0, index);
445 level++;
446 if (level >= 2) {
447 if (!reverse) {
448 QTRY_VERIFY(view.HorizontalOffset() < 0);
449 QTRY_VERIFY(last > view.HorizontalOffset());
450 } else {
451 QTRY_VERIFY(view.HorizontalOffset() > 0);
452 QTRY_VERIFY(last < view.HorizontalOffset());
453 }
454 }
455 last = view.HorizontalOffset();
456 }
457
458 // scroll to the left
459 int start = level;
460 while(index.parent().isValid() && index != view.rootIndex()) {
461 view.setCurrentIndex(index);
462 QTest::qWait(ANIMATION_DELAY);
463 view.scrollTo(index, QAbstractItemView::EnsureVisible);
464 QTest::qWait(ANIMATION_DELAY);
465 index = index.parent();
466 if (start != level) {
467 if (!reverse)
468 QTRY_VERIFY(last < view.HorizontalOffset());
469 else
470 QTRY_VERIFY(last > view.HorizontalOffset());
471 }
472 level--;
473 last = view.HorizontalOffset();
474 }
475 // It shouldn't automatically steal focus if it doesn't have it
476 QTRY_COMPARE(view.hasFocus(), giveFocus);
477
478 // Try scrolling to something that is above the root index
479 home = model.index(0, 0, QModelIndex());
480 QModelIndex temp = model.index(1, 0, home);
481 home = model.index(0, 0, home);
482 home = model.index(0, 0, home);
483 view.setRootIndex(home);
484 view.scrollTo(model.index(0, 0, home));
485 QTest::qWait(ANIMATION_DELAY);
486 view.scrollTo(temp);
487 }
488
moveCursor_data()489 void tst_QColumnView::moveCursor_data()
490 {
491 QTest::addColumn<bool>("reverse");
492 QTest::newRow("normal") << false;
493 QTest::newRow("reverse") << true;
494 }
495
moveCursor()496 void tst_QColumnView::moveCursor()
497 {
498 QFETCH(bool, reverse);
499 if (reverse)
500 qApp->setLayoutDirection(Qt::RightToLeft);
501 ColumnView view;
502
503 // don't crash
504 view.MoveCursor(ColumnView::MoveUp, Qt::NoModifier);
505
506 // don't do anything
507 QCOMPARE(view.MoveCursor(ColumnView::MoveEnd, Qt::NoModifier), QModelIndex());
508
509 QDirModel model;
510 view.setModel(&model);
511 QModelIndex home = model.index(QDir::homePath());
512 QModelIndex ci = view.currentIndex();
513 QCOMPARE(view.MoveCursor(ColumnView::MoveUp, Qt::NoModifier), QModelIndex());
514 QCOMPARE(view.MoveCursor(ColumnView::MoveDown, Qt::NoModifier), QModelIndex());
515
516 // left at root
517 view.setCurrentIndex(model.index(0,0));
518 ColumnView::PublicCursorAction action = reverse ? ColumnView::MoveRight : ColumnView::MoveLeft;
519 QCOMPARE(view.MoveCursor(action, Qt::NoModifier), model.index(0,0));
520
521 // left shouldn't move up
522 int i = 0;
523 ci = model.index(0, 0);
524 while (i < model.rowCount() - 1 && !model.hasChildren(ci))
525 ci = model.index(++i, 0);
526 QVERIFY(model.hasChildren(ci));
527 view.setCurrentIndex(ci);
528 action = reverse ? ColumnView::MoveRight : ColumnView::MoveLeft;
529 QCOMPARE(view.MoveCursor(action, Qt::NoModifier), ci);
530
531 // now move to the left (i.e. move over one column)
532 view.setCurrentIndex(home);
533 QCOMPARE(view.MoveCursor(action, Qt::NoModifier), home.parent());
534
535 // right
536 action = reverse ? ColumnView::MoveLeft : ColumnView::MoveRight;
537 view.setCurrentIndex(ci);
538 QModelIndex mc = view.MoveCursor(action, Qt::NoModifier);
539 QCOMPARE(mc, model.index(0,0, ci));
540
541 // next one should move down
542 QModelIndex idx = model.index(0, 0, ci);
543 while (model.hasChildren(idx) && model.rowCount(ci) > idx.row() + 1)
544 idx = idx.sibling(idx.row() + 1, idx.column());
545 view.setCurrentIndex(idx);
546 mc = view.MoveCursor(action, Qt::NoModifier);
547 QCOMPARE(mc, idx.sibling(idx.row() + 1, idx.column()));
548 }
549
selectAll()550 void tst_QColumnView::selectAll()
551 {
552 ColumnView view;
553 view.selectAll();
554
555 QDirModel model;
556 view.setModel(&model);
557 view.selectAll();
558 QVERIFY(view.selectionModel()->selectedIndexes().count() >= 0);
559
560 QModelIndex home = model.index(QDir::homePath());
561 view.setCurrentIndex(home);
562 view.selectAll();
563 QVERIFY(view.selectionModel()->selectedIndexes().count() > 0);
564
565 QModelIndex file;
566 for (int i = 0; i < model.rowCount(home); ++i)
567 if (!model.hasChildren(model.index(i, 0, home))) {
568 file = model.index(i, 0, home);
569 break;
570 }
571 view.setCurrentIndex(file);
572 view.selectAll();
573 QVERIFY(view.selectionModel()->selectedIndexes().count() > 0);
574
575 view.setCurrentIndex(QModelIndex());
576 QVERIFY(view.selectionModel()->selectedIndexes().count() == 0);
577 }
578
clicked()579 void tst_QColumnView::clicked()
580 {
581 ColumnView view;
582
583 QDirModel model;
584 view.setModel(&model);
585 view.resize(800,300);
586 view.show();
587
588 QModelIndex home = model.index(QDir::homePath());
589 QVERIFY(home.isValid());
590 view.setCurrentIndex(home);
591 QTest::qWait(ANIMATION_DELAY);
592
593 QModelIndex parent = home.parent();
594 QVERIFY(parent.isValid());
595
596 qRegisterMetaType<QModelIndex>("QModelIndex");
597 QSignalSpy clickedSpy(&view, SIGNAL(clicked(const QModelIndex &)));
598
599 QPoint localPoint = view.visualRect(home).center();
600 QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, localPoint);
601 QCOMPARE(clickedSpy.count(), 1);
602 qApp->processEvents();
603
604 if (sizeof(qreal) != sizeof(double)) {
605 QSKIP("Skipped due to rounding errors", SkipAll);
606 }
607
608 for (int i = 0; i < view.createdColumns.count(); ++i) {
609 QAbstractItemView *column = view.createdColumns.at(i);
610 if (column && column->selectionModel() && (column->rootIndex() == home))
611 QVERIFY(column->selectionModel()->selectedIndexes().isEmpty());
612 }
613 }
614
selectedColumns()615 void tst_QColumnView::selectedColumns()
616 {
617 ColumnView view;
618 QDirModel model;
619 view.setModel(&model);
620 view.resize(800,300);
621 view.show();
622
623 QModelIndex home = model.index(QDir::homePath());
624 view.setCurrentIndex(home);
625
626 QTest::qWait(ANIMATION_DELAY);
627
628 for (int i = 0; i < view.createdColumns.count(); ++i) {
629 QAbstractItemView *column = view.createdColumns.at(i);
630 if (!column)
631 continue;
632 if (!column->rootIndex().isValid() || column->rootIndex() == home)
633 continue;
634 QTRY_VERIFY(column->currentIndex().isValid());
635 }
636 }
637
setSelection()638 void tst_QColumnView::setSelection()
639 {
640 ColumnView view;
641 // shouldn't do anything, it falls to the columns to handle this
642 QRect r;
643 view.setSelection(r, QItemSelectionModel::NoUpdate);
644 }
645
setSelectionModel()646 void tst_QColumnView::setSelectionModel()
647 {
648 ColumnView view;
649 QDirModel model;
650 view.setModel(&model);
651 view.show();
652
653 QModelIndex home = model.index(QDir::homePath());
654 view.setCurrentIndex(home);
655 QTest::qWait(ANIMATION_DELAY);
656
657 QItemSelectionModel *selectionModel = new QItemSelectionModel(&model);
658 view.setSelectionModel(selectionModel);
659
660 bool found = false;
661 for (int i = 0; i < view.createdColumns.count(); ++i) {
662 if (view.createdColumns.at(i)->selectionModel() == selectionModel) {
663 found = true;
664 break;
665 }
666 }
667 QVERIFY(found);
668 }
669
visualRegionForSelection()670 void tst_QColumnView::visualRegionForSelection()
671 {
672 ColumnView view;
673 QItemSelection emptyItemSelection;
674 QCOMPARE(QRegion(), view.visualRegionForSelection(emptyItemSelection));
675
676 // a region that isn't empty
677 QDirModel model;
678 view.setModel(&model);
679
680 // On Windows CE the home directory might actually be empty.
681 #ifndef Q_OS_WINCE
682 QString location = QDir::homePath();
683 #else
684 QString location = QLatin1String("/Windows");
685 #endif
686
687 QModelIndex home = model.index(location);
688 QVERIFY(model.rowCount(home) > 1);
689 QItemSelection itemSelection(model.index(0, 0, home), model.index(model.rowCount(home) - 1, 0, home));
690 QVERIFY(QRegion() != view.visualRegionForSelection(itemSelection));
691 }
692
moveGrip_basic()693 void tst_QColumnView::moveGrip_basic()
694 {
695 QColumnView view;
696 QColumnViewGrip *grip = new QColumnViewGrip(&view);
697 QSignalSpy spy(grip, SIGNAL(gripMoved(int)));
698 view.setCornerWidget(grip);
699 int oldX = view.width();
700 grip->moveGrip(10);
701 QCOMPARE(oldX + 10, view.width());
702 grip->moveGrip(-10);
703 QCOMPARE(oldX, view.width());
704 grip->moveGrip(-800);
705 QVERIFY(view.width() == 0 || view.width() == 1);
706 grip->moveGrip(800);
707 view.setMinimumWidth(200);
708 grip->moveGrip(-800);
709 QCOMPARE(view.width(), 200);
710 QCOMPARE(spy.count(), 5);
711 }
712
moveGrip_data()713 void tst_QColumnView::moveGrip_data()
714 {
715 QTest::addColumn<bool>("reverse");
716 QTest::newRow("normal") << false;
717 QTest::newRow("reverse") << true;
718 }
719
moveGrip()720 void tst_QColumnView::moveGrip()
721 {
722 QFETCH(bool, reverse);
723 if (reverse)
724 qApp->setLayoutDirection(Qt::RightToLeft);
725 QWidget topLevel;
726 ColumnView view(&topLevel);
727 TreeModel model;
728 view.setModel(&model);
729 QModelIndex home = model.thirdLevel();
730 view.setCurrentIndex(home);
731 view.resize(640, 200);
732 topLevel.show();
733 QTest::qWait(ANIMATION_DELAY);
734
735 int columnNum = view.createdColumns.count() - 2;
736 QVERIFY(columnNum >= 0);
737 QObjectList list = view.createdColumns[columnNum]->children();
738 QColumnViewGrip *grip = 0;
739 for (int i = 0; i < list.count(); ++i) {
740 if ((grip = qobject_cast<QColumnViewGrip *>(list[i]))) {
741 break;
742 }
743 }
744 if (!grip)
745 return;
746
747 QAbstractItemView *column = qobject_cast<QAbstractItemView *>(grip->parent());
748 int oldX = column->width();
749 QCOMPARE(view.columnWidths().value(columnNum), oldX);
750 grip->moveGrip(10);
751 QCOMPARE(view.columnWidths().value(columnNum), (oldX + (reverse ? -10 : 10)));
752 }
753
doubleClick()754 void tst_QColumnView::doubleClick()
755 {
756 QColumnView view;
757 QColumnViewGrip *grip = new QColumnViewGrip(&view);
758 QSignalSpy spy(grip, SIGNAL(gripMoved(int)));
759 view.setCornerWidget(grip);
760 view.resize(200, 200);
761 QCOMPARE(view.width(), 200);
762 QTest::mouseDClick(grip, Qt::LeftButton);
763 QCOMPARE(view.width(), view.sizeHint().width());
764 QCOMPARE(spy.count(), 1);
765 }
766
gripMoved()767 void tst_QColumnView::gripMoved()
768 {
769 QColumnView view;
770 QColumnViewGrip *grip = new QColumnViewGrip(&view);
771 QSignalSpy spy(grip, SIGNAL(gripMoved(int)));
772 view.setCornerWidget(grip);
773 view.move(300, 300);
774 view.resize(200, 200);
775 qApp->processEvents();
776
777 int oldWidth = view.width();
778
779 QTest::mousePress(grip, Qt::LeftButton, 0, QPoint(1,1));
780 //QTest::mouseMove(grip, QPoint(grip->globalX()+50, y));
781
782 QPoint posNew = QPoint(grip->mapToGlobal(QPoint(1,1)).x() + 65, 0);
783 QMouseEvent *event = new QMouseEvent(QEvent::MouseMove, posNew, posNew, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
784 QCoreApplication::postEvent(grip, event);
785 QCoreApplication::processEvents();
786 QTest::mouseRelease(grip, Qt::LeftButton);
787
788 QCOMPARE(spy.count(), 1);
789 QCOMPARE(view.width(), oldWidth + 65);
790 }
791
preview()792 void tst_QColumnView::preview()
793 {
794 QColumnView view;
795 QCOMPARE(view.previewWidget(), (QWidget*)0);
796 TreeModel model;
797 view.setModel(&model);
798 QCOMPARE(view.previewWidget(), (QWidget*)0);
799 QModelIndex home = model.index(0, 0);
800 QVERIFY(home.isValid());
801 QVERIFY(model.hasChildren(home));
802 view.setCurrentIndex(home);
803 QCOMPARE(view.previewWidget(), (QWidget*)0);
804
805 QModelIndex file;
806 QVERIFY(model.rowCount(home) > 0);
807 for (int i = 0; i < model.rowCount(home); ++i) {
808 if (!model.hasChildren(model.index(i, 0, home))) {
809 file = model.index(i, 0, home);
810 break;
811 }
812 }
813 QVERIFY(file.isValid());
814 view.setCurrentIndex(file);
815 QVERIFY(view.previewWidget() != (QWidget*)0);
816
817 QWidget *previewWidget = new QWidget(&view);
818 view.setPreviewWidget(previewWidget);
819 QCOMPARE(view.previewWidget(), previewWidget);
820 QVERIFY(previewWidget->parent() != ((QWidget*)&view));
821 view.setCurrentIndex(home);
822
823 // previewWidget should be marked for deletion
824 QWidget *previewWidget2 = new QWidget(&view);
825 view.setPreviewWidget(previewWidget2);
826 QCOMPARE(view.previewWidget(), previewWidget2);
827 }
828
swapPreview()829 void tst_QColumnView::swapPreview()
830 {
831 // swap the preview widget in updatePreviewWidget
832 QColumnView view;
833 QStringList sl;
834 sl << QLatin1String("test");
835 QStringListModel model(sl);
836 view.setModel(&model);
837 view.setCurrentIndex(view.indexAt(QPoint(1, 1)));
838 connect(&view, SIGNAL(updatePreviewWidget(const QModelIndex &)),
839 this, SLOT(setPreviewWidget()));
840 view.setCurrentIndex(view.indexAt(QPoint(1, 1)));
841 QTest::qWait(ANIMATION_DELAY);
842 qApp->processEvents();
843 }
844
setPreviewWidget()845 void tst_QColumnView::setPreviewWidget()
846 {
847 ((QColumnView*)sender())->setPreviewWidget(new QWidget);
848 }
849
sizes()850 void tst_QColumnView::sizes()
851 {
852 QColumnView view;
853 QCOMPARE(view.columnWidths().count(), 0);
854
855 QList<int> newSizes;
856 newSizes << 10 << 4 << 50 << 6;
857
858 QList<int> visibleSizes;
859 view.setColumnWidths(newSizes);
860 QCOMPARE(view.columnWidths(), visibleSizes);
861
862 QDirModel model;
863 view.setModel(&model);
864 QModelIndex home = model.index(QDir::homePath());
865 view.setCurrentIndex(home);
866
867 QList<int> postSizes = view.columnWidths().mid(0, newSizes.count());
868 QCOMPARE(postSizes, newSizes.mid(0, postSizes.count()));
869
870 QVERIFY(view.columnWidths().count() > 1);
871 QList<int> smallerSizes;
872 smallerSizes << 6;
873 view.setColumnWidths(smallerSizes);
874 QList<int> expectedSizes = newSizes;
875 expectedSizes[0] = 6;
876 postSizes = view.columnWidths().mid(0, newSizes.count());
877 QCOMPARE(postSizes, expectedSizes.mid(0, postSizes.count()));
878 }
879
rowDelegate()880 void tst_QColumnView::rowDelegate()
881 {
882 ColumnView view;
883 QItemDelegate *d = new QItemDelegate;
884 view.setItemDelegateForRow(3, d);
885
886 QDirModel model;
887 view.setModel(&model);
888 for (int i = 0; i < view.createdColumns.count(); ++i) {
889 QAbstractItemView *column = view.createdColumns.at(i);
890 QCOMPARE(column->itemDelegateForRow(3), (QAbstractItemDelegate*)d);
891 }
892 delete d;
893 }
894
resize()895 void tst_QColumnView::resize()
896 {
897 QWidget topLevel;
898 ColumnView view(&topLevel);
899 QDirModel model;
900 view.setModel(&model);
901 view.resize(200, 200);
902
903 topLevel.show();
904 QModelIndex home = model.index(QDir::homePath()).parent();
905 view.setCurrentIndex(home);
906 QTest::qWait(ANIMATION_DELAY);
907 view.resize(200, 300);
908 QTest::qWait(ANIMATION_DELAY);
909
910 QVERIFY(view.horizontalScrollBar()->maximum() != 0);
911 view.resize(view.horizontalScrollBar()->maximum() * 10, 300);
912 QTest::qWait(ANIMATION_DELAY);
913 QVERIFY(view.horizontalScrollBar()->maximum() <= 0);
914 }
915
changeSameColumn()916 void tst_QColumnView::changeSameColumn()
917 {
918 ColumnView view;
919 TreeModel model;
920 view.setModel(&model);
921 QModelIndex second;
922
923 QModelIndex home = model.secondLevel();
924 //index(QDir::homePath());
925 view.setCurrentIndex(home);
926 for (int i = 0; i < model.rowCount(home.parent()); ++i) {
927 QModelIndex idx = model.index(i, 0, home.parent());
928 if (model.hasChildren(idx) && idx != home) {
929 second = idx;
930 break;
931 }
932 }
933 QVERIFY(second.isValid());
934
935 QList<QPointer<QAbstractItemView> > old = view.createdColumns;
936 view.setCurrentIndex(second);
937
938 QCOMPARE(old, view.createdColumns);
939 }
940
parentCurrentIndex_data()941 void tst_QColumnView::parentCurrentIndex_data()
942 {
943 QTest::addColumn<int>("firstRow");
944 QTest::addColumn<int>("secondRow");
945 QTest::newRow("down") << 0 << 1;
946 QTest::newRow("up") << 1 << 0;
947 }
948
parentCurrentIndex()949 void tst_QColumnView::parentCurrentIndex()
950 {
951 QFETCH(int, firstRow);
952 QFETCH(int, secondRow);
953
954 ColumnView view;
955 TreeModel model;
956 view.setModel(&model);
957 view.show();
958
959 QModelIndex first;
960 QModelIndex second;
961 QModelIndex third;
962 first = model.index(0, 0, QModelIndex());
963 second = model.index(firstRow, 0, first);
964 third = model.index(0, 0, second);
965 QVERIFY(first.isValid());
966 QVERIFY(second.isValid());
967 QVERIFY(third.isValid());
968 view.setCurrentIndex(third);
969 QTest::qWait(ANIMATION_DELAY);
970 QTRY_COMPARE(view.createdColumns[0]->currentIndex(), first);
971 QTRY_COMPARE(view.createdColumns[1]->currentIndex(), second);
972 QTRY_COMPARE(view.createdColumns[2]->currentIndex(), third);
973
974 first = model.index(0, 0, QModelIndex());
975 second = model.index(secondRow, 0, first);
976 third = model.index(0, 0, second);
977 QVERIFY(first.isValid());
978 QVERIFY(second.isValid());
979 QVERIFY(third.isValid());
980 view.setCurrentIndex(third);
981 QTest::qWait(ANIMATION_DELAY);
982 QTRY_COMPARE(view.createdColumns[0]->currentIndex(), first);
983 QTRY_COMPARE(view.createdColumns[1]->currentIndex(), second);
984 QTRY_COMPARE(view.createdColumns[2]->currentIndex(), third);
985 }
986
pullRug_data()987 void tst_QColumnView::pullRug_data()
988 {
989 QTest::addColumn<bool>("removeModel");
990 QTest::newRow("model") << true;
991 QTest::newRow("index") << false;
992 }
993
pullRug()994 void tst_QColumnView::pullRug()
995 {
996 QFETCH(bool, removeModel);
997 ColumnView view;
998 TreeModel model;
999 view.setModel(&model);
1000 QModelIndex home = model.thirdLevel();
1001 view.setCurrentIndex(home);
1002 if (removeModel)
1003 view.setModel(0);
1004 else
1005 view.setCurrentIndex(QModelIndex());
1006 QTest::qWait(ANIMATION_DELAY);
1007 // don't crash
1008 }
1009
dynamicModelChanges()1010 void tst_QColumnView::dynamicModelChanges()
1011 {
1012 struct MyItemDelegate : public QItemDelegate
1013 {
1014 void paint(QPainter *painter,
1015 const QStyleOptionViewItem &option,
1016 const QModelIndex &index) const
1017 {
1018 paintedIndexes += index;
1019 QItemDelegate::paint(painter, option, index);
1020 }
1021
1022 mutable QSet<QModelIndex> paintedIndexes;
1023
1024 } delegate;;
1025 QStandardItemModel model;
1026 ColumnView view;
1027 view.setModel(&model);
1028 view.setItemDelegate(&delegate);
1029 view.show();
1030
1031 QStandardItem *item = new QStandardItem(QLatin1String("item"));
1032 model.appendRow(item);
1033
1034 QTest::qWait(200); //let the time for painting to occur
1035 QCOMPARE(delegate.paintedIndexes.count(), 1);
1036 QCOMPARE(*delegate.paintedIndexes.begin(), model.index(0,0));
1037
1038
1039 }
1040
1041
1042 QTEST_MAIN(tst_QColumnView)
1043 #include "tst_qcolumnview.moc"
1044
1045