Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qdeclarativelistview / tst_qdeclarativelistview.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 <QtGui/QStringListModel>
44 #include <QtDeclarative/qdeclarativeview.h>
45 #include <QtDeclarative/qdeclarativeengine.h>
46 #include <QtDeclarative/qdeclarativecontext.h>
47 #include <QtDeclarative/qdeclarativeexpression.h>
48 #include <QtDeclarative/private/qdeclarativeitem_p.h>
49 #include <QtDeclarative/private/qdeclarativelistview_p.h>
50 #include <QtDeclarative/private/qdeclarativetext_p.h>
51 #include <QtDeclarative/private/qdeclarativevisualitemmodel_p.h>
52 #include <QtDeclarative/private/qdeclarativelistmodel_p.h>
53 #include <QtDeclarative/private/qlistmodelinterface_p.h>
54 #include "../../../shared/util.h"
55 #include "incrementalmodel.h"
56
57 #ifdef Q_OS_SYMBIAN
58 // In Symbian OS test data is located in applications private dir
59 #define SRCDIR "."
60 #endif
61
62 class tst_QDeclarativeListView : public QObject
63 {
64     Q_OBJECT
65 public:
66     tst_QDeclarativeListView();
67
68 private slots:
69     // Test both QListModelInterface and QAbstractItemModel model types
70     void qListModelInterface_items();
71     void qAbstractItemModel_items();
72
73     void qListModelInterface_changed();
74     void qAbstractItemModel_changed();
75
76     void qListModelInterface_inserted();
77     void qAbstractItemModel_inserted();
78
79     void qListModelInterface_removed();
80     void qAbstractItemModel_removed();
81
82     void qListModelInterface_moved();
83     void qAbstractItemModel_moved();
84
85     void qListModelInterface_clear();
86     void qAbstractItemModel_clear();
87
88     void itemList();
89     void currentIndex();
90     void noCurrentIndex();
91     void enforceRange();
92     void spacing();
93     void sections();
94     void sectionsDelegate();
95     void cacheBuffer();
96     void positionViewAtIndex();
97     void resetModel();
98     void propertyChanges();
99     void componentChanges();
100     void modelChanges();
101     void QTBUG_9791();
102     void manualHighlight();
103     void QTBUG_11105();
104     void header();
105     void footer();
106     void headerFooter();
107     void resizeView();
108     void sizeLessThan1();
109     void QTBUG_14821();
110     void resizeDelegate();
111     void QTBUG_16037();
112     void indexAt();
113     void incrementalModel();
114     void onAdd();
115     void onAdd_data();
116     void onRemove();
117     void onRemove_data();
118     void testQtQuick11Attributes();
119     void testQtQuick11Attributes_data();
120     void rightToLeft();
121     void test_mirroring();
122     void orientationChange();
123     void contentPosJump();
124
125 private:
126     template <class T> void items();
127     template <class T> void changed();
128     template <class T> void inserted();
129     template <class T> void removed(bool animated);
130     template <class T> void moved();
131     template <class T> void clear();
132     QDeclarativeView *createView();
133     template<typename T>
134     T *findItem(QGraphicsObject *parent, const QString &id, int index=-1);
135     template<typename T>
136     QList<T*> findItems(QGraphicsObject *parent, const QString &objectName);
137     void dumpTree(QDeclarativeItem *parent, int depth = 0);
138 };
139
140 class TestObject : public QObject
141 {
142     Q_OBJECT
143
144     Q_PROPERTY(bool error READ error WRITE setError NOTIFY changedError)
145     Q_PROPERTY(bool animate READ animate NOTIFY changedAnim)
146     Q_PROPERTY(bool invalidHighlight READ invalidHighlight NOTIFY changedHl)
147     Q_PROPERTY(int cacheBuffer READ cacheBuffer NOTIFY changedCacheBuffer)
148
149 public:
150     TestObject(QObject *parent = 0)
151         : QObject(parent), mError(true), mAnimate(false), mInvalidHighlight(false)
152         , mCacheBuffer(0) {}
153
154     bool error() const { return mError; }
155     void setError(bool err) { mError = err; emit changedError(); }
156
157     bool animate() const { return mAnimate; }
158     void setAnimate(bool anim) { mAnimate = anim; emit changedAnim(); }
159
160     bool invalidHighlight() const { return mInvalidHighlight; }
161     void setInvalidHighlight(bool invalid) { mInvalidHighlight = invalid; emit changedHl(); }
162
163     int cacheBuffer() const { return mCacheBuffer; }
164     void setCacheBuffer(int buffer) { mCacheBuffer = buffer; emit changedCacheBuffer(); }
165
166 signals:
167     void changedError();
168     void changedAnim();
169     void changedHl();
170     void changedCacheBuffer();
171
172 public:
173     bool mError;
174     bool mAnimate;
175     bool mInvalidHighlight;
176     int mCacheBuffer;
177 };
178
179 class TestModel : public QListModelInterface
180 {
181     Q_OBJECT
182 public:
183     TestModel(QObject *parent = 0) : QListModelInterface(parent) {}
184     ~TestModel() {}
185
186     enum Roles { Name, Number };
187
188     QString name(int index) const { return list.at(index).first; }
189     QString number(int index) const { return list.at(index).second; }
190
191     int count() const { return list.count(); }
192
193     QList<int> roles() const { return QList<int>() << Name << Number; }
194     QString toString(int role) const {
195         switch(role) {
196         case Name:
197             return "name";
198         case Number:
199             return "number";
200         default:
201             return "";
202         }
203     }
204
205     QVariant data(int index, int role) const
206     {
207         if (role==0)
208             return list.at(index).first;
209         if (role==1)
210             return list.at(index).second;
211         return QVariant();
212     }
213     QHash<int, QVariant> data(int index, const QList<int> &roles) const {
214         QHash<int,QVariant> returnHash;
215
216         for (int i = 0; i < roles.size(); ++i) {
217             int role = roles.at(i);
218             QVariant info;
219             switch(role) {
220             case Name:
221                 info = list.at(index).first;
222                 break;
223             case Number:
224                 info = list.at(index).second;
225                 break;
226             default:
227                 break;
228             }
229             returnHash.insert(role, info);
230         }
231         return returnHash;
232     }
233
234     void addItem(const QString &name, const QString &number) {
235         list.append(QPair<QString,QString>(name, number));
236         emit itemsInserted(list.count()-1, 1);
237     }
238
239     void insertItem(int index, const QString &name, const QString &number) {
240         list.insert(index, QPair<QString,QString>(name, number));
241         emit itemsInserted(index, 1);
242     }
243
244     void removeItem(int index) {
245         list.removeAt(index);
246         emit itemsRemoved(index, 1);
247     }
248
249     void removeItems(int index, int count) {
250         int c = count;
251         while (c--)
252             list.removeAt(index);
253         emit itemsRemoved(index, count);
254     }
255
256     void moveItem(int from, int to) {
257         list.move(from, to);
258         emit itemsMoved(from, to, 1);
259     }
260
261     void modifyItem(int index, const QString &name, const QString &number) {
262         list[index] = QPair<QString,QString>(name, number);
263         emit itemsChanged(index, 1, roles());
264     }
265
266     void clear() {
267         int count = list.count();
268         list.clear();
269         emit itemsRemoved(0, count);
270     }
271
272 private:
273     QList<QPair<QString,QString> > list;
274 };
275
276
277 class TestModel2 : public QAbstractListModel
278 {
279 public:
280     enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
281
282     TestModel2(QObject *parent=0) : QAbstractListModel(parent) {
283         QHash<int, QByteArray> roles;
284         roles[Name] = "name";
285         roles[Number] = "number";
286         setRoleNames(roles);
287     }
288
289     int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
290     QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
291         QVariant rv;
292         if (role == Name)
293             rv = list.at(index.row()).first;
294         else if (role == Number)
295             rv = list.at(index.row()).second;
296
297         return rv;
298     }
299
300     int count() const { return rowCount(); }
301     QString name(int index) const { return list.at(index).first; }
302     QString number(int index) const { return list.at(index).second; }
303
304     void addItem(const QString &name, const QString &number) {
305         emit beginInsertRows(QModelIndex(), list.count(), list.count());
306         list.append(QPair<QString,QString>(name, number));
307         emit endInsertRows();
308     }
309
310     void addItems(const QList<QPair<QString, QString> > &items) {
311         emit beginInsertRows(QModelIndex(), list.count(), list.count()+items.count()-1);
312         for (int i=0; i<items.count(); i++)
313             list.append(QPair<QString,QString>(items[i].first, items[i].second));
314         emit endInsertRows();
315     }
316
317     void insertItem(int index, const QString &name, const QString &number) {
318         emit beginInsertRows(QModelIndex(), index, index);
319         list.insert(index, QPair<QString,QString>(name, number));
320         emit endInsertRows();
321     }
322
323     void removeItem(int index) {
324         emit beginRemoveRows(QModelIndex(), index, index);
325         list.removeAt(index);
326         emit endRemoveRows();
327     }
328
329     void removeItems(int index, int count) {
330         emit beginRemoveRows(QModelIndex(), index, index+count-1);
331         while (count--)
332             list.removeAt(index);
333         emit endRemoveRows();
334     }
335
336     void moveItem(int from, int to) {
337         emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
338         list.move(from, to);
339         emit endMoveRows();
340     }
341
342     void modifyItem(int idx, const QString &name, const QString &number) {
343         list[idx] = QPair<QString,QString>(name, number);
344         emit dataChanged(index(idx,0), index(idx,0));
345     }
346
347     void clear() {
348         int count = list.count();
349         emit beginRemoveRows(QModelIndex(), 0, count-1);
350         list.clear();
351         emit endRemoveRows();
352     }
353
354 private:
355     QList<QPair<QString,QString> > list;
356 };
357
358 tst_QDeclarativeListView::tst_QDeclarativeListView()
359 {
360 }
361
362 template <class T>
363 void tst_QDeclarativeListView::items()
364 {
365     QDeclarativeView *canvas = createView();
366
367     T model;
368     model.addItem("Fred", "12345");
369     model.addItem("John", "2345");
370     model.addItem("Bob", "54321");
371
372     QDeclarativeContext *ctxt = canvas->rootContext();
373     ctxt->setContextProperty("testModel", &model);
374
375     TestObject *testObject = new TestObject;
376     ctxt->setContextProperty("testObject", testObject);
377
378     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
379     qApp->processEvents();
380
381     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
382     QTRY_VERIFY(listview != 0);
383
384     QDeclarativeItem *contentItem = listview->contentItem();
385     QTRY_VERIFY(contentItem != 0);
386
387     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
388     QTRY_VERIFY(testObject->error() == false);
389
390     QTRY_VERIFY(listview->highlightItem() != 0);
391     QTRY_COMPARE(listview->count(), model.count());
392     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
393
394     // current item should be first item
395     QTRY_COMPARE(listview->currentItem(), findItem<QDeclarativeItem>(contentItem, "wrapper", 0));
396
397     for (int i = 0; i < model.count(); ++i) {
398         QDeclarativeText *name = findItem<QDeclarativeText>(contentItem, "textName", i);
399         QTRY_VERIFY(name != 0);
400         QTRY_COMPARE(name->text(), model.name(i));
401         QDeclarativeText *number = findItem<QDeclarativeText>(contentItem, "textNumber", i);
402         QTRY_VERIFY(number != 0);
403         QTRY_COMPARE(number->text(), model.number(i));
404     }
405
406     // switch to other delegate
407     testObject->setAnimate(true);
408     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
409     QTRY_VERIFY(testObject->error() == false);
410     QTRY_VERIFY(listview->currentItem());
411
412     // set invalid highlight
413     testObject->setInvalidHighlight(true);
414     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
415     QTRY_VERIFY(testObject->error() == false);
416     QTRY_VERIFY(listview->currentItem());
417     QTRY_VERIFY(listview->highlightItem() == 0);
418
419     // back to normal highlight
420     testObject->setInvalidHighlight(false);
421     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
422     QTRY_VERIFY(testObject->error() == false);
423     QTRY_VERIFY(listview->currentItem());
424     QTRY_VERIFY(listview->highlightItem() != 0);
425
426     // set an empty model and confirm that items are destroyed
427     T model2;
428     ctxt->setContextProperty("testModel", &model2);
429
430     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
431     QTRY_VERIFY(itemCount == 0);
432
433     QTRY_COMPARE(listview->highlightResizeSpeed(), 1000.0);
434     QTRY_COMPARE(listview->highlightMoveSpeed(), 1000.0);
435
436     delete canvas;
437 }
438
439
440 template <class T>
441 void tst_QDeclarativeListView::changed()
442 {
443     QDeclarativeView *canvas = createView();
444
445     T model;
446     model.addItem("Fred", "12345");
447     model.addItem("John", "2345");
448     model.addItem("Bob", "54321");
449
450     QDeclarativeContext *ctxt = canvas->rootContext();
451     ctxt->setContextProperty("testModel", &model);
452
453     TestObject *testObject = new TestObject;
454     ctxt->setContextProperty("testObject", testObject);
455
456     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
457     qApp->processEvents();
458
459     QDeclarativeFlickable *listview = findItem<QDeclarativeFlickable>(canvas->rootObject(), "list");
460     QTRY_VERIFY(listview != 0);
461
462     QDeclarativeItem *contentItem = listview->contentItem();
463     QTRY_VERIFY(contentItem != 0);
464
465     model.modifyItem(1, "Will", "9876");
466     QDeclarativeText *name = findItem<QDeclarativeText>(contentItem, "textName", 1);
467     QTRY_VERIFY(name != 0);
468     QTRY_COMPARE(name->text(), model.name(1));
469     QDeclarativeText *number = findItem<QDeclarativeText>(contentItem, "textNumber", 1);
470     QTRY_VERIFY(number != 0);
471     QTRY_COMPARE(number->text(), model.number(1));
472
473     delete canvas;
474 }
475
476 template <class T>
477 void tst_QDeclarativeListView::inserted()
478 {
479     QDeclarativeView *canvas = createView();
480
481     T model;
482     model.addItem("Fred", "12345");
483     model.addItem("John", "2345");
484     model.addItem("Bob", "54321");
485
486     QDeclarativeContext *ctxt = canvas->rootContext();
487     ctxt->setContextProperty("testModel", &model);
488
489     TestObject *testObject = new TestObject;
490     ctxt->setContextProperty("testObject", testObject);
491
492     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
493     qApp->processEvents();
494
495     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
496     QTRY_VERIFY(listview != 0);
497
498     QDeclarativeItem *contentItem = listview->contentItem();
499     QTRY_VERIFY(contentItem != 0);
500
501     model.insertItem(1, "Will", "9876");
502
503     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
504
505     QDeclarativeText *name = findItem<QDeclarativeText>(contentItem, "textName", 1);
506     QTRY_VERIFY(name != 0);
507     QTRY_COMPARE(name->text(), model.name(1));
508     QDeclarativeText *number = findItem<QDeclarativeText>(contentItem, "textNumber", 1);
509     QTRY_VERIFY(number != 0);
510     QTRY_COMPARE(number->text(), model.number(1));
511
512     // Confirm items positioned correctly
513     for (int i = 0; i < model.count(); ++i) {
514         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
515         QTRY_COMPARE(item->y(), i*20.0);
516     }
517
518     model.insertItem(0, "Foo", "1111"); // zero index, and current item
519
520     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
521
522     name = findItem<QDeclarativeText>(contentItem, "textName", 0);
523     QTRY_VERIFY(name != 0);
524     QTRY_COMPARE(name->text(), model.name(0));
525     number = findItem<QDeclarativeText>(contentItem, "textNumber", 0);
526     QTRY_VERIFY(number != 0);
527     QTRY_COMPARE(number->text(), model.number(0));
528
529     QTRY_COMPARE(listview->currentIndex(), 1);
530
531     // Confirm items positioned correctly
532     for (int i = 0; i < model.count(); ++i) {
533         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
534         QTRY_COMPARE(item->y(), i*20.0);
535     }
536
537     for (int i = model.count(); i < 30; ++i)
538         model.insertItem(i, "Hello", QString::number(i));
539
540     listview->setContentY(80);
541
542     // Insert item outside visible area
543     model.insertItem(1, "Hello", "1324");
544
545     QTRY_VERIFY(listview->contentY() == 80);
546
547     // Confirm items positioned correctly
548     for (int i = 5; i < 5+15; ++i) {
549         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
550         if (!item) qWarning() << "Item" << i << "not found";
551         QTRY_VERIFY(item);
552         QTRY_COMPARE(item->y(), i*20.0 - 20.0);
553     }
554
555 //    QTRY_COMPARE(listview->contentItemHeight(), model.count() * 20.0);
556
557     delete canvas;
558 }
559
560 template <class T>
561 void tst_QDeclarativeListView::removed(bool animated)
562 {
563     QDeclarativeView *canvas = createView();
564
565     T model;
566     for (int i = 0; i < 50; i++)
567         model.addItem("Item" + QString::number(i), "");
568
569     QDeclarativeContext *ctxt = canvas->rootContext();
570     ctxt->setContextProperty("testModel", &model);
571
572     TestObject *testObject = new TestObject;
573     testObject->setAnimate(animated);
574     ctxt->setContextProperty("testObject", testObject);
575
576     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
577     qApp->processEvents();
578
579     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
580     QTRY_VERIFY(listview != 0);
581
582     QDeclarativeItem *contentItem = listview->contentItem();
583     QTRY_VERIFY(contentItem != 0);
584
585     model.removeItem(1);
586
587     QDeclarativeText *name = findItem<QDeclarativeText>(contentItem, "textName", 1);
588     QTRY_VERIFY(name != 0);
589     QTRY_COMPARE(name->text(), model.name(1));
590     QDeclarativeText *number = findItem<QDeclarativeText>(contentItem, "textNumber", 1);
591     QTRY_VERIFY(number != 0);
592     QTRY_COMPARE(number->text(), model.number(1));
593
594     // Confirm items positioned correctly
595     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
596     for (int i = 0; i < model.count() && i < itemCount; ++i) {
597         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
598         if (!item) qWarning() << "Item" << i << "not found";
599         QTRY_VERIFY(item);
600         QTRY_VERIFY(item->y() == i*20);
601     }
602
603     // Remove first item (which is the current item);
604     model.removeItem(0);  // post: top item starts at 20
605
606     QTest::qWait(300);
607
608     name = findItem<QDeclarativeText>(contentItem, "textName", 0);
609     QTRY_VERIFY(name != 0);
610     QTRY_COMPARE(name->text(), model.name(0));
611     number = findItem<QDeclarativeText>(contentItem, "textNumber", 0);
612     QTRY_VERIFY(number != 0);
613     QTRY_COMPARE(number->text(), model.number(0));
614
615     // Confirm items positioned correctly
616     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
617     for (int i = 0; i < model.count() && i < itemCount; ++i) {
618         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
619         if (!item) qWarning() << "Item" << i << "not found";
620         QTRY_VERIFY(item);
621         QTRY_COMPARE(item->y(),i*20.0 + 20.0);
622     }
623
624     // Remove items not visible
625     model.removeItem(18);
626     qApp->processEvents();
627
628     // Confirm items positioned correctly
629     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
630     for (int i = 0; i < model.count() && i < itemCount; ++i) {
631         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
632         if (!item) qWarning() << "Item" << i << "not found";
633         QTRY_VERIFY(item);
634         QTRY_COMPARE(item->y(),i*20.0+20.0);
635     }
636
637     // Remove items before visible
638     listview->setContentY(80);
639     listview->setCurrentIndex(10);
640
641     model.removeItem(1); // post: top item will be at 40
642     qApp->processEvents();
643
644     // Confirm items positioned correctly
645     for (int i = 2; i < 18; ++i) {
646         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
647         if (!item) qWarning() << "Item" << i << "not found";
648         QTRY_VERIFY(item);
649         QTRY_COMPARE(item->y(),40+i*20.0);
650     }
651
652     // Remove current index
653     QTRY_VERIFY(listview->currentIndex() == 9);
654     QDeclarativeItem *oldCurrent = listview->currentItem();
655     model.removeItem(9);
656
657     QTRY_COMPARE(listview->currentIndex(), 9);
658     QTRY_VERIFY(listview->currentItem() != oldCurrent);
659
660     listview->setContentY(40); // That's the top now
661     // let transitions settle.
662     QTest::qWait(300);
663
664     // Confirm items positioned correctly
665     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
666     for (int i = 0; i < model.count() && i < itemCount; ++i) {
667         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
668         if (!item) qWarning() << "Item" << i << "not found";
669         QTRY_VERIFY(item);
670         QTRY_COMPARE(item->y(),40+i*20.0);
671     }
672
673     // remove current item beyond visible items.
674     listview->setCurrentIndex(20);
675     listview->setContentY(40);
676     model.removeItem(20);
677
678     QTRY_COMPARE(listview->currentIndex(), 20);
679     QTRY_VERIFY(listview->currentItem() != 0);
680
681     // remove item before current, but visible
682     listview->setCurrentIndex(8);
683     oldCurrent = listview->currentItem();
684     model.removeItem(6);
685
686     QTRY_COMPARE(listview->currentIndex(), 7);
687     QTRY_VERIFY(listview->currentItem() == oldCurrent);
688
689     listview->setContentY(80);
690     QTest::qWait(300);
691
692     // remove all visible items
693     model.removeItems(1, 18);
694     QTest::qWait(300);
695
696     // Confirm items positioned correctly
697     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
698     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
699         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i+2);
700         if (!item) qWarning() << "Item" << i+2 << "not found";
701         QTRY_VERIFY(item);
702         QTRY_COMPARE(item->y(),80+i*20.0);
703     }
704
705     model.removeItems(1, 17);
706 //    QTest::qWait(300);
707
708     model.removeItems(2, 1);
709     model.addItem("New", "1");
710
711     QTRY_VERIFY(name = findItem<QDeclarativeText>(contentItem, "textName", model.count()-1));
712     QCOMPARE(name->text(), QString("New"));
713
714     delete canvas;
715 }
716
717 template <class T>
718 void tst_QDeclarativeListView::clear()
719 {
720     QDeclarativeView *canvas = createView();
721
722     T model;
723     for (int i = 0; i < 30; i++)
724         model.addItem("Item" + QString::number(i), "");
725
726     QDeclarativeContext *ctxt = canvas->rootContext();
727     ctxt->setContextProperty("testModel", &model);
728
729     TestObject *testObject = new TestObject;
730     ctxt->setContextProperty("testObject", testObject);
731
732     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
733     qApp->processEvents();
734
735     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
736     QTRY_VERIFY(listview != 0);
737
738     QDeclarativeItem *contentItem = listview->contentItem();
739     QTRY_VERIFY(contentItem != 0);
740
741     model.clear();
742
743     QTRY_VERIFY(listview->count() == 0);
744     QTRY_VERIFY(listview->currentItem() == 0);
745     QTRY_VERIFY(listview->contentY() == 0);
746     QVERIFY(listview->currentIndex() == -1);
747
748     // confirm sanity when adding an item to cleared list
749     model.addItem("New", "1");
750     QTRY_VERIFY(listview->count() == 1);
751     QVERIFY(listview->currentItem() != 0);
752     QVERIFY(listview->currentIndex() == 0);
753
754     delete canvas;
755 }
756
757
758 template <class T>
759 void tst_QDeclarativeListView::moved()
760 {
761     QDeclarativeView *canvas = createView();
762
763     T model;
764     for (int i = 0; i < 30; i++)
765         model.addItem("Item" + QString::number(i), "");
766
767     QDeclarativeContext *ctxt = canvas->rootContext();
768     ctxt->setContextProperty("testModel", &model);
769
770     TestObject *testObject = new TestObject;
771     ctxt->setContextProperty("testObject", testObject);
772
773     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
774     qApp->processEvents();
775
776     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
777     QTRY_VERIFY(listview != 0);
778
779     QDeclarativeItem *contentItem = listview->contentItem();
780     QTRY_VERIFY(contentItem != 0);
781
782     model.moveItem(1, 4);
783
784     QDeclarativeText *name = findItem<QDeclarativeText>(contentItem, "textName", 1);
785     QTRY_VERIFY(name != 0);
786     QTRY_COMPARE(name->text(), model.name(1));
787     QDeclarativeText *number = findItem<QDeclarativeText>(contentItem, "textNumber", 1);
788     QTRY_VERIFY(number != 0);
789     QTRY_COMPARE(number->text(), model.number(1));
790
791     name = findItem<QDeclarativeText>(contentItem, "textName", 4);
792     QTRY_VERIFY(name != 0);
793     QTRY_COMPARE(name->text(), model.name(4));
794     number = findItem<QDeclarativeText>(contentItem, "textNumber", 4);
795     QTRY_VERIFY(number != 0);
796     QTRY_COMPARE(number->text(), model.number(4));
797
798     // Confirm items positioned correctly
799     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
800     for (int i = 0; i < model.count() && i < itemCount; ++i) {
801         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
802         if (!item) qWarning() << "Item" << i << "not found";
803         QTRY_VERIFY(item);
804         QTRY_VERIFY(item->y() == i*20);
805     }
806
807     listview->setContentY(80);
808
809     // move outside visible area
810     model.moveItem(1, 18);
811
812     // Confirm items positioned correctly and indexes correct
813     for (int i = 3; i < model.count() && i < itemCount; ++i) {
814         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
815         if (!item) qWarning() << "Item" << i << "not found";
816         QTRY_VERIFY(item);
817         QTRY_COMPARE(item->y(), i*20.0 + 20);
818         name = findItem<QDeclarativeText>(contentItem, "textName", i);
819         QTRY_VERIFY(name != 0);
820         QTRY_COMPARE(name->text(), model.name(i));
821         number = findItem<QDeclarativeText>(contentItem, "textNumber", i);
822         QTRY_VERIFY(number != 0);
823         QTRY_COMPARE(number->text(), model.number(i));
824     }
825
826     // move from outside visible into visible
827     model.moveItem(20, 4);
828
829     // Confirm items positioned correctly and indexes correct
830     for (int i = 3; i < model.count() && i < itemCount; ++i) {
831         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
832         if (!item) qWarning() << "Item" << i << "not found";
833         QTRY_VERIFY(item);
834         QTRY_COMPARE(item->y(), i*20.0 + 20);
835         name = findItem<QDeclarativeText>(contentItem, "textName", i);
836         QTRY_VERIFY(name != 0);
837         QTRY_COMPARE(name->text(), model.name(i));
838         number = findItem<QDeclarativeText>(contentItem, "textNumber", i);
839         QTRY_VERIFY(number != 0);
840         QTRY_COMPARE(number->text(), model.number(i));
841     }
842
843     delete canvas;
844 }
845
846 void tst_QDeclarativeListView::enforceRange()
847 {
848     QDeclarativeView *canvas = createView();
849
850     TestModel model;
851     for (int i = 0; i < 30; i++)
852         model.addItem("Item" + QString::number(i), "");
853
854     QDeclarativeContext *ctxt = canvas->rootContext();
855     ctxt->setContextProperty("testModel", &model);
856
857     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listview-enforcerange.qml"));
858     qApp->processEvents();
859
860     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
861     QTRY_VERIFY(listview != 0);
862
863     QTRY_COMPARE(listview->preferredHighlightBegin(), 100.0);
864     QTRY_COMPARE(listview->preferredHighlightEnd(), 100.0);
865     QTRY_COMPARE(listview->highlightRangeMode(), QDeclarativeListView::StrictlyEnforceRange);
866
867     QDeclarativeItem *contentItem = listview->contentItem();
868     QTRY_VERIFY(contentItem != 0);
869
870     // view should be positioned at the top of the range.
871     QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", 0);
872     QTRY_VERIFY(item);
873     QTRY_COMPARE(listview->contentY(), -100.0);
874
875     QDeclarativeText *name = findItem<QDeclarativeText>(contentItem, "textName", 0);
876     QTRY_VERIFY(name != 0);
877     QTRY_COMPARE(name->text(), model.name(0));
878     QDeclarativeText *number = findItem<QDeclarativeText>(contentItem, "textNumber", 0);
879     QTRY_VERIFY(number != 0);
880     QTRY_COMPARE(number->text(), model.number(0));
881
882     // Check currentIndex is updated when contentItem moves
883     listview->setContentY(20);
884
885     QTRY_COMPARE(listview->currentIndex(), 6);
886
887     // change model
888     TestModel model2;
889     for (int i = 0; i < 5; i++)
890         model2.addItem("Item" + QString::number(i), "");
891
892     ctxt->setContextProperty("testModel", &model2);
893     QCOMPARE(listview->count(), 5);
894
895     delete canvas;
896 }
897
898 void tst_QDeclarativeListView::spacing()
899 {
900     QDeclarativeView *canvas = createView();
901
902     TestModel model;
903     for (int i = 0; i < 30; i++)
904         model.addItem("Item" + QString::number(i), "");
905
906     QDeclarativeContext *ctxt = canvas->rootContext();
907     ctxt->setContextProperty("testModel", &model);
908
909     TestObject *testObject = new TestObject;
910     ctxt->setContextProperty("testObject", testObject);
911
912     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
913     qApp->processEvents();
914
915     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
916     QTRY_VERIFY(listview != 0);
917
918     QDeclarativeItem *contentItem = listview->contentItem();
919     QTRY_VERIFY(contentItem != 0);
920
921     // Confirm items positioned correctly
922     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
923     for (int i = 0; i < model.count() && i < itemCount; ++i) {
924         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
925         if (!item) qWarning() << "Item" << i << "not found";
926         QTRY_VERIFY(item);
927         QTRY_VERIFY(item->y() == i*20);
928     }
929
930     listview->setSpacing(10);
931     QTRY_VERIFY(listview->spacing() == 10);
932
933     // Confirm items positioned correctly
934     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
935     for (int i = 0; i < model.count() && i < itemCount; ++i) {
936         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
937         if (!item) qWarning() << "Item" << i << "not found";
938         QTRY_VERIFY(item);
939         QTRY_VERIFY(item->y() == i*30);
940     }
941
942     listview->setSpacing(0);
943
944     // Confirm items positioned correctly
945     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
946     for (int i = 0; i < model.count() && i < itemCount; ++i) {
947         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
948         if (!item) qWarning() << "Item" << i << "not found";
949         QTRY_VERIFY(item);
950         QTRY_COMPARE(item->y(), i*20.0);
951     }
952
953     delete canvas;
954 }
955
956 void tst_QDeclarativeListView::sections()
957 {
958     QDeclarativeView *canvas = createView();
959
960     TestModel model;
961     for (int i = 0; i < 30; i++)
962         model.addItem("Item" + QString::number(i), QString::number(i/5));
963
964     QDeclarativeContext *ctxt = canvas->rootContext();
965     ctxt->setContextProperty("testModel", &model);
966
967     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listview-sections.qml"));
968     qApp->processEvents();
969
970     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
971     QTRY_VERIFY(listview != 0);
972
973     QDeclarativeItem *contentItem = listview->contentItem();
974     QTRY_VERIFY(contentItem != 0);
975
976     // Confirm items positioned correctly
977     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
978     for (int i = 0; i < model.count() && i < itemCount; ++i) {
979         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
980         QTRY_VERIFY(item);
981         QTRY_COMPARE(item->y(), qreal(i*20 + ((i+4)/5) * 20));
982         QDeclarativeText *next = findItem<QDeclarativeText>(item, "nextSection");
983         QCOMPARE(next->text().toInt(), (i+1)/5);
984     }
985
986     QSignalSpy currentSectionChangedSpy(listview, SIGNAL(currentSectionChanged()));
987
988     // Remove section boundary
989     model.removeItem(5);
990
991     // New section header created
992     QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", 5);
993     QTRY_VERIFY(item);
994     QTRY_COMPARE(item->height(), 40.0);
995
996     model.insertItem(3, "New Item", "0");
997
998     // Section header moved
999     item = findItem<QDeclarativeItem>(contentItem, "wrapper", 5);
1000     QTRY_VERIFY(item);
1001     QTRY_COMPARE(item->height(), 20.0);
1002
1003     item = findItem<QDeclarativeItem>(contentItem, "wrapper", 6);
1004     QTRY_VERIFY(item);
1005     QTRY_COMPARE(item->height(), 40.0);
1006
1007     // insert item which will become a section header
1008     model.insertItem(6, "Replace header", "1");
1009
1010     item = findItem<QDeclarativeItem>(contentItem, "wrapper", 6);
1011     QTRY_VERIFY(item);
1012     QTRY_COMPARE(item->height(), 40.0);
1013
1014     item = findItem<QDeclarativeItem>(contentItem, "wrapper", 7);
1015     QTRY_VERIFY(item);
1016     QTRY_COMPARE(item->height(), 20.0);
1017
1018     QTRY_COMPARE(listview->currentSection(), QString("0"));
1019
1020     listview->setContentY(140);
1021     QTRY_COMPARE(listview->currentSection(), QString("1"));
1022
1023     QTRY_COMPARE(currentSectionChangedSpy.count(), 1);
1024
1025     listview->setContentY(20);
1026     QTRY_COMPARE(listview->currentSection(), QString("0"));
1027
1028     QTRY_COMPARE(currentSectionChangedSpy.count(), 2);
1029
1030     item = findItem<QDeclarativeItem>(contentItem, "wrapper", 1);
1031     QTRY_VERIFY(item);
1032     QTRY_COMPARE(item->height(), 20.0);
1033
1034     // check that headers change when item changes
1035     listview->setContentY(0);
1036     model.modifyItem(0, "changed", "2");
1037
1038     item = findItem<QDeclarativeItem>(contentItem, "wrapper", 1);
1039     QTRY_VERIFY(item);
1040     QTRY_COMPARE(item->height(), 40.0);
1041
1042     delete canvas;
1043 }
1044
1045 void tst_QDeclarativeListView::sectionsDelegate()
1046 {
1047     QDeclarativeView *canvas = createView();
1048
1049     TestModel model;
1050     for (int i = 0; i < 30; i++)
1051         model.addItem("Item" + QString::number(i), QString::number(i/5));
1052
1053     QDeclarativeContext *ctxt = canvas->rootContext();
1054     ctxt->setContextProperty("testModel", &model);
1055
1056     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listview-sections_delegate.qml"));
1057     qApp->processEvents();
1058
1059     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1060     QTRY_VERIFY(listview != 0);
1061
1062     QDeclarativeItem *contentItem = listview->contentItem();
1063     QTRY_VERIFY(contentItem != 0);
1064
1065     // Confirm items positioned correctly
1066     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1067     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1068         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1069         QTRY_VERIFY(item);
1070         QTRY_COMPARE(item->y(), qreal(i*20 + ((i+5)/5) * 20));
1071         QDeclarativeText *next = findItem<QDeclarativeText>(item, "nextSection");
1072         QCOMPARE(next->text().toInt(), (i+1)/5);
1073     }
1074
1075     for (int i = 0; i < 3; ++i) {
1076         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "sect_" + QString::number(i));
1077         QVERIFY(item);
1078         QTRY_COMPARE(item->y(), qreal(i*20*6));
1079     }
1080
1081     model.modifyItem(0, "One", "aaa");
1082     model.modifyItem(1, "Two", "aaa");
1083     model.modifyItem(2, "Three", "aaa");
1084     model.modifyItem(3, "Four", "aaa");
1085     model.modifyItem(4, "Five", "aaa");
1086
1087     for (int i = 0; i < 3; ++i) {
1088         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem,
1089                 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
1090         QVERIFY(item);
1091         QTRY_COMPARE(item->y(), qreal(i*20*6));
1092     }
1093
1094     // remove section boundary
1095     model.removeItem(5);
1096     qApp->processEvents();
1097     for (int i = 0; i < 3; ++i) {
1098         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem,
1099                 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
1100         QVERIFY(item);
1101     }
1102
1103     // QTBUG-17606
1104     QList<QDeclarativeItem*> items = findItems<QDeclarativeItem>(contentItem, "sect_1");
1105     QCOMPARE(items.count(), 1);
1106
1107     // QTBUG-17759
1108     model.modifyItem(0, "One", "aaa");
1109     model.modifyItem(1, "One", "aaa");
1110     model.modifyItem(2, "One", "aaa");
1111     model.modifyItem(3, "Four", "aaa");
1112     model.modifyItem(4, "Four", "aaa");
1113     model.modifyItem(5, "Four", "aaa");
1114     model.modifyItem(6, "Five", "aaa");
1115     model.modifyItem(7, "Five", "aaa");
1116     model.modifyItem(8, "Five", "aaa");
1117     model.modifyItem(9, "Two", "aaa");
1118     model.modifyItem(10, "Two", "aaa");
1119     model.modifyItem(11, "Two", "aaa");
1120     QTRY_COMPARE(findItems<QDeclarativeItem>(contentItem, "sect_aaa").count(), 1);
1121     canvas->rootObject()->setProperty("sectionProperty", "name");
1122     // ensure view has settled.
1123     QTRY_COMPARE(findItems<QDeclarativeItem>(contentItem, "sect_Four").count(), 1);
1124     for (int i = 0; i < 4; ++i) {
1125         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem,
1126                 "sect_" + model.name(i*3));
1127         QVERIFY(item);
1128         QTRY_COMPARE(item->y(), qreal(i*20*4));
1129     }
1130
1131     // QTBUG-17769
1132     model.removeItems(10, 20);
1133     // ensure view has settled.
1134     QTRY_COMPARE(findItems<QDeclarativeItem>(contentItem, "wrapper").count(), 10);
1135     // Drag view up beyond bounds
1136     QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(20,20)));
1137     {
1138         QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(20,0)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1139         QApplication::sendEvent(canvas->viewport(), &mv);
1140     }
1141     {
1142         QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(20,-50)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1143         QApplication::sendEvent(canvas->viewport(), &mv);
1144     }
1145     {
1146         QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(20,-200)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1147         QApplication::sendEvent(canvas->viewport(), &mv);
1148     }
1149     QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(20,-200)));
1150     // view should settle back at 0
1151     QTRY_COMPARE(listview->contentY(), 0.0);
1152
1153     delete canvas;
1154 }
1155
1156 void tst_QDeclarativeListView::currentIndex()
1157 {
1158     TestModel model;
1159     for (int i = 0; i < 30; i++)
1160         model.addItem("Item" + QString::number(i), QString::number(i));
1161
1162     QDeclarativeView *canvas = new QDeclarativeView(0);
1163     canvas->setFixedSize(240,320);
1164
1165     QDeclarativeContext *ctxt = canvas->rootContext();
1166     ctxt->setContextProperty("testModel", &model);
1167     ctxt->setContextProperty("testWrap", QVariant(false));
1168
1169     QString filename(SRCDIR "/data/listview-initCurrent.qml");
1170     canvas->setSource(QUrl::fromLocalFile(filename));
1171
1172     qApp->processEvents();
1173
1174     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1175     QTRY_VERIFY(listview != 0);
1176
1177     QDeclarativeItem *contentItem = listview->contentItem();
1178     QTRY_VERIFY(contentItem != 0);
1179
1180     // current item should be 20th item at startup
1181     // and current item should be in view
1182     QCOMPARE(listview->currentIndex(), 20);
1183     QCOMPARE(listview->contentY(), 100.0);
1184     QCOMPARE(listview->currentItem(), findItem<QDeclarativeItem>(contentItem, "wrapper", 20));
1185     QCOMPARE(listview->highlightItem()->y(), listview->currentItem()->y());
1186
1187     // no wrap
1188     listview->setCurrentIndex(0);
1189     QCOMPARE(listview->currentIndex(), 0);
1190     // confirm that the velocity is updated
1191     QTRY_VERIFY(listview->verticalVelocity() != 0.0);
1192
1193     listview->incrementCurrentIndex();
1194     QCOMPARE(listview->currentIndex(), 1);
1195     listview->decrementCurrentIndex();
1196     QCOMPARE(listview->currentIndex(), 0);
1197
1198     listview->decrementCurrentIndex();
1199     QCOMPARE(listview->currentIndex(), 0);
1200
1201     // with wrap
1202     ctxt->setContextProperty("testWrap", QVariant(true));
1203     QVERIFY(listview->isWrapEnabled());
1204
1205     listview->decrementCurrentIndex();
1206     QCOMPARE(listview->currentIndex(), model.count()-1);
1207
1208     QTRY_COMPARE(listview->contentY(), 280.0);
1209
1210     listview->incrementCurrentIndex();
1211     QCOMPARE(listview->currentIndex(), 0);
1212
1213     QTRY_COMPARE(listview->contentY(), 0.0);
1214
1215     // Test keys
1216     canvas->show();
1217     qApp->setActiveWindow(canvas);
1218 #ifdef Q_WS_X11
1219     // to be safe and avoid failing setFocus with window managers
1220     qt_x11_wait_for_window_manager(canvas);
1221 #endif
1222     QTRY_VERIFY(canvas->hasFocus());
1223     QTRY_VERIFY(canvas->scene()->hasFocus());
1224     qApp->processEvents();
1225
1226     QTest::keyClick(canvas, Qt::Key_Down);
1227     QCOMPARE(listview->currentIndex(), 1);
1228
1229     QTest::keyClick(canvas, Qt::Key_Up);
1230     QCOMPARE(listview->currentIndex(), 0);
1231
1232     // turn off auto highlight
1233     listview->setHighlightFollowsCurrentItem(false);
1234     QVERIFY(listview->highlightFollowsCurrentItem() == false);
1235
1236     QVERIFY(listview->highlightItem());
1237     qreal hlPos = listview->highlightItem()->y();
1238
1239     listview->setCurrentIndex(4);
1240     QTRY_COMPARE(listview->highlightItem()->y(), hlPos);
1241
1242     // insert item before currentIndex
1243     listview->setCurrentIndex(28);
1244     model.insertItem(0, "Foo", "1111");
1245     QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
1246
1247     // check removing highlight by setting currentIndex to -1;
1248     listview->setCurrentIndex(-1);
1249
1250     QCOMPARE(listview->currentIndex(), -1);
1251     QVERIFY(!listview->highlightItem());
1252     QVERIFY(!listview->currentItem());
1253
1254     delete canvas;
1255 }
1256
1257 void tst_QDeclarativeListView::noCurrentIndex()
1258 {
1259     TestModel model;
1260     for (int i = 0; i < 30; i++)
1261         model.addItem("Item" + QString::number(i), QString::number(i));
1262
1263     QDeclarativeView *canvas = new QDeclarativeView(0);
1264     canvas->setFixedSize(240,320);
1265
1266     QDeclarativeContext *ctxt = canvas->rootContext();
1267     ctxt->setContextProperty("testModel", &model);
1268
1269     QString filename(SRCDIR "/data/listview-noCurrent.qml");
1270     canvas->setSource(QUrl::fromLocalFile(filename));
1271
1272     qApp->processEvents();
1273
1274     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1275     QTRY_VERIFY(listview != 0);
1276
1277     QDeclarativeItem *contentItem = listview->contentItem();
1278     QTRY_VERIFY(contentItem != 0);
1279
1280     // current index should be -1 at startup
1281     // and we should not have a currentItem or highlightItem
1282     QCOMPARE(listview->currentIndex(), -1);
1283     QCOMPARE(listview->contentY(), 0.0);
1284     QVERIFY(!listview->highlightItem());
1285     QVERIFY(!listview->currentItem());
1286
1287     listview->setCurrentIndex(2);
1288     QCOMPARE(listview->currentIndex(), 2);
1289     QVERIFY(listview->highlightItem());
1290     QVERIFY(listview->currentItem());
1291 }
1292
1293 void tst_QDeclarativeListView::itemList()
1294 {
1295     QDeclarativeView *canvas = createView();
1296
1297     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/itemlist.qml"));
1298     qApp->processEvents();
1299
1300     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "view");
1301     QTRY_VERIFY(listview != 0);
1302
1303     QDeclarativeItem *contentItem = listview->contentItem();
1304     QTRY_VERIFY(contentItem != 0);
1305
1306     QDeclarativeVisualItemModel *model = canvas->rootObject()->findChild<QDeclarativeVisualItemModel*>("itemModel");
1307     QTRY_VERIFY(model != 0);
1308
1309     QTRY_VERIFY(model->count() == 3);
1310     QTRY_COMPARE(listview->currentIndex(), 0);
1311
1312     QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "item1");
1313     QTRY_VERIFY(item);
1314     QTRY_COMPARE(item->x(), 0.0);
1315     QCOMPARE(item->height(), listview->height());
1316
1317     QDeclarativeText *text = findItem<QDeclarativeText>(contentItem, "text1");
1318     QTRY_VERIFY(text);
1319     QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
1320
1321     listview->setCurrentIndex(2);
1322
1323     item = findItem<QDeclarativeItem>(contentItem, "item3");
1324     QTRY_VERIFY(item);
1325     QTRY_COMPARE(item->x(), 480.0);
1326
1327     text = findItem<QDeclarativeText>(contentItem, "text3");
1328     QTRY_VERIFY(text);
1329     QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
1330
1331     delete canvas;
1332 }
1333
1334 void tst_QDeclarativeListView::cacheBuffer()
1335 {
1336     QDeclarativeView *canvas = createView();
1337
1338     TestModel model;
1339     for (int i = 0; i < 30; i++)
1340         model.addItem("Item" + QString::number(i), "");
1341
1342     QDeclarativeContext *ctxt = canvas->rootContext();
1343     ctxt->setContextProperty("testModel", &model);
1344
1345     TestObject *testObject = new TestObject;
1346     ctxt->setContextProperty("testObject", testObject);
1347
1348     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
1349     qApp->processEvents();
1350
1351     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1352     QTRY_VERIFY(listview != 0);
1353
1354     QDeclarativeItem *contentItem = listview->contentItem();
1355     QTRY_VERIFY(contentItem != 0);
1356     QTRY_VERIFY(listview->delegate() != 0);
1357     QTRY_VERIFY(listview->model() != 0);
1358     QTRY_VERIFY(listview->highlight() != 0);
1359
1360     // Confirm items positioned correctly
1361     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1362     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1363         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1364         if (!item) qWarning() << "Item" << i << "not found";
1365         QTRY_VERIFY(item);
1366         QTRY_VERIFY(item->y() == i*20);
1367     }
1368
1369     testObject->setCacheBuffer(400);
1370     QTRY_VERIFY(listview->cacheBuffer() == 400);
1371
1372     int newItemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1373     QTRY_VERIFY(newItemCount > itemCount);
1374
1375     // Confirm items positioned correctly
1376     for (int i = 0; i < model.count() && i < newItemCount; ++i) {
1377         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1378         if (!item) qWarning() << "Item" << i << "not found";
1379         QTRY_VERIFY(item);
1380         QTRY_VERIFY(item->y() == i*20);
1381     }
1382
1383     delete canvas;
1384 }
1385
1386 void tst_QDeclarativeListView::positionViewAtIndex()
1387 {
1388     QDeclarativeView *canvas = createView();
1389
1390     TestModel model;
1391     for (int i = 0; i < 40; i++)
1392         model.addItem("Item" + QString::number(i), "");
1393
1394     QDeclarativeContext *ctxt = canvas->rootContext();
1395     ctxt->setContextProperty("testModel", &model);
1396
1397     TestObject *testObject = new TestObject;
1398     ctxt->setContextProperty("testObject", testObject);
1399
1400     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
1401     qApp->processEvents();
1402
1403     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1404     QTRY_VERIFY(listview != 0);
1405
1406     QDeclarativeItem *contentItem = listview->contentItem();
1407     QTRY_VERIFY(contentItem != 0);
1408
1409     // Confirm items positioned correctly
1410     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1411     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1412         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1413         if (!item) qWarning() << "Item" << i << "not found";
1414         QTRY_VERIFY(item);
1415         QTRY_COMPARE(item->y(), i*20.);
1416     }
1417
1418     // Position on a currently visible item
1419     listview->positionViewAtIndex(3, QDeclarativeListView::Beginning);
1420     QTRY_COMPARE(listview->contentY(), 60.);
1421
1422     // Confirm items positioned correctly
1423     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1424     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
1425         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1426         if (!item) qWarning() << "Item" << i << "not found";
1427         QTRY_VERIFY(item);
1428         QTRY_COMPARE(item->y(), i*20.);
1429     }
1430
1431     // Position on an item beyond the visible items
1432     listview->positionViewAtIndex(22, QDeclarativeListView::Beginning);
1433     QTRY_COMPARE(listview->contentY(), 440.);
1434
1435     // Confirm items positioned correctly
1436     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1437     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
1438         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1439         if (!item) qWarning() << "Item" << i << "not found";
1440         QTRY_VERIFY(item);
1441         QTRY_COMPARE(item->y(), i*20.);
1442     }
1443
1444     // Position on an item that would leave empty space if positioned at the top
1445     listview->positionViewAtIndex(28, QDeclarativeListView::Beginning);
1446     QTRY_COMPARE(listview->contentY(), 480.);
1447
1448     // Confirm items positioned correctly
1449     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1450     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
1451         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1452         if (!item) qWarning() << "Item" << i << "not found";
1453         QTRY_VERIFY(item);
1454         QTRY_COMPARE(item->y(), i*20.);
1455     }
1456
1457     // Position at the beginning again
1458     listview->positionViewAtIndex(0, QDeclarativeListView::Beginning);
1459     QTRY_COMPARE(listview->contentY(), 0.);
1460
1461     // Confirm items positioned correctly
1462     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1463     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
1464         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1465         if (!item) qWarning() << "Item" << i << "not found";
1466         QTRY_VERIFY(item);
1467         QTRY_COMPARE(item->y(), i*20.);
1468     }
1469
1470     // Position at End using last index
1471     listview->positionViewAtIndex(model.count()-1, QDeclarativeListView::End);
1472     QTRY_COMPARE(listview->contentY(), 480.);
1473
1474     // Confirm items positioned correctly
1475     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1476     for (int i = 24; i < model.count(); ++i) {
1477         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1478         if (!item) qWarning() << "Item" << i << "not found";
1479         QTRY_VERIFY(item);
1480         QTRY_COMPARE(item->y(), i*20.);
1481     }
1482
1483     // Position at End
1484     listview->positionViewAtIndex(20, QDeclarativeListView::End);
1485     QTRY_COMPARE(listview->contentY(), 100.);
1486
1487     // Position in Center
1488     listview->positionViewAtIndex(15, QDeclarativeListView::Center);
1489     QTRY_COMPARE(listview->contentY(), 150.);
1490
1491     // Ensure at least partially visible
1492     listview->positionViewAtIndex(15, QDeclarativeListView::Visible);
1493     QTRY_COMPARE(listview->contentY(), 150.);
1494
1495     listview->setContentY(302);
1496     listview->positionViewAtIndex(15, QDeclarativeListView::Visible);
1497     QTRY_COMPARE(listview->contentY(), 302.);
1498
1499     listview->setContentY(320);
1500     listview->positionViewAtIndex(15, QDeclarativeListView::Visible);
1501     QTRY_COMPARE(listview->contentY(), 300.);
1502
1503     listview->setContentY(85);
1504     listview->positionViewAtIndex(20, QDeclarativeListView::Visible);
1505     QTRY_COMPARE(listview->contentY(), 85.);
1506
1507     listview->setContentY(75);
1508     listview->positionViewAtIndex(20, QDeclarativeListView::Visible);
1509     QTRY_COMPARE(listview->contentY(), 100.);
1510
1511     // Ensure completely visible
1512     listview->setContentY(120);
1513     listview->positionViewAtIndex(20, QDeclarativeListView::Contain);
1514     QTRY_COMPARE(listview->contentY(), 120.);
1515
1516     listview->setContentY(302);
1517     listview->positionViewAtIndex(15, QDeclarativeListView::Contain);
1518     QTRY_COMPARE(listview->contentY(), 300.);
1519
1520     listview->setContentY(85);
1521     listview->positionViewAtIndex(20, QDeclarativeListView::Contain);
1522     QTRY_COMPARE(listview->contentY(), 100.);
1523
1524     // positionAtBeginnging
1525     listview->positionViewAtBeginning();
1526     QTRY_COMPARE(listview->contentY(), 0.);
1527
1528     listview->setContentY(80);
1529     canvas->rootObject()->setProperty("showHeader", true);
1530     listview->positionViewAtBeginning();
1531     QTRY_COMPARE(listview->contentY(), -30.);
1532
1533     // positionAtEnd
1534     listview->positionViewAtEnd();
1535     QTRY_COMPARE(listview->contentY(), 480.); // 40*20 - 320
1536
1537     listview->setContentY(80);
1538     canvas->rootObject()->setProperty("showFooter", true);
1539     listview->positionViewAtEnd();
1540     QTRY_COMPARE(listview->contentY(), 510.);
1541
1542     delete canvas;
1543 }
1544
1545 void tst_QDeclarativeListView::resetModel()
1546 {
1547     QDeclarativeView *canvas = createView();
1548
1549     QStringList strings;
1550     strings << "one" << "two" << "three";
1551     QStringListModel model(strings);
1552
1553     QDeclarativeContext *ctxt = canvas->rootContext();
1554     ctxt->setContextProperty("testModel", &model);
1555
1556     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaylist.qml"));
1557     qApp->processEvents();
1558
1559     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1560     QTRY_VERIFY(listview != 0);
1561
1562     QDeclarativeItem *contentItem = listview->contentItem();
1563     QTRY_VERIFY(contentItem != 0);
1564
1565     QTRY_COMPARE(listview->count(), model.rowCount());
1566
1567     for (int i = 0; i < model.rowCount(); ++i) {
1568         QDeclarativeText *display = findItem<QDeclarativeText>(contentItem, "displayText", i);
1569         QTRY_VERIFY(display != 0);
1570         QTRY_COMPARE(display->text(), strings.at(i));
1571     }
1572
1573     strings.clear();
1574     strings << "four" << "five" << "six" << "seven";
1575     model.setStringList(strings);
1576
1577     QTRY_COMPARE(listview->count(), model.rowCount());
1578
1579     for (int i = 0; i < model.rowCount(); ++i) {
1580         QDeclarativeText *display = findItem<QDeclarativeText>(contentItem, "displayText", i);
1581         QTRY_VERIFY(display != 0);
1582         QTRY_COMPARE(display->text(), strings.at(i));
1583     }
1584
1585     delete canvas;
1586 }
1587
1588 void tst_QDeclarativeListView::propertyChanges()
1589 {
1590     QDeclarativeView *canvas = createView();
1591     QTRY_VERIFY(canvas);
1592     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
1593
1594     QDeclarativeListView *listView = canvas->rootObject()->findChild<QDeclarativeListView*>("listView");
1595     QTRY_VERIFY(listView);
1596
1597     QSignalSpy highlightFollowsCurrentItemSpy(listView, SIGNAL(highlightFollowsCurrentItemChanged()));
1598     QSignalSpy preferredHighlightBeginSpy(listView, SIGNAL(preferredHighlightBeginChanged()));
1599     QSignalSpy preferredHighlightEndSpy(listView, SIGNAL(preferredHighlightEndChanged()));
1600     QSignalSpy highlightRangeModeSpy(listView, SIGNAL(highlightRangeModeChanged()));
1601     QSignalSpy keyNavigationWrapsSpy(listView, SIGNAL(keyNavigationWrapsChanged()));
1602     QSignalSpy cacheBufferSpy(listView, SIGNAL(cacheBufferChanged()));
1603     QSignalSpy snapModeSpy(listView, SIGNAL(snapModeChanged()));
1604
1605     QTRY_COMPARE(listView->highlightFollowsCurrentItem(), true);
1606     QTRY_COMPARE(listView->preferredHighlightBegin(), 0.0);
1607     QTRY_COMPARE(listView->preferredHighlightEnd(), 0.0);
1608     QTRY_COMPARE(listView->highlightRangeMode(), QDeclarativeListView::ApplyRange);
1609     QTRY_COMPARE(listView->isWrapEnabled(), true);
1610     QTRY_COMPARE(listView->cacheBuffer(), 10);
1611     QTRY_COMPARE(listView->snapMode(), QDeclarativeListView::SnapToItem);
1612
1613     listView->setHighlightFollowsCurrentItem(false);
1614     listView->setPreferredHighlightBegin(1.0);
1615     listView->setPreferredHighlightEnd(1.0);
1616     listView->setHighlightRangeMode(QDeclarativeListView::StrictlyEnforceRange);
1617     listView->setWrapEnabled(false);
1618     listView->setCacheBuffer(3);
1619     listView->setSnapMode(QDeclarativeListView::SnapOneItem);
1620
1621     QTRY_COMPARE(listView->highlightFollowsCurrentItem(), false);
1622     QTRY_COMPARE(listView->preferredHighlightBegin(), 1.0);
1623     QTRY_COMPARE(listView->preferredHighlightEnd(), 1.0);
1624     QTRY_COMPARE(listView->highlightRangeMode(), QDeclarativeListView::StrictlyEnforceRange);
1625     QTRY_COMPARE(listView->isWrapEnabled(), false);
1626     QTRY_COMPARE(listView->cacheBuffer(), 3);
1627     QTRY_COMPARE(listView->snapMode(), QDeclarativeListView::SnapOneItem);
1628
1629     QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
1630     QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
1631     QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
1632     QTRY_COMPARE(highlightRangeModeSpy.count(),1);
1633     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
1634     QTRY_COMPARE(cacheBufferSpy.count(),1);
1635     QTRY_COMPARE(snapModeSpy.count(),1);
1636
1637     listView->setHighlightFollowsCurrentItem(false);
1638     listView->setPreferredHighlightBegin(1.0);
1639     listView->setPreferredHighlightEnd(1.0);
1640     listView->setHighlightRangeMode(QDeclarativeListView::StrictlyEnforceRange);
1641     listView->setWrapEnabled(false);
1642     listView->setCacheBuffer(3);
1643     listView->setSnapMode(QDeclarativeListView::SnapOneItem);
1644
1645     QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
1646     QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
1647     QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
1648     QTRY_COMPARE(highlightRangeModeSpy.count(),1);
1649     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
1650     QTRY_COMPARE(cacheBufferSpy.count(),1);
1651     QTRY_COMPARE(snapModeSpy.count(),1);
1652
1653     delete canvas;
1654 }
1655
1656 void tst_QDeclarativeListView::componentChanges()
1657 {
1658     QDeclarativeView *canvas = createView();
1659     QTRY_VERIFY(canvas);
1660     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
1661
1662     QDeclarativeListView *listView = canvas->rootObject()->findChild<QDeclarativeListView*>("listView");
1663     QTRY_VERIFY(listView);
1664
1665     QDeclarativeComponent component(canvas->engine());
1666     component.setData("import QtQuick 1.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
1667
1668     QDeclarativeComponent delegateComponent(canvas->engine());
1669     delegateComponent.setData("import QtQuick 1.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
1670
1671     QSignalSpy highlightSpy(listView, SIGNAL(highlightChanged()));
1672     QSignalSpy delegateSpy(listView, SIGNAL(delegateChanged()));
1673     QSignalSpy headerSpy(listView, SIGNAL(headerChanged()));
1674     QSignalSpy footerSpy(listView, SIGNAL(footerChanged()));
1675
1676     listView->setHighlight(&component);
1677     listView->setHeader(&component);
1678     listView->setFooter(&component);
1679     listView->setDelegate(&delegateComponent);
1680
1681     QTRY_COMPARE(listView->highlight(), &component);
1682     QTRY_COMPARE(listView->header(), &component);
1683     QTRY_COMPARE(listView->footer(), &component);
1684     QTRY_COMPARE(listView->delegate(), &delegateComponent);
1685
1686     QTRY_COMPARE(highlightSpy.count(),1);
1687     QTRY_COMPARE(delegateSpy.count(),1);
1688     QTRY_COMPARE(headerSpy.count(),1);
1689     QTRY_COMPARE(footerSpy.count(),1);
1690
1691     listView->setHighlight(&component);
1692     listView->setHeader(&component);
1693     listView->setFooter(&component);
1694     listView->setDelegate(&delegateComponent);
1695
1696     QTRY_COMPARE(highlightSpy.count(),1);
1697     QTRY_COMPARE(delegateSpy.count(),1);
1698     QTRY_COMPARE(headerSpy.count(),1);
1699     QTRY_COMPARE(footerSpy.count(),1);
1700
1701     delete canvas;
1702 }
1703
1704 void tst_QDeclarativeListView::modelChanges()
1705 {
1706     QDeclarativeView *canvas = createView();
1707     QTRY_VERIFY(canvas);
1708     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
1709
1710     QDeclarativeListView *listView = canvas->rootObject()->findChild<QDeclarativeListView*>("listView");
1711     QTRY_VERIFY(listView);
1712
1713     QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
1714     QTRY_VERIFY(alternateModel);
1715     QVariant modelVariant = QVariant::fromValue(alternateModel);
1716     QSignalSpy modelSpy(listView, SIGNAL(modelChanged()));
1717
1718     listView->setModel(modelVariant);
1719     QTRY_COMPARE(listView->model(), modelVariant);
1720     QTRY_COMPARE(modelSpy.count(),1);
1721
1722     listView->setModel(modelVariant);
1723     QTRY_COMPARE(modelSpy.count(),1);
1724
1725     listView->setModel(QVariant());
1726     QTRY_COMPARE(modelSpy.count(),2);
1727
1728     delete canvas;
1729 }
1730
1731 void tst_QDeclarativeListView::QTBUG_9791()
1732 {
1733     QDeclarativeView *canvas = createView();
1734
1735     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/strictlyenforcerange.qml"));
1736     qApp->processEvents();
1737
1738     QDeclarativeListView *listview = qobject_cast<QDeclarativeListView*>(canvas->rootObject());
1739     QTRY_VERIFY(listview != 0);
1740
1741     QDeclarativeItem *contentItem = listview->contentItem();
1742     QTRY_VERIFY(contentItem != 0);
1743     QTRY_VERIFY(listview->delegate() != 0);
1744     QTRY_VERIFY(listview->model() != 0);
1745
1746     QMetaObject::invokeMethod(listview, "fillModel");
1747     qApp->processEvents();
1748
1749     // Confirm items positioned correctly
1750     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1751     QCOMPARE(itemCount, 3);
1752
1753     for (int i = 0; i < itemCount; ++i) {
1754         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1755         if (!item) qWarning() << "Item" << i << "not found";
1756         QTRY_VERIFY(item);
1757         QTRY_COMPARE(item->x(), i*300.0);
1758     }
1759
1760     // check that view is positioned correctly
1761     QTRY_COMPARE(listview->contentX(), 590.0);
1762
1763     delete canvas;
1764 }
1765
1766 void tst_QDeclarativeListView::manualHighlight()
1767 {
1768     QDeclarativeView *canvas = new QDeclarativeView(0);
1769     canvas->setFixedSize(240,320);
1770
1771     QString filename(SRCDIR "/data/manual-highlight.qml");
1772     canvas->setSource(QUrl::fromLocalFile(filename));
1773
1774     qApp->processEvents();
1775
1776     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1777     QTRY_VERIFY(listview != 0);
1778
1779     QDeclarativeItem *contentItem = listview->contentItem();
1780     QTRY_VERIFY(contentItem != 0);
1781
1782     QTRY_COMPARE(listview->currentIndex(), 0);
1783     QTRY_COMPARE(listview->currentItem(), findItem<QDeclarativeItem>(contentItem, "wrapper", 0));
1784     QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
1785
1786     listview->setCurrentIndex(2);
1787
1788     QTRY_COMPARE(listview->currentIndex(), 2);
1789     QTRY_COMPARE(listview->currentItem(), findItem<QDeclarativeItem>(contentItem, "wrapper", 2));
1790     QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
1791
1792     // QTBUG-15972
1793     listview->positionViewAtIndex(3, QDeclarativeListView::Contain);
1794
1795     QTRY_COMPARE(listview->currentIndex(), 2);
1796     QTRY_COMPARE(listview->currentItem(), findItem<QDeclarativeItem>(contentItem, "wrapper", 2));
1797     QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
1798
1799     delete canvas;
1800 }
1801
1802 void tst_QDeclarativeListView::QTBUG_11105()
1803 {
1804     QDeclarativeView *canvas = createView();
1805
1806     TestModel model;
1807     for (int i = 0; i < 30; i++)
1808         model.addItem("Item" + QString::number(i), "");
1809
1810     QDeclarativeContext *ctxt = canvas->rootContext();
1811     ctxt->setContextProperty("testModel", &model);
1812
1813     TestObject *testObject = new TestObject;
1814     ctxt->setContextProperty("testObject", testObject);
1815
1816     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
1817     qApp->processEvents();
1818
1819     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1820     QTRY_VERIFY(listview != 0);
1821
1822     QDeclarativeItem *contentItem = listview->contentItem();
1823     QTRY_VERIFY(contentItem != 0);
1824
1825     // Confirm items positioned correctly
1826     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1827     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1828         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
1829         if (!item) qWarning() << "Item" << i << "not found";
1830         QTRY_VERIFY(item);
1831         QTRY_VERIFY(item->y() == i*20);
1832     }
1833
1834     listview->positionViewAtIndex(20, QDeclarativeListView::Beginning);
1835     QCOMPARE(listview->contentY(), 280.);
1836
1837     TestModel model2;
1838     for (int i = 0; i < 5; i++)
1839         model2.addItem("Item" + QString::number(i), "");
1840
1841     ctxt->setContextProperty("testModel", &model2);
1842
1843     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
1844     QCOMPARE(itemCount, 5);
1845
1846     delete canvas;
1847 }
1848
1849 void tst_QDeclarativeListView::header()
1850 {
1851     {
1852         QDeclarativeView *canvas = createView();
1853
1854         TestModel model;
1855         for (int i = 0; i < 30; i++)
1856             model.addItem("Item" + QString::number(i), "");
1857
1858         QDeclarativeContext *ctxt = canvas->rootContext();
1859         ctxt->setContextProperty("testModel", &model);
1860
1861         canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml"));
1862         qApp->processEvents();
1863
1864         QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1865         QTRY_VERIFY(listview != 0);
1866
1867         QDeclarativeItem *contentItem = listview->contentItem();
1868         QTRY_VERIFY(contentItem != 0);
1869
1870         QDeclarativeText *header = findItem<QDeclarativeText>(contentItem, "header");
1871         QVERIFY(header);
1872         QCOMPARE(header->y(), 0.0);
1873         QCOMPARE(header->height(), 20.0);
1874
1875         QCOMPARE(listview->contentY(), 0.0);
1876
1877         model.clear();
1878         QTRY_COMPARE(header->y(), 0.0);
1879
1880         for (int i = 0; i < 30; i++)
1881             model.addItem("Item" + QString::number(i), "");
1882
1883         QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
1884
1885         header = findItem<QDeclarativeText>(contentItem, "header");
1886         QVERIFY(!header);
1887         header = findItem<QDeclarativeText>(contentItem, "header2");
1888         QVERIFY(header);
1889
1890         QCOMPARE(header->y(), 10.0);
1891         QCOMPARE(header->height(), 10.0);
1892         QCOMPARE(listview->contentY(), 10.0);
1893
1894         delete canvas;
1895     }
1896     {
1897         QDeclarativeView *canvas = createView();
1898
1899         TestModel model;
1900
1901         canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header1.qml"));
1902         qApp->processEvents();
1903
1904         QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1905         QTRY_VERIFY(listview != 0);
1906
1907         QDeclarativeItem *contentItem = listview->contentItem();
1908         QTRY_VERIFY(contentItem != 0);
1909
1910         QDeclarativeText *header = findItem<QDeclarativeText>(contentItem, "header");
1911         QVERIFY(header);
1912         QCOMPARE(header->y(), 0.0);
1913
1914         QCOMPARE(listview->contentY(), 0.0);
1915
1916         model.clear();
1917         QTRY_COMPARE(header->y(), 0.0);
1918
1919         delete canvas;
1920     }
1921 }
1922
1923 void tst_QDeclarativeListView::footer()
1924 {
1925     QDeclarativeView *canvas = createView();
1926
1927     TestModel model;
1928     for (int i = 0; i < 3; i++)
1929         model.addItem("Item" + QString::number(i), "");
1930
1931     QDeclarativeContext *ctxt = canvas->rootContext();
1932     ctxt->setContextProperty("testModel", &model);
1933
1934     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/footer.qml"));
1935     qApp->processEvents();
1936
1937     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
1938     QTRY_VERIFY(listview != 0);
1939
1940     QDeclarativeItem *contentItem = listview->contentItem();
1941     QTRY_VERIFY(contentItem != 0);
1942
1943     QDeclarativeText *footer = findItem<QDeclarativeText>(contentItem, "footer");
1944     QVERIFY(footer);
1945     QCOMPARE(footer->y(), 60.0);
1946     QCOMPARE(footer->height(), 30.0);
1947
1948     model.removeItem(1);
1949     QTRY_COMPARE(footer->y(), 40.0);
1950
1951     model.clear();
1952     QTRY_COMPARE(footer->y(), 0.0);
1953
1954     for (int i = 0; i < 30; i++)
1955         model.addItem("Item" + QString::number(i), "");
1956
1957     QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
1958
1959     footer = findItem<QDeclarativeText>(contentItem, "footer");
1960     QVERIFY(!footer);
1961     footer = findItem<QDeclarativeText>(contentItem, "footer2");
1962     QVERIFY(footer);
1963
1964     QCOMPARE(footer->y(), 600.0);
1965     QCOMPARE(footer->height(), 20.0);
1966     QCOMPARE(listview->contentY(), 0.0);
1967
1968     delete canvas;
1969 }
1970
1971 class LVAccessor : public QDeclarativeListView
1972 {
1973 public:
1974     qreal minY() const { return minYExtent(); }
1975     qreal maxY() const { return maxYExtent(); }
1976     qreal minX() const { return minXExtent(); }
1977     qreal maxX() const { return maxXExtent(); }
1978 };
1979
1980 void tst_QDeclarativeListView::headerFooter()
1981 {
1982     {
1983         // Vertical
1984         QDeclarativeView *canvas = createView();
1985
1986         TestModel model;
1987         QDeclarativeContext *ctxt = canvas->rootContext();
1988         ctxt->setContextProperty("testModel", &model);
1989
1990         canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/headerfooter.qml"));
1991         qApp->processEvents();
1992
1993         QDeclarativeListView *listview = qobject_cast<QDeclarativeListView*>(canvas->rootObject());
1994         QTRY_VERIFY(listview != 0);
1995
1996         QDeclarativeItem *contentItem = listview->contentItem();
1997         QTRY_VERIFY(contentItem != 0);
1998
1999         QDeclarativeItem *header = findItem<QDeclarativeItem>(contentItem, "header");
2000         QVERIFY(header);
2001         QCOMPARE(header->y(), 0.0);
2002
2003         QDeclarativeItem *footer = findItem<QDeclarativeItem>(contentItem, "footer");
2004         QVERIFY(footer);
2005         QCOMPARE(footer->y(), 20.0);
2006
2007         QVERIFY(static_cast<LVAccessor*>(listview)->minY() == 0);
2008         QVERIFY(static_cast<LVAccessor*>(listview)->maxY() == 0);
2009
2010         delete canvas;
2011     }
2012     {
2013         // Horizontal
2014         QDeclarativeView *canvas = createView();
2015
2016         TestModel model;
2017         QDeclarativeContext *ctxt = canvas->rootContext();
2018         ctxt->setContextProperty("testModel", &model);
2019
2020         canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/headerfooter.qml"));
2021         canvas->rootObject()->setProperty("horizontal", true);
2022         qApp->processEvents();
2023
2024         QDeclarativeListView *listview = qobject_cast<QDeclarativeListView*>(canvas->rootObject());
2025         QTRY_VERIFY(listview != 0);
2026
2027         QDeclarativeItem *contentItem = listview->contentItem();
2028         QTRY_VERIFY(contentItem != 0);
2029
2030         QDeclarativeItem *header = findItem<QDeclarativeItem>(contentItem, "header");
2031         QVERIFY(header);
2032         QCOMPARE(header->x(), 0.0);
2033
2034         QDeclarativeItem *footer = findItem<QDeclarativeItem>(contentItem, "footer");
2035         QVERIFY(footer);
2036         QCOMPARE(footer->x(), 20.0);
2037
2038         QVERIFY(static_cast<LVAccessor*>(listview)->minX() == 0);
2039         QVERIFY(static_cast<LVAccessor*>(listview)->maxX() == 0);
2040
2041         delete canvas;
2042     }
2043     {
2044         // Horizontal RTL
2045         QDeclarativeView *canvas = createView();
2046
2047         TestModel model;
2048         QDeclarativeContext *ctxt = canvas->rootContext();
2049         ctxt->setContextProperty("testModel", &model);
2050
2051         canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/headerfooter.qml"));
2052         canvas->rootObject()->setProperty("horizontal", true);
2053         canvas->rootObject()->setProperty("rtl", true);
2054         qApp->processEvents();
2055
2056         QDeclarativeListView *listview = qobject_cast<QDeclarativeListView*>(canvas->rootObject());
2057         QTRY_VERIFY(listview != 0);
2058
2059         QDeclarativeItem *contentItem = listview->contentItem();
2060         QTRY_VERIFY(contentItem != 0);
2061
2062         QDeclarativeItem *header = findItem<QDeclarativeItem>(contentItem, "header");
2063         QVERIFY(header);
2064         QCOMPARE(header->x(), -20.0);
2065
2066         QDeclarativeItem *footer = findItem<QDeclarativeItem>(contentItem, "footer");
2067         QVERIFY(footer);
2068         QCOMPARE(footer->x(), -50.0);
2069
2070         QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240.);
2071         QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240.);
2072
2073         delete canvas;
2074     }
2075 }
2076
2077 void tst_QDeclarativeListView::resizeView()
2078 {
2079     QDeclarativeView *canvas = createView();
2080
2081     TestModel model;
2082     for (int i = 0; i < 40; i++)
2083         model.addItem("Item" + QString::number(i), "");
2084
2085     QDeclarativeContext *ctxt = canvas->rootContext();
2086     ctxt->setContextProperty("testModel", &model);
2087
2088     TestObject *testObject = new TestObject;
2089     ctxt->setContextProperty("testObject", testObject);
2090
2091     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
2092     qApp->processEvents();
2093
2094     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
2095     QTRY_VERIFY(listview != 0);
2096
2097     QDeclarativeItem *contentItem = listview->contentItem();
2098     QTRY_VERIFY(contentItem != 0);
2099
2100     // Confirm items positioned correctly
2101     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
2102     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2103         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2104         if (!item) qWarning() << "Item" << i << "not found";
2105         QTRY_VERIFY(item);
2106         QTRY_COMPARE(item->y(), i*20.);
2107     }
2108
2109     QVariant heightRatio;
2110     QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
2111     QCOMPARE(heightRatio.toReal(), 0.4);
2112
2113     listview->setHeight(200);
2114
2115     QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
2116     QCOMPARE(heightRatio.toReal(), 0.25);
2117
2118     delete canvas;
2119 }
2120
2121 void tst_QDeclarativeListView::sizeLessThan1()
2122 {
2123     QDeclarativeView *canvas = createView();
2124
2125     TestModel model;
2126     for (int i = 0; i < 30; i++)
2127         model.addItem("Item" + QString::number(i), "");
2128
2129     QDeclarativeContext *ctxt = canvas->rootContext();
2130     ctxt->setContextProperty("testModel", &model);
2131
2132     TestObject *testObject = new TestObject;
2133     ctxt->setContextProperty("testObject", testObject);
2134
2135     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/sizelessthan1.qml"));
2136     qApp->processEvents();
2137
2138     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
2139     QTRY_VERIFY(listview != 0);
2140
2141     QDeclarativeItem *contentItem = listview->contentItem();
2142     QTRY_VERIFY(contentItem != 0);
2143
2144     // Confirm items positioned correctly
2145     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
2146     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2147         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2148         if (!item) qWarning() << "Item" << i << "not found";
2149         QTRY_VERIFY(item);
2150         QTRY_COMPARE(item->y(), i*0.5);
2151     }
2152
2153     delete canvas;
2154 }
2155
2156 void tst_QDeclarativeListView::QTBUG_14821()
2157 {
2158     QDeclarativeView *canvas = createView();
2159
2160     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/qtbug14821.qml"));
2161     qApp->processEvents();
2162
2163     QDeclarativeListView *listview = qobject_cast<QDeclarativeListView*>(canvas->rootObject());
2164     QVERIFY(listview != 0);
2165
2166     QDeclarativeItem *contentItem = listview->contentItem();
2167     QVERIFY(contentItem != 0);
2168
2169     listview->decrementCurrentIndex();
2170     QCOMPARE(listview->currentIndex(), 99);
2171
2172     listview->incrementCurrentIndex();
2173     QCOMPARE(listview->currentIndex(), 0);
2174
2175     delete canvas;
2176 }
2177
2178 void tst_QDeclarativeListView::resizeDelegate()
2179 {
2180     QDeclarativeView *canvas = createView();
2181
2182     QStringList strings;
2183     for (int i = 0; i < 30; ++i)
2184         strings << QString::number(i);
2185     QStringListModel model(strings);
2186
2187     QDeclarativeContext *ctxt = canvas->rootContext();
2188     ctxt->setContextProperty("testModel", &model);
2189
2190     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaylist.qml"));
2191     qApp->processEvents();
2192
2193     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
2194     QTRY_VERIFY(listview != 0);
2195
2196     QDeclarativeItem *contentItem = listview->contentItem();
2197     QTRY_VERIFY(contentItem != 0);
2198
2199     QTRY_COMPARE(listview->count(), model.rowCount());
2200
2201     listview->setCurrentIndex(25);
2202     listview->setContentY(0);
2203
2204     for (int i = 0; i < 16; ++i) {
2205         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2206         QVERIFY(item != 0);
2207         QCOMPARE(item->y(), i*20.0);
2208     }
2209
2210     QCOMPARE(listview->currentItem()->y(), 500.0);
2211     QTRY_COMPARE(listview->highlightItem()->y(), 500.0);
2212
2213     canvas->rootObject()->setProperty("delegateHeight", 30);
2214     qApp->processEvents();
2215
2216     for (int i = 0; i < 11; ++i) {
2217         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2218         QVERIFY(item != 0);
2219         QTRY_COMPARE(item->y(), i*30.0);
2220     }
2221
2222     QTRY_COMPARE(listview->currentItem()->y(), 750.0);
2223     QTRY_COMPARE(listview->highlightItem()->y(), 750.0);
2224
2225     listview->setCurrentIndex(1);
2226     listview->positionViewAtIndex(25, QDeclarativeListView::Beginning);
2227     listview->positionViewAtIndex(5, QDeclarativeListView::Beginning);
2228
2229     for (int i = 5; i < 16; ++i) {
2230         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2231         QVERIFY(item != 0);
2232         QCOMPARE(item->y(), i*30.0);
2233     }
2234
2235     QTRY_COMPARE(listview->currentItem()->y(), 30.0);
2236     QTRY_COMPARE(listview->highlightItem()->y(), 30.0);
2237
2238     canvas->rootObject()->setProperty("delegateHeight", 20);
2239     qApp->processEvents();
2240
2241     for (int i = 5; i < 11; ++i) {
2242         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2243         QVERIFY(item != 0);
2244         QTRY_COMPARE(item->y(), 150 + (i-5)*20.0);
2245     }
2246
2247     QTRY_COMPARE(listview->currentItem()->y(), 70.0);
2248     QTRY_COMPARE(listview->highlightItem()->y(), 70.0);
2249
2250     delete canvas;
2251 }
2252
2253 void tst_QDeclarativeListView::QTBUG_16037()
2254 {
2255     QDeclarativeView *canvas = createView();
2256
2257     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/qtbug16037.qml"));
2258     qApp->processEvents();
2259
2260     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "listview");
2261     QTRY_VERIFY(listview != 0);
2262
2263     QVERIFY(listview->contentHeight() <= 0.0);
2264
2265     QMetaObject::invokeMethod(canvas->rootObject(), "setModel");
2266
2267     QTRY_COMPARE(listview->contentHeight(), 80.0);
2268
2269     delete canvas;
2270 }
2271
2272 void tst_QDeclarativeListView::indexAt()
2273 {
2274     QDeclarativeView *canvas = createView();
2275
2276     TestModel model;
2277     for (int i = 0; i < 30; i++)
2278         model.addItem("Item" + QString::number(i), "");
2279
2280     QDeclarativeContext *ctxt = canvas->rootContext();
2281     ctxt->setContextProperty("testModel", &model);
2282
2283     TestObject *testObject = new TestObject;
2284     ctxt->setContextProperty("testObject", testObject);
2285
2286     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
2287     qApp->processEvents();
2288
2289     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
2290     QTRY_VERIFY(listview != 0);
2291
2292     QDeclarativeItem *contentItem = listview->contentItem();
2293     QTRY_VERIFY(contentItem != 0);
2294
2295     QCOMPARE(listview->indexAt(0,0), 0);
2296     QCOMPARE(listview->indexAt(0,19), 0);
2297     QCOMPARE(listview->indexAt(239,19), 0);
2298     QCOMPARE(listview->indexAt(0,20), 1);
2299     QCOMPARE(listview->indexAt(240,20), -1);
2300
2301     delete canvas;
2302 }
2303
2304 void tst_QDeclarativeListView::incrementalModel()
2305 {
2306     QDeclarativeView *canvas = createView();
2307
2308     IncrementalModel model;
2309     QDeclarativeContext *ctxt = canvas->rootContext();
2310     ctxt->setContextProperty("testModel", &model);
2311
2312     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaylist.qml"));
2313     qApp->processEvents();
2314
2315     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
2316     QTRY_VERIFY(listview != 0);
2317
2318     QDeclarativeItem *contentItem = listview->contentItem();
2319     QTRY_VERIFY(contentItem != 0);
2320
2321     QTRY_COMPARE(listview->count(), 20);
2322
2323     listview->positionViewAtIndex(10, QDeclarativeListView::Beginning);
2324
2325     QTRY_COMPARE(listview->count(), 25);
2326
2327     delete canvas;
2328 }
2329
2330 void tst_QDeclarativeListView::onAdd()
2331 {
2332     QFETCH(int, initialItemCount);
2333     QFETCH(int, itemsToAdd);
2334
2335     const int delegateHeight = 10;
2336     TestModel2 model;
2337
2338     // these initial items should not trigger ListView.onAdd
2339     for (int i=0; i<initialItemCount; i++)
2340         model.addItem("dummy value", "dummy value");
2341
2342     QDeclarativeView *canvas = createView();
2343     canvas->setFixedSize(200, delegateHeight * (initialItemCount + itemsToAdd));
2344     QDeclarativeContext *ctxt = canvas->rootContext();
2345     ctxt->setContextProperty("testModel", &model);
2346     ctxt->setContextProperty("delegateHeight", delegateHeight);
2347     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml"));
2348
2349     QObject *object = canvas->rootObject();
2350     object->setProperty("width", canvas->width());
2351     object->setProperty("height", canvas->height());
2352     qApp->processEvents();
2353
2354     QList<QPair<QString, QString> > items;
2355     for (int i=0; i<itemsToAdd; i++)
2356         items << qMakePair(QString("value %1").arg(i), QString::number(i));
2357     model.addItems(items);
2358
2359     qApp->processEvents();
2360
2361     QVariantList result = object->property("addedDelegates").toList();
2362     QCOMPARE(result.count(), items.count());
2363     for (int i=0; i<items.count(); i++)
2364         QCOMPARE(result[i].toString(), items[i].first);
2365
2366     delete canvas;
2367 }
2368
2369 void tst_QDeclarativeListView::onAdd_data()
2370 {
2371     QTest::addColumn<int>("initialItemCount");
2372     QTest::addColumn<int>("itemsToAdd");
2373
2374     QTest::newRow("0, add 1") << 0 << 1;
2375     QTest::newRow("0, add 2") << 0 << 2;
2376     QTest::newRow("0, add 10") << 0 << 10;
2377
2378     QTest::newRow("1, add 1") << 1 << 1;
2379     QTest::newRow("1, add 2") << 1 << 2;
2380     QTest::newRow("1, add 10") << 1 << 10;
2381
2382     QTest::newRow("5, add 1") << 5 << 1;
2383     QTest::newRow("5, add 2") << 5 << 2;
2384     QTest::newRow("5, add 10") << 5 << 10;
2385 }
2386
2387 void tst_QDeclarativeListView::onRemove()
2388 {
2389     QFETCH(int, initialItemCount);
2390     QFETCH(int, indexToRemove);
2391     QFETCH(int, removeCount);
2392
2393     const int delegateHeight = 10;
2394     TestModel2 model;
2395     for (int i=0; i<initialItemCount; i++)
2396         model.addItem(QString("value %1").arg(i), "dummy value");
2397
2398     QDeclarativeView *canvas = createView();
2399     QDeclarativeContext *ctxt = canvas->rootContext();
2400     ctxt->setContextProperty("testModel", &model);
2401     ctxt->setContextProperty("delegateHeight", delegateHeight);
2402     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml"));
2403     QObject *object = canvas->rootObject();
2404
2405     qApp->processEvents();
2406
2407     model.removeItems(indexToRemove, removeCount);
2408     qApp->processEvents();
2409     QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
2410
2411     delete canvas;
2412 }
2413
2414 void tst_QDeclarativeListView::onRemove_data()
2415 {
2416     QTest::addColumn<int>("initialItemCount");
2417     QTest::addColumn<int>("indexToRemove");
2418     QTest::addColumn<int>("removeCount");
2419
2420     QTest::newRow("remove first") << 1 << 0 << 1;
2421     QTest::newRow("two items, remove first") << 2 << 0 << 1;
2422     QTest::newRow("two items, remove last") << 2 << 1 << 1;
2423     QTest::newRow("two items, remove all") << 2 << 0 << 2;
2424
2425     QTest::newRow("four items, remove first") << 4 << 0 << 1;
2426     QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
2427     QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
2428     QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
2429     QTest::newRow("four items, remove last") << 4 << 3 << 1;
2430     QTest::newRow("four items, remove all") << 4 << 0 << 4;
2431
2432     QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
2433     QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
2434     QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
2435 }
2436
2437 void tst_QDeclarativeListView::testQtQuick11Attributes()
2438 {
2439     QFETCH(QString, code);
2440     QFETCH(QString, warning);
2441     QFETCH(QString, error);
2442
2443     QDeclarativeEngine engine;
2444     QObject *obj;
2445
2446     QDeclarativeComponent valid(&engine);
2447     valid.setData("import QtQuick 1.1; ListView { " + code.toUtf8() + " }", QUrl(""));
2448     obj = valid.create();
2449     QVERIFY(obj);
2450     QVERIFY(valid.errorString().isEmpty());
2451     delete obj;
2452
2453     QDeclarativeComponent invalid(&engine);
2454     invalid.setData("import QtQuick 1.0; ListView { " + code.toUtf8() + " }", QUrl(""));
2455     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2456     obj = invalid.create();
2457     QCOMPARE(invalid.errorString(), error);
2458     delete obj;
2459 }
2460
2461 void tst_QDeclarativeListView::testQtQuick11Attributes_data()
2462 {
2463     QTest::addColumn<QString>("code");
2464     QTest::addColumn<QString>("warning");
2465     QTest::addColumn<QString>("error");
2466
2467     QTest::newRow("positionViewAtBeginning") << "Component.onCompleted: positionViewAtBeginning()"
2468         << "<Unknown File>:1: ReferenceError: Can't find variable: positionViewAtBeginning"
2469         << "";
2470
2471     QTest::newRow("positionViewAtEnd") << "Component.onCompleted: positionViewAtEnd()"
2472         << "<Unknown File>:1: ReferenceError: Can't find variable: positionViewAtEnd"
2473         << "";
2474 }
2475
2476 void tst_QDeclarativeListView::rightToLeft()
2477 {
2478     QDeclarativeView *canvas = createView();
2479     canvas->setFixedSize(640,320);
2480     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/rightToLeft.qml"));
2481     qApp->processEvents();
2482
2483     QVERIFY(canvas->rootObject() != 0);
2484     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "view");
2485     QTRY_VERIFY(listview != 0);
2486
2487     QDeclarativeItem *contentItem = listview->contentItem();
2488     QTRY_VERIFY(contentItem != 0);
2489
2490     QDeclarativeVisualItemModel *model = canvas->rootObject()->findChild<QDeclarativeVisualItemModel*>("itemModel");
2491     QTRY_VERIFY(model != 0);
2492
2493     QTRY_VERIFY(model->count() == 3);
2494     QTRY_COMPARE(listview->currentIndex(), 0);
2495
2496     // initial position at first item, right edge aligned
2497     QCOMPARE(listview->contentX(), -640.);
2498
2499     QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "item1");
2500     QTRY_VERIFY(item);
2501     QTRY_COMPARE(item->x(), -100.0);
2502     QCOMPARE(item->height(), listview->height());
2503
2504     QDeclarativeText *text = findItem<QDeclarativeText>(contentItem, "text1");
2505     QTRY_VERIFY(text);
2506     QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
2507
2508     listview->setCurrentIndex(2);
2509
2510     item = findItem<QDeclarativeItem>(contentItem, "item3");
2511     QTRY_VERIFY(item);
2512     QTRY_COMPARE(item->x(), -540.0);
2513
2514     text = findItem<QDeclarativeText>(contentItem, "text3");
2515     QTRY_VERIFY(text);
2516     QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
2517
2518     QCOMPARE(listview->contentX(), -640.);
2519
2520     // Ensure resizing maintains position relative to right edge
2521     qobject_cast<QDeclarativeItem*>(canvas->rootObject())->setWidth(600);
2522     QTRY_COMPARE(listview->contentX(), -600.);
2523
2524     delete canvas;
2525 }
2526
2527 void tst_QDeclarativeListView::test_mirroring()
2528 {
2529     QDeclarativeView *canvasA = createView();
2530     canvasA->setSource(QUrl::fromLocalFile(SRCDIR "/data/rightToLeft.qml"));
2531     QDeclarativeListView *listviewA = findItem<QDeclarativeListView>(canvasA->rootObject(), "view");
2532     QTRY_VERIFY(listviewA != 0);
2533
2534     QDeclarativeView *canvasB = createView();
2535     canvasB->setSource(QUrl::fromLocalFile(SRCDIR "/data/rightToLeft.qml"));
2536     QDeclarativeListView *listviewB = findItem<QDeclarativeListView>(canvasB->rootObject(), "view");
2537     QTRY_VERIFY(listviewA != 0);
2538     qApp->processEvents();
2539
2540     QList<QString> objectNames;
2541     objectNames << "item1" << "item2"; // << "item3"
2542
2543     listviewA->setProperty("layoutDirection", Qt::LeftToRight);
2544     listviewB->setProperty("layoutDirection", Qt::RightToLeft);
2545     QCOMPARE(listviewA->layoutDirection(), listviewA->effectiveLayoutDirection());
2546
2547     // LTR != RTL
2548     foreach(const QString objectName, objectNames)
2549         QVERIFY(findItem<QDeclarativeItem>(listviewA, objectName)->x() != findItem<QDeclarativeItem>(listviewB, objectName)->x());
2550
2551     listviewA->setProperty("layoutDirection", Qt::LeftToRight);
2552     listviewB->setProperty("layoutDirection", Qt::LeftToRight);
2553
2554     // LTR == LTR
2555     foreach(const QString objectName, objectNames)
2556         QCOMPARE(findItem<QDeclarativeItem>(listviewA, objectName)->x(), findItem<QDeclarativeItem>(listviewB, objectName)->x());
2557
2558     QVERIFY(listviewB->layoutDirection() == listviewB->effectiveLayoutDirection());
2559     QDeclarativeItemPrivate::get(listviewB)->setLayoutMirror(true);
2560     QVERIFY(listviewB->layoutDirection() != listviewB->effectiveLayoutDirection());
2561
2562     // LTR != LTR+mirror
2563     foreach(const QString objectName, objectNames)
2564         QVERIFY(findItem<QDeclarativeItem>(listviewA, objectName)->x() != findItem<QDeclarativeItem>(listviewB, objectName)->x());
2565
2566     listviewA->setProperty("layoutDirection", Qt::RightToLeft);
2567
2568     // RTL == LTR+mirror
2569     foreach(const QString objectName, objectNames)
2570         QCOMPARE(findItem<QDeclarativeItem>(listviewA, objectName)->x(), findItem<QDeclarativeItem>(listviewB, objectName)->x());
2571
2572     listviewB->setProperty("layoutDirection", Qt::RightToLeft);
2573
2574     // RTL != RTL+mirror
2575     foreach(const QString objectName, objectNames)
2576         QVERIFY(findItem<QDeclarativeItem>(listviewA, objectName)->x() != findItem<QDeclarativeItem>(listviewB, objectName)->x());
2577
2578     listviewA->setProperty("layoutDirection", Qt::LeftToRight);
2579
2580     // LTR == RTL+mirror
2581     foreach(const QString objectName, objectNames)
2582         QCOMPARE(findItem<QDeclarativeItem>(listviewA, objectName)->x(), findItem<QDeclarativeItem>(listviewB, objectName)->x());
2583
2584     delete canvasA;
2585     delete canvasB;
2586 }
2587
2588 void tst_QDeclarativeListView::orientationChange()
2589 {
2590     QDeclarativeView *canvas = createView();
2591
2592     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/orientchange.qml"));
2593     qApp->processEvents();
2594
2595     QDeclarativeListView *listview = qobject_cast<QDeclarativeListView*>(canvas->rootObject());
2596     QVERIFY(listview != 0);
2597
2598     QDeclarativeItem *contentItem = listview->contentItem();
2599     QVERIFY(contentItem != 0);
2600
2601     listview->positionViewAtIndex(50, QDeclarativeListView::Beginning);
2602
2603     // Confirm items positioned correctly
2604     for (int i = 50; i < 54; ++i) {
2605         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2606         QVERIFY(item);
2607         QCOMPARE(item->y(), i*80.0);
2608     }
2609
2610     listview->setOrientation(QDeclarativeListView::Horizontal);
2611     QCOMPARE(listview->contentY(), 0.);
2612
2613     // Confirm items positioned correctly
2614     for (int i = 0; i < 3; ++i) {
2615         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2616         QVERIFY(item);
2617         QCOMPARE(item->x(), i*80.0);
2618     }
2619
2620     listview->positionViewAtIndex(50, QDeclarativeListView::Beginning);
2621     listview->setOrientation(QDeclarativeListView::Vertical);
2622     QCOMPARE(listview->contentX(), 0.);
2623     //
2624     // Confirm items positioned correctly
2625     for (int i = 0; i < 4; ++i) {
2626         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2627         QVERIFY(item);
2628         QCOMPARE(item->y(), i*80.0);
2629     }
2630
2631     delete canvas;
2632 }
2633
2634 void tst_QDeclarativeListView::contentPosJump()
2635 {
2636     QDeclarativeView *canvas = createView();
2637
2638     TestModel model;
2639     for (int i = 0; i < 50; i++)
2640         model.addItem("Item" + QString::number(i), "");
2641
2642     QDeclarativeContext *ctxt = canvas->rootContext();
2643     ctxt->setContextProperty("testModel", &model);
2644
2645     TestObject *testObject = new TestObject;
2646     ctxt->setContextProperty("testObject", testObject);
2647
2648     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/listviewtest.qml"));
2649     qApp->processEvents();
2650
2651     QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "list");
2652     QTRY_VERIFY(listview != 0);
2653
2654     QDeclarativeItem *contentItem = listview->contentItem();
2655     QTRY_VERIFY(contentItem != 0);
2656
2657     // Confirm items positioned correctly
2658     int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
2659     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2660         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2661         if (!item) qWarning() << "Item" << i << "not found";
2662         QTRY_VERIFY(item);
2663         QTRY_VERIFY(item->y() == i*20);
2664     }
2665
2666     // Test jumping more than a page of items.
2667     listview->setContentY(500);
2668     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
2669     for (int i = 25; i < model.count() && i < itemCount; ++i) {
2670         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2671         if (!item) qWarning() << "Item" << i << "not found";
2672         QTRY_VERIFY(item);
2673         QTRY_VERIFY(item->y() == i*20);
2674     }
2675
2676     listview->setContentY(-100);
2677     itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count();
2678     QVERIFY(itemCount < 20);
2679     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2680         QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i);
2681         if (!item) qWarning() << "Item" << i << "not found";
2682         QTRY_VERIFY(item);
2683         QTRY_VERIFY(item->y() == i*20);
2684     }
2685
2686     delete canvas;
2687 }
2688
2689 void tst_QDeclarativeListView::qListModelInterface_items()
2690 {
2691     items<TestModel>();
2692 }
2693
2694 void tst_QDeclarativeListView::qAbstractItemModel_items()
2695 {
2696     items<TestModel2>();
2697 }
2698
2699 void tst_QDeclarativeListView::qListModelInterface_changed()
2700 {
2701     changed<TestModel>();
2702 }
2703
2704 void tst_QDeclarativeListView::qAbstractItemModel_changed()
2705 {
2706     changed<TestModel2>();
2707 }
2708
2709 void tst_QDeclarativeListView::qListModelInterface_inserted()
2710 {
2711     inserted<TestModel>();
2712 }
2713
2714 void tst_QDeclarativeListView::qAbstractItemModel_inserted()
2715 {
2716     inserted<TestModel2>();
2717 }
2718
2719 void tst_QDeclarativeListView::qListModelInterface_removed()
2720 {
2721     removed<TestModel>(false);
2722     removed<TestModel>(true);
2723 }
2724
2725 void tst_QDeclarativeListView::qAbstractItemModel_removed()
2726 {
2727     removed<TestModel2>(false);
2728     removed<TestModel2>(true);
2729 }
2730
2731 void tst_QDeclarativeListView::qListModelInterface_moved()
2732 {
2733     moved<TestModel>();
2734 }
2735
2736 void tst_QDeclarativeListView::qAbstractItemModel_moved()
2737 {
2738     moved<TestModel2>();
2739 }
2740
2741 void tst_QDeclarativeListView::qListModelInterface_clear()
2742 {
2743     clear<TestModel>();
2744 }
2745
2746 void tst_QDeclarativeListView::qAbstractItemModel_clear()
2747 {
2748     clear<TestModel2>();
2749 }
2750
2751 QDeclarativeView *tst_QDeclarativeListView::createView()
2752 {
2753     QDeclarativeView *canvas = new QDeclarativeView(0);
2754     canvas->setFixedSize(240,320);
2755
2756     return canvas;
2757 }
2758
2759 /*
2760    Find an item with the specified objectName.  If index is supplied then the
2761    item must also evaluate the {index} expression equal to index
2762 */
2763 template<typename T>
2764 T *tst_QDeclarativeListView::findItem(QGraphicsObject *parent, const QString &objectName, int index)
2765 {
2766     const QMetaObject &mo = T::staticMetaObject;
2767     //qDebug() << parent->childItems().count() << "children";
2768     for (int i = 0; i < parent->childItems().count(); ++i) {
2769         QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(parent->childItems().at(i));
2770         if(!item)
2771             continue;
2772         //qDebug() << "try" << item;
2773         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
2774             if (index != -1) {
2775                 QDeclarativeExpression e(qmlContext(item), item, "index");
2776                 if (e.evaluate().toInt() == index)
2777                     return static_cast<T*>(item);
2778             } else {
2779                 return static_cast<T*>(item);
2780             }
2781         }
2782         item = findItem<T>(item, objectName, index);
2783         if (item)
2784             return static_cast<T*>(item);
2785     }
2786
2787     return 0;
2788 }
2789
2790 template<typename T>
2791 QList<T*> tst_QDeclarativeListView::findItems(QGraphicsObject *parent, const QString &objectName)
2792 {
2793     QList<T*> items;
2794     const QMetaObject &mo = T::staticMetaObject;
2795     //qDebug() << parent->childItems().count() << "children";
2796     for (int i = 0; i < parent->childItems().count(); ++i) {
2797         QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(parent->childItems().at(i));
2798         if(!item || !item->isVisible())
2799             continue;
2800         //qDebug() << "try" << item;
2801         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
2802             items.append(static_cast<T*>(item));
2803         items += findItems<T>(item, objectName);
2804     }
2805
2806     return items;
2807 }
2808
2809 void tst_QDeclarativeListView::dumpTree(QDeclarativeItem *parent, int depth)
2810 {
2811     static QString padding("                       ");
2812     for (int i = 0; i < parent->childItems().count(); ++i) {
2813         QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(parent->childItems().at(i));
2814         if(!item)
2815             continue;
2816         qDebug() << padding.left(depth*2) << item;
2817         dumpTree(item, depth+1);
2818     }
2819 }
2820
2821
2822 QTEST_MAIN(tst_QDeclarativeListView)
2823
2824 #include "tst_qdeclarativelistview.moc"