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