Section headers ignore list delegate size changes when "colliding"
[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->forceLayoutPolish();
1618     }
1619 }
1620
1621 qreal QQuickGridView::cellHeight() const
1622 {
1623     Q_D(const QQuickGridView);
1624     return d->cellHeight;
1625 }
1626
1627 void QQuickGridView::setCellHeight(qreal cellHeight)
1628 {
1629     Q_D(QQuickGridView);
1630     if (cellHeight != d->cellHeight && cellHeight > 0) {
1631         d->cellHeight = qMax(qreal(1), cellHeight);
1632         d->updateViewport();
1633         emit cellHeightChanged();
1634         d->forceLayoutPolish();
1635     }
1636 }
1637 /*!
1638     \qmlproperty enumeration QtQuick2::GridView::snapMode
1639
1640     This property determines how the view scrolling will settle following a drag or flick.
1641     The possible values are:
1642
1643     \list
1644     \li GridView.NoSnap (default) - the view stops anywhere within the visible area.
1645     \li GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow)
1646     aligned with the start of the view.
1647     \li GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow)
1648     away from the first visible row at the time the mouse button is released.
1649     This mode is particularly useful for moving one page at a time.
1650     \endlist
1651
1652 */
1653 QQuickGridView::SnapMode QQuickGridView::snapMode() const
1654 {
1655     Q_D(const QQuickGridView);
1656     return d->snapMode;
1657 }
1658
1659 void QQuickGridView::setSnapMode(SnapMode mode)
1660 {
1661     Q_D(QQuickGridView);
1662     if (d->snapMode != mode) {
1663         d->snapMode = mode;
1664         emit snapModeChanged();
1665     }
1666 }
1667
1668
1669 /*!
1670     \qmlproperty Component QtQuick2::GridView::footer
1671     This property holds the component to use as the footer.
1672
1673     An instance of the footer component is created for each view.  The
1674     footer is positioned at the end of the view, after any items.
1675
1676     \sa header, footerItem
1677 */
1678 /*!
1679     \qmlproperty Component QtQuick2::GridView::header
1680     This property holds the component to use as the header.
1681
1682     An instance of the header component is created for each view.  The
1683     header is positioned at the beginning of the view, before any items.
1684
1685     \sa footer, headerItem
1686 */
1687
1688 /*!
1689     \qmlproperty Item QtQuick2::GridView::headerItem
1690     This holds the header item created from the \l header component.
1691
1692     An instance of the header component is created for each view.  The
1693     header is positioned at the beginning of the view, before any items.
1694
1695     \sa header, footerItem
1696 */
1697
1698 /*!
1699     \qmlproperty Item QtQuick2::GridView::footerItem
1700     This holds the footer item created from the \l footer component.
1701
1702     An instance of the footer component is created for each view.  The
1703     footer is positioned at the end of the view, after any items.
1704
1705     \sa footer, headerItem
1706 */
1707
1708 /*!
1709     \qmlproperty Transition QtQuick2::GridView::populate
1710
1711     This property holds the transition to apply to the items that are initially created
1712     for a view.
1713
1714     It is applied to all items that are created when:
1715
1716     \list
1717     \li The view is first created
1718     \li The view's \l model changes
1719     \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
1720     \endlist
1721
1722     For example, here is a view that specifies such a transition:
1723
1724     \code
1725     GridView {
1726         ...
1727         populate: Transition {
1728             NumberAnimation { properties: "x,y"; duration: 1000 }
1729         }
1730     }
1731     \endcode
1732
1733     When the view is initialized, the view will create all the necessary items for the view,
1734     then animate them to their correct positions within the view over one second.
1735
1736     For more details and examples on how to use view transitions, see the ViewTransition
1737     documentation.
1738
1739     \sa add, ViewTransition
1740 */
1741
1742 /*!
1743     \qmlproperty Transition QtQuick2::GridView::add
1744
1745     This property holds the transition to apply to items that are added to the view.
1746
1747     For example, here is a view that specifies such a transition:
1748
1749     \code
1750     GridView {
1751         ...
1752         add: Transition {
1753             NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
1754         }
1755     }
1756     \endcode
1757
1758     Whenever an item is added to the above view, the item will be animated from the position (100,100)
1759     to its final x,y position within the view, over one second. The transition only applies to
1760     the new items that are added to the view; it does not apply to the items below that are
1761     displaced by the addition of the new items. To animate the displaced items, set the \l displaced
1762     or \l addDisplaced properties.
1763
1764     For more details and examples on how to use view transitions, see the ViewTransition
1765     documentation.
1766
1767     \note This transition is not applied to the items that are created when the view is initially
1768     populated, or when the view's \l model changes. In those cases, the \l populate transition is
1769     applied instead.
1770
1771     \sa addDisplaced, populate, ViewTransition
1772 */
1773
1774 /*!
1775     \qmlproperty Transition QtQuick2::GridView::addDisplaced
1776
1777     This property holds the transition to apply to items within the view that are displaced by
1778     the addition of other items to the view.
1779
1780     For example, here is a view that specifies such a transition:
1781
1782     \code
1783     GridView {
1784         ...
1785         addDisplaced: Transition {
1786             NumberAnimation { properties: "x,y"; duration: 1000 }
1787         }
1788     }
1789     \endcode
1790
1791     Whenever an item is added to the above view, all items beneath the new item are displaced, causing
1792     them to move down (or sideways, if horizontally orientated) within the view. As this
1793     displacement occurs, the items' movement to their new x,y positions within the view will be
1794     animated by a NumberAnimation over one second, as specified. This transition is not applied to
1795     the new item that has been added to the view; to animate the added items, set the \l add
1796     property.
1797
1798     If an item is displaced by multiple types of operations at the same time, it is not defined as to
1799     whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
1800     if it is not necessary to specify different transitions depending on whether an item is displaced
1801     by an add, move or remove operation, consider setting the \l displaced property instead.
1802
1803     For more details and examples on how to use view transitions, see the ViewTransition
1804     documentation.
1805
1806     \note This transition is not applied to the items that are created when the view is initially
1807     populated, or when the view's \l model changes. In those cases, the \l populate transition is
1808     applied instead.
1809
1810     \sa displaced, add, populate, ViewTransition
1811 */
1812 /*!
1813     \qmlproperty Transition QtQuick2::GridView::move
1814
1815     This property holds the transition to apply to items in the view that are being moved due
1816     to a move operation in the view's \l model.
1817
1818     For example, here is a view that specifies such a transition:
1819
1820     \code
1821     GridView {
1822         ...
1823         move: Transition {
1824             NumberAnimation { properties: "x,y"; duration: 1000 }
1825         }
1826     }
1827     \endcode
1828
1829     Whenever the \l model performs a move operation to move a particular set of indexes, the
1830     respective items in the view will be animated to their new positions in the view over one
1831     second. The transition only applies to the items that are the subject of the move operation
1832     in the model; it does not apply to items below them that are displaced by the move operation.
1833     To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
1834
1835     For more details and examples on how to use view transitions, see the ViewTransition
1836     documentation.
1837
1838     \sa moveDisplaced, ViewTransition
1839 */
1840
1841 /*!
1842     \qmlproperty Transition QtQuick2::GridView::moveDisplaced
1843
1844     This property holds the transition to apply to items that are displaced by a move operation in
1845     the view's \l model.
1846
1847     For example, here is a view that specifies such a transition:
1848
1849     \code
1850     GridView {
1851         ...
1852         moveDisplaced: Transition {
1853             NumberAnimation { properties: "x,y"; duration: 1000 }
1854         }
1855     }
1856     \endcode
1857
1858     Whenever the \l model performs a move operation to move a particular set of indexes, the items
1859     between the source and destination indexes of the move operation are displaced, causing them
1860     to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
1861     displacement occurs, the items' movement to their new x,y positions within the view will be
1862     animated by a NumberAnimation over one second, as specified. This transition is not applied to
1863     the items that are the actual subjects of the move operation; to animate the moved items, set
1864     the \l move property.
1865
1866     If an item is displaced by multiple types of operations at the same time, it is not defined as to
1867     whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
1868     if it is not necessary to specify different transitions depending on whether an item is displaced
1869     by an add, move or remove operation, consider setting the \l displaced property instead.
1870
1871     For more details and examples on how to use view transitions, see the ViewTransition
1872     documentation.
1873
1874     \sa displaced, move, ViewTransition
1875 */
1876
1877 /*!
1878     \qmlproperty Transition QtQuick2::GridView::remove
1879
1880     This property holds the transition to apply to items that are removed from the view.
1881
1882     For example, here is a view that specifies such a transition:
1883
1884     \code
1885     GridView {
1886         ...
1887         remove: Transition {
1888             ParallelAnimation {
1889                 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
1890                 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
1891             }
1892         }
1893     }
1894     \endcode
1895
1896     Whenever an item is removed from the above view, the item will be animated to the position (100,100)
1897     over one second, and in parallel will also change its opacity to 0. The transition
1898     only applies to the items that are removed from the view; it does not apply to the items below
1899     them that are displaced by the removal of the items. To animate the displaced items, set the
1900     \l displaced or \l removeDisplaced properties.
1901
1902     Note that by the time the transition is applied, the item has already been removed from the
1903     model; any references to the model data for the removed index will not be valid.
1904
1905     Additionally, if the \l delayRemove attached property has been set for a delegate item, the
1906     remove transition will not be applied until \l delayRemove becomes false again.
1907
1908     For more details and examples on how to use view transitions, see the ViewTransition
1909     documentation.
1910
1911     \sa removeDisplaced, ViewTransition
1912 */
1913
1914 /*!
1915     \qmlproperty Transition QtQuick2::GridView::removeDisplaced
1916
1917     This property holds the transition to apply to items in the view that are displaced by the
1918     removal of other items in the view.
1919
1920     For example, here is a view that specifies such a transition:
1921
1922     \code
1923     GridView {
1924         ...
1925         removeDisplaced: Transition {
1926             NumberAnimation { properties: "x,y"; duration: 1000 }
1927         }
1928     }
1929     \endcode
1930
1931     Whenever an item is removed from the above view, all items beneath it are displaced, causing
1932     them to move upwards (or sideways, if horizontally orientated) within the view. As this
1933     displacement occurs, the items' movement to their new x,y positions within the view will be
1934     animated by a NumberAnimation over one second, as specified. This transition is not applied to
1935     the item that has actually been removed from the view; to animate the removed items, set the
1936     \l remove property.
1937
1938     If an item is displaced by multiple types of operations at the same time, it is not defined as to
1939     whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
1940     if it is not necessary to specify different transitions depending on whether an item is displaced
1941     by an add, move or remove operation, consider setting the \l displaced property instead.
1942
1943     For more details and examples on how to use view transitions, see the ViewTransition
1944     documentation.
1945
1946     \sa displaced, remove, ViewTransition
1947 */
1948
1949 /*!
1950     \qmlproperty Transition QtQuick2::GridView::displaced
1951     This property holds the generic transition to apply to items that have been displaced by
1952     any model operation that affects the view.
1953
1954     This is a convenience for specifying a generic transition for items that are displaced
1955     by add, move or remove operations, without having to specify the individual addDisplaced,
1956     moveDisplaced and removeDisplaced properties. For example, here is a view that specifies
1957     a displaced transition:
1958
1959     \code
1960     GridView {
1961         ...
1962         displaced: Transition {
1963             NumberAnimation { properties: "x,y"; duration: 1000 }
1964         }
1965     }
1966     \endcode
1967
1968     When any item is added, moved or removed within the above view, the items below it are
1969     displaced, causing them to move down (or sideways, if horizontally orientated) within the
1970     view. As this displacement occurs, the items' movement to their new x,y positions within
1971     the view will be animated by a NumberAnimation over one second, as specified.
1972
1973     If a view specifies this generic displaced transition as well as a specific addDisplaced,
1974     moveDisplaced or removeDisplaced transition, the more specific transition will be used
1975     instead of the generic displaced transition when the relevant operation occurs, providing that
1976     the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
1977     to false). If it has indeed been disabled, the generic displaced transition is applied instead.
1978
1979     For more details and examples on how to use view transitions, see the ViewTransition
1980     documentation.
1981
1982     \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
1983 */
1984
1985 void QQuickGridView::viewportMoved(Qt::Orientations orient)
1986 {
1987     Q_D(QQuickGridView);
1988     QQuickItemView::viewportMoved(orient);
1989     if (!d->itemCount)
1990         return;
1991     if (d->inViewportMoved)
1992         return;
1993     d->inViewportMoved = true;
1994
1995     if (yflick()) {
1996         if (d->isContentFlowReversed())
1997             d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
1998         else
1999             d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
2000     } else {
2001         if (d->isContentFlowReversed())
2002             d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
2003         else
2004             d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
2005     }
2006
2007     d->refillOrLayout();
2008
2009     // Set visibility of items to eliminate cost of items outside the visible area.
2010     qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2011     qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2012     for (int i = 0; i < d->visibleItems.count(); ++i) {
2013         FxGridItemSG *item = static_cast<FxGridItemSG*>(d->visibleItems.at(i));
2014         QQuickItemPrivate::get(item->item)->setCulled(item->rowPos() + d->rowSize() < from || item->rowPos() > to);
2015     }
2016     if (d->currentItem) {
2017         FxGridItemSG *item = static_cast<FxGridItemSG*>(d->currentItem);
2018         QQuickItemPrivate::get(item->item)->setCulled(item->rowPos() + d->rowSize() < from || item->rowPos() > to);
2019     }
2020
2021     if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2022         d->moveReason = QQuickGridViewPrivate::Mouse;
2023     if (d->moveReason != QQuickGridViewPrivate::SetIndex) {
2024         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2025             // reposition highlight
2026             qreal pos = d->highlight->position();
2027             qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2028             if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2029                 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2030             if (pos < viewPos + d->highlightRangeStart)
2031                 pos = viewPos + d->highlightRangeStart;
2032
2033             if (pos != d->highlight->position()) {
2034                 d->highlightXAnimator->stop();
2035                 d->highlightYAnimator->stop();
2036                 static_cast<FxGridItemSG*>(d->highlight)->setPosition(static_cast<FxGridItemSG*>(d->highlight)->colPos(), pos);
2037             } else {
2038                 d->updateHighlight();
2039             }
2040
2041             // update current index
2042             int idx = d->snapIndex();
2043             if (idx >= 0 && idx != d->currentIndex) {
2044                 d->updateCurrent(idx);
2045                 if (d->currentItem && static_cast<FxGridItemSG*>(d->currentItem)->colPos() != static_cast<FxGridItemSG*>(d->highlight)->colPos() && d->autoHighlight) {
2046                     if (d->flow == FlowLeftToRight)
2047                         d->highlightXAnimator->to = d->currentItem->itemX();
2048                     else
2049                         d->highlightYAnimator->to = d->currentItem->itemY();
2050                 }
2051             }
2052         }
2053     }
2054
2055     d->inViewportMoved = false;
2056 }
2057
2058 void QQuickGridView::keyPressEvent(QKeyEvent *event)
2059 {
2060     Q_D(QQuickGridView);
2061     if (d->model && d->model->count() && d->interactive) {
2062         d->moveReason = QQuickGridViewPrivate::SetIndex;
2063         int oldCurrent = currentIndex();
2064         switch (event->key()) {
2065         case Qt::Key_Up:
2066             moveCurrentIndexUp();
2067             break;
2068         case Qt::Key_Down:
2069             moveCurrentIndexDown();
2070             break;
2071         case Qt::Key_Left:
2072             moveCurrentIndexLeft();
2073             break;
2074         case Qt::Key_Right:
2075             moveCurrentIndexRight();
2076             break;
2077         default:
2078             break;
2079         }
2080         if (oldCurrent != currentIndex() || d->wrap) {
2081             event->accept();
2082             return;
2083         }
2084     }
2085     event->ignore();
2086     QQuickItemView::keyPressEvent(event);
2087 }
2088
2089 void QQuickGridView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2090 {
2091     Q_D(QQuickGridView);
2092     d->resetColumns();
2093
2094     if (newGeometry.width() != oldGeometry.width()
2095             && newGeometry.height() != oldGeometry.height()) {
2096         d->setPosition(d->position());
2097     } else if (newGeometry.width() != oldGeometry.width()) {
2098         QQuickFlickable::setContentX(d->contentXForPosition(d->position()));
2099     } else if (newGeometry.height() != oldGeometry.height()) {
2100         QQuickFlickable::setContentY(d->contentYForPosition(d->position()));
2101     }
2102
2103     QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2104 }
2105
2106 void QQuickGridView::initItem(int index, QQuickItem *item)
2107 {
2108     QQuickItemView::initItem(index, item);
2109     QQuickGridViewAttached *attached = static_cast<QQuickGridViewAttached *>(
2110             qmlAttachedPropertiesObject<QQuickGridView>(item));
2111     if (attached)
2112         attached->setView(this);
2113 }
2114
2115 /*!
2116     \qmlmethod QtQuick2::GridView::moveCurrentIndexUp()
2117
2118     Move the currentIndex up one item in the view.
2119     The current index will wrap if keyNavigationWraps is true and it
2120     is currently at the end. This method has no effect if the \l count is zero.
2121
2122     \b Note: methods should only be called after the Component has completed.
2123 */
2124
2125
2126 void QQuickGridView::moveCurrentIndexUp()
2127 {
2128     Q_D(QQuickGridView);
2129     const int count = d->model ? d->model->count() : 0;
2130     if (!count)
2131         return;
2132     if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
2133         if (d->flow == QQuickGridView::FlowLeftToRight) {
2134             if (currentIndex() >= d->columns || d->wrap) {
2135                 int index = currentIndex() - d->columns;
2136                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2137             }
2138         } else {
2139             if (currentIndex() > 0 || d->wrap) {
2140                 int index = currentIndex() - 1;
2141                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2142             }
2143         }
2144     } else {
2145         if (d->flow == QQuickGridView::FlowLeftToRight) {
2146             if (currentIndex() < count - d->columns || d->wrap) {
2147                 int index = currentIndex()+d->columns;
2148                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2149             }
2150         } else {
2151             if (currentIndex() < count - 1 || d->wrap) {
2152                 int index = currentIndex() + 1;
2153                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2154             }
2155         }
2156     }
2157 }
2158
2159 /*!
2160     \qmlmethod QtQuick2::GridView::moveCurrentIndexDown()
2161
2162     Move the currentIndex down one item in the view.
2163     The current index will wrap if keyNavigationWraps is true and it
2164     is currently at the end. This method has no effect if the \l count is zero.
2165
2166     \b Note: methods should only be called after the Component has completed.
2167 */
2168 void QQuickGridView::moveCurrentIndexDown()
2169 {
2170     Q_D(QQuickGridView);
2171     const int count = d->model ? d->model->count() : 0;
2172     if (!count)
2173         return;
2174
2175     if (d->verticalLayoutDirection == QQuickItemView::TopToBottom) {
2176         if (d->flow == QQuickGridView::FlowLeftToRight) {
2177             if (currentIndex() < count - d->columns || d->wrap) {
2178                 int index = currentIndex()+d->columns;
2179                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2180             }
2181         } else {
2182             if (currentIndex() < count - 1 || d->wrap) {
2183                 int index = currentIndex() + 1;
2184                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2185             }
2186         }
2187     } else {
2188         if (d->flow == QQuickGridView::FlowLeftToRight) {
2189             if (currentIndex() >= d->columns || d->wrap) {
2190                 int index = currentIndex() - d->columns;
2191                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2192             }
2193         } else {
2194             if (currentIndex() > 0 || d->wrap) {
2195                 int index = currentIndex() - 1;
2196                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2197             }
2198         }
2199     }
2200 }
2201
2202 /*!
2203     \qmlmethod QtQuick2::GridView::moveCurrentIndexLeft()
2204
2205     Move the currentIndex left one item in the view.
2206     The current index will wrap if keyNavigationWraps is true and it
2207     is currently at the end. This method has no effect if the \l count is zero.
2208
2209     \b Note: methods should only be called after the Component has completed.
2210 */
2211 void QQuickGridView::moveCurrentIndexLeft()
2212 {
2213     Q_D(QQuickGridView);
2214     const int count = d->model ? d->model->count() : 0;
2215     if (!count)
2216         return;
2217     if (effectiveLayoutDirection() == Qt::LeftToRight) {
2218         if (d->flow == QQuickGridView::FlowLeftToRight) {
2219             if (currentIndex() > 0 || d->wrap) {
2220                 int index = currentIndex() - 1;
2221                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2222             }
2223         } else {
2224             if (currentIndex() >= d->columns || d->wrap) {
2225                 int index = currentIndex() - d->columns;
2226                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2227             }
2228         }
2229     } else {
2230         if (d->flow == QQuickGridView::FlowLeftToRight) {
2231             if (currentIndex() < count - 1 || d->wrap) {
2232                 int index = currentIndex() + 1;
2233                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2234             }
2235         } else {
2236             if (currentIndex() < count - d->columns || d->wrap) {
2237                 int index = currentIndex() + d->columns;
2238                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2239             }
2240         }
2241     }
2242 }
2243
2244
2245 /*!
2246     \qmlmethod QtQuick2::GridView::moveCurrentIndexRight()
2247
2248     Move the currentIndex right one item in the view.
2249     The current index will wrap if keyNavigationWraps is true and it
2250     is currently at the end. This method has no effect if the \l count is zero.
2251
2252     \b Note: methods should only be called after the Component has completed.
2253 */
2254 void QQuickGridView::moveCurrentIndexRight()
2255 {
2256     Q_D(QQuickGridView);
2257     const int count = d->model ? d->model->count() : 0;
2258     if (!count)
2259         return;
2260     if (effectiveLayoutDirection() == Qt::LeftToRight) {
2261         if (d->flow == QQuickGridView::FlowLeftToRight) {
2262             if (currentIndex() < count - 1 || d->wrap) {
2263                 int index = currentIndex() + 1;
2264                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2265             }
2266         } else {
2267             if (currentIndex() < count - d->columns || d->wrap) {
2268                 int index = currentIndex()+d->columns;
2269                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2270             }
2271         }
2272     } else {
2273         if (d->flow == QQuickGridView::FlowLeftToRight) {
2274             if (currentIndex() > 0 || d->wrap) {
2275                 int index = currentIndex() - 1;
2276                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2277             }
2278         } else {
2279             if (currentIndex() >= d->columns || d->wrap) {
2280                 int index = currentIndex() - d->columns;
2281                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2282             }
2283         }
2284     }
2285 }
2286
2287 bool QQuickGridViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2288 {
2289     Q_Q(QQuickGridView);
2290
2291     int modelIndex = change.index;
2292     int count = change.count;
2293
2294     int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2295
2296     if (index < 0) {
2297         int i = visibleItems.count() - 1;
2298         while (i > 0 && visibleItems.at(i)->index == -1)
2299             --i;
2300         if (visibleItems.at(i)->index + 1 == modelIndex) {
2301             // Special case of appending an item to the model.
2302             index = visibleItems.count();
2303         } else {
2304             if (modelIndex <= visibleIndex) {
2305                 // Insert before visible items
2306                 visibleIndex += count;
2307                 for (int i = 0; i < visibleItems.count(); ++i) {
2308                     FxViewItem *item = visibleItems.at(i);
2309                     if (item->index != -1 && item->index >= modelIndex)
2310                         item->index += count;
2311                 }
2312             }
2313             return true;
2314         }
2315     }
2316
2317     qreal tempPos = isContentFlowReversed() ? -position()-size()+q->width()+1 : position();
2318     qreal colPos = 0;
2319     qreal rowPos = 0;
2320     int colNum = 0;
2321     if (visibleItems.count()) {
2322         if (index < visibleItems.count()) {
2323             FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index));
2324             colPos = gridItem->colPos();
2325             rowPos = gridItem->rowPos();
2326             colNum = qFloor((colPos+colSize()/2) / colSize());
2327         } else {
2328             // appending items to visible list
2329             FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index-1));
2330             rowPos = gridItem->rowPos();
2331             colNum = qFloor((gridItem->colPos()+colSize()/2) / colSize());
2332             if (++colNum >= columns) {
2333                 colNum = 0;
2334                 rowPos += rowSize();
2335             }
2336             colPos = colNum * colSize();
2337         }
2338     }
2339
2340     // Update the indexes of the following visible items.
2341     for (int i = 0; i < visibleItems.count(); ++i) {
2342         FxViewItem *item = visibleItems.at(i);
2343         if (item->index != -1 && item->index >= modelIndex) {
2344             item->index += count;
2345             if (change.isMove())
2346                 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2347             else
2348                 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2349         }
2350     }
2351
2352     int prevVisibleCount = visibleItems.count();
2353     if (insertResult->visiblePos.isValid() && rowPos < insertResult->visiblePos) {
2354         // Insert items before the visible item.
2355         int insertionIdx = index;
2356         int i = count - 1;
2357         int from = tempPos - buffer;
2358
2359         while (i >= 0) {
2360             if (rowPos > from && insertionIdx < visibleIndex) {
2361                 // item won't be visible, just note the size for repositioning
2362                 insertResult->countChangeBeforeVisible++;
2363             } else {
2364                 // item is before first visible e.g. in cache buffer
2365                 FxViewItem *item = 0;
2366                 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2367                     item->index = modelIndex + i;
2368                 if (!item)
2369                     item = createItem(modelIndex + i);
2370                 if (!item)
2371                     return false;
2372
2373                 QQuickItemPrivate::get(item->item)->setCulled(false);
2374                 visibleItems.insert(insertionIdx, item);
2375                 if (insertionIdx == 0)
2376                     insertResult->changedFirstItem = true;
2377                 if (!change.isMove()) {
2378                     addedItems->append(item);
2379                     item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2380                 }
2381                 insertResult->sizeChangesBeforeVisiblePos += rowSize();
2382             }
2383
2384             if (--colNum < 0 ) {
2385                 colNum = columns - 1;
2386                 rowPos -= rowSize();
2387             }
2388             colPos = colNum * colSize();
2389             index++;
2390             i--;
2391         }
2392     } else {
2393         int i = 0;
2394         int to = buffer+tempPos+size()-1;
2395         while (i < count && rowPos <= to + rowSize()*(columns - colNum)/qreal(columns+1)) {
2396             FxViewItem *item = 0;
2397             if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2398                 item->index = modelIndex + i;
2399             bool newItem = !item;
2400             if (!item)
2401                 item = createItem(modelIndex + i);
2402             if (!item)
2403                 return false;
2404
2405             QQuickItemPrivate::get(item->item)->setCulled(false);
2406             visibleItems.insert(index, item);
2407             if (index == 0)
2408                 insertResult->changedFirstItem = true;
2409             if (change.isMove()) {
2410                 // we know this is a move target, since move displaced items that are
2411                 // shuffled into view due to a move would be added in refill()
2412                 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2413                     movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2414             } else {
2415                 addedItems->append(item);
2416                 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2417             }
2418             insertResult->sizeChangesAfterVisiblePos += rowSize();
2419
2420             if (++colNum >= columns) {
2421                 colNum = 0;
2422                 rowPos += rowSize();
2423             }
2424             colPos = colNum * colSize();
2425             ++index;
2426             ++i;
2427         }
2428     }
2429
2430     updateVisibleIndex();
2431
2432     return visibleItems.count() > prevVisibleCount;
2433 }
2434
2435 void QQuickGridViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2436 {
2437     if (!transitioner)
2438         return;
2439
2440     int markerItemIndex = -1;
2441     for (int i=0; i<visibleItems.count(); i++) {
2442         if (visibleItems[i]->index == afterModelIndex) {
2443             markerItemIndex = i;
2444             break;
2445         }
2446     }
2447     if (markerItemIndex < 0)
2448         return;
2449
2450     const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2451     int countItemsRemoved = -(removalResult.sizeChangesAfterVisiblePos / rowSize());
2452
2453     // account for whether first item has changed if < 1 row was removed before visible
2454     int changeBeforeVisible = insertionResult.countChangeBeforeVisible - removalResult.countChangeBeforeVisible;
2455     if (changeBeforeVisible != 0)
2456         countItemsRemoved += (changeBeforeVisible % columns) - (columns - 1);
2457
2458     countItemsRemoved -= removalResult.countChangeAfterVisibleItems;
2459
2460     for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
2461         FxGridItemSG *gridItem = static_cast<FxGridItemSG *>(visibleItems[i]);
2462         if (!gridItem->transitionScheduledOrRunning()) {
2463             qreal origRowPos = gridItem->colPos();
2464             qreal origColPos = gridItem->rowPos();
2465             int indexDiff = gridItem->index - countItemsRemoved;
2466             gridItem->setPosition((indexDiff % columns) * colSize(), (indexDiff / columns) * rowSize());
2467             gridItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2468             gridItem->setPosition(origRowPos, origColPos);
2469         }
2470     }
2471 }
2472
2473 bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) const
2474 {
2475     // If we add or remove items before visible items, a layout may be
2476     // required to ensure item 0 is in the first column.
2477     return modelIndex < visibleIndex;
2478 }
2479
2480 /*!
2481     \qmlmethod QtQuick2::GridView::positionViewAtIndex(int index, PositionMode mode)
2482
2483     Positions the view such that the \a index is at the position specified by
2484     \a mode:
2485
2486     \list
2487     \li GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view.
2488     \li GridView.Center - position item in the center of the view.
2489     \li GridView.End - position item at bottom (or right for horizontal orientation) of the view.
2490     \li GridView.Visible - if any part of the item is visible then take no action, otherwise
2491     bring the item into view.
2492     \li GridView.Contain - ensure the entire item is visible.  If the item is larger than
2493     the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view.
2494     \endlist
2495
2496     If positioning the view at the index would cause empty space to be displayed at
2497     the beginning or end of the view, the view will be positioned at the boundary.
2498
2499     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2500     at a particular index.  This is unreliable since removing items from the start
2501     of the view does not cause all other items to be repositioned.
2502     The correct way to bring an item into view is with \c positionViewAtIndex.
2503
2504     \b Note: methods should only be called after the Component has completed.  To position
2505     the view at startup, this method should be called by Component.onCompleted.  For
2506     example, to position the view at the end:
2507
2508     \code
2509     Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning)
2510     \endcode
2511 */
2512
2513 /*!
2514     \qmlmethod QtQuick2::GridView::positionViewAtBeginning()
2515     \qmlmethod QtQuick2::GridView::positionViewAtEnd()
2516
2517     Positions the view at the beginning or end, taking into account any header or footer.
2518
2519     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2520     at a particular index.  This is unreliable since removing items from the start
2521     of the list does not cause all other items to be repositioned, and because
2522     the actual start of the view can vary based on the size of the delegates.
2523
2524     \b Note: methods should only be called after the Component has completed.  To position
2525     the view at startup, this method should be called by Component.onCompleted.  For
2526     example, to position the view at the end on startup:
2527
2528     \code
2529     Component.onCompleted: positionViewAtEnd()
2530     \endcode
2531 */
2532
2533 /*!
2534     \qmlmethod int QtQuick2::GridView::indexAt(int x, int y)
2535
2536     Returns the index of the visible item containing the point \a x, \a y in content
2537     coordinates.  If there is no item at the point specified, or the item is
2538     not visible -1 is returned.
2539
2540     If the item is outside the visible area, -1 is returned, regardless of
2541     whether an item will exist at that point when scrolled into view.
2542
2543     \b Note: methods should only be called after the Component has completed.
2544 */
2545
2546 /*!
2547     \qmlmethod Item QtQuick2::GridView::itemAt(int x, int y)
2548
2549     Returns the visible item containing the point \a x, \a y in content
2550     coordinates.  If there is no item at the point specified, or the item is
2551     not visible null is returned.
2552
2553     If the item is outside the visible area, null is returned, regardless of
2554     whether an item will exist at that point when scrolled into view.
2555
2556     \b Note: methods should only be called after the Component has completed.
2557 */
2558
2559 QQuickGridViewAttached *QQuickGridView::qmlAttachedProperties(QObject *obj)
2560 {
2561     return new QQuickGridViewAttached(obj);
2562 }
2563
2564 QT_END_NAMESPACE