Remove "All rights reserved" line from 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 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
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 #ifdef Q_OS_MAC
1207     QSKIP("QTBUG-21590 view does not reliably receive polish without a running animation");
1208 #endif
1209
1210     QQuickItem *item = findItem<QQuickItem>(pathView, "pathItem");
1211     QVERIFY(item);
1212     QVERIFY(item->scale() != 1.0);
1213
1214     delete canvas;
1215 }
1216
1217 //QTBUG-13017
1218 void tst_QQuickPathView::emptyModel()
1219 {
1220     QQuickView *canvas = createView();
1221
1222     QStringListModel model;
1223
1224     QDeclarativeContext *ctxt = canvas->rootContext();
1225     ctxt->setContextProperty("emptyModel", &model);
1226
1227     canvas->setSource(testFileUrl("emptymodel.qml"));
1228     qApp->processEvents();
1229
1230     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1231     QVERIFY(pathview != 0);
1232
1233     QCOMPARE(pathview->offset(), qreal(0.0));
1234
1235     delete canvas;
1236 }
1237
1238 void tst_QQuickPathView::closed()
1239 {
1240     QDeclarativeEngine engine;
1241
1242     {
1243         QDeclarativeComponent c(&engine, testFileUrl("openPath.qml"));
1244         QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
1245         QVERIFY(obj);
1246         QCOMPARE(obj->isClosed(), false);
1247         delete obj;
1248     }
1249
1250     {
1251         QDeclarativeComponent c(&engine, testFileUrl("closedPath.qml"));
1252         QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
1253         QVERIFY(obj);
1254         QCOMPARE(obj->isClosed(), true);
1255         delete obj;
1256     }
1257 }
1258
1259 // QTBUG-14239
1260 void tst_QQuickPathView::pathUpdate()
1261 {
1262     QQuickView *canvas = createView();
1263     QVERIFY(canvas);
1264     canvas->setSource(testFileUrl("pathUpdate.qml"));
1265
1266     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
1267     QVERIFY(pathView);
1268
1269     QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
1270     QVERIFY(item);
1271     QCOMPARE(item->x(), 150.0);
1272
1273     delete canvas;
1274 }
1275
1276 void tst_QQuickPathView::visualDataModel()
1277 {
1278     QDeclarativeEngine engine;
1279     QDeclarativeComponent c(&engine, testFileUrl("vdm.qml"));
1280
1281     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
1282     QVERIFY(obj != 0);
1283
1284     QCOMPARE(obj->count(), 3);
1285
1286     delete obj;
1287 }
1288
1289 void tst_QQuickPathView::undefinedPath()
1290 {
1291     QDeclarativeEngine engine;
1292
1293     // QPainterPath warnings are only received if QT_NO_DEBUG is not defined
1294     if (QLibraryInfo::isDebugBuild()) {
1295         QString warning1("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call");
1296         QTest::ignoreMessage(QtWarningMsg,qPrintable(warning1));
1297
1298         QString warning2("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call");
1299         QTest::ignoreMessage(QtWarningMsg,qPrintable(warning2));
1300     }
1301
1302     QDeclarativeComponent c(&engine, testFileUrl("undefinedpath.qml"));
1303
1304     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
1305     QVERIFY(obj != 0);
1306
1307     QCOMPARE(obj->count(), 3);
1308
1309     delete obj;
1310 }
1311
1312 void tst_QQuickPathView::mouseDrag()
1313 {
1314     QQuickView *canvas = createView();
1315     canvas->setSource(testFileUrl("dragpath.qml"));
1316     canvas->show();
1317     canvas->requestActivateWindow();
1318     QTest::qWaitForWindowShown(canvas);
1319     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1320
1321     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1322     QVERIFY(pathview != 0);
1323
1324     int current = pathview->currentIndex();
1325
1326     QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
1327     QTest::qWait(100);
1328
1329     {
1330         QMouseEvent mv(QEvent::MouseMove, QPoint(30,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1331         QGuiApplication::sendEvent(canvas, &mv);
1332     }
1333     {
1334         QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1335         QGuiApplication::sendEvent(canvas, &mv);
1336     }
1337
1338     QVERIFY(pathview->currentIndex() != current);
1339
1340     QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(40,100));
1341
1342     delete canvas;
1343 }
1344
1345 void tst_QQuickPathView::treeModel()
1346 {
1347     QQuickView *canvas = createView();
1348     canvas->show();
1349
1350     QStandardItemModel model;
1351     initStandardTreeModel(&model);
1352     canvas->engine()->rootContext()->setContextProperty("myModel", &model);
1353
1354     canvas->setSource(testFileUrl("treemodel.qml"));
1355
1356     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1357     QVERIFY(pathview != 0);
1358     QCOMPARE(pathview->count(), 3);
1359
1360     QQuickText *item = findItem<QQuickText>(pathview, "wrapper", 0);
1361     QVERIFY(item);
1362     QCOMPARE(item->text(), QLatin1String("Row 1 Item"));
1363
1364     QVERIFY(QMetaObject::invokeMethod(pathview, "setRoot", Q_ARG(QVariant, 1)));
1365     QCOMPARE(pathview->count(), 1);
1366
1367     QTRY_VERIFY(item = findItem<QQuickText>(pathview, "wrapper", 0));
1368     QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item"));
1369
1370     delete canvas;
1371 }
1372
1373 void tst_QQuickPathView::changePreferredHighlight()
1374 {
1375     QQuickView *canvas = createView();
1376     canvas->setGeometry(0,0,400,200);
1377     canvas->setSource(testFileUrl("dragpath.qml"));
1378     canvas->show();
1379     canvas->requestActivateWindow();
1380     QTest::qWaitForWindowShown(canvas);
1381     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1382
1383     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1384     QVERIFY(pathview != 0);
1385
1386     int current = pathview->currentIndex();
1387     QCOMPARE(current, 0);
1388
1389     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1390     QVERIFY(firstItem);
1391     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
1392     QVERIFY(path);
1393     QPointF start = path->pointAt(0.5);
1394     start.setX(qRound(start.x()));
1395     start.setY(qRound(start.y()));
1396     QPointF offset;//Center of item is at point, but pos is from corner
1397     offset.setX(firstItem->width()/2);
1398     offset.setY(firstItem->height()/2);
1399     QTRY_COMPARE(firstItem->pos() + offset, start);
1400
1401     pathview->setPreferredHighlightBegin(0.8);
1402     pathview->setPreferredHighlightEnd(0.8);
1403     start = path->pointAt(0.8);
1404     start.setX(qRound(start.x()));
1405     start.setY(qRound(start.y()));
1406     QTRY_COMPARE(firstItem->pos() + offset, start);
1407     QCOMPARE(pathview->currentIndex(), 0);
1408
1409     delete canvas;
1410 }
1411
1412 void tst_QQuickPathView::creationContext()
1413 {
1414     QQuickView canvas;
1415     canvas.setGeometry(0,0,240,320);
1416     canvas.setSource(testFileUrl("creationContext.qml"));
1417
1418     QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
1419     QVERIFY(rootItem);
1420     QVERIFY(rootItem->property("count").toInt() > 0);
1421
1422     QQuickItem *item;
1423     QVERIFY(item = findItem<QQuickItem>(rootItem, "listItem", 0));
1424     QCOMPARE(item->property("text").toString(), QString("Hello!"));
1425 }
1426
1427 // QTBUG-21320
1428 void tst_QQuickPathView::currentOffsetOnInsertion()
1429 {
1430     QQuickView *canvas = createView();
1431     canvas->show();
1432
1433     TestModel model;
1434
1435     QDeclarativeContext *ctxt = canvas->rootContext();
1436     ctxt->setContextProperty("testModel", &model);
1437
1438     canvas->setSource(testFileUrl("pathline.qml"));
1439     qApp->processEvents();
1440
1441     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
1442     QVERIFY(pathview != 0);
1443
1444     pathview->setPreferredHighlightBegin(0.5);
1445     pathview->setPreferredHighlightEnd(0.5);
1446
1447     QCOMPARE(pathview->count(), model.count());
1448
1449     model.addItem("item0", "0");
1450
1451     QCOMPARE(pathview->count(), model.count());
1452
1453     QQuickRectangle *item = 0;
1454     QTRY_VERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 0));
1455
1456     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
1457     QVERIFY(path);
1458
1459     QPointF start = path->pointAt(0.5);
1460     start = QPointF(qRound(start.x()), qRound(start.y()));
1461     QPointF offset;//Center of item is at point, but pos is from corner
1462     offset.setX(item->width()/2);
1463     offset.setY(item->height()/2);
1464     QCOMPARE(item->pos() + offset, start);
1465
1466     QSignalSpy currentIndexSpy(pathview, SIGNAL(currentIndexChanged()));
1467
1468     // insert an item at the beginning
1469     model.insertItem(0, "item1", "1");
1470     qApp->processEvents();
1471
1472     QCOMPARE(currentIndexSpy.count(), 1);
1473
1474     // currentIndex is now 1
1475     QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 1));
1476
1477     // verify that current item (item 1) is still at offset 0.5
1478     QCOMPARE(item->pos() + offset, start);
1479
1480     // insert another item at the beginning
1481     model.insertItem(0, "item2", "2");
1482     qApp->processEvents();
1483
1484     QCOMPARE(currentIndexSpy.count(), 2);
1485
1486     // currentIndex is now 2
1487     QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 2));
1488
1489     // verify that current item (item 2) is still at offset 0.5
1490     QCOMPARE(item->pos() + offset, start);
1491
1492     // verify that remove before current maintains current item
1493     model.removeItem(0);
1494     qApp->processEvents();
1495
1496     QCOMPARE(currentIndexSpy.count(), 3);
1497
1498     // currentIndex is now 1
1499     QVERIFY(item = findItem<QQuickRectangle>(pathview, "wrapper", 1));
1500
1501     // verify that current item (item 1) is still at offset 0.5
1502     QCOMPARE(item->pos() + offset, start);
1503
1504     delete canvas;
1505 }
1506
1507 void tst_QQuickPathView::asynchronous()
1508 {
1509     QQuickView *canvas = createView();
1510     canvas->show();
1511     QDeclarativeIncubationController controller;
1512     canvas->engine()->setIncubationController(&controller);
1513
1514     canvas->setSource(testFileUrl("asyncloader.qml"));
1515
1516     QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
1517     QVERIFY(rootObject);
1518
1519     QQuickPathView *pathview = 0;
1520     while (!pathview) {
1521         bool b = false;
1522         controller.incubateWhile(&b);
1523         pathview = rootObject->findChild<QQuickPathView*>("view");
1524     }
1525
1526     // items will be created one at a time
1527     for (int i = 0; i < 5; ++i) {
1528         QVERIFY(findItem<QQuickItem>(pathview, "wrapper", i) == 0);
1529         QQuickItem *item = 0;
1530         while (!item) {
1531             bool b = false;
1532             controller.incubateWhile(&b);
1533             item = findItem<QQuickItem>(pathview, "wrapper", i);
1534         }
1535     }
1536
1537     {
1538         bool b = true;
1539         controller.incubateWhile(&b);
1540     }
1541
1542     // verify positioning
1543     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1544     QVERIFY(firstItem);
1545     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
1546     QVERIFY(path);
1547     QPointF start = path->pointAt(0.0);
1548     QPointF offset;//Center of item is at point, but pos is from corner
1549     offset.setX(firstItem->width()/2);
1550     offset.setY(firstItem->height()/2);
1551     QTRY_COMPARE(firstItem->pos() + offset, start);
1552     pathview->setOffset(1.0);
1553
1554     for (int i=0; i<5; i++) {
1555         QQuickItem *curItem = findItem<QQuickItem>(pathview, "wrapper", i);
1556         QPointF itemPos(path->pointAt(0.2 + i*0.2));
1557         QCOMPARE(curItem->pos() + offset, QPointF(qRound(itemPos.x()), qRound(itemPos.y())));
1558     }
1559
1560     delete canvas;
1561 }
1562
1563 QQuickView *tst_QQuickPathView::createView()
1564 {
1565     QQuickView *canvas = new QQuickView(0);
1566     canvas->setGeometry(0,0,240,320);
1567
1568     return canvas;
1569 }
1570
1571 /*
1572    Find an item with the specified objectName.  If index is supplied then the
1573    item must also evaluate the {index} expression equal to index
1574  */
1575 template<typename T>
1576 T *tst_QQuickPathView::findItem(QQuickItem *parent, const QString &objectName, int index)
1577 {
1578     const QMetaObject &mo = T::staticMetaObject;
1579     //qDebug() << parent->childItems().count() << "children";
1580     for (int i = 0; i < parent->childItems().count(); ++i) {
1581         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
1582         if (!item)
1583             continue;
1584         //qDebug() << "try" << item;
1585         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
1586             if (index != -1) {
1587                 QDeclarativeExpression e(qmlContext(item), item, "index");
1588                 if (e.evaluate().toInt() == index)
1589                     return static_cast<T*>(item);
1590             } else {
1591                 return static_cast<T*>(item);
1592             }
1593         }
1594         item = findItem<T>(item, objectName, index);
1595         if (item)
1596             return static_cast<T*>(item);
1597     }
1598
1599     return 0;
1600 }
1601
1602 template<typename T>
1603 QList<T*> tst_QQuickPathView::findItems(QQuickItem *parent, const QString &objectName)
1604 {
1605     QList<T*> items;
1606     const QMetaObject &mo = T::staticMetaObject;
1607     //qDebug() << parent->QQuickItem::children().count() << "children";
1608     for (int i = 0; i < parent->childItems().count(); ++i) {
1609         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
1610         if (!item)
1611             continue;
1612         //qDebug() << "try" << item;
1613         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
1614             items.append(static_cast<T*>(item));
1615         items += findItems<T>(item, objectName);
1616     }
1617
1618     return items;
1619 }
1620
1621 void tst_QQuickPathView::missingPercent()
1622 {
1623     QDeclarativeEngine engine;
1624     QDeclarativeComponent c(&engine, testFileUrl("missingPercent.qml"));
1625     QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
1626     QVERIFY(obj);
1627     QCOMPARE(obj->attributeAt("_qfx_percent", 1.0), qreal(1.0));
1628     delete obj;
1629 }
1630
1631
1632 QTEST_MAIN(tst_QQuickPathView)
1633
1634 #include "tst_qquickpathview.moc"