Show header/footer if current index is set to first/last item or row
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsggridview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsggridview_p.h"
43 #include "qsgvisualitemmodel_p.h"
44 #include "qsgflickable_p_p.h"
45 #include "qsgitemview_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
55 QT_BEGIN_NAMESPACE
56
57 //----------------------------------------------------------------------------
58
59 class FxGridItemSG : public FxViewItem
60 {
61 public:
62     FxGridItemSG(QSGItem *i, QSGGridView *v, bool own) : FxViewItem(i, own), view(v) {
63         attached = static_cast<QSGGridViewAttached*>(qmlAttachedPropertiesObject<QSGGridView>(item));
64         if (attached)
65             static_cast<QSGGridViewAttached*>(attached)->setView(view);
66     }
67
68     ~FxGridItemSG() {}
69
70     qreal position() const {
71         return rowPos();
72     }
73
74     qreal endPosition() const {
75         return endRowPos();
76     }
77
78     qreal size() const {
79         return view->flow() == QSGGridView::LeftToRight ? view->cellHeight() : view->cellWidth();
80     }
81
82     qreal sectionSize() const {
83         return 0.0;
84     }
85
86     qreal rowPos() const {
87         if (view->flow() == QSGGridView::LeftToRight)
88             return item->y();
89         else
90             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -view->cellWidth()-item->x() : item->x());
91     }
92
93     qreal colPos() const {
94         if (view->flow() == QSGGridView::LeftToRight) {
95             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
96                 int colSize = view->cellWidth();
97                 int columns = view->width()/colSize;
98                 return colSize * (columns-1) - item->x();
99             } else {
100                 return item->x();
101             }
102         } else {
103             return item->y();
104         }
105     }
106     qreal endRowPos() const {
107         if (view->flow() == QSGGridView::LeftToRight) {
108             return item->y() + view->cellHeight();
109         } else {
110             if (view->effectiveLayoutDirection() == Qt::RightToLeft)
111                 return -item->x();
112             else
113                 return item->x() + view->cellWidth();
114         }
115     }
116     void setPosition(qreal col, qreal row) {
117         if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
118             if (view->flow() == QSGGridView::LeftToRight) {
119                 int columns = view->width()/view->cellWidth();
120                 item->setPos(QPointF((view->cellWidth() * (columns-1) - col), row));
121             } else {
122                 item->setPos(QPointF(-view->cellWidth()-row, col));
123             }
124         } else {
125             if (view->flow() == QSGGridView::LeftToRight)
126                 item->setPos(QPointF(col, row));
127             else
128                 item->setPos(QPointF(row, col));
129         }
130     }
131     bool contains(qreal x, qreal y) const {
132         return (x >= item->x() && x < item->x() + view->cellWidth() &&
133                 y >= item->y() && y < item->y() + view->cellHeight());
134     }
135
136     QSGGridView *view;
137 };
138
139 //----------------------------------------------------------------------------
140
141 class QSGGridViewPrivate : public QSGItemViewPrivate
142 {
143     Q_DECLARE_PUBLIC(QSGGridView)
144
145 public:
146     virtual Qt::Orientation layoutOrientation() const;
147     virtual bool isContentFlowReversed() const;
148     bool isRightToLeftTopToBottom() const;
149
150     virtual qreal positionAt(int index) const;
151     virtual qreal endPositionAt(int index) const;
152     virtual qreal originPosition() const;
153     virtual qreal lastPosition() const;
154
155     int rowSize() const;
156     int colSize() const;
157     qreal colPosAt(int modelIndex) const;
158     qreal rowPosAt(int modelIndex) const;
159     qreal snapPosAt(qreal pos) const;
160     FxViewItem *snapItemAt(qreal pos) const;
161     int snapIndex() const;
162
163     virtual bool addVisibleItems(int fillFrom, int fillTo, bool doBuffer);
164     virtual bool removeNonVisibleItems(int bufferFrom, int bufferTo);
165     virtual void visibleItemsChanged();
166
167     virtual FxViewItem *newViewItem(int index, QSGItem *item);
168     virtual void repositionPackageItemAt(QSGItem *item, int index);
169
170     virtual void createHighlight();
171     virtual void updateHighlight();
172     virtual void resetHighlightPosition();
173
174     virtual void setPosition(qreal pos);
175     virtual void layoutVisibleItems();
176
177     virtual qreal headerSize() const;
178     virtual qreal footerSize() const;
179     virtual bool showHeaderForIndex(int index) const;
180     virtual bool showFooterForIndex(int index) const;
181     virtual void updateHeader();
182     virtual void updateFooter();
183
184     virtual void changedVisibleIndex(int newIndex);
185     virtual void initializeCurrentItem();
186
187     virtual void updateViewport();
188     virtual void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
189     virtual void fixupPosition();
190     virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
191     virtual void flick(QSGItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
192                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
193
194     QSGGridView::Flow flow;
195     int cellWidth;
196     int cellHeight;
197     int columns;
198     QSGGridView::SnapMode snapMode;
199
200     QSmoothedAnimation *highlightXAnimator;
201     QSmoothedAnimation *highlightYAnimator;
202
203     QSGGridViewPrivate()
204         : flow(QSGGridView::LeftToRight)
205         , cellWidth(100), cellHeight(100), columns(1)
206         , snapMode(QSGGridView::NoSnap)
207         , highlightXAnimator(0), highlightYAnimator(0)
208     {}
209 };
210
211 Qt::Orientation QSGGridViewPrivate::layoutOrientation() const
212 {
213     return flow == QSGGridView::LeftToRight ? Qt::Vertical : Qt::Horizontal;
214 }
215
216 bool QSGGridViewPrivate::isContentFlowReversed() const
217 {
218     return isRightToLeftTopToBottom();
219 }
220
221 bool QSGGridViewPrivate::isRightToLeftTopToBottom() const
222 {
223     Q_Q(const QSGGridView);
224     return flow == QSGGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft;
225 }
226
227 void QSGGridViewPrivate::changedVisibleIndex(int newIndex)
228 {
229     visibleIndex = newIndex / columns * columns;
230 }
231
232 void QSGGridViewPrivate::setPosition(qreal pos)
233 {
234     Q_Q(QSGGridView);
235     if (flow == QSGGridView::LeftToRight) {
236         q->QSGFlickable::setContentY(pos);
237         q->QSGFlickable::setContentX(0);
238     } else {
239         if (q->effectiveLayoutDirection() == Qt::LeftToRight)
240             q->QSGFlickable::setContentX(pos);
241         else
242             q->QSGFlickable::setContentX(-pos-size());
243         q->QSGFlickable::setContentY(0);
244     }
245 }
246
247 qreal QSGGridViewPrivate::originPosition() const
248 {
249     qreal pos = 0;
250     if (!visibleItems.isEmpty())
251         pos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
252     return pos;
253 }
254
255 qreal QSGGridViewPrivate::lastPosition() const
256 {
257     qreal pos = 0;
258     if (model && model->count()) {
259         // get end position of last item
260         pos = (rowPosAt(model->count() - 1) + rowSize());
261     }
262     return pos;
263 }
264
265 qreal QSGGridViewPrivate::positionAt(int index) const
266 {
267     return rowPosAt(index);
268 }
269
270 qreal QSGGridViewPrivate::endPositionAt(int index) const
271 {
272     return rowPosAt(index) + rowSize();
273 }
274
275 int QSGGridViewPrivate::rowSize() const {
276     return flow == QSGGridView::LeftToRight ? cellHeight : cellWidth;
277 }
278 int QSGGridViewPrivate::colSize() const {
279     return flow == QSGGridView::LeftToRight ? cellWidth : cellHeight;
280 }
281
282 qreal QSGGridViewPrivate::colPosAt(int modelIndex) const
283 {
284     if (FxViewItem *item = visibleItem(modelIndex))
285         return static_cast<FxGridItemSG*>(item)->colPos();
286     if (!visibleItems.isEmpty()) {
287         if (modelIndex < visibleIndex) {
288             int count = (visibleIndex - modelIndex) % columns;
289             int col = static_cast<FxGridItemSG*>(visibleItems.first())->colPos() / colSize();
290             col = (columns - count + col) % columns;
291             return col * colSize();
292         } else {
293             int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns;
294             return static_cast<FxGridItemSG*>(visibleItems.last())->colPos() - count * colSize();
295         }
296     }
297     return (modelIndex % columns) * colSize();
298 }
299
300 qreal QSGGridViewPrivate::rowPosAt(int modelIndex) const
301 {
302     if (FxViewItem *item = visibleItem(modelIndex))
303         return static_cast<FxGridItemSG*>(item)->rowPos();
304     if (!visibleItems.isEmpty()) {
305         if (modelIndex < visibleIndex) {
306             FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
307             int firstCol = firstItem->colPos() / colSize();
308             int col = visibleIndex - modelIndex + (columns - firstCol - 1);
309             int rows = col / columns;
310             return firstItem->rowPos() - rows * rowSize();
311         } else {
312             FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
313             int count = modelIndex - lastItem->index;
314             int col = lastItem->colPos() + count * colSize();
315             int rows = col / (columns * colSize());
316             return lastItem->rowPos() + rows * rowSize();
317         }
318     }
319     return (modelIndex / columns) * rowSize();
320 }
321
322
323 qreal QSGGridViewPrivate::snapPosAt(qreal pos) const
324 {
325     Q_Q(const QSGGridView);
326     qreal snapPos = 0;
327     if (!visibleItems.isEmpty()) {
328         pos += rowSize()/2;
329         snapPos = static_cast<FxGridItemSG*>(visibleItems.first())->rowPos() - visibleIndex / columns * rowSize();
330         snapPos = pos - fmodf(pos - snapPos, qreal(rowSize()));
331         qreal maxExtent;
332         qreal minExtent;
333         if (isRightToLeftTopToBottom()) {
334             maxExtent = q->minXExtent();
335             minExtent = q->maxXExtent();
336         } else {
337             maxExtent = flow == QSGGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent();
338             minExtent = flow == QSGGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent();
339         }
340         if (snapPos > maxExtent)
341             snapPos = maxExtent;
342         if (snapPos < minExtent)
343             snapPos = minExtent;
344     }
345     return snapPos;
346 }
347
348 FxViewItem *QSGGridViewPrivate::snapItemAt(qreal pos) const
349 {
350     for (int i = 0; i < visibleItems.count(); ++i) {
351         FxViewItem *item = visibleItems.at(i);
352         if (item->index == -1)
353             continue;
354         qreal itemTop = item->position();
355         if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos)
356             return item;
357     }
358     return 0;
359 }
360
361 int QSGGridViewPrivate::snapIndex() const
362 {
363     int index = currentIndex;
364     for (int i = 0; i < visibleItems.count(); ++i) {
365         FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
366         if (item->index == -1)
367             continue;
368         qreal itemTop = item->position();
369         FxGridItemSG *hItem = static_cast<FxGridItemSG*>(highlight);
370         if (itemTop >= hItem->rowPos()-rowSize()/2 && itemTop < hItem->rowPos()+rowSize()/2) {
371             index = item->index;
372             if (item->colPos() >= hItem->colPos()-colSize()/2 && item->colPos() < hItem->colPos()+colSize()/2)
373                 return item->index;
374         }
375     }
376     return index;
377 }
378
379 FxViewItem *QSGGridViewPrivate::newViewItem(int modelIndex, QSGItem *item)
380 {
381     Q_Q(QSGGridView);
382     Q_UNUSED(modelIndex);
383     return new FxGridItemSG(item, q, false);
384 }
385
386 bool QSGGridViewPrivate::addVisibleItems(int fillFrom, int fillTo, bool doBuffer)
387 {
388     int colPos = colPosAt(visibleIndex);
389     int rowPos = rowPosAt(visibleIndex);
390     if (visibleItems.count()) {
391         FxGridItemSG *lastItem = static_cast<FxGridItemSG*>(visibleItems.last());
392         rowPos = lastItem->rowPos();
393         colPos = lastItem->colPos() + colSize();
394         if (colPos > colSize() * (columns-1)) {
395             colPos = 0;
396             rowPos += rowSize();
397         }
398     }
399
400     int modelIndex = findLastVisibleIndex();
401     modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
402
403     if (visibleItems.count() && (fillFrom > rowPos + rowSize()*2
404         || fillTo < rowPosAt(visibleIndex) - rowSize())) {
405         // We've jumped more than a page.  Estimate which items are now
406         // visible and fill from there.
407         int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns;
408         for (int i = 0; i < visibleItems.count(); ++i)
409             releaseItem(visibleItems.at(i));
410         visibleItems.clear();
411         modelIndex += count;
412         if (modelIndex >= model->count())
413             modelIndex = model->count() - 1;
414         else if (modelIndex < 0)
415             modelIndex = 0;
416         modelIndex = modelIndex / columns * columns;
417         visibleIndex = modelIndex;
418         colPos = colPosAt(visibleIndex);
419         rowPos = rowPosAt(visibleIndex);
420     }
421
422     int colNum = colPos / colSize();
423     FxGridItemSG *item = 0;
424     bool changed = false;
425
426     while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) {
427 //        qDebug() << "refill: append item" << modelIndex;
428         if (!(item = static_cast<FxGridItemSG*>(createItem(modelIndex))))
429             break;
430         item->setPosition(colPos, rowPos);
431         visibleItems.append(item);
432         colPos += colSize();
433         colNum++;
434         if (colPos > colSize() * (columns-1)) {
435             colPos = 0;
436             colNum = 0;
437             rowPos += rowSize();
438         }
439         ++modelIndex;
440         changed = true;
441         if (doBuffer) // never buffer more than one item per frame
442             break;
443     }
444
445     if (visibleItems.count()) {
446         FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
447         rowPos = firstItem->rowPos();
448         colPos = firstItem->colPos() - colSize();
449         if (colPos < 0) {
450             colPos = colSize() * (columns - 1);
451             rowPos -= rowSize();
452         }
453     }
454
455     colNum = colPos / colSize();
456     while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){
457 //        qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos;
458         if (!(item = static_cast<FxGridItemSG*>(createItem(visibleIndex-1))))
459             break;
460         --visibleIndex;
461         item->setPosition(colPos, rowPos);
462         visibleItems.prepend(item);
463         colPos -= colSize();
464         colNum--;
465         if (colPos < 0) {
466             colPos = colSize() * (columns - 1);
467             colNum = columns-1;
468             rowPos -= rowSize();
469         }
470         changed = true;
471         if (doBuffer) // never buffer more than one item per frame
472             break;
473     }
474
475     return changed;
476 }
477
478 bool QSGGridViewPrivate::removeNonVisibleItems(int bufferFrom, int bufferTo)
479 {
480     FxGridItemSG *item = 0;
481     bool changed = false;
482
483     while (visibleItems.count() > 1
484            && (item = static_cast<FxGridItemSG*>(visibleItems.first()))
485                 && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) {
486         if (item->attached->delayRemove())
487             break;
488 //            qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos();
489         if (item->index != -1)
490             visibleIndex++;
491         visibleItems.removeFirst();
492         releaseItem(item);
493         changed = true;
494     }
495     while (visibleItems.count() > 1
496            && (item = static_cast<FxGridItemSG*>(visibleItems.last()))
497                 && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) {
498         if (item->attached->delayRemove())
499             break;
500 //            qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1;
501         visibleItems.removeLast();
502         releaseItem(item);
503         changed = true;
504     }
505
506     return changed;
507 }
508
509 void QSGGridViewPrivate::visibleItemsChanged()
510 {
511     updateHeader();
512     updateFooter();
513     updateViewport();
514 }
515
516 void QSGGridViewPrivate::updateViewport()
517 {
518     Q_Q(QSGGridView);
519     columns = (int)qMax((flow == QSGGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.));
520     QSGItemViewPrivate::updateViewport();
521 }
522
523 void QSGGridViewPrivate::layoutVisibleItems()
524 {
525     if (visibleItems.count()) {
526         FxGridItemSG *firstItem = static_cast<FxGridItemSG*>(visibleItems.first());
527         qreal rowPos = firstItem->rowPos();
528         qreal colPos = firstItem->colPos();
529         int col = visibleIndex % columns;
530         if (colPos != col * colSize()) {
531             colPos = col * colSize();
532             firstItem->setPosition(colPos, rowPos);
533         }
534         for (int i = 1; i < visibleItems.count(); ++i) {
535             FxGridItemSG *item = static_cast<FxGridItemSG*>(visibleItems.at(i));
536             colPos += colSize();
537             if (colPos > colSize() * (columns-1)) {
538                 colPos = 0;
539                 rowPos += rowSize();
540             }
541             item->setPosition(colPos, rowPos);
542         }
543     }
544 }
545
546 void QSGGridViewPrivate::repositionPackageItemAt(QSGItem *item, int index)
547 {
548     Q_Q(QSGGridView);
549     qreal pos = position();
550     if (flow == QSGGridView::LeftToRight) {
551         if (item->y() + item->height() > pos && item->y() < pos + q->height())
552             item->setPos(QPointF(colPosAt(index), rowPosAt(index)));
553     } else {
554         if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
555             if (isRightToLeftTopToBottom())
556                 item->setPos(QPointF(-rowPosAt(index)-item->width(), colPosAt(index)));
557             else
558                 item->setPos(QPointF(rowPosAt(index), colPosAt(index)));
559         }
560     }
561 }
562
563
564 void QSGGridViewPrivate::createHighlight()
565 {
566     Q_Q(QSGGridView);
567     bool changed = false;
568     if (highlight) {
569         if (trackedItem == highlight)
570             trackedItem = 0;
571         delete highlight;
572         highlight = 0;
573
574         delete highlightXAnimator;
575         delete highlightYAnimator;
576         highlightXAnimator = 0;
577         highlightYAnimator = 0;
578
579         changed = true;
580     }
581
582     if (currentItem) {
583         QSGItem *item = createHighlightItem();
584         if (item) {
585             FxGridItemSG *newHighlight = new FxGridItemSG(item, q, true);
586             if (autoHighlight)
587                 resetHighlightPosition();
588             highlightXAnimator = new QSmoothedAnimation(q);
589             highlightXAnimator->target = QDeclarativeProperty(item, QLatin1String("x"));
590             highlightXAnimator->userDuration = highlightMoveDuration;
591             highlightYAnimator = new QSmoothedAnimation(q);
592             highlightYAnimator->target = QDeclarativeProperty(item, QLatin1String("y"));
593             highlightYAnimator->userDuration = highlightMoveDuration;
594
595             highlight = newHighlight;
596             changed = true;
597         }
598     }
599     if (changed)
600         emit q->highlightItemChanged();
601 }
602
603 void QSGGridViewPrivate::updateHighlight()
604 {
605     if ((!currentItem && highlight) || (currentItem && !highlight))
606         createHighlight();
607     bool strictHighlight = haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange;
608     if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
609         // auto-update highlight
610         highlightXAnimator->to = currentItem->item->x();
611         highlightYAnimator->to = currentItem->item->y();
612         highlight->item->setWidth(currentItem->item->width());
613         highlight->item->setHeight(currentItem->item->height());
614
615         highlightXAnimator->restart();
616         highlightYAnimator->restart();
617     }
618     updateTrackedItem();
619 }
620
621 void QSGGridViewPrivate::resetHighlightPosition()
622 {
623     if (highlight && currentItem) {
624         FxGridItemSG *cItem = static_cast<FxGridItemSG*>(currentItem);
625         static_cast<FxGridItemSG*>(highlight)->setPosition(cItem->colPos(), cItem->rowPos());
626     }
627 }
628
629 qreal QSGGridViewPrivate::headerSize() const
630 {
631     if (!header)
632         return 0.0;
633     return flow == QSGGridView::LeftToRight ? header->item->height() : header->item->width();
634 }
635
636 qreal QSGGridViewPrivate::footerSize() const
637 {
638     if (!footer)
639         return 0.0;
640     return flow == QSGGridView::LeftToRight? footer->item->height() : footer->item->width();
641 }
642
643 bool QSGGridViewPrivate::showHeaderForIndex(int index) const
644 {
645     return index / columns == 0;
646 }
647
648 bool QSGGridViewPrivate::showFooterForIndex(int index) const
649 {
650     return index / columns == (model->count()-1) / columns;
651 }
652
653 void QSGGridViewPrivate::updateFooter()
654 {
655     Q_Q(QSGGridView);
656     bool created = false;
657     if (!footer) {
658         QSGItem *item = createComponentItem(footerComponent, true);
659         if (!item)
660             return;
661         item->setZ(1);
662         footer = new FxGridItemSG(item, q, true);
663         created = true;
664     }
665
666     FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(footer);
667     qreal colOffset = 0;
668     qreal rowOffset = 0;
669     if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
670         if (flow == QSGGridView::TopToBottom)
671             rowOffset = gridItem->item->width() - cellWidth;
672         else
673             colOffset = gridItem->item->width() - cellWidth;
674     }
675     if (visibleItems.count()) {
676         qreal endPos = lastPosition();
677         if (findLastVisibleIndex() == model->count()-1) {
678             gridItem->setPosition(colOffset, endPos + rowOffset);
679         } else {
680             qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size();
681             if (endPos <= visiblePos || gridItem->endPosition() <= endPos + rowOffset)
682                 gridItem->setPosition(colOffset, endPos + rowOffset);
683         }
684     } else {
685         gridItem->setPosition(colOffset, rowOffset);
686     }
687
688     if (created)
689         emit q->footerItemChanged();
690 }
691
692 void QSGGridViewPrivate::updateHeader()
693 {
694     Q_Q(QSGGridView);
695     bool created = false;
696     if (!header) {
697         QSGItem *item = createComponentItem(headerComponent, true);
698         if (!item)
699             return;
700         item->setZ(1);
701         header = new FxGridItemSG(item, q, true);
702         created = true;
703     }
704
705     FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(header);
706     qreal colOffset = 0;
707     qreal rowOffset = -headerSize();
708     if (q->effectiveLayoutDirection() == Qt::RightToLeft) {
709         if (flow == QSGGridView::TopToBottom)
710             rowOffset += gridItem->item->width()-cellWidth;
711         else
712             colOffset = gridItem->item->width()-cellWidth;
713     }
714     if (visibleItems.count()) {
715         qreal startPos = originPosition();
716         if (visibleIndex == 0) {
717             gridItem->setPosition(colOffset, startPos + rowOffset);
718         } else {
719             qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position();
720             qreal headerPos = isRightToLeftTopToBottom() ? gridItem->rowPos() + cellWidth - headerSize() : gridItem->rowPos();
721             if (tempPos <= startPos || headerPos > startPos + rowOffset)
722                 gridItem->setPosition(colOffset, startPos + rowOffset);
723         }
724     } else {
725         if (isRightToLeftTopToBottom())
726             gridItem->setPosition(colOffset, rowOffset);
727         else
728             gridItem->setPosition(colOffset, -headerSize());
729     }
730
731     if (created)
732         emit q->headerItemChanged();
733 }
734
735 void QSGGridViewPrivate::initializeCurrentItem()
736 {
737     if (currentItem && currentIndex >= 0) {
738         FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(currentItem);
739         if (gridItem)
740             gridItem->setPosition(colPosAt(currentIndex), rowPosAt(currentIndex));
741     }
742 }
743
744 void QSGGridViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
745 {
746     Q_Q(QSGGridView);
747     QSGItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
748     if (!q->isComponentComplete())
749         return;
750     if (item == q) {
751         if (newGeometry.height() != oldGeometry.height() || newGeometry.width() != oldGeometry.width()) {
752             updateViewport();
753             scheduleLayout();
754         }
755     }
756 }
757
758 void QSGGridViewPrivate::fixupPosition()
759 {
760     moveReason = Other;
761     if (flow == QSGGridView::LeftToRight)
762         fixupY();
763     else
764         fixupX();
765 }
766
767 void QSGGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
768 {
769     if ((flow == QSGGridView::TopToBottom && &data == &vData)
770         || (flow == QSGGridView::LeftToRight && &data == &hData))
771         return;
772
773     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
774
775     qreal highlightStart;
776     qreal highlightEnd;
777     qreal viewPos;
778     if (isRightToLeftTopToBottom()) {
779         // Handle Right-To-Left exceptions
780         viewPos = -position()-size();
781         highlightStart = highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
782         highlightEnd = highlightRangeEndValid ? size()-highlightRangeStart : highlightRangeEnd;
783     } else {
784         viewPos = position();
785         highlightStart = highlightRangeStart;
786         highlightEnd = highlightRangeEnd;
787     }
788
789     if (snapMode != QSGGridView::NoSnap) {
790         qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position();
791         FxViewItem *topItem = snapItemAt(tempPosition+highlightStart);
792         FxViewItem *bottomItem = snapItemAt(tempPosition+highlightEnd);
793         qreal pos;
794         if (topItem && bottomItem && haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) {
795             qreal topPos = qMin(topItem->position() - highlightStart, -maxExtent);
796             qreal bottomPos = qMax(bottomItem->position() - highlightEnd, -minExtent);
797             pos = qAbs(data.move + topPos) < qAbs(data.move + bottomPos) ? topPos : bottomPos;
798         } else if (topItem) {
799             qreal headerPos = 0;
800             if (header)
801                 headerPos = isRightToLeftTopToBottom() ? static_cast<FxGridItemSG*>(header)->rowPos() + cellWidth - headerSize() : static_cast<FxGridItemSG*>(header)->rowPos();
802             if (topItem->index == 0 && header && tempPosition+highlightStart < headerPos+headerSize()/2) {
803                 pos = isRightToLeftTopToBottom() ? - headerPos + highlightStart - size() : headerPos - highlightStart;
804             } else {
805                 if (isRightToLeftTopToBottom())
806                     pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
807                 else
808                     pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
809             }
810         } else if (bottomItem) {
811             if (isRightToLeftTopToBottom())
812                 pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent);
813             else
814                 pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent);
815         } else {
816             QSGItemViewPrivate::fixup(data, minExtent, maxExtent);
817             return;
818         }
819         if (currentItem && haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) {
820             updateHighlight();
821             qreal currPos = static_cast<FxGridItemSG*>(currentItem)->rowPos();
822             if (isRightToLeftTopToBottom())
823                 pos = -pos-size(); // Transform Pos if required
824             if (pos < currPos + rowSize() - highlightEnd)
825                 pos = currPos + rowSize() - highlightEnd;
826             if (pos > currPos - highlightStart)
827                 pos = currPos - highlightStart;
828             if (isRightToLeftTopToBottom())
829                 pos = -pos-size(); // Untransform
830         }
831
832         qreal dist = qAbs(data.move + pos);
833         if (dist > 0) {
834             timeline.reset(data.move);
835             if (fixupMode != Immediate) {
836                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
837                 data.fixingUp = true;
838             } else {
839                 timeline.set(data.move, -pos);
840             }
841             vTime = timeline.time();
842         }
843     } else if (haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) {
844         if (currentItem) {
845             updateHighlight();
846             qreal pos = static_cast<FxGridItemSG*>(currentItem)->rowPos();
847             if (viewPos < pos + rowSize() - highlightEnd)
848                 viewPos = pos + rowSize() - highlightEnd;
849             if (viewPos > pos - highlightStart)
850                 viewPos = pos - highlightStart;
851             if (isRightToLeftTopToBottom())
852                 viewPos = -viewPos-size();
853             timeline.reset(data.move);
854             if (viewPos != position()) {
855                 if (fixupMode != Immediate) {
856                     timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
857                     data.fixingUp = true;
858                 } else {
859                     timeline.set(data.move, -viewPos);
860                 }
861             }
862             vTime = timeline.time();
863         }
864     } else {
865         QSGItemViewPrivate::fixup(data, minExtent, maxExtent);
866     }
867     data.inOvershoot = false;
868     fixupMode = Normal;
869 }
870
871 void QSGGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
872                                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
873 {
874     Q_Q(QSGGridView);
875     data.fixingUp = false;
876     moveReason = Mouse;
877     if ((!haveHighlightRange || highlightRange != QSGGridView::StrictlyEnforceRange)
878         && snapMode == QSGGridView::NoSnap) {
879         QSGItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
880         return;
881     }
882     qreal maxDistance = 0;
883     qreal dataValue = isRightToLeftTopToBottom() ? -data.move.value()+size() : data.move.value();
884     // -ve velocity means list is moving up/left
885     if (velocity > 0) {
886         if (data.move.value() < minExtent) {
887             if (snapMode == QSGGridView::SnapOneRow) {
888                 if (FxViewItem *item = firstVisibleItem())
889                     maxDistance = qAbs(item->position() + dataValue);
890             } else {
891                 maxDistance = qAbs(minExtent - data.move.value());
892             }
893         }
894         if (snapMode == QSGGridView::NoSnap && highlightRange != QSGGridView::StrictlyEnforceRange)
895             data.flickTarget = minExtent;
896     } else {
897         if (data.move.value() > maxExtent) {
898             if (snapMode == QSGGridView::SnapOneRow) {
899                 qreal pos = snapPosAt(-dataValue) + (isRightToLeftTopToBottom() ? 0 : rowSize());
900                 maxDistance = qAbs(pos + dataValue);
901             } else {
902                 maxDistance = qAbs(maxExtent - data.move.value());
903             }
904         }
905         if (snapMode == QSGGridView::NoSnap && highlightRange != QSGGridView::StrictlyEnforceRange)
906             data.flickTarget = maxExtent;
907     }
908     bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
909     qreal highlightStart = isRightToLeftTopToBottom() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
910     if (maxDistance > 0 || overShoot) {
911         // This mode requires the grid to stop exactly on a row boundary.
912         qreal v = velocity;
913         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
914             if (v < 0)
915                 v = -maxVelocity;
916             else
917                 v = maxVelocity;
918         }
919         qreal accel = deceleration;
920         qreal v2 = v * v;
921         qreal overshootDist = 0.0;
922         if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGGridView::SnapOneRow) {
923             // + rowSize()/4 to encourage moving at least one item in the flick direction
924             qreal dist = v2 / (accel * 2.0) + rowSize()/4;
925             dist = qMin(dist, maxDistance);
926             if (v > 0)
927                 dist = -dist;
928             qreal distTemp = isRightToLeftTopToBottom() ? -dist : dist;
929             data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
930             data.flickTarget = isRightToLeftTopToBottom() ? -data.flickTarget+size() : data.flickTarget;
931             qreal adjDist = -data.flickTarget + data.move.value();
932             if (qAbs(adjDist) > qAbs(dist)) {
933                 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
934                 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
935                 if (adjv2 > v2) {
936                     v2 = adjv2;
937                     v = qSqrt(v2);
938                     if (dist > 0)
939                         v = -v;
940                 }
941             }
942             dist = adjDist;
943             accel = v2 / (2.0f * qAbs(dist));
944         } else {
945             data.flickTarget = velocity > 0 ? minExtent : maxExtent;
946             overshootDist = overShoot ? overShootDistance(vSize) : 0;
947         }
948         timeline.reset(data.move);
949         timeline.accel(data.move, v, accel, maxDistance + overshootDist);
950         timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
951         if (!flickingHorizontally && q->xflick()) {
952             flickingHorizontally = true;
953             emit q->flickingChanged();
954             emit q->flickingHorizontallyChanged();
955             emit q->flickStarted();
956         }
957         if (!flickingVertically && q->yflick()) {
958             flickingVertically = true;
959             emit q->flickingChanged();
960             emit q->flickingVerticallyChanged();
961             emit q->flickStarted();
962         }
963     } else {
964         timeline.reset(data.move);
965         fixup(data, minExtent, maxExtent);
966     }
967 }
968
969
970 //----------------------------------------------------------------------------
971
972 QSGGridView::QSGGridView(QSGItem *parent)
973     : QSGItemView(*(new QSGGridViewPrivate), parent)
974 {
975 }
976
977 QSGGridView::~QSGGridView()
978 {
979 }
980
981 void QSGGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
982 {
983     Q_D(QSGGridView);
984     if (d->autoHighlight != autoHighlight) {
985         if (!autoHighlight && d->highlightXAnimator) {
986             d->highlightXAnimator->stop();
987             d->highlightYAnimator->stop();
988         }
989         QSGItemView::setHighlightFollowsCurrentItem(autoHighlight);
990     }
991 }
992
993 void QSGGridView::setHighlightMoveDuration(int duration)
994 {
995     Q_D(QSGGridView);
996     if (d->highlightMoveDuration != duration) {
997         if (d->highlightYAnimator) {
998             d->highlightXAnimator->userDuration = duration;
999             d->highlightYAnimator->userDuration = duration;
1000         }
1001         QSGItemView::setHighlightMoveDuration(duration);
1002     }
1003 }
1004
1005 QSGGridView::Flow QSGGridView::flow() const
1006 {
1007     Q_D(const QSGGridView);
1008     return d->flow;
1009 }
1010
1011 void QSGGridView::setFlow(Flow flow)
1012 {
1013     Q_D(QSGGridView);
1014     if (d->flow != flow) {
1015         d->flow = flow;
1016         if (d->flow == LeftToRight) {
1017             setContentWidth(-1);
1018             setFlickableDirection(VerticalFlick);
1019         } else {
1020             setContentHeight(-1);
1021             setFlickableDirection(HorizontalFlick);
1022         }
1023         setContentX(0);
1024         setContentY(0);
1025         d->regenerate();
1026         emit flowChanged();
1027     }
1028 }
1029
1030
1031 int QSGGridView::cellWidth() const
1032 {
1033     Q_D(const QSGGridView);
1034     return d->cellWidth;
1035 }
1036
1037 void QSGGridView::setCellWidth(int cellWidth)
1038 {
1039     Q_D(QSGGridView);
1040     if (cellWidth != d->cellWidth && cellWidth > 0) {
1041         d->cellWidth = qMax(1, cellWidth);
1042         d->updateViewport();
1043         emit cellWidthChanged();
1044         d->layout();
1045     }
1046 }
1047
1048 int QSGGridView::cellHeight() const
1049 {
1050     Q_D(const QSGGridView);
1051     return d->cellHeight;
1052 }
1053
1054 void QSGGridView::setCellHeight(int cellHeight)
1055 {
1056     Q_D(QSGGridView);
1057     if (cellHeight != d->cellHeight && cellHeight > 0) {
1058         d->cellHeight = qMax(1, cellHeight);
1059         d->updateViewport();
1060         emit cellHeightChanged();
1061         d->layout();
1062     }
1063 }
1064
1065 QSGGridView::SnapMode QSGGridView::snapMode() const
1066 {
1067     Q_D(const QSGGridView);
1068     return d->snapMode;
1069 }
1070
1071 void QSGGridView::setSnapMode(SnapMode mode)
1072 {
1073     Q_D(QSGGridView);
1074     if (d->snapMode != mode) {
1075         d->snapMode = mode;
1076         emit snapModeChanged();
1077     }
1078 }
1079
1080
1081 void QSGGridView::viewportMoved()
1082 {
1083     Q_D(QSGGridView);
1084     QSGItemView::viewportMoved();
1085     if (!d->itemCount)
1086         return;
1087     if (d->inViewportMoved)
1088         return;
1089     d->inViewportMoved = true;
1090
1091     d->lazyRelease = true;
1092     if (d->flickingHorizontally || d->flickingVertically) {
1093         if (yflick()) {
1094             if (d->vData.velocity > 0)
1095                 d->bufferMode = QSGGridViewPrivate::BufferBefore;
1096             else if (d->vData.velocity < 0)
1097                 d->bufferMode = QSGGridViewPrivate::BufferAfter;
1098         }
1099
1100         if (xflick()) {
1101             if (d->hData.velocity > 0)
1102                 d->bufferMode = QSGGridViewPrivate::BufferBefore;
1103             else if (d->hData.velocity < 0)
1104                 d->bufferMode = QSGGridViewPrivate::BufferAfter;
1105         }
1106     }
1107     d->refill();
1108     if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
1109         d->moveReason = QSGGridViewPrivate::Mouse;
1110     if (d->moveReason != QSGGridViewPrivate::SetIndex) {
1111         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
1112             // reposition highlight
1113             qreal pos = d->highlight->position();
1114             qreal viewPos;
1115             qreal highlightStart;
1116             qreal highlightEnd;
1117             if (d->isRightToLeftTopToBottom()) {
1118                 viewPos = -d->position()-d->size();
1119                 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
1120                 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
1121             } else {
1122                 viewPos = d->position();
1123                 highlightStart = d->highlightRangeStart;
1124                 highlightEnd = d->highlightRangeEnd;
1125             }
1126             if (pos > viewPos + highlightEnd - d->highlight->size())
1127                 pos = viewPos + highlightEnd - d->highlight->size();
1128             if (pos < viewPos + highlightStart)
1129                 pos = viewPos + highlightStart;
1130
1131             if (pos != d->highlight->position()) {
1132                 d->highlightXAnimator->stop();
1133                 d->highlightYAnimator->stop();
1134                 static_cast<FxGridItemSG*>(d->highlight)->setPosition(static_cast<FxGridItemSG*>(d->highlight)->colPos(), pos);
1135             } else {
1136                 d->updateHighlight();
1137             }
1138
1139             // update current index
1140             int idx = d->snapIndex();
1141             if (idx >= 0 && idx != d->currentIndex) {
1142                 d->updateCurrent(idx);
1143                 if (d->currentItem && static_cast<FxGridItemSG*>(d->currentItem)->colPos() != static_cast<FxGridItemSG*>(d->highlight)->colPos() && d->autoHighlight) {
1144                     if (d->flow == LeftToRight)
1145                         d->highlightXAnimator->to = d->currentItem->item->x();
1146                     else
1147                         d->highlightYAnimator->to = d->currentItem->item->y();
1148                 }
1149             }
1150         }
1151     }
1152
1153     d->inViewportMoved = false;
1154 }
1155
1156 void QSGGridView::keyPressEvent(QKeyEvent *event)
1157 {
1158     Q_D(QSGGridView);
1159     if (d->model && d->model->count() && d->interactive) {
1160         d->moveReason = QSGGridViewPrivate::SetIndex;
1161         int oldCurrent = currentIndex();
1162         switch (event->key()) {
1163         case Qt::Key_Up:
1164             moveCurrentIndexUp();
1165             break;
1166         case Qt::Key_Down:
1167             moveCurrentIndexDown();
1168             break;
1169         case Qt::Key_Left:
1170             moveCurrentIndexLeft();
1171             break;
1172         case Qt::Key_Right:
1173             moveCurrentIndexRight();
1174             break;
1175         default:
1176             break;
1177         }
1178         if (oldCurrent != currentIndex()) {
1179             event->accept();
1180             return;
1181         }
1182     }
1183     event->ignore();
1184     QSGItemView::keyPressEvent(event);
1185 }
1186
1187 void QSGGridView::moveCurrentIndexUp()
1188 {
1189     Q_D(QSGGridView);
1190     const int count = d->model ? d->model->count() : 0;
1191     if (!count)
1192         return;
1193     if (d->flow == QSGGridView::LeftToRight) {
1194         if (currentIndex() >= d->columns || d->wrap) {
1195             int index = currentIndex() - d->columns;
1196             setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1197         }
1198     } else {
1199         if (currentIndex() > 0 || d->wrap) {
1200             int index = currentIndex() - 1;
1201             setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1202         }
1203     }
1204 }
1205
1206 void QSGGridView::moveCurrentIndexDown()
1207 {
1208     Q_D(QSGGridView);
1209     const int count = d->model ? d->model->count() : 0;
1210     if (!count)
1211         return;
1212     if (d->flow == QSGGridView::LeftToRight) {
1213         if (currentIndex() < count - d->columns || d->wrap) {
1214             int index = currentIndex()+d->columns;
1215             setCurrentIndex((index >= 0 && index < count) ? index : 0);
1216         }
1217     } else {
1218         if (currentIndex() < count - 1 || d->wrap) {
1219             int index = currentIndex() + 1;
1220             setCurrentIndex((index >= 0 && index < count) ? index : 0);
1221         }
1222     }
1223 }
1224
1225 void QSGGridView::moveCurrentIndexLeft()
1226 {
1227     Q_D(QSGGridView);
1228     const int count = d->model ? d->model->count() : 0;
1229     if (!count)
1230         return;
1231     if (effectiveLayoutDirection() == Qt::LeftToRight) {
1232         if (d->flow == QSGGridView::LeftToRight) {
1233             if (currentIndex() > 0 || d->wrap) {
1234                 int index = currentIndex() - 1;
1235                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1236             }
1237         } else {
1238             if (currentIndex() >= d->columns || d->wrap) {
1239                 int index = currentIndex() - d->columns;
1240                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1241             }
1242         }
1243     } else {
1244         if (d->flow == QSGGridView::LeftToRight) {
1245             if (currentIndex() < count - 1 || d->wrap) {
1246                 int index = currentIndex() + 1;
1247                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1248             }
1249         } else {
1250             if (currentIndex() < count - d->columns || d->wrap) {
1251                 int index = currentIndex() + d->columns;
1252                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1253             }
1254         }
1255     }
1256 }
1257
1258 void QSGGridView::moveCurrentIndexRight()
1259 {
1260     Q_D(QSGGridView);
1261     const int count = d->model ? d->model->count() : 0;
1262     if (!count)
1263         return;
1264     if (effectiveLayoutDirection() == Qt::LeftToRight) {
1265         if (d->flow == QSGGridView::LeftToRight) {
1266             if (currentIndex() < count - 1 || d->wrap) {
1267                 int index = currentIndex() + 1;
1268                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1269             }
1270         } else {
1271             if (currentIndex() < count - d->columns || d->wrap) {
1272                 int index = currentIndex()+d->columns;
1273                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
1274             }
1275         }
1276     } else {
1277         if (d->flow == QSGGridView::LeftToRight) {
1278             if (currentIndex() > 0 || d->wrap) {
1279                 int index = currentIndex() - 1;
1280                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1281             }
1282         } else {
1283             if (currentIndex() >= d->columns || d->wrap) {
1284                 int index = currentIndex() - d->columns;
1285                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1286             }
1287         }
1288     }
1289 }
1290
1291
1292 void QSGGridView::itemsInserted(int modelIndex, int count)
1293 {
1294     Q_D(QSGGridView);
1295     if (!isComponentComplete() || !d->model || !d->model->isValid())
1296         return;
1297
1298     int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
1299     if (index < 0) {
1300         int i = d->visibleItems.count() - 1;
1301         while (i > 0 && d->visibleItems.at(i)->index == -1)
1302             --i;
1303         if (d->visibleItems.at(i)->index + 1 == modelIndex) {
1304             // Special case of appending an item to the model.
1305             index = d->visibleIndex + d->visibleItems.count();
1306         } else {
1307             if (modelIndex <= d->visibleIndex) {
1308                 // Insert before visible items
1309                 d->visibleIndex += count;
1310                 for (int i = 0; i < d->visibleItems.count(); ++i) {
1311                     FxViewItem *item = d->visibleItems.at(i);
1312                     if (item->index != -1 && item->index >= modelIndex)
1313                         item->index += count;
1314                 }
1315             }
1316             if (d->currentIndex >= modelIndex) {
1317                 // adjust current item index
1318                 d->currentIndex += count;
1319                 if (d->currentItem)
1320                     d->currentItem->index = d->currentIndex;
1321                 emit currentIndexChanged();
1322             }
1323             d->scheduleLayout();
1324             d->itemCount += count;
1325             emit countChanged();
1326             return;
1327         }
1328     }
1329
1330     int insertCount = count;
1331     if (index < d->visibleIndex && d->visibleItems.count()) {
1332         insertCount -= d->visibleIndex - index;
1333         index = d->visibleIndex;
1334         modelIndex = d->visibleIndex;
1335     }
1336
1337     qreal tempPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size()+width()+1 : d->position();
1338     int to = d->buffer+tempPos+d->size()-1;
1339     int colPos = 0;
1340     int rowPos = 0;
1341     if (d->visibleItems.count()) {
1342         index -= d->visibleIndex;
1343         if (index < d->visibleItems.count()) {
1344             FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(d->visibleItems.at(index));
1345             colPos = gridItem->colPos();
1346             rowPos = gridItem->rowPos();
1347         } else {
1348             // appending items to visible list
1349             FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(d->visibleItems.at(index-1));
1350             colPos = gridItem->colPos() + d->colSize();
1351             rowPos = gridItem->rowPos();
1352             if (colPos > d->colSize() * (d->columns-1)) {
1353                 colPos = 0;
1354                 rowPos += d->rowSize();
1355             }
1356         }
1357     }
1358
1359     // Update the indexes of the following visible items.
1360     for (int i = 0; i < d->visibleItems.count(); ++i) {
1361         FxViewItem *item = d->visibleItems.at(i);
1362         if (item->index != -1 && item->index >= modelIndex)
1363             item->index += count;
1364     }
1365
1366     bool addedVisible = false;
1367     QList<FxGridItemSG*> added;
1368     int i = 0;
1369     while (i < insertCount && rowPos <= to + d->rowSize()*(d->columns - (colPos/d->colSize()))/qreal(d->columns)) {
1370         if (!addedVisible) {
1371             d->scheduleLayout();
1372             addedVisible = true;
1373         }
1374         FxGridItemSG *item = static_cast<FxGridItemSG*>(d->createItem(modelIndex + i));
1375         d->visibleItems.insert(index, item);
1376         item->setPosition(colPos, rowPos);
1377         added.append(item);
1378         colPos += d->colSize();
1379         if (colPos > d->colSize() * (d->columns-1)) {
1380             colPos = 0;
1381             rowPos += d->rowSize();
1382         }
1383         ++index;
1384         ++i;
1385     }
1386     if (i < insertCount) {
1387         // We didn't insert all our new items, which means anything
1388         // beyond the current index is not visible - remove it.
1389         while (d->visibleItems.count() > index) {
1390             d->releaseItem(d->visibleItems.takeLast());
1391         }
1392     }
1393
1394     // update visibleIndex
1395     d->visibleIndex = 0;
1396     for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1397         if ((*it)->index != -1) {
1398             d->visibleIndex = (*it)->index;
1399             break;
1400         }
1401     }
1402
1403     if (d->itemCount && d->currentIndex >= modelIndex) {
1404         // adjust current item index
1405         d->currentIndex += count;
1406         if (d->currentItem) {
1407             d->currentItem->index = d->currentIndex;
1408             static_cast<FxGridItemSG*>(d->currentItem)->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex));
1409         }
1410         emit currentIndexChanged();
1411     } else if (d->itemCount == 0 && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
1412         setCurrentIndex(0);
1413     }
1414
1415     // everything is in order now - emit add() signal
1416     for (int j = 0; j < added.count(); ++j)
1417         added.at(j)->attached->emitAdd();
1418
1419     d->itemCount += count;
1420     emit countChanged();
1421 }
1422
1423 void QSGGridView::itemsRemoved(int modelIndex, int count)
1424 {
1425     Q_D(QSGGridView);
1426     if (!isComponentComplete() || !d->model || !d->model->isValid())
1427         return;
1428
1429     d->itemCount -= count;
1430     bool currentRemoved = d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count;
1431     bool removedVisible = false;
1432
1433     // Remove the items from the visible list, skipping anything already marked for removal
1434     QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1435     while (it != d->visibleItems.end()) {
1436         FxViewItem *item = *it;
1437         if (item->index == -1 || item->index < modelIndex) {
1438             // already removed, or before removed items
1439             if (item->index < modelIndex && !removedVisible) {
1440                 d->scheduleLayout();
1441                 removedVisible = true;
1442             }
1443             ++it;
1444         } else if (item->index >= modelIndex + count) {
1445             // after removed items
1446             item->index -= count;
1447             ++it;
1448         } else {
1449             // removed item
1450             if (!removedVisible) {
1451                 d->scheduleLayout();
1452                 removedVisible = true;
1453             }
1454             item->attached->emitRemove();
1455             if (item->attached->delayRemove()) {
1456                 item->index = -1;
1457                 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
1458                 ++it;
1459             } else {
1460                 it = d->visibleItems.erase(it);
1461                 d->releaseItem(item);
1462             }
1463         }
1464     }
1465
1466     // update visibleIndex
1467     d->visibleIndex = 0;
1468     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1469         if ((*it)->index != -1) {
1470             d->visibleIndex = (*it)->index;
1471             break;
1472         }
1473     }
1474
1475     // fix current
1476     if (d->currentIndex >= modelIndex + count) {
1477         d->currentIndex -= count;
1478         if (d->currentItem)
1479             d->currentItem->index -= count;
1480         emit currentIndexChanged();
1481     } else if (currentRemoved) {
1482         // current item has been removed.
1483         d->releaseItem(d->currentItem);
1484         d->currentItem = 0;
1485         d->currentIndex = -1;
1486         if (d->itemCount)
1487             d->updateCurrent(qMin(modelIndex, d->itemCount-1));
1488         else
1489             emit currentIndexChanged();
1490     }
1491
1492     if (removedVisible && d->visibleItems.isEmpty()) {
1493         d->timeline.clear();
1494         if (d->itemCount == 0) {
1495             d->setPosition(d->contentStartPosition());
1496             d->updateHeader();
1497             d->updateFooter();
1498         }
1499     }
1500
1501     emit countChanged();
1502 }
1503
1504
1505 void QSGGridView::itemsMoved(int from, int to, int count)
1506 {
1507     Q_D(QSGGridView);
1508     if (!isComponentComplete() || !d->isValid())
1509         return;
1510     d->updateUnrequestedIndexes();
1511
1512     if (d->visibleItems.isEmpty()) {
1513         d->refill();
1514         return;
1515     }
1516
1517     d->moveReason = QSGGridViewPrivate::Other;
1518
1519     bool movingBackwards = from > to;
1520     d->adjustMoveParameters(&from, &to, &count);
1521
1522     QHash<int,FxGridItemSG*> moved;
1523     int moveByCount = 0;
1524     FxGridItemSG *firstVisible = static_cast<FxGridItemSG*>(d->firstVisibleItem());
1525     int firstItemIndex = firstVisible ? firstVisible->index : -1;
1526
1527     // if visibleItems.first() is above the content start pos, and the items
1528     // beneath it are moved, ensure this first item is later repositioned correctly
1529     // (to above the next visible item) so that subsequent layout() is correct
1530     bool repositionFirstItem = firstVisible
1531             && d->visibleItems.first()->position() < firstVisible->position()
1532             && from > d->visibleItems.first()->index;
1533
1534     QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1535     while (it != d->visibleItems.end()) {
1536         FxViewItem *item = *it;
1537         if (item->index >= from && item->index < from + count) {
1538             // take the items that are moving
1539             item->index += (to-from);
1540             moved.insert(item->index, static_cast<FxGridItemSG*>(item));
1541             if (repositionFirstItem)
1542                 moveByCount++;
1543             it = d->visibleItems.erase(it);
1544         } else {
1545             if (item->index > from && item->index != -1) {
1546                 // move everything after the moved items.
1547                 item->index -= count;
1548                 if (item->index < d->visibleIndex)
1549                     d->visibleIndex = item->index;
1550             }
1551             ++it;
1552         }
1553     }
1554
1555     int movedCount = 0;
1556     int endIndex = d->visibleIndex;
1557     it = d->visibleItems.begin();
1558     while (it != d->visibleItems.end()) {
1559         FxViewItem *item = *it;
1560         if (movedCount < count && item->index >= to && item->index < to + count) {
1561             // place items in the target position, reusing any existing items
1562             int targetIndex = item->index + movedCount;
1563             FxGridItemSG *movedItem = moved.take(targetIndex);
1564             if (!movedItem)
1565                 movedItem = static_cast<FxGridItemSG*>(d->createItem(targetIndex));
1566             it = d->visibleItems.insert(it, movedItem);
1567             ++it;
1568             ++movedCount;
1569         } else {
1570             if (item->index != -1) {
1571                 if (item->index >= to) {
1572                     // update everything after the moved items.
1573                     item->index += count;
1574                 }
1575                 endIndex = item->index;
1576             }
1577             ++it;
1578         }
1579     }
1580
1581     // If we have moved items to the end of the visible items
1582     // then add any existing moved items that we have
1583     while (FxGridItemSG *item = moved.take(endIndex+1)) {
1584         d->visibleItems.append(item);
1585         ++endIndex;
1586     }
1587
1588     // update visibleIndex
1589     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1590         if ((*it)->index != -1) {
1591             d->visibleIndex = (*it)->index;
1592             break;
1593         }
1594     }
1595
1596     // if first visible item is moving but another item is moving up to replace it,
1597     // do this positioning now to avoid shifting all content forwards
1598     if (movingBackwards && firstItemIndex >= 0) {
1599         for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1600             if ((*it)->index == firstItemIndex) {
1601                 static_cast<FxGridItemSG*>(*it)->setPosition(firstVisible->colPos(),
1602                                                              firstVisible->rowPos());
1603                 break;
1604             }
1605         }
1606     }
1607
1608     // Fix current index
1609     if (d->currentIndex >= 0 && d->currentItem) {
1610         int oldCurrent = d->currentIndex;
1611         d->currentIndex = d->model->indexOf(d->currentItem->item, this);
1612         if (oldCurrent != d->currentIndex) {
1613             d->currentItem->index = d->currentIndex;
1614             emit currentIndexChanged();
1615         }
1616     }
1617
1618     // Whatever moved items remain are no longer visible items.
1619     while (moved.count()) {
1620         int idx = moved.begin().key();
1621         FxGridItemSG *item = moved.take(idx);
1622         if (d->currentItem && item->item == d->currentItem->item)
1623             item->setPosition(d->colPosAt(idx), d->rowPosAt(idx));
1624         d->releaseItem(item);
1625     }
1626
1627     // Ensure we don't cause an ugly list scroll.
1628     if (d->visibleItems.count() && moveByCount > 0) {
1629         FxGridItemSG *first = static_cast<FxGridItemSG*>(d->visibleItems.first());
1630         first->setPosition(first->colPos(), first->rowPos() + ((moveByCount / d->columns) * d->rowSize()));
1631     }
1632
1633     d->layout();
1634 }
1635
1636
1637 QSGGridViewAttached *QSGGridView::qmlAttachedProperties(QObject *obj)
1638 {
1639     return new QSGGridViewAttached(obj);
1640 }
1641
1642 QT_END_NAMESPACE