Update copyright year in license headers.
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick2 / qquickpathview / tst_qquickpathview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <QtQuick/qquickview.h>
44 #include <QtDeclarative/qdeclarativeengine.h>
45 #include <QtDeclarative/qdeclarativecomponent.h>
46 #include <QtDeclarative/qdeclarativecontext.h>
47 #include <QtDeclarative/qdeclarativeexpression.h>
48 #include <QtDeclarative/qdeclarativeincubator.h>
49 #include <QtQuick/private/qquickpathview_p.h>
50 #include <QtQuick/private/qdeclarativepath_p.h>
51 #include <QtQuick/private/qquicktext_p.h>
52 #include <QtQuick/private/qquickrectangle_p.h>
53 #include <QtDeclarative/private/qdeclarativelistmodel_p.h>
54 #include <QtDeclarative/private/qdeclarativevaluetype_p.h>
55 #include <QAbstractListModel>
56 #include <QStringListModel>
57 #include <QStandardItemModel>
58 #include <QFile>
59
60 #include "../../shared/util.h"
61
62 static void initStandardTreeModel(QStandardItemModel *model)
63 {
64     QStandardItem *item;
65     item = new QStandardItem(QLatin1String("Row 1 Item"));
66     model->insertRow(0, item);
67
68     item = new QStandardItem(QLatin1String("Row 2 Item"));
69     item->setCheckable(true);
70     model->insertRow(1, item);
71
72     QStandardItem *childItem = new QStandardItem(QLatin1String("Row 2 Child Item"));
73     item->setChild(0, childItem);
74
75     item = new QStandardItem(QLatin1String("Row 3 Item"));
76     item->setIcon(QIcon());
77     model->insertRow(2, item);
78 }
79
80
81 class tst_QQuickPathView : public QDeclarativeDataTest
82 {
83     Q_OBJECT
84 public:
85     tst_QQuickPathView();
86
87 private slots:
88     void initValues();
89     void items();
90     void dataModel();
91     void pathview2();
92     void pathview3();
93     void insertModel_data();
94     void insertModel();
95     void removeModel_data();
96     void removeModel();
97     void moveModel_data();
98     void moveModel();
99     void path();
100     void pathMoved();
101     void setCurrentIndex();
102     void resetModel();
103     void propertyChanges();
104     void pathChanges();
105     void componentChanges();
106     void modelChanges();
107     void pathUpdateOnStartChanged();
108     void package();
109     void emptyModel();
110     void closed();
111     void pathUpdate();
112     void visualDataModel();
113     void undefinedPath();
114     void mouseDrag();
115     void treeModel();
116     void changePreferredHighlight();
117     void missingPercent();
118     void creationContext();
119     void currentOffsetOnInsertion();
120     void asynchronous();
121
122 private:
123     QQuickView *createView();
124     template<typename T>
125     T *findItem(QQuickItem *parent, const QString &objectName, int index=-1);
126     template<typename T>
127     QList<T*> findItems(QQuickItem *parent, const QString &objectName);
128 };
129
130 class TestObject : public QObject
131 {
132     Q_OBJECT
133
134     Q_PROPERTY(bool error READ error WRITE setError)
135     Q_PROPERTY(bool useModel READ useModel NOTIFY useModelChanged)
136     Q_PROPERTY(int pathItemCount READ pathItemCount NOTIFY pathItemCountChanged)
137
138 public:
139     TestObject() : QObject(), mError(true), mUseModel(true), mPathItemCount(-1) {}
140
141     bool error() const { return mError; }
142     void setError(bool err) { mError = err; }
143
144     bool useModel() const { return mUseModel; }
145     void setUseModel(bool use) { mUseModel = use; emit useModelChanged(); }
146
147     int pathItemCount() const { return mPathItemCount; }
148     void setPathItemCount(int count) { mPathItemCount = count; emit pathItemCountChanged(); }
149
150 signals:
151     void useModelChanged();
152     void pathItemCountChanged();
153
154 private:
155     bool mError;
156     bool mUseModel;
157     int mPathItemCount;
158 };
159
160 class TestModel : public QAbstractListModel
161 {
162 public:
163     enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
164
165     TestModel(QObject *parent=0) : QAbstractListModel(parent) {
166         QHash<int, QByteArray> roles;
167         roles[Name] = "name";
168         roles[Number] = "number";
169         setRoleNames(roles);
170     }
171
172     int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
173     QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
174         QVariant rv;
175         if (role == Name)
176             rv = list.at(index.row()).first;
177         else if (role == Number)
178             rv = list.at(index.row()).second;
179
180         return rv;
181     }
182
183     int count() const { return rowCount(); }
184     QString name(int index) const { return list.at(index).first; }
185     QString number(int index) const { return list.at(index).second; }
186
187     void addItem(const QString &name, const QString &number) {
188         beginInsertRows(QModelIndex(), list.count(), list.count());
189         list.append(QPair<QString,QString>(name, number));
190         endInsertRows();
191     }
192
193     void insertItem(int index, const QString &name, const QString &number) {
194         beginInsertRows(QModelIndex(), index, index);
195         list.insert(index, QPair<QString,QString>(name, number));
196         endInsertRows();
197     }
198
199     void insertItems(int index, const QList<QPair<QString, QString> > &items) {
200         beginInsertRows(QModelIndex(), index, index+items.count()-1);
201         for (int i=0; i<items.count(); i++)
202             list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second));
203         endInsertRows();
204     }
205
206     void removeItem(int index) {
207         beginRemoveRows(QModelIndex(), index, index);
208         list.removeAt(index);
209         endRemoveRows();
210     }
211
212     void removeItems(int index, int count) {
213         emit beginRemoveRows(QModelIndex(), index, index+count-1);
214         while (count--)
215             list.removeAt(index);
216         emit endRemoveRows();
217     }
218
219     void moveItem(int from, int to) {
220         beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
221         list.move(from, to);
222         endMoveRows();
223     }
224
225     void moveItems(int from, int to, int count) {
226         beginMoveRows(QModelIndex(), from, from+count-1, QModelIndex(), to > from ? to+count : to);
227         move(from, to, count);
228         endMoveRows();
229     }
230
231     void modifyItem(int idx, const QString &name, const QString &number) {
232         list[idx] = QPair<QString,QString>(name, number);
233         emit dataChanged(index(idx,0), index(idx,0));
234     }
235
236     void move(int from, int to, int n)
237     {
238         if (from > to) {
239             // Only move forwards - flip if backwards moving
240             int tfrom = from;
241             int tto = to;
242             from = tto;
243             to = tto+n;
244             n = tfrom-tto;
245         }
246         if (n == 1) {
247             list.move(from, to);
248         } else {
249             QList<QPair<QString,QString> > replaced;
250             int i=0;
251             QList<QPair<QString,QString> >::ConstIterator it=list.begin(); it += from+n;
252             for (; i<to-from; ++i,++it)
253                 replaced.append(*it);
254             i=0;
255             it=list.begin(); it += from;
256             for (; i<n; ++i,++it)
257                 replaced.append(*it);
258             QList<QPair<QString,QString> >::ConstIterator f=replaced.begin();
259             QList<QPair<QString,QString> >::Iterator t=list.begin(); t += from;
260             for (; f != replaced.end(); ++f, ++t)
261                 *t = *f;
262         }
263     }
264
265 private:
266     QList<QPair<QString,QString> > list;
267 };
268
269
270 tst_QQuickPathView::tst_QQuickPathView()
271 {
272 }
273
274 void tst_QQuickPathView::initValues()
275 {
276     QDeclarativeEngine engine;
277     QDeclarativeComponent c(&engine, testFileUrl("pathview1.qml"));
278     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
279
280     QVERIFY(obj != 0);
281     QVERIFY(obj->path() == 0);
282     QVERIFY(obj->delegate() == 0);
283     QCOMPARE(obj->model(), QVariant());
284     QCOMPARE(obj->currentIndex(), 0);
285     QCOMPARE(obj->offset(), 0.);
286     QCOMPARE(obj->preferredHighlightBegin(), 0.);
287     QCOMPARE(obj->dragMargin(), 0.);
288     QCOMPARE(obj->count(), 0);
289     QCOMPARE(obj->pathItemCount(), -1);
290
291     delete obj;
292 }
293
294 void tst_QQuickPathView::items()
295 {
296     QQuickView *canvas = createView();
297
298     TestModel model;
299     model.addItem("Fred", "12345");
300     model.addItem("John", "2345");
301     model.addItem("Bob", "54321");
302     model.addItem("Bill", "4321");
303
304     QDeclarativeContext *ctxt = canvas->rootContext();
305     ctxt->setContextProperty("testModel", &model);
306
307     canvas->setSource(testFileUrl("pathview0.qml"));
308     qApp->processEvents();
309
310     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
311     QVERIFY(pathview != 0);
312
313     QCOMPARE(pathview->count(), model.count());
314     QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count());
315     QCOMPARE(pathview->childItems().count(), model.count()+1); // assumes all are visible, including highlight
316
317     for (int i = 0; i < model.count(); ++i) {
318         QQuickText *name = findItem<QQuickText>(pathview, "textName", i);
319         QVERIFY(name != 0);
320         QCOMPARE(name->text(), model.name(i));
321         QQuickText *number = findItem<QQuickText>(pathview, "textNumber", i);
322         QVERIFY(number != 0);
323         QCOMPARE(number->text(), model.number(i));
324     }
325
326     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
327     QVERIFY(path);
328
329     QVERIFY(pathview->highlightItem());
330     QPointF start = path->pointAt(0.0);
331     QPointF offset;
332     offset.setX(pathview->highlightItem()->width()/2);
333     offset.setY(pathview->highlightItem()->height()/2);
334     QCOMPARE(pathview->highlightItem()->pos() + offset, start);
335
336     delete canvas;
337 }
338
339 void tst_QQuickPathView::pathview2()
340 {
341     QDeclarativeEngine engine;
342     QDeclarativeComponent c(&engine, testFileUrl("pathview2.qml"));
343     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
344
345     QVERIFY(obj != 0);
346     QVERIFY(obj->path() != 0);
347     QVERIFY(obj->delegate() != 0);
348     QVERIFY(obj->model() != QVariant());
349     QCOMPARE(obj->currentIndex(), 0);
350     QCOMPARE(obj->offset(), 0.);
351     QCOMPARE(obj->preferredHighlightBegin(), 0.);
352     QCOMPARE(obj->dragMargin(), 0.);
353     QCOMPARE(obj->count(), 8);
354     QCOMPARE(obj->pathItemCount(), 10);
355
356     delete obj;
357 }
358
359 void tst_QQuickPathView::pathview3()
360 {
361     QDeclarativeEngine engine;
362     QDeclarativeComponent c(&engine, testFileUrl("pathview3.qml"));
363     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
364
365     QVERIFY(obj != 0);
366     QVERIFY(obj->path() != 0);
367     QVERIFY(obj->delegate() != 0);
368     QVERIFY(obj->model() != QVariant());
369     QCOMPARE(obj->currentIndex(), 0);
370     QCOMPARE(obj->offset(), 1.0);
371     QCOMPARE(obj->preferredHighlightBegin(), 0.5);
372     QCOMPARE(obj->dragMargin(), 24.);
373     QCOMPARE(obj->count(), 8);
374     QCOMPARE(obj->pathItemCount(), 4);
375
376     delete obj;
377 }
378
379 void tst_QQuickPathView::insertModel_data()
380 {
381     QTest::addColumn<int>("mode");
382     QTest::addColumn<int>("idx");
383     QTest::addColumn<int>("count");
384     QTest::addColumn<qreal>("offset");
385
386     // We have 8 items, with currentIndex == 4
387     QTest::newRow("insert after current")
388         << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 5.;
389     QTest::newRow("insert before current")
390         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4.;
391     QTest::newRow("insert multiple after current")
392         << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 6.;
393     QTest::newRow("insert multiple before current")
394         << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4.;
395     QTest::newRow("insert at end")
396         << int(QQuickPathView::StrictlyEnforceRange) << 8 << 1 << 5.;
397     QTest::newRow("insert at beginning")
398         << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4.;
399     QTest::newRow("insert at current")
400         << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 4.;
401
402     QTest::newRow("no range - insert after current")
403         << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 5.;
404     QTest::newRow("no range - insert before current")
405         << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4.;
406     QTest::newRow("no range - insert multiple after current")
407         << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 6.;
408     QTest::newRow("no range - insert multiple before current")
409         << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4.;
410     QTest::newRow("no range - insert at end")
411         << int(QQuickPathView::NoHighlightRange) << 8 << 1 << 5.;
412     QTest::newRow("no range - insert at beginning")
413         << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4.;
414     QTest::newRow("no range - insert at current")
415         << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4.;
416 }
417
418 void tst_QQuickPathView::insertModel()
419 {
420     QFETCH(int, mode);
421     QFETCH(int, idx);
422     QFETCH(int, count);
423     QFETCH(qreal, offset);
424
425     QQuickView *canvas = createView();
426     canvas->show();
427
428     TestModel model;
429     model.addItem("Ben", "12345");
430     model.addItem("Bohn", "2345");
431     model.addItem("Bob", "54321");
432     model.addItem("Bill", "4321");
433     model.addItem("Jinny", "679");
434     model.addItem("Milly", "73378");
435     model.addItem("Jimmy", "3535");
436     model.addItem("Barb", "9039");
437
438     QDeclarativeContext *ctxt = canvas->rootContext();
439     ctxt->setContextProperty("testModel", &model);
440
441     canvas->setSource(testFileUrl("pathview0.qml"));
442     qApp->processEvents();
443
444     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
445     QVERIFY(pathview != 0);
446
447     pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
448
449     pathview->setCurrentIndex(4);
450     if (mode == QQuickPathView::StrictlyEnforceRange)
451         QTRY_COMPARE(pathview->offset(), 4.0);
452     else
453         pathview->setOffset(4);
454
455     QList<QPair<QString, QString> > items;
456     for (int i = 0; i < count; ++i)
457         items.append(qMakePair(QString("New"), QString::number(i)));
458
459     model.insertItems(idx, items);
460     QTRY_COMPARE(pathview->offset(), offset);
461
462     delete canvas;
463 }
464
465 void tst_QQuickPathView::removeModel_data()
466 {
467     QTest::addColumn<int>("mode");
468     QTest::addColumn<int>("idx");
469     QTest::addColumn<int>("count");
470     QTest::addColumn<qreal>("offset");
471
472     // We have 8 items, with currentIndex == 4
473     QTest::newRow("remove after current")
474         << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 3.;
475     QTest::newRow("remove before current")
476         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4.;
477     QTest::newRow("remove multiple after current")
478         << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 2.;
479     QTest::newRow("remove multiple before current")
480         << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4.;
481     QTest::newRow("remove last")
482         << int(QQuickPathView::StrictlyEnforceRange) << 7 << 1 << 3.;
483     QTest::newRow("remove first")
484         << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4.;
485     QTest::newRow("remove current")
486         << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 3.;
487
488     QTest::newRow("no range - remove after current")
489         << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 3.;
490     QTest::newRow("no range - remove before current")
491         << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4.;
492     QTest::newRow("no range - remove multiple after current")
493         << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 2.;
494     QTest::newRow("no range - remove multiple before current")
495         << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4.;
496     QTest::newRow("no range - remove last")
497         << int(QQuickPathView::NoHighlightRange) << 7 << 1 << 3.;
498     QTest::newRow("no range - remove first")
499         << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4.;
500     QTest::newRow("no range - remove current offset")
501         << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4.;
502 }
503
504 void tst_QQuickPathView::removeModel()
505 {
506     QFETCH(int, mode);
507     QFETCH(int, idx);
508     QFETCH(int, count);
509     QFETCH(qreal, offset);
510
511     QQuickView *canvas = createView();
512     canvas->show();
513
514     TestModel model;
515     model.addItem("Ben", "12345");
516     model.addItem("Bohn", "2345");
517     model.addItem("Bob", "54321");
518     model.addItem("Bill", "4321");
519     model.addItem("Jinny", "679");
520     model.addItem("Milly", "73378");
521     model.addItem("Jimmy", "3535");
522     model.addItem("Barb", "9039");
523
524     QDeclarativeContext *ctxt = canvas->rootContext();
525     ctxt->setContextProperty("testModel", &model);
526
527     canvas->setSource(testFileUrl("pathview0.qml"));
528     qApp->processEvents();
529
530     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
531     QVERIFY(pathview != 0);
532
533     pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
534
535     pathview->setCurrentIndex(4);
536     if (mode == QQuickPathView::StrictlyEnforceRange)
537         QTRY_COMPARE(pathview->offset(), 4.0);
538     else
539         pathview->setOffset(4);
540
541     model.removeItems(idx, count);
542     QTRY_COMPARE(pathview->offset(), offset);
543
544     delete canvas;
545 }
546
547
548 void tst_QQuickPathView::moveModel_data()
549 {
550     QTest::addColumn<int>("mode");
551     QTest::addColumn<int>("from");
552     QTest::addColumn<int>("to");
553     QTest::addColumn<int>("count");
554     QTest::addColumn<qreal>("offset");
555
556     // We have 8 items, with currentIndex == 4
557     QTest::newRow("move after current")
558         << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 1 << 4.;
559     QTest::newRow("move before current")
560         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 3 << 1 << 4.;
561     QTest::newRow("move before current to after")
562         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 6 << 1 << 5.;
563     QTest::newRow("move multiple after current")
564         << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 2 << 4.;
565     QTest::newRow("move multiple before current")
566         << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 2 << 4.;
567     QTest::newRow("move before current to end")
568         << int(QQuickPathView::StrictlyEnforceRange) << 2 << 7 << 1 << 5.;
569     QTest::newRow("move last to beginning")
570         << int(QQuickPathView::StrictlyEnforceRange) << 7 << 0 << 1 << 3.;
571     QTest::newRow("move current")
572         << int(QQuickPathView::StrictlyEnforceRange) << 4 << 6 << 1 << 2.;
573
574     QTest::newRow("no range - move after current")
575         << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 1 << 4.;
576     QTest::newRow("no range - move before current")
577         << int(QQuickPathView::NoHighlightRange) << 2 << 3 << 1 << 4.;
578     QTest::newRow("no range - move before current to after")
579         << int(QQuickPathView::NoHighlightRange) << 2 << 6 << 1 << 5.;
580     QTest::newRow("no range - move multiple after current")
581         << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 2 << 4.;
582     QTest::newRow("no range - move multiple before current")
583         << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 2 << 4.;
584     QTest::newRow("no range - move before current to end")
585         << int(QQuickPathView::NoHighlightRange) << 2 << 7 << 1 << 5.;
586     QTest::newRow("no range - move last to beginning")
587         << int(QQuickPathView::NoHighlightRange) << 7 << 0 << 1 << 3.;
588     QTest::newRow("no range - move current")
589         << int(QQuickPathView::NoHighlightRange) << 4 << 6 << 1 << 4.;
590     QTest::newRow("no range - move multiple incl. current")
591         << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 5 << 4.;
592 }
593
594 void tst_QQuickPathView::moveModel()
595 {
596     QFETCH(int, mode);
597     QFETCH(int, from);
598     QFETCH(int, to);
599     QFETCH(int, count);
600     QFETCH(qreal, offset);
601
602     QQuickView *canvas = createView();
603     canvas->show();
604
605     TestModel model;
606     model.addItem("Ben", "12345");
607     model.addItem("Bohn", "2345");
608     model.addItem("Bob", "54321");
609     model.addItem("Bill", "4321");
610     model.addItem("Jinny", "679");
611     model.addItem("Milly", "73378");
612     model.addItem("Jimmy", "3535");
613     model.addItem("Barb", "9039");
614
615     QDeclarativeContext *ctxt = canvas->rootContext();
616     ctxt->setContextProperty("testModel", &model);
617
618     canvas->setSource(testFileUrl("pathview0.qml"));
619     qApp->processEvents();
620
621     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
622     QVERIFY(pathview != 0);
623
624     pathview->setHighlightRangeMode((QQuickPathView::HighlightRangeMode)mode);
625
626     pathview->setCurrentIndex(4);
627     if (mode == QQuickPathView::StrictlyEnforceRange)
628         QTRY_COMPARE(pathview->offset(), 4.0);
629     else
630         pathview->setOffset(4);
631
632     model.moveItems(from, to, count);
633     QTRY_COMPARE(pathview->offset(), offset);
634
635     delete canvas;
636 }
637
638 void tst_QQuickPathView::path()
639 {
640     QDeclarativeEngine engine;
641     QDeclarativeComponent c(&engine, testFileUrl("pathtest.qml"));
642     QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
643
644     QVERIFY(obj != 0);
645     QCOMPARE(obj->startX(), 120.);
646     QCOMPARE(obj->startY(), 100.);
647     QVERIFY(obj->path() != QPainterPath());
648
649     QDeclarativeListReference list(obj, "pathElements");
650     QCOMPARE(list.count(), 5);
651
652     QDeclarativePathAttribute* attr = qobject_cast<QDeclarativePathAttribute*>(list.at(0));
653     QVERIFY(attr != 0);
654     QCOMPARE(attr->name(), QString("scale"));
655     QCOMPARE(attr->value(), 1.0);
656
657     QDeclarativePathQuad* quad = qobject_cast<QDeclarativePathQuad*>(list.at(1));
658     QVERIFY(quad != 0);
659     QCOMPARE(quad->x(), 120.);
660     QCOMPARE(quad->y(), 25.);
661     QCOMPARE(quad->controlX(), 260.);
662     QCOMPARE(quad->controlY(), 75.);
663
664     QDeclarativePathPercent* perc = qobject_cast<QDeclarativePathPercent*>(list.at(2));
665     QVERIFY(perc != 0);
666     QCOMPARE(perc->value(), 0.3);
667
668     QDeclarativePathLine* line = qobject_cast<QDeclarativePathLine*>(list.at(3));
669     QVERIFY(line != 0);
670     QCOMPARE(line->x(), 120.);
671     QCOMPARE(line->y(), 100.);
672
673     QDeclarativePathCubic* cubic = qobject_cast<QDeclarativePathCubic*>(list.at(4));
674     QVERIFY(cubic != 0);
675     QCOMPARE(cubic->x(), 180.);
676     QCOMPARE(cubic->y(), 0.);
677     QCOMPARE(cubic->control1X(), -10.);
678     QCOMPARE(cubic->control1Y(), 90.);
679     QCOMPARE(cubic->control2X(), 210.);
680     QCOMPARE(cubic->control2Y(), 90.);
681
682     delete obj;
683 }
684
685 void tst_QQuickPathView::dataModel()
686 {
687     QQuickView *canvas = createView();
688     canvas->show();
689
690     QDeclarativeContext *ctxt = canvas->rootContext();
691     TestObject *testObject = new TestObject;
692     ctxt->setContextProperty("testObject", testObject);
693
694     TestModel model;
695     model.addItem("red", "1");
696     model.addItem("green", "2");
697     model.addItem("blue", "3");
698     model.addItem("purple", "4");
699     model.addItem("gray", "5");
700     model.addItem("brown", "6");
701     model.addItem("yellow", "7");
702     model.addItem("thistle", "8");
703     model.addItem("cyan", "9");
704     model.addItem("peachpuff", "10");
705     model.addItem("powderblue", "11");
706     model.addItem("gold", "12");
707     model.addItem("sandybrown", "13");
708
709     ctxt->setContextProperty("testData", &model);
710
711     canvas->setSource(testFileUrl("datamodel.qml"));
712     qApp->processEvents();
713
714     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
715     QVERIFY(pathview != 0);
716
717     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
718     QVERIFY(testObject->error() == false);
719
720     QQuickItem *item = findItem<QQuickItem>(pathview, "wrapper", 0);
721     QVERIFY(item);
722     QCOMPARE(item->x(), 110.0);
723     QCOMPARE(item->y(), 10.0);
724
725     model.insertItem(4, "orange", "10");
726     QTest::qWait(100);
727
728     QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
729     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 14);
730
731     QVERIFY(pathview->currentIndex() == 0);
732     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 0));
733
734     QQuickText *text = findItem<QQuickText>(pathview, "myText", 4);
735     QVERIFY(text);
736     QCOMPARE(text->text(), model.name(4));
737
738     model.removeItem(2);
739     QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
740     text = findItem<QQuickText>(pathview, "myText", 2);
741     QVERIFY(text);
742     QCOMPARE(text->text(), model.name(2));
743     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 0));
744
745     testObject->setPathItemCount(5);
746     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
747     QVERIFY(testObject->error() == false);
748
749     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
750
751     QQuickRectangle *testItem = findItem<QQuickRectangle>(pathview, "wrapper", 4);
752     QVERIFY(testItem != 0);
753     testItem = findItem<QQuickRectangle>(pathview, "wrapper", 5);
754     QVERIFY(testItem == 0);
755
756     pathview->setCurrentIndex(1);
757     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
758     QTest::qWait(100);
759
760     model.insertItem(2, "pink", "2");
761     QTest::qWait(100);
762
763     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
764     QVERIFY(pathview->currentIndex() == 1);
765     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
766
767     text = findItem<QQuickText>(pathview, "myText", 2);
768     QVERIFY(text);
769     QCOMPARE(text->text(), model.name(2));
770
771     model.removeItem(3);
772     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
773     text = findItem<QQuickText>(pathview, "myText", 3);
774     QVERIFY(text);
775     QCOMPARE(text->text(), model.name(3));
776     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
777
778     model.moveItem(3, 5);
779     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
780     QList<QQuickItem*> items = findItems<QQuickItem>(pathview, "wrapper");
781     foreach (QQuickItem *item, items) {
782         QVERIFY(item->property("onPath").toBool());
783     }
784     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
785
786     // QTBUG-14199
787     pathview->setOffset(7);
788     pathview->setOffset(0);
789     QCOMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
790
791     pathview->setCurrentIndex(model.count()-1);
792     model.removeItem(model.count()-1);
793     QCOMPARE(pathview->currentIndex(), model.count()-1);
794
795     delete canvas;
796     delete testObject;
797 }
798
799 void tst_QQuickPathView::pathMoved()
800 {
801     QQuickView *canvas = createView();
802     canvas->show();
803
804     TestModel model;
805     model.addItem("Ben", "12345");
806     model.addItem("Bohn", "2345");
807     model.addItem("Bob", "54321");
808     model.addItem("Bill", "4321");
809
810     QDeclarativeContext *ctxt = canvas->rootContext();
811     ctxt->setContextProperty("testModel", &model);
812
813     canvas->setSource(testFileUrl("pathview0.qml"));
814     qApp->processEvents();
815
816     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
817     QVERIFY(pathview != 0);
818
819     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
820     QVERIFY(firstItem);
821     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
822     QVERIFY(path);
823     QPointF start = path->pointAt(0.0);
824     QPointF offset;//Center of item is at point, but pos is from corner
825     offset.setX(firstItem->width()/2);
826     offset.setY(firstItem->height()/2);
827     QTRY_COMPARE(firstItem->pos() + offset, start);
828     pathview->setOffset(1.0);
829
830     for (int i=0; i<model.count(); i++) {
831         QQuickRectangle *curItem = findItem<QQuickRectangle>(pathview, "wrapper", i);
832         QPointF itemPos(path->pointAt(0.25 + i*0.25));
833         QCOMPARE(curItem->pos() + offset, QPointF(qRound(itemPos.x()), qRound(itemPos.y())));
834     }
835
836     pathview->setOffset(0.0);
837     QCOMPARE(firstItem->pos() + offset, start);
838
839     // Change delegate size
840     pathview->setOffset(0.1);
841     pathview->setOffset(0.0);
842     canvas->rootObject()->setProperty("delegateWidth", 30);
843     QCOMPARE(firstItem->width(), 30.0);
844     offset.setX(firstItem->width()/2);
845     QTRY_COMPARE(firstItem->pos() + offset, start);
846
847     // Change delegate scale
848     pathview->setOffset(0.1);
849     pathview->setOffset(0.0);
850     canvas->rootObject()->setProperty("delegateScale", 1.2);
851     QTRY_COMPARE(firstItem->pos() + offset, start);
852
853     delete canvas;
854 }
855
856 void tst_QQuickPathView::setCurrentIndex()
857 {
858     QQuickView *canvas = createView();
859     canvas->show();
860
861     TestModel model;
862     model.addItem("Ben", "12345");
863     model.addItem("Bohn", "2345");
864     model.addItem("Bob", "54321");
865     model.addItem("Bill", "4321");
866
867     QDeclarativeContext *ctxt = canvas->rootContext();
868     ctxt->setContextProperty("testModel", &model);
869
870     canvas->setSource(testFileUrl("pathview0.qml"));
871     qApp->processEvents();
872
873     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
874     QVERIFY(pathview != 0);
875
876     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
877     QVERIFY(firstItem);
878     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
879     QVERIFY(path);
880     QPointF start = path->pointAt(0.0);
881     QPointF offset;//Center of item is at point, but pos is from corner
882     offset.setX(firstItem->width()/2);
883     offset.setY(firstItem->height()/2);
884     QCOMPARE(firstItem->pos() + offset, start);
885     QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 0);
886     QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 0);
887
888     pathview->setCurrentIndex(2);
889
890     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
891     QTRY_COMPARE(firstItem->pos() + offset, start);
892     QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 2);
893     QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 2);
894     QCOMPARE(pathview->currentItem(), firstItem);
895     QCOMPARE(firstItem->property("onPath"), QVariant(true));
896
897     pathview->decrementCurrentIndex();
898     QTRY_COMPARE(pathview->currentIndex(), 1);
899     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
900     QVERIFY(firstItem);
901     QTRY_COMPARE(firstItem->pos() + offset, start);
902     QCOMPARE(pathview->currentItem(), firstItem);
903     QCOMPARE(firstItem->property("onPath"), QVariant(true));
904
905     pathview->decrementCurrentIndex();
906     QTRY_COMPARE(pathview->currentIndex(), 0);
907     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
908     QVERIFY(firstItem);
909     QTRY_COMPARE(firstItem->pos() + offset, start);
910     QCOMPARE(pathview->currentItem(), firstItem);
911     QCOMPARE(firstItem->property("onPath"), QVariant(true));
912
913     pathview->decrementCurrentIndex();
914     QTRY_COMPARE(pathview->currentIndex(), 3);
915     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
916     QVERIFY(firstItem);
917     QTRY_COMPARE(firstItem->pos() + offset, start);
918     QCOMPARE(pathview->currentItem(), firstItem);
919     QCOMPARE(firstItem->property("onPath"), QVariant(true));
920
921     pathview->incrementCurrentIndex();
922     QTRY_COMPARE(pathview->currentIndex(), 0);
923     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
924     QVERIFY(firstItem);
925     QTRY_COMPARE(firstItem->pos() + offset, start);
926     QCOMPARE(pathview->currentItem(), firstItem);
927     QCOMPARE(firstItem->property("onPath"), QVariant(true));
928
929     // move an item, set move duration to 0, and change currentIndex to moved item. QTBUG-22786
930     model.moveItem(0, 3);
931     pathview->setHighlightMoveDuration(0);
932     pathview->setCurrentIndex(3);
933     QCOMPARE(pathview->currentIndex(), 3);
934     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
935     QVERIFY(firstItem);
936     QCOMPARE(pathview->currentItem(), firstItem);
937     QTRY_COMPARE(firstItem->pos() + offset, start);
938     model.moveItem(3, 0);
939     pathview->setCurrentIndex(0);
940     pathview->setHighlightMoveDuration(300);
941
942     // Check the current item is still created when outside the bounds of pathItemCount.
943     pathview->setPathItemCount(2);
944     pathview->setHighlightRangeMode(QQuickPathView::NoHighlightRange);
945     QVERIFY(findItem<QQuickRectangle>(pathview, "wrapper", 0));
946     QVERIFY(findItem<QQuickRectangle>(pathview, "wrapper", 1));
947     QVERIFY(!findItem<QQuickRectangle>(pathview, "wrapper", 2));
948     QVERIFY(!findItem<QQuickRectangle>(pathview, "wrapper", 3));
949
950     pathview->setCurrentIndex(2);
951     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
952     QCOMPARE(pathview->currentItem(), firstItem);
953     QCOMPARE(firstItem->property("onPath"), QVariant(false));
954
955     pathview->decrementCurrentIndex();
956     QTRY_COMPARE(pathview->currentIndex(), 1);
957     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
958     QVERIFY(firstItem);
959     QCOMPARE(pathview->currentItem(), firstItem);
960     QCOMPARE(firstItem->property("onPath"), QVariant(true));
961
962     pathview->decrementCurrentIndex();
963     QTRY_COMPARE(pathview->currentIndex(), 0);
964     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
965     QVERIFY(firstItem);
966     QCOMPARE(pathview->currentItem(), firstItem);
967     QCOMPARE(firstItem->property("onPath"), QVariant(true));
968
969     pathview->decrementCurrentIndex();
970     QTRY_COMPARE(pathview->currentIndex(), 3);
971     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
972     QVERIFY(firstItem);
973     QCOMPARE(pathview->currentItem(), firstItem);
974     QCOMPARE(firstItem->property("onPath"), QVariant(false));
975
976     pathview->incrementCurrentIndex();
977     QTRY_COMPARE(pathview->currentIndex(), 0);
978     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
979     QVERIFY(firstItem);
980     QCOMPARE(pathview->currentItem(), firstItem);
981     QCOMPARE(firstItem->property("onPath"), QVariant(true));
982
983     delete canvas;
984 }
985
986 void tst_QQuickPathView::resetModel()
987 {
988     QQuickView *canvas = createView();
989
990     QStringList strings;
991     strings << "one" << "two" << "three";
992     QStringListModel model(strings);
993
994     QDeclarativeContext *ctxt = canvas->rootContext();
995     ctxt->setContextProperty("testModel", &model);
996
997     canvas->setSource(testFileUrl("displaypath.qml"));
998     qApp->processEvents();
999
1000     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
1001     QVERIFY(pathview != 0);
1002
1003     QCOMPARE(pathview->count(), model.rowCount());
1004
1005     for (int i = 0; i < model.rowCount(); ++i) {
1006         QQuickText *display = findItem<QQuickText>(pathview, "displayText", i);
1007         QVERIFY(display != 0);
1008         QCOMPARE(display->text(), strings.at(i));
1009     }
1010
1011     strings.clear();
1012     strings << "four" << "five" << "six" << "seven";
1013     model.setStringList(strings);
1014
1015     QCOMPARE(pathview->count(), model.rowCount());
1016
1017     for (int i = 0; i < model.rowCount(); ++i) {
1018         QQuickText *display = findItem<QQuickText>(pathview, "displayText", i);
1019         QVERIFY(display != 0);
1020         QCOMPARE(display->text(), strings.at(i));
1021     }
1022
1023     delete canvas;
1024 }
1025
1026 void tst_QQuickPathView::propertyChanges()
1027 {
1028     QQuickView *canvas = createView();
1029     QVERIFY(canvas);
1030     canvas->setSource(testFileUrl("propertychanges.qml"));
1031
1032     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1033     QVERIFY(pathView);
1034
1035     QSignalSpy snapPositionSpy(pathView, SIGNAL(preferredHighlightBeginChanged()));
1036     QSignalSpy dragMarginSpy(pathView, SIGNAL(dragMarginChanged()));
1037
1038     QCOMPARE(pathView->preferredHighlightBegin(), 0.1);
1039     QCOMPARE(pathView->dragMargin(), 5.0);
1040
1041     pathView->setPreferredHighlightBegin(0.4);
1042     pathView->setPreferredHighlightEnd(0.4);
1043     pathView->setDragMargin(20.0);
1044
1045     QCOMPARE(pathView->preferredHighlightBegin(), 0.4);
1046     QCOMPARE(pathView->preferredHighlightEnd(), 0.4);
1047     QCOMPARE(pathView->dragMargin(), 20.0);
1048
1049     QCOMPARE(snapPositionSpy.count(), 1);
1050     QCOMPARE(dragMarginSpy.count(), 1);
1051
1052     pathView->setPreferredHighlightBegin(0.4);
1053     pathView->setPreferredHighlightEnd(0.4);
1054     pathView->setDragMargin(20.0);
1055
1056     QCOMPARE(snapPositionSpy.count(), 1);
1057     QCOMPARE(dragMarginSpy.count(), 1);
1058     delete canvas;
1059 }
1060
1061 void tst_QQuickPathView::pathChanges()
1062 {
1063     QQuickView *canvas = createView();
1064     QVERIFY(canvas);
1065     canvas->setSource(testFileUrl("propertychanges.qml"));
1066
1067     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1068     QVERIFY(pathView);
1069
1070     QDeclarativePath *path = canvas->rootObject()->findChild<QDeclarativePath*>("path");
1071     QVERIFY(path);
1072
1073     QSignalSpy startXSpy(path, SIGNAL(startXChanged()));
1074     QSignalSpy startYSpy(path, SIGNAL(startYChanged()));
1075
1076     QCOMPARE(path->startX(), 220.0);
1077     QCOMPARE(path->startY(), 200.0);
1078
1079     path->setStartX(240.0);
1080     path->setStartY(220.0);
1081
1082     QCOMPARE(path->startX(), 240.0);
1083     QCOMPARE(path->startY(), 220.0);
1084
1085     QCOMPARE(startXSpy.count(),1);
1086     QCOMPARE(startYSpy.count(),1);
1087
1088     path->setStartX(240);
1089     path->setStartY(220);
1090
1091     QCOMPARE(startXSpy.count(),1);
1092     QCOMPARE(startYSpy.count(),1);
1093
1094     QDeclarativePath *alternatePath = canvas->rootObject()->findChild<QDeclarativePath*>("alternatePath");
1095     QVERIFY(alternatePath);
1096
1097     QSignalSpy pathSpy(pathView, SIGNAL(pathChanged()));
1098
1099     QCOMPARE(pathView->path(), path);
1100
1101     pathView->setPath(alternatePath);
1102     QCOMPARE(pathView->path(), alternatePath);
1103     QCOMPARE(pathSpy.count(),1);
1104
1105     pathView->setPath(alternatePath);
1106     QCOMPARE(pathSpy.count(),1);
1107
1108     QDeclarativePathAttribute *pathAttribute = canvas->rootObject()->findChild<QDeclarativePathAttribute*>("pathAttribute");
1109     QVERIFY(pathAttribute);
1110
1111     QSignalSpy nameSpy(pathAttribute, SIGNAL(nameChanged()));
1112     QCOMPARE(pathAttribute->name(), QString("opacity"));
1113
1114     pathAttribute->setName("scale");
1115     QCOMPARE(pathAttribute->name(), QString("scale"));
1116     QCOMPARE(nameSpy.count(),1);
1117
1118     pathAttribute->setName("scale");
1119     QCOMPARE(nameSpy.count(),1);
1120     delete canvas;
1121 }
1122
1123 void tst_QQuickPathView::componentChanges()
1124 {
1125     QQuickView *canvas = createView();
1126     QVERIFY(canvas);
1127     canvas->setSource(testFileUrl("propertychanges.qml"));
1128
1129     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1130     QVERIFY(pathView);
1131
1132     QDeclarativeComponent delegateComponent(canvas->engine());
1133     delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
1134
1135     QSignalSpy delegateSpy(pathView, SIGNAL(delegateChanged()));
1136
1137     pathView->setDelegate(&delegateComponent);
1138     QCOMPARE(pathView->delegate(), &delegateComponent);
1139     QCOMPARE(delegateSpy.count(),1);
1140
1141     pathView->setDelegate(&delegateComponent);
1142     QCOMPARE(delegateSpy.count(),1);
1143     delete canvas;
1144 }
1145
1146 void tst_QQuickPathView::modelChanges()
1147 {
1148     QQuickView *canvas = createView();
1149     QVERIFY(canvas);
1150     canvas->setSource(testFileUrl("propertychanges.qml"));
1151
1152     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1153     QVERIFY(pathView);
1154
1155     QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
1156     QVERIFY(alternateModel);
1157     QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
1158     QSignalSpy modelSpy(pathView, SIGNAL(modelChanged()));
1159
1160     pathView->setModel(modelVariant);
1161     QCOMPARE(pathView->model(), modelVariant);
1162     QCOMPARE(modelSpy.count(),1);
1163
1164     pathView->setModel(modelVariant);
1165     QCOMPARE(modelSpy.count(),1);
1166
1167     pathView->setModel(QVariant());
1168     QCOMPARE(modelSpy.count(),2);
1169
1170     delete canvas;
1171 }
1172
1173 void tst_QQuickPathView::pathUpdateOnStartChanged()
1174 {
1175     QQuickView *canvas = createView();
1176     QVERIFY(canvas);
1177     canvas->setSource(testFileUrl("pathUpdateOnStartChanged.qml"));
1178
1179     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1180     QVERIFY(pathView);
1181
1182     QDeclarativePath *path = canvas->rootObject()->findChild<QDeclarativePath*>("path");
1183     QVERIFY(path);
1184     QCOMPARE(path->startX(), 400.0);
1185     QCOMPARE(path->startY(), 300.0);
1186
1187     QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
1188     QVERIFY(item);
1189     QCOMPARE(item->x(), path->startX() - item->width() / 2.0);
1190     QCOMPARE(item->y(), path->startY() - item->height() / 2.0);
1191
1192     delete canvas;
1193 }
1194
1195 void tst_QQuickPathView::package()
1196 {
1197     QQuickView *canvas = createView();
1198     QVERIFY(canvas);
1199     canvas->setSource(testFileUrl("pathview_package.qml"));
1200     canvas->show();
1201     QTest::qWaitForWindowShown(canvas);
1202
1203     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("photoPathView");
1204     QVERIFY(pathView);
1205
1206     QQuickItem *item = findItem<QQuickItem>(pathView, "pathItem");
1207     QVERIFY(item);
1208     QVERIFY(item->scale() != 1.0);
1209
1210     delete canvas;
1211 }
1212
1213 //QTBUG-13017
1214 void tst_QQuickPathView::emptyModel()
1215 {
1216     QQuickView *canvas = createView();
1217
1218     QStringListModel model;
1219
1220     QDeclarativeContext *ctxt = canvas->rootContext();
1221     ctxt->setContextProperty("emptyModel", &model);
1222
1223     canvas->setSource(testFileUrl("emptymodel.qml"));
1224     qApp->processEvents();
1225
1226     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1227     QVERIFY(pathview != 0);
1228
1229     QCOMPARE(pathview->offset(), qreal(0.0));
1230
1231     delete canvas;
1232 }
1233
1234 void tst_QQuickPathView::closed()
1235 {
1236     QDeclarativeEngine engine;
1237
1238     {
1239         QDeclarativeComponent c(&engine, testFileUrl("openPath.qml"));
1240         QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
1241         QVERIFY(obj);
1242         QCOMPARE(obj->isClosed(), false);
1243         delete obj;
1244     }
1245
1246     {
1247         QDeclarativeComponent c(&engine, testFileUrl("closedPath.qml"));
1248         QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
1249         QVERIFY(obj);
1250         QCOMPARE(obj->isClosed(), true);
1251         delete obj;
1252     }
1253 }
1254
1255 // QTBUG-14239
1256 void tst_QQuickPathView::pathUpdate()
1257 {
1258     QQuickView *canvas = createView();
1259     QVERIFY(canvas);
1260     canvas->setSource(testFileUrl("pathUpdate.qml"));
1261
1262     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1263     QVERIFY(pathView);
1264
1265     QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
1266     QVERIFY(item);
1267     QCOMPARE(item->x(), 150.0);
1268
1269     delete canvas;
1270 }
1271
1272 void tst_QQuickPathView::visualDataModel()
1273 {
1274     QDeclarativeEngine engine;
1275     QDeclarativeComponent c(&engine, testFileUrl("vdm.qml"));
1276
1277     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
1278     QVERIFY(obj != 0);
1279
1280     QCOMPARE(obj->count(), 3);
1281
1282     delete obj;
1283 }
1284
1285 void tst_QQuickPathView::undefinedPath()
1286 {
1287     QDeclarativeEngine engine;
1288
1289     QString warning1("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call");
1290     QTest::ignoreMessage(QtWarningMsg,qPrintable(warning1));
1291
1292     QString warning2("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call");
1293     QTest::ignoreMessage(QtWarningMsg,qPrintable(warning2));
1294
1295     QDeclarativeComponent c(&engine, testFileUrl("undefinedpath.qml"));
1296
1297     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
1298     QVERIFY(obj != 0);
1299
1300     QCOMPARE(obj->count(), 3);
1301
1302     delete obj;
1303 }
1304
1305 void tst_QQuickPathView::mouseDrag()
1306 {
1307     QQuickView *canvas = createView();
1308     canvas->setSource(testFileUrl("dragpath.qml"));
1309     canvas->show();
1310     canvas->requestActivateWindow();
1311     QTest::qWaitForWindowShown(canvas);
1312     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1313
1314     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1315     QVERIFY(pathview != 0);
1316
1317     int current = pathview->currentIndex();
1318
1319     QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
1320     QTest::qWait(100);
1321
1322     {
1323         QMouseEvent mv(QEvent::MouseMove, QPoint(30,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1324         QApplication::sendEvent(canvas, &mv);
1325     }
1326     {
1327         QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1328         QApplication::sendEvent(canvas, &mv);
1329     }
1330
1331     QVERIFY(pathview->currentIndex() != current);
1332
1333     QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(40,100));
1334
1335     delete canvas;
1336 }
1337
1338 void tst_QQuickPathView::treeModel()
1339 {
1340     QQuickView *canvas = createView();
1341     canvas->show();
1342
1343     QStandardItemModel model;
1344     initStandardTreeModel(&model);
1345     canvas->engine()->rootContext()->setContextProperty("myModel", &model);
1346
1347     canvas->setSource(testFileUrl("treemodel.qml"));
1348
1349     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1350     QVERIFY(pathview != 0);
1351     QCOMPARE(pathview->count(), 3);
1352
1353     QQuickText *item = findItem<QQuickText>(pathview, "wrapper", 0);
1354     QVERIFY(item);
1355     QCOMPARE(item->text(), QLatin1String("Row 1 Item"));
1356
1357     QVERIFY(QMetaObject::invokeMethod(pathview, "setRoot", Q_ARG(QVariant, 1)));
1358     QCOMPARE(pathview->count(), 1);
1359
1360     QTRY_VERIFY(item = findItem<QQuickText>(pathview, "wrapper", 0));
1361     QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item"));
1362
1363     delete canvas;
1364 }
1365
1366 void tst_QQuickPathView::changePreferredHighlight()
1367 {
1368     QQuickView *canvas = createView();
1369     canvas->setGeometry(0,0,400,200);
1370     canvas->setSource(testFileUrl("dragpath.qml"));
1371     canvas->show();
1372     canvas->requestActivateWindow();
1373     QTest::qWaitForWindowShown(canvas);
1374     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1375
1376     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1377     QVERIFY(pathview != 0);
1378
1379     int current = pathview->currentIndex();
1380     QCOMPARE(current, 0);
1381
1382     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1383     QVERIFY(firstItem);
1384     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
1385     QVERIFY(path);
1386     QPointF start = path->pointAt(0.5);
1387     start.setX(qRound(start.x()));
1388     start.setY(qRound(start.y()));
1389     QPointF offset;//Center of item is at point, but pos is from corner
1390     offset.setX(firstItem->width()/2);
1391     offset.setY(firstItem->height()/2);
1392     QTRY_COMPARE(firstItem->pos() + offset, start);
1393
1394     pathview->setPreferredHighlightBegin(0.8);
1395     pathview->setPreferredHighlightEnd(0.8);
1396     start = path->pointAt(0.8);
1397     start.setX(qRound(start.x()));
1398     start.setY(qRound(start.y()));
1399     QTRY_COMPARE(firstItem->pos() + offset, start);
1400     QCOMPARE(pathview->currentIndex(), 0);
1401
1402     delete canvas;
1403 }
1404
1405 void tst_QQuickPathView::creationContext()
1406 {
1407     QQuickView canvas;
1408     canvas.setGeometry(0,0,240,320);
1409     canvas.setSource(testFileUrl("creationContext.qml"));
1410
1411     QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
1412     QVERIFY(rootItem);
1413     QVERIFY(rootItem->property("count").toInt() > 0);
1414
1415     QQuickItem *item;
1416     QVERIFY(item = findItem<QQuickItem>(rootItem, "listItem", 0));
1417     QCOMPARE(item->property("text").toString(), QString("Hello!"));
1418 }
1419
1420 // QTBUG-21320
1421 void tst_QQuickPathView::currentOffsetOnInsertion()
1422 {
1423     QQuickView *canvas = createView();
1424     canvas->show();
1425
1426     TestModel model;
1427
1428     QDeclarativeContext *ctxt = canvas->rootContext();
1429     ctxt->setContextProperty("testModel", &model);
1430
1431     canvas->setSource(testFileUrl("pathline.qml"));
1432     qApp->processEvents();
1433
1434     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
1435     QVERIFY(pathview != 0);
1436
1437     pathview->setPreferredHighlightBegin(0.5);
1438     pathview->setPreferredHighlightEnd(0.5);
1439
1440     QCOMPARE(pathview->count(), model.count());
1441
1442     model.addItem("item0", "0");
1443
1444     QCOMPARE(pathview->count(), model.count());
1445
1446     QQuickRectangle *item = 0;
1447     QTRY_VERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 0));
1448
1449     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
1450     QVERIFY(path);
1451
1452     QPointF start = path->pointAt(0.5);
1453     start = QPointF(qRound(start.x()), qRound(start.y()));
1454     QPointF offset;//Center of item is at point, but pos is from corner
1455     offset.setX(item->width()/2);
1456     offset.setY(item->height()/2);
1457     QCOMPARE(item->pos() + offset, start);
1458
1459     QSignalSpy currentIndexSpy(pathview, SIGNAL(currentIndexChanged()));
1460
1461     // insert an item at the beginning
1462     model.insertItem(0, "item1", "1");
1463     qApp->processEvents();
1464
1465     QCOMPARE(currentIndexSpy.count(), 1);
1466
1467     // currentIndex is now 1
1468     QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 1));
1469
1470     // verify that current item (item 1) is still at offset 0.5
1471     QCOMPARE(item->pos() + offset, start);
1472
1473     // insert another item at the beginning
1474     model.insertItem(0, "item2", "2");
1475     qApp->processEvents();
1476
1477     QCOMPARE(currentIndexSpy.count(), 2);
1478
1479     // currentIndex is now 2
1480     QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 2));
1481
1482     // verify that current item (item 2) is still at offset 0.5
1483     QCOMPARE(item->pos() + offset, start);
1484
1485     // verify that remove before current maintains current item
1486     model.removeItem(0);
1487     qApp->processEvents();
1488
1489     QCOMPARE(currentIndexSpy.count(), 3);
1490
1491     // currentIndex is now 1
1492     QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 1));
1493
1494     // verify that current item (item 1) is still at offset 0.5
1495     QCOMPARE(item->pos() + offset, start);
1496
1497     delete canvas;
1498 }
1499
1500 void tst_QQuickPathView::asynchronous()
1501 {
1502     QQuickView *canvas = createView();
1503     canvas->show();
1504     QDeclarativeIncubationController controller;
1505     canvas->engine()->setIncubationController(&controller);
1506
1507     canvas->setSource(testFileUrl("asyncloader.qml"));
1508
1509     QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
1510     QVERIFY(rootObject);
1511
1512     QQuickPathView *pathview = 0;
1513     while (!pathview) {
1514         bool b = false;
1515         controller.incubateWhile(&b);
1516         pathview = rootObject->findChild<QQuickPathView*>("view");
1517     }
1518
1519     // items will be created one at a time
1520     for (int i = 0; i < 5; ++i) {
1521         QVERIFY(findItem<QQuickItem>(pathview, "wrapper", i) == 0);
1522         QQuickItem *item = 0;
1523         while (!item) {
1524             bool b = false;
1525             controller.incubateWhile(&b);
1526             item = findItem<QQuickItem>(pathview, "wrapper", i);
1527         }
1528     }
1529
1530     {
1531         bool b = true;
1532         controller.incubateWhile(&b);
1533     }
1534
1535     // verify positioning
1536     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1537     QVERIFY(firstItem);
1538     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
1539     QVERIFY(path);
1540     QPointF start = path->pointAt(0.0);
1541     QPointF offset;//Center of item is at point, but pos is from corner
1542     offset.setX(firstItem->width()/2);
1543     offset.setY(firstItem->height()/2);
1544     QTRY_COMPARE(firstItem->pos() + offset, start);
1545     pathview->setOffset(1.0);
1546
1547     for (int i=0; i<5; i++) {
1548         QQuickItem *curItem = findItem<QQuickItem>(pathview, "wrapper", i);
1549         QPointF itemPos(path->pointAt(0.2 + i*0.2));
1550         QCOMPARE(curItem->pos() + offset, QPointF(qRound(itemPos.x()), qRound(itemPos.y())));
1551     }
1552
1553     delete canvas;
1554 }
1555
1556 QQuickView *tst_QQuickPathView::createView()
1557 {
1558     QQuickView *canvas = new QQuickView(0);
1559     canvas->setGeometry(0,0,240,320);
1560
1561     return canvas;
1562 }
1563
1564 /*
1565    Find an item with the specified objectName.  If index is supplied then the
1566    item must also evaluate the {index} expression equal to index
1567  */
1568 template<typename T>
1569 T *tst_QQuickPathView::findItem(QQuickItem *parent, const QString &objectName, int index)
1570 {
1571     const QMetaObject &mo = T::staticMetaObject;
1572     //qDebug() << parent->childItems().count() << "children";
1573     for (int i = 0; i < parent->childItems().count(); ++i) {
1574         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
1575         if (!item)
1576             continue;
1577         //qDebug() << "try" << item;
1578         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
1579             if (index != -1) {
1580                 QDeclarativeExpression e(qmlContext(item), item, "index");
1581                 if (e.evaluate().toInt() == index)
1582                     return static_cast<T*>(item);
1583             } else {
1584                 return static_cast<T*>(item);
1585             }
1586         }
1587         item = findItem<T>(item, objectName, index);
1588         if (item)
1589             return static_cast<T*>(item);
1590     }
1591
1592     return 0;
1593 }
1594
1595 template<typename T>
1596 QList<T*> tst_QQuickPathView::findItems(QQuickItem *parent, const QString &objectName)
1597 {
1598     QList<T*> items;
1599     const QMetaObject &mo = T::staticMetaObject;
1600     //qDebug() << parent->QQuickItem::children().count() << "children";
1601     for (int i = 0; i < parent->childItems().count(); ++i) {
1602         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
1603         if (!item)
1604             continue;
1605         //qDebug() << "try" << item;
1606         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
1607             items.append(static_cast<T*>(item));
1608         items += findItems<T>(item, objectName);
1609     }
1610
1611     return items;
1612 }
1613
1614 void tst_QQuickPathView::missingPercent()
1615 {
1616     QDeclarativeEngine engine;
1617     QDeclarativeComponent c(&engine, testFileUrl("missingPercent.qml"));
1618     QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
1619     QVERIFY(obj);
1620     QCOMPARE(obj->attributeAt("_qfx_percent", 1.0), qreal(1.0));
1621     delete obj;
1622 }
1623
1624
1625 QTEST_MAIN(tst_QQuickPathView)
1626
1627 #include "tst_qquickpathview.moc"