Views do not notify count changes properly
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qdeclarativepathview / tst_qdeclarativepathview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <QtDeclarative/qdeclarativeview.h>
44 #include <QtDeclarative/qdeclarativeengine.h>
45 #include <QtDeclarative/qdeclarativecomponent.h>
46 #include <QtDeclarative/qdeclarativecontext.h>
47 #include <QtDeclarative/qdeclarativeexpression.h>
48 #include <QtDeclarative/private/qdeclarativepathview_p.h>
49 #include <QtDeclarative/private/qdeclarativepath_p.h>
50 #include <QtDeclarative/private/qdeclarativetext_p.h>
51 #include <QtDeclarative/private/qdeclarativerectangle_p.h>
52 #include <QtDeclarative/private/qdeclarativelistmodel_p.h>
53 #include <QtDeclarative/private/qdeclarativevaluetype_p.h>
54 #include <QAbstractListModel>
55 #include <QStringListModel>
56 #include <QStandardItemModel>
57 #include <QFile>
58
59 #include "../../../shared/util.h"
60
61 #ifdef Q_OS_SYMBIAN
62 // In Symbian OS test data is located in applications private dir
63 #define SRCDIR "."
64 #endif
65
66 static void initStandardTreeModel(QStandardItemModel *model)
67 {
68     QStandardItem *item;
69     item = new QStandardItem(QLatin1String("Row 1 Item"));
70     model->insertRow(0, item);
71
72     item = new QStandardItem(QLatin1String("Row 2 Item"));
73     item->setCheckable(true);
74     model->insertRow(1, item);
75
76     QStandardItem *childItem = new QStandardItem(QLatin1String("Row 2 Child Item"));
77     item->setChild(0, childItem);
78
79     item = new QStandardItem(QLatin1String("Row 3 Item"));
80     item->setIcon(QIcon());
81     model->insertRow(2, item);
82 }
83
84
85 class tst_QDeclarativePathView : public QObject
86 {
87     Q_OBJECT
88 public:
89     tst_QDeclarativePathView();
90
91 private slots:
92     void initValues();
93     void items();
94     void dataModel();
95     void pathview2();
96     void pathview3();
97     void path();
98     void pathMoved();
99     void setCurrentIndex();
100     void resetModel();
101     void propertyChanges();
102     void pathChanges();
103     void componentChanges();
104     void modelChanges();
105     void pathUpdateOnStartChanged();
106     void package();
107     void emptyModel();
108     void closed();
109     void pathUpdate();
110     void visualDataModel();
111     void undefinedPath();
112     void mouseDrag();
113     void treeModel();
114     void changePreferredHighlight();
115
116 private:
117     QDeclarativeView *createView();
118     template<typename T>
119     T *findItem(QGraphicsObject *parent, const QString &objectName, int index=-1);
120     template<typename T>
121     QList<T*> findItems(QGraphicsObject *parent, const QString &objectName);
122 };
123
124 class TestObject : public QObject
125 {
126     Q_OBJECT
127
128     Q_PROPERTY(bool error READ error WRITE setError)
129     Q_PROPERTY(bool useModel READ useModel NOTIFY useModelChanged)
130     Q_PROPERTY(int pathItemCount READ pathItemCount NOTIFY pathItemCountChanged)
131
132 public:
133     TestObject() : QObject(), mError(true), mUseModel(true), mPathItemCount(-1) {}
134
135     bool error() const { return mError; }
136     void setError(bool err) { mError = err; }
137
138     bool useModel() const { return mUseModel; }
139     void setUseModel(bool use) { mUseModel = use; emit useModelChanged(); }
140
141     int pathItemCount() const { return mPathItemCount; }
142     void setPathItemCount(int count) { mPathItemCount = count; emit pathItemCountChanged(); }
143
144 signals:
145     void useModelChanged();
146     void pathItemCountChanged();
147
148 private:
149     bool mError;
150     bool mUseModel;
151     int mPathItemCount;
152 };
153
154 class TestModel : public QAbstractListModel
155 {
156 public:
157     enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
158
159     TestModel(QObject *parent=0) : QAbstractListModel(parent) {
160         QHash<int, QByteArray> roles;
161         roles[Name] = "name";
162         roles[Number] = "number";
163         setRoleNames(roles);
164     }
165
166     int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
167     QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
168         QVariant rv;
169         if (role == Name)
170             rv = list.at(index.row()).first;
171         else if (role == Number)
172             rv = list.at(index.row()).second;
173
174         return rv;
175     }
176
177     int count() const { return rowCount(); }
178     QString name(int index) const { return list.at(index).first; }
179     QString number(int index) const { return list.at(index).second; }
180
181     void addItem(const QString &name, const QString &number) {
182         beginInsertRows(QModelIndex(), list.count(), list.count());
183         list.append(QPair<QString,QString>(name, number));
184         endInsertRows();
185     }
186
187     void insertItem(int index, const QString &name, const QString &number) {
188         beginInsertRows(QModelIndex(), index, index);
189         list.insert(index, QPair<QString,QString>(name, number));
190         endInsertRows();
191     }
192
193     void removeItem(int index) {
194         beginRemoveRows(QModelIndex(), index, index);
195         list.removeAt(index);
196         endRemoveRows();
197     }
198
199     void moveItem(int from, int to) {
200         beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
201         list.move(from, to);
202         endMoveRows();
203     }
204
205     void modifyItem(int idx, const QString &name, const QString &number) {
206         list[idx] = QPair<QString,QString>(name, number);
207         emit dataChanged(index(idx,0), index(idx,0));
208     }
209
210 private:
211     QList<QPair<QString,QString> > list;
212 };
213
214
215 tst_QDeclarativePathView::tst_QDeclarativePathView()
216 {
217 }
218
219 void tst_QDeclarativePathView::initValues()
220 {
221     QDeclarativeEngine engine;
222     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathview1.qml"));
223     QDeclarativePathView *obj = qobject_cast<QDeclarativePathView*>(c.create());
224
225     QVERIFY(obj != 0);
226     QVERIFY(obj->path() == 0);
227     QVERIFY(obj->delegate() == 0);
228     QCOMPARE(obj->model(), QVariant());
229     QCOMPARE(obj->currentIndex(), 0);
230     QCOMPARE(obj->offset(), 0.);
231     QCOMPARE(obj->preferredHighlightBegin(), 0.);
232     QCOMPARE(obj->dragMargin(), 0.);
233     QCOMPARE(obj->count(), 0);
234     QCOMPARE(obj->pathItemCount(), -1);
235 }
236
237 void tst_QDeclarativePathView::items()
238 {
239     QDeclarativeView *canvas = createView();
240
241     TestModel model;
242     model.addItem("Fred", "12345");
243     model.addItem("John", "2345");
244     model.addItem("Bob", "54321");
245     model.addItem("Bill", "4321");
246
247     QDeclarativeContext *ctxt = canvas->rootContext();
248     ctxt->setContextProperty("testModel", &model);
249
250     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathview0.qml"));
251     qApp->processEvents();
252
253     QDeclarativePathView *pathview = findItem<QDeclarativePathView>(canvas->rootObject(), "view");
254     QVERIFY(pathview != 0);
255
256     QCOMPARE(pathview->count(), model.count());
257     QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count());
258     QCOMPARE(pathview->childItems().count(), model.count()+1); // assumes all are visible, including highlight
259
260     for (int i = 0; i < model.count(); ++i) {
261         QDeclarativeText *name = findItem<QDeclarativeText>(pathview, "textName", i);
262         QVERIFY(name != 0);
263         QCOMPARE(name->text(), model.name(i));
264         QDeclarativeText *number = findItem<QDeclarativeText>(pathview, "textNumber", i);
265         QVERIFY(number != 0);
266         QCOMPARE(number->text(), model.number(i));
267     }
268
269     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
270     QVERIFY(path);
271
272     QVERIFY(pathview->highlightItem());
273     QPointF start = path->pointAt(0.0);
274     QPointF offset;
275     offset.setX(pathview->highlightItem()->width()/2);
276     offset.setY(pathview->highlightItem()->height()/2);
277     QCOMPARE(pathview->highlightItem()->pos() + offset, start);
278
279     delete canvas;
280 }
281
282 void tst_QDeclarativePathView::pathview2()
283 {
284     QDeclarativeEngine engine;
285     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathview2.qml"));
286     QDeclarativePathView *obj = qobject_cast<QDeclarativePathView*>(c.create());
287
288     QVERIFY(obj != 0);
289     QVERIFY(obj->path() != 0);
290     QVERIFY(obj->delegate() != 0);
291     QVERIFY(obj->model() != QVariant());
292     QCOMPARE(obj->currentIndex(), 0);
293     QCOMPARE(obj->offset(), 0.);
294     QCOMPARE(obj->preferredHighlightBegin(), 0.);
295     QCOMPARE(obj->dragMargin(), 0.);
296     QCOMPARE(obj->count(), 8);
297     QCOMPARE(obj->pathItemCount(), 10);
298 }
299
300 void tst_QDeclarativePathView::pathview3()
301 {
302     QDeclarativeEngine engine;
303     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathview3.qml"));
304     QDeclarativePathView *obj = qobject_cast<QDeclarativePathView*>(c.create());
305
306     QVERIFY(obj != 0);
307     QVERIFY(obj->path() != 0);
308     QVERIFY(obj->delegate() != 0);
309     QVERIFY(obj->model() != QVariant());
310     QCOMPARE(obj->currentIndex(), 0);
311     QCOMPARE(obj->offset(), 1.0);
312     QCOMPARE(obj->preferredHighlightBegin(), 0.5);
313     QCOMPARE(obj->dragMargin(), 24.);
314     QCOMPARE(obj->count(), 8);
315     QCOMPARE(obj->pathItemCount(), 4);
316 }
317
318 void tst_QDeclarativePathView::path()
319 {
320     QDeclarativeEngine engine;
321     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/pathtest.qml"));
322     QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
323
324     QVERIFY(obj != 0);
325     QCOMPARE(obj->startX(), 120.);
326     QCOMPARE(obj->startY(), 100.);
327     QVERIFY(obj->path() != QPainterPath());
328
329     QDeclarativeListReference list(obj, "pathElements");
330     QCOMPARE(list.count(), 5);
331
332     QDeclarativePathAttribute* attr = qobject_cast<QDeclarativePathAttribute*>(list.at(0));
333     QVERIFY(attr != 0);
334     QCOMPARE(attr->name(), QString("scale"));
335     QCOMPARE(attr->value(), 1.0);
336
337     QDeclarativePathQuad* quad = qobject_cast<QDeclarativePathQuad*>(list.at(1));
338     QVERIFY(quad != 0);
339     QCOMPARE(quad->x(), 120.);
340     QCOMPARE(quad->y(), 25.);
341     QCOMPARE(quad->controlX(), 260.);
342     QCOMPARE(quad->controlY(), 75.);
343
344     QDeclarativePathPercent* perc = qobject_cast<QDeclarativePathPercent*>(list.at(2));
345     QVERIFY(perc != 0);
346     QCOMPARE(perc->value(), 0.3);
347
348     QDeclarativePathLine* line = qobject_cast<QDeclarativePathLine*>(list.at(3));
349     QVERIFY(line != 0);
350     QCOMPARE(line->x(), 120.);
351     QCOMPARE(line->y(), 100.);
352
353     QDeclarativePathCubic* cubic = qobject_cast<QDeclarativePathCubic*>(list.at(4));
354     QVERIFY(cubic != 0);
355     QCOMPARE(cubic->x(), 180.);
356     QCOMPARE(cubic->y(), 0.);
357     QCOMPARE(cubic->control1X(), -10.);
358     QCOMPARE(cubic->control1Y(), 90.);
359     QCOMPARE(cubic->control2X(), 210.);
360     QCOMPARE(cubic->control2Y(), 90.);
361 }
362
363 void tst_QDeclarativePathView::dataModel()
364 {
365     QDeclarativeView *canvas = createView();
366
367     QDeclarativeContext *ctxt = canvas->rootContext();
368     TestObject *testObject = new TestObject;
369     ctxt->setContextProperty("testObject", testObject);
370
371     TestModel model;
372     model.addItem("red", "1");
373     model.addItem("green", "2");
374     model.addItem("blue", "3");
375     model.addItem("purple", "4");
376     model.addItem("gray", "5");
377     model.addItem("brown", "6");
378     model.addItem("yellow", "7");
379     model.addItem("thistle", "8");
380     model.addItem("cyan", "9");
381     model.addItem("peachpuff", "10");
382     model.addItem("powderblue", "11");
383     model.addItem("gold", "12");
384     model.addItem("sandybrown", "13");
385
386     ctxt->setContextProperty("testData", &model);
387
388     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/datamodel.qml"));
389     qApp->processEvents();
390
391     QDeclarativePathView *pathview = qobject_cast<QDeclarativePathView*>(canvas->rootObject());
392     QVERIFY(pathview != 0);
393
394     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
395     QVERIFY(testObject->error() == false);
396
397     QDeclarativeItem *item = findItem<QDeclarativeItem>(pathview, "wrapper", 0);
398     QVERIFY(item);
399     QCOMPARE(item->x(), 110.0);
400     QCOMPARE(item->y(), 10.0);
401
402     model.insertItem(4, "orange", "10");
403     QTest::qWait(100);
404
405     QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
406     QTRY_COMPARE(findItems<QDeclarativeItem>(pathview, "wrapper").count(), 14);
407
408     QVERIFY(pathview->currentIndex() == 0);
409
410     QDeclarativeText *text = findItem<QDeclarativeText>(pathview, "myText", 4);
411     QVERIFY(text);
412     QCOMPARE(text->text(), model.name(4));
413
414     model.removeItem(2);
415     QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
416     text = findItem<QDeclarativeText>(pathview, "myText", 2);
417     QVERIFY(text);
418     QCOMPARE(text->text(), model.name(2));
419
420     testObject->setPathItemCount(5);
421     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
422     QVERIFY(testObject->error() == false);
423
424     QTRY_COMPARE(findItems<QDeclarativeItem>(pathview, "wrapper").count(), 5);
425
426     QDeclarativeRectangle *testItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 4);
427     QVERIFY(testItem != 0);
428     testItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 5);
429     QVERIFY(testItem == 0);
430
431     pathview->setCurrentIndex(1);
432
433     model.insertItem(2, "pink", "2");
434     QTest::qWait(100);
435
436     QTRY_COMPARE(findItems<QDeclarativeItem>(pathview, "wrapper").count(), 5);
437     QVERIFY(pathview->currentIndex() == 1);
438
439     text = findItem<QDeclarativeText>(pathview, "myText", 2);
440     QVERIFY(text);
441     QCOMPARE(text->text(), model.name(2));
442
443     model.removeItem(3);
444     QTRY_COMPARE(findItems<QDeclarativeItem>(pathview, "wrapper").count(), 5);
445     text = findItem<QDeclarativeText>(pathview, "myText", 3);
446     QVERIFY(text);
447     QCOMPARE(text->text(), model.name(3));
448
449     model.moveItem(3, 5);
450     QTRY_COMPARE(findItems<QDeclarativeItem>(pathview, "wrapper").count(), 5);
451     QList<QDeclarativeItem*> items = findItems<QDeclarativeItem>(pathview, "wrapper");
452     foreach (QDeclarativeItem *item, items) {
453         QVERIFY(item->property("onPath").toBool());
454     }
455
456     // QTBUG-14199
457     pathview->setOffset(7);
458     pathview->setOffset(0);
459     QCOMPARE(findItems<QDeclarativeItem>(pathview, "wrapper").count(), 5);
460
461     pathview->setCurrentIndex(model.count()-1);
462     model.removeItem(model.count()-1);
463     QCOMPARE(pathview->currentIndex(), model.count()-1);
464
465     // QTBUG-18825
466     // Confirm that the target offset is adjusted when removing items
467     pathview->setCurrentIndex(model.count()-1);
468     QTRY_COMPARE(pathview->offset(), 1.);
469     pathview->setCurrentIndex(model.count()-5);
470     model.removeItem(model.count()-1);
471     model.removeItem(model.count()-1);
472     model.removeItem(model.count()-1);
473     QTRY_COMPARE(pathview->offset(), 2.);
474
475     delete canvas;
476 }
477
478 void tst_QDeclarativePathView::pathMoved()
479 {
480     QDeclarativeView *canvas = createView();
481
482     TestModel model;
483     model.addItem("Ben", "12345");
484     model.addItem("Bohn", "2345");
485     model.addItem("Bob", "54321");
486     model.addItem("Bill", "4321");
487
488     QDeclarativeContext *ctxt = canvas->rootContext();
489     ctxt->setContextProperty("testModel", &model);
490
491     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathview0.qml"));
492     qApp->processEvents();
493
494     QDeclarativePathView *pathview = findItem<QDeclarativePathView>(canvas->rootObject(), "view");
495     QVERIFY(pathview != 0);
496
497     QDeclarativeRectangle *firstItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 0);
498     QVERIFY(firstItem);
499     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
500     QVERIFY(path);
501     QPointF start = path->pointAt(0.0);
502     QPointF offset;//Center of item is at point, but pos is from corner
503     offset.setX(firstItem->width()/2);
504     offset.setY(firstItem->height()/2);
505     QCOMPARE(firstItem->pos() + offset, start);
506     pathview->setOffset(1.0);
507
508     for(int i=0; i<model.count(); i++){
509         QDeclarativeRectangle *curItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", i);
510         QPointF itemPos(path->pointAt(0.25 + i*0.25));
511         QCOMPARE(curItem->pos() + offset, QPointF(qRound(itemPos.x()), qRound(itemPos.y())));
512     }
513
514     pathview->setOffset(0.0);
515     QCOMPARE(firstItem->pos() + offset, start);
516
517     // Change delegate size
518     pathview->setOffset(0.1);
519     pathview->setOffset(0.0);
520     canvas->rootObject()->setProperty("delegateWidth", 30);
521     QCOMPARE(firstItem->width(), 30.0);
522     offset.setX(firstItem->width()/2);
523     QTRY_COMPARE(firstItem->pos() + offset, start);
524
525     // Change delegate scale
526     pathview->setOffset(0.1);
527     pathview->setOffset(0.0);
528     canvas->rootObject()->setProperty("delegateScale", 1.2);
529     QTRY_COMPARE(firstItem->pos() + offset, start);
530
531     delete canvas;
532 }
533
534 void tst_QDeclarativePathView::setCurrentIndex()
535 {
536     QDeclarativeView *canvas = createView();
537
538     TestModel model;
539     model.addItem("Ben", "12345");
540     model.addItem("Bohn", "2345");
541     model.addItem("Bob", "54321");
542     model.addItem("Bill", "4321");
543
544     QDeclarativeContext *ctxt = canvas->rootContext();
545     ctxt->setContextProperty("testModel", &model);
546
547     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathview0.qml"));
548     qApp->processEvents();
549
550     QDeclarativePathView *pathview = findItem<QDeclarativePathView>(canvas->rootObject(), "view");
551     QVERIFY(pathview != 0);
552
553     QDeclarativeRectangle *firstItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 0);
554     QVERIFY(firstItem);
555     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
556     QVERIFY(path);
557     QPointF start = path->pointAt(0.0);
558     QPointF offset;//Center of item is at point, but pos is from corner
559     offset.setX(firstItem->width()/2);
560     offset.setY(firstItem->height()/2);
561     QCOMPARE(firstItem->pos() + offset, start);
562     QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 0);
563     QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 0);
564
565     pathview->setCurrentIndex(2);
566
567     firstItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 2);
568     QTRY_COMPARE(firstItem->pos() + offset, start);
569     QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 2);
570     QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 2);
571
572     pathview->decrementCurrentIndex();
573     QTRY_COMPARE(pathview->currentIndex(), 1);
574     firstItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 1);
575     QVERIFY(firstItem);
576     QTRY_COMPARE(firstItem->pos() + offset, start);
577
578     pathview->decrementCurrentIndex();
579     QTRY_COMPARE(pathview->currentIndex(), 0);
580     firstItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 0);
581     QVERIFY(firstItem);
582     QTRY_COMPARE(firstItem->pos() + offset, start);
583
584     pathview->decrementCurrentIndex();
585     QTRY_COMPARE(pathview->currentIndex(), 3);
586     firstItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 3);
587     QVERIFY(firstItem);
588     QTRY_COMPARE(firstItem->pos() + offset, start);
589
590     pathview->incrementCurrentIndex();
591     QTRY_COMPARE(pathview->currentIndex(), 0);
592     firstItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 0);
593     QVERIFY(firstItem);
594     QTRY_COMPARE(firstItem->pos() + offset, start);
595
596     delete canvas;
597 }
598
599 void tst_QDeclarativePathView::resetModel()
600 {
601     QDeclarativeView *canvas = createView();
602
603     QStringList strings;
604     strings << "one" << "two" << "three";
605     QStringListModel model(strings);
606
607     QDeclarativeContext *ctxt = canvas->rootContext();
608     ctxt->setContextProperty("testModel", &model);
609
610     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaypath.qml"));
611     qApp->processEvents();
612
613     QDeclarativePathView *pathview = findItem<QDeclarativePathView>(canvas->rootObject(), "view");
614     QVERIFY(pathview != 0);
615
616     QCOMPARE(pathview->count(), model.rowCount());
617
618     for (int i = 0; i < model.rowCount(); ++i) {
619         QDeclarativeText *display = findItem<QDeclarativeText>(pathview, "displayText", i);
620         QVERIFY(display != 0);
621         QCOMPARE(display->text(), strings.at(i));
622     }
623
624     strings.clear();
625     strings << "four" << "five" << "six" << "seven";
626     model.setStringList(strings);
627
628     QCOMPARE(pathview->count(), model.rowCount());
629
630     for (int i = 0; i < model.rowCount(); ++i) {
631         QDeclarativeText *display = findItem<QDeclarativeText>(pathview, "displayText", i);
632         QVERIFY(display != 0);
633         QCOMPARE(display->text(), strings.at(i));
634     }
635 }
636
637 void tst_QDeclarativePathView::propertyChanges()
638 {
639     QDeclarativeView *canvas = createView();
640     QVERIFY(canvas);
641     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml"));
642
643     QDeclarativePathView *pathView = canvas->rootObject()->findChild<QDeclarativePathView*>("pathView");
644     QVERIFY(pathView);
645
646     QSignalSpy snapPositionSpy(pathView, SIGNAL(preferredHighlightBeginChanged()));
647     QSignalSpy dragMarginSpy(pathView, SIGNAL(dragMarginChanged()));
648
649     QCOMPARE(pathView->preferredHighlightBegin(), 0.1);
650     QCOMPARE(pathView->dragMargin(), 5.0);
651
652     pathView->setPreferredHighlightBegin(0.4);
653     pathView->setPreferredHighlightEnd(0.4);
654     pathView->setDragMargin(20.0);
655
656     QCOMPARE(pathView->preferredHighlightBegin(), 0.4);
657     QCOMPARE(pathView->preferredHighlightEnd(), 0.4);
658     QCOMPARE(pathView->dragMargin(), 20.0);
659
660     QCOMPARE(snapPositionSpy.count(), 1);
661     QCOMPARE(dragMarginSpy.count(), 1);
662
663     pathView->setPreferredHighlightBegin(0.4);
664     pathView->setPreferredHighlightEnd(0.4);
665     pathView->setDragMargin(20.0);
666
667     QCOMPARE(snapPositionSpy.count(), 1);
668     QCOMPARE(dragMarginSpy.count(), 1);
669     delete canvas;
670 }
671
672 void tst_QDeclarativePathView::pathChanges()
673 {
674     QDeclarativeView *canvas = createView();
675     QVERIFY(canvas);
676     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml"));
677
678     QDeclarativePathView *pathView = canvas->rootObject()->findChild<QDeclarativePathView*>("pathView");
679     QVERIFY(pathView);
680
681     QDeclarativePath *path = canvas->rootObject()->findChild<QDeclarativePath*>("path");
682     QVERIFY(path);
683
684     QSignalSpy startXSpy(path, SIGNAL(startXChanged()));
685     QSignalSpy startYSpy(path, SIGNAL(startYChanged()));
686
687     QCOMPARE(path->startX(), 220.0);
688     QCOMPARE(path->startY(), 200.0);
689
690     path->setStartX(240.0);
691     path->setStartY(220.0);
692
693     QCOMPARE(path->startX(), 240.0);
694     QCOMPARE(path->startY(), 220.0);
695
696     QCOMPARE(startXSpy.count(),1);
697     QCOMPARE(startYSpy.count(),1);
698
699     path->setStartX(240);
700     path->setStartY(220);
701
702     QCOMPARE(startXSpy.count(),1);
703     QCOMPARE(startYSpy.count(),1);
704
705     QDeclarativePath *alternatePath = canvas->rootObject()->findChild<QDeclarativePath*>("alternatePath");
706     QVERIFY(alternatePath);
707
708     QSignalSpy pathSpy(pathView, SIGNAL(pathChanged()));
709
710     QCOMPARE(pathView->path(), path);
711
712     pathView->setPath(alternatePath);
713     QCOMPARE(pathView->path(), alternatePath);
714     QCOMPARE(pathSpy.count(),1);
715
716     pathView->setPath(alternatePath);
717     QCOMPARE(pathSpy.count(),1);
718
719     QDeclarativePathAttribute *pathAttribute = canvas->rootObject()->findChild<QDeclarativePathAttribute*>("pathAttribute");
720     QVERIFY(pathAttribute);
721
722     QSignalSpy nameSpy(pathAttribute, SIGNAL(nameChanged()));
723     QCOMPARE(pathAttribute->name(), QString("opacity"));
724
725     pathAttribute->setName("scale");
726     QCOMPARE(pathAttribute->name(), QString("scale"));
727     QCOMPARE(nameSpy.count(),1);
728
729     pathAttribute->setName("scale");
730     QCOMPARE(nameSpy.count(),1);
731     delete canvas;
732 }
733
734 void tst_QDeclarativePathView::componentChanges()
735 {
736     QDeclarativeView *canvas = createView();
737     QVERIFY(canvas);
738     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml"));
739
740     QDeclarativePathView *pathView = canvas->rootObject()->findChild<QDeclarativePathView*>("pathView");
741     QVERIFY(pathView);
742
743     QDeclarativeComponent delegateComponent(canvas->engine());
744     delegateComponent.setData("import QtQuick 1.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
745
746     QSignalSpy delegateSpy(pathView, SIGNAL(delegateChanged()));
747
748     pathView->setDelegate(&delegateComponent);
749     QCOMPARE(pathView->delegate(), &delegateComponent);
750     QCOMPARE(delegateSpy.count(),1);
751
752     pathView->setDelegate(&delegateComponent);
753     QCOMPARE(delegateSpy.count(),1);
754     delete canvas;
755 }
756
757 void tst_QDeclarativePathView::modelChanges()
758 {
759     QDeclarativeView *canvas = createView();
760     QVERIFY(canvas);
761     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml"));
762
763     QDeclarativePathView *pathView = canvas->rootObject()->findChild<QDeclarativePathView*>("pathView");
764     QVERIFY(pathView);
765
766     QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
767     QVERIFY(alternateModel);
768     QVariant modelVariant = QVariant::fromValue(alternateModel);
769     QSignalSpy modelSpy(pathView, SIGNAL(modelChanged()));
770
771     pathView->setModel(modelVariant);
772     QCOMPARE(pathView->model(), modelVariant);
773     QCOMPARE(modelSpy.count(),1);
774
775     pathView->setModel(modelVariant);
776     QCOMPARE(modelSpy.count(),1);
777
778     pathView->setModel(QVariant());
779     QCOMPARE(modelSpy.count(),2);
780
781     delete canvas;
782 }
783
784 void tst_QDeclarativePathView::pathUpdateOnStartChanged()
785 {
786     QDeclarativeView *canvas = createView();
787     QVERIFY(canvas);
788     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathUpdateOnStartChanged.qml"));
789
790     QDeclarativePathView *pathView = canvas->rootObject()->findChild<QDeclarativePathView*>("pathView");
791     QVERIFY(pathView);
792
793     QDeclarativePath *path = canvas->rootObject()->findChild<QDeclarativePath*>("path");
794     QVERIFY(path);
795     QCOMPARE(path->startX(), 400.0);
796     QCOMPARE(path->startY(), 300.0);
797
798     QDeclarativeItem *item = findItem<QDeclarativeItem>(pathView, "wrapper", 0);
799     QVERIFY(item);
800     QCOMPARE(item->x(), path->startX() - item->width() / 2.0);
801     QCOMPARE(item->y(), path->startY() - item->height() / 2.0);
802
803     delete canvas;
804 }
805
806 void tst_QDeclarativePathView::package()
807 {
808     QDeclarativeView *canvas = createView();
809     QVERIFY(canvas);
810     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathview_package.qml"));
811
812     QDeclarativePathView *pathView = canvas->rootObject()->findChild<QDeclarativePathView*>("photoPathView");
813     QVERIFY(pathView);
814
815     QDeclarativeItem *item = findItem<QDeclarativeItem>(pathView, "pathItem");
816     QVERIFY(item);
817     QVERIFY(item->scale() != 1.0);
818
819     delete canvas;
820 }
821
822 //QTBUG-13017
823 void tst_QDeclarativePathView::emptyModel()
824 {
825     QDeclarativeView *canvas = createView();
826
827     QStringListModel model;
828
829     QDeclarativeContext *ctxt = canvas->rootContext();
830     ctxt->setContextProperty("emptyModel", &model);
831
832     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/emptymodel.qml"));
833     qApp->processEvents();
834
835     QDeclarativePathView *pathview = qobject_cast<QDeclarativePathView*>(canvas->rootObject());
836     QVERIFY(pathview != 0);
837
838     QCOMPARE(pathview->offset(), qreal(0.0));
839
840     delete canvas;
841 }
842
843 void tst_QDeclarativePathView::closed()
844 {
845     QDeclarativeEngine engine;
846
847     {
848         QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/openPath.qml"));
849         QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
850         QVERIFY(obj);
851         QCOMPARE(obj->isClosed(), false);
852         delete obj;
853     }
854
855     {
856         QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/closedPath.qml"));
857         QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
858         QVERIFY(obj);
859         QCOMPARE(obj->isClosed(), true);
860         delete obj;
861     }
862 }
863
864 // QTBUG-14239
865 void tst_QDeclarativePathView::pathUpdate()
866 {
867     QDeclarativeView *canvas = createView();
868     QVERIFY(canvas);
869     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/pathUpdate.qml"));
870
871     QDeclarativePathView *pathView = canvas->rootObject()->findChild<QDeclarativePathView*>("pathView");
872     QVERIFY(pathView);
873
874     QDeclarativeItem *item = findItem<QDeclarativeItem>(pathView, "wrapper", 0);
875     QVERIFY(item);
876     QCOMPARE(item->x(), 150.0);
877
878     delete canvas;
879 }
880
881 void tst_QDeclarativePathView::visualDataModel()
882 {
883     QDeclarativeEngine engine;
884     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/vdm.qml"));
885
886     QDeclarativePathView *obj = qobject_cast<QDeclarativePathView*>(c.create());
887     QVERIFY(obj != 0);
888
889     QCOMPARE(obj->count(), 3);
890
891     delete obj;
892 }
893
894 void tst_QDeclarativePathView::undefinedPath()
895 {
896     QDeclarativeEngine engine;
897     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/undefinedpath.qml"));
898
899     QDeclarativePathView *obj = qobject_cast<QDeclarativePathView*>(c.create());
900     QVERIFY(obj != 0);
901
902     QCOMPARE(obj->count(), 3);
903
904     delete obj;
905 }
906
907 void tst_QDeclarativePathView::mouseDrag()
908 {
909     QDeclarativeView *canvas = createView();
910     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/dragpath.qml"));
911     canvas->show();
912     QApplication::setActiveWindow(canvas);
913     QTest::qWaitForWindowShown(canvas);
914     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
915
916     QDeclarativePathView *pathview = qobject_cast<QDeclarativePathView*>(canvas->rootObject());
917     QVERIFY(pathview != 0);
918
919     int current = pathview->currentIndex();
920
921     QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(10,100)));
922
923     {
924         QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(30,100)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
925         QApplication::sendEvent(canvas->viewport(), &mv);
926     }
927     {
928         QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(90,100)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
929         QApplication::sendEvent(canvas->viewport(), &mv);
930     }
931
932     QVERIFY(pathview->currentIndex() != current);
933
934     QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(40,100)));
935
936     delete canvas;
937 }
938
939 void tst_QDeclarativePathView::treeModel()
940 {
941     QDeclarativeView *canvas = createView();
942
943     QStandardItemModel model;
944     initStandardTreeModel(&model);
945     canvas->engine()->rootContext()->setContextProperty("myModel", &model);
946
947     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/treemodel.qml"));
948
949     QDeclarativePathView *pathview = qobject_cast<QDeclarativePathView*>(canvas->rootObject());
950     QVERIFY(pathview != 0);
951     QCOMPARE(pathview->count(), 3);
952
953     QDeclarativeText *item = findItem<QDeclarativeText>(pathview, "wrapper", 0);
954     QVERIFY(item);
955     QCOMPARE(item->text(), QLatin1String("Row 1 Item"));
956
957     QVERIFY(QMetaObject::invokeMethod(pathview, "setRoot", Q_ARG(QVariant, 1)));
958     QCOMPARE(pathview->count(), 1);
959
960     QTRY_VERIFY(item = findItem<QDeclarativeText>(pathview, "wrapper", 0));
961     QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item"));
962
963     delete canvas;
964 }
965
966 void tst_QDeclarativePathView::changePreferredHighlight()
967 {
968     QDeclarativeView *canvas = createView();
969     canvas->setFixedSize(400,200);
970     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/dragpath.qml"));
971     canvas->show();
972     QApplication::setActiveWindow(canvas);
973     QTest::qWaitForWindowShown(canvas);
974     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
975
976     QDeclarativePathView *pathview = qobject_cast<QDeclarativePathView*>(canvas->rootObject());
977     QVERIFY(pathview != 0);
978
979     int current = pathview->currentIndex();
980     QCOMPARE(current, 0);
981
982     QDeclarativeRectangle *firstItem = findItem<QDeclarativeRectangle>(pathview, "wrapper", 0);
983     QVERIFY(firstItem);
984     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
985     QVERIFY(path);
986     QPointF start = path->pointAt(0.5);
987     start.setX(qRound(start.x()));
988     start.setY(qRound(start.y()));
989     QPointF offset;//Center of item is at point, but pos is from corner
990     offset.setX(firstItem->width()/2);
991     offset.setY(firstItem->height()/2);
992     QTRY_COMPARE(firstItem->pos() + offset, start);
993
994     pathview->setPreferredHighlightBegin(0.8);
995     pathview->setPreferredHighlightEnd(0.8);
996     start = path->pointAt(0.8);
997     start.setX(qRound(start.x()));
998     start.setY(qRound(start.y()));
999     QTRY_COMPARE(firstItem->pos() + offset, start);
1000     QCOMPARE(pathview->currentIndex(), 0);
1001
1002     delete canvas;
1003 }
1004
1005 QDeclarativeView *tst_QDeclarativePathView::createView()
1006 {
1007     QDeclarativeView *canvas = new QDeclarativeView(0);
1008     canvas->setFixedSize(240,320);
1009
1010     return canvas;
1011 }
1012
1013 /*
1014    Find an item with the specified objectName.  If index is supplied then the
1015    item must also evaluate the {index} expression equal to index
1016  */
1017 template<typename T>
1018 T *tst_QDeclarativePathView::findItem(QGraphicsObject *parent, const QString &objectName, int index)
1019 {
1020     const QMetaObject &mo = T::staticMetaObject;
1021     //qDebug() << parent->childItems().count() << "children";
1022     for (int i = 0; i < parent->childItems().count(); ++i) {
1023         QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(parent->childItems().at(i));
1024         if(!item)
1025             continue;
1026         //qDebug() << "try" << item;
1027         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
1028             if (index != -1) {
1029                 QDeclarativeExpression e(qmlContext(item), item, "index");
1030                 if (e.evaluate().toInt() == index)
1031                     return static_cast<T*>(item);
1032             } else {
1033                 return static_cast<T*>(item);
1034             }
1035         }
1036         item = findItem<T>(item, objectName, index);
1037         if (item)
1038             return static_cast<T*>(item);
1039     }
1040
1041     return 0;
1042 }
1043
1044 template<typename T>
1045 QList<T*> tst_QDeclarativePathView::findItems(QGraphicsObject *parent, const QString &objectName)
1046 {
1047     QList<T*> items;
1048     const QMetaObject &mo = T::staticMetaObject;
1049     //qDebug() << parent->QGraphicsObject::children().count() << "children";
1050     for (int i = 0; i < parent->childItems().count(); ++i) {
1051         QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(parent->childItems().at(i));
1052         if(!item)
1053             continue;
1054         //qDebug() << "try" << item;
1055         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
1056             items.append(static_cast<T*>(item));
1057         items += findItems<T>(item, objectName);
1058     }
1059
1060     return items;
1061 }
1062
1063 QTEST_MAIN(tst_QDeclarativePathView)
1064
1065 #include "tst_qdeclarativepathview.moc"