Say hello to QtQuick module
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicklistview.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 QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquicklistview_p.h"
43 #include "qquickitemview_p_p.h"
44 #include "qquickvisualitemmodel_p.h"
45
46 #include <QtDeclarative/qdeclarativeexpression.h>
47 #include <QtDeclarative/qdeclarativeengine.h>
48 #include <QtDeclarative/qdeclarativeinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qcoreapplication.h>
52
53 #include <private/qdeclarativesmoothedanimation_p_p.h>
54 #include <private/qlistmodelinterface_p.h>
55 #include "qplatformdefs.h"
56
57 QT_BEGIN_NAMESPACE
58
59 #ifndef QML_FLICK_SNAPONETHRESHOLD
60 #define QML_FLICK_SNAPONETHRESHOLD 30
61 #endif
62
63 class FxListItemSG;
64
65 class QQuickListViewPrivate : public QQuickItemViewPrivate
66 {
67     Q_DECLARE_PUBLIC(QQuickListView)
68 public:
69     static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); }
70
71     virtual Qt::Orientation layoutOrientation() const;
72     virtual bool isContentFlowReversed() const;
73     bool isRightToLeft() const;
74
75     virtual qreal positionAt(int index) const;
76     virtual qreal endPositionAt(int index) const;
77     virtual qreal originPosition() const;
78     virtual qreal lastPosition() const;
79
80     FxViewItem *itemBefore(int modelIndex) const;
81     QString sectionAt(int modelIndex);
82     qreal snapPosAt(qreal pos);
83     FxViewItem *snapItemAt(qreal pos);
84
85     virtual void init();
86     virtual void clear();
87
88     virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer);
89     virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
90     virtual void visibleItemsChanged();
91
92     virtual FxViewItem *newViewItem(int index, QQuickItem *item);
93     virtual void initializeViewItem(FxViewItem *item);
94     virtual void releaseItem(FxViewItem *item);
95     virtual void repositionPackageItemAt(QQuickItem *item, int index);
96     virtual void resetItemPosition(FxViewItem *item, FxViewItem *toItem);
97     virtual void resetFirstItemPosition();
98     virtual void moveItemBy(FxViewItem *item, qreal forwards, qreal backwards);
99
100     virtual void createHighlight();
101     virtual void updateHighlight();
102     virtual void resetHighlightPosition();
103
104     virtual void setPosition(qreal pos);
105     virtual void layoutVisibleItems();
106     bool applyInsertionChange(const QDeclarativeChangeSet::Insert &, FxViewItem *firstVisible, InsertionsResult *);
107
108     virtual void updateSections();
109     QQuickItem *getSectionItem(const QString &section);
110     void releaseSectionItem(QQuickItem *item);
111     void updateInlineSection(FxListItemSG *);
112     void updateCurrentSection();
113     void updateStickySections();
114
115     virtual qreal headerSize() const;
116     virtual qreal footerSize() const;
117     virtual bool showHeaderForIndex(int index) const;
118     virtual bool showFooterForIndex(int index) const;
119     virtual void updateHeader();
120     virtual void updateFooter();
121
122     virtual void changedVisibleIndex(int newIndex);
123     virtual void initializeCurrentItem();
124
125     void updateAverage();
126
127     void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
128     virtual void fixupPosition();
129     virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
130     virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
131                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
132
133     QQuickListView::Orientation orient;
134     qreal visiblePos;
135     qreal averageSize;
136     qreal spacing;
137     QQuickListView::SnapMode snapMode;
138
139     QSmoothedAnimation *highlightPosAnimator;
140     QSmoothedAnimation *highlightSizeAnimator;
141     qreal highlightMoveSpeed;
142     qreal highlightResizeSpeed;
143     int highlightResizeDuration;
144
145     QQuickViewSection *sectionCriteria;
146     QString currentSection;
147     static const int sectionCacheSize = 5;
148     QQuickItem *sectionCache[sectionCacheSize];
149     QQuickItem *currentSectionItem;
150     QString currentStickySection;
151     QQuickItem *nextSectionItem;
152     QString nextStickySection;
153     QString lastVisibleSection;
154     QString nextSection;
155
156     qreal overshootDist;
157     bool correctFlick : 1;
158     bool inFlickCorrection : 1;
159
160     QQuickListViewPrivate()
161         : orient(QQuickListView::Vertical)
162         , visiblePos(0)
163         , averageSize(100.0), spacing(0.0)
164         , snapMode(QQuickListView::NoSnap)
165         , highlightPosAnimator(0), highlightSizeAnimator(0)
166         , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1)
167         , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
168         , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
169     {}
170
171     friend class QQuickViewSection;
172 };
173
174 //----------------------------------------------------------------------------
175
176 QQuickViewSection::QQuickViewSection(QQuickListView *parent)
177     : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels)
178     , m_view(parent ? QQuickListViewPrivate::get(parent) : 0)
179 {
180 }
181
182 void QQuickViewSection::setProperty(const QString &property)
183 {
184     if (property != m_property) {
185         m_property = property;
186         emit propertyChanged();
187         m_view->updateSections();
188     }
189 }
190
191 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
192 {
193     if (criteria != m_criteria) {
194         m_criteria = criteria;
195         emit criteriaChanged();
196         m_view->updateSections();
197     }
198 }
199
200 void QQuickViewSection::setDelegate(QDeclarativeComponent *delegate)
201 {
202     if (delegate != m_delegate) {
203         m_delegate = delegate;
204         emit delegateChanged();
205         m_view->updateSections();
206     }
207 }
208
209 QString QQuickViewSection::sectionString(const QString &value)
210 {
211     if (m_criteria == FirstCharacter)
212         return value.isEmpty() ? QString() : value.at(0);
213     else
214         return value;
215 }
216
217 void QQuickViewSection::setLabelPositioning(int l)
218 {
219     if (m_labelPositioning != l) {
220         m_labelPositioning = l;
221         emit labelPositioningChanged();
222         m_view->updateSections();
223     }
224 }
225
226 //----------------------------------------------------------------------------
227
228 class FxListItemSG : public FxViewItem
229 {
230 public:
231     FxListItemSG(QQuickItem *i, QQuickListView *v, bool own) : FxViewItem(i, own), section(0), view(v) {
232         attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
233         if (attached)
234             static_cast<QQuickListViewAttached*>(attached)->setView(view);
235     }
236
237     ~FxListItemSG() {}
238
239     qreal position() const {
240         if (section) {
241             if (view->orientation() == QQuickListView::Vertical)
242                 return section->y();
243             else
244                 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
245         } else {
246             return itemPosition();
247         }
248     }
249     qreal itemPosition() const {
250         if (view->orientation() == QQuickListView::Vertical)
251             return item->y();
252         else
253             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
254     }
255     qreal size() const {
256         if (section)
257             return (view->orientation() == QQuickListView::Vertical ? item->height()+section->height() : item->width()+section->width());
258         else
259             return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
260     }
261     qreal itemSize() const {
262         return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
263     }
264     qreal sectionSize() const {
265         if (section)
266             return (view->orientation() == QQuickListView::Vertical ? section->height() : section->width());
267         return 0.0;
268     }
269     qreal endPosition() const {
270         if (view->orientation() == QQuickListView::Vertical) {
271             return item->y() + item->height();
272         } else {
273             return (view->effectiveLayoutDirection() == Qt::RightToLeft
274                     ? -item->x()
275                     : item->x() + item->width());
276         }
277     }
278     void setPosition(qreal pos) {
279         if (view->orientation() == QQuickListView::Vertical) {
280             if (section) {
281                 section->setY(pos);
282                 pos += section->height();
283             }
284             item->setY(pos);
285         } else {
286             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
287                 if (section) {
288                     section->setX(-section->width()-pos);
289                     pos += section->width();
290                 }
291                 item->setX(-item->width()-pos);
292             } else {
293                 if (section) {
294                     section->setX(pos);
295                     pos += section->width();
296                 }
297                 item->setX(pos);
298             }
299         }
300     }
301     void setSize(qreal size) {
302         if (view->orientation() == QQuickListView::Vertical)
303             item->setHeight(size);
304         else
305             item->setWidth(size);
306     }
307     bool contains(qreal x, qreal y) const {
308         return (x >= item->x() && x < item->x() + item->width() &&
309                 y >= item->y() && y < item->y() + item->height());
310     }
311
312     QQuickItem *section;
313     QQuickListView *view;
314 };
315
316 //----------------------------------------------------------------------------
317
318 bool QQuickListViewPrivate::isContentFlowReversed() const
319 {
320     return isRightToLeft();
321 }
322
323 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
324 {
325     return static_cast<Qt::Orientation>(orient);
326 }
327
328 bool QQuickListViewPrivate::isRightToLeft() const
329 {
330     Q_Q(const QQuickListView);
331     return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
332 }
333
334 // Returns the item before modelIndex, if created.
335 // May return an item marked for removal.
336 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
337 {
338     if (modelIndex < visibleIndex)
339         return 0;
340     int idx = 1;
341     int lastIndex = -1;
342     while (idx < visibleItems.count()) {
343         FxViewItem *item = visibleItems.at(idx);
344         if (item->index != -1)
345             lastIndex = item->index;
346         if (item->index == modelIndex)
347             return visibleItems.at(idx-1);
348         ++idx;
349     }
350     if (lastIndex == modelIndex-1)
351         return visibleItems.last();
352     return 0;
353 }
354
355 void QQuickListViewPrivate::setPosition(qreal pos)
356 {
357     Q_Q(QQuickListView);
358     if (orient == QQuickListView::Vertical) {
359         q->QQuickFlickable::setContentY(pos);
360     } else {
361         if (isRightToLeft())
362             q->QQuickFlickable::setContentX(-pos-size());
363         else
364             q->QQuickFlickable::setContentX(pos);
365     }
366 }
367
368 qreal QQuickListViewPrivate::originPosition() const
369 {
370     qreal pos = 0;
371     if (!visibleItems.isEmpty()) {
372         pos = (*visibleItems.constBegin())->position();
373         if (visibleIndex > 0)
374             pos -= visibleIndex * (averageSize + spacing);
375     }
376     return pos;
377 }
378
379 qreal QQuickListViewPrivate::lastPosition() const
380 {
381     qreal pos = 0;
382     if (!visibleItems.isEmpty()) {
383         int invisibleCount = visibleItems.count() - visibleIndex;
384         for (int i = visibleItems.count()-1; i >= 0; --i) {
385             if (visibleItems.at(i)->index != -1) {
386                 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
387                 break;
388             }
389         }
390         pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
391     } else if (model && model->count()) {
392         pos = (model->count() * averageSize + (model->count()-1) * spacing);
393     }
394     return pos;
395 }
396
397 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
398 {
399     if (FxViewItem *item = visibleItem(modelIndex))
400         return item->position();
401     if (!visibleItems.isEmpty()) {
402         if (modelIndex < visibleIndex) {
403             int count = visibleIndex - modelIndex;
404             qreal cs = 0;
405             if (modelIndex == currentIndex && currentItem) {
406                 cs = currentItem->size() + spacing;
407                 --count;
408             }
409             return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
410         } else {
411             int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
412             return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
413         }
414     }
415     return 0;
416 }
417
418 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
419 {
420     if (FxViewItem *item = visibleItem(modelIndex))
421         return item->endPosition();
422     if (!visibleItems.isEmpty()) {
423         if (modelIndex < visibleIndex) {
424             int count = visibleIndex - modelIndex;
425             return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
426         } else {
427             int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
428             return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
429         }
430     }
431     return 0;
432 }
433
434 QString QQuickListViewPrivate::sectionAt(int modelIndex)
435 {
436     if (FxViewItem *item = visibleItem(modelIndex))
437         return item->attached->section();
438
439     QString section;
440     if (sectionCriteria) {
441         QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
442         section = sectionCriteria->sectionString(propValue);
443     }
444
445     return section;
446 }
447
448 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
449 {
450     if (FxViewItem *snapItem = snapItemAt(pos))
451         return snapItem->position();
452     if (visibleItems.count()) {
453         qreal firstPos = (*visibleItems.constBegin())->position();
454         qreal endPos = (*(--visibleItems.constEnd()))->position();
455         if (pos < firstPos) {
456             return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
457         } else if (pos > endPos)
458             return endPos + qRound((pos - endPos) / averageSize) * averageSize;
459     }
460     return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
461 }
462
463 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
464 {
465     FxViewItem *snapItem = 0;
466     qreal prevItemSize = 0;
467     for (int i = 0; i < visibleItems.count(); ++i) {
468         FxViewItem *item = visibleItems.at(i);
469         if (item->index == -1)
470             continue;
471         qreal itemTop = item->position();
472         if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
473             return item;
474         if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
475             snapItem = item;
476         prevItemSize = item->size();
477     }
478     return snapItem;
479 }
480
481 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
482 {
483     visiblePos = positionAt(newIndex);
484     visibleIndex = newIndex;
485 }
486
487 void QQuickListViewPrivate::init()
488 {
489     QQuickItemViewPrivate::init();
490     ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
491 }
492
493 void QQuickListViewPrivate::clear()
494 {
495     for (int i = 0; i < sectionCacheSize; ++i) {
496         delete sectionCache[i];
497         sectionCache[i] = 0;
498     }
499     visiblePos = 0;
500     currentSectionItem = 0;
501     nextSectionItem = 0;
502     lastVisibleSection = QString();
503     QQuickItemViewPrivate::clear();
504 }
505
506 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
507 {
508     Q_Q(QQuickListView);
509
510     FxListItemSG *listItem = new FxListItemSG(item, q, false);
511     listItem->index = modelIndex;
512
513     // initialise attached properties
514     if (sectionCriteria) {
515         QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
516         listItem->attached->m_section = sectionCriteria->sectionString(propValue);
517         if (modelIndex > 0) {
518             if (FxViewItem *item = itemBefore(modelIndex))
519                 listItem->attached->m_prevSection = item->attached->section();
520             else
521                 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
522         }
523         if (modelIndex < model->count()-1) {
524             if (FxViewItem *item = visibleItem(modelIndex+1))
525                 listItem->attached->m_nextSection = static_cast<QQuickListViewAttached*>(item->attached)->section();
526             else
527                 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
528         }
529     }
530
531     return listItem;
532 }
533
534 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
535 {
536     QQuickItemViewPrivate::initializeViewItem(item);
537
538     QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
539     itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
540
541     if (sectionCriteria && sectionCriteria->delegate()) {
542         if (item->attached->m_prevSection != item->attached->m_section)
543             updateInlineSection(static_cast<FxListItemSG*>(item));
544     }
545 }
546
547 void QQuickListViewPrivate::releaseItem(FxViewItem *item)
548 {
549     if (item) {
550         FxListItemSG* listItem = static_cast<FxListItemSG*>(item);
551         if (listItem->section) {
552             int i = 0;
553             do {
554                 if (!sectionCache[i]) {
555                     sectionCache[i] = listItem->section;
556                     sectionCache[i]->setVisible(false);
557                     listItem->section = 0;
558                     break;
559                 }
560                 ++i;
561             } while (i < sectionCacheSize);
562             delete listItem->section;
563         }
564     }
565     QQuickItemViewPrivate::releaseItem(item);
566 }
567
568 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer)
569 {
570     qreal itemEnd = visiblePos;
571     if (visibleItems.count()) {
572         visiblePos = (*visibleItems.constBegin())->position();
573         itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
574     }
575
576     int modelIndex = findLastVisibleIndex();
577     bool haveValidItems = modelIndex >= 0;
578     modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
579
580     if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
581         || fillTo < visiblePos - averageSize - spacing)) {
582         // We've jumped more than a page.  Estimate which items are now
583         // visible and fill from there.
584         int count = (fillFrom - itemEnd) / (averageSize + spacing);
585         for (int i = 0; i < visibleItems.count(); ++i)
586             releaseItem(visibleItems.at(i));
587         visibleItems.clear();
588         modelIndex += count;
589         if (modelIndex >= model->count()) {
590             count -= modelIndex - model->count() + 1;
591             modelIndex = model->count() - 1;
592         } else if (modelIndex < 0) {
593             count -= modelIndex;
594             modelIndex = 0;
595         }
596         visibleIndex = modelIndex;
597         visiblePos = itemEnd + count * (averageSize + spacing);
598         itemEnd = visiblePos;
599     }
600
601     bool changed = false;
602     FxListItemSG *item = 0;
603     qreal pos = itemEnd;
604     while (modelIndex < model->count() && pos <= fillTo) {
605 //        qDebug() << "refill: append item" << modelIndex << "pos" << pos;
606         if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
607             break;
608         item->setPosition(pos);
609         item->item->setVisible(!doBuffer);
610         pos += item->size() + spacing;
611         visibleItems.append(item);
612         ++modelIndex;
613         changed = true;
614     }
615     while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
616 //        qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
617         if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
618             break;
619         --visibleIndex;
620         visiblePos -= item->size() + spacing;
621         item->setPosition(visiblePos);
622         item->item->setVisible(!doBuffer);
623         visibleItems.prepend(item);
624         changed = true;
625     }
626
627     return changed;
628 }
629
630 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
631 {
632     FxViewItem *item = 0;
633     bool changed = false;
634
635     // Remove items from the start of the view.
636     // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
637     // removed, otherwise a zero-sized item is infinitely added and removed over and
638     // over by refill().
639     int index = 0;
640     while (visibleItems.count() > 1 && index < visibleItems.count()
641            && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
642         if (item->attached->delayRemove())
643             break;
644         if (item->size() > 0) {
645 //            qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
646
647             // remove this item and all zero-sized items before it
648             while (item) {
649                 if (item->index != -1)
650                     visibleIndex++;
651                 visibleItems.removeAt(index);
652                 releaseItem(item);
653                 if (index == 0)
654                     break;
655                 item = visibleItems.at(--index);
656             }
657             changed = true;
658         } else {
659             index++;
660         }
661     }
662
663     while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
664         if (item->attached->delayRemove())
665             break;
666 //        qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
667         visibleItems.removeLast();
668         releaseItem(item);
669         changed = true;
670     }
671
672     return changed;
673 }
674
675 void QQuickListViewPrivate::visibleItemsChanged()
676 {
677     if (visibleItems.count())
678         visiblePos = (*visibleItems.constBegin())->position();
679     updateAverage();
680     if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
681         static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
682         updateHighlight();
683     }
684     if (sectionCriteria)
685         updateCurrentSection();
686     updateHeader();
687     updateFooter();
688     updateViewport();
689     updateUnrequestedPositions();
690 }
691
692 void QQuickListViewPrivate::layoutVisibleItems()
693 {
694     if (!visibleItems.isEmpty()) {
695         bool fixedCurrent = currentItem && (*visibleItems.constBegin())->item == currentItem->item;
696         qreal sum = (*visibleItems.constBegin())->size();
697         qreal pos = (*visibleItems.constBegin())->position() + (*visibleItems.constBegin())->size() + spacing;
698         for (int i=1; i < visibleItems.count(); ++i) {
699             FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
700             item->setPosition(pos);
701             pos += item->size() + spacing;
702             sum += item->size();
703             fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
704         }
705         averageSize = qRound(sum / visibleItems.count());
706
707         // move current item if it is not a visible item.
708         if (currentIndex >= 0 && currentItem && !fixedCurrent) {
709             static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
710         }
711     }
712 }
713
714 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
715 {
716     Q_Q(QQuickListView);
717     qreal pos = position();
718     if (orient == QQuickListView::Vertical) {
719         if (item->y() + item->height() > pos && item->y() < pos + q->height())
720             item->setY(positionAt(index));
721     } else {
722         if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
723             if (isRightToLeft())
724                 item->setX(-positionAt(index)-item->width());
725             else
726                 item->setX(positionAt(index));
727         }
728     }
729 }
730
731 void QQuickListViewPrivate::resetItemPosition(FxViewItem *item, FxViewItem *toItem)
732 {
733     if (item == toItem)
734         return;
735     static_cast<FxListItemSG*>(item)->setPosition(toItem->position());
736 }
737
738 void QQuickListViewPrivate::resetFirstItemPosition()
739 {
740     FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
741     item->setPosition(0);
742 }
743
744 void QQuickListViewPrivate::moveItemBy(FxViewItem *item, qreal forwards, qreal backwards)
745 {
746     qreal diff = forwards - backwards;
747     static_cast<FxListItemSG*>(item)->setPosition(item->position() + diff);
748 }
749
750 void QQuickListViewPrivate::createHighlight()
751 {
752     Q_Q(QQuickListView);
753     bool changed = false;
754     if (highlight) {
755         if (trackedItem == highlight)
756             trackedItem = 0;
757         delete highlight;
758         highlight = 0;
759
760         delete highlightPosAnimator;
761         delete highlightSizeAnimator;
762         highlightPosAnimator = 0;
763         highlightSizeAnimator = 0;
764
765         changed = true;
766     }
767
768     if (currentItem) {
769         QQuickItem *item = createHighlightItem();
770         if (item) {
771             FxListItemSG *newHighlight = new FxListItemSG(item, q, true);
772
773             if (autoHighlight) {
774                 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
775                 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
776             }
777             const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
778             highlightPosAnimator = new QSmoothedAnimation(q);
779             highlightPosAnimator->target = QDeclarativeProperty(item, posProp);
780             highlightPosAnimator->velocity = highlightMoveSpeed;
781             highlightPosAnimator->userDuration = highlightMoveDuration;
782
783             const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
784             highlightSizeAnimator = new QSmoothedAnimation(q);
785             highlightSizeAnimator->velocity = highlightResizeSpeed;
786             highlightSizeAnimator->userDuration = highlightResizeDuration;
787             highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp);
788
789             highlight = newHighlight;
790             changed = true;
791         }
792     }
793     if (changed)
794         emit q->highlightItemChanged();
795 }
796
797 void QQuickListViewPrivate::updateHighlight()
798 {
799     applyPendingChanges();
800
801     if ((!currentItem && highlight) || (currentItem && !highlight))
802         createHighlight();
803     bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
804     if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
805         // auto-update highlight
806         FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
807         highlightPosAnimator->to = isRightToLeft()
808                 ? -listItem->itemPosition()-listItem->itemSize()
809                 : listItem->itemPosition();
810         highlightSizeAnimator->to = listItem->itemSize();
811         if (orient == QQuickListView::Vertical) {
812             if (highlight->item->width() == 0)
813                 highlight->item->setWidth(currentItem->item->width());
814         } else {
815             if (highlight->item->height() == 0)
816                 highlight->item->setHeight(currentItem->item->height());
817         }
818
819         highlightPosAnimator->restart();
820         highlightSizeAnimator->restart();
821     }
822     updateTrackedItem();
823 }
824
825 void QQuickListViewPrivate::resetHighlightPosition()
826 {
827     if (highlight && currentItem)
828         static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
829 }
830
831 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString &section)
832 {
833     Q_Q(QQuickListView);
834     QQuickItem *sectionItem = 0;
835     int i = sectionCacheSize-1;
836     while (i >= 0 && !sectionCache[i])
837         --i;
838     if (i >= 0) {
839         sectionItem = sectionCache[i];
840         sectionCache[i] = 0;
841         sectionItem->setVisible(true);
842         QDeclarativeContext *context = QDeclarativeEngine::contextForObject(sectionItem)->parentContext();
843         context->setContextProperty(QLatin1String("section"), section);
844     } else {
845         QDeclarativeContext *creationContext = sectionCriteria->delegate()->creationContext();
846         QDeclarativeContext *context = new QDeclarativeContext(
847                 creationContext ? creationContext : qmlContext(q));
848         context->setContextProperty(QLatin1String("section"), section);
849         QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
850         if (nobj) {
851             QDeclarative_setParent_noEvent(context, nobj);
852             sectionItem = qobject_cast<QQuickItem *>(nobj);
853             if (!sectionItem) {
854                 delete nobj;
855             } else {
856                 sectionItem->setZ(2);
857                 QDeclarative_setParent_noEvent(sectionItem, contentItem);
858                 sectionItem->setParentItem(contentItem);
859             }
860         } else {
861             delete context;
862         }
863         sectionCriteria->delegate()->completeCreate();
864     }
865
866     return sectionItem;
867 }
868
869 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
870 {
871     int i = 0;
872     do {
873         if (!sectionCache[i]) {
874             sectionCache[i] = item;
875             sectionCache[i]->setVisible(false);
876             return;
877         }
878         ++i;
879     } while (i < sectionCacheSize);
880     delete item;
881 }
882
883 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
884 {
885     if (!sectionCriteria || !sectionCriteria->delegate())
886         return;
887     if (listItem->attached->m_prevSection != listItem->attached->m_section
888             && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
889                 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
890         if (!listItem->section) {
891             qreal pos = listItem->position();
892             listItem->section = getSectionItem(listItem->attached->m_section);
893             listItem->setPosition(pos);
894         } else {
895             QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
896             context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
897         }
898     } else if (listItem->section) {
899         qreal pos = listItem->position();
900         releaseSectionItem(listItem->section);
901         listItem->section = 0;
902         listItem->setPosition(pos);
903     }
904 }
905
906 void QQuickListViewPrivate::updateStickySections()
907 {
908     if (!sectionCriteria || visibleItems.isEmpty()
909             || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
910         return;
911
912     bool isRtl = isRightToLeft();
913     qreal viewPos = isRightToLeft() ? -position()-size() : position();
914     QQuickItem *sectionItem = 0;
915     QQuickItem *lastSectionItem = 0;
916     int index = 0;
917     while (index < visibleItems.count()) {
918         if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section) {
919             // Find the current section header and last visible section header
920             // and hide them if they will overlap a static section header.
921             qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
922             qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
923             bool visTop = true;
924             if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
925                 visTop = isRtl ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
926             bool visBot = true;
927             if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
928                 visBot = isRtl ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
929             section->setVisible(visBot && visTop);
930             if (visTop && !sectionItem)
931                 sectionItem = section;
932             if (isRtl) {
933                if (-sectionPos <= viewPos + size())
934                     lastSectionItem = section;
935             } else {
936                 if (sectionPos + sectionSize < viewPos + size())
937                     lastSectionItem = section;
938             }
939         }
940         ++index;
941     }
942
943     // Current section header
944     if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart) {
945         if (!currentSectionItem) {
946             currentSectionItem = getSectionItem(currentSection);
947         } else if (currentStickySection != currentSection) {
948             QDeclarativeContext *context = QDeclarativeEngine::contextForObject(currentSectionItem)->parentContext();
949             context->setContextProperty(QLatin1String("section"), currentSection);
950         }
951         currentStickySection = currentSection;
952         if (!currentSectionItem)
953             return;
954
955         qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
956         bool atBeginning = orient == QQuickListView::Vertical ? vData.atBeginning : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
957         currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
958         qreal pos = isRtl ? position() + size() - sectionSize : viewPos;
959         if (sectionItem) {
960             qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
961             pos = isRtl ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
962         }
963         if (header)
964             pos = isRtl ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
965         if (footer)
966             pos = isRtl ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
967         if (orient == QQuickListView::Vertical)
968             currentSectionItem->setY(pos);
969         else
970             currentSectionItem->setX(pos);
971     } else if (currentSectionItem) {
972         releaseSectionItem(currentSectionItem);
973         currentSectionItem = 0;
974     }
975
976     // Next section footer
977     if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
978         if (!nextSectionItem) {
979             nextSectionItem = getSectionItem(nextSection);
980         } else if (nextStickySection != nextSection) {
981             QDeclarativeContext *context = QDeclarativeEngine::contextForObject(nextSectionItem)->parentContext();
982             context->setContextProperty(QLatin1String("section"), nextSection);
983         }
984         nextStickySection = nextSection;
985         if (!nextSectionItem)
986             return;
987
988         qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
989         nextSectionItem->setVisible(!nextSection.isEmpty());
990         qreal pos = isRtl ? position() : viewPos + size() - sectionSize;
991         if (lastSectionItem) {
992             qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
993             pos = isRtl ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
994         }
995         if (header)
996             pos = isRtl ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
997         if (orient == QQuickListView::Vertical)
998             nextSectionItem->setY(pos);
999         else
1000             nextSectionItem->setX(pos);
1001     } else if (nextSectionItem) {
1002         releaseSectionItem(nextSectionItem);
1003         nextSectionItem = 0;
1004     }
1005 }
1006
1007 void QQuickListViewPrivate::updateSections()
1008 {
1009     Q_Q(QQuickListView);
1010     if (!q->isComponentComplete())
1011         return;
1012
1013     QQuickItemViewPrivate::updateSections();
1014
1015     if (sectionCriteria && !visibleItems.isEmpty()) {
1016         QString prevSection;
1017         if (visibleIndex > 0)
1018             prevSection = sectionAt(visibleIndex-1);
1019         QQuickListViewAttached *prevAtt = 0;
1020         int idx = -1;
1021         for (int i = 0; i < visibleItems.count(); ++i) {
1022             QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1023             attached->setPrevSection(prevSection);
1024             if (visibleItems.at(i)->index != -1) {
1025                 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1026                 attached->setSection(sectionCriteria->sectionString(propValue));
1027                 idx = visibleItems.at(i)->index;
1028             }
1029             updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1030             if (prevAtt)
1031                 prevAtt->setNextSection(attached->section());
1032             prevSection = attached->section();
1033             prevAtt = attached;
1034         }
1035         if (prevAtt) {
1036             if (idx > 0 && idx < model->count()-1)
1037                 prevAtt->setNextSection(sectionAt(idx+1));
1038             else
1039                 prevAtt->setNextSection(QString());
1040         }
1041     }
1042
1043     lastVisibleSection = QString();
1044     updateCurrentSection();
1045     updateStickySections();
1046 }
1047
1048 void QQuickListViewPrivate::updateCurrentSection()
1049 {
1050     Q_Q(QQuickListView);
1051     if (!sectionCriteria || visibleItems.isEmpty()) {
1052         if (!currentSection.isEmpty()) {
1053             currentSection.clear();
1054             emit q->currentSectionChanged();
1055         }
1056         return;
1057     }
1058     bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1059     qreal sectionThreshold = position();
1060     if (currentSectionItem && !inlineSections)
1061         sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1062     int index = 0;
1063     int modelIndex = visibleIndex;
1064     while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1065         if (visibleItems.at(index)->index != -1)
1066             modelIndex = visibleItems.at(index)->index;
1067         ++index;
1068     }
1069
1070     QString newSection = currentSection;
1071     if (index < visibleItems.count())
1072         newSection = visibleItems.at(index)->attached->section();
1073     else
1074         newSection = (*visibleItems.constBegin())->attached->section();
1075     if (newSection != currentSection) {
1076         currentSection = newSection;
1077         updateStickySections();
1078         emit q->currentSectionChanged();
1079     }
1080
1081     if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1082         // Don't want to scan for next section on every movement, so remember
1083         // the last section in the visible area and only scan for the next
1084         // section when that changes.  Clearing lastVisibleSection will also
1085         // force searching.
1086         QString lastSection = currentSection;
1087         qreal endPos = isRightToLeft() ? -position() : position() + size();
1088         if (nextSectionItem && !inlineSections)
1089             endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1090         while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1091             if (visibleItems.at(index)->index != -1)
1092                 modelIndex = visibleItems.at(index)->index;
1093             lastSection = visibleItems.at(index)->attached->section();
1094             ++index;
1095         }
1096
1097         if (lastVisibleSection != lastSection) {
1098             nextSection = QString();
1099             lastVisibleSection = lastSection;
1100             for (int i = modelIndex; i < itemCount; ++i) {
1101                 QString section = sectionAt(i);
1102                 if (section != lastSection) {
1103                     nextSection = section;
1104                     updateStickySections();
1105                     break;
1106                 }
1107             }
1108         }
1109     }
1110 }
1111
1112 void QQuickListViewPrivate::initializeCurrentItem()
1113 {
1114     QQuickItemViewPrivate::initializeCurrentItem();
1115
1116     if (currentItem) {
1117         FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1118
1119         if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1120             // We can calculate exact postion in this case
1121             listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1122         } else {
1123             // Create current item now and position as best we can.
1124             // Its position will be corrected when it becomes visible.
1125             listItem->setPosition(positionAt(currentIndex));
1126         }
1127
1128         // Avoid showing section delegate twice.  We still need the section heading so that
1129         // currentItem positioning works correctly.
1130         // This is slightly sub-optimal, but section heading caching minimizes the impact.
1131         if (listItem->section)
1132             listItem->section->setVisible(false);
1133
1134         if (visibleItems.isEmpty())
1135             averageSize = listItem->size();
1136     }
1137 }
1138
1139 void QQuickListViewPrivate::updateAverage()
1140 {
1141     if (!visibleItems.count())
1142         return;
1143     qreal sum = 0.0;
1144     for (int i = 0; i < visibleItems.count(); ++i)
1145         sum += visibleItems.at(i)->size();
1146     averageSize = qRound(sum / visibleItems.count());
1147 }
1148
1149 qreal QQuickListViewPrivate::headerSize() const
1150 {
1151     return header ? header->size() : 0.0;
1152 }
1153
1154 qreal QQuickListViewPrivate::footerSize() const
1155 {
1156     return footer ? footer->size() : 0.0;
1157 }
1158
1159 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1160 {
1161     return index == 0;
1162 }
1163
1164 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1165 {
1166     return index == model->count()-1;
1167 }
1168
1169 void QQuickListViewPrivate::updateFooter()
1170 {
1171     Q_Q(QQuickListView);
1172     bool created = false;
1173     if (!footer) {
1174         QQuickItem *item = createComponentItem(footerComponent, true);
1175         if (!item)
1176             return;
1177         item->setZ(1);
1178         footer = new FxListItemSG(item, q, true);
1179         created = true;
1180     }
1181
1182     FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1183     if (visibleItems.count()) {
1184         qreal endPos = lastPosition();
1185         if (findLastVisibleIndex() == model->count()-1) {
1186             listItem->setPosition(endPos);
1187         } else {
1188             qreal visiblePos = position() + q->height();
1189             if (endPos <= visiblePos || listItem->position() < endPos)
1190                 listItem->setPosition(endPos);
1191         }
1192     } else {
1193         listItem->setPosition(visiblePos);
1194     }
1195
1196     if (created)
1197         emit q->footerItemChanged();
1198 }
1199
1200 void QQuickListViewPrivate::updateHeader()
1201 {
1202     Q_Q(QQuickListView);
1203     bool created = false;
1204     if (!header) {
1205         QQuickItem *item = createComponentItem(headerComponent, true);
1206         if (!item)
1207             return;
1208         item->setZ(1);
1209         header = new FxListItemSG(item, q, true);
1210         created = true;
1211     }
1212
1213     FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1214     if (listItem) {
1215         if (visibleItems.count()) {
1216             qreal startPos = originPosition();
1217             if (visibleIndex == 0) {
1218                 listItem->setPosition(startPos - headerSize());
1219             } else {
1220                 if (position() <= startPos || listItem->position() > startPos - headerSize())
1221                     listItem->setPosition(startPos - headerSize());
1222             }
1223         } else {
1224             listItem->setPosition(-headerSize());
1225         }
1226     }
1227
1228     if (created)
1229         emit q->headerItemChanged();
1230 }
1231
1232 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1233 {
1234     Q_Q(QQuickListView);
1235     QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1236     if (!q->isComponentComplete())
1237         return;
1238     if (item != contentItem && (!highlight || item != highlight->item)) {
1239         if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1240             || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1241             forceLayout = true;
1242             q->polish();
1243         }
1244     }
1245 }
1246
1247 void QQuickListViewPrivate::fixupPosition()
1248 {
1249     if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1250         || snapMode != QQuickListView::NoSnap)
1251         moveReason = Other;
1252     if (orient == QQuickListView::Vertical)
1253         fixupY();
1254     else
1255         fixupX();
1256 }
1257
1258 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1259 {
1260     if ((orient == QQuickListView::Horizontal && &data == &vData)
1261         || (orient == QQuickListView::Vertical && &data == &hData))
1262         return;
1263
1264     correctFlick = false;
1265     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1266     bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1267
1268     qreal viewPos = isRightToLeft() ? -position()-size() : position();
1269
1270     if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1271         qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1272         if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1273             // if we've been dragged < averageSize/2 then bias towards the next item
1274             qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1275             qreal bias = 0;
1276             if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1277                 bias = averageSize/2;
1278             else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1279                 bias = -averageSize/2;
1280             if (isRightToLeft())
1281                 bias = -bias;
1282             tempPosition -= bias;
1283         }
1284         FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1285         if (!topItem && strictHighlightRange && currentItem) {
1286             // StrictlyEnforceRange always keeps an item in range
1287             updateHighlight();
1288             topItem = currentItem;
1289         }
1290         FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1291         if (!bottomItem && strictHighlightRange && currentItem) {
1292             // StrictlyEnforceRange always keeps an item in range
1293             updateHighlight();
1294             bottomItem = currentItem;
1295         }
1296         qreal pos;
1297         bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1298         if (topItem && (isInBounds || strictHighlightRange)) {
1299             if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1300                 pos = isRightToLeft() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1301             } else {
1302                 if (isRightToLeft())
1303                     pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1304                 else
1305                     pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1306             }
1307         } else if (bottomItem && isInBounds) {
1308             if (isRightToLeft())
1309                 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1310             else
1311                 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1312         } else {
1313             QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1314             return;
1315         }
1316
1317         qreal dist = qAbs(data.move + pos);
1318         if (dist > 0) {
1319             timeline.reset(data.move);
1320             if (fixupMode != Immediate) {
1321                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1322                 data.fixingUp = true;
1323             } else {
1324                 timeline.set(data.move, -pos);
1325             }
1326             vTime = timeline.time();
1327         }
1328     } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1329         updateHighlight();
1330         qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1331         if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1332             viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1333         if (viewPos > pos - highlightRangeStart)
1334             viewPos = pos - highlightRangeStart;
1335         if (isRightToLeft())
1336             viewPos = -viewPos-size();
1337
1338         timeline.reset(data.move);
1339         if (viewPos != position()) {
1340             if (fixupMode != Immediate) {
1341                 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1342                 data.fixingUp = true;
1343             } else {
1344                 timeline.set(data.move, -viewPos);
1345             }
1346         }
1347         vTime = timeline.time();
1348     } else {
1349         QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1350     }
1351     data.inOvershoot = false;
1352     fixupMode = Normal;
1353 }
1354
1355 void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1356                                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1357 {
1358     Q_Q(QQuickListView);
1359
1360     data.fixingUp = false;
1361     moveReason = Mouse;
1362     if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1363         correctFlick = true;
1364         QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1365         return;
1366     }
1367     qreal maxDistance = 0;
1368     qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1369
1370     // -ve velocity means list is moving up/left
1371     if (velocity > 0) {
1372         if (data.move.value() < minExtent) {
1373             if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1374                 // if we've been dragged < averageSize/2 then bias towards the next item
1375                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1376                 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1377                 if (isRightToLeft())
1378                     bias = -bias;
1379                 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1380                 maxDistance = qAbs(data.flickTarget - data.move.value());
1381                 velocity = maxVelocity;
1382             } else {
1383                 maxDistance = qAbs(minExtent - data.move.value());
1384             }
1385         }
1386         if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1387             data.flickTarget = minExtent;
1388     } else {
1389         if (data.move.value() > maxExtent) {
1390             if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1391                 // if we've been dragged < averageSize/2 then bias towards the next item
1392                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1393                 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1394                 if (isRightToLeft())
1395                     bias = -bias;
1396                 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1397                 maxDistance = qAbs(data.flickTarget - data.move.value());
1398                 velocity = -maxVelocity;
1399             } else {
1400                 maxDistance = qAbs(maxExtent - data.move.value());
1401             }
1402         }
1403         if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1404             data.flickTarget = maxExtent;
1405     }
1406     bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1407     if (maxDistance > 0 || overShoot) {
1408         // These modes require the list to stop exactly on an item boundary.
1409         // The initial flick will estimate the boundary to stop on.
1410         // Since list items can have variable sizes, the boundary will be
1411         // reevaluated and adjusted as we approach the boundary.
1412         qreal v = velocity;
1413         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1414             if (v < 0)
1415                 v = -maxVelocity;
1416             else
1417                 v = maxVelocity;
1418         }
1419         if (!hData.flicking && !vData.flicking) {
1420             // the initial flick - estimate boundary
1421             qreal accel = deceleration;
1422             qreal v2 = v * v;
1423             overshootDist = 0.0;
1424             // + averageSize/4 to encourage moving at least one item in the flick direction
1425             qreal dist = v2 / (accel * 2.0) + averageSize/4;
1426             if (maxDistance > 0)
1427                 dist = qMin(dist, maxDistance);
1428             if (v > 0)
1429                 dist = -dist;
1430             if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1431                 if (snapMode != QQuickListView::SnapOneItem) {
1432                     qreal distTemp = isRightToLeft() ? -dist : dist;
1433                     data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1434                 }
1435                 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1436                 if (overShoot) {
1437                     if (data.flickTarget >= minExtent) {
1438                         overshootDist = overShootDistance(vSize);
1439                         data.flickTarget += overshootDist;
1440                     } else if (data.flickTarget <= maxExtent) {
1441                         overshootDist = overShootDistance(vSize);
1442                         data.flickTarget -= overshootDist;
1443                     }
1444                 }
1445                 qreal adjDist = -data.flickTarget + data.move.value();
1446                 if (qAbs(adjDist) > qAbs(dist)) {
1447                     // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1448                     qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1449                     if (adjv2 > v2) {
1450                         v2 = adjv2;
1451                         v = qSqrt(v2);
1452                         if (dist > 0)
1453                             v = -v;
1454                     }
1455                 }
1456                 dist = adjDist;
1457                 accel = v2 / (2.0f * qAbs(dist));
1458             } else if (overShoot) {
1459                 data.flickTarget = data.move.value() - dist;
1460                 if (data.flickTarget >= minExtent) {
1461                     overshootDist = overShootDistance(vSize);
1462                     data.flickTarget += overshootDist;
1463                 } else if (data.flickTarget <= maxExtent) {
1464                     overshootDist = overShootDistance(vSize);
1465                     data.flickTarget -= overshootDist;
1466                 }
1467             }
1468             timeline.reset(data.move);
1469             timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1470             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1471             if (!hData.flicking && q->xflick()) {
1472                 hData.flicking = true;
1473                 emit q->flickingChanged();
1474                 emit q->flickingHorizontallyChanged();
1475                 emit q->flickStarted();
1476             }
1477             if (!vData.flicking && q->yflick()) {
1478                 vData.flicking = true;
1479                 emit q->flickingChanged();
1480                 emit q->flickingVerticallyChanged();
1481                 emit q->flickStarted();
1482             }
1483             correctFlick = true;
1484         } else {
1485             // reevaluate the target boundary.
1486             qreal newtarget = data.flickTarget;
1487             if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1488                 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1489                 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1490                 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1491             }
1492             if (velocity < 0 && newtarget <= maxExtent)
1493                 newtarget = maxExtent - overshootDist;
1494             else if (velocity > 0 && newtarget >= minExtent)
1495                 newtarget = minExtent + overshootDist;
1496             if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1497                 if (qAbs(velocity) < MinimumFlickVelocity)
1498                     correctFlick = false;
1499                 return;
1500             }
1501             data.flickTarget = newtarget;
1502             qreal dist = -newtarget + data.move.value();
1503             if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1504                 correctFlick = false;
1505                 timeline.reset(data.move);
1506                 fixup(data, minExtent, maxExtent);
1507                 return;
1508             }
1509             timeline.reset(data.move);
1510             timeline.accelDistance(data.move, v, -dist);
1511             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1512         }
1513     } else {
1514         correctFlick = false;
1515         timeline.reset(data.move);
1516         fixup(data, minExtent, maxExtent);
1517     }
1518 }
1519
1520 //----------------------------------------------------------------------------
1521
1522 /*!
1523     \qmlclass ListView QQuickListView
1524     \inqmlmodule QtQuick 2
1525     \ingroup qml-view-elements
1526     \inherits Flickable
1527     \brief The ListView item provides a list view of items provided by a model.
1528
1529     A ListView displays data from models created from built-in QML elements like ListModel
1530     and XmlListModel, or custom model classes defined in C++ that inherit from
1531     QAbstractListModel.
1532
1533     A ListView has a \l model, which defines the data to be displayed, and
1534     a \l delegate, which defines how the data should be displayed. Items in a
1535     ListView are laid out horizontally or vertically. List views are inherently
1536     flickable because ListView inherits from \l Flickable.
1537
1538     \section1 Example Usage
1539
1540     The following example shows the definition of a simple list model defined
1541     in a file called \c ContactModel.qml:
1542
1543     \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0
1544
1545     Another component can display this model data in a ListView, like this:
1546
1547     \snippet doc/src/snippets/declarative/listview/listview.qml import
1548     \codeline
1549     \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple
1550
1551     \image listview-simple.png
1552
1553     Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1554     for its delegate. The view will create a new \l Text component for each item in the model. Notice
1555     the delegate is able to access the model's \c name and \c number data directly.
1556
1557     An improved list view is shown below. The delegate is visually improved and is moved
1558     into a separate \c contactDelegate component.
1559
1560     \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced
1561     \image listview-highlight.png
1562
1563     The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1564     and \c focus is set to \c true to enable keyboard navigation for the list view.
1565     The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1566
1567     Delegates are instantiated as needed and may be destroyed at any time.
1568     State should \e never be stored in a delegate.
1569
1570     ListView attaches a number of properties to the root item of the delegate, for example
1571     \c {ListView.isCurrentItem}.  In the following example, the root delegate item can access
1572     this attached property directly as \c ListView.isCurrentItem, while the child
1573     \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1574
1575     \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1576
1577     \note Views do not enable \e clip automatically.  If the view
1578     is not clipped by another item or the screen, it will be necessary
1579     to set \e {clip: true} in order to have the out of view items clipped
1580     nicely.
1581
1582     \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
1583 */
1584 QQuickListView::QQuickListView(QQuickItem *parent)
1585     : QQuickItemView(*(new QQuickListViewPrivate), parent)
1586 {
1587 }
1588
1589 QQuickListView::~QQuickListView()
1590 {
1591 }
1592
1593 /*!
1594     \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1595     This attached property is true if this delegate is the current item; otherwise false.
1596
1597     It is attached to each instance of the delegate.
1598
1599     This property may be used to adjust the appearance of the current item, for example:
1600
1601     \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1602 */
1603
1604 /*!
1605     \qmlattachedproperty ListView QtQuick2::ListView::view
1606     This attached property holds the view that manages this delegate instance.
1607
1608     It is attached to each instance of the delegate.
1609 */
1610
1611 /*!
1612     \qmlattachedproperty string QtQuick2::ListView::previousSection
1613     This attached property holds the section of the previous element.
1614
1615     It is attached to each instance of the delegate.
1616
1617     The section is evaluated using the \l {ListView::section.property}{section} properties.
1618 */
1619
1620 /*!
1621     \qmlattachedproperty string QtQuick2::ListView::nextSection
1622     This attached property holds the section of the next element.
1623
1624     It is attached to each instance of the delegate.
1625
1626     The section is evaluated using the \l {ListView::section.property}{section} properties.
1627 */
1628
1629 /*!
1630     \qmlattachedproperty string QtQuick2::ListView::section
1631     This attached property holds the section of this element.
1632
1633     It is attached to each instance of the delegate.
1634
1635     The section is evaluated using the \l {ListView::section.property}{section} properties.
1636 */
1637
1638 /*!
1639     \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1640     This attached property holds whether the delegate may be destroyed.
1641
1642     It is attached to each instance of the delegate.
1643
1644     It is sometimes necessary to delay the destruction of an item
1645     until an animation completes.
1646
1647     The example delegate below ensures that the animation completes before
1648     the item is removed from the list.
1649
1650     \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove
1651 */
1652
1653 /*!
1654     \qmlattachedsignal QtQuick2::ListView::onAdd()
1655     This attached handler is called immediately after an item is added to the view.
1656 */
1657
1658 /*!
1659     \qmlattachedsignal QtQuick2::ListView::onRemove()
1660     This attached handler is called immediately before an item is removed from the view.
1661 */
1662
1663 /*!
1664     \qmlproperty model QtQuick2::ListView::model
1665     This property holds the model providing data for the list.
1666
1667     The model provides the set of data that is used to create the items
1668     in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1669     or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1670     used, it must be a subclass of \l QAbstractItemModel or a simple list.
1671
1672     \sa {qmlmodels}{Data Models}
1673 */
1674
1675 /*!
1676     \qmlproperty Component QtQuick2::ListView::delegate
1677
1678     The delegate provides a template defining each item instantiated by the view.
1679     The index is exposed as an accessible \c index property.  Properties of the
1680     model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1681
1682     The number of elements in the delegate has a direct effect on the
1683     flicking performance of the view.  If at all possible, place functionality
1684     that is not needed for the normal display of the delegate in a \l Loader which
1685     can load additional elements when needed.
1686
1687     The ListView will lay out the items based on the size of the root item
1688     in the delegate.
1689
1690     It is recommended that the delegate's size be a whole number to avoid sub-pixel
1691     alignment of items.
1692
1693     \note Delegates are instantiated as needed and may be destroyed at any time.
1694     State should \e never be stored in a delegate.
1695 */
1696 /*!
1697     \qmlproperty int QtQuick2::ListView::currentIndex
1698     \qmlproperty Item QtQuick2::ListView::currentItem
1699
1700     The \c currentIndex property holds the index of the current item, and
1701     \c currentItem holds the current item.   Setting the currentIndex to -1
1702     will clear the highlight and set currentItem to null.
1703
1704     If highlightFollowsCurrentItem is \c true, setting either of these
1705     properties will smoothly scroll the ListView so that the current
1706     item becomes visible.
1707
1708     Note that the position of the current item
1709     may only be approximate until it becomes visible in the view.
1710 */
1711
1712 /*!
1713   \qmlproperty Item QtQuick2::ListView::highlightItem
1714
1715     This holds the highlight item created from the \l highlight component.
1716
1717   The \c highlightItem is managed by the view unless
1718   \l highlightFollowsCurrentItem is set to false.
1719
1720   \sa highlight, highlightFollowsCurrentItem
1721 */
1722
1723 /*!
1724   \qmlproperty int QtQuick2::ListView::count
1725   This property holds the number of items in the view.
1726 */
1727
1728 /*!
1729     \qmlproperty Component QtQuick2::ListView::highlight
1730     This property holds the component to use as the highlight.
1731
1732     An instance of the highlight component is created for each list.
1733     The geometry of the resulting component instance is managed by the list
1734     so as to stay with the current item, unless the highlightFollowsCurrentItem
1735     property is false.
1736
1737     \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1738 */
1739
1740 /*!
1741     \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1742     This property holds whether the highlight is managed by the view.
1743
1744     If this property is true (the default value), the highlight is moved smoothly
1745     to follow the current item.  Otherwise, the
1746     highlight is not moved by the view, and any movement must be implemented
1747     by the highlight.
1748
1749     Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1750
1751     \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1752
1753     Note that the highlight animation also affects the way that the view
1754     is scrolled.  This is because the view moves to maintain the
1755     highlight within the preferred highlight range (or visible viewport).
1756
1757     \sa highlight, highlightMoveSpeed
1758 */
1759 //###Possibly rename these properties, since they are very useful even without a highlight?
1760 /*!
1761     \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1762     \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1763     \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1764
1765     These properties define the preferred range of the highlight (for the current item)
1766     within the view. The \c preferredHighlightBegin value must be less than the
1767     \c preferredHighlightEnd value.
1768
1769     These properties affect the position of the current item when the list is scrolled.
1770     For example, if the currently selected item should stay in the middle of the
1771     list when the view is scrolled, set the \c preferredHighlightBegin and
1772     \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1773     item would be. If the \c currentItem is changed programmatically, the list will
1774     automatically scroll so that the current item is in the middle of the view.
1775     Furthermore, the behavior of the current item index will occur whether or not a
1776     highlight exists.
1777
1778     Valid values for \c highlightRangeMode are:
1779
1780     \list
1781     \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1782        However, the highlight can move outside of the range at the ends of the list or due
1783        to mouse interaction.
1784     \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1785        The current item changes if a keyboard or mouse action would cause the highlight to move
1786        outside of the range.
1787     \o ListView.NoHighlightRange - this is the default value.
1788     \endlist
1789 */
1790 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1791 {
1792     Q_D(QQuickListView);
1793     if (d->autoHighlight != autoHighlight) {
1794         if (!autoHighlight) {
1795             if (d->highlightPosAnimator)
1796                 d->highlightPosAnimator->stop();
1797             if (d->highlightSizeAnimator)
1798                 d->highlightSizeAnimator->stop();
1799         }
1800         QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1801     }
1802 }
1803
1804 /*!
1805     \qmlproperty real QtQuick2::ListView::spacing
1806
1807     This property holds the spacing between items.
1808
1809     The default value is 0.
1810 */
1811 qreal QQuickListView::spacing() const
1812 {
1813     Q_D(const QQuickListView);
1814     return d->spacing;
1815 }
1816
1817 void QQuickListView::setSpacing(qreal spacing)
1818 {
1819     Q_D(QQuickListView);
1820     if (spacing != d->spacing) {
1821         d->spacing = spacing;
1822         d->forceLayout = true;
1823         d->layout();
1824         emit spacingChanged();
1825     }
1826 }
1827
1828 /*!
1829     \qmlproperty enumeration QtQuick2::ListView::orientation
1830     This property holds the orientation of the list.
1831
1832     Possible values:
1833
1834     \list
1835     \o ListView.Horizontal - Items are laid out horizontally
1836     \o ListView.Vertical (default) - Items are laid out vertically
1837     \endlist
1838
1839     \table
1840     \row
1841     \o Horizontal orientation:
1842     \image ListViewHorizontal.png
1843
1844     \row
1845     \o Vertical orientation:
1846     \image listview-highlight.png
1847     \endtable
1848 */
1849 QQuickListView::Orientation QQuickListView::orientation() const
1850 {
1851     Q_D(const QQuickListView);
1852     return d->orient;
1853 }
1854
1855 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
1856 {
1857     Q_D(QQuickListView);
1858     if (d->orient != orientation) {
1859         d->orient = orientation;
1860         if (d->orient == Vertical) {
1861             setContentWidth(-1);
1862             setFlickableDirection(VerticalFlick);
1863             setContentX(0);
1864         } else {
1865             setContentHeight(-1);
1866             setFlickableDirection(HorizontalFlick);
1867             setContentY(0);
1868         }
1869         d->regenerate();
1870         emit orientationChanged();
1871     }
1872 }
1873
1874 /*!
1875   \qmlproperty enumeration QtQuick2::ListView::layoutDirection
1876   This property holds the layout direction of the horizontal list.
1877
1878   Possible values:
1879
1880   \list
1881   \o Qt.LeftToRight (default) - Items will be laid out from left to right.
1882   \o Qt.RightToLeft - Items will be laid out from right to let.
1883   \endlist
1884
1885   \sa ListView::effectiveLayoutDirection
1886 */
1887
1888
1889 /*!
1890     \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
1891     This property holds the effective layout direction of the horizontal list.
1892
1893     When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1894     the visual layout direction of the horizontal list will be mirrored. However, the
1895     property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
1896
1897     \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1898 */
1899
1900 /*!
1901     \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
1902     This property holds whether the list wraps key navigation.
1903
1904     If this is true, key navigation that would move the current item selection
1905     past the end of the list instead wraps around and moves the selection to
1906     the start of the list, and vice-versa.
1907
1908     By default, key navigation is not wrapped.
1909 */
1910
1911
1912 /*!
1913     \qmlproperty int QtQuick2::ListView::cacheBuffer
1914     This property determines whether delegates are retained outside the
1915     visible area of the view.
1916
1917     If this value is non-zero, the view may keep as many delegates
1918     instantiated as it can fit within the buffer specified.  For example,
1919     if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
1920     set to 40, then up to 2 delegates above and 2 delegates below the visible
1921     area may be created/retained.  The buffered delegates are created asynchronously,
1922     allowing creation to occur across multiple frames and reducing the
1923     likelihood of skipping frames.  In order to improve painting performance
1924     delegates outside the visible area have their \l visible property set to
1925     false until they are moved into the visible area.
1926
1927     Note that cacheBuffer is not a pixel buffer - it only maintains additional
1928     instantiated delegates.
1929
1930     Setting this value can improve the smoothness of scrolling behavior at the expense
1931     of additional memory usage.  It is not a substitute for creating efficient
1932     delegates; the fewer elements in a delegate, the faster a view can be
1933     scrolled.
1934 */
1935
1936
1937 /*!
1938     \qmlproperty string QtQuick2::ListView::section.property
1939     \qmlproperty enumeration QtQuick2::ListView::section.criteria
1940     \qmlproperty Component QtQuick2::ListView::section.delegate
1941     \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning
1942
1943     These properties determine the expression to be evaluated and appearance
1944     of the section labels.
1945
1946     \c section.property holds the name of the property that is the basis
1947     of each section.
1948
1949     \c section.criteria holds the criteria for forming each section based on
1950     \c section.property. This value can be one of:
1951
1952     \list
1953     \o ViewSection.FullString (default) - sections are created based on the
1954     \c section.property value.
1955     \o ViewSection.FirstCharacter - sections are created based on the first
1956     character of the \c section.property value (for example, 'A', 'B', 'C'
1957     sections, etc. for an address book)
1958     \endlist
1959
1960     \c section.delegate holds the delegate component for each section.
1961
1962     \c section.labelPositioning determines whether the current and/or
1963     next section labels stick to the start/end of the view, and whether
1964     the labels are shown inline.  This value can be a combination of:
1965
1966     \list
1967     \o ViewSection.InlineLabels - section labels are shown inline between
1968     the item delegates separating sections (default).
1969     \o ViewSection.CurrentLabelAtStart - the current section label sticks to the
1970     start of the view as it is moved.
1971     \o ViewSection.NextLabelAtEnd - the next section label (beyond all visible
1972     sections) sticks to the end of the view as it is moved. \note Enabling
1973     \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
1974     section, which has performance implications, especially for slower models.
1975     \endlist
1976
1977     Each item in the list has attached properties named \c ListView.section,
1978     \c ListView.previousSection and \c ListView.nextSection.
1979
1980     For example, here is a ListView that displays a list of animals, separated
1981     into sections. Each item in the ListView is placed in a different section
1982     depending on the "size" property of the model item. The \c sectionHeading
1983     delegate component provides the light blue bar that marks the beginning of
1984     each section.
1985
1986
1987     \snippet examples/declarative/modelviews/listview/sections.qml 0
1988
1989     \image qml-listview-sections-example.png
1990
1991     \note Adding sections to a ListView does not automatically re-order the
1992     list items by the section criteria.
1993     If the model is not ordered by section, then it is possible that
1994     the sections created will not be unique; each boundary between
1995     differing sections will result in a section header being created
1996     even if that section exists elsewhere.
1997
1998     \sa {declarative/modelviews/listview}{ListView examples}
1999 */
2000 QQuickViewSection *QQuickListView::sectionCriteria()
2001 {
2002     Q_D(QQuickListView);
2003     if (!d->sectionCriteria) {
2004         d->sectionCriteria = new QQuickViewSection(this);
2005         connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2006     }
2007     return d->sectionCriteria;
2008 }
2009
2010 /*!
2011     \qmlproperty string QtQuick2::ListView::currentSection
2012     This property holds the section that is currently at the beginning of the view.
2013 */
2014 QString QQuickListView::currentSection() const
2015 {
2016     Q_D(const QQuickListView);
2017     return d->currentSection;
2018 }
2019
2020 /*!
2021     \qmlproperty real QtQuick2::ListView::highlightMoveSpeed
2022     \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2023     \qmlproperty real QtQuick2::ListView::highlightResizeSpeed
2024     \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2025
2026     These properties hold the move and resize animation speed of the highlight delegate.
2027
2028     \l highlightFollowsCurrentItem must be true for these properties
2029     to have effect.
2030
2031     The default value for the speed properties is 400 pixels/second.
2032     The default value for the duration properties is -1, i.e. the
2033     highlight will take as much time as necessary to move at the set speed.
2034
2035     These properties have the same characteristics as a SmoothedAnimation.
2036
2037     \sa highlightFollowsCurrentItem
2038 */
2039 qreal QQuickListView::highlightMoveSpeed() const
2040 {
2041     Q_D(const QQuickListView);
2042     return d->highlightMoveSpeed;
2043 }
2044
2045 void QQuickListView::setHighlightMoveSpeed(qreal speed)
2046 {
2047     Q_D(QQuickListView);
2048     if (d->highlightMoveSpeed != speed) {
2049         d->highlightMoveSpeed = speed;
2050         if (d->highlightPosAnimator)
2051             d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2052         emit highlightMoveSpeedChanged();
2053     }
2054 }
2055
2056 void QQuickListView::setHighlightMoveDuration(int duration)
2057 {
2058     Q_D(QQuickListView);
2059     if (d->highlightMoveDuration != duration) {
2060         if (d->highlightPosAnimator)
2061             d->highlightPosAnimator->userDuration = duration;
2062         QQuickItemView::setHighlightMoveDuration(duration);
2063     }
2064 }
2065
2066 qreal QQuickListView::highlightResizeSpeed() const
2067 {
2068     Q_D(const QQuickListView);
2069     return d->highlightResizeSpeed;
2070 }
2071
2072 void QQuickListView::setHighlightResizeSpeed(qreal speed)
2073 {
2074     Q_D(QQuickListView);
2075     if (d->highlightResizeSpeed != speed) {
2076         d->highlightResizeSpeed = speed;
2077         if (d->highlightSizeAnimator)
2078             d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2079         emit highlightResizeSpeedChanged();
2080     }
2081 }
2082
2083 int QQuickListView::highlightResizeDuration() const
2084 {
2085     Q_D(const QQuickListView);
2086     return d->highlightResizeDuration;
2087 }
2088
2089 void QQuickListView::setHighlightResizeDuration(int duration)
2090 {
2091     Q_D(QQuickListView);
2092     if (d->highlightResizeDuration != duration) {
2093         d->highlightResizeDuration = duration;
2094         if (d->highlightSizeAnimator)
2095             d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2096         emit highlightResizeDurationChanged();
2097     }
2098 }
2099
2100 /*!
2101     \qmlproperty enumeration QtQuick2::ListView::snapMode
2102
2103     This property determines how the view scrolling will settle following a drag or flick.
2104     The possible values are:
2105
2106     \list
2107     \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2108     \o ListView.SnapToItem - the view settles with an item aligned with the start of
2109     the view.
2110     \o ListView.SnapOneItem - the view settles no more than one item away from the first
2111     visible item at the time the mouse button is released.  This mode is particularly
2112     useful for moving one page at a time.
2113     \endlist
2114
2115     \c snapMode does not affect the \l currentIndex.  To update the
2116     \l currentIndex as the list is moved, set \l highlightRangeMode
2117     to \c ListView.StrictlyEnforceRange.
2118
2119     \sa highlightRangeMode
2120 */
2121 QQuickListView::SnapMode QQuickListView::snapMode() const
2122 {
2123     Q_D(const QQuickListView);
2124     return d->snapMode;
2125 }
2126
2127 void QQuickListView::setSnapMode(SnapMode mode)
2128 {
2129     Q_D(QQuickListView);
2130     if (d->snapMode != mode) {
2131         d->snapMode = mode;
2132         emit snapModeChanged();
2133     }
2134 }
2135
2136
2137 /*!
2138     \qmlproperty Component QtQuick2::ListView::footer
2139     This property holds the component to use as the footer.
2140
2141     An instance of the footer component is created for each view.  The
2142     footer is positioned at the end of the view, after any items.
2143
2144     \sa header
2145 */
2146
2147
2148 /*!
2149     \qmlproperty Component QtQuick2::ListView::header
2150     This property holds the component to use as the header.
2151
2152     An instance of the header component is created for each view.  The
2153     header is positioned at the beginning of the view, before any items.
2154
2155     \sa footer
2156 */
2157
2158 void QQuickListView::viewportMoved()
2159 {
2160     Q_D(QQuickListView);
2161     QQuickItemView::viewportMoved();
2162     if (!d->itemCount)
2163         return;
2164     // Recursion can occur due to refill changing the content size.
2165     if (d->inViewportMoved)
2166         return;
2167     d->inViewportMoved = true;
2168
2169     // Set visibility of items to eliminate cost of items outside the visible area.
2170     qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2171     qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2172     for (int i = 0; i < d->visibleItems.count(); ++i) {
2173         FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2174         item->item->setVisible(item->endPosition() >= from && item->position() <= to);
2175     }
2176
2177     if (yflick())
2178         d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2179     else if (d->isRightToLeft())
2180         d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2181     else
2182         d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2183
2184     d->refill();
2185     if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2186         d->moveReason = QQuickListViewPrivate::Mouse;
2187     if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2188         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2189             // reposition highlight
2190             qreal pos = d->highlight->position();
2191             qreal viewPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
2192             if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2193                 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2194             if (pos < viewPos + d->highlightRangeStart)
2195                 pos = viewPos + d->highlightRangeStart;
2196             if (pos != d->highlight->position()) {
2197                 d->highlightPosAnimator->stop();
2198                 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2199             } else {
2200                 d->updateHighlight();
2201             }
2202
2203             // update current index
2204             if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2205                 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2206                     d->updateCurrent(snapItem->index);
2207             }
2208         }
2209     }
2210
2211     if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2212         d->inFlickCorrection = true;
2213         // Near an end and it seems that the extent has changed?
2214         // Recalculate the flick so that we don't end up in an odd position.
2215         if (yflick() && !d->vData.inOvershoot) {
2216             if (d->vData.velocity > 0) {
2217                 const qreal minY = minYExtent();
2218                 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2219                     && minY != d->vData.flickTarget)
2220                     d->flickY(-d->vData.smoothVelocity.value());
2221             } else if (d->vData.velocity < 0) {
2222                 const qreal maxY = maxYExtent();
2223                 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2224                     && maxY != d->vData.flickTarget)
2225                     d->flickY(-d->vData.smoothVelocity.value());
2226             }
2227         }
2228
2229         if (xflick() && !d->hData.inOvershoot) {
2230             if (d->hData.velocity > 0) {
2231                 const qreal minX = minXExtent();
2232                 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2233                     && minX != d->hData.flickTarget)
2234                     d->flickX(-d->hData.smoothVelocity.value());
2235             } else if (d->hData.velocity < 0) {
2236                 const qreal maxX = maxXExtent();
2237                 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2238                     && maxX != d->hData.flickTarget)
2239                     d->flickX(-d->hData.smoothVelocity.value());
2240             }
2241         }
2242         d->inFlickCorrection = false;
2243     }
2244     if (d->sectionCriteria) {
2245         d->updateCurrentSection();
2246         d->updateStickySections();
2247     }
2248     d->inViewportMoved = false;
2249 }
2250
2251 void QQuickListView::keyPressEvent(QKeyEvent *event)
2252 {
2253     Q_D(QQuickListView);
2254     if (d->model && d->model->count() && d->interactive) {
2255         if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2256                     || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2257                     || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Up)) {
2258             if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2259                 decrementCurrentIndex();
2260                 event->accept();
2261                 return;
2262             } else if (d->wrap) {
2263                 event->accept();
2264                 return;
2265             }
2266         } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2267                     || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2268                     || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Down)) {
2269             if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2270                 incrementCurrentIndex();
2271                 event->accept();
2272                 return;
2273             } else if (d->wrap) {
2274                 event->accept();
2275                 return;
2276             }
2277         }
2278     }
2279     event->ignore();
2280     QQuickItemView::keyPressEvent(event);
2281 }
2282
2283 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2284 {
2285     Q_D(QQuickListView);
2286     if (d->isRightToLeft() && d->orient == QQuickListView::Horizontal) {
2287         // maintain position relative to the right edge
2288         int dx = newGeometry.width() - oldGeometry.width();
2289         setContentX(contentX() - dx);
2290     }
2291     QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2292 }
2293
2294
2295 /*!
2296     \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2297
2298     Increments the current index.  The current index will wrap
2299     if keyNavigationWraps is true and it is currently at the end.
2300     This method has no effect if the \l count is zero.
2301
2302     \bold Note: methods should only be called after the Component has completed.
2303 */
2304 void QQuickListView::incrementCurrentIndex()
2305 {
2306     Q_D(QQuickListView);
2307     int count = d->model ? d->model->count() : 0;
2308     if (count && (currentIndex() < count - 1 || d->wrap)) {
2309         d->moveReason = QQuickListViewPrivate::SetIndex;
2310         int index = currentIndex()+1;
2311         setCurrentIndex((index >= 0 && index < count) ? index : 0);
2312     }
2313 }
2314
2315 /*!
2316     \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2317
2318     Decrements the current index.  The current index will wrap
2319     if keyNavigationWraps is true and it is currently at the beginning.
2320     This method has no effect if the \l count is zero.
2321
2322     \bold Note: methods should only be called after the Component has completed.
2323 */
2324 void QQuickListView::decrementCurrentIndex()
2325 {
2326     Q_D(QQuickListView);
2327     int count = d->model ? d->model->count() : 0;
2328     if (count && (currentIndex() > 0 || d->wrap)) {
2329         d->moveReason = QQuickListViewPrivate::SetIndex;
2330         int index = currentIndex()-1;
2331         setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2332     }
2333 }
2334
2335 void QQuickListView::updateSections()
2336 {
2337     Q_D(QQuickListView);
2338     if (isComponentComplete() && d->model) {
2339         QList<QByteArray> roles;
2340         if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2341             roles << d->sectionCriteria->property().toUtf8();
2342         d->model->setWatchedRoles(roles);
2343         d->updateSections();
2344         if (d->itemCount) {
2345             d->forceLayout = true;
2346             d->layout();
2347         }
2348     }
2349 }
2350
2351 bool QQuickListViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, FxViewItem *firstVisible, InsertionsResult *insertResult)
2352 {
2353     int modelIndex = change.index;
2354     int count = change.count;
2355
2356
2357     qreal tempPos = isRightToLeft() ? -position()-size() : position();
2358     int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2359
2360     if (index < 0) {
2361         int i = visibleItems.count() - 1;
2362         while (i > 0 && visibleItems.at(i)->index == -1)
2363             --i;
2364         if (i == 0 && visibleItems.first()->index == -1) {
2365             // there are no visible items except items marked for removal
2366             index = visibleItems.count();
2367         } else if (visibleItems.at(i)->index + 1 == modelIndex
2368             && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2369             // Special case of appending an item to the model.
2370             index = visibleItems.count();
2371         } else {
2372             if (modelIndex < visibleIndex) {
2373                 // Insert before visible items
2374                 visibleIndex += count;
2375                 for (int i = 0; i < visibleItems.count(); ++i) {
2376                     FxViewItem *item = visibleItems.at(i);
2377                     if (item->index != -1 && item->index >= modelIndex)
2378                         item->index += count;
2379                 }
2380             }
2381             return true;
2382         }
2383     }
2384
2385     // index can be the next item past the end of the visible items list (i.e. appended)
2386     int pos = 0;
2387     if (visibleItems.count()) {
2388         pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2389                                                 : visibleItems.last()->endPosition()+spacing;
2390     }
2391
2392     int prevAddedCount = insertResult->addedItems.count();
2393     if (firstVisible && pos < firstVisible->position()) {
2394         // Insert items before the visible item.
2395         int insertionIdx = index;
2396         int i = 0;
2397         int from = tempPos - buffer;
2398
2399         for (i = count-1; i >= 0; --i) {
2400             if (pos > from && insertionIdx < visibleIndex) {
2401                 // item won't be visible, just note the size for repositioning
2402                 insertResult->sizeAddedBeforeVisible += averageSize + spacing;
2403                 pos -= averageSize + spacing;
2404             } else {
2405                 // item is before first visible e.g. in cache buffer
2406                 FxViewItem *item = 0;
2407                 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) {
2408                     if (item->index > modelIndex + i)
2409                         insertResult->movedBackwards.append(item);
2410                     item->index = modelIndex + i;
2411                 }
2412                 if (!item)
2413                     item = createItem(modelIndex + i);
2414                 if (!item)
2415                     return false;
2416
2417                 visibleItems.insert(insertionIdx, item);
2418                 if (!change.isMove()) {
2419                     insertResult->addedItems.append(item);
2420                     insertResult->sizeAddedBeforeVisible += item->size();
2421                 }
2422                 pos -= item->size() + spacing;
2423             }
2424             index++;
2425         }
2426     } else {
2427         int i = 0;
2428         int to = buffer+tempPos+size();
2429         for (i = 0; i < count && pos <= to; ++i) {
2430             FxViewItem *item = 0;
2431             if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) {
2432                 if (item->index > modelIndex + i)
2433                     insertResult->movedBackwards.append(item);
2434                 item->index = modelIndex + i;
2435             }
2436             if (!item)
2437                 item = createItem(modelIndex + i);
2438             if (!item)
2439                 return false;
2440
2441             visibleItems.insert(index, item);
2442             if (!change.isMove())
2443                 insertResult->addedItems.append(item);
2444             pos += item->size() + spacing;
2445             ++index;
2446         }
2447     }
2448
2449     for (; index < visibleItems.count(); ++index) {
2450         FxViewItem *item = visibleItems.at(index);
2451         if (item->index != -1)
2452             item->index += count;
2453     }
2454
2455     updateVisibleIndex();
2456
2457     return insertResult->addedItems.count() > prevAddedCount;
2458 }
2459
2460
2461 /*!
2462     \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
2463
2464     Positions the view such that the \a index is at the position specified by
2465     \a mode:
2466
2467     \list
2468     \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2469     \o ListView.Center - position item in the center of the view.
2470     \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2471     \o ListView.Visible - if any part of the item is visible then take no action, otherwise
2472     bring the item into view.
2473     \o ListView.Contain - ensure the entire item is visible.  If the item is larger than
2474     the view the item is positioned at the top (or left for horizontal orientation) of the view.
2475     \endlist
2476
2477     If positioning the view at \a index would cause empty space to be displayed at
2478     the beginning or end of the view, the view will be positioned at the boundary.
2479
2480     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2481     at a particular index.  This is unreliable since removing items from the start
2482     of the list does not cause all other items to be repositioned, and because
2483     the actual start of the view can vary based on the size of the delegates.
2484     The correct way to bring an item into view is with \c positionViewAtIndex.
2485
2486     \bold Note: methods should only be called after the Component has completed.  To position
2487     the view at startup, this method should be called by Component.onCompleted.  For
2488     example, to position the view at the end:
2489
2490     \code
2491     Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
2492     \endcode
2493 */
2494
2495 /*!
2496     \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
2497     \qmlmethod QtQuick2::ListView::positionViewAtEnd()
2498
2499     Positions the view at the beginning or end, taking into account any header or footer.
2500
2501     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2502     at a particular index.  This is unreliable since removing items from the start
2503     of the list does not cause all other items to be repositioned, and because
2504     the actual start of the view can vary based on the size of the delegates.
2505
2506     \bold Note: methods should only be called after the Component has completed.  To position
2507     the view at startup, this method should be called by Component.onCompleted.  For
2508     example, to position the view at the end on startup:
2509
2510     \code
2511     Component.onCompleted: positionViewAtEnd()
2512     \endcode
2513 */
2514
2515 /*!
2516     \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
2517
2518     Returns the index of the visible item containing the point \a x, \a y in content
2519     coordinates.  If there is no item at the point specified, or the item is
2520     not visible -1 is returned.
2521
2522     If the item is outside the visible area, -1 is returned, regardless of
2523     whether an item will exist at that point when scrolled into view.
2524
2525     \bold Note: methods should only be called after the Component has completed.
2526 */
2527
2528 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
2529 {
2530     return new QQuickListViewAttached(obj);
2531 }
2532
2533 QT_END_NAMESPACE