594bbce2201707bff449d3e1d2ccb130e0745b6c
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickgridview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickgridview_p.h"
43 #include "qquickvisualitemmodel_p.h"
44 #include "qquickflickable_p_p.h"
45 #include "qquickitemview_p_p.h"
46
47 #include <private/qdeclarativesmoothedanimation_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) : FxViewItem(i, own), view(v) {
70         attached = static_cast<QQuickGridViewAttached*>(qmlAttachedPropertiesObject<QQuickGridView>(item));
71         if (attached)
72             static_cast<QQuickGridViewAttached*>(attached)->setView(view);
73     }
74
75     ~FxGridItemSG() {}
76
77     qreal position() const {
78         return rowPos();
79     }
80
81     qreal endPosition() const {
82         return endRowPos();
83     }
84
85     qreal size() const {
86         return view->flow() == QQuickGridView::LeftToRight ? view->cellHeight() : view->cellWidth();
87     }
88
89     qreal sectionSize() const {
90         return 0.0;
91     }
92
93     qreal rowPos() const {
94         if (view->flow() == QQuickGridView::LeftToRight)
95             return item->y();
96         else
97             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -view->cellWidth()-item->x() : item->x());
98     }
99
100     qreal colPos() const {
101         if (view->flow() == QQuickGridView::LeftToRight) {
102             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
103                 qreal colSize = view->cellWidth();
104                 int columns = view->width()/colSize;
105                 return colSize * (columns-1) - item->x();
106             } else {
107                 return item->x();
108             }
109         } else {
110             return item->y();
111         }
112     }
113     qreal endRowPos() const {
114         if (view->flow() == QQuickGridView::LeftToRight) {
115             return item->y() + view->cellHeight();
116         } else {
117             if (view->effectiveLayoutDirection() == Qt::RightToLeft)
118                 return -item->x();
119             else
120                 return item->x() + view->cellWidth();
121         }
122     }
123     void setPosition(qreal col, qreal row) {
124         if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
125             if (view->flow() == QQuickGridView::LeftToRight) {
126                 int columns = view->width()/view->cellWidth();
127                 item->setPos(QPointF((view->cellWidth() * (columns-1) - col), row));
128             } else {
129                 item->setPos(QPointF(-view->cellWidth()-row, col));
130             }
131         } else {
132             if (view->flow() == QQuickGridView::LeftToRight)
133                 item->setPos(QPointF(col, row));
134             else
135                 item->setPos(QPointF(row, col));
136         }
137     }
138     bool contains(qreal x, qreal y) const {
139         return (x >= item->x() && x < item->x() + view->cellWidth() &&
140                 y >= item->y() && y < item->y() + view->cellHeight());
141     }
142
143     QQuickGridView *view;
144 };
145
146 //----------------------------------------------------------------------------
147
148 class QQuickGridViewPrivate : public QQuickItemViewPrivate
149 {
150     Q_DECLARE_PUBLIC(QQuickGridView)
151
152 public:
153     virtual Qt::Orientation layoutOrientation() const;
154     virtual bool isContentFlowReversed() const;
155     bool isRightToLeftTopToBottom() const;
156
157     virtual qreal positionAt(int index) const;
158     virtual qreal endPositionAt(int index) const;
159     virtual qreal originPosition() const;
160     virtual qreal lastPosition() const;
161
162     qreal rowSize() const;
163     qreal colSize() const;
164     qreal colPosAt(int modelIndex) const;
165     qreal rowPosAt(int modelIndex) const;
166     qreal snapPosAt(qreal pos) const;
167     FxViewItem *snapItemAt(qreal pos) const;
168     int snapIndex() const;
169
170     virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer);
171     virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
172     virtual void visibleItemsChanged();
173
174     virtual FxViewItem *newViewItem(int index, QQuickItem *item);
175     virtual void repositionPackageItemAt(QQuickItem *item, int index);
176     virtual void resetFirstItemPosition(qreal pos = 0.0);
177     virtual void adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible);
178
179     virtual void createHighlight();
180     virtual void updateHighlight();
181     virtual void resetHighlightPosition();
182
183     virtual void setPosition(qreal pos);
184     virtual void layoutVisibleItems(int fromModelIndex = 0);
185     virtual bool applyInsertionChange(const QDeclarativeChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems);
186     virtual bool needsRefillForAddedOrRemovedIndex(int index) const;
187
188     virtual qreal headerSize() const;
189     virtual qreal footerSize() const;
190     virtual bool showHeaderForIndex(int index) const;
191     virtual bool showFooterForIndex(int index) const;
192     virtual void updateHeader();
193     virtual void updateFooter();
194
195     virtual void changedVisibleIndex(int newIndex);
196     virtual void initializeCurrentItem();
197
198     virtual void updateViewport();
199     virtual void fixupPosition();
200     virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
201     virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
202                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
203
204     QQuickGridView::Flow flow;
205     qreal cellWidth;
206     qreal cellHeight;
207     int columns;
208     QQuickGridView::SnapMode snapMode;
209
210     QSmoothedAnimation *highlightXAnimator;
211     QSmoothedAnimation *highlightYAnimator;
212
213     QQuickGridViewPrivate()
214         : flow(QQuickGridView::LeftToRight)
215         , cellWidth(100), cellHeight(100), columns(1)
216         , snapMode(QQuickGridView::NoSnap)
217         , highlightXAnimator(0), highlightYAnimator(0)
218     {}
219 };
220
221 Qt::Orientation QQuickGridViewPrivate::layoutOrientation() const
222 {
223     return flow == QQuickGridView::LeftToRight ? Qt::Vertical : Qt::Horizontal;
224 }
225
226 bool QQuickGridViewPrivate::isContentFlowReversed() const
227 {
228     return isRightToLeftTopToBottom();
229 }
230
231 bool QQuickGridViewPrivate::isRightToLeftTopToBottom() const
232 {
233     Q_Q(const QQuickGridView);
234     return flow == QQuickGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft;
235 }
236
237 void QQuickGridViewPrivate::changedVisibleIndex(int newIndex)
238 {
239     visibleIndex = newIndex / columns * columns;
240 }
241
242 void QQuickGridViewPrivate::setPosition(qreal pos)
243 {
244     Q_Q(QQuickGridView);
245     if (flow == QQuickGridView::LeftToRight) {
246         q->QQuickFlickable::setContentY(pos);
247         q->QQuickFlickable::setContentX(0);
248     } else {
249         if (q->effectiveLayoutDirection() == Qt::LeftToRight)
250             q->QQuickFlickable::setContentX(pos);
251         else
252             q->QQuickFlickable::setContentX(-pos-size());
253         q->QQuickFlickable::setContentY(0);
254     }
255 }
256
257 qreal QQuickGridViewPrivate::originPosition() const
258 {
259     qreal pos = 0;
260     if (!visibleItems.isEmpty())
261         pos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
262     return pos;
263 }
264
265 qreal QQuickGridViewPrivate::lastPosition() const
266 {
267     qreal pos = 0;
268     if (model && model->count()) {
269         // get end position of last item
270         pos = (rowPosAt(model->count() - 1) + rowSize());
271     }
272     return pos;
273 }
274
275 qreal QQuickGridViewPrivate::positionAt(int index) const
276 {
277     return rowPosAt(index);
278 }
279
280 qreal QQuickGridViewPrivate::endPositionAt(int index) const
281 {
282     return rowPosAt(index) + rowSize();
283 }
284
285 qreal QQuickGridViewPrivate::rowSize() const {
286     return flow == QQuickGridView::LeftToRight ? cellHeight : cellWidth;
287 }
288 qreal QQuickGridViewPrivate::colSize() const {
289     return flow == QQuickGridView::LeftToRight ? cellWidth : cellHeight;
290 }
291
292 qreal QQuickGridViewPrivate::colPosAt(int modelIndex) const
293 {
294     if (FxViewItem *item = visibleItem(modelIndex))
295         return static_cast<FxGridItemSG*>(item)->colPos();
296     if (!visibleItems.isEmpty()) {
297         if (modelIndex == visibleIndex) {
298             FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
299             return firstItem->colPos();
300         } else if (modelIndex < visibleIndex) {
301             int count = (visibleIndex - modelIndex) % columns;
302             int col = static_cast<FxGridItemSG*>(visibleItems.first())->colPos() / colSize();
303             col = (columns - count + col) % columns;
304             return col * colSize();
305         } else {
306             int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns;
307             return static_cast<FxGridItemSG*>(visibleItems.last())->colPos() - count * colSize();
308         }
309     }
310     return (modelIndex % columns) * colSize();
311 }
312
313 qreal QQuickGridViewPrivate::rowPosAt(int modelIndex) const
314 {
315     if (FxViewItem *item = visibleItem(modelIndex))
316         return static_cast<FxGridItemSG*>(item)->rowPos();
317     if (!visibleItems.isEmpty()) {
318         if (modelIndex == visibleIndex) {
319             FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
320             return firstItem->rowPos();
321         } else if (modelIndex < visibleIndex) {
322             FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
323             int firstCol = firstItem->colPos() / colSize();
324             int col = visibleIndex - modelIndex + (columns - firstCol - 1);
325             int rows = col / columns;
326             return firstItem->rowPos() - rows * rowSize();
327         } else {
328             FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
329             int count = modelIndex - lastItem->index;
330             int col = lastItem->colPos() + count * colSize();
331             int rows = col / (columns * colSize());
332             return lastItem->rowPos() + rows * rowSize();
333         }
334     }
335     return (modelIndex / columns) * rowSize();
336 }
337
338
339 qreal QQuickGridViewPrivate::snapPosAt(qreal pos) const
340 {
341     Q_Q(const QQuickGridView);
342     qreal snapPos = 0;
343     if (!visibleItems.isEmpty()) {
344         qreal highlightStart = highlightRangeStart;
345         pos += highlightStart;
346         pos += rowSize()/2;
347         snapPos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
348         snapPos = pos - fmodf(pos - snapPos, qreal(rowSize()));
349         snapPos -= highlightStart;
350         qreal maxExtent;
351         qreal minExtent;
352         if (isRightToLeftTopToBottom()) {
353             maxExtent = q->minXExtent()-size();
354             minExtent = q->maxXExtent()-size();
355         } else {
356             maxExtent = flow == QQuickGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent();
357             minExtent = flow == QQuickGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent();
358         }
359         if (snapPos > maxExtent)
360             snapPos = maxExtent;
361         if (snapPos < minExtent)
362             snapPos = minExtent;
363     }
364     return snapPos;
365 }
366
367 FxViewItem *QQuickGridViewPrivate::snapItemAt(qreal pos) const
368 {
369     for (int i = 0; i < visibleItems.count(); ++i) {
370         FxViewItem *item = visibleItems.at(i);
371         if (item->index == -1)
372             continue;
373         qreal itemTop = item->position();
374         if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos)
375             return item;
376     }
377     return 0;
378 }
379
380 int QQuickGridViewPrivate::snapIndex() const
381 {
382     int index = currentIndex;
383     for (int i = 0; i < visibleItems.count(); ++i) {
384         FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
385         if (item->index == -1)
386             continue;
387         qreal itemTop = item->position();
388         FxGridItemSG *hItem = static_cast<FxGridItemSG*>(highlight);
389         if (itemTop >= hItem->rowPos()-rowSize()/2 && itemTop < hItem->rowPos()+rowSize()/2) {
390             index = item->index;
391             if (item->colPos() >= hItem->colPos()-colSize()/2 && item->colPos() < hItem->colPos()+colSize()/2)
392                 return item->index;
393         }
394     }
395     return index;
396 }
397
398 FxViewItem *QQuickGridViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
399 {
400     Q_Q(QQuickGridView);
401     Q_UNUSED(modelIndex);
402     return new FxGridItemSG(item, q, false);
403 }
404
405 bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer)
406 {
407     qreal colPos = colPosAt(visibleIndex);
408     qreal rowPos = rowPosAt(visibleIndex);
409     if (visibleItems.count()) {
410         FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
411         rowPos = lastItem->rowPos();
412         int colNum = qFloor((lastItem->colPos()+colSize()/2) / colSize());
413         if (++colNum >= columns) {
414             colNum = 0;
415             rowPos += rowSize();
416         }
417         colPos = colNum * colSize();
418     }
419
420     int modelIndex = findLastVisibleIndex();
421     modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
422
423     if (visibleItems.count() && (fillFrom > rowPos + rowSize()*2
424         || fillTo < rowPosAt(visibleIndex) - rowSize())) {
425         // We've jumped more than a page.  Estimate which items are now
426         // visible and fill from there.
427         int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns;
428         for (int i = 0; i < visibleItems.count(); ++i)
429             releaseItem(visibleItems.at(i));
430         visibleItems.clear();
431         modelIndex += count;
432         if (modelIndex >= model->count())
433             modelIndex = model->count() - 1;
434         else if (modelIndex < 0)
435             modelIndex = 0;
436         modelIndex = modelIndex / columns * columns;
437         visibleIndex = modelIndex;
438         colPos = colPosAt(visibleIndex);
439         rowPos = rowPosAt(visibleIndex);
440     }
441
442     int colNum = qFloor((colPos+colSize()/2) / colSize());
443     FxGridItemSG *item = 0;
444     bool changed = false;
445
446     while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) {
447 #ifdef DEBUG_DELEGATE_LIFECYCLE
448         qDebug() << "refill: append item" << modelIndex << colPos << rowPos;
449 #endif
450         if (!(item = static_cast<FxGridItemSG*>(createItem(modelIndex, doBuffer))))
451             break;
452         item->setPosition(colPos, rowPos);
453         item->item->setVisible(!doBuffer);
454         visibleItems.append(item);
455         if (++colNum >= columns) {
456             colNum = 0;
457             rowPos += rowSize();
458         }
459         colPos = colNum * colSize();
460         ++modelIndex;
461         changed = true;
462     }
463
464     if (doBuffer && requestedIndex != -1) // already waiting for an item
465         return changed;
466
467     // Find first column
468     if (visibleItems.count()) {
469         FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
470         rowPos = firstItem->rowPos();
471         colNum = qFloor((firstItem->colPos()+colSize()/2) / colSize());
472         if (--colNum < 0) {
473             colNum = columns - 1;
474             rowPos -= rowSize();
475         }
476     } else {
477         colNum = qFloor((colPos+colSize()/2) / colSize());
478     }
479
480     // Prepend
481     colPos = colNum * colSize();
482     while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){
483 #ifdef DEBUG_DELEGATE_LIFECYCLE
484         qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos;
485 #endif
486         if (!(item = static_cast<FxGridItemSG*>(createItem(visibleIndex-1, doBuffer))))
487             break;
488         --visibleIndex;
489         item->setPosition(colPos, rowPos);
490         item->item->setVisible(!doBuffer);
491         visibleItems.prepend(item);
492         if (--colNum < 0) {
493             colNum = columns-1;
494             rowPos -= rowSize();
495         }
496         colPos = colNum * colSize();
497         changed = true;
498     }
499
500     return changed;
501 }
502
503 bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
504 {
505     FxGridItemSG *item = 0;
506     bool changed = false;
507
508     while (visibleItems.count() > 1
509            && (item = static_cast<FxGridItemSG*>(visibleItems.first()))
510                 && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) {
511         if (item->attached->delayRemove())
512             break;
513 #ifdef DEBUG_DELEGATE_LIFECYCLE
514         qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos();
515 #endif
516         if (item->index != -1)
517             visibleIndex++;
518         visibleItems.removeFirst();
519         releaseItem(item);
520         changed = true;
521     }
522     while (visibleItems.count() > 1
523            && (item = static_cast<FxGridItemSG*>(visibleItems.last()))
524                 && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) {
525         if (item->attached->delayRemove())
526             break;
527 #ifdef DEBUG_DELEGATE_LIFECYCLE
528         qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1;
529 #endif
530         visibleItems.removeLast();
531         releaseItem(item);
532         changed = true;
533     }
534
535     return changed;
536 }
537
538 void QQuickGridViewPrivate::visibleItemsChanged()
539 {
540     updateHeader();
541     updateFooter();
542     updateViewport();
543 }
544
545 void QQuickGridViewPrivate::updateViewport()
546 {
547     Q_Q(QQuickGridView);
548     qreal length = flow == QQuickGridView::LeftToRight ? q->width() : q->height();
549     columns = (int)qMax((length + colSize()/2) / colSize(), qreal(1.));
550     QQuickItemViewPrivate::updateViewport();
551 }
552
553 void QQuickGridViewPrivate::layoutVisibleItems(int fromModelIndex)
554 {
555     if (visibleItems.count()) {
556         const qreal from = isContentFlowReversed() ? -position() - size() : position();
557         const qreal to = isContentFlowReversed() ? -position() : position() + size();
558
559         FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
560         qreal rowPos = firstItem->rowPos();
561         qreal colPos = firstItem->colPos();
562         int col = visibleIndex % columns;
563         if (colPos != col * colSize()) {
564             colPos = col * colSize();
565             firstItem->setPosition(colPos, rowPos);
566             firstItem->item->setVisible(rowPos + rowSize() >= from && rowPos <= to);
567         }
568         for (int i = 1; i < visibleItems.count(); ++i) {
569             FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
570             if (++col >= columns) {
571                 col = 0;
572                 rowPos += rowSize();
573             }
574             colPos = col * colSize();
575             if (item->index >= fromModelIndex) {
576                 item->setPosition(colPos, rowPos);
577                 item->item->setVisible(rowPos + rowSize() >= from && rowPos <= to);
578             }
579         }
580     }
581 }
582
583 void QQuickGridViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
584 {
585     Q_Q(QQuickGridView);
586     qreal pos = position();
587     if (flow == QQuickGridView::LeftToRight) {
588         if (item->y() + item->height() > pos && item->y() < pos + q->height())
589             item->setPos(QPointF(colPosAt(index), rowPosAt(index)));
590     } else {
591         if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
592             if (isRightToLeftTopToBottom())
593                 item->setPos(QPointF(-rowPosAt(index)-item->width(), colPosAt(index)));
594             else
595                 item->setPos(QPointF(rowPosAt(index), colPosAt(index)));
596         }
597     }
598 }
599
600 void QQuickGridViewPrivate::resetFirstItemPosition(qreal pos)
601 {
602     FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.first());
603     item->setPosition(0, pos);
604 }
605
606 void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible)
607 {
608     if (!visibleItems.count())
609         return;
610
611     int moveCount = (forwards - backwards) / rowSize();
612
613     if (changeBeforeVisible)
614         moveCount += (changeBeforeVisible%columns) - (columns - 1);
615
616     FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.first());
617     gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize()));
618 }
619
620 void QQuickGridViewPrivate::createHighlight()
621 {
622     Q_Q(QQuickGridView);
623     bool changed = false;
624     if (highlight) {
625         if (trackedItem == highlight)
626             trackedItem = 0;
627         delete highlight;
628         highlight = 0;
629
630         delete highlightXAnimator;
631         delete highlightYAnimator;
632         highlightXAnimator = 0;
633         highlightYAnimator = 0;
634
635         changed = true;
636     }
637
638     if (currentItem) {
639         QQuickItem *item = createHighlightItem();
640         if (item) {
641             FxGridItemSG *newHighlight = new FxGridItemSG(item, q, true);
642             if (autoHighlight)
643                 resetHighlightPosition();
644             highlightXAnimator = new QSmoothedAnimation(q);
645             highlightXAnimator->target = QDeclarativeProperty(item, QLatin1String("x"));
646             highlightXAnimator->userDuration = highlightMoveDuration;
647             highlightYAnimator = new QSmoothedAnimation(q);
648             highlightYAnimator->target = QDeclarativeProperty(item, QLatin1String("y"));
649             highlightYAnimator->userDuration = highlightMoveDuration;
650
651             highlight = newHighlight;
652             changed = true;
653         }
654     }
655     if (changed)
656         emit q->highlightItemChanged();
657 }
658
659 void QQuickGridViewPrivate::updateHighlight()
660 {
661     applyPendingChanges();
662
663     if ((!currentItem && highlight) || (currentItem && !highlight))
664         createHighlight();
665     bool strictHighlight = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange;
666     if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
667         // auto-update highlight
668         highlightXAnimator->to = currentItem->item->x();
669         highlightYAnimator->to = currentItem->item->y();
670         highlight->item->setWidth(currentItem->item->width());
671         highlight->item->setHeight(currentItem->item->height());
672
673         highlightXAnimator->restart();
674         highlightYAnimator->restart();
675     }
676     updateTrackedItem();
677 }
678
679 void QQuickGridViewPrivate::resetHighlightPosition()
680 {
681     if (highlight && currentItem) {
682         FxGridItemSG *cItem = static_cast<FxGridItemSG*>(currentItem);
683         static_cast<FxGridItemSG*>(highlight)->setPosition(cItem->colPos(), cItem->rowPos());
684     }
685 }
686
687 qreal QQuickGridViewPrivate::headerSize() const
688 {
689     if (!header)
690         return 0.0;
691     return flow == QQuickGridView::LeftToRight ? header->item->height() : header->item->width();
692 }
693
694 qreal QQuickGridViewPrivate::footerSize() const
695 {
696     if (!footer)
697         return 0.0;
698     return flow == QQuickGridView::LeftToRight? footer->item->height() : footer->item->width();
699 }
700
701 bool QQuickGridViewPrivate::showHeaderForIndex(int index) const
702 {
703     return index / columns == 0;
704 }
705
706 bool QQuickGridViewPrivate::showFooterForIndex(int index) const
707 {
708     return index / columns == (model->count()-1) / columns;
709 }
710
711 void QQuickGridViewPrivate::updateFooter()
712 {
713     Q_Q(QQuickGridView);
714     bool created = false;
715     if (!footer) {
716         QQuickItem *item = createComponentItem(footerComponent, true);
717         if (!item)
718             return;
719         item->setZ(1);
720         footer = new FxGridItemSG(item, q, true);
721         created = true;
722     }
723
724     FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(footer);
725     qreal colOffset = 0;
726     qreal rowOffset = 0;
727     if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
728         if (flow == QQuickGridView::TopToBottom)
729             rowOffset = gridItem->item->width() - cellWidth;
730         else
731             colOffset = gridItem->item->width() - cellWidth;
732     }
733     if (visibleItems.count()) {
734         qreal endPos = lastPosition();
735         if (findLastVisibleIndex() == model->count()-1) {
736             gridItem->setPosition(colOffset, endPos + rowOffset);
737         } else {
738             qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size();
739             if (endPos <= visiblePos || gridItem->endPosition() <= endPos + rowOffset)
740                 gridItem->setPosition(colOffset, endPos + rowOffset);
741         }
742     } else {
743         gridItem->setPosition(colOffset, rowOffset);
744     }
745
746     if (created)
747         emit q->footerItemChanged();
748 }
749
750 void QQuickGridViewPrivate::updateHeader()
751 {
752     Q_Q(QQuickGridView);
753     bool created = false;
754     if (!header) {
755         QQuickItem *item = createComponentItem(headerComponent, true);
756         if (!item)
757             return;
758         item->setZ(1);
759         header = new FxGridItemSG(item, q, true);
760         created = true;
761     }
762
763     FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(header);
764     qreal colOffset = 0;
765     qreal rowOffset = -headerSize();
766     if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
767         if (flow == QQuickGridView::TopToBottom)
768             rowOffset += gridItem->item->width()-cellWidth;
769         else
770             colOffset = gridItem->item->width()-cellWidth;
771     }
772     if (visibleItems.count()) {
773         qreal startPos = originPosition();
774         if (visibleIndex == 0) {
775             gridItem->setPosition(colOffset, startPos + rowOffset);
776         } else {
777             qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position();
778             qreal headerPos = isRightToLeftTopToBottom() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos();
779             if (tempPos <= startPos || headerPos > startPos + rowOffset)
780                 gridItem->setPosition(colOffset, startPos + rowOffset);
781         }
782     } else {
783         if (isRightToLeftTopToBottom())
784             gridItem->setPosition(colOffset, rowOffset);
785         else
786             gridItem->setPosition(colOffset, -headerSize());
787     }
788
789     if (created)
790         emit q->headerItemChanged();
791 }
792
793 void QQuickGridViewPrivate::initializeCurrentItem()
794 {
795     if (currentItem && currentIndex >= 0) {
796         FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(currentItem);
797         gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex));
798     }
799 }
800
801 void QQuickGridViewPrivate::fixupPosition()
802 {
803     moveReason = Other;
804     if (flow == QQuickGridView::LeftToRight)
805         fixupY();
806     else
807         fixupX();
808 }
809
810 void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
811 {
812     if ((flow == QQuickGridView::TopToBottom && &data == &vData)
813         || (flow == QQuickGridView::LeftToRight && &data == &hData))
814         return;
815
816     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
817
818     qreal viewPos = isRightToLeftTopToBottom() ? -position()-size() : position();
819
820     bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange;
821     if (snapMode != QQuickGridView::NoSnap) {
822         qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position();
823         if (snapMode == QQuickGridView::SnapOneRow && moveReason == Mouse) {
824             // if we've been dragged < rowSize()/2 then bias towards the next row
825             qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
826             qreal bias = 0;
827             if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2)
828                 bias = rowSize()/2;
829             else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -rowSize()/2)
830                 bias = -rowSize()/2;
831             if (isRightToLeftTopToBottom())
832                 bias = -bias;
833             tempPosition -= bias;
834         }
835         FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
836         if (!topItem && strictHighlightRange && currentItem) {
837             // StrictlyEnforceRange always keeps an item in range
838             updateHighlight();
839             topItem = currentItem;
840         }
841         FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
842         if (!bottomItem && strictHighlightRange && currentItem) {
843             // StrictlyEnforceRange always keeps an item in range
844             updateHighlight();
845             bottomItem = currentItem;
846         }
847         qreal pos;
848         bool isInBounds = -position() > maxExtent && -position() <= minExtent;
849         if (topItem && (isInBounds || strictHighlightRange)) {
850             qreal headerPos = header ? static_cast<FxGridItemSG*>(header)->rowPos() : 0;
851             if (topItem->index == 0 && header && tempPosition+highlightRangeStart < headerPos+headerSize()/2 && !strictHighlightRange) {
852                 pos = isRightToLeftTopToBottom() ? - headerPos + highlightRangeStart - size() : headerPos - highlightRangeStart;
853             } else {
854                 if (isRightToLeftTopToBottom())
855                     pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
856                 else
857                     pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
858             }
859         } else if (bottomItem && isInBounds) {
860             if (isRightToLeftTopToBottom())
861                 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
862             else
863                 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
864         } else {
865             QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
866             return;
867         }
868
869         qreal dist = qAbs(data.move + pos);
870         if (dist > 0) {
871             timeline.reset(data.move);
872             if (fixupMode != Immediate) {
873                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
874                 data.fixingUp = true;
875             } else {
876                 timeline.set(data.move, -pos);
877             }
878             vTime = timeline.time();
879         }
880     } else if (haveHighlightRange && highlightRange == QQuickGridView::StrictlyEnforceRange) {
881         if (currentItem) {
882             updateHighlight();
883             qreal pos = static_cast<FxGridItemSG*>(currentItem)->rowPos();
884             if (viewPos < pos + rowSize() - highlightRangeEnd)
885                 viewPos = pos + rowSize() - highlightRangeEnd;
886             if (viewPos > pos - highlightRangeStart)
887                 viewPos = pos - highlightRangeStart;
888             if (isRightToLeftTopToBottom())
889                 viewPos = -viewPos-size();
890             timeline.reset(data.move);
891             if (viewPos != position()) {
892                 if (fixupMode != Immediate) {
893                     timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
894                     data.fixingUp = true;
895                 } else {
896                     timeline.set(data.move, -viewPos);
897                 }
898             }
899             vTime = timeline.time();
900         }
901     } else {
902         QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
903     }
904     data.inOvershoot = false;
905     fixupMode = Normal;
906 }
907
908 void QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
909                                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
910 {
911     Q_Q(QQuickGridView);
912     data.fixingUp = false;
913     moveReason = Mouse;
914     if ((!haveHighlightRange || highlightRange != QQuickGridView::StrictlyEnforceRange)
915         && snapMode == QQuickGridView::NoSnap) {
916         QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
917         return;
918     }
919     qreal maxDistance = 0;
920     qreal dataValue = isRightToLeftTopToBottom() ? -data.move.value()+size() : data.move.value();
921     // -ve velocity means list is moving up/left
922     if (velocity > 0) {
923         if (data.move.value() < minExtent) {
924             if (snapMode == QQuickGridView::SnapOneRow) {
925                 // if we've been dragged < averageSize/2 then bias towards the next item
926                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
927                 qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0;
928                 if (isRightToLeftTopToBottom())
929                     bias = -bias;
930                 data.flickTarget = -snapPosAt(-dataValue - bias);
931                 maxDistance = qAbs(data.flickTarget - data.move.value());
932                 velocity = maxVelocity;
933             } else {
934                 maxDistance = qAbs(minExtent - data.move.value());
935             }
936         }
937         if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange)
938             data.flickTarget = minExtent;
939     } else {
940         if (data.move.value() > maxExtent) {
941             if (snapMode == QQuickGridView::SnapOneRow) {
942                 // if we've been dragged < averageSize/2 then bias towards the next item
943                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
944                 qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0;
945                 if (isRightToLeftTopToBottom())
946                     bias = -bias;
947                 data.flickTarget = -snapPosAt(-dataValue + bias);
948                 maxDistance = qAbs(data.flickTarget - data.move.value());
949                 velocity = -maxVelocity;
950             } else {
951                 maxDistance = qAbs(maxExtent - data.move.value());
952             }
953         }
954         if (snapMode == QQuickGridView::NoSnap && highlightRange != QQuickGridView::StrictlyEnforceRange)
955             data.flickTarget = maxExtent;
956     }
957     bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
958     if (maxDistance > 0 || overShoot) {
959         // This mode requires the grid to stop exactly on a row boundary.
960         qreal v = velocity;
961         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
962             if (v < 0)
963                 v = -maxVelocity;
964             else
965                 v = maxVelocity;
966         }
967         qreal accel = deceleration;
968         qreal v2 = v * v;
969         qreal overshootDist = 0.0;
970         if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickGridView::SnapOneRow) {
971             // + rowSize()/4 to encourage moving at least one item in the flick direction
972             qreal dist = v2 / (accel * 2.0) + rowSize()/4;
973             dist = qMin(dist, maxDistance);
974             if (v > 0)
975                 dist = -dist;
976             if (snapMode != QQuickGridView::SnapOneRow) {
977                 qreal distTemp = isRightToLeftTopToBottom() ? -dist : dist;
978                 data.flickTarget = -snapPosAt(-dataValue + distTemp);
979             }
980             data.flickTarget = isRightToLeftTopToBottom() ? -data.flickTarget+size() : data.flickTarget;
981             if (overShoot) {
982                 if (data.flickTarget >= minExtent) {
983                     overshootDist = overShootDistance(vSize);
984                     data.flickTarget += overshootDist;
985                 } else if (data.flickTarget <= maxExtent) {
986                     overshootDist = overShootDistance(vSize);
987                     data.flickTarget -= overshootDist;
988                 }
989             }
990             qreal adjDist = -data.flickTarget + data.move.value();
991             if (qAbs(adjDist) > qAbs(dist)) {
992                 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
993                 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
994                 if (adjv2 > v2) {
995                     v2 = adjv2;
996                     v = qSqrt(v2);
997                     if (dist > 0)
998                         v = -v;
999                 }
1000             }
1001             dist = adjDist;
1002             accel = v2 / (2.0f * qAbs(dist));
1003         } else {
1004             data.flickTarget = velocity > 0 ? minExtent : maxExtent;
1005             overshootDist = overShoot ? overShootDistance(vSize) : 0;
1006         }
1007         timeline.reset(data.move);
1008         timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1009         timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1010         if (!hData.flicking && q->xflick()) {
1011             hData.flicking = true;
1012             emit q->flickingChanged();
1013             emit q->flickingHorizontallyChanged();
1014             emit q->flickStarted();
1015         }
1016         if (!vData.flicking && q->yflick()) {
1017             vData.flicking = true;
1018             emit q->flickingChanged();
1019             emit q->flickingVerticallyChanged();
1020             emit q->flickStarted();
1021         }
1022     } else {
1023         timeline.reset(data.move);
1024         fixup(data, minExtent, maxExtent);
1025     }
1026 }
1027
1028
1029 //----------------------------------------------------------------------------
1030 /*!
1031     \qmlclass GridView QQuickGridView
1032     \inqmlmodule QtQuick 2
1033     \ingroup qml-view-elements
1034
1035     \inherits Flickable
1036     \brief The GridView item provides a grid view of items provided by a model.
1037
1038     A GridView displays data from models created from built-in QML elements like ListModel
1039     and XmlListModel, or custom model classes defined in C++ that inherit from
1040     QAbstractListModel.
1041
1042     A GridView has a \l model, which defines the data to be displayed, and
1043     a \l delegate, which defines how the data should be displayed. Items in a
1044     GridView are laid out horizontally or vertically. Grid views are inherently flickable
1045     as GridView inherits from \l Flickable.
1046
1047     \section1 Example Usage
1048
1049     The following example shows the definition of a simple list model defined
1050     in a file called \c ContactModel.qml:
1051
1052     \snippet doc/src/snippets/declarative/gridview/ContactModel.qml 0
1053
1054     \div {class="float-right"}
1055     \inlineimage gridview-simple.png
1056     \enddiv
1057
1058     This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules}
1059     for more information about creating reusable components like this.
1060
1061     Another component can display this model data in a GridView, as in the following
1062     example, which creates a \c ContactModel component for its model, and a \l Column element
1063     (containing \l Image and \l Text elements) for its delegate.
1064
1065     \clearfloat
1066     \snippet doc/src/snippets/declarative/gridview/gridview.qml import
1067     \codeline
1068     \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs simple
1069
1070     \div {class="float-right"}
1071     \inlineimage gridview-highlight.png
1072     \enddiv
1073
1074     The view will create a new delegate for each item in the model. Note that the delegate
1075     is able to access the model's \c name and \c portrait data directly.
1076
1077     An improved grid view is shown below. The delegate is visually improved and is moved
1078     into a separate \c contactDelegate component.
1079
1080     \clearfloat
1081     \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs advanced
1082
1083     The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1084     and \c focus is set to \c true to enable keyboard navigation for the grid view.
1085     The grid view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1086
1087     Delegates are instantiated as needed and may be destroyed at any time.
1088     State should \e never be stored in a delegate.
1089
1090     GridView attaches a number of properties to the root item of the delegate, for example
1091     \c {GridView.isCurrentItem}.  In the following example, the root delegate item can access
1092     this attached property directly as \c GridView.isCurrentItem, while the child
1093     \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem.
1094
1095     \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem
1096
1097     \note Views do not set the \l{Item::}{clip} property automatically.
1098     If the view is not clipped by another item or the screen, it will be necessary
1099     to set this property to true in order to clip the items that are partially or
1100     fully outside the view.
1101
1102     \sa {declarative/modelviews/gridview}{GridView example}
1103 */
1104
1105 QQuickGridView::QQuickGridView(QQuickItem *parent)
1106     : QQuickItemView(*(new QQuickGridViewPrivate), parent)
1107 {
1108 }
1109
1110 QQuickGridView::~QQuickGridView()
1111 {
1112 }
1113
1114 void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
1115 {
1116     Q_D(QQuickGridView);
1117     if (d->autoHighlight != autoHighlight) {
1118         if (!autoHighlight && d->highlightXAnimator) {
1119             d->highlightXAnimator->stop();
1120             d->highlightYAnimator->stop();
1121         }
1122         QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1123     }
1124 }
1125
1126 /*!
1127     \qmlattachedproperty bool QtQuick2::GridView::isCurrentItem
1128     This attached property is true if this delegate is the current item; otherwise false.
1129
1130     It is attached to each instance of the delegate.
1131 */
1132
1133 /*!
1134     \qmlattachedproperty GridView QtQuick2::GridView::view
1135     This attached property holds the view that manages this delegate instance.
1136
1137     It is attached to each instance of the delegate.
1138
1139     \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem
1140 */
1141
1142 /*!
1143     \qmlattachedproperty bool QtQuick2::GridView::delayRemove
1144     This attached property holds whether the delegate may be destroyed.
1145
1146     It is attached to each instance of the delegate.
1147
1148     It is sometimes necessary to delay the destruction of an item
1149     until an animation completes.
1150
1151     The example below ensures that the animation completes before
1152     the item is removed from the grid.
1153
1154     \snippet doc/src/snippets/declarative/gridview/gridview.qml delayRemove
1155 */
1156
1157 /*!
1158     \qmlattachedsignal QtQuick2::GridView::onAdd()
1159     This attached handler is called immediately after an item is added to the view.
1160 */
1161
1162 /*!
1163     \qmlattachedsignal QtQuick2::GridView::onRemove()
1164     This attached handler is called immediately before an item is removed from the view.
1165 */
1166
1167
1168 /*!
1169   \qmlproperty model QtQuick2::GridView::model
1170   This property holds the model providing data for the grid.
1171
1172     The model provides the set of data that is used to create the items
1173     in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1174     or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1175     used, it must be a subclass of \l QAbstractItemModel or a simple list.
1176
1177   \sa {qmlmodels}{Data Models}
1178 */
1179
1180 /*!
1181     \qmlproperty Component QtQuick2::GridView::delegate
1182
1183     The delegate provides a template defining each item instantiated by the view.
1184     The index is exposed as an accessible \c index property.  Properties of the
1185     model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1186
1187     The number of elements in the delegate has a direct effect on the
1188     flicking performance of the view.  If at all possible, place functionality
1189     that is not needed for the normal display of the delegate in a \l Loader which
1190     can load additional elements when needed.
1191
1192     The GridView will layout the items based on the size of the root item
1193     in the delegate.
1194
1195     \note Delegates are instantiated as needed and may be destroyed at any time.
1196     State should \e never be stored in a delegate.
1197 */
1198
1199 /*!
1200   \qmlproperty int QtQuick2::GridView::currentIndex
1201   \qmlproperty Item QtQuick2::GridView::currentItem
1202
1203     The \c currentIndex property holds the index of the current item, and
1204     \c currentItem holds the current item.  Setting the currentIndex to -1
1205     will clear the highlight and set currentItem to null.
1206
1207     If highlightFollowsCurrentItem is \c true, setting either of these
1208     properties will smoothly scroll the GridView so that the current
1209     item becomes visible.
1210
1211     Note that the position of the current item
1212     may only be approximate until it becomes visible in the view.
1213 */
1214
1215
1216 /*!
1217   \qmlproperty Item QtQuick2::GridView::highlightItem
1218
1219   This holds the highlight item created from the \l highlight component.
1220
1221   The highlightItem is managed by the view unless
1222   \l highlightFollowsCurrentItem is set to false.
1223
1224   \sa highlight, highlightFollowsCurrentItem
1225 */
1226
1227
1228 /*!
1229   \qmlproperty int QtQuick2::GridView::count
1230   This property holds the number of items in the view.
1231 */
1232
1233
1234 /*!
1235   \qmlproperty Component QtQuick2::GridView::highlight
1236   This property holds the component to use as the highlight.
1237
1238   An instance of the highlight component is created for each view.
1239   The geometry of the resulting component instance will be managed by the view
1240   so as to stay with the current item, unless the highlightFollowsCurrentItem property is false.
1241
1242   \sa highlightItem, highlightFollowsCurrentItem
1243 */
1244
1245 /*!
1246   \qmlproperty bool QtQuick2::GridView::highlightFollowsCurrentItem
1247   This property sets whether the highlight is managed by the view.
1248
1249     If this property is true (the default value), the highlight is moved smoothly
1250     to follow the current item.  Otherwise, the
1251     highlight is not moved by the view, and any movement must be implemented
1252     by the highlight.
1253
1254     Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1255
1256     \snippet doc/src/snippets/declarative/gridview/gridview.qml highlightFollowsCurrentItem
1257 */
1258
1259
1260 /*!
1261     \qmlproperty int QtQuick2::GridView::highlightMoveDuration
1262     This property holds the move animation duration of the highlight delegate.
1263
1264     highlightFollowsCurrentItem must be true for this property
1265     to have effect.
1266
1267     The default value for the duration is 150ms.
1268
1269     \sa highlightFollowsCurrentItem
1270 */
1271
1272 /*!
1273     \qmlproperty real QtQuick2::GridView::preferredHighlightBegin
1274     \qmlproperty real QtQuick2::GridView::preferredHighlightEnd
1275     \qmlproperty enumeration QtQuick2::GridView::highlightRangeMode
1276
1277     These properties define the preferred range of the highlight (for the current item)
1278     within the view. The \c preferredHighlightBegin value must be less than the
1279     \c preferredHighlightEnd value.
1280
1281     These properties affect the position of the current item when the view is scrolled.
1282     For example, if the currently selected item should stay in the middle of the
1283     view when it is scrolled, set the \c preferredHighlightBegin and
1284     \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1285     item would be. If the \c currentItem is changed programmatically, the view will
1286     automatically scroll so that the current item is in the middle of the view.
1287     Furthermore, the behavior of the current item index will occur whether or not a
1288     highlight exists.
1289
1290     Valid values for \c highlightRangeMode are:
1291
1292     \list
1293     \o GridView.ApplyRange - the view attempts to maintain the highlight within the range.
1294        However, the highlight can move outside of the range at the ends of the view or due
1295        to mouse interaction.
1296     \o GridView.StrictlyEnforceRange - the highlight never moves outside of the range.
1297        The current item changes if a keyboard or mouse action would cause the highlight to move
1298        outside of the range.
1299     \o GridView.NoHighlightRange - this is the default value.
1300     \endlist
1301 */
1302
1303
1304 /*!
1305   \qmlproperty enumeration QtQuick2::GridView::layoutDirection
1306   This property holds the layout direction of the grid.
1307
1308     Possible values:
1309
1310   \list
1311   \o Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is
1312   dependent on the \l GridView::flow property.
1313   \o Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent
1314   on the \l GridView::flow property.
1315   \endlist
1316
1317   \bold Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if
1318   GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply
1319   indicates that the flow is horizontal.
1320 */
1321
1322
1323 /*!
1324     \qmlproperty enumeration QtQuick2::GridView::effectiveLayoutDirection
1325     This property holds the effective layout direction of the grid.
1326
1327     When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1328     the visual layout direction of the grid will be mirrored. However, the
1329     property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged.
1330
1331     \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1332 */
1333 /*!
1334   \qmlproperty bool QtQuick2::GridView::keyNavigationWraps
1335   This property holds whether the grid wraps key navigation
1336
1337     If this is true, key navigation that would move the current item selection
1338     past one end of the view instead wraps around and moves the selection to
1339     the other end of the view.
1340
1341     By default, key navigation is not wrapped.
1342 */
1343 /*!
1344     \qmlproperty int QtQuick2::GridView::cacheBuffer
1345     This property determines whether delegates are retained outside the
1346     visible area of the view.
1347
1348     If non-zero the view may keep as many delegates
1349     instantiated as will fit within the buffer specified.  For example,
1350     if in a vertical view the delegate is 20 pixels high, there are 3
1351     columns and \c cacheBuffer is
1352     set to 40, then up to 6 delegates above and 6 delegates below the visible
1353     area may be created/retained.  The buffered delegates are created asynchronously,
1354     allowing creation to occur across multiple frames and reducing the
1355     likelihood of skipping frames.  In order to improve painting performance
1356     delegates outside the visible area have their \l visible property set to
1357     false until they are moved into the visible area.
1358
1359     Note that cacheBuffer is not a pixel buffer - it only maintains additional
1360     instantiated delegates.
1361
1362     Setting this value can make scrolling the list smoother at the expense
1363     of additional memory usage.  It is not a substitute for creating efficient
1364     delegates; the fewer elements in a delegate, the faster a view may be
1365     scrolled.
1366 */
1367 void QQuickGridView::setHighlightMoveDuration(int duration)
1368 {
1369     Q_D(QQuickGridView);
1370     if (d->highlightMoveDuration != duration) {
1371         if (d->highlightYAnimator) {
1372             d->highlightXAnimator->userDuration = duration;
1373             d->highlightYAnimator->userDuration = duration;
1374         }
1375         QQuickItemView::setHighlightMoveDuration(duration);
1376     }
1377 }
1378
1379 /*!
1380   \qmlproperty enumeration QtQuick2::GridView::flow
1381   This property holds the flow of the grid.
1382
1383     Possible values:
1384
1385     \list
1386     \o GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically
1387     \o GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally
1388     \endlist
1389 */
1390 QQuickGridView::Flow QQuickGridView::flow() const
1391 {
1392     Q_D(const QQuickGridView);
1393     return d->flow;
1394 }
1395
1396 void QQuickGridView::setFlow(Flow flow)
1397 {
1398     Q_D(QQuickGridView);
1399     if (d->flow != flow) {
1400         d->flow = flow;
1401         if (d->flow == LeftToRight) {
1402             setContentWidth(-1);
1403             setFlickableDirection(VerticalFlick);
1404         } else {
1405             setContentHeight(-1);
1406             setFlickableDirection(HorizontalFlick);
1407         }
1408         setContentX(0);
1409         setContentY(0);
1410         d->regenerate();
1411         emit flowChanged();
1412     }
1413 }
1414
1415
1416 /*!
1417   \qmlproperty real QtQuick2::GridView::cellWidth
1418   \qmlproperty real QtQuick2::GridView::cellHeight
1419
1420   These properties holds the width and height of each cell in the grid.
1421
1422   The default cell size is 100x100.
1423 */
1424 qreal QQuickGridView::cellWidth() const
1425 {
1426     Q_D(const QQuickGridView);
1427     return d->cellWidth;
1428 }
1429
1430 void QQuickGridView::setCellWidth(qreal cellWidth)
1431 {
1432     Q_D(QQuickGridView);
1433     if (cellWidth != d->cellWidth && cellWidth > 0) {
1434         d->cellWidth = qMax(qreal(1), cellWidth);
1435         d->updateViewport();
1436         emit cellWidthChanged();
1437         d->forceLayout = true;
1438         polish();
1439     }
1440 }
1441
1442 qreal QQuickGridView::cellHeight() const
1443 {
1444     Q_D(const QQuickGridView);
1445     return d->cellHeight;
1446 }
1447
1448 void QQuickGridView::setCellHeight(qreal cellHeight)
1449 {
1450     Q_D(QQuickGridView);
1451     if (cellHeight != d->cellHeight && cellHeight > 0) {
1452         d->cellHeight = qMax(qreal(1), cellHeight);
1453         d->updateViewport();
1454         emit cellHeightChanged();
1455         d->forceLayout = true;
1456         polish();
1457     }
1458 }
1459 /*!
1460     \qmlproperty enumeration QtQuick2::GridView::snapMode
1461
1462     This property determines how the view scrolling will settle following a drag or flick.
1463     The possible values are:
1464
1465     \list
1466     \o GridView.NoSnap (default) - the view stops anywhere within the visible area.
1467     \o GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow)
1468     aligned with the start of the view.
1469     \o GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow)
1470     away from the first visible row at the time the mouse button is released.
1471     This mode is particularly useful for moving one page at a time.
1472     \endlist
1473
1474 */
1475 QQuickGridView::SnapMode QQuickGridView::snapMode() const
1476 {
1477     Q_D(const QQuickGridView);
1478     return d->snapMode;
1479 }
1480
1481 void QQuickGridView::setSnapMode(SnapMode mode)
1482 {
1483     Q_D(QQuickGridView);
1484     if (d->snapMode != mode) {
1485         d->snapMode = mode;
1486         emit snapModeChanged();
1487     }
1488 }
1489
1490
1491 /*!
1492     \qmlproperty Component QtQuick2::GridView::footer
1493     This property holds the component to use as the footer.
1494
1495     An instance of the footer component is created for each view.  The
1496     footer is positioned at the end of the view, after any items.
1497
1498     \sa header, footerItem
1499 */
1500 /*!
1501     \qmlproperty Component QtQuick2::GridView::header
1502     This property holds the component to use as the header.
1503
1504     An instance of the header component is created for each view.  The
1505     header is positioned at the beginning of the view, before any items.
1506
1507     \sa footer, headerItem
1508 */
1509
1510 /*!
1511     \qmlproperty Item QtQuick2::GridView::headerItem
1512     This holds the header item created from the \l header component.
1513
1514     An instance of the header component is created for each view.  The
1515     header is positioned at the beginning of the view, before any items.
1516
1517     \sa header, footerItem
1518 */
1519
1520 /*!
1521     \qmlproperty Item QtQuick2::GridView::footerItem
1522     This holds the footer item created from the \l footer component.
1523
1524     An instance of the footer component is created for each view.  The
1525     footer is positioned at the end of the view, after any items.
1526
1527     \sa footer, headerItem
1528 */
1529
1530 void QQuickGridView::viewportMoved()
1531 {
1532     Q_D(QQuickGridView);
1533     QQuickItemView::viewportMoved();
1534     if (!d->itemCount)
1535         return;
1536     if (d->inViewportMoved)
1537         return;
1538     d->inViewportMoved = true;
1539
1540     if (yflick())
1541         d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
1542     else if (d->isRightToLeftTopToBottom())
1543         d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferAfter : QQuickItemViewPrivate::BufferBefore;
1544     else
1545         d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickItemViewPrivate::BufferBefore : QQuickItemViewPrivate::BufferAfter;
1546
1547     d->refill();
1548
1549     // Set visibility of items to eliminate cost of items outside the visible area.
1550     qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1551     qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
1552     for (int i = 0; i < d->visibleItems.count(); ++i) {
1553         FxGridItemSG *item = static_cast<FxGridItemSG*>(d->visibleItems.at(i));
1554         item->item->setVisible(item->rowPos() + d->rowSize() >= from && item->rowPos() <= to);
1555     }
1556
1557     if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
1558         d->moveReason = QQuickGridViewPrivate::Mouse;
1559     if (d->moveReason != QQuickGridViewPrivate::SetIndex) {
1560         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
1561             // reposition highlight
1562             qreal pos = d->highlight->position();
1563             qreal viewPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size() : d->position();
1564             if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
1565                 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
1566             if (pos < viewPos + d->highlightRangeStart)
1567                 pos = viewPos + d->highlightRangeStart;
1568
1569             if (pos != d->highlight->position()) {
1570                 d->highlightXAnimator->stop();
1571                 d->highlightYAnimator->stop();
1572                 static_cast<FxGridItemSG*>(d->highlight)->setPosition(static_cast<FxGridItemSG*>(d->highlight)->colPos(), pos);
1573             } else {
1574                 d->updateHighlight();
1575             }
1576
1577             // update current index
1578             int idx = d->snapIndex();
1579             if (idx >= 0 && idx != d->currentIndex) {
1580                 d->updateCurrent(idx);
1581                 if (d->currentItem && static_cast<FxGridItemSG*>(d->currentItem)->colPos() != static_cast<FxGridItemSG*>(d->highlight)->colPos() && d->autoHighlight) {
1582                     if (d->flow == LeftToRight)
1583                         d->highlightXAnimator->to = d->currentItem->item->x();
1584                     else
1585                         d->highlightYAnimator->to = d->currentItem->item->y();
1586                 }
1587             }
1588         }
1589     }
1590
1591     d->inViewportMoved = false;
1592 }
1593
1594 void QQuickGridView::keyPressEvent(QKeyEvent *event)
1595 {
1596     Q_D(QQuickGridView);
1597     if (d->model && d->model->count() && d->interactive) {
1598         d->moveReason = QQuickGridViewPrivate::SetIndex;
1599         int oldCurrent = currentIndex();
1600         switch (event->key()) {
1601         case Qt::Key_Up:
1602             moveCurrentIndexUp();
1603             break;
1604         case Qt::Key_Down:
1605             moveCurrentIndexDown();
1606             break;
1607         case Qt::Key_Left:
1608             moveCurrentIndexLeft();
1609             break;
1610         case Qt::Key_Right:
1611             moveCurrentIndexRight();
1612             break;
1613         default:
1614             break;
1615         }
1616         if (oldCurrent != currentIndex()) {
1617             event->accept();
1618             return;
1619         }
1620     }
1621     event->ignore();
1622     QQuickItemView::keyPressEvent(event);
1623 }
1624 /*!
1625     \qmlmethod QtQuick2::GridView::moveCurrentIndexUp()
1626
1627     Move the currentIndex up one item in the view.
1628     The current index will wrap if keyNavigationWraps is true and it
1629     is currently at the end. This method has no effect if the \l count is zero.
1630
1631     \bold Note: methods should only be called after the Component has completed.
1632 */
1633
1634
1635 void QQuickGridView::moveCurrentIndexUp()
1636 {
1637     Q_D(QQuickGridView);
1638     const int count = d->model ? d->model->count() : 0;
1639     if (!count)
1640         return;
1641     if (d->flow == QQuickGridView::LeftToRight) {
1642         if (currentIndex() >= d->columns || d->wrap) {
1643             int index = currentIndex() - d->columns;
1644             setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1645         }
1646     } else {
1647         if (currentIndex() > 0 || d->wrap) {
1648             int index = currentIndex() - 1;
1649             setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1650         }
1651     }
1652 }
1653
1654 /*!
1655     \qmlmethod QtQuick2::GridView::moveCurrentIndexDown()
1656
1657     Move the currentIndex down one item in the view.
1658     The current index will wrap if keyNavigationWraps is true and it
1659     is currently at the end. This method has no effect if the \l count is zero.
1660
1661     \bold Note: methods should only be called after the Component has completed.
1662 */
1663 void QQuickGridView::moveCurrentIndexDown()
1664 {
1665     Q_D(QQuickGridView);
1666     const int count = d->model ? d->model->count() : 0;
1667     if (!count)
1668         return;
1669     if (d->flow == QQuickGridView::LeftToRight) {
1670         if (currentIndex() < count - d->columns || d->wrap) {
1671             int index = currentIndex()+d->columns;
1672             setCurrentIndex((index >= 0 && index < count) ? index : 0);
1673         }
1674     } else {
1675         if (currentIndex() < count - 1 || d->wrap) {
1676             int index = currentIndex() + 1;
1677             setCurrentIndex((index >= 0 && index < count) ? index : 0);
1678         }
1679     }
1680 }
1681
1682 /*!
1683     \qmlmethod QtQuick2::GridView::moveCurrentIndexLeft()
1684
1685     Move the currentIndex left one item in the view.
1686     The current index will wrap if keyNavigationWraps is true and it
1687     is currently at the end. This method has no effect if the \l count is zero.
1688
1689     \bold Note: methods should only be called after the Component has completed.
1690 */
1691 void QQuickGridView::moveCurrentIndexLeft()
1692 {
1693     Q_D(QQuickGridView);
1694     const int count = d->model ? d->model->count() : 0;
1695     if (!count)
1696         return;
1697     if (effectiveLayoutDirection() == Qt::LeftToRight) {
1698         if (d->flow == QQuickGridView::LeftToRight) {
1699             if (currentIndex() > 0 || d->wrap) {
1700                 int index = currentIndex() - 1;
1701                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1702             }
1703         } else {
1704             if (currentIndex() >= d->columns || d->wrap) {
1705                 int index = currentIndex() - d->columns;
1706                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1707             }
1708         }
1709     } else {
1710         if (d->flow == QQuickGridView::LeftToRight) {
1711             if (currentIndex() < count - 1 || d->wrap) {
1712                 int index = currentIndex() + 1;
1713                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1714             }
1715         } else {
1716             if (currentIndex() < count - d->columns || d->wrap) {
1717                 int index = currentIndex() + d->columns;
1718                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1719             }
1720         }
1721     }
1722 }
1723
1724
1725 /*!
1726     \qmlmethod QtQuick2::GridView::moveCurrentIndexRight()
1727
1728     Move the currentIndex right one item in the view.
1729     The current index will wrap if keyNavigationWraps is true and it
1730     is currently at the end. This method has no effect if the \l count is zero.
1731
1732     \bold Note: methods should only be called after the Component has completed.
1733 */
1734 void QQuickGridView::moveCurrentIndexRight()
1735 {
1736     Q_D(QQuickGridView);
1737     const int count = d->model ? d->model->count() : 0;
1738     if (!count)
1739         return;
1740     if (effectiveLayoutDirection() == Qt::LeftToRight) {
1741         if (d->flow == QQuickGridView::LeftToRight) {
1742             if (currentIndex() < count - 1 || d->wrap) {
1743                 int index = currentIndex() + 1;
1744                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1745             }
1746         } else {
1747             if (currentIndex() < count - d->columns || d->wrap) {
1748                 int index = currentIndex()+d->columns;
1749                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1750             }
1751         }
1752     } else {
1753         if (d->flow == QQuickGridView::LeftToRight) {
1754             if (currentIndex() > 0 || d->wrap) {
1755                 int index = currentIndex() - 1;
1756                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1757             }
1758         } else {
1759             if (currentIndex() >= d->columns || d->wrap) {
1760                 int index = currentIndex() - d->columns;
1761                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1762             }
1763         }
1764     }
1765 }
1766
1767 bool QQuickGridViewPrivate::applyInsertionChange(const QDeclarativeChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems)
1768 {
1769     Q_Q(QQuickGridView);
1770
1771     int modelIndex = change.index;
1772     int count = change.count;
1773
1774     int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
1775
1776     if (index < 0) {
1777         int i = visibleItems.count() - 1;
1778         while (i > 0 && visibleItems.at(i)->index == -1)
1779             --i;
1780         if (visibleItems.at(i)->index + 1 == modelIndex) {
1781             // Special case of appending an item to the model.
1782             index = visibleItems.count();
1783         } else {
1784             if (modelIndex <= visibleIndex) {
1785                 // Insert before visible items
1786                 visibleIndex += count;
1787                 for (int i = 0; i < visibleItems.count(); ++i) {
1788                     FxViewItem *item = visibleItems.at(i);
1789                     if (item->index != -1 && item->index >= modelIndex)
1790                         item->index += count;
1791                 }
1792             }
1793             return true;
1794         }
1795     }
1796
1797     qreal tempPos = isRightToLeftTopToBottom() ? -position()-size()+q->width()+1 : position();
1798     qreal colPos = 0;
1799     qreal rowPos = 0;
1800     int colNum = 0;
1801     if (visibleItems.count()) {
1802         if (index < visibleItems.count()) {
1803             FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index));
1804             colPos = gridItem->colPos();
1805             rowPos = gridItem->rowPos();
1806             colNum = qFloor((colPos+colSize()/2) / colSize());
1807         } else {
1808             // appending items to visible list
1809             FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(visibleItems.at(index-1));
1810             rowPos = gridItem->rowPos();
1811             colNum = qFloor((gridItem->colPos()+colSize()/2) / colSize());
1812             if (++colNum >= columns) {
1813                 colNum = 0;
1814                 rowPos += rowSize();
1815             }
1816             colPos = colNum * colSize();
1817         }
1818     }
1819
1820     // Update the indexes of the following visible items.
1821     for (int i = 0; i < visibleItems.count(); ++i) {
1822         FxViewItem *item = visibleItems.at(i);
1823         if (item->index != -1 && item->index >= modelIndex)
1824             item->index += count;
1825     }
1826
1827     int prevVisibleCount = visibleItems.count();
1828     if (insertResult->visiblePos.isValid() && rowPos < insertResult->visiblePos) {
1829         // Insert items before the visible item.
1830         int insertionIdx = index;
1831         int i = count - 1;
1832         int from = tempPos - buffer;
1833
1834         while (i >= 0) {
1835             if (rowPos > from && insertionIdx < visibleIndex) {
1836                 // item won't be visible, just note the size for repositioning
1837                 insertResult->changeBeforeVisible++;
1838             } else {
1839                 // item is before first visible e.g. in cache buffer
1840                 FxViewItem *item = 0;
1841                 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
1842                     item->index = modelIndex + i;
1843                 if (!item)
1844                     item = createItem(modelIndex + i);
1845                 if (!item)
1846                     return false;
1847
1848                 item->item->setVisible(true);
1849                 visibleItems.insert(insertionIdx, item);
1850                 if (!change.isMove())
1851                     addedItems->append(item);
1852                 insertResult->sizeChangesBeforeVisiblePos += rowSize();
1853             }
1854
1855             if (--colNum < 0 ) {
1856                 colNum = columns - 1;
1857                 rowPos -= rowSize();
1858             }
1859             colPos = colNum * colSize();
1860             index++;
1861             i--;
1862         }
1863     } else {
1864         int i = 0;
1865         int to = buffer+tempPos+size()-1;
1866         while (i < count && rowPos <= to + rowSize()*(columns - colNum)/qreal(columns+1)) {
1867             FxViewItem *item = 0;
1868             if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
1869                 item->index = modelIndex + i;
1870             if (!item)
1871                 item = createItem(modelIndex + i);
1872             if (!item)
1873                 return false;
1874
1875             item->item->setVisible(true);
1876             visibleItems.insert(index, item);
1877             if (index == 0)
1878                 insertResult->changedFirstItem = true;
1879             if (!change.isMove())
1880                 addedItems->append(item);
1881             insertResult->sizeChangesAfterVisiblePos += rowSize();
1882
1883             if (++colNum >= columns) {
1884                 colNum = 0;
1885                 rowPos += rowSize();
1886             }
1887             colPos = colNum * colSize();
1888             ++index;
1889             ++i;
1890         }
1891     }
1892
1893     updateVisibleIndex();
1894
1895     return visibleItems.count() > prevVisibleCount;
1896 }
1897
1898 bool QQuickGridViewPrivate::needsRefillForAddedOrRemovedIndex(int modelIndex) const
1899 {
1900     // If we add or remove items before visible items, a layout may be
1901     // required to ensure item 0 is in the first column.
1902     return modelIndex < visibleIndex;
1903 }
1904
1905 /*!
1906     \qmlmethod QtQuick2::GridView::positionViewAtIndex(int index, PositionMode mode)
1907
1908     Positions the view such that the \a index is at the position specified by
1909     \a mode:
1910
1911     \list
1912     \o GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view.
1913     \o GridView.Center - position item in the center of the view.
1914     \o GridView.End - position item at bottom (or right for horizontal orientation) of the view.
1915     \o GridView.Visible - if any part of the item is visible then take no action, otherwise
1916     bring the item into view.
1917     \o GridView.Contain - ensure the entire item is visible.  If the item is larger than
1918     the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view.
1919     \endlist
1920
1921     If positioning the view at the index would cause empty space to be displayed at
1922     the beginning or end of the view, the view will be positioned at the boundary.
1923
1924     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
1925     at a particular index.  This is unreliable since removing items from the start
1926     of the view does not cause all other items to be repositioned.
1927     The correct way to bring an item into view is with \c positionViewAtIndex.
1928
1929     \bold Note: methods should only be called after the Component has completed.  To position
1930     the view at startup, this method should be called by Component.onCompleted.  For
1931     example, to position the view at the end:
1932
1933     \code
1934     Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning)
1935     \endcode
1936 */
1937
1938 /*!
1939     \qmlmethod QtQuick2::GridView::positionViewAtBeginning()
1940     \qmlmethod QtQuick2::GridView::positionViewAtEnd()
1941
1942     Positions the view at the beginning or end, taking into account any header or footer.
1943
1944     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
1945     at a particular index.  This is unreliable since removing items from the start
1946     of the list does not cause all other items to be repositioned, and because
1947     the actual start of the view can vary based on the size of the delegates.
1948
1949     \bold Note: methods should only be called after the Component has completed.  To position
1950     the view at startup, this method should be called by Component.onCompleted.  For
1951     example, to position the view at the end on startup:
1952
1953     \code
1954     Component.onCompleted: positionViewAtEnd()
1955     \endcode
1956 */
1957
1958 /*!
1959     \qmlmethod int QtQuick2::GridView::indexAt(int x, int y)
1960
1961     Returns the index of the visible item containing the point \a x, \a y in content
1962     coordinates.  If there is no item at the point specified, or the item is
1963     not visible -1 is returned.
1964
1965     If the item is outside the visible area, -1 is returned, regardless of
1966     whether an item will exist at that point when scrolled into view.
1967
1968     \bold Note: methods should only be called after the Component has completed.
1969 */
1970
1971 /*!
1972     \qmlmethod Item QtQuick2::GridView::itemAt(int x, int y)
1973
1974     Returns the visible item containing the point \a x, \a y in content
1975     coordinates.  If there is no item at the point specified, or the item is
1976     not visible null is returned.
1977
1978     If the item is outside the visible area, null is returned, regardless of
1979     whether an item will exist at that point when scrolled into view.
1980
1981     \bold Note: methods should only be called after the Component has completed.
1982 */
1983
1984 QQuickGridViewAttached *QQuickGridView::qmlAttachedProperties(QObject *obj)
1985 {
1986     return new QQuickGridViewAttached(obj);
1987 }
1988
1989 QT_END_NAMESPACE