1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "private/qdeclarativelistview_p.h"
43 
44 #include "private/qdeclarativeflickable_p_p.h"
45 #include "private/qdeclarativevisualitemmodel_p.h"
46 
47 #include "private/qdeclarativesmoothedanimation_p_p.h"
48 #include <qdeclarativeexpression.h>
49 #include <qdeclarativeengine.h>
50 #include <qdeclarativeguard_p.h>
51 #include <qdeclarativeinfo.h>
52 
53 #include <qlistmodelinterface_p.h>
54 #include <qmath.h>
55 #include <QKeyEvent>
56 #include "qplatformdefs.h"
57 
58 QT_BEGIN_NAMESPACE
59 
60 #ifndef QML_FLICK_SNAPONETHRESHOLD
61 #define QML_FLICK_SNAPONETHRESHOLD 30
62 #endif
63 
setProperty(const QString & property)64 void QDeclarativeViewSection::setProperty(const QString &property)
65 {
66     if (property != m_property) {
67         m_property = property;
68         emit propertyChanged();
69     }
70 }
71 
setCriteria(QDeclarativeViewSection::SectionCriteria criteria)72 void QDeclarativeViewSection::setCriteria(QDeclarativeViewSection::SectionCriteria criteria)
73 {
74     if (criteria != m_criteria) {
75         m_criteria = criteria;
76         emit criteriaChanged();
77     }
78 }
79 
setDelegate(QDeclarativeComponent * delegate)80 void QDeclarativeViewSection::setDelegate(QDeclarativeComponent *delegate)
81 {
82     if (delegate != m_delegate) {
83         m_delegate = delegate;
84         emit delegateChanged();
85     }
86 }
87 
sectionString(const QString & value)88 QString QDeclarativeViewSection::sectionString(const QString &value)
89 {
90     if (m_criteria == FirstCharacter)
91         return value.isEmpty() ? QString() : value.at(0);
92     else
93         return value;
94 }
95 
96 //----------------------------------------------------------------------------
97 
98 class FxListItem
99 {
100 public:
FxListItem(QDeclarativeItem * i,QDeclarativeListView * v)101     FxListItem(QDeclarativeItem *i, QDeclarativeListView *v) : item(i), section(0), view(v) {
102         attached = static_cast<QDeclarativeListViewAttached*>(qmlAttachedPropertiesObject<QDeclarativeListView>(item));
103         if (attached)
104             attached->setView(view);
105     }
~FxListItem()106     ~FxListItem() {}
position() const107     qreal position() const {
108         if (section) {
109             if (view->orientation() == QDeclarativeListView::Vertical)
110                 return section->y();
111             else
112                 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
113         } else {
114             return itemPosition();
115         }
116     }
117 
itemPosition() const118     qreal itemPosition() const {
119         if (view->orientation() == QDeclarativeListView::Vertical)
120             return item->y();
121         else
122             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
123     }
size() const124     qreal size() const {
125         if (section)
126             return (view->orientation() == QDeclarativeListView::Vertical ? item->height()+section->height() : item->width()+section->width());
127         else
128             return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width());
129     }
itemSize() const130     qreal itemSize() const {
131         return (view->orientation() == QDeclarativeListView::Vertical ? item->height() : item->width());
132     }
sectionSize() const133     qreal sectionSize() const {
134         if (section)
135             return (view->orientation() == QDeclarativeListView::Vertical ? section->height() : section->width());
136         return 0.0;
137     }
endPosition() const138     qreal endPosition() const {
139         if (view->orientation() == QDeclarativeListView::Vertical) {
140             return item->y() + (item->height() >= 1.0 ? item->height() : 1) - 1;
141         } else {
142             return (view->effectiveLayoutDirection() == Qt::RightToLeft
143                     ? -item->width()-item->x() + (item->width() >= 1.0 ? item->width() : 1)
144                     : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1;
145         }
146     }
setPosition(qreal pos)147     void setPosition(qreal pos) {
148         if (view->orientation() == QDeclarativeListView::Vertical) {
149             if (section) {
150                 section->setY(pos);
151                 pos += section->height();
152             }
153             item->setY(pos);
154         } else {
155             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
156                 if (section) {
157                     section->setX(-section->width()-pos);
158                     pos += section->width();
159                 }
160                 item->setX(-item->width()-pos);
161             } else {
162                 if (section) {
163                     section->setX(pos);
164                     pos += section->width();
165                 }
166                 item->setX(pos);
167             }
168         }
169     }
setSize(qreal size)170     void setSize(qreal size) {
171         if (view->orientation() == QDeclarativeListView::Vertical)
172             item->setHeight(size);
173         else
174             item->setWidth(size);
175     }
contains(qreal x,qreal y) const176     bool contains(qreal x, qreal y) const {
177         return (x >= item->x() && x < item->x() + item->width() &&
178                 y >= item->y() && y < item->y() + item->height());
179     }
180 
181     QDeclarativeItem *item;
182     QDeclarativeItem *section;
183     QDeclarativeListView *view;
184     QDeclarativeListViewAttached *attached;
185     int index;
186 };
187 
188 //----------------------------------------------------------------------------
189 
190 class QDeclarativeListViewPrivate : public QDeclarativeFlickablePrivate
191 {
192     Q_DECLARE_PUBLIC(QDeclarativeListView)
193 
194 public:
QDeclarativeListViewPrivate()195     QDeclarativeListViewPrivate()
196         : currentItem(0), orient(QDeclarativeListView::Vertical), layoutDirection(Qt::LeftToRight)
197         , visiblePos(0), visibleIndex(0)
198         , averageSize(100.0), currentIndex(-1), requestedIndex(-1)
199         , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0)
200         , highlightRangeStartValid(false), highlightRangeEndValid(false)
201         , highlightComponent(0), highlight(0), trackedItem(0)
202         , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0)
203         , sectionCriteria(0), spacing(0.0)
204         , highlightMoveSpeed(400), highlightMoveDuration(-1)
205         , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QDeclarativeListView::NoHighlightRange)
206         , snapMode(QDeclarativeListView::NoSnap), overshootDist(0.0)
207         , footerComponent(0), footer(0), headerComponent(0), header(0)
208         , bufferMode(BufferBefore | BufferAfter)
209         , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false)
210         , correctFlick(false), inFlickCorrection(false), lazyRelease(false)
211         , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false)
212         , inViewportMoved(false)
213         , minExtentDirty(true), maxExtentDirty(true)
214     {}
215 
216     void init();
217     void clear();
218     FxListItem *createItem(int modelIndex);
219     void releaseItem(FxListItem *item);
220     QDeclarativeItem *createComponentItem(QDeclarativeComponent *component);
221 
visibleItem(int modelIndex) const222     FxListItem *visibleItem(int modelIndex) const {
223         if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
224             for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
225                 FxListItem *item = visibleItems.at(i);
226                 if (item->index == modelIndex)
227                     return item;
228             }
229         }
230         return 0;
231     }
232 
firstVisibleItem() const233     FxListItem *firstVisibleItem() const {
234         const qreal pos = isRightToLeft() ? -position()-size() : position();
235         for (int i = 0; i < visibleItems.count(); ++i) {
236             FxListItem *item = visibleItems.at(i);
237             if (item->index != -1 && item->endPosition() > pos)
238                 return item;
239         }
240         return visibleItems.count() ? visibleItems.first() : 0;
241     }
242 
243     // Returns the item before modelIndex, if created.
244     // May return an item marked for removal.
itemBefore(int modelIndex) const245     FxListItem *itemBefore(int modelIndex) const {
246         if (modelIndex < visibleIndex)
247             return 0;
248         int idx = 1;
249         int lastIndex = -1;
250         while (idx < visibleItems.count()) {
251             FxListItem *item = visibleItems.at(idx);
252             if (item->index != -1)
253                 lastIndex = item->index;
254             if (item->index == modelIndex)
255                 return visibleItems.at(idx-1);
256             ++idx;
257         }
258         if (lastIndex == modelIndex-1)
259             return visibleItems.last();
260         return 0;
261     }
262 
regenerate()263     void regenerate() {
264         Q_Q(QDeclarativeListView);
265         if (q->isComponentComplete()) {
266             if (header) {
267                 if (q->scene())
268                     q->scene()->removeItem(header->item);
269                 header->item->deleteLater();
270                 delete header;
271                 header = 0;
272             }
273             if (footer) {
274                 if (q->scene())
275                     q->scene()->removeItem(footer->item);
276                 footer->item->deleteLater();
277                 delete footer;
278                 footer = 0;
279             }
280             updateHeader();
281             updateFooter();
282             clear();
283             setPosition(0);
284             q->refill();
285             updateCurrent(currentIndex);
286         }
287     }
288 
mirrorChange()289     void mirrorChange() {
290         regenerate();
291     }
292 
isRightToLeft() const293     bool isRightToLeft() const {
294         Q_Q(const QDeclarativeListView);
295         return orient == QDeclarativeListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
296     }
297 
position() const298     qreal position() const {
299         Q_Q(const QDeclarativeListView);
300         return orient == QDeclarativeListView::Vertical ? q->contentY() : q->contentX();
301     }
302 
setPosition(qreal pos)303     void setPosition(qreal pos) {
304         Q_Q(QDeclarativeListView);
305         if (orient == QDeclarativeListView::Vertical) {
306             q->QDeclarativeFlickable::setContentY(pos);
307         } else {
308             if (isRightToLeft())
309                 q->QDeclarativeFlickable::setContentX(-pos-size());
310             else
311                 q->QDeclarativeFlickable::setContentX(pos);
312         }
313     }
size() const314     qreal size() const {
315         Q_Q(const QDeclarativeListView);
316         return orient == QDeclarativeListView::Vertical ? q->height() : q->width();
317     }
318 
originPosition() const319     qreal originPosition() const {
320         qreal pos = 0;
321         if (!visibleItems.isEmpty()) {
322             pos = (*visibleItems.constBegin())->position();
323             if (visibleIndex > 0)
324                 pos -= visibleIndex * (averageSize + spacing);
325         }
326         return pos;
327     }
328 
lastPosition() const329     qreal lastPosition() const {
330         qreal pos = 0;
331         if (!visibleItems.isEmpty()) {
332             int invisibleCount = visibleItems.count() - visibleIndex;
333             for (int i = visibleItems.count()-1; i >= 0; --i) {
334                 if (visibleItems.at(i)->index != -1) {
335                     invisibleCount = model->count() - visibleItems.at(i)->index - 1;
336                     break;
337                 }
338             }
339             pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
340         } else if (model && model->count()) {
341             pos = model->count() * averageSize + (model->count()-1) * spacing;
342         }
343         return pos;
344     }
345 
startPosition() const346     qreal startPosition() const {
347         return isRightToLeft() ? -lastPosition()-1 : originPosition();
348     }
349 
endPosition() const350     qreal endPosition() const {
351         return isRightToLeft() ? -originPosition()-1 : lastPosition();
352     }
353 
positionAt(int modelIndex) const354     qreal positionAt(int modelIndex) const {
355         if (FxListItem *item = visibleItem(modelIndex))
356             return item->position();
357         if (!visibleItems.isEmpty()) {
358             if (modelIndex < visibleIndex) {
359                 int count = visibleIndex - modelIndex;
360                 qreal cs = 0;
361                 if (modelIndex == currentIndex && currentItem) {
362                     cs = currentItem->size() + spacing;
363                     --count;
364                 }
365                 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
366             } else {
367                 int idx = visibleItems.count() - 1;
368                 while (idx >= 0 && visibleItems.at(idx)->index == -1)
369                     --idx;
370                 if (idx < 0)
371                     idx = visibleIndex;
372                 else
373                     idx = visibleItems.at(idx)->index;
374                 int count = modelIndex - idx - 1;
375 
376                 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1;
377             }
378         }
379         return 0;
380     }
381 
endPositionAt(int modelIndex) const382     qreal endPositionAt(int modelIndex) const {
383         if (FxListItem *item = visibleItem(modelIndex))
384             return item->endPosition();
385         if (!visibleItems.isEmpty()) {
386             if (modelIndex < visibleIndex) {
387                 int count = visibleIndex - modelIndex;
388                 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1;
389             } else {
390                 int idx = visibleItems.count() - 1;
391                 while (idx >= 0 && visibleItems.at(idx)->index == -1)
392                     --idx;
393                 if (idx < 0)
394                     idx = visibleIndex;
395                 else
396                     idx = visibleItems.at(idx)->index;
397                 int count = modelIndex - idx - 1;
398                 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
399             }
400         }
401         return 0;
402     }
403 
sectionAt(int modelIndex)404     QString sectionAt(int modelIndex) {
405         if (FxListItem *item = visibleItem(modelIndex))
406             return item->attached->section();
407 
408         QString section;
409         if (sectionCriteria) {
410             QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
411             section = sectionCriteria->sectionString(propValue);
412         }
413 
414         return section;
415     }
416 
isValid() const417     bool isValid() const {
418         return model && model->count() && model->isValid();
419     }
420 
snapPosAt(qreal pos)421     qreal snapPosAt(qreal pos) {
422         if (FxListItem *snapItem = snapItemAt(pos))
423             return snapItem->position();
424         if (visibleItems.count()) {
425             qreal firstPos = visibleItems.first()->position();
426             qreal endPos = visibleItems.last()->position();
427             if (pos < firstPos) {
428                 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
429             } else if (pos > endPos)
430                 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
431         }
432         return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
433     }
434 
snapItemAt(qreal pos)435     FxListItem *snapItemAt(qreal pos) {
436         FxListItem *snapItem = 0;
437         for (int i = 0; i < visibleItems.count(); ++i) {
438             FxListItem *item = visibleItems[i];
439             if (item->index == -1)
440                 continue;
441             qreal itemTop = item->position();
442             if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1)
443                 return item;
444             if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos)
445                 snapItem = item;
446         }
447         return snapItem;
448     }
449 
lastVisibleIndex() const450     int lastVisibleIndex() const {
451         int lastIndex = -1;
452         for (int i = visibleItems.count()-1; i >= 0; --i) {
453             FxListItem *listItem = visibleItems.at(i);
454             if (listItem->index != -1) {
455                 lastIndex = listItem->index;
456                 break;
457             }
458         }
459         return lastIndex;
460     }
461 
462     // map a model index to visibleItems index.
mapFromModel(int modelIndex) const463     int mapFromModel(int modelIndex) const {
464         if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
465             return -1;
466         for (int i = 0; i < visibleItems.count(); ++i) {
467             FxListItem *listItem = visibleItems.at(i);
468             if (listItem->index == modelIndex)
469                 return i;
470             if (listItem->index > modelIndex)
471                 return -1;
472         }
473         return -1; // Not in visibleList
474     }
475 
updateViewport()476     void updateViewport() {
477         Q_Q(QDeclarativeListView);
478         if (orient == QDeclarativeListView::Vertical) {
479             q->setContentHeight(endPosition() - startPosition() + 1);
480         } else {
481             q->setContentWidth(endPosition() - startPosition() + 1);
482         }
483     }
484 
itemGeometryChanged(QDeclarativeItem * item,const QRectF & newGeometry,const QRectF & oldGeometry)485     void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
486         Q_Q(QDeclarativeListView);
487         QDeclarativeFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
488         if (!q->isComponentComplete())
489             return;
490         if (item != contentItem && (!highlight || item != highlight->item)) {
491             if ((orient == QDeclarativeListView::Vertical && newGeometry.height() != oldGeometry.height())
492                 || (orient == QDeclarativeListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
493                 scheduleLayout();
494             }
495         }
496         if ((header && header->item == item) || (footer && footer->item == item)) {
497             if (header)
498                 updateHeader();
499             if (footer)
500                 updateFooter();
501         }
502         if (currentItem && currentItem->item == item)
503             updateHighlight();
504         if (trackedItem && trackedItem->item == item)
505             q->trackedPositionChanged();
506     }
507 
508     // for debugging only
checkVisible() const509     void checkVisible() const {
510         int skip = 0;
511         for (int i = 0; i < visibleItems.count(); ++i) {
512             FxListItem *listItem = visibleItems.at(i);
513             if (listItem->index == -1) {
514                 ++skip;
515             } else if (listItem->index != visibleIndex + i - skip) {
516                 qFatal("index %d %d %d", visibleIndex, i, listItem->index);
517             }
518         }
519     }
520 
521     void refill(qreal from, qreal to, bool doBuffer = false);
522     void scheduleLayout();
523     void layout();
524     void updateUnrequestedIndexes();
525     void updateUnrequestedPositions();
526     void updateTrackedItem();
527     void createHighlight();
528     void updateHighlight();
529     void createSection(FxListItem *);
530     void updateSections();
531     void updateCurrentSection();
532     void updateCurrent(int);
533     void updateAverage();
534     void updateHeader();
535     void updateFooter();
536     void fixupPosition();
537     void positionViewAtIndex(int index, int mode);
538     virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
539     virtual void flick(QDeclarativeFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
540                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
541 
542     QDeclarativeGuard<QDeclarativeVisualModel> model;
543     QVariant modelVariant;
544     QList<FxListItem*> visibleItems;
545     QHash<QDeclarativeItem*,int> unrequestedItems;
546     FxListItem *currentItem;
547     QDeclarativeListView::Orientation orient;
548     Qt::LayoutDirection layoutDirection;
549     qreal visiblePos;
550     int visibleIndex;
551     qreal averageSize;
552     int currentIndex;
553     int requestedIndex;
554     int itemCount;
555     qreal highlightRangeStart;
556     qreal highlightRangeEnd;
557     bool highlightRangeStartValid;
558     bool highlightRangeEndValid;
559     QDeclarativeComponent *highlightComponent;
560     FxListItem *highlight;
561     FxListItem *trackedItem;
562     enum MovementReason { Other, SetIndex, Mouse };
563     MovementReason moveReason;
564     int buffer;
565     QSmoothedAnimation *highlightPosAnimator;
566     QSmoothedAnimation *highlightSizeAnimator;
567     QDeclarativeViewSection *sectionCriteria;
568     QString currentSection;
569     static const int sectionCacheSize = 4;
570     QDeclarativeItem *sectionCache[sectionCacheSize];
571     qreal spacing;
572     qreal highlightMoveSpeed;
573     int highlightMoveDuration;
574     qreal highlightResizeSpeed;
575     int highlightResizeDuration;
576     QDeclarativeListView::HighlightRangeMode highlightRange;
577     QDeclarativeListView::SnapMode snapMode;
578     qreal overshootDist;
579     QDeclarativeComponent *footerComponent;
580     FxListItem *footer;
581     QDeclarativeComponent *headerComponent;
582     FxListItem *header;
583     enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
584     int bufferMode;
585     mutable qreal minExtent;
586     mutable qreal maxExtent;
587 
588     bool ownModel : 1;
589     bool wrap : 1;
590     bool autoHighlight : 1;
591     bool haveHighlightRange : 1;
592     bool correctFlick : 1;
593     bool inFlickCorrection : 1;
594     bool lazyRelease : 1;
595     bool deferredRelease : 1;
596     bool layoutScheduled : 1;
597     bool currentIndexCleared : 1;
598     bool inViewportMoved : 1;
599     mutable bool minExtentDirty : 1;
600     mutable bool maxExtentDirty : 1;
601 };
602 
init()603 void QDeclarativeListViewPrivate::init()
604 {
605     Q_Q(QDeclarativeListView);
606     q->setFlag(QGraphicsItem::ItemIsFocusScope);
607     addItemChangeListener(this, Geometry);
608     QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
609     q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick);
610     ::memset(sectionCache, 0, sizeof(QDeclarativeItem*) * sectionCacheSize);
611 }
612 
clear()613 void QDeclarativeListViewPrivate::clear()
614 {
615     timeline.clear();
616     for (int i = 0; i < visibleItems.count(); ++i)
617         releaseItem(visibleItems.at(i));
618     visibleItems.clear();
619     for (int i = 0; i < sectionCacheSize; ++i) {
620         delete sectionCache[i];
621         sectionCache[i] = 0;
622     }
623     visiblePos = header ? header->size() : 0;
624     visibleIndex = 0;
625     releaseItem(currentItem);
626     currentItem = 0;
627     createHighlight();
628     trackedItem = 0;
629     minExtentDirty = true;
630     maxExtentDirty = true;
631     itemCount = 0;
632 }
633 
createItem(int modelIndex)634 FxListItem *QDeclarativeListViewPrivate::createItem(int modelIndex)
635 {
636     Q_Q(QDeclarativeListView);
637     // create object
638     requestedIndex = modelIndex;
639     FxListItem *listItem = 0;
640     if (QDeclarativeItem *item = model->item(modelIndex, false)) {
641         listItem = new FxListItem(item, q);
642         listItem->index = modelIndex;
643         // initialise attached properties
644         if (sectionCriteria) {
645             QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
646             listItem->attached->m_section = sectionCriteria->sectionString(propValue);
647             if (modelIndex > 0) {
648                 if (FxListItem *item = itemBefore(modelIndex))
649                     listItem->attached->m_prevSection = item->attached->section();
650                 else
651                     listItem->attached->m_prevSection = sectionAt(modelIndex-1);
652             }
653             if (modelIndex < model->count()-1) {
654                 if (FxListItem *item = visibleItem(modelIndex+1))
655                     listItem->attached->m_nextSection = item->attached->section();
656                 else
657                     listItem->attached->m_nextSection = sectionAt(modelIndex+1);
658             }
659         }
660         if (model->completePending()) {
661             // complete
662             listItem->item->setZValue(1);
663             listItem->item->setParentItem(q->contentItem());
664             model->completeItem();
665         } else {
666             listItem->item->setParentItem(q->contentItem());
667         }
668         QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
669         itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
670         if (sectionCriteria && sectionCriteria->delegate()) {
671             if (listItem->attached->m_prevSection != listItem->attached->m_section)
672                 createSection(listItem);
673         }
674         unrequestedItems.remove(listItem->item);
675     }
676     requestedIndex = -1;
677 
678     return listItem;
679 }
680 
createComponentItem(QDeclarativeComponent * component)681 QDeclarativeItem *QDeclarativeListViewPrivate::createComponentItem(QDeclarativeComponent *component)
682 {
683     Q_Q(QDeclarativeListView);
684     QDeclarativeItem *item = 0;
685     QDeclarativeContext *creationContext = component->creationContext();
686     QDeclarativeContext *context = new QDeclarativeContext(
687                 creationContext ? creationContext : qmlContext(q));
688     QObject *nobj = component->create(context);
689     if (nobj) {
690         QDeclarative_setParent_noEvent(context, nobj);
691         item = qobject_cast<QDeclarativeItem *>(nobj);
692         if (!item)
693             delete nobj;
694     } else {
695         delete context;
696     }
697 
698     return item;
699 }
700 
releaseItem(FxListItem * item)701 void QDeclarativeListViewPrivate::releaseItem(FxListItem *item)
702 {
703     Q_Q(QDeclarativeListView);
704     if (!item || !model)
705         return;
706     if (trackedItem == item)
707         trackedItem = 0;
708     QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item->item));
709     itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
710     if (model->release(item->item) == 0) {
711         // item was not destroyed, and we no longer reference it.
712         unrequestedItems.insert(item->item, model->indexOf(item->item, q));
713     }
714     if (item->section) {
715         int i = 0;
716         do {
717             if (!sectionCache[i]) {
718                 sectionCache[i] = item->section;
719                 sectionCache[i]->setVisible(false);
720                 item->section = 0;
721                 break;
722             }
723             ++i;
724         } while (i < sectionCacheSize);
725         delete item->section;
726     }
727     delete item;
728 }
729 
refill(qreal from,qreal to,bool doBuffer)730 void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer)
731 {
732     Q_Q(QDeclarativeListView);
733     if (!isValid() || !q->isComponentComplete())
734         return;
735     itemCount = model->count();
736     qreal bufferFrom = from - buffer;
737     qreal bufferTo = to + buffer;
738     qreal fillFrom = from;
739     qreal fillTo = to;
740     if (doBuffer && (bufferMode & BufferAfter))
741         fillTo = bufferTo;
742     if (doBuffer && (bufferMode & BufferBefore))
743         fillFrom = bufferFrom;
744 
745     bool haveValidItems = false;
746     int modelIndex = visibleIndex;
747     qreal itemEnd = visiblePos-1;
748     if (!visibleItems.isEmpty()) {
749         visiblePos = (*visibleItems.constBegin())->position();
750         itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
751         int i = visibleItems.count() - 1;
752         while (i > 0 && visibleItems.at(i)->index == -1)
753             --i;
754         if (visibleItems.at(i)->index != -1) {
755             haveValidItems = true;
756             modelIndex = visibleItems.at(i)->index + 1;
757         }
758     }
759 
760     if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
761         || fillTo < visiblePos - averageSize - spacing)) {
762         // We've jumped more than a page.  Estimate which items are now
763         // visible and fill from there.
764         int count = (fillFrom - itemEnd) / (averageSize + spacing);
765         for (int i = 0; i < visibleItems.count(); ++i)
766             releaseItem(visibleItems.at(i));
767         visibleItems.clear();
768         modelIndex += count;
769         if (modelIndex >= model->count()) {
770             count -= modelIndex - model->count() + 1;
771             modelIndex = model->count() - 1;
772         } else if (modelIndex < 0) {
773             count -= modelIndex;
774             modelIndex = 0;
775         }
776         visibleIndex = modelIndex;
777         visiblePos = itemEnd + count * (averageSize + spacing) + 1;
778         itemEnd = visiblePos-1;
779     }
780 
781     bool changed = false;
782     FxListItem *item = 0;
783     qreal pos = itemEnd + 1;
784     while (modelIndex < model->count() && pos <= fillTo) {
785 //        qDebug() << "refill: append item" << modelIndex << "pos" << pos;
786         if (!(item = createItem(modelIndex)))
787             break;
788         item->setPosition(pos);
789         pos += item->size() + spacing;
790         visibleItems.append(item);
791         ++modelIndex;
792         changed = true;
793         if (doBuffer) // never buffer more than one item per frame
794             break;
795     }
796     while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) {
797 //        qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
798         if (!(item = createItem(visibleIndex-1)))
799             break;
800         --visibleIndex;
801         visiblePos -= item->size() + spacing;
802         item->setPosition(visiblePos);
803         visibleItems.prepend(item);
804         changed = true;
805         if (doBuffer) // never buffer more than one item per frame
806             break;
807     }
808 
809     if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
810         while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) {
811             if (item->attached->delayRemove())
812                 break;
813 //            qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
814             if (item->index != -1)
815                 visibleIndex++;
816             visibleItems.removeFirst();
817             releaseItem(item);
818             changed = true;
819         }
820         while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
821             if (item->attached->delayRemove())
822                 break;
823 //            qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
824             visibleItems.removeLast();
825             releaseItem(item);
826             changed = true;
827         }
828         deferredRelease = false;
829     } else {
830         deferredRelease = true;
831     }
832     if (changed) {
833         minExtentDirty = true;
834         maxExtentDirty = true;
835         if (visibleItems.count())
836             visiblePos = (*visibleItems.constBegin())->position();
837         updateAverage();
838         if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
839             currentItem->setPosition(positionAt(currentIndex));
840             updateHighlight();
841         }
842 
843         if (sectionCriteria)
844             updateCurrentSection();
845         if (header)
846             updateHeader();
847         if (footer)
848             updateFooter();
849         updateViewport();
850         updateUnrequestedPositions();
851     } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
852         refill(from, to, true);
853     }
854     lazyRelease = false;
855 }
856 
scheduleLayout()857 void QDeclarativeListViewPrivate::scheduleLayout()
858 {
859     Q_Q(QDeclarativeListView);
860     if (!layoutScheduled) {
861         layoutScheduled = true;
862         QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority);
863     }
864 }
865 
layout()866 void QDeclarativeListViewPrivate::layout()
867 {
868     Q_Q(QDeclarativeListView);
869     layoutScheduled = false;
870     if (!isValid() && !visibleItems.count()) {
871         clear();
872         setPosition(0);
873         return;
874     }
875     if (!visibleItems.isEmpty()) {
876         bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item;
877         qreal sum = visibleItems.first()->size();
878         qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing;
879         for (int i=1; i < visibleItems.count(); ++i) {
880             FxListItem *item = visibleItems.at(i);
881             item->setPosition(pos);
882             pos += item->size() + spacing;
883             sum += item->size();
884             fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
885         }
886         averageSize = qRound(sum / visibleItems.count());
887         // move current item if it is not a visible item.
888         if (currentIndex >= 0 && currentItem && !fixedCurrent)
889             currentItem->setPosition(positionAt(currentIndex));
890     }
891     q->refill();
892     minExtentDirty = true;
893     maxExtentDirty = true;
894     updateHighlight();
895     if (!q->isMoving() && !q->isFlicking()) {
896         fixupPosition();
897         q->refill();
898     }
899     if (sectionCriteria)
900         updateCurrentSection();
901     if (header)
902         updateHeader();
903     if (footer)
904         updateFooter();
905     updateViewport();
906 }
907 
updateUnrequestedIndexes()908 void QDeclarativeListViewPrivate::updateUnrequestedIndexes()
909 {
910     Q_Q(QDeclarativeListView);
911     QHash<QDeclarativeItem*,int>::iterator it;
912     for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
913         *it = model->indexOf(it.key(), q);
914 }
915 
updateUnrequestedPositions()916 void QDeclarativeListViewPrivate::updateUnrequestedPositions()
917 {
918     Q_Q(QDeclarativeListView);
919     if (unrequestedItems.count()) {
920         qreal pos = position();
921         QHash<QDeclarativeItem*,int>::const_iterator it;
922         for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
923             QDeclarativeItem *item = it.key();
924             if (orient == QDeclarativeListView::Vertical) {
925                 if (item->y() + item->height() > pos && item->y() < pos + q->height())
926                     item->setY(positionAt(*it));
927             } else {
928                 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
929                     if (isRightToLeft())
930                         item->setX(-positionAt(*it)-item->width());
931                     else
932                         item->setX(positionAt(*it));
933                 }
934             }
935         }
936     }
937 }
938 
updateTrackedItem()939 void QDeclarativeListViewPrivate::updateTrackedItem()
940 {
941     Q_Q(QDeclarativeListView);
942     FxListItem *item = currentItem;
943     if (highlight)
944         item = highlight;
945     trackedItem = item;
946     if (trackedItem)
947         q->trackedPositionChanged();
948 }
949 
createHighlight()950 void QDeclarativeListViewPrivate::createHighlight()
951 {
952     Q_Q(QDeclarativeListView);
953     bool changed = false;
954     if (highlight) {
955         if (trackedItem == highlight)
956             trackedItem = 0;
957         if (highlight->item->scene())
958             highlight->item->scene()->removeItem(highlight->item);
959         highlight->item->deleteLater();
960         delete highlight;
961         highlight = 0;
962         delete highlightPosAnimator;
963         delete highlightSizeAnimator;
964         highlightPosAnimator = 0;
965         highlightSizeAnimator = 0;
966         changed = true;
967     }
968 
969     if (currentItem) {
970         QDeclarativeItem *item = 0;
971         if (highlightComponent) {
972             item = createComponentItem(highlightComponent);
973         } else {
974             item = new QDeclarativeItem;
975         }
976         if (item) {
977             QDeclarative_setParent_noEvent(item, q->contentItem());
978             item->setParentItem(q->contentItem());
979             highlight = new FxListItem(item, q);
980             if (currentItem && autoHighlight) {
981                 if (orient == QDeclarativeListView::Vertical) {
982                     highlight->item->setHeight(currentItem->item->height());
983                 } else {
984                     highlight->item->setWidth(currentItem->item->width());
985                 }
986                 highlight->setPosition(currentItem->itemPosition());
987             }
988             QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
989             itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
990             const QLatin1String posProp(orient == QDeclarativeListView::Vertical ? "y" : "x");
991             highlightPosAnimator = new QSmoothedAnimation(q);
992             highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp);
993             highlightPosAnimator->velocity = highlightMoveSpeed;
994             highlightPosAnimator->userDuration = highlightMoveDuration;
995             const QLatin1String sizeProp(orient == QDeclarativeListView::Vertical ? "height" : "width");
996             highlightSizeAnimator = new QSmoothedAnimation(q);
997             highlightSizeAnimator->velocity = highlightResizeSpeed;
998             highlightSizeAnimator->userDuration = highlightResizeDuration;
999             highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp);
1000             if (autoHighlight) {
1001                 highlightPosAnimator->restart();
1002                 highlightSizeAnimator->restart();
1003             }
1004             changed = true;
1005         }
1006     }
1007     if (changed)
1008         emit q->highlightItemChanged();
1009 }
1010 
updateHighlight()1011 void QDeclarativeListViewPrivate::updateHighlight()
1012 {
1013     if ((!currentItem && highlight) || (currentItem && !highlight))
1014         createHighlight();
1015     if (currentItem && autoHighlight && highlight && !hData.moving && !vData.moving) {
1016         // auto-update highlight
1017         highlightPosAnimator->to = isRightToLeft()
1018                 ? -currentItem->itemPosition()-currentItem->itemSize()
1019                 : currentItem->itemPosition();
1020         highlightSizeAnimator->to = currentItem->itemSize();
1021         if (orient == QDeclarativeListView::Vertical) {
1022             if (highlight->item->width() == 0)
1023                 highlight->item->setWidth(currentItem->item->width());
1024         } else {
1025             if (highlight->item->height() == 0)
1026                 highlight->item->setHeight(currentItem->item->height());
1027         }
1028         highlightPosAnimator->restart();
1029         highlightSizeAnimator->restart();
1030     }
1031     updateTrackedItem();
1032 }
1033 
createSection(FxListItem * listItem)1034 void QDeclarativeListViewPrivate::createSection(FxListItem *listItem)
1035 {
1036     Q_Q(QDeclarativeListView);
1037     if (!sectionCriteria || !sectionCriteria->delegate())
1038         return;
1039     if (listItem->attached->m_prevSection != listItem->attached->m_section) {
1040         if (!listItem->section) {
1041             qreal pos = listItem->position();
1042             int i = sectionCacheSize-1;
1043             while (i >= 0 && !sectionCache[i])
1044                 --i;
1045             if (i >= 0) {
1046                 listItem->section = sectionCache[i];
1047                 sectionCache[i] = 0;
1048                 listItem->section->setVisible(true);
1049                 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1050                 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1051             } else {
1052                 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1053                 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1054                 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
1055                 if (nobj) {
1056                     QDeclarative_setParent_noEvent(context, nobj);
1057                     listItem->section = qobject_cast<QDeclarativeItem *>(nobj);
1058                     if (!listItem->section) {
1059                         delete nobj;
1060                     } else {
1061                         listItem->section->setZValue(1);
1062                         QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
1063                         listItem->section->setParentItem(q->contentItem());
1064                     }
1065                 } else {
1066                     delete context;
1067                 }
1068                 sectionCriteria->delegate()->completeCreate();
1069             }
1070             listItem->setPosition(pos);
1071         } else {
1072             QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1073             context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1074         }
1075     } else if (listItem->section) {
1076         qreal pos = listItem->position();
1077         int i = 0;
1078         do {
1079             if (!sectionCache[i]) {
1080                 sectionCache[i] = listItem->section;
1081                 sectionCache[i]->setVisible(false);
1082                 listItem->section = 0;
1083                 return;
1084             }
1085             ++i;
1086         } while (i < sectionCacheSize);
1087         delete listItem->section;
1088         listItem->section = 0;
1089         listItem->setPosition(pos);
1090     }
1091 }
1092 
updateSections()1093 void QDeclarativeListViewPrivate::updateSections()
1094 {
1095     if (sectionCriteria && !visibleItems.isEmpty()) {
1096         QString prevSection;
1097         if (visibleIndex > 0)
1098             prevSection = sectionAt(visibleIndex-1);
1099         QDeclarativeListViewAttached *prevAtt = 0;
1100         int idx = -1;
1101         for (int i = 0; i < visibleItems.count(); ++i) {
1102             QDeclarativeListViewAttached *attached = visibleItems.at(i)->attached;
1103             attached->setPrevSection(prevSection);
1104             if (visibleItems.at(i)->index != -1) {
1105                 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1106                 attached->setSection(sectionCriteria->sectionString(propValue));
1107                 idx = visibleItems.at(i)->index;
1108             }
1109             createSection(visibleItems.at(i));
1110             if (prevAtt)
1111                 prevAtt->setNextSection(attached->section());
1112             prevSection = attached->section();
1113             prevAtt = attached;
1114         }
1115         if (prevAtt) {
1116             if (idx > 0 && idx < model->count()-1)
1117                 prevAtt->setNextSection(sectionAt(idx+1));
1118             else
1119                 prevAtt->setNextSection(QString());
1120         }
1121     }
1122 }
1123 
updateCurrentSection()1124 void QDeclarativeListViewPrivate::updateCurrentSection()
1125 {
1126     Q_Q(QDeclarativeListView);
1127     if (!sectionCriteria || visibleItems.isEmpty()) {
1128         if (!currentSection.isEmpty()) {
1129             currentSection.clear();
1130             emit q->currentSectionChanged();
1131         }
1132         return;
1133     }
1134     int index = 0;
1135     while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position())
1136         ++index;
1137 
1138     QString newSection = currentSection;
1139     if (index < visibleItems.count())
1140         newSection = visibleItems.at(index)->attached->section();
1141     else
1142         newSection = visibleItems.first()->attached->section();
1143     if (newSection != currentSection) {
1144         currentSection = newSection;
1145         emit q->currentSectionChanged();
1146     }
1147 }
1148 
updateCurrent(int modelIndex)1149 void QDeclarativeListViewPrivate::updateCurrent(int modelIndex)
1150 {
1151     Q_Q(QDeclarativeListView);
1152     if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1153         if (currentItem) {
1154             currentItem->attached->setIsCurrentItem(false);
1155             releaseItem(currentItem);
1156             currentItem = 0;
1157             currentIndex = modelIndex;
1158             emit q->currentIndexChanged();
1159             updateHighlight();
1160         } else if (currentIndex != modelIndex) {
1161             currentIndex = modelIndex;
1162             emit q->currentIndexChanged();
1163         }
1164         return;
1165     }
1166 
1167     if (currentItem && currentIndex == modelIndex) {
1168         updateHighlight();
1169         return;
1170     }
1171     FxListItem *oldCurrentItem = currentItem;
1172     currentIndex = modelIndex;
1173     currentItem = createItem(modelIndex);
1174     if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1175         oldCurrentItem->attached->setIsCurrentItem(false);
1176     if (currentItem) {
1177         if (modelIndex == visibleIndex - 1 && visibleItems.count()) {
1178             // We can calculate exact postion in this case
1179             currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1180         } else {
1181             // Create current item now and position as best we can.
1182             // Its position will be corrected when it becomes visible.
1183             currentItem->setPosition(positionAt(modelIndex));
1184         }
1185         currentItem->item->setFocus(true);
1186         currentItem->attached->setIsCurrentItem(true);
1187         // Avoid showing section delegate twice.  We still need the section heading so that
1188         // currentItem positioning works correctly.
1189         // This is slightly sub-optimal, but section heading caching minimizes the impact.
1190         if (currentItem->section)
1191             currentItem->section->setVisible(false);
1192         if (visibleItems.isEmpty())
1193             averageSize = currentItem->size();
1194     }
1195     updateHighlight();
1196     emit q->currentIndexChanged();
1197     // Release the old current item
1198     releaseItem(oldCurrentItem);
1199 }
1200 
updateAverage()1201 void QDeclarativeListViewPrivate::updateAverage()
1202 {
1203     if (!visibleItems.count())
1204         return;
1205     qreal sum = 0.0;
1206     for (int i = 0; i < visibleItems.count(); ++i)
1207         sum += visibleItems.at(i)->size();
1208     averageSize = qRound(sum / visibleItems.count());
1209 }
1210 
updateFooter()1211 void QDeclarativeListViewPrivate::updateFooter()
1212 {
1213     Q_Q(QDeclarativeListView);
1214     if (!footer && footerComponent) {
1215         QDeclarativeItem *item = createComponentItem(footerComponent);
1216         if (item) {
1217             QDeclarative_setParent_noEvent(item, q->contentItem());
1218             item->setParentItem(q->contentItem());
1219             item->setZValue(1);
1220             QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1221             itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1222             footer = new FxListItem(item, q);
1223         }
1224     }
1225     if (footer) {
1226         if (visibleItems.count()) {
1227             qreal endPos = lastPosition() + 1;
1228             if (lastVisibleIndex() == model->count()-1) {
1229                 footer->setPosition(endPos);
1230             } else {
1231                 qreal visiblePos = position() + q->height();
1232                 if (endPos <= visiblePos || footer->position() < endPos)
1233                     footer->setPosition(endPos);
1234             }
1235         } else {
1236             footer->setPosition(visiblePos);
1237         }
1238     }
1239 }
1240 
updateHeader()1241 void QDeclarativeListViewPrivate::updateHeader()
1242 {
1243     Q_Q(QDeclarativeListView);
1244     if (!header && headerComponent) {
1245         QDeclarativeItem *item = createComponentItem(headerComponent);
1246         if (item) {
1247             QDeclarative_setParent_noEvent(item, q->contentItem());
1248             item->setParentItem(q->contentItem());
1249             item->setZValue(1);
1250             QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1251             itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1252             header = new FxListItem(item, q);
1253         }
1254     }
1255     if (header) {
1256         if (visibleItems.count()) {
1257             qreal startPos = originPosition();
1258             if (visibleIndex == 0) {
1259                 header->setPosition(startPos - header->size());
1260             } else {
1261                 if (position() <= startPos || header->position() > startPos - header->size())
1262                     header->setPosition(startPos - header->size());
1263             }
1264         } else {
1265             if (itemCount == 0)
1266                 visiblePos = header->size();
1267             header->setPosition(0);
1268         }
1269     }
1270 }
1271 
fixupPosition()1272 void QDeclarativeListViewPrivate::fixupPosition()
1273 {
1274     if ((haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange)
1275         || snapMode != QDeclarativeListView::NoSnap)
1276         moveReason = Other;
1277     if (orient == QDeclarativeListView::Vertical)
1278         fixupY();
1279     else
1280         fixupX();
1281 }
1282 
fixup(AxisData & data,qreal minExtent,qreal maxExtent)1283 void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1284 {
1285     if ((orient == QDeclarativeListView::Horizontal && &data == &vData)
1286         || (orient == QDeclarativeListView::Vertical && &data == &hData))
1287         return;
1288 
1289     correctFlick = false;
1290     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1291     bool strictHighlightRange = haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange;
1292 
1293     qreal highlightStart;
1294     qreal highlightEnd;
1295     qreal viewPos;
1296     if (isRightToLeft()) {
1297         // Handle Right-To-Left exceptions
1298         viewPos = -position()-size();
1299         highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
1300         highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
1301     } else {
1302         viewPos = position();
1303         highlightStart = highlightRangeStart;
1304         highlightEnd = highlightRangeEnd;
1305     }
1306 
1307     if (snapMode != QDeclarativeListView::NoSnap && moveReason != QDeclarativeListViewPrivate::SetIndex) {
1308         qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1309         if (snapMode == QDeclarativeListView::SnapOneItem && moveReason == Mouse) {
1310             // if we've been dragged < averageSize/2 then bias towards the next item
1311             qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1312             qreal bias = 0;
1313             if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1314                 bias = averageSize/2;
1315             else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1316                 bias = -averageSize/2;
1317             if (isRightToLeft())
1318                 bias = -bias;
1319             tempPosition -= bias;
1320         }
1321         FxListItem *topItem = snapItemAt(tempPosition+highlightStart);
1322         if (!topItem && strictHighlightRange && currentItem) {
1323             // StrictlyEnforceRange always keeps an item in range
1324             updateHighlight();
1325             topItem = currentItem;
1326         }
1327         FxListItem *bottomItem = snapItemAt(tempPosition+highlightEnd);
1328         if (!bottomItem && strictHighlightRange && currentItem) {
1329             // StrictlyEnforceRange always keeps an item in range
1330             updateHighlight();
1331             bottomItem = currentItem;
1332         }
1333         qreal pos;
1334         bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1335         if (topItem && (isInBounds || strictHighlightRange)) {
1336             if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2 && !strictHighlightRange) {
1337                 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1338             } else {
1339                 if (isRightToLeft())
1340                     pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1341                 else
1342                     pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1343             }
1344         } else if (bottomItem && isInBounds) {
1345             if (isRightToLeft())
1346                 pos = qMax(qMin(-bottomItem->position() + highlightEnd - size(), -maxExtent), -minExtent);
1347             else
1348                 pos = qMax(qMin(bottomItem->position() - highlightEnd, -maxExtent), -minExtent);
1349         } else {
1350             QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1351             return;
1352         }
1353 
1354         qreal dist = qAbs(data.move + pos);
1355         if (dist > 0) {
1356             timeline.reset(data.move);
1357             if (fixupMode != Immediate) {
1358                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1359                 data.fixingUp = true;
1360             } else {
1361                 timeline.set(data.move, -pos);
1362             }
1363             vTime = timeline.time();
1364         }
1365     } else if (currentItem && strictHighlightRange
1366                 && moveReason != QDeclarativeListViewPrivate::SetIndex) {
1367         updateHighlight();
1368         qreal pos = currentItem->itemPosition();
1369         if (viewPos < pos + currentItem->itemSize() - highlightEnd)
1370             viewPos = pos + currentItem->itemSize() - highlightEnd;
1371         if (viewPos > pos - highlightStart)
1372             viewPos = pos - highlightStart;
1373         if (isRightToLeft())
1374             viewPos = -viewPos-size();
1375 
1376         timeline.reset(data.move);
1377         if (viewPos != position()) {
1378             if (fixupMode != Immediate) {
1379                 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1380                 data.fixingUp = true;
1381             } else {
1382                 timeline.set(data.move, -viewPos);
1383             }
1384         }
1385         vTime = timeline.time();
1386     } else {
1387         QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1388     }
1389     data.inOvershoot = false;
1390     fixupMode = Normal;
1391 }
1392 
flick(AxisData & data,qreal minExtent,qreal maxExtent,qreal vSize,QDeclarativeTimeLineCallback::Callback fixupCallback,qreal velocity)1393 void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1394                                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1395 {
1396     Q_Q(QDeclarativeListView);
1397 
1398     data.fixingUp = false;
1399     moveReason = Mouse;
1400     if ((!haveHighlightRange || highlightRange != QDeclarativeListView::StrictlyEnforceRange) && snapMode == QDeclarativeListView::NoSnap) {
1401         correctFlick = true;
1402         QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1403         return;
1404     }
1405     qreal maxDistance = 0;
1406     qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1407     qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1408     // -ve velocity means list is moving up/left
1409     if (velocity > 0) {
1410         if (data.move.value() < minExtent) {
1411             if (snapMode == QDeclarativeListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1412                 // if we've been dragged < averageSize/2 then bias towards the next item
1413                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1414                 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1415                 if (isRightToLeft())
1416                     bias = -bias;
1417                 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) - bias) + highlightStart;
1418                 maxDistance = qAbs(data.flickTarget - data.move.value());
1419                 velocity = maxVelocity;
1420             } else {
1421                 maxDistance = qAbs(minExtent - data.move.value());
1422             }
1423         }
1424         if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange)
1425             data.flickTarget = minExtent;
1426     } else {
1427         if (data.move.value() > maxExtent) {
1428             if (snapMode == QDeclarativeListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1429                 // if we've been dragged < averageSize/2 then bias towards the next item
1430                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1431                 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1432                 if (isRightToLeft())
1433                     bias = -bias;
1434                 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + bias) + highlightStart;
1435                 maxDistance = qAbs(data.flickTarget - data.move.value());
1436                 velocity = -maxVelocity;
1437             } else {
1438                 maxDistance = qAbs(maxExtent - data.move.value());
1439             }
1440         }
1441         if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange)
1442             data.flickTarget = maxExtent;
1443     }
1444 
1445     bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds;
1446 
1447     if (maxDistance > 0 || overShoot) {
1448         // These modes require the list to stop exactly on an item boundary.
1449         // The initial flick will estimate the boundary to stop on.
1450         // Since list items can have variable sizes, the boundary will be
1451         // reevaluated and adjusted as we approach the boundary.
1452         qreal v = velocity;
1453         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1454             if (v < 0)
1455                 v = -maxVelocity;
1456             else
1457                 v = maxVelocity;
1458         }
1459         if (!hData.flicking && !vData.flicking) {
1460             // the initial flick - estimate boundary
1461             qreal accel = deceleration;
1462             qreal v2 = v * v;
1463             overshootDist = qreal(0.0);
1464             // + averageSize/4 to encourage moving at least one item in the flick direction
1465             qreal dist = v2 / (accel * qreal(2.0)) + averageSize/4;
1466             if (maxDistance > 0)
1467                 dist = qMin(dist, maxDistance);
1468             if (v > 0)
1469                 dist = -dist;
1470             if ((maxDistance > qreal(0.0) && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeListView::SnapOneItem) {
1471                 if (snapMode != QDeclarativeListView::SnapOneItem) {
1472                     qreal distTemp = isRightToLeft() ? -dist : dist;
1473                     data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1474                 }
1475                 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1476                 if (overShoot) {
1477                     if (data.flickTarget >= minExtent) {
1478                         overshootDist = overShootDistance(vSize);
1479                         data.flickTarget += overshootDist;
1480                     } else if (data.flickTarget <= maxExtent) {
1481                         overshootDist = overShootDistance(vSize);
1482                         data.flickTarget -= overshootDist;
1483                     }
1484                 }
1485                 qreal adjDist = -data.flickTarget + data.move.value();
1486                 if (qAbs(adjDist) > qAbs(dist)) {
1487                     // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1488                     qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1489                     if (adjv2 > v2) {
1490                         v2 = adjv2;
1491                         v = qSqrt(v2);
1492                         if (dist > 0)
1493                             v = -v;
1494                     }
1495                 }
1496                 dist = adjDist;
1497                 accel = v2 / (2.0f * qAbs(dist));
1498             } else if (overShoot) {
1499                 data.flickTarget = data.move.value() - dist;
1500                 if (data.flickTarget >= minExtent) {
1501                     overshootDist = overShootDistance(vSize);
1502                     data.flickTarget += overshootDist;
1503                 } else if (data.flickTarget <= maxExtent) {
1504                     overshootDist = overShootDistance(vSize);
1505                     data.flickTarget -= overshootDist;
1506                 }
1507             }
1508 
1509             timeline.reset(data.move);
1510             timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1511             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1512             if (!hData.flicking && q->xflick()) {
1513                 hData.flicking = true;
1514                 emit q->flickingChanged();
1515                 emit q->flickingHorizontallyChanged();
1516                 emit q->flickStarted();
1517             }
1518             if (!vData.flicking && q->yflick()) {
1519                 vData.flicking = true;
1520                 emit q->flickingChanged();
1521                 emit q->flickingVerticallyChanged();
1522                 emit q->flickStarted();
1523             }
1524             correctFlick = true;
1525         } else {
1526             // reevaluate the target boundary.
1527             qreal newtarget = data.flickTarget;
1528             if (snapMode != QDeclarativeListView::NoSnap || highlightRange == QDeclarativeListView::StrictlyEnforceRange) {
1529                 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1530                 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1531                 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1532             }
1533             if (velocity < 0 && newtarget <= maxExtent)
1534                 newtarget = maxExtent - overshootDist;
1535             else if (velocity > 0 && newtarget >= minExtent)
1536                 newtarget = minExtent + overshootDist;
1537             if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1538                 if (qAbs(velocity) < MinimumFlickVelocity)
1539                     correctFlick = false;
1540                 return;
1541             }
1542             data.flickTarget = newtarget;
1543             qreal dist = -newtarget + data.move.value();
1544             if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1545                 correctFlick = false;
1546                 timeline.reset(data.move);
1547                 fixup(data, minExtent, maxExtent);
1548                 return;
1549             }
1550 
1551             timeline.reset(data.move);
1552             timeline.accelDistance(data.move, v, -dist);
1553             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1554         }
1555     } else {
1556         correctFlick = false;
1557         timeline.reset(data.move);
1558         fixup(data, minExtent, maxExtent);
1559     }
1560 }
1561 
1562 //----------------------------------------------------------------------------
1563 
1564 /*!
1565     \qmlclass ListView QDeclarativeListView
1566     \ingroup qml-view-elements
1567     \since 4.7
1568     \inherits Flickable
1569     \brief The ListView item provides a list view of items provided by a model.
1570 
1571     A ListView displays data from models created from built-in QML elements like ListModel
1572     and XmlListModel, or custom model classes defined in C++ that inherit from
1573     QAbstractListModel.
1574 
1575     A ListView has a \l model, which defines the data to be displayed, and
1576     a \l delegate, which defines how the data should be displayed. Items in a
1577     ListView are laid out horizontally or vertically. List views are inherently
1578     flickable because ListView inherits from \l Flickable.
1579 
1580     \section1 Example Usage
1581 
1582     The following example shows the definition of a simple list model defined
1583     in a file called \c ContactModel.qml:
1584 
1585     \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0
1586 
1587     Another component can display this model data in a ListView, like this:
1588 
1589     \snippet doc/src/snippets/declarative/listview/listview.qml import
1590     \codeline
1591     \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple
1592 
1593     \image listview-simple.png
1594 
1595     Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1596     for its delegate. The view will create a new \l Text component for each item in the model. Notice
1597     the delegate is able to access the model's \c name and \c number data directly.
1598 
1599     An improved list view is shown below. The delegate is visually improved and is moved
1600     into a separate \c contactDelegate component.
1601 
1602     \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced
1603     \image listview-highlight.png
1604 
1605     The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1606     and \c focus is set to \c true to enable keyboard navigation for the list view.
1607     The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1608 
1609     Delegates are instantiated as needed and may be destroyed at any time.
1610     State should \e never be stored in a delegate.
1611 
1612     ListView attaches a number of properties to the root item of the delegate, for example
1613     \c {ListView.isCurrentItem}.  In the following example, the root delegate item can access
1614     this attached property directly as \c ListView.isCurrentItem, while the child
1615     \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1616 
1617     \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1618 
1619     \note Views do not enable \e clip automatically.  If the view
1620     is not clipped by another item or the screen, it will be necessary
1621     to set \e {clip: true} in order to have the out of view items clipped
1622     nicely.
1623 
1624     \sa {QML Data Models}, GridView, {Models and Views: ListView Examples}{ListView examples}
1625 */
1626 
QDeclarativeListView(QDeclarativeItem * parent)1627 QDeclarativeListView::QDeclarativeListView(QDeclarativeItem *parent)
1628     : QDeclarativeFlickable(*(new QDeclarativeListViewPrivate), parent)
1629 {
1630     Q_D(QDeclarativeListView);
1631     d->init();
1632 }
1633 
~QDeclarativeListView()1634 QDeclarativeListView::~QDeclarativeListView()
1635 {
1636     Q_D(QDeclarativeListView);
1637     d->clear();
1638     if (d->ownModel)
1639         delete d->model;
1640     delete d->header;
1641     delete d->footer;
1642 }
1643 
1644 /*!
1645     \qmlattachedproperty bool ListView::isCurrentItem
1646     This attached property is true if this delegate is the current item; otherwise false.
1647 
1648     It is attached to each instance of the delegate.
1649 
1650     This property may be used to adjust the appearance of the current item, for example:
1651 
1652     \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1653 */
1654 
1655 /*!
1656     \qmlattachedproperty ListView ListView::view
1657     This attached property holds the view that manages this delegate instance.
1658 
1659     It is attached to each instance of the delegate.
1660 */
1661 
1662 /*!
1663     \qmlattachedproperty string ListView::previousSection
1664     This attached property holds the section of the previous element.
1665 
1666     It is attached to each instance of the delegate.
1667 
1668     The section is evaluated using the \l {ListView::section.property}{section} properties.
1669 */
1670 
1671 /*!
1672     \qmlattachedproperty string ListView::nextSection
1673     This attached property holds the section of the next element.
1674 
1675     It is attached to each instance of the delegate.
1676 
1677     The section is evaluated using the \l {ListView::section.property}{section} properties.
1678 */
1679 
1680 /*!
1681     \qmlattachedproperty string ListView::section
1682     This attached property holds the section of this element.
1683 
1684     It is attached to each instance of the delegate.
1685 
1686     The section is evaluated using the \l {ListView::section.property}{section} properties.
1687 */
1688 
1689 /*!
1690     \qmlattachedproperty bool ListView::delayRemove
1691     This attached property holds whether the delegate may be destroyed.
1692 
1693     It is attached to each instance of the delegate.
1694 
1695     It is sometimes necessary to delay the destruction of an item
1696     until an animation completes.
1697 
1698     The example delegate below ensures that the animation completes before
1699     the item is removed from the list.
1700 
1701     \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove
1702 */
1703 
1704 /*!
1705     \qmlattachedsignal ListView::onAdd()
1706     This attached handler is called immediately after an item is added to the view.
1707 */
1708 
1709 /*!
1710     \qmlattachedsignal ListView::onRemove()
1711     This attached handler is called immediately before an item is removed from the view.
1712 */
1713 
1714 /*!
1715     \qmlproperty model ListView::model
1716     This property holds the model providing data for the list.
1717 
1718     The model provides the set of data that is used to create the items
1719     in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1720     or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1721     used, it must be a subclass of \l QAbstractItemModel or a simple list.
1722 
1723     \sa {qmlmodels}{Data Models}
1724 */
model() const1725 QVariant QDeclarativeListView::model() const
1726 {
1727     Q_D(const QDeclarativeListView);
1728     return d->modelVariant;
1729 }
1730 
setModel(const QVariant & model)1731 void QDeclarativeListView::setModel(const QVariant &model)
1732 {
1733     Q_D(QDeclarativeListView);
1734     if (d->modelVariant == model)
1735         return;
1736     if (d->model) {
1737         disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1738         disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1739         disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1740         disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1741         disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1742         disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1743         disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1744     }
1745     d->clear();
1746     QDeclarativeVisualModel *oldModel = d->model;
1747     d->model = 0;
1748     d->setPosition(0);
1749     d->modelVariant = model;
1750     QObject *object = qvariant_cast<QObject*>(model);
1751     QDeclarativeVisualModel *vim = 0;
1752     if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) {
1753         if (d->ownModel) {
1754             delete oldModel;
1755             d->ownModel = false;
1756         }
1757         d->model = vim;
1758     } else {
1759         if (!d->ownModel) {
1760             d->model = new QDeclarativeVisualDataModel(qmlContext(this), this);
1761             d->ownModel = true;
1762         } else {
1763             d->model = oldModel;
1764         }
1765         if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1766             dataModel->setModel(model);
1767     }
1768     if (d->model) {
1769         d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
1770         if (isComponentComplete()) {
1771             updateSections();
1772             refill();
1773             if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
1774                 setCurrentIndex(0);
1775             } else {
1776                 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1777                 d->updateCurrent(d->currentIndex);
1778                 if (d->highlight && d->currentItem) {
1779                     if (d->autoHighlight)
1780                         d->highlight->setPosition(d->currentItem->position());
1781                     d->updateTrackedItem();
1782                 }
1783             }
1784             d->updateViewport();
1785         }
1786         connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1787         connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1788         connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1789         connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1790         connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1791         connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1792         connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1793         emit countChanged();
1794     }
1795     emit modelChanged();
1796 }
1797 
1798 /*!
1799     \qmlproperty Component ListView::delegate
1800 
1801     The delegate provides a template defining each item instantiated by the view.
1802     The index is exposed as an accessible \c index property.  Properties of the
1803     model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1804 
1805     The number of elements in the delegate has a direct effect on the
1806     flicking performance of the view.  If at all possible, place functionality
1807     that is not needed for the normal display of the delegate in a \l Loader which
1808     can load additional elements when needed.
1809 
1810     The ListView will lay out the items based on the size of the root item
1811     in the delegate.
1812 
1813     It is recommended that the delagate's size be a whole number to avoid sub-pixel
1814     alignment of items.
1815 
1816     \note Delegates are instantiated as needed and may be destroyed at any time.
1817     State should \e never be stored in a delegate.
1818 */
delegate() const1819 QDeclarativeComponent *QDeclarativeListView::delegate() const
1820 {
1821     Q_D(const QDeclarativeListView);
1822     if (d->model) {
1823         if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1824             return dataModel->delegate();
1825     }
1826 
1827     return 0;
1828 }
1829 
setDelegate(QDeclarativeComponent * delegate)1830 void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate)
1831 {
1832     Q_D(QDeclarativeListView);
1833     if (delegate == this->delegate())
1834         return;
1835     if (!d->ownModel) {
1836         d->model = new QDeclarativeVisualDataModel(qmlContext(this));
1837         d->ownModel = true;
1838     }
1839     if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) {
1840         int oldCount = dataModel->count();
1841         dataModel->setDelegate(delegate);
1842         if (isComponentComplete()) {
1843             for (int i = 0; i < d->visibleItems.count(); ++i)
1844                 d->releaseItem(d->visibleItems.at(i));
1845             d->visibleItems.clear();
1846             d->releaseItem(d->currentItem);
1847             d->currentItem = 0;
1848             updateSections();
1849             refill();
1850             d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1851             d->updateCurrent(d->currentIndex);
1852             if (d->highlight && d->currentItem) {
1853                 if (d->autoHighlight)
1854                     d->highlight->setPosition(d->currentItem->position());
1855                 d->updateTrackedItem();
1856             }
1857             d->updateViewport();
1858         }
1859         if (oldCount != dataModel->count())
1860             emit countChanged();
1861     }
1862     emit delegateChanged();
1863 }
1864 
1865 /*!
1866     \qmlproperty int ListView::currentIndex
1867     \qmlproperty Item ListView::currentItem
1868 
1869     The \c currentIndex property holds the index of the current item, and
1870     \c currentItem holds the current item.   Setting the currentIndex to -1
1871     will clear the highlight and set currentItem to null.
1872 
1873     If highlightFollowsCurrentItem is \c true, setting either of these
1874     properties will smoothly scroll the ListView so that the current
1875     item becomes visible.
1876 
1877     Note that the position of the current item
1878     may only be approximate until it becomes visible in the view.
1879 */
currentIndex() const1880 int QDeclarativeListView::currentIndex() const
1881 {
1882     Q_D(const QDeclarativeListView);
1883     return d->currentIndex;
1884 }
1885 
setCurrentIndex(int index)1886 void QDeclarativeListView::setCurrentIndex(int index)
1887 {
1888     Q_D(QDeclarativeListView);
1889     if (d->requestedIndex >= 0)  // currently creating item
1890         return;
1891     d->currentIndexCleared = (index == -1);
1892     if (index == d->currentIndex)
1893         return;
1894     if (isComponentComplete() && d->isValid()) {
1895         if (d->layoutScheduled)
1896             d->layout();
1897         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1898         d->updateCurrent(index);
1899     } else if (d->currentIndex != index) {
1900         d->currentIndex = index;
1901         emit currentIndexChanged();
1902     }
1903 }
1904 
currentItem()1905 QDeclarativeItem *QDeclarativeListView::currentItem()
1906 {
1907     Q_D(QDeclarativeListView);
1908     if (!d->currentItem)
1909         return 0;
1910     return d->currentItem->item;
1911 }
1912 
1913 /*!
1914   \qmlproperty Item ListView::highlightItem
1915 
1916     This holds the highlight item created from the \l highlight component.
1917 
1918   The \c highlightItem is managed by the view unless
1919   \l highlightFollowsCurrentItem is set to false.
1920 
1921   \sa highlight, highlightFollowsCurrentItem
1922 */
highlightItem()1923 QDeclarativeItem *QDeclarativeListView::highlightItem()
1924 {
1925     Q_D(QDeclarativeListView);
1926     if (!d->highlight)
1927         return 0;
1928     return d->highlight->item;
1929 }
1930 
1931 /*!
1932   \qmlproperty int ListView::count
1933   This property holds the number of items in the view.
1934 */
count() const1935 int QDeclarativeListView::count() const
1936 {
1937     Q_D(const QDeclarativeListView);
1938     if (d->model)
1939         return d->model->count();
1940     return 0;
1941 }
1942 
1943 /*!
1944     \qmlproperty Component ListView::highlight
1945     This property holds the component to use as the highlight.
1946 
1947     An instance of the highlight component is created for each list.
1948     The geometry of the resulting component instance is managed by the list
1949     so as to stay with the current item, unless the highlightFollowsCurrentItem
1950     property is false.
1951 
1952     \sa highlightItem, highlightFollowsCurrentItem, {Models and Views: ListView Examples}{ListView examples}
1953 */
highlight() const1954 QDeclarativeComponent *QDeclarativeListView::highlight() const
1955 {
1956     Q_D(const QDeclarativeListView);
1957     return d->highlightComponent;
1958 }
1959 
setHighlight(QDeclarativeComponent * highlight)1960 void QDeclarativeListView::setHighlight(QDeclarativeComponent *highlight)
1961 {
1962     Q_D(QDeclarativeListView);
1963     if (highlight != d->highlightComponent) {
1964         d->highlightComponent = highlight;
1965         d->createHighlight();
1966         if (d->currentItem)
1967             d->updateHighlight();
1968         emit highlightChanged();
1969     }
1970 }
1971 
1972 /*!
1973     \qmlproperty bool ListView::highlightFollowsCurrentItem
1974     This property holds whether the highlight is managed by the view.
1975 
1976     If this property is true (the default value), the highlight is moved smoothly
1977     to follow the current item.  Otherwise, the
1978     highlight is not moved by the view, and any movement must be implemented
1979     by the highlight.
1980 
1981     Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1982 
1983     \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1984 
1985     Note that the highlight animation also affects the way that the view
1986     is scrolled.  This is because the view moves to maintain the
1987     highlight within the preferred highlight range (or visible viewport).
1988 
1989     \sa highlight, highlightMoveSpeed
1990 */
highlightFollowsCurrentItem() const1991 bool QDeclarativeListView::highlightFollowsCurrentItem() const
1992 {
1993     Q_D(const QDeclarativeListView);
1994     return d->autoHighlight;
1995 }
1996 
setHighlightFollowsCurrentItem(bool autoHighlight)1997 void QDeclarativeListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1998 {
1999     Q_D(QDeclarativeListView);
2000     if (d->autoHighlight != autoHighlight) {
2001         d->autoHighlight = autoHighlight;
2002         if (autoHighlight) {
2003             d->updateHighlight();
2004         } else {
2005             if (d->highlightPosAnimator)
2006                 d->highlightPosAnimator->stop();
2007             if (d->highlightSizeAnimator)
2008                 d->highlightSizeAnimator->stop();
2009         }
2010         emit highlightFollowsCurrentItemChanged();
2011     }
2012 }
2013 
2014 //###Possibly rename these properties, since they are very useful even without a highlight?
2015 /*!
2016     \qmlproperty real ListView::preferredHighlightBegin
2017     \qmlproperty real ListView::preferredHighlightEnd
2018     \qmlproperty enumeration ListView::highlightRangeMode
2019 
2020     These properties define the preferred range of the highlight (for the current item)
2021     within the view. The \c preferredHighlightBegin value must be less than the
2022     \c preferredHighlightEnd value.
2023 
2024     These properties affect the position of the current item when the list is scrolled.
2025     For example, if the currently selected item should stay in the middle of the
2026     list when the view is scrolled, set the \c preferredHighlightBegin and
2027     \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
2028     item would be. If the \c currentItem is changed programmatically, the list will
2029     automatically scroll so that the current item is in the middle of the view.
2030     Furthermore, the behavior of the current item index will occur whether or not a
2031     highlight exists.
2032 
2033     Valid values for \c highlightRangeMode are:
2034 
2035     \list
2036     \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
2037        However, the highlight can move outside of the range at the ends of the list or due
2038        to mouse interaction.
2039     \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
2040        The current item changes if a keyboard or mouse action would cause the highlight to move
2041        outside of the range.
2042     \o ListView.NoHighlightRange - this is the default value.
2043     \endlist
2044 */
preferredHighlightBegin() const2045 qreal QDeclarativeListView::preferredHighlightBegin() const
2046 {
2047     Q_D(const QDeclarativeListView);
2048     return d->highlightRangeStart;
2049 }
2050 
setPreferredHighlightBegin(qreal start)2051 void QDeclarativeListView::setPreferredHighlightBegin(qreal start)
2052 {
2053     Q_D(QDeclarativeListView);
2054     d->highlightRangeStartValid = true;
2055     if (d->highlightRangeStart == start)
2056         return;
2057     d->highlightRangeStart = start;
2058     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2059     emit preferredHighlightBeginChanged();
2060 }
2061 
resetPreferredHighlightBegin()2062 void QDeclarativeListView::resetPreferredHighlightBegin()
2063 {
2064     Q_D(QDeclarativeListView);
2065     d->highlightRangeStartValid = false;
2066     if (d->highlightRangeStart == 0)
2067         return;
2068     d->highlightRangeStart = 0;
2069     emit preferredHighlightBeginChanged();
2070 }
2071 
preferredHighlightEnd() const2072 qreal QDeclarativeListView::preferredHighlightEnd() const
2073 {
2074     Q_D(const QDeclarativeListView);
2075     return d->highlightRangeEnd;
2076 }
2077 
setPreferredHighlightEnd(qreal end)2078 void QDeclarativeListView::setPreferredHighlightEnd(qreal end)
2079 {
2080     Q_D(QDeclarativeListView);
2081     d->highlightRangeEndValid = true;
2082     if (d->highlightRangeEnd == end)
2083         return;
2084     d->highlightRangeEnd = end;
2085     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2086     emit preferredHighlightEndChanged();
2087 }
2088 
resetPreferredHighlightEnd()2089 void QDeclarativeListView::resetPreferredHighlightEnd()
2090 {
2091     Q_D(QDeclarativeListView);
2092     d->highlightRangeEndValid = false;
2093     if (d->highlightRangeEnd == 0)
2094         return;
2095     d->highlightRangeEnd = 0;
2096     emit preferredHighlightEndChanged();
2097 }
2098 
highlightRangeMode() const2099 QDeclarativeListView::HighlightRangeMode QDeclarativeListView::highlightRangeMode() const
2100 {
2101     Q_D(const QDeclarativeListView);
2102     return d->highlightRange;
2103 }
2104 
setHighlightRangeMode(HighlightRangeMode mode)2105 void QDeclarativeListView::setHighlightRangeMode(HighlightRangeMode mode)
2106 {
2107     Q_D(QDeclarativeListView);
2108     if (d->highlightRange == mode)
2109         return;
2110     d->highlightRange = mode;
2111     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2112     emit highlightRangeModeChanged();
2113 }
2114 
2115 /*!
2116     \qmlproperty real ListView::spacing
2117 
2118     This property holds the spacing between items.
2119 
2120     The default value is 0.
2121 */
spacing() const2122 qreal QDeclarativeListView::spacing() const
2123 {
2124     Q_D(const QDeclarativeListView);
2125     return d->spacing;
2126 }
2127 
setSpacing(qreal spacing)2128 void QDeclarativeListView::setSpacing(qreal spacing)
2129 {
2130     Q_D(QDeclarativeListView);
2131     if (spacing != d->spacing) {
2132         d->spacing = spacing;
2133         d->layout();
2134         emit spacingChanged();
2135     }
2136 }
2137 
2138 /*!
2139     \qmlproperty enumeration ListView::orientation
2140     This property holds the orientation of the list.
2141 
2142     Possible values:
2143 
2144     \list
2145     \o ListView.Horizontal - Items are laid out horizontally
2146     \o ListView.Vertical (default) - Items are laid out vertically
2147     \endlist
2148 
2149     \table
2150     \row
2151     \o Horizontal orientation:
2152     \image ListViewHorizontal.png
2153 
2154     \row
2155     \o Vertical orientation:
2156     \image listview-highlight.png
2157     \endtable
2158 */
orientation() const2159 QDeclarativeListView::Orientation QDeclarativeListView::orientation() const
2160 {
2161     Q_D(const QDeclarativeListView);
2162     return d->orient;
2163 }
2164 
setOrientation(QDeclarativeListView::Orientation orientation)2165 void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orientation)
2166 {
2167     Q_D(QDeclarativeListView);
2168     if (d->orient != orientation) {
2169         d->orient = orientation;
2170         if (d->orient == QDeclarativeListView::Vertical) {
2171             setContentWidth(-1);
2172             setFlickableDirection(VerticalFlick);
2173             setContentX(0);
2174         } else {
2175             setContentHeight(-1);
2176             setFlickableDirection(HorizontalFlick);
2177             setContentY(0);
2178         }
2179         d->regenerate();
2180         emit orientationChanged();
2181     }
2182 }
2183 
2184 /*!
2185   \qmlproperty enumeration ListView::layoutDirection
2186   This property holds the layout direction of the horizontal list.
2187 
2188   Possible values:
2189 
2190   \list
2191   \o Qt.LeftToRight (default) - Items will be laid out from left to right.
2192   \o Qt.RightToLeft - Items will be laid out from right to let.
2193   \endlist
2194 
2195   When using the attached property \l {LayoutMirroring::enabled} for locale layouts,
2196   the layout direction of the horizontal list will be mirrored. However, the actual property
2197   \c layoutDirection will remain unchanged. You can use the property
2198   \l {LayoutMirroring::enabled} to determine whether the direction has been mirrored.
2199 
2200   \sa {LayoutMirroring}{LayoutMirroring}
2201 */
2202 
layoutDirection() const2203 Qt::LayoutDirection QDeclarativeListView::layoutDirection() const
2204 {
2205     Q_D(const QDeclarativeListView);
2206     return d->layoutDirection;
2207 }
2208 
setLayoutDirection(Qt::LayoutDirection layoutDirection)2209 void QDeclarativeListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
2210 {
2211     Q_D(QDeclarativeListView);
2212     if (d->layoutDirection != layoutDirection) {
2213         d->layoutDirection = layoutDirection;
2214         d->regenerate();
2215         emit layoutDirectionChanged();
2216     }
2217 }
2218 
effectiveLayoutDirection() const2219 Qt::LayoutDirection QDeclarativeListView::effectiveLayoutDirection() const
2220 {
2221     Q_D(const QDeclarativeListView);
2222     if (d->effectiveLayoutMirror)
2223         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
2224     else
2225         return d->layoutDirection;
2226 }
2227 
2228 /*!
2229     \qmlproperty bool ListView::keyNavigationWraps
2230     This property holds whether the list wraps key navigation.
2231 
2232     If this is true, key navigation that would move the current item selection
2233     past the end of the list instead wraps around and moves the selection to
2234     the start of the list, and vice-versa.
2235 
2236     By default, key navigation is not wrapped.
2237 */
isWrapEnabled() const2238 bool QDeclarativeListView::isWrapEnabled() const
2239 {
2240     Q_D(const QDeclarativeListView);
2241     return d->wrap;
2242 }
2243 
setWrapEnabled(bool wrap)2244 void QDeclarativeListView::setWrapEnabled(bool wrap)
2245 {
2246     Q_D(QDeclarativeListView);
2247     if (d->wrap == wrap)
2248         return;
2249     d->wrap = wrap;
2250     emit keyNavigationWrapsChanged();
2251 }
2252 
2253 /*!
2254     \qmlproperty int ListView::cacheBuffer
2255     This property determines whether delegates are retained outside the
2256     visible area of the view.
2257 
2258     If this value is non-zero, the view keeps as many delegates
2259     instantiated as it can fit within the buffer specified.  For example,
2260     if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2261     set to 40, then up to 2 delegates above and 2 delegates below the visible
2262     area may be retained.
2263 
2264     Note that cacheBuffer is not a pixel buffer - it only maintains additional
2265     instantiated delegates.
2266 
2267     Setting this value can improve the smoothness of scrolling behavior at the expense
2268     of additional memory usage.  It is not a substitute for creating efficient
2269     delegates; the fewer elements in a delegate, the faster a view can be
2270     scrolled.
2271 */
cacheBuffer() const2272 int QDeclarativeListView::cacheBuffer() const
2273 {
2274     Q_D(const QDeclarativeListView);
2275     return d->buffer;
2276 }
2277 
setCacheBuffer(int b)2278 void QDeclarativeListView::setCacheBuffer(int b)
2279 {
2280     Q_D(QDeclarativeListView);
2281     if (d->buffer != b) {
2282         d->buffer = b;
2283         if (isComponentComplete()) {
2284             d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
2285             refill();
2286         }
2287         emit cacheBufferChanged();
2288     }
2289 }
2290 
2291 /*!
2292     \qmlproperty string ListView::section.property
2293     \qmlproperty enumeration ListView::section.criteria
2294     \qmlproperty Component ListView::section.delegate
2295 
2296     These properties hold the expression to be evaluated for the \l section attached property.
2297 
2298     The \l section attached property enables a ListView to be visually
2299     separated into different parts. These properties determine how sections
2300     are created.
2301 
2302     \c section.property holds the name of the property that is the basis
2303     of each section.
2304 
2305     \c section.criteria holds the criteria for forming each section based on
2306     \c section.property. This value can be one of:
2307 
2308     \list
2309     \o ViewSection.FullString (default) - sections are created based on the
2310     \c section.property value.
2311     \o ViewSection.FirstCharacter - sections are created based on the first
2312     character of the \c section.property value (for example, 'A', 'B', 'C'
2313     sections, etc. for an address book)
2314     \endlist
2315 
2316     \c section.delegate holds the delegate component for each section.
2317 
2318     Each item in the list has attached properties named \c ListView.section,
2319     \c ListView.previousSection and \c ListView.nextSection.  These may be
2320     used to place a section header for related items.
2321 
2322     For example, here is a ListView that displays a list of animals, separated
2323     into sections. Each item in the ListView is placed in a different section
2324     depending on the "size" property of the model item. The \c sectionHeading
2325     delegate component provides the light blue bar that marks the beginning of
2326     each section.
2327 
2328 
2329     \snippet examples/declarative/modelviews/listview/sections.qml 0
2330 
2331     \image qml-listview-sections-example.png
2332 
2333     \note Adding sections to a ListView does not automatically re-order the
2334     list items by the section criteria.
2335     If the model is not ordered by section, then it is possible that
2336     the sections created will not be unique; each boundary between
2337     differing sections will result in a section header being created
2338     even if that section exists elsewhere.
2339 
2340     \sa {Models and Views: ListView Examples}{ListView examples}
2341 */
sectionCriteria()2342 QDeclarativeViewSection *QDeclarativeListView::sectionCriteria()
2343 {
2344     Q_D(QDeclarativeListView);
2345     if (!d->sectionCriteria) {
2346         d->sectionCriteria = new QDeclarativeViewSection(this);
2347         connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2348     }
2349     return d->sectionCriteria;
2350 }
2351 
2352 /*!
2353     \qmlproperty string ListView::currentSection
2354     This property holds the section that is currently at the beginning of the view.
2355 */
currentSection() const2356 QString QDeclarativeListView::currentSection() const
2357 {
2358     Q_D(const QDeclarativeListView);
2359     return d->currentSection;
2360 }
2361 
2362 /*!
2363     \qmlproperty real ListView::highlightMoveSpeed
2364     \qmlproperty int ListView::highlightMoveDuration
2365     \qmlproperty real ListView::highlightResizeSpeed
2366     \qmlproperty int ListView::highlightResizeDuration
2367 
2368     These properties hold the move and resize animation speed of the highlight delegate.
2369 
2370     \l highlightFollowsCurrentItem must be true for these properties
2371     to have effect.
2372 
2373     The default value for the speed properties is 400 pixels/second.
2374     The default value for the duration properties is -1, i.e. the
2375     highlight will take as much time as necessary to move at the set speed.
2376 
2377     These properties have the same characteristics as a SmoothedAnimation.
2378 
2379     \sa highlightFollowsCurrentItem
2380 */
highlightMoveSpeed() const2381 qreal QDeclarativeListView::highlightMoveSpeed() const
2382 {
2383     Q_D(const QDeclarativeListView);\
2384     return d->highlightMoveSpeed;
2385 }
2386 
setHighlightMoveSpeed(qreal speed)2387 void QDeclarativeListView::setHighlightMoveSpeed(qreal speed)
2388 {
2389     Q_D(QDeclarativeListView);\
2390     if (d->highlightMoveSpeed != speed) {
2391         d->highlightMoveSpeed = speed;
2392         if (d->highlightPosAnimator)
2393             d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2394         emit highlightMoveSpeedChanged();
2395     }
2396 }
2397 
highlightMoveDuration() const2398 int QDeclarativeListView::highlightMoveDuration() const
2399 {
2400     Q_D(const QDeclarativeListView);
2401     return d->highlightMoveDuration;
2402 }
2403 
setHighlightMoveDuration(int duration)2404 void QDeclarativeListView::setHighlightMoveDuration(int duration)
2405 {
2406     Q_D(QDeclarativeListView);\
2407     if (d->highlightMoveDuration != duration) {
2408         d->highlightMoveDuration = duration;
2409         if (d->highlightPosAnimator)
2410             d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
2411         emit highlightMoveDurationChanged();
2412     }
2413 }
2414 
highlightResizeSpeed() const2415 qreal QDeclarativeListView::highlightResizeSpeed() const
2416 {
2417     Q_D(const QDeclarativeListView);\
2418     return d->highlightResizeSpeed;
2419 }
2420 
setHighlightResizeSpeed(qreal speed)2421 void QDeclarativeListView::setHighlightResizeSpeed(qreal speed)
2422 {
2423     Q_D(QDeclarativeListView);\
2424     if (d->highlightResizeSpeed != speed) {
2425         d->highlightResizeSpeed = speed;
2426         if (d->highlightSizeAnimator)
2427             d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2428         emit highlightResizeSpeedChanged();
2429     }
2430 }
2431 
highlightResizeDuration() const2432 int QDeclarativeListView::highlightResizeDuration() const
2433 {
2434     Q_D(const QDeclarativeListView);
2435     return d->highlightResizeDuration;
2436 }
2437 
setHighlightResizeDuration(int duration)2438 void QDeclarativeListView::setHighlightResizeDuration(int duration)
2439 {
2440     Q_D(QDeclarativeListView);\
2441     if (d->highlightResizeDuration != duration) {
2442         d->highlightResizeDuration = duration;
2443         if (d->highlightSizeAnimator)
2444             d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2445         emit highlightResizeDurationChanged();
2446     }
2447 }
2448 
2449 /*!
2450     \qmlproperty enumeration ListView::snapMode
2451 
2452     This property determines how the view scrolling will settle following a drag or flick.
2453     The possible values are:
2454 
2455     \list
2456     \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2457     \o ListView.SnapToItem - the view settles with an item aligned with the start of
2458     the view.
2459     \o ListView.SnapOneItem - the view settles no more than one item away from the first
2460     visible item at the time the mouse button is released.  This mode is particularly
2461     useful for moving one page at a time.
2462     \endlist
2463 
2464     \c snapMode does not affect the \l currentIndex.  To update the
2465     \l currentIndex as the list is moved, set \l highlightRangeMode
2466     to \c ListView.StrictlyEnforceRange.
2467 
2468     \sa highlightRangeMode
2469 */
snapMode() const2470 QDeclarativeListView::SnapMode QDeclarativeListView::snapMode() const
2471 {
2472     Q_D(const QDeclarativeListView);
2473     return d->snapMode;
2474 }
2475 
setSnapMode(SnapMode mode)2476 void QDeclarativeListView::setSnapMode(SnapMode mode)
2477 {
2478     Q_D(QDeclarativeListView);
2479     if (d->snapMode != mode) {
2480         d->snapMode = mode;
2481         emit snapModeChanged();
2482     }
2483 }
2484 
2485 /*!
2486     \qmlproperty Component ListView::footer
2487     This property holds the component to use as the footer.
2488 
2489     An instance of the footer component is created for each view.  The
2490     footer is positioned at the end of the view, after any items.
2491 
2492     \sa header
2493 */
footer() const2494 QDeclarativeComponent *QDeclarativeListView::footer() const
2495 {
2496     Q_D(const QDeclarativeListView);
2497     return d->footerComponent;
2498 }
2499 
setFooter(QDeclarativeComponent * footer)2500 void QDeclarativeListView::setFooter(QDeclarativeComponent *footer)
2501 {
2502     Q_D(QDeclarativeListView);
2503     if (d->footerComponent != footer) {
2504         if (d->footer) {
2505             if (scene())
2506                 scene()->removeItem(d->footer->item);
2507             d->footer->item->deleteLater();
2508             delete d->footer;
2509             d->footer = 0;
2510         }
2511         d->footerComponent = footer;
2512         d->minExtentDirty = true;
2513         d->maxExtentDirty = true;
2514         if (isComponentComplete()) {
2515             d->updateFooter();
2516             d->updateViewport();
2517             d->fixupPosition();
2518         }
2519         emit footerChanged();
2520     }
2521 }
2522 
2523 /*!
2524     \qmlproperty Component ListView::header
2525     This property holds the component to use as the header.
2526 
2527     An instance of the header component is created for each view.  The
2528     header is positioned at the beginning of the view, before any items.
2529 
2530     \sa footer
2531 */
header() const2532 QDeclarativeComponent *QDeclarativeListView::header() const
2533 {
2534     Q_D(const QDeclarativeListView);
2535     return d->headerComponent;
2536 }
2537 
setHeader(QDeclarativeComponent * header)2538 void QDeclarativeListView::setHeader(QDeclarativeComponent *header)
2539 {
2540     Q_D(QDeclarativeListView);
2541     if (d->headerComponent != header) {
2542         if (d->header) {
2543             if (scene())
2544                 scene()->removeItem(d->header->item);
2545             d->header->item->deleteLater();
2546             delete d->header;
2547             d->header = 0;
2548         }
2549         d->headerComponent = header;
2550         d->minExtentDirty = true;
2551         d->maxExtentDirty = true;
2552         if (isComponentComplete()) {
2553             d->updateHeader();
2554             d->updateFooter();
2555             d->updateViewport();
2556             d->fixupPosition();
2557         }
2558         emit headerChanged();
2559     }
2560 }
2561 
setContentX(qreal pos)2562 void QDeclarativeListView::setContentX(qreal pos)
2563 {
2564     Q_D(QDeclarativeListView);
2565     // Positioning the view manually should override any current movement state
2566     d->moveReason = QDeclarativeListViewPrivate::Other;
2567     QDeclarativeFlickable::setContentX(pos);
2568 }
2569 
setContentY(qreal pos)2570 void QDeclarativeListView::setContentY(qreal pos)
2571 {
2572     Q_D(QDeclarativeListView);
2573     // Positioning the view manually should override any current movement state
2574     d->moveReason = QDeclarativeListViewPrivate::Other;
2575     QDeclarativeFlickable::setContentY(pos);
2576 }
2577 
event(QEvent * event)2578 bool QDeclarativeListView::event(QEvent *event)
2579 {
2580     Q_D(QDeclarativeListView);
2581     if (event->type() == QEvent::User) {
2582         if (d->layoutScheduled)
2583             d->layout();
2584         return true;
2585     }
2586 
2587     return QDeclarativeFlickable::event(event);
2588 }
2589 
viewportMoved()2590 void QDeclarativeListView::viewportMoved()
2591 {
2592     Q_D(QDeclarativeListView);
2593     QDeclarativeFlickable::viewportMoved();
2594     if (!d->itemCount)
2595         return;
2596     // Recursion can occur due to refill changing the content size.
2597     if (d->inViewportMoved)
2598         return;
2599     d->inViewportMoved = true;
2600     d->lazyRelease = true;
2601     refill();
2602     if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2603         d->moveReason = QDeclarativeListViewPrivate::Mouse;
2604     if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) {
2605         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2606             // reposition highlight
2607             qreal pos = d->highlight->position();
2608             qreal viewPos;
2609             qreal highlightStart;
2610             qreal highlightEnd;
2611             if (d->isRightToLeft()) {
2612                 // Handle Right-To-Left exceptions
2613                 viewPos = -d->position()-d->size();
2614                 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2615                 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2616             } else {
2617                 viewPos = d->position();
2618                 highlightStart = d->highlightRangeStart;
2619                 highlightEnd = d->highlightRangeEnd;
2620             }
2621             if (pos > viewPos + highlightEnd - d->highlight->size())
2622                 pos = viewPos + highlightEnd - d->highlight->size();
2623             if (pos < viewPos + highlightStart)
2624                 pos = viewPos + highlightStart;
2625             d->highlightPosAnimator->stop();
2626             d->highlight->setPosition(qRound(pos));
2627 
2628             // update current index
2629             if (FxListItem *snapItem = d->snapItemAt(d->highlight->position())) {
2630                 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2631                     d->updateCurrent(snapItem->index);
2632             }
2633         }
2634     }
2635 
2636     if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2637         d->inFlickCorrection = true;
2638         // Near an end and it seems that the extent has changed?
2639         // Recalculate the flick so that we don't end up in an odd position.
2640         if (yflick() && !d->vData.inOvershoot) {
2641             if (d->vData.velocity > 0) {
2642                 const qreal minY = minYExtent();
2643                 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2644                     && minY != d->vData.flickTarget)
2645                     d->flickY(-d->vData.smoothVelocity.value());
2646                 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore;
2647             } else if (d->vData.velocity < 0) {
2648                 const qreal maxY = maxYExtent();
2649                 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2650                     && maxY != d->vData.flickTarget)
2651                     d->flickY(-d->vData.smoothVelocity.value());
2652                 d->bufferMode = QDeclarativeListViewPrivate::BufferAfter;
2653             }
2654         }
2655 
2656         if (xflick() && !d->hData.inOvershoot) {
2657             if (d->hData.velocity > 0) {
2658                 const qreal minX = minXExtent();
2659                 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2660                     && minX != d->hData.flickTarget)
2661                     d->flickX(-d->hData.smoothVelocity.value());
2662                 d->bufferMode = d->isRightToLeft()
2663                         ? QDeclarativeListViewPrivate::BufferAfter : QDeclarativeListViewPrivate::BufferBefore;
2664             } else if (d->hData.velocity < 0) {
2665                 const qreal maxX = maxXExtent();
2666                 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2667                     && maxX != d->hData.flickTarget)
2668                     d->flickX(-d->hData.smoothVelocity.value());
2669                 d->bufferMode = d->isRightToLeft()
2670                         ? QDeclarativeListViewPrivate::BufferBefore : QDeclarativeListViewPrivate::BufferAfter;
2671             }
2672         }
2673         d->inFlickCorrection = false;
2674     }
2675     d->inViewportMoved = false;
2676 }
2677 
minYExtent() const2678 qreal QDeclarativeListView::minYExtent() const
2679 {
2680     Q_D(const QDeclarativeListView);
2681     if (d->orient == QDeclarativeListView::Horizontal)
2682         return QDeclarativeFlickable::minYExtent();
2683     if (d->minExtentDirty) {
2684         d->minExtent = -d->startPosition();
2685         if (d->header && d->visibleItems.count())
2686             d->minExtent += d->header->size();
2687         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2688             d->minExtent += d->highlightRangeStart;
2689             if (d->sectionCriteria) {
2690                 if (d->visibleItem(0))
2691                     d->minExtent -= d->visibleItem(0)->sectionSize();
2692             }
2693             d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2694         }
2695         d->minExtentDirty = false;
2696     }
2697 
2698     return d->minExtent;
2699 }
2700 
maxYExtent() const2701 qreal QDeclarativeListView::maxYExtent() const
2702 {
2703     Q_D(const QDeclarativeListView);
2704     if (d->orient == QDeclarativeListView::Horizontal)
2705         return height();
2706     if (d->maxExtentDirty) {
2707         if (!d->model || !d->model->count()) {
2708             d->maxExtent = d->header ? -d->header->size() : 0;
2709             d->maxExtent += height();
2710         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2711             d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2712             if (d->highlightRangeEnd != d->highlightRangeStart)
2713                 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2714         } else {
2715             d->maxExtent = -(d->endPosition() - height() + 1);
2716         }
2717         if (d->footer)
2718             d->maxExtent -= d->footer->size();
2719         qreal minY = minYExtent();
2720         if (d->maxExtent > minY)
2721             d->maxExtent = minY;
2722         d->maxExtentDirty = false;
2723     }
2724     return d->maxExtent;
2725 }
2726 
minXExtent() const2727 qreal QDeclarativeListView::minXExtent() const
2728 {
2729     Q_D(const QDeclarativeListView);
2730     if (d->orient == QDeclarativeListView::Vertical)
2731         return QDeclarativeFlickable::minXExtent();
2732     if (d->minExtentDirty) {
2733         d->minExtent = -d->startPosition();
2734 
2735         qreal highlightStart;
2736         qreal highlightEnd;
2737         qreal endPositionFirstItem = 0;
2738         if (d->isRightToLeft()) {
2739             if (d->model && d->model->count())
2740                 endPositionFirstItem = d->positionAt(d->model->count()-1);
2741             else if (d->header)
2742                 d->minExtent += d->header->size();
2743             highlightStart = d->highlightRangeStartValid
2744                     ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2745                     : d->size() - (d->lastPosition()-endPositionFirstItem);
2746             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2747             if (d->footer)
2748                 d->minExtent += d->footer->size();
2749             qreal maxX = maxXExtent();
2750             if (d->minExtent < maxX)
2751                 d->minExtent = maxX;
2752         } else {
2753             endPositionFirstItem = d->endPositionAt(0);
2754             highlightStart = d->highlightRangeStart;
2755             highlightEnd = d->highlightRangeEnd;
2756             if (d->header && d->visibleItems.count())
2757                 d->minExtent += d->header->size();
2758         }
2759         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2760             d->minExtent += d->isRightToLeft() ? -highlightStart : highlightStart;
2761             d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
2762         }
2763         d->minExtentDirty = false;
2764     }
2765 
2766     return d->minExtent;
2767 }
2768 
maxXExtent() const2769 qreal QDeclarativeListView::maxXExtent() const
2770 {
2771     Q_D(const QDeclarativeListView);
2772     if (d->orient == QDeclarativeListView::Vertical)
2773         return width();
2774     if (d->maxExtentDirty) {
2775         qreal highlightStart;
2776         qreal highlightEnd;
2777         qreal lastItemPosition = 0;
2778         d->maxExtent = 0;
2779         if (d->isRightToLeft()) {
2780             highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2781             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2782             lastItemPosition = d->endPosition();
2783         } else {
2784             highlightStart = d->highlightRangeStart;
2785             highlightEnd = d->highlightRangeEnd;
2786             if (d->model && d->model->count())
2787                 lastItemPosition = d->positionAt(d->model->count()-1);
2788         }
2789         if (!d->model || !d->model->count()) {
2790             if (!d->isRightToLeft())
2791                 d->maxExtent = d->header ? -d->header->size() : 0;
2792             d->maxExtent += width();
2793         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2794             d->maxExtent = -(lastItemPosition - highlightStart);
2795             if (highlightEnd != highlightStart) {
2796                 d->maxExtent = d->isRightToLeft()
2797                         ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
2798                         : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
2799             }
2800         } else {
2801             d->maxExtent = -(d->endPosition() - width() + 1);
2802         }
2803         if (d->isRightToLeft()) {
2804             if (d->header && d->visibleItems.count())
2805                 d->maxExtent -= d->header->size();
2806         } else {
2807             if (d->footer)
2808                 d->maxExtent -= d->footer->size();
2809             qreal minX = minXExtent();
2810             if (d->maxExtent > minX)
2811                 d->maxExtent = minX;
2812         }
2813         d->maxExtentDirty = false;
2814     }
2815     return d->maxExtent;
2816 }
2817 
keyPressEvent(QKeyEvent * event)2818 void QDeclarativeListView::keyPressEvent(QKeyEvent *event)
2819 {
2820     Q_D(QDeclarativeListView);
2821     keyPressPreHandler(event);
2822     if (event->isAccepted())
2823         return;
2824 
2825     if (d->model && d->model->count() && d->interactive) {
2826         if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2827                     || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2828                     || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) {
2829             if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2830                 decrementCurrentIndex();
2831                 event->accept();
2832                 return;
2833             } else if (d->wrap) {
2834                 event->accept();
2835                 return;
2836             }
2837         } else if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2838                     || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2839                     || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) {
2840             if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2841                 incrementCurrentIndex();
2842                 event->accept();
2843                 return;
2844             } else if (d->wrap) {
2845                 event->accept();
2846                 return;
2847             }
2848         }
2849     }
2850     event->ignore();
2851     QDeclarativeFlickable::keyPressEvent(event);
2852 }
2853 
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)2854 void QDeclarativeListView::geometryChanged(const QRectF &newGeometry,
2855                              const QRectF &oldGeometry)
2856 {
2857     Q_D(QDeclarativeListView);
2858     d->maxExtentDirty = true;
2859     d->minExtentDirty = true;
2860     if (d->isRightToLeft() && d->orient == QDeclarativeListView::Horizontal) {
2861         // maintain position relative to the right edge
2862         int dx = newGeometry.width() - oldGeometry.width();
2863         setContentX(contentX() - dx);
2864     }
2865     QDeclarativeFlickable::geometryChanged(newGeometry, oldGeometry);
2866 }
2867 
2868 
2869 /*!
2870     \qmlmethod ListView::incrementCurrentIndex()
2871 
2872     Increments the current index.  The current index will wrap
2873     if keyNavigationWraps is true and it is currently at the end.
2874     This method has no effect if the \l count is zero.
2875 
2876     \bold Note: methods should only be called after the Component has completed.
2877 */
incrementCurrentIndex()2878 void QDeclarativeListView::incrementCurrentIndex()
2879 {
2880     Q_D(QDeclarativeListView);
2881     int count = d->model ? d->model->count() : 0;
2882     if (count && (currentIndex() < count - 1 || d->wrap)) {
2883         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2884         int index = currentIndex()+1;
2885         setCurrentIndex((index >= 0 && index < count) ? index : 0);
2886     }
2887 }
2888 
2889 /*!
2890     \qmlmethod ListView::decrementCurrentIndex()
2891 
2892     Decrements the current index.  The current index will wrap
2893     if keyNavigationWraps is true and it is currently at the beginning.
2894     This method has no effect if the \l count is zero.
2895 
2896     \bold Note: methods should only be called after the Component has completed.
2897 */
decrementCurrentIndex()2898 void QDeclarativeListView::decrementCurrentIndex()
2899 {
2900     Q_D(QDeclarativeListView);
2901     int count = d->model ? d->model->count() : 0;
2902     if (count && (currentIndex() > 0 || d->wrap)) {
2903         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2904         int index = currentIndex()-1;
2905         setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2906     }
2907 }
2908 
positionViewAtIndex(int index,int mode)2909 void QDeclarativeListViewPrivate::positionViewAtIndex(int index, int mode)
2910 {
2911     Q_Q(QDeclarativeListView);
2912     if (!isValid())
2913         return;
2914     if (mode < QDeclarativeListView::Beginning || mode > QDeclarativeListView::Contain)
2915         return;
2916     int idx = qMax(qMin(index, model->count()-1), 0);
2917 
2918     if (layoutScheduled)
2919         layout();
2920     qreal pos = isRightToLeft() ? -position() - size() : position();
2921     FxListItem *item = visibleItem(idx);
2922     qreal maxExtent;
2923     if (orient == QDeclarativeListView::Vertical)
2924         maxExtent = -q->maxYExtent();
2925     else
2926         maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
2927 
2928     if (!item) {
2929         int itemPos = positionAt(idx);
2930         // save the currently visible items in case any of them end up visible again
2931         QList<FxListItem*> oldVisible = visibleItems;
2932         visibleItems.clear();
2933         visiblePos = itemPos;
2934         visibleIndex = idx;
2935         setPosition(qMin(qreal(itemPos), maxExtent));
2936         // now release the reference to all the old visible items.
2937         for (int i = 0; i < oldVisible.count(); ++i)
2938             releaseItem(oldVisible.at(i));
2939         item = visibleItem(idx);
2940     }
2941     if (item) {
2942         const qreal itemPos = item->position();
2943         switch (mode) {
2944         case QDeclarativeListView::Beginning:
2945             pos = itemPos;
2946             if (index < 0 && header)
2947                 pos -= header->size();
2948             break;
2949         case QDeclarativeListView::Center:
2950             pos = itemPos - (size() - item->size())/2;
2951             break;
2952         case QDeclarativeListView::End:
2953             pos = itemPos - size() + item->size();
2954             if (index >= model->count() && footer)
2955                 pos += footer->size();
2956             break;
2957         case QDeclarativeListView::Visible:
2958             if (itemPos > pos + size())
2959                 pos = itemPos - size() + item->size();
2960             else if (item->endPosition() < pos)
2961                 pos = itemPos;
2962             break;
2963         case QDeclarativeListView::Contain:
2964             if (item->endPosition() > pos + size())
2965                 pos = itemPos - size() + item->size();
2966             if (itemPos < pos)
2967                 pos = itemPos;
2968         }
2969         pos = qMin(pos, maxExtent);
2970         qreal minExtent;
2971         if (orient == QDeclarativeListView::Vertical) {
2972             minExtent = -q->minYExtent();
2973         } else {
2974             minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
2975         }
2976         pos = qMax(pos, minExtent);
2977         moveReason = QDeclarativeListViewPrivate::Other;
2978         q->cancelFlick();
2979         setPosition(pos);
2980         if (highlight) {
2981             if (autoHighlight) {
2982                 highlight->setPosition(currentItem->itemPosition());
2983                 highlight->setSize(currentItem->itemSize());
2984             }
2985             updateHighlight();
2986         }
2987     }
2988     fixupPosition();
2989 }
2990 
2991 /*!
2992     \qmlmethod ListView::positionViewAtIndex(int index, PositionMode mode)
2993 
2994     Positions the view such that the \a index is at the position specified by
2995     \a mode:
2996 
2997     \list
2998     \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2999     \o ListView.Center - position item in the center of the view.
3000     \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
3001     \o ListView.Visible - if any part of the item is visible then take no action, otherwise
3002     bring the item into view.
3003     \o ListView.Contain - ensure the entire item is visible.  If the item is larger than
3004     the view the item is positioned at the top (or left for horizontal orientation) of the view.
3005     \endlist
3006 
3007     If positioning the view at \a index would cause empty space to be displayed at
3008     the beginning or end of the view, the view will be positioned at the boundary.
3009 
3010     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3011     at a particular index.  This is unreliable since removing items from the start
3012     of the list does not cause all other items to be repositioned, and because
3013     the actual start of the view can vary based on the size of the delegates.
3014     The correct way to bring an item into view is with \c positionViewAtIndex.
3015 
3016     \bold Note: methods should only be called after the Component has completed.  To position
3017     the view at startup, this method should be called by Component.onCompleted.  For
3018     example, to position the view at the end:
3019 
3020     \code
3021     Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3022     \endcode
3023 */
positionViewAtIndex(int index,int mode)3024 void QDeclarativeListView::positionViewAtIndex(int index, int mode)
3025 {
3026     Q_D(QDeclarativeListView);
3027     if (!d->isValid() || index < 0 || index >= d->model->count())
3028         return;
3029     d->positionViewAtIndex(index, mode);
3030 }
3031 
3032 /*!
3033     \qmlmethod ListView::positionViewAtBeginning()
3034     \qmlmethod ListView::positionViewAtEnd()
3035     \since QtQuick 1.1
3036 
3037     Positions the view at the beginning or end, taking into account any header or footer.
3038 
3039     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3040     at a particular index.  This is unreliable since removing items from the start
3041     of the list does not cause all other items to be repositioned, and because
3042     the actual start of the view can vary based on the size of the delegates.
3043 
3044     \bold Note: methods should only be called after the Component has completed.  To position
3045     the view at startup, this method should be called by Component.onCompleted.  For
3046     example, to position the view at the end on startup:
3047 
3048     \code
3049     Component.onCompleted: positionViewAtEnd()
3050     \endcode
3051 */
positionViewAtBeginning()3052 void QDeclarativeListView::positionViewAtBeginning()
3053 {
3054     Q_D(QDeclarativeListView);
3055     if (!d->isValid())
3056         return;
3057     d->positionViewAtIndex(-1, Beginning);
3058 }
3059 
positionViewAtEnd()3060 void QDeclarativeListView::positionViewAtEnd()
3061 {
3062     Q_D(QDeclarativeListView);
3063     if (!d->isValid())
3064         return;
3065     d->positionViewAtIndex(d->model->count(), End);
3066 }
3067 
3068 /*!
3069     \qmlmethod int ListView::indexAt(int x, int y)
3070 
3071     Returns the index of the visible item containing the point \a x, \a y in content
3072     coordinates.  If there is no item at the point specified, or the item is
3073     not visible -1 is returned.
3074 
3075     If the item is outside the visible area, -1 is returned, regardless of
3076     whether an item will exist at that point when scrolled into view.
3077 
3078     \bold Note: methods should only be called after the Component has completed.
3079 */
indexAt(qreal x,qreal y) const3080 int QDeclarativeListView::indexAt(qreal x, qreal y) const
3081 {
3082     Q_D(const QDeclarativeListView);
3083     for (int i = 0; i < d->visibleItems.count(); ++i) {
3084         const FxListItem *listItem = d->visibleItems.at(i);
3085         if(listItem->contains(x, y))
3086             return listItem->index;
3087     }
3088 
3089     return -1;
3090 }
3091 
componentComplete()3092 void QDeclarativeListView::componentComplete()
3093 {
3094     Q_D(QDeclarativeListView);
3095     QDeclarativeFlickable::componentComplete();
3096     updateSections();
3097     d->updateHeader();
3098     d->updateFooter();
3099     if (d->isValid()) {
3100         refill();
3101         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3102         if (d->currentIndex < 0 && !d->currentIndexCleared)
3103             d->updateCurrent(0);
3104         else
3105             d->updateCurrent(d->currentIndex);
3106         if (d->highlight && d->currentItem) {
3107             if (d->autoHighlight)
3108                 d->highlight->setPosition(d->currentItem->position());
3109             d->updateTrackedItem();
3110         }
3111         d->moveReason = QDeclarativeListViewPrivate::Other;
3112         d->fixupPosition();
3113     }
3114 }
3115 
updateSections()3116 void QDeclarativeListView::updateSections()
3117 {
3118     Q_D(QDeclarativeListView);
3119     if (isComponentComplete() && d->model) {
3120         QList<QByteArray> roles;
3121         if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
3122             roles << d->sectionCriteria->property().toUtf8();
3123         d->model->setWatchedRoles(roles);
3124         d->updateSections();
3125         if (d->itemCount)
3126             d->layout();
3127     }
3128 }
3129 
refill()3130 void QDeclarativeListView::refill()
3131 {
3132     Q_D(QDeclarativeListView);
3133     if (d->isRightToLeft())
3134         d->refill(-d->position()-d->size()+1, -d->position());
3135     else
3136         d->refill(d->position(), d->position()+d->size()-1);
3137 }
3138 
trackedPositionChanged()3139 void QDeclarativeListView::trackedPositionChanged()
3140 {
3141     Q_D(QDeclarativeListView);
3142     if (!d->trackedItem || !d->currentItem)
3143         return;
3144     if (d->moveReason == QDeclarativeListViewPrivate::SetIndex) {
3145         qreal trackedPos = qCeil(d->trackedItem->position());
3146         qreal trackedSize = d->trackedItem->size();
3147         if (d->trackedItem != d->currentItem) {
3148             trackedPos -= d->currentItem->sectionSize();
3149             trackedSize += d->currentItem->sectionSize();
3150         }
3151         qreal viewPos;
3152         qreal highlightStart;
3153         qreal highlightEnd;
3154         if (d->isRightToLeft()) {
3155             viewPos = -d->position()-d->size();
3156             highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
3157             highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
3158         } else {
3159             viewPos = d->position();
3160             highlightStart = d->highlightRangeStart;
3161             highlightEnd = d->highlightRangeEnd;
3162         }
3163         qreal pos = viewPos;
3164         if (d->haveHighlightRange) {
3165             if (d->highlightRange == StrictlyEnforceRange) {
3166                 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
3167                     pos = trackedPos - highlightEnd + d->trackedItem->size();
3168                 if (trackedPos < pos + highlightStart)
3169                     pos = trackedPos - highlightStart;
3170             } else {
3171                 if (trackedPos < d->startPosition() + highlightStart) {
3172                     pos = d->startPosition();
3173                 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
3174                     pos = d->endPosition() - d->size() + 1;
3175                     if (pos < d->startPosition())
3176                         pos = d->startPosition();
3177                 } else {
3178                     if (trackedPos < viewPos + highlightStart) {
3179                         pos = trackedPos - highlightStart;
3180                     } else if (trackedPos > viewPos + highlightEnd - trackedSize) {
3181                         pos = trackedPos - highlightEnd + trackedSize;
3182                     }
3183                 }
3184             }
3185         } else {
3186             if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
3187                 pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
3188             } else if (d->trackedItem->endPosition() >= viewPos + d->size()
3189                         && d->currentItem->endPosition() >= viewPos + d->size()) {
3190                 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
3191                     pos = d->trackedItem->endPosition() - d->size() + 1;
3192                      if (trackedSize > d->size())
3193                         pos = trackedPos;
3194                 } else {
3195                     pos = d->currentItem->endPosition() - d->size() + 1;
3196                     if (d->currentItem->size() > d->size())
3197                         pos = d->currentItem->position();
3198                 }
3199             }
3200         }
3201         if (viewPos != pos) {
3202             cancelFlick();
3203             d->calcVelocity = true;
3204             d->setPosition(pos);
3205             d->calcVelocity = false;
3206         }
3207     }
3208 }
3209 
itemsInserted(int modelIndex,int count)3210 void QDeclarativeListView::itemsInserted(int modelIndex, int count)
3211 {
3212     Q_D(QDeclarativeListView);
3213     if (!isComponentComplete())
3214         return;
3215     d->updateUnrequestedIndexes();
3216     d->moveReason = QDeclarativeListViewPrivate::Other;
3217 
3218     qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
3219     int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
3220 
3221     if (index < 0) {
3222         int i = d->visibleItems.count() - 1;
3223         while (i > 0 && d->visibleItems.at(i)->index == -1)
3224             --i;
3225         if (i == 0 && d->visibleItems.first()->index == -1) {
3226             // there are no visible items except items marked for removal
3227             index = d->visibleItems.count();
3228         } else if (d->visibleItems.at(i)->index + 1 == modelIndex
3229             && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) {
3230             // Special case of appending an item to the model.
3231             index = d->visibleItems.count();
3232         } else {
3233             if (modelIndex < d->visibleIndex) {
3234                 // Insert before visible items
3235                 d->visibleIndex += count;
3236                 for (int i = 0; i < d->visibleItems.count(); ++i) {
3237                     FxListItem *listItem = d->visibleItems.at(i);
3238                     if (listItem->index != -1 && listItem->index >= modelIndex)
3239                         listItem->index += count;
3240                 }
3241             }
3242             if (d->currentIndex >= modelIndex) {
3243                 // adjust current item index
3244                 d->currentIndex += count;
3245                 if (d->currentItem)
3246                     d->currentItem->index = d->currentIndex;
3247                 emit currentIndexChanged();
3248             }
3249             d->scheduleLayout();
3250             d->itemCount += count;
3251             emit countChanged();
3252             return;
3253         }
3254     }
3255 
3256     // index can be the next item past the end of the visible items list (i.e. appended)
3257     int pos = 0;
3258     if (d->visibleItems.count()) {
3259         pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
3260                                                 : d->visibleItems.last()->endPosition()+d->spacing+1;
3261     } else if (d->itemCount == 0 && d->header) {
3262         pos = d->header->size();
3263     }
3264 
3265     int initialPos = pos;
3266     int diff = 0;
3267     QList<FxListItem*> added;
3268     bool addedVisible = false;
3269     FxListItem *firstVisible = d->firstVisibleItem();
3270     if (firstVisible && pos < firstVisible->position()) {
3271         // Insert items before the visible item.
3272         int insertionIdx = index;
3273         int i = 0;
3274         int from = tempPos - d->buffer;
3275         for (i = count-1; i >= 0 && pos > from; --i) {
3276             if (!addedVisible) {
3277                 d->scheduleLayout();
3278                 addedVisible = true;
3279             }
3280             FxListItem *item = d->createItem(modelIndex + i);
3281             if (!item) {
3282                 // broken or no delegate
3283                 d->clear();
3284                 return;
3285             }
3286             d->visibleItems.insert(insertionIdx, item);
3287             pos -= item->size() + d->spacing;
3288             item->setPosition(pos);
3289             index++;
3290         }
3291         if (i >= 0) {
3292             // If we didn't insert all our new items - anything
3293             // before the current index is not visible - remove it.
3294             while (insertionIdx--) {
3295                 FxListItem *item = d->visibleItems.takeFirst();
3296                 if (item->index != -1)
3297                     d->visibleIndex++;
3298                 d->releaseItem(item);
3299             }
3300         } else {
3301             // adjust pos of items before inserted items.
3302             for (int i = insertionIdx-1; i >= 0; i--) {
3303                 FxListItem *listItem = d->visibleItems.at(i);
3304                 listItem->setPosition(listItem->position() - (initialPos - pos));
3305             }
3306         }
3307     } else {
3308         int i = 0;
3309         int to = d->buffer+tempPos+d->size()-1;
3310         for (i = 0; i < count && pos <= to; ++i) {
3311             if (!addedVisible) {
3312                 d->scheduleLayout();
3313                 addedVisible = true;
3314             }
3315             FxListItem *item = d->createItem(modelIndex + i);
3316             if (!item) {
3317                 // broken or no delegate
3318                 d->clear();
3319                 return;
3320             }
3321             d->visibleItems.insert(index, item);
3322             item->setPosition(pos);
3323             added.append(item);
3324             pos += item->size() + d->spacing;
3325             ++index;
3326         }
3327         if (i != count) {
3328             // We didn't insert all our new items, which means anything
3329             // beyond the current index is not visible - remove it.
3330             while (d->visibleItems.count() > index)
3331                 d->releaseItem(d->visibleItems.takeLast());
3332         }
3333         diff = pos - initialPos;
3334     }
3335     if (d->itemCount && d->currentIndex >= modelIndex) {
3336         // adjust current item index
3337         d->currentIndex += count;
3338         if (d->currentItem) {
3339             d->currentItem->index = d->currentIndex;
3340             d->currentItem->setPosition(d->currentItem->position() + diff);
3341         }
3342         emit currentIndexChanged();
3343     } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
3344         d->updateCurrent(0);
3345     }
3346     // Update the indexes of the following visible items.
3347     for (; index < d->visibleItems.count(); ++index) {
3348         FxListItem *listItem = d->visibleItems.at(index);
3349         if (d->currentItem && listItem->item != d->currentItem->item)
3350             listItem->setPosition(listItem->position() + diff);
3351         if (listItem->index != -1)
3352             listItem->index += count;
3353     }
3354     // everything is in order now - emit add() signal
3355     for (int j = 0; j < added.count(); ++j)
3356         added.at(j)->attached->emitAdd();
3357 
3358     d->updateSections();
3359     d->itemCount += count;
3360     emit countChanged();
3361 }
3362 
itemsRemoved(int modelIndex,int count)3363 void QDeclarativeListView::itemsRemoved(int modelIndex, int count)
3364 {
3365     Q_D(QDeclarativeListView);
3366     if (!isComponentComplete())
3367         return;
3368     d->moveReason = QDeclarativeListViewPrivate::Other;
3369     d->updateUnrequestedIndexes();
3370     d->itemCount -= count;
3371 
3372     FxListItem *firstVisible = d->firstVisibleItem();
3373     int preRemovedSize = 0;
3374     bool removedVisible = false;
3375     // Remove the items from the visible list, skipping anything already marked for removal
3376     QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3377     while (it != d->visibleItems.end()) {
3378         FxListItem *item = *it;
3379         if (item->index == -1 || item->index < modelIndex) {
3380             // already removed, or before removed items
3381             ++it;
3382         } else if (item->index >= modelIndex + count) {
3383             // after removed items
3384             item->index -= count;
3385             ++it;
3386         } else {
3387             // removed item
3388             if (!removedVisible) {
3389                 d->scheduleLayout();
3390                 removedVisible = true;
3391             }
3392             item->attached->emitRemove();
3393             if (item->attached->delayRemove()) {
3394                 item->index = -1;
3395                 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
3396                 ++it;
3397             } else {
3398                 if (item == firstVisible)
3399                     firstVisible = 0;
3400                 if (firstVisible && item->position() < firstVisible->position())
3401                     preRemovedSize += item->size();
3402                 it = d->visibleItems.erase(it);
3403                 d->releaseItem(item);
3404             }
3405         }
3406     }
3407 
3408     if (firstVisible && d->visibleItems.first() != firstVisible)
3409         d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
3410 
3411     // fix current
3412     if (d->currentIndex >= modelIndex + count) {
3413         d->currentIndex -= count;
3414         if (d->currentItem)
3415             d->currentItem->index -= count;
3416         emit currentIndexChanged();
3417     } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
3418         // current item has been removed.
3419         d->currentItem->attached->setIsCurrentItem(false);
3420         d->releaseItem(d->currentItem);
3421         d->currentItem = 0;
3422         d->currentIndex = -1;
3423         if (d->itemCount)
3424             d->updateCurrent(qMin(modelIndex, d->itemCount-1));
3425         else
3426             emit currentIndexChanged();
3427     }
3428 
3429     // update visibleIndex
3430     bool haveVisibleIndex = false;
3431     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3432         if ((*it)->index != -1) {
3433             d->visibleIndex = (*it)->index;
3434             haveVisibleIndex = true;
3435             break;
3436         }
3437     }
3438 
3439     if (!haveVisibleIndex) {
3440         d->timeline.clear();
3441         if (removedVisible && d->itemCount == 0) {
3442             d->visibleIndex = 0;
3443             d->visiblePos = d->header ? d->header->size() : 0;
3444             d->setPosition(0);
3445             d->updateHeader();
3446             d->updateFooter();
3447             update();
3448         } else {
3449             if (modelIndex < d->visibleIndex)
3450                 d->visibleIndex = modelIndex+1;
3451             d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
3452         }
3453     }
3454 
3455     d->updateSections();
3456     emit countChanged();
3457 }
3458 
destroyRemoved()3459 void QDeclarativeListView::destroyRemoved()
3460 {
3461     Q_D(QDeclarativeListView);
3462     for (QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3463             it != d->visibleItems.end();) {
3464         FxListItem *listItem = *it;
3465         if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
3466             d->releaseItem(listItem);
3467             it = d->visibleItems.erase(it);
3468         } else {
3469             ++it;
3470         }
3471     }
3472 
3473     // Correct the positioning of the items
3474     d->updateSections();
3475     d->layout();
3476 }
3477 
itemsMoved(int from,int to,int count)3478 void QDeclarativeListView::itemsMoved(int from, int to, int count)
3479 {
3480     Q_D(QDeclarativeListView);
3481     if (!isComponentComplete())
3482         return;
3483     d->updateUnrequestedIndexes();
3484 
3485     if (d->visibleItems.isEmpty()) {
3486         refill();
3487         return;
3488     }
3489 
3490     d->moveReason = QDeclarativeListViewPrivate::Other;
3491     FxListItem *firstVisible = d->firstVisibleItem();
3492     qreal firstItemPos = firstVisible->position();
3493     QHash<int,FxListItem*> moved;
3494     int moveBy = 0;
3495 
3496     QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3497     while (it != d->visibleItems.end()) {
3498         FxListItem *item = *it;
3499         if (item->index >= from && item->index < from + count) {
3500             // take the items that are moving
3501             item->index += (to-from);
3502             moved.insert(item->index, item);
3503             if (item->position() < firstItemPos)
3504                 moveBy += item->size();
3505             it = d->visibleItems.erase(it);
3506         } else {
3507             // move everything after the moved items.
3508             if (item->index > from && item->index != -1)
3509                 item->index -= count;
3510             ++it;
3511         }
3512     }
3513 
3514     int remaining = count;
3515     int endIndex = d->visibleIndex;
3516     it = d->visibleItems.begin();
3517     while (it != d->visibleItems.end()) {
3518         FxListItem *item = *it;
3519         if (remaining && item->index >= to && item->index < to + count) {
3520             // place items in the target position, reusing any existing items
3521             FxListItem *movedItem = moved.take(item->index);
3522             if (!movedItem)
3523                 movedItem = d->createItem(item->index);
3524             if (!movedItem) {
3525                 // broken or no delegate
3526                 d->clear();
3527                 return;
3528             }
3529             if (item->index <= firstVisible->index)
3530                 moveBy -= movedItem->size();
3531             it = d->visibleItems.insert(it, movedItem);
3532             ++it;
3533             --remaining;
3534         } else {
3535             if (item->index != -1) {
3536                 if (item->index >= to) {
3537                     // update everything after the moved items.
3538                     item->index += count;
3539                 }
3540                 endIndex = item->index;
3541             }
3542             ++it;
3543         }
3544     }
3545 
3546     // If we have moved items to the end of the visible items
3547     // then add any existing moved items that we have
3548     while (FxListItem *item = moved.take(endIndex+1)) {
3549         d->visibleItems.append(item);
3550         ++endIndex;
3551     }
3552 
3553     // update visibleIndex
3554     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3555         if ((*it)->index != -1) {
3556             d->visibleIndex = (*it)->index;
3557             break;
3558         }
3559     }
3560 
3561     // Fix current index
3562     if (d->currentIndex >= 0 && d->currentItem) {
3563         int oldCurrent = d->currentIndex;
3564         d->currentIndex = d->model->indexOf(d->currentItem->item, this);
3565         if (oldCurrent != d->currentIndex) {
3566             d->currentItem->index = d->currentIndex;
3567             emit currentIndexChanged();
3568         }
3569     }
3570 
3571     // Whatever moved items remain are no longer visible items.
3572     while (moved.count()) {
3573         int idx = moved.begin().key();
3574         FxListItem *item = moved.take(idx);
3575         if (d->currentItem && item->item == d->currentItem->item)
3576             item->setPosition(d->positionAt(idx));
3577         d->releaseItem(item);
3578     }
3579 
3580     // Ensure we don't cause an ugly list scroll.
3581     if (!d->visibleItems.isEmpty())
3582         d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
3583 
3584     d->updateSections();
3585     d->layout();
3586 }
3587 
itemsChanged(int,int)3588 void QDeclarativeListView::itemsChanged(int, int)
3589 {
3590     Q_D(QDeclarativeListView);
3591     d->updateSections();
3592     d->layout();
3593 }
3594 
modelReset()3595 void QDeclarativeListView::modelReset()
3596 {
3597     Q_D(QDeclarativeListView);
3598     d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3599     d->regenerate();
3600     if (d->highlight && d->currentItem) {
3601         if (d->autoHighlight)
3602             d->highlight->setPosition(d->currentItem->position());
3603         d->updateTrackedItem();
3604     }
3605     d->moveReason = QDeclarativeListViewPrivate::Other;
3606     emit countChanged();
3607 }
3608 
createdItem(int index,QDeclarativeItem * item)3609 void QDeclarativeListView::createdItem(int index, QDeclarativeItem *item)
3610 {
3611     Q_D(QDeclarativeListView);
3612     if (d->requestedIndex != index) {
3613         item->setParentItem(contentItem());
3614         d->unrequestedItems.insert(item, index);
3615         if (d->orient == QDeclarativeListView::Vertical) {
3616             item->setY(d->positionAt(index));
3617         } else {
3618             if (d->isRightToLeft())
3619                 item->setX(-d->positionAt(index)-item->width());
3620             else
3621                 item->setX(d->positionAt(index));
3622         }
3623     }
3624 }
3625 
destroyingItem(QDeclarativeItem * item)3626 void QDeclarativeListView::destroyingItem(QDeclarativeItem *item)
3627 {
3628     Q_D(QDeclarativeListView);
3629     d->unrequestedItems.remove(item);
3630 }
3631 
animStopped()3632 void QDeclarativeListView::animStopped()
3633 {
3634     Q_D(QDeclarativeListView);
3635     d->bufferMode = QDeclarativeListViewPrivate::NoBuffer;
3636     if (d->haveHighlightRange && d->highlightRange == QDeclarativeListView::StrictlyEnforceRange)
3637         d->updateHighlight();
3638 }
3639 
qmlAttachedProperties(QObject * obj)3640 QDeclarativeListViewAttached *QDeclarativeListView::qmlAttachedProperties(QObject *obj)
3641 {
3642     return new QDeclarativeListViewAttached(obj);
3643 }
3644 
3645 QT_END_NAMESPACE
3646