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