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