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