Views do not notify count changes properly
[profile/ivi/qtdeclarative.git] / src / declarative / graphicsitems / qdeclarativegridview.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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativegridview_p.h"
43
44 #include "private/qdeclarativevisualitemmodel_p.h"
45 #include "private/qdeclarativeflickable_p_p.h"
46
47 #include "private/qdeclarativesmoothedanimation_p_p.h"
48 #include <qdeclarativeguard_p.h>
49
50 #include <qlistmodelinterface_p.h>
51 #include <QKeyEvent>
52
53 #include <qmath.h>
54 #include <math.h>
55
56 QT_BEGIN_NAMESPACE
57
58
59 //----------------------------------------------------------------------------
60
61 class FxGridItem
62 {
63 public:
64     FxGridItem(QDeclarativeItem *i, QDeclarativeGridView *v) : item(i), view(v) {
65         attached = static_cast<QDeclarativeGridViewAttached*>(qmlAttachedPropertiesObject<QDeclarativeGridView>(item));
66         if (attached)
67             attached->setView(view);
68     }
69     ~FxGridItem() {}
70
71     qreal rowPos() const {
72         qreal rowPos = 0;
73         if (view->flow() == QDeclarativeGridView::LeftToRight) {
74             rowPos = item->y();
75         } else {
76             if (view->effectiveLayoutDirection() == Qt::RightToLeft)
77                 rowPos = -view->cellWidth()-item->x();
78             else
79                 rowPos = item->x();
80         }
81         return rowPos;
82     }
83     qreal colPos() const {
84         qreal colPos = 0;
85         if (view->flow() == QDeclarativeGridView::LeftToRight) {
86             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
87                 int colSize = view->cellWidth();
88                 int columns = view->width()/colSize;
89                 colPos = colSize * (columns-1) - item->x();
90             } else {
91                 colPos = item->x();
92             }
93         } else {
94             colPos = item->y();
95         }
96
97         return colPos;
98     }
99
100     qreal endRowPos() const {
101         if (view->flow() == QDeclarativeGridView::LeftToRight) {
102             return item->y() + view->cellHeight() - 1;
103         } else {
104             if (view->effectiveLayoutDirection() == Qt::RightToLeft)
105                 return -item->x() - 1;
106             else
107                 return item->x() + view->cellWidth() - 1;
108         }
109     }
110     void setPosition(qreal col, qreal row) {
111         if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
112             if (view->flow() == QDeclarativeGridView::LeftToRight) {
113                 int columns = view->width()/view->cellWidth();
114                 item->setPos(QPointF((view->cellWidth() * (columns-1) - col), row));
115             } else {
116                 item->setPos(QPointF(-view->cellWidth()-row, col));
117             }
118         } else {
119             if (view->flow() == QDeclarativeGridView::LeftToRight)
120                 item->setPos(QPointF(col, row));
121             else
122                 item->setPos(QPointF(row, col));
123         }
124
125     }
126     bool contains(qreal x, qreal y) const {
127         return (x >= item->x() && x < item->x() + view->cellWidth() &&
128                 y >= item->y() && y < item->y() + view->cellHeight());
129     }
130
131     QDeclarativeItem *item;
132     QDeclarativeGridView *view;
133     QDeclarativeGridViewAttached *attached;
134     int index;
135 };
136
137 //----------------------------------------------------------------------------
138
139 class QDeclarativeGridViewPrivate : public QDeclarativeFlickablePrivate
140 {
141     Q_DECLARE_PUBLIC(QDeclarativeGridView)
142
143 public:
144     QDeclarativeGridViewPrivate()
145     : currentItem(0), layoutDirection(Qt::LeftToRight), flow(QDeclarativeGridView::LeftToRight)
146     , visibleIndex(0) , currentIndex(-1)
147     , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), itemCount(0)
148     , highlightRangeStart(0), highlightRangeEnd(0)
149     , highlightRangeStartValid(false), highlightRangeEndValid(false)
150     , highlightRange(QDeclarativeGridView::NoHighlightRange)
151     , highlightComponent(0), highlight(0), trackedItem(0)
152     , moveReason(Other), buffer(0), highlightXAnimator(0), highlightYAnimator(0)
153     , highlightMoveDuration(150)
154     , footerComponent(0), footer(0), headerComponent(0), header(0)
155     , bufferMode(BufferBefore | BufferAfter), snapMode(QDeclarativeGridView::NoSnap)
156     , ownModel(false), wrap(false), autoHighlight(true)
157     , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false)
158     , deferredRelease(false), haveHighlightRange(false), currentIndexCleared(false) {}
159
160     void init();
161     void clear();
162     FxGridItem *createItem(int modelIndex);
163     void releaseItem(FxGridItem *item);
164     void refill(qreal from, qreal to, bool doBuffer=false);
165
166     void updateGrid();
167     void scheduleLayout();
168     void layout();
169     void updateUnrequestedIndexes();
170     void updateUnrequestedPositions();
171     void updateTrackedItem();
172     void createHighlight();
173     void updateHighlight();
174     void updateCurrent(int modelIndex);
175     void updateHeader();
176     void updateFooter();
177     void fixupPosition();
178
179     FxGridItem *visibleItem(int modelIndex) const {
180         if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
181             for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
182                 FxGridItem *item = visibleItems.at(i);
183                 if (item->index == modelIndex)
184                     return item;
185             }
186         }
187         return 0;
188     }
189
190     bool isRightToLeftTopToBottom() const {
191         Q_Q(const QDeclarativeGridView);
192         return flow == QDeclarativeGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft;
193     }
194
195     void regenerate() {
196         Q_Q(QDeclarativeGridView);
197         if (q->isComponentComplete()) {
198             clear();
199             updateGrid();
200             setPosition(0);
201             q->refill();
202             updateCurrent(currentIndex);
203         }
204     }
205
206     void mirrorChange() {
207         Q_Q(QDeclarativeGridView);
208         regenerate();
209         emit q->effectiveLayoutDirectionChanged();
210     }
211
212     qreal position() const {
213         Q_Q(const QDeclarativeGridView);
214         return flow == QDeclarativeGridView::LeftToRight ? q->contentY() : q->contentX();
215     }
216     void setPosition(qreal pos) {
217         Q_Q(QDeclarativeGridView);
218         if (flow == QDeclarativeGridView::LeftToRight) {
219             q->QDeclarativeFlickable::setContentY(pos);
220             q->QDeclarativeFlickable::setContentX(0);
221         } else {
222             if (q->effectiveLayoutDirection() == Qt::LeftToRight)
223                 q->QDeclarativeFlickable::setContentX(pos);
224             else
225                 q->QDeclarativeFlickable::setContentX(-pos-size());
226             q->QDeclarativeFlickable::setContentY(0);
227         }
228     }
229     int size() const {
230         Q_Q(const QDeclarativeGridView);
231         return flow == QDeclarativeGridView::LeftToRight ? q->height() : q->width();
232     }
233     qreal originPosition() const {
234         qreal pos = 0;
235         if (!visibleItems.isEmpty())
236             pos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize();
237         return pos;
238     }
239
240     qreal lastPosition() const {
241         qreal pos = 0;
242         if (model && model->count())
243             pos = rowPosAt(model->count() - 1) + rowSize();
244         return pos;
245     }
246
247     qreal startPosition() const {
248         return isRightToLeftTopToBottom() ? -lastPosition()+1 : originPosition();
249     }
250
251     qreal endPosition() const {
252         return isRightToLeftTopToBottom() ? -originPosition()+1 : lastPosition();
253
254     }
255
256     bool isValid() const {
257         return model && model->count() && model->isValid();
258     }
259
260     int rowSize() const {
261         return flow == QDeclarativeGridView::LeftToRight ? cellHeight : cellWidth;
262     }
263     int colSize() const {
264         return flow == QDeclarativeGridView::LeftToRight ? cellWidth : cellHeight;
265     }
266
267     qreal colPosAt(int modelIndex) const {
268         if (FxGridItem *item = visibleItem(modelIndex))
269             return item->colPos();
270         if (!visibleItems.isEmpty()) {
271             if (modelIndex < visibleIndex) {
272                 int count = (visibleIndex - modelIndex) % columns;
273                 int col = visibleItems.first()->colPos() / colSize();
274                 col = (columns - count + col) % columns;
275                 return col * colSize();
276             } else {
277                 int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns;
278                 return visibleItems.last()->colPos() - count * colSize();
279             }
280         } else {
281             return (modelIndex % columns) * colSize();
282         }
283         return 0;
284     }
285     qreal rowPosAt(int modelIndex) const {
286         if (FxGridItem *item = visibleItem(modelIndex))
287             return item->rowPos();
288         if (!visibleItems.isEmpty()) {
289             if (modelIndex < visibleIndex) {
290                 int firstCol = visibleItems.first()->colPos() / colSize();
291                 int col = visibleIndex - modelIndex + (columns - firstCol - 1);
292                 int rows = col / columns;
293                 return visibleItems.first()->rowPos() - rows * rowSize();
294             } else {
295                 int count = modelIndex - visibleItems.last()->index;
296                 int col = visibleItems.last()->colPos() + count * colSize();
297                 int rows = col / (columns * colSize());
298                 return visibleItems.last()->rowPos() + rows * rowSize();
299             }
300         } else {
301             qreal pos = (modelIndex / columns) * rowSize();
302             if (header)
303                 pos += headerSize();
304             return pos;
305         }
306         return 0;
307     }
308
309     FxGridItem *firstVisibleItem() const {
310         const qreal pos = isRightToLeftTopToBottom() ? -position()-size() : position();
311         for (int i = 0; i < visibleItems.count(); ++i) {
312             FxGridItem *item = visibleItems.at(i);
313             if (item->index != -1 && item->endRowPos() > pos)
314                 return item;
315         }
316         return visibleItems.count() ? visibleItems.first() : 0;
317     }
318
319     int lastVisibleIndex() const {
320         for (int i = 0; i < visibleItems.count(); ++i) {
321             FxGridItem *item = visibleItems.at(i);
322             if (item->index != -1)
323                 return item->index;
324         }
325         return -1;
326     }
327
328     // Map a model index to visibleItems list index.
329     // These may differ if removed items are still present in the visible list,
330     // e.g. doing a removal animation
331     int mapFromModel(int modelIndex) const {
332         if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
333             return -1;
334         for (int i = 0; i < visibleItems.count(); ++i) {
335             FxGridItem *listItem = visibleItems.at(i);
336             if (listItem->index == modelIndex)
337                 return i + visibleIndex;
338             if (listItem->index > modelIndex)
339                 return -1;
340         }
341         return -1; // Not in visibleList
342     }
343
344     qreal snapPosAt(qreal pos) const {
345         Q_Q(const QDeclarativeGridView);
346         qreal snapPos = 0;
347         if (!visibleItems.isEmpty()) {
348             pos += rowSize()/2;
349             snapPos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize();
350             snapPos = pos - fmodf(pos - snapPos, qreal(rowSize()));
351             qreal maxExtent;
352             qreal minExtent;
353             if (isRightToLeftTopToBottom()) {
354                 maxExtent = q->minXExtent();
355                 minExtent = q->maxXExtent();
356             } else {
357                 maxExtent = flow == QDeclarativeGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent();
358                 minExtent = flow == QDeclarativeGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent();
359             }
360             if (snapPos > maxExtent)
361                 snapPos = maxExtent;
362             if (snapPos < minExtent)
363                 snapPos = minExtent;
364         }
365         return snapPos;
366     }
367
368     FxGridItem *snapItemAt(qreal pos) {
369         for (int i = 0; i < visibleItems.count(); ++i) {
370             FxGridItem *item = visibleItems[i];
371             if (item->index == -1)
372                 continue;
373             qreal itemTop = item->rowPos();
374             if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos)
375                 return item;
376         }
377         return 0;
378     }
379
380     int snapIndex() {
381         int index = currentIndex;
382         for (int i = 0; i < visibleItems.count(); ++i) {
383             FxGridItem *item = visibleItems[i];
384             if (item->index == -1)
385                 continue;
386             qreal itemTop = item->rowPos();
387             if (itemTop >= highlight->rowPos()-rowSize()/2 && itemTop < highlight->rowPos()+rowSize()/2) {
388                 index = item->index;
389                 if (item->colPos() >= highlight->colPos()-colSize()/2 && item->colPos() < highlight->colPos()+colSize()/2)
390                     return item->index;
391             }
392         }
393         return index;
394     }
395
396     qreal headerSize() const {
397         if (!header)
398             return 0.0;
399
400         return flow == QDeclarativeGridView::LeftToRight
401                        ? header->item->height()
402                        : header->item->width();
403     }
404
405
406     virtual void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
407         Q_Q(const QDeclarativeGridView);
408         QDeclarativeFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
409         if (item == q) {
410             if (newGeometry.height() != oldGeometry.height()
411                 || newGeometry.width() != oldGeometry.width()) {
412                 if (q->isComponentComplete()) {
413                     updateGrid();
414                     scheduleLayout();
415                 }
416             }
417         } else if ((header && header->item == item) || (footer && footer->item == item)) {
418             if (header)
419                 updateHeader();
420             if (footer)
421                 updateFooter();
422         }
423     }
424
425     void positionViewAtIndex(int index, int mode);
426     virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
427     virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
428                 QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
429
430     // for debugging only
431     void checkVisible() const {
432         int skip = 0;
433         for (int i = 0; i < visibleItems.count(); ++i) {
434             FxGridItem *listItem = visibleItems.at(i);
435             if (listItem->index == -1) {
436                 ++skip;
437             } else if (listItem->index != visibleIndex + i - skip) {
438                 for (int j = 0; j < visibleItems.count(); j++)
439                     qDebug() << " index" << j << "item index" << visibleItems.at(j)->index;
440                 qFatal("index %d %d %d", visibleIndex, i, listItem->index);
441             }
442         }
443     }
444
445     QDeclarativeGuard<QDeclarativeVisualModel> model;
446     QVariant modelVariant;
447     QList<FxGridItem*> visibleItems;
448     QHash<QDeclarativeItem*,int> unrequestedItems;
449     FxGridItem *currentItem;
450     Qt::LayoutDirection layoutDirection;
451     QDeclarativeGridView::Flow flow;
452     int visibleIndex;
453     int currentIndex;
454     int cellWidth;
455     int cellHeight;
456     int columns;
457     int requestedIndex;
458     int itemCount;
459     qreal highlightRangeStart;
460     qreal highlightRangeEnd;
461     bool highlightRangeStartValid;
462     bool highlightRangeEndValid;
463     QDeclarativeGridView::HighlightRangeMode highlightRange;
464     QDeclarativeComponent *highlightComponent;
465     FxGridItem *highlight;
466     FxGridItem *trackedItem;
467     enum MovementReason { Other, SetIndex, Mouse };
468     MovementReason moveReason;
469     int buffer;
470     QSmoothedAnimation *highlightXAnimator;
471     QSmoothedAnimation *highlightYAnimator;
472     int highlightMoveDuration;
473     QDeclarativeComponent *footerComponent;
474     FxGridItem *footer;
475     QDeclarativeComponent *headerComponent;
476     FxGridItem *header;
477     enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
478     int bufferMode;
479     QDeclarativeGridView::SnapMode snapMode;
480
481     bool ownModel : 1;
482     bool wrap : 1;
483     bool autoHighlight : 1;
484     bool fixCurrentVisibility : 1;
485     bool lazyRelease : 1;
486     bool layoutScheduled : 1;
487     bool deferredRelease : 1;
488     bool haveHighlightRange : 1;
489     bool currentIndexCleared : 1;
490 };
491
492 void QDeclarativeGridViewPrivate::init()
493 {
494     Q_Q(QDeclarativeGridView);
495     QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
496     q->setFlag(QGraphicsItem::ItemIsFocusScope);
497     q->setFlickableDirection(QDeclarativeFlickable::VerticalFlick);
498     addItemChangeListener(this, Geometry);
499 }
500
501 void QDeclarativeGridViewPrivate::clear()
502 {
503     for (int i = 0; i < visibleItems.count(); ++i)
504         releaseItem(visibleItems.at(i));
505     visibleItems.clear();
506     visibleIndex = 0;
507     releaseItem(currentItem);
508     currentItem = 0;
509     createHighlight();
510     trackedItem = 0;
511     itemCount = 0;
512 }
513
514 FxGridItem *QDeclarativeGridViewPrivate::createItem(int modelIndex)
515 {
516     Q_Q(QDeclarativeGridView);
517     // create object
518     requestedIndex = modelIndex;
519     FxGridItem *listItem = 0;
520     if (QDeclarativeItem *item = model->item(modelIndex, false)) {
521         listItem = new FxGridItem(item, q);
522         listItem->index = modelIndex;
523         if (model->completePending()) {
524             // complete
525             listItem->item->setZValue(1);
526             listItem->item->setParentItem(q->contentItem());
527             model->completeItem();
528         } else {
529             listItem->item->setParentItem(q->contentItem());
530         }
531         unrequestedItems.remove(listItem->item);
532     }
533     requestedIndex = -1;
534     return listItem;
535 }
536
537
538 void QDeclarativeGridViewPrivate::releaseItem(FxGridItem *item)
539 {
540     Q_Q(QDeclarativeGridView);
541     if (!item || !model)
542         return;
543     if (trackedItem == item) {
544         QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged()));
545         QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged()));
546         trackedItem = 0;
547     }
548     if (model->release(item->item) == 0) {
549         // item was not destroyed, and we no longer reference it.
550         unrequestedItems.insert(item->item, model->indexOf(item->item, q));
551     }
552     delete item;
553 }
554
555 void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer)
556 {
557     Q_Q(QDeclarativeGridView);
558     if (!isValid() || !q->isComponentComplete())
559         return;
560     itemCount = model->count();
561     qreal bufferFrom = from - buffer;
562     qreal bufferTo = to + buffer;
563     qreal fillFrom = from;
564     qreal fillTo = to;
565     if (doBuffer && (bufferMode & BufferAfter))
566         fillTo = bufferTo;
567     if (doBuffer && (bufferMode & BufferBefore))
568         fillFrom = bufferFrom;
569
570     bool changed = false;
571
572     int colPos = colPosAt(visibleIndex);
573     int rowPos = rowPosAt(visibleIndex);
574     int modelIndex = visibleIndex;
575     if (visibleItems.count()) {
576         rowPos = visibleItems.last()->rowPos();
577         colPos = visibleItems.last()->colPos() + colSize();
578         if (colPos > colSize() * (columns-1)) {
579             colPos = 0;
580             rowPos += rowSize();
581         }
582         int i = visibleItems.count() - 1;
583         while (i > 0 && visibleItems.at(i)->index == -1)
584             --i;
585         modelIndex = visibleItems.at(i)->index + 1;
586     }
587
588     if (visibleItems.count() && (fillFrom > rowPos + rowSize()*2
589         || fillTo < rowPosAt(visibleIndex) - rowSize())) {
590         // We've jumped more than a page.  Estimate which items are now
591         // visible and fill from there.
592         int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns;
593         for (int i = 0; i < visibleItems.count(); ++i)
594             releaseItem(visibleItems.at(i));
595         visibleItems.clear();
596         modelIndex += count;
597         if (modelIndex >= model->count())
598             modelIndex = model->count() - 1;
599         else if (modelIndex < 0)
600             modelIndex = 0;
601         modelIndex = modelIndex / columns * columns;
602         visibleIndex = modelIndex;
603         colPos = colPosAt(visibleIndex);
604         rowPos = rowPosAt(visibleIndex);
605     }
606
607     int colNum = colPos / colSize();
608
609     FxGridItem *item = 0;
610
611     // Item creation and release is staggered in order to avoid
612     // creating/releasing multiple items in one frame
613     // while flicking (as much as possible).
614     while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) {
615 //        qDebug() << "refill: append item" << modelIndex;
616         if (!(item = createItem(modelIndex)))
617             break;
618         item->setPosition(colPos, rowPos);
619         visibleItems.append(item);
620         colPos += colSize();
621         colNum++;
622         if (colPos > colSize() * (columns-1)) {
623             colPos = 0;
624             colNum = 0;
625             rowPos += rowSize();
626         }
627         ++modelIndex;
628         changed = true;
629         if (doBuffer) // never buffer more than one item per frame
630             break;
631     }
632
633     if (visibleItems.count()) {
634         rowPos = visibleItems.first()->rowPos();
635         colPos = visibleItems.first()->colPos() - colSize();
636         if (colPos < 0) {
637             colPos = colSize() * (columns - 1);
638             rowPos -= rowSize();
639         }
640     }
641     colNum = colPos / colSize();
642     while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){
643 //        qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos;
644         if (!(item = createItem(visibleIndex-1)))
645             break;
646         --visibleIndex;
647         item->setPosition(colPos, rowPos);
648         visibleItems.prepend(item);
649         colPos -= colSize();
650         colNum--;
651         if (colPos < 0) {
652             colPos = colSize() * (columns - 1);
653             colNum = columns-1;
654             rowPos -= rowSize();
655         }
656         changed = true;
657         if (doBuffer) // never buffer more than one item per frame
658             break;
659     }
660
661     if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
662         while (visibleItems.count() > 1
663                && (item = visibleItems.first())
664                     && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) {
665             if (item->attached->delayRemove())
666                 break;
667 //            qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos();
668             if (item->index != -1)
669                 visibleIndex++;
670             visibleItems.removeFirst();
671             releaseItem(item);
672             changed = true;
673         }
674         while (visibleItems.count() > 1
675                && (item = visibleItems.last())
676                     && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) {
677             if (item->attached->delayRemove())
678                 break;
679 //            qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1;
680             visibleItems.removeLast();
681             releaseItem(item);
682             changed = true;
683         }
684         deferredRelease = false;
685     } else {
686         deferredRelease = true;
687     }
688     if (changed) {
689         if (header)
690             updateHeader();
691         if (footer)
692             updateFooter();
693         if (flow == QDeclarativeGridView::LeftToRight)
694             q->setContentHeight(endPosition() - startPosition());
695         else
696             q->setContentWidth(endPosition() - startPosition());
697     } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
698         refill(from, to, true);
699     }
700     lazyRelease = false;
701 }
702
703 void QDeclarativeGridViewPrivate::updateGrid()
704 {
705     Q_Q(QDeclarativeGridView);
706
707     columns = (int)qMax((flow == QDeclarativeGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.));
708     if (isValid()) {
709         if (flow == QDeclarativeGridView::LeftToRight)
710             q->setContentHeight(endPosition() - startPosition());
711         else
712             q->setContentWidth(lastPosition() - originPosition());
713     }
714 }
715
716 void QDeclarativeGridViewPrivate::scheduleLayout()
717 {
718     Q_Q(QDeclarativeGridView);
719     if (!layoutScheduled) {
720         layoutScheduled = true;
721         QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority);
722     }
723 }
724
725 void QDeclarativeGridViewPrivate::layout()
726 {
727     Q_Q(QDeclarativeGridView);
728     layoutScheduled = false;
729     if (!isValid() && !visibleItems.count()) {
730         clear();
731         return;
732     }
733     if (visibleItems.count()) {
734         qreal rowPos = visibleItems.first()->rowPos();
735         qreal colPos = visibleItems.first()->colPos();
736         int col = visibleIndex % columns;
737         if (colPos != col * colSize()) {
738             colPos = col * colSize();
739             visibleItems.first()->setPosition(colPos, rowPos);
740         }
741         for (int i = 1; i < visibleItems.count(); ++i) {
742             FxGridItem *item = visibleItems.at(i);
743             colPos += colSize();
744             if (colPos > colSize() * (columns-1)) {
745                 colPos = 0;
746                 rowPos += rowSize();
747             }
748             item->setPosition(colPos, rowPos);
749         }
750     }
751     if (header)
752         updateHeader();
753     if (footer)
754         updateFooter();
755     q->refill();
756     updateHighlight();
757     moveReason = Other;
758     if (flow == QDeclarativeGridView::LeftToRight) {
759         q->setContentHeight(endPosition() - startPosition());
760         fixupY();
761     } else {
762         q->setContentWidth(endPosition() - startPosition());
763         fixupX();
764     }
765     updateUnrequestedPositions();
766 }
767
768 void QDeclarativeGridViewPrivate::updateUnrequestedIndexes()
769 {
770     Q_Q(QDeclarativeGridView);
771     QHash<QDeclarativeItem*,int>::iterator it;
772     for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
773         *it = model->indexOf(it.key(), q);
774 }
775
776 void QDeclarativeGridViewPrivate::updateUnrequestedPositions()
777 {
778     QHash<QDeclarativeItem*,int>::const_iterator it;
779     for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
780         QDeclarativeItem *item = it.key();
781         if (flow == QDeclarativeGridView::LeftToRight) {
782             item->setPos(QPointF(colPosAt(*it), rowPosAt(*it)));
783         } else {
784             if (isRightToLeftTopToBottom())
785                 item->setPos(QPointF(-rowPosAt(*it)-item->width(), colPosAt(*it)));
786             else
787                 item->setPos(QPointF(rowPosAt(*it), colPosAt(*it)));
788         }
789     }
790 }
791
792 void QDeclarativeGridViewPrivate::updateTrackedItem()
793 {
794     Q_Q(QDeclarativeGridView);
795     FxGridItem *item = currentItem;
796     if (highlight)
797         item = highlight;
798
799     if (trackedItem && item != trackedItem) {
800         QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged()));
801         QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged()));
802         trackedItem = 0;
803     }
804
805     if (!trackedItem && item) {
806         trackedItem = item;
807         QObject::connect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged()));
808         QObject::connect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged()));
809     }
810     if (trackedItem)
811         q->trackedPositionChanged();
812 }
813
814 void QDeclarativeGridViewPrivate::createHighlight()
815 {
816     Q_Q(QDeclarativeGridView);
817     bool changed = false;
818     if (highlight) {
819         if (trackedItem == highlight)
820             trackedItem = 0;
821         delete highlight->item;
822         delete highlight;
823         highlight = 0;
824         delete highlightXAnimator;
825         delete highlightYAnimator;
826         highlightXAnimator = 0;
827         highlightYAnimator = 0;
828         changed = true;
829     }
830
831     if (currentItem) {
832         QDeclarativeItem *item = 0;
833         if (highlightComponent) {
834             QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
835             QObject *nobj = highlightComponent->create(highlightContext);
836             if (nobj) {
837                 QDeclarative_setParent_noEvent(highlightContext, nobj);
838                 item = qobject_cast<QDeclarativeItem *>(nobj);
839                 if (!item)
840                     delete nobj;
841             } else {
842                 delete highlightContext;
843             }
844         } else {
845             item = new QDeclarativeItem;
846             QDeclarative_setParent_noEvent(item, q->contentItem());
847             item->setParentItem(q->contentItem());
848         }
849         if (item) {
850             QDeclarative_setParent_noEvent(item, q->contentItem());
851             item->setParentItem(q->contentItem());
852             highlight = new FxGridItem(item, q);
853             if (currentItem && autoHighlight)
854                 highlight->setPosition(currentItem->colPos(), currentItem->rowPos());
855             highlightXAnimator = new QSmoothedAnimation(q);
856             highlightXAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("x"));
857             highlightXAnimator->userDuration = highlightMoveDuration;
858             highlightYAnimator = new QSmoothedAnimation(q);
859             highlightYAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("y"));
860             highlightYAnimator->userDuration = highlightMoveDuration;
861             if (autoHighlight) {
862                 highlightXAnimator->restart();
863                 highlightYAnimator->restart();
864             }
865             changed = true;
866         }
867     }
868     if (changed)
869         emit q->highlightItemChanged();
870 }
871
872 void QDeclarativeGridViewPrivate::updateHighlight()
873 {
874     if ((!currentItem && highlight) || (currentItem && !highlight))
875         createHighlight();
876     if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
877         // auto-update highlight
878         highlightXAnimator->to = currentItem->item->x();
879         highlightYAnimator->to = currentItem->item->y();
880         highlight->item->setWidth(currentItem->item->width());
881         highlight->item->setHeight(currentItem->item->height());
882         highlightXAnimator->restart();
883         highlightYAnimator->restart();
884     }
885     updateTrackedItem();
886 }
887
888 void QDeclarativeGridViewPrivate::updateCurrent(int modelIndex)
889 {
890     Q_Q(QDeclarativeGridView);
891     if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
892         if (currentItem) {
893             currentItem->attached->setIsCurrentItem(false);
894             releaseItem(currentItem);
895             currentItem = 0;
896             currentIndex = modelIndex;
897             emit q->currentIndexChanged();
898             updateHighlight();
899         } else if (currentIndex != modelIndex) {
900             currentIndex = modelIndex;
901             emit q->currentIndexChanged();
902         }
903         return;
904     }
905
906     if (currentItem && currentIndex == modelIndex) {
907         updateHighlight();
908         return;
909     }
910
911     FxGridItem *oldCurrentItem = currentItem;
912     currentIndex = modelIndex;
913     currentItem = createItem(modelIndex);
914     fixCurrentVisibility = true;
915     if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
916         oldCurrentItem->attached->setIsCurrentItem(false);
917     if (currentItem) {
918         currentItem->setPosition(colPosAt(modelIndex), rowPosAt(modelIndex));
919         currentItem->item->setFocus(true);
920         currentItem->attached->setIsCurrentItem(true);
921     }
922     updateHighlight();
923     emit q->currentIndexChanged();
924     releaseItem(oldCurrentItem);
925 }
926
927 void QDeclarativeGridViewPrivate::updateFooter()
928 {
929     Q_Q(QDeclarativeGridView);
930     if (!footer && footerComponent) {
931         QDeclarativeItem *item = 0;
932         QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
933         QObject *nobj = footerComponent->create(context);
934         if (nobj) {
935             QDeclarative_setParent_noEvent(context, nobj);
936             item = qobject_cast<QDeclarativeItem *>(nobj);
937             if (!item)
938                 delete nobj;
939         } else {
940             delete context;
941         }
942         if (item) {
943             QDeclarative_setParent_noEvent(item, q->contentItem());
944             item->setParentItem(q->contentItem());
945             item->setZValue(1);
946             QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
947             itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
948             footer = new FxGridItem(item, q);
949         }
950     }
951     if (footer) {
952         qreal colOffset = 0;
953         qreal rowOffset;
954         if (isRightToLeftTopToBottom()) {
955             rowOffset = footer->item->width()-cellWidth;
956         } else {
957             rowOffset = 0;
958             if (q->effectiveLayoutDirection() == Qt::RightToLeft)
959                 colOffset = footer->item->width()-cellWidth;
960         }
961         if (visibleItems.count()) {
962             qreal endPos = lastPosition();
963             if (lastVisibleIndex() == model->count()-1) {
964                 footer->setPosition(colOffset, endPos + rowOffset);
965             } else {
966                 qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size();
967                 if (endPos <= visiblePos || footer->endRowPos() < endPos + rowOffset)
968                     footer->setPosition(colOffset, endPos + rowOffset);
969             }
970         } else {
971             qreal endPos = 0;
972             if (header) {
973                 endPos += flow == QDeclarativeGridView::LeftToRight ? header->item->height() : header->item->width();
974             }
975             footer->setPosition(colOffset, endPos);
976         }
977     }
978 }
979
980 void QDeclarativeGridViewPrivate::updateHeader()
981 {
982     Q_Q(QDeclarativeGridView);
983     if (!header && headerComponent) {
984         QDeclarativeItem *item = 0;
985         QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
986         QObject *nobj = headerComponent->create(context);
987         if (nobj) {
988             QDeclarative_setParent_noEvent(context, nobj);
989             item = qobject_cast<QDeclarativeItem *>(nobj);
990             if (!item)
991                 delete nobj;
992         } else {
993             delete context;
994         }
995         if (item) {
996             QDeclarative_setParent_noEvent(item, q->contentItem());
997             item->setParentItem(q->contentItem());
998             item->setZValue(1);
999             QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item));
1000             itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry);
1001             header = new FxGridItem(item, q);
1002         }
1003     }
1004     if (header) {
1005         qreal colOffset = 0;
1006         qreal rowOffset;
1007         if (isRightToLeftTopToBottom()) {
1008             rowOffset = -cellWidth;
1009         } else {
1010             rowOffset = -headerSize();
1011             if (q->effectiveLayoutDirection() == Qt::RightToLeft)
1012                 colOffset = header->item->width()-cellWidth;
1013         }
1014         if (visibleItems.count()) {
1015             qreal startPos = originPosition();
1016             if (visibleIndex == 0) {
1017                 header->setPosition(colOffset, startPos + rowOffset);
1018             } else {
1019                 qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position();
1020                 qreal headerPos = isRightToLeftTopToBottom() ? header->rowPos() + cellWidth - headerSize() : header->rowPos();
1021                 if (tempPos <= startPos || headerPos > startPos + rowOffset)
1022                     header->setPosition(colOffset, startPos + rowOffset);
1023             }
1024         } else {
1025             header->setPosition(colOffset, 0);
1026         }
1027     }
1028 }
1029
1030 void QDeclarativeGridViewPrivate::fixupPosition()
1031 {
1032     moveReason = Other;
1033     if (flow == QDeclarativeGridView::LeftToRight)
1034         fixupY();
1035     else
1036         fixupX();
1037 }
1038
1039 void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1040 {
1041     if ((flow == QDeclarativeGridView::TopToBottom && &data == &vData)
1042         || (flow == QDeclarativeGridView::LeftToRight && &data == &hData))
1043         return;
1044
1045     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1046
1047     qreal highlightStart;
1048     qreal highlightEnd;
1049     qreal viewPos;
1050     if (isRightToLeftTopToBottom()) {
1051         // Handle Right-To-Left exceptions
1052         viewPos = -position()-size();
1053         highlightStart = highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1054         highlightEnd = highlightRangeEndValid ? size()-highlightRangeStart : highlightRangeEnd;
1055     } else {
1056         viewPos = position();
1057         highlightStart = highlightRangeStart;
1058         highlightEnd = highlightRangeEnd;
1059     }
1060
1061     if (snapMode != QDeclarativeGridView::NoSnap) {
1062         qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position();
1063         FxGridItem *topItem = snapItemAt(tempPosition+highlightStart);
1064         FxGridItem *bottomItem = snapItemAt(tempPosition+highlightEnd);
1065         qreal pos;
1066         if (topItem && bottomItem && haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) {
1067             qreal topPos = qMin(topItem->rowPos() - highlightStart, -maxExtent);
1068             qreal bottomPos = qMax(bottomItem->rowPos() - highlightEnd, -minExtent);
1069             pos = qAbs(data.move + topPos) < qAbs(data.move + bottomPos) ? topPos : bottomPos;
1070         } else if (topItem) {
1071             qreal headerPos = 0;
1072             if (header)
1073                 headerPos = isRightToLeftTopToBottom() ? header->rowPos() + cellWidth - headerSize() : header->rowPos();
1074             if (topItem->index == 0 && header && tempPosition+highlightStart < headerPos+headerSize()/2) {
1075                 pos = isRightToLeftTopToBottom() ? - headerPos + highlightStart - size() : headerPos - highlightStart;
1076             } else {
1077                 if (isRightToLeftTopToBottom())
1078                     pos = qMax(qMin(-topItem->rowPos() + highlightStart - size(), -maxExtent), -minExtent);
1079                 else
1080                     pos = qMax(qMin(topItem->rowPos() - highlightStart, -maxExtent), -minExtent);
1081             }
1082         } else if (bottomItem) {
1083             if (isRightToLeftTopToBottom())
1084                 pos = qMax(qMin(-bottomItem->rowPos() + highlightStart - size(), -maxExtent), -minExtent);
1085             else
1086                 pos = qMax(qMin(bottomItem->rowPos() - highlightStart, -maxExtent), -minExtent);
1087         } else {
1088             QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1089             return;
1090         }
1091         if (currentItem && haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) {
1092             updateHighlight();
1093             qreal currPos = currentItem->rowPos();
1094             if (isRightToLeftTopToBottom())
1095                 pos = -pos-size(); // Transform Pos if required
1096             if (pos < currPos + rowSize() - highlightEnd)
1097                 pos = currPos + rowSize() - highlightEnd;
1098             if (pos > currPos - highlightStart)
1099                 pos = currPos - highlightStart;
1100             if (isRightToLeftTopToBottom())
1101                 pos = -pos-size(); // Untransform
1102         }
1103         qreal dist = qAbs(data.move + pos);
1104         if (dist > 0) {
1105             timeline.reset(data.move);
1106             if (fixupMode != Immediate) {
1107                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1108                 data.fixingUp = true;
1109             } else {
1110                 timeline.set(data.move, -pos);
1111             }
1112             vTime = timeline.time();
1113         }
1114     } else if (haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) {
1115         if (currentItem) {
1116             updateHighlight();
1117             qreal pos = currentItem->rowPos();
1118             if (viewPos < pos + rowSize() - highlightEnd)
1119                 viewPos = pos + rowSize() - highlightEnd;
1120             if (viewPos > pos - highlightStart)
1121                 viewPos = pos - highlightStart;
1122             if (isRightToLeftTopToBottom())
1123                 viewPos = -viewPos-size();
1124             timeline.reset(data.move);
1125             if (viewPos != position()) {
1126                 if (fixupMode != Immediate) {
1127                     timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1128                     data.fixingUp = true;
1129                 } else {
1130                     timeline.set(data.move, -viewPos);
1131                 }
1132             }
1133             vTime = timeline.time();
1134         }
1135     } else {
1136         QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1137     }
1138     data.inOvershoot = false;
1139     fixupMode = Normal;
1140 }
1141
1142 void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1143                                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1144 {
1145     Q_Q(QDeclarativeGridView);
1146     data.fixingUp = false;
1147     moveReason = Mouse;
1148     if ((!haveHighlightRange || highlightRange != QDeclarativeGridView::StrictlyEnforceRange)
1149         && snapMode == QDeclarativeGridView::NoSnap) {
1150         QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1151         return;
1152     }
1153     qreal maxDistance = 0;
1154     qreal dataValue = isRightToLeftTopToBottom() ? -data.move.value()+size() : data.move.value();
1155     // -ve velocity means list is moving up/left
1156     if (velocity > 0) {
1157         if (data.move.value() < minExtent) {
1158             if (snapMode == QDeclarativeGridView::SnapOneRow) {
1159                 if (FxGridItem *item = firstVisibleItem()) {
1160                     maxDistance = qAbs(item->rowPos() + dataValue);
1161                 }
1162             } else {
1163                 maxDistance = qAbs(minExtent - data.move.value());
1164             }
1165         }
1166         if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange)
1167             data.flickTarget = minExtent;
1168     } else {
1169         if (data.move.value() > maxExtent) {
1170             if (snapMode == QDeclarativeGridView::SnapOneRow) {
1171                 qreal pos = snapPosAt(-dataValue) + (isRightToLeftTopToBottom() ? 0 : rowSize());
1172                 maxDistance = qAbs(pos + dataValue);
1173             } else {
1174                 maxDistance = qAbs(maxExtent - data.move.value());
1175             }
1176         }
1177         if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange)
1178             data.flickTarget = maxExtent;
1179     }
1180
1181     bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds;
1182     qreal highlightStart = isRightToLeftTopToBottom() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1183
1184     if (maxDistance > 0 || overShoot) {
1185         // This mode requires the grid to stop exactly on a row boundary.
1186         qreal v = velocity;
1187         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1188             if (v < 0)
1189                 v = -maxVelocity;
1190             else
1191                 v = maxVelocity;
1192         }
1193         qreal accel = deceleration;
1194         qreal v2 = v * v;
1195         qreal overshootDist = 0.0;
1196         if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeGridView::SnapOneRow) {
1197             // + rowSize()/4 to encourage moving at least one item in the flick direction
1198             qreal dist = v2 / (accel * 2.0) + rowSize()/4;
1199             dist = qMin(dist, maxDistance);
1200             if (v > 0)
1201                 dist = -dist;
1202             qreal distTemp = isRightToLeftTopToBottom() ? -dist : dist;
1203             data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1204             data.flickTarget = isRightToLeftTopToBottom() ? -data.flickTarget+size() : data.flickTarget;
1205             qreal adjDist = -data.flickTarget + data.move.value();
1206             if (qAbs(adjDist) > qAbs(dist)) {
1207                 // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1208                 qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1209                 if (adjv2 > v2) {
1210                     v2 = adjv2;
1211                     v = qSqrt(v2);
1212                     if (dist > 0)
1213                         v = -v;
1214                 }
1215             }
1216             dist = adjDist;
1217             accel = v2 / (2.0f * qAbs(dist));
1218         } else {
1219             data.flickTarget = velocity > 0 ? minExtent : maxExtent;
1220             overshootDist = overShoot ? overShootDistance(vSize) : 0;
1221         }
1222         timeline.reset(data.move);
1223         timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1224         timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1225         if (!flickingHorizontally && q->xflick()) {
1226             flickingHorizontally = true;
1227             emit q->flickingChanged();
1228             emit q->flickingHorizontallyChanged();
1229             emit q->flickStarted();
1230         }
1231         if (!flickingVertically && q->yflick()) {
1232             flickingVertically = true;
1233             emit q->flickingChanged();
1234             emit q->flickingVerticallyChanged();
1235             emit q->flickStarted();
1236         }
1237     } else {
1238         timeline.reset(data.move);
1239         fixup(data, minExtent, maxExtent);
1240     }
1241 }
1242
1243
1244 //----------------------------------------------------------------------------
1245
1246 /*!
1247     \qmlclass GridView QDeclarativeGridView
1248     \since 4.7
1249     \ingroup qml-view-elements
1250
1251     \inherits Flickable
1252     \brief The GridView item provides a grid view of items provided by a model.
1253
1254     A GridView displays data from models created from built-in QML elements like ListModel
1255     and XmlListModel, or custom model classes defined in C++ that inherit from
1256     QAbstractListModel.
1257
1258     A GridView has a \l model, which defines the data to be displayed, and
1259     a \l delegate, which defines how the data should be displayed. Items in a 
1260     GridView are laid out horizontally or vertically. Grid views are inherently flickable
1261     as GridView inherits from \l Flickable.
1262
1263     \section1 Example Usage
1264
1265     The following example shows the definition of a simple list model defined
1266     in a file called \c ContactModel.qml:
1267
1268     \snippet doc/src/snippets/declarative/gridview/ContactModel.qml 0
1269
1270     \div {class="float-right"}
1271     \inlineimage gridview-simple.png
1272     \enddiv
1273
1274     This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules}
1275     for more information about creating reusable components like this.
1276
1277     Another component can display this model data in a GridView, as in the following
1278     example, which creates a \c ContactModel component for its model, and a \l Column element
1279     (containing \l Image and \l Text elements) for its delegate.
1280
1281     \clearfloat
1282     \snippet doc/src/snippets/declarative/gridview/gridview.qml import
1283     \codeline
1284     \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs simple
1285
1286     \div {class="float-right"}
1287     \inlineimage gridview-highlight.png
1288     \enddiv
1289
1290     The view will create a new delegate for each item in the model. Note that the delegate
1291     is able to access the model's \c name and \c portrait data directly.
1292
1293     An improved grid view is shown below. The delegate is visually improved and is moved 
1294     into a separate \c contactDelegate component.
1295
1296     \clearfloat
1297     \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs advanced
1298
1299     The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1300     and \c focus is set to \c true to enable keyboard navigation for the grid view.
1301     The grid view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1302
1303     Delegates are instantiated as needed and may be destroyed at any time.
1304     State should \e never be stored in a delegate.
1305
1306     GridView attaches a number of properties to the root item of the delegate, for example
1307     \c {GridView.isCurrentItem}.  In the following example, the root delegate item can access
1308     this attached property directly as \c GridView.isCurrentItem, while the child
1309     \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem.
1310
1311     \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem
1312
1313     \note Views do not set the \l{Item::}{clip} property automatically.
1314     If the view is not clipped by another item or the screen, it will be necessary
1315     to set this property to true in order to clip the items that are partially or
1316     fully outside the view.
1317
1318     \sa {declarative/modelviews/gridview}{GridView example}
1319 */
1320 QDeclarativeGridView::QDeclarativeGridView(QDeclarativeItem *parent)
1321     : QDeclarativeFlickable(*(new QDeclarativeGridViewPrivate), parent)
1322 {
1323     Q_D(QDeclarativeGridView);
1324     d->init();
1325 }
1326
1327 QDeclarativeGridView::~QDeclarativeGridView()
1328 {
1329     Q_D(QDeclarativeGridView);
1330     d->clear();
1331     if (d->ownModel)
1332         delete d->model;
1333     delete d->header;
1334     delete d->footer;
1335 }
1336
1337 /*!
1338     \qmlattachedproperty bool GridView::isCurrentItem
1339     This attached property is true if this delegate is the current item; otherwise false.
1340
1341     It is attached to each instance of the delegate.
1342 */
1343
1344 /*!
1345     \qmlattachedproperty GridView GridView::view
1346     This attached property holds the view that manages this delegate instance.
1347
1348     It is attached to each instance of the delegate.
1349
1350     \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem
1351 */
1352
1353 /*!
1354     \qmlattachedproperty bool GridView::delayRemove
1355     This attached property holds whether the delegate may be destroyed.
1356
1357     It is attached to each instance of the delegate.
1358
1359     It is sometimes necessary to delay the destruction of an item
1360     until an animation completes.
1361
1362     The example below ensures that the animation completes before
1363     the item is removed from the grid.
1364
1365     \snippet doc/src/snippets/declarative/gridview/gridview.qml delayRemove
1366 */
1367
1368 /*!
1369     \qmlattachedsignal GridView::onAdd()
1370     This attached handler is called immediately after an item is added to the view.
1371 */
1372
1373 /*!
1374     \qmlattachedsignal GridView::onRemove()
1375     This attached handler is called immediately before an item is removed from the view.
1376 */
1377
1378
1379 /*!
1380   \qmlproperty model GridView::model
1381   This property holds the model providing data for the grid.
1382
1383     The model provides the set of data that is used to create the items
1384     in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1385     or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1386     used, it must be a subclass of \l QAbstractItemModel or a simple list.
1387
1388   \sa {qmlmodels}{Data Models}
1389 */
1390 QVariant QDeclarativeGridView::model() const
1391 {
1392     Q_D(const QDeclarativeGridView);
1393     return d->modelVariant;
1394 }
1395
1396 // For internal use
1397 int QDeclarativeGridView::modelCount() const
1398 {
1399     Q_D(const QDeclarativeGridView);
1400     return d->model->count();
1401 }
1402
1403 void QDeclarativeGridView::setModel(const QVariant &model)
1404 {
1405     Q_D(QDeclarativeGridView);
1406     if (d->modelVariant == model)
1407         return;
1408     if (d->model) {
1409         disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1410         disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1411         disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1412         disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1413         disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1414         disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1415     }
1416     d->clear();
1417     d->modelVariant = model;
1418     QObject *object = qvariant_cast<QObject*>(model);
1419     QDeclarativeVisualModel *vim = 0;
1420     if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) {
1421         if (d->ownModel) {
1422             delete d->model;
1423             d->ownModel = false;
1424         }
1425         d->model = vim;
1426     } else {
1427         if (!d->ownModel) {
1428             d->model = new QDeclarativeVisualDataModel(qmlContext(this), this);
1429             d->ownModel = true;
1430         }
1431         if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1432             dataModel->setModel(model);
1433     }
1434     if (d->model) {
1435         d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore | QDeclarativeGridViewPrivate::BufferAfter;
1436         if (isComponentComplete()) {
1437             refill();
1438             if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
1439                 setCurrentIndex(0);
1440             } else {
1441                 d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
1442                 d->updateCurrent(d->currentIndex);
1443                 if (d->highlight && d->currentItem) {
1444                     if (d->autoHighlight)
1445                         d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
1446                     d->updateTrackedItem();
1447                 }
1448                 d->moveReason = QDeclarativeGridViewPrivate::Other;
1449             }
1450         }
1451         connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1452         connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1453         connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1454         connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1455         connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1456         connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1457         emit countChanged();
1458     }
1459     emit modelChanged();
1460 }
1461
1462 /*!
1463     \qmlproperty Component GridView::delegate
1464
1465     The delegate provides a template defining each item instantiated by the view.
1466     The index is exposed as an accessible \c index property.  Properties of the
1467     model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1468
1469     The number of elements in the delegate has a direct effect on the
1470     flicking performance of the view.  If at all possible, place functionality
1471     that is not needed for the normal display of the delegate in a \l Loader which
1472     can load additional elements when needed.
1473
1474     The GridView will layout the items based on the size of the root item
1475     in the delegate.
1476
1477     \note Delegates are instantiated as needed and may be destroyed at any time.
1478     State should \e never be stored in a delegate.
1479 */
1480 QDeclarativeComponent *QDeclarativeGridView::delegate() const
1481 {
1482     Q_D(const QDeclarativeGridView);
1483     if (d->model) {
1484         if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1485             return dataModel->delegate();
1486     }
1487
1488     return 0;
1489 }
1490
1491 void QDeclarativeGridView::setDelegate(QDeclarativeComponent *delegate)
1492 {
1493     Q_D(QDeclarativeGridView);
1494     if (delegate == this->delegate())
1495         return;
1496
1497     if (!d->ownModel) {
1498         d->model = new QDeclarativeVisualDataModel(qmlContext(this));
1499         d->ownModel = true;
1500     }
1501     if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) {
1502         int oldCount = dataModel->count();
1503         dataModel->setDelegate(delegate);
1504         if (isComponentComplete()) {
1505             for (int i = 0; i < d->visibleItems.count(); ++i)
1506                 d->releaseItem(d->visibleItems.at(i));
1507             d->visibleItems.clear();
1508             d->releaseItem(d->currentItem);
1509             d->currentItem = 0;
1510             refill();
1511             d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
1512             d->updateCurrent(d->currentIndex);
1513             if (d->highlight && d->currentItem) {
1514                 if (d->autoHighlight)
1515                     d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
1516                 d->updateTrackedItem();
1517             }
1518             d->moveReason = QDeclarativeGridViewPrivate::Other;
1519         }
1520         if (oldCount != dataModel->count())
1521             emit countChanged();
1522         emit delegateChanged();
1523     }
1524 }
1525
1526 /*!
1527   \qmlproperty int GridView::currentIndex
1528   \qmlproperty Item GridView::currentItem
1529
1530     The \c currentIndex property holds the index of the current item, and
1531     \c currentItem holds the current item.  Setting the currentIndex to -1
1532     will clear the highlight and set currentItem to null.
1533
1534     If highlightFollowsCurrentItem is \c true, setting either of these 
1535     properties will smoothly scroll the GridView so that the current 
1536     item becomes visible.
1537     
1538     Note that the position of the current item
1539     may only be approximate until it becomes visible in the view.
1540 */
1541 int QDeclarativeGridView::currentIndex() const
1542 {
1543     Q_D(const QDeclarativeGridView);
1544     return d->currentIndex;
1545 }
1546
1547 void QDeclarativeGridView::setCurrentIndex(int index)
1548 {
1549     Q_D(QDeclarativeGridView);
1550     if (d->requestedIndex >= 0) // currently creating item
1551         return;
1552     d->currentIndexCleared = (index == -1);
1553     if (index == d->currentIndex)
1554         return;
1555     if (isComponentComplete() && d->isValid()) {
1556         d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
1557         d->updateCurrent(index);
1558     } else {
1559         d->currentIndex = index;
1560         emit currentIndexChanged();
1561     }
1562 }
1563
1564 QDeclarativeItem *QDeclarativeGridView::currentItem()
1565 {
1566     Q_D(QDeclarativeGridView);
1567     if (!d->currentItem)
1568         return 0;
1569     return d->currentItem->item;
1570 }
1571
1572 /*!
1573   \qmlproperty Item GridView::highlightItem
1574
1575   This holds the highlight item created from the \l highlight component.
1576
1577   The highlightItem is managed by the view unless
1578   \l highlightFollowsCurrentItem is set to false.
1579
1580   \sa highlight, highlightFollowsCurrentItem
1581 */
1582 QDeclarativeItem *QDeclarativeGridView::highlightItem()
1583 {
1584     Q_D(QDeclarativeGridView);
1585     if (!d->highlight)
1586         return 0;
1587     return d->highlight->item;
1588 }
1589
1590 /*!
1591   \qmlproperty int GridView::count
1592   This property holds the number of items in the view.
1593 */
1594 int QDeclarativeGridView::count() const
1595 {
1596     Q_D(const QDeclarativeGridView);
1597     if (d->model)
1598         return d->model->count();
1599     return 0;
1600 }
1601
1602 /*!
1603   \qmlproperty Component GridView::highlight
1604   This property holds the component to use as the highlight.
1605
1606   An instance of the highlight component is created for each view.
1607   The geometry of the resulting component instance will be managed by the view
1608   so as to stay with the current item, unless the highlightFollowsCurrentItem property is false.
1609
1610   \sa highlightItem, highlightFollowsCurrentItem
1611 */
1612 QDeclarativeComponent *QDeclarativeGridView::highlight() const
1613 {
1614     Q_D(const QDeclarativeGridView);
1615     return d->highlightComponent;
1616 }
1617
1618 void QDeclarativeGridView::setHighlight(QDeclarativeComponent *highlight)
1619 {
1620     Q_D(QDeclarativeGridView);
1621     if (highlight != d->highlightComponent) {
1622         d->highlightComponent = highlight;
1623         d->updateCurrent(d->currentIndex);
1624         emit highlightChanged();
1625     }
1626 }
1627
1628 /*!
1629   \qmlproperty bool GridView::highlightFollowsCurrentItem
1630   This property sets whether the highlight is managed by the view.
1631
1632     If this property is true (the default value), the highlight is moved smoothly
1633     to follow the current item.  Otherwise, the
1634     highlight is not moved by the view, and any movement must be implemented
1635     by the highlight.  
1636     
1637     Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1638
1639     \snippet doc/src/snippets/declarative/gridview/gridview.qml highlightFollowsCurrentItem
1640 */
1641 bool QDeclarativeGridView::highlightFollowsCurrentItem() const
1642 {
1643     Q_D(const QDeclarativeGridView);
1644     return d->autoHighlight;
1645 }
1646
1647 void QDeclarativeGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
1648 {
1649     Q_D(QDeclarativeGridView);
1650     if (d->autoHighlight != autoHighlight) {
1651         d->autoHighlight = autoHighlight;
1652         if (autoHighlight) {
1653             d->updateHighlight();
1654         } else if (d->highlightXAnimator) {
1655             d->highlightXAnimator->stop();
1656             d->highlightYAnimator->stop();
1657         }
1658     }
1659 }
1660
1661 /*!
1662     \qmlproperty int GridView::highlightMoveDuration
1663     This property holds the move animation duration of the highlight delegate.
1664
1665     highlightFollowsCurrentItem must be true for this property
1666     to have effect.
1667
1668     The default value for the duration is 150ms.
1669
1670     \sa highlightFollowsCurrentItem
1671 */
1672 int QDeclarativeGridView::highlightMoveDuration() const
1673 {
1674     Q_D(const QDeclarativeGridView);
1675     return d->highlightMoveDuration;
1676 }
1677
1678 void QDeclarativeGridView::setHighlightMoveDuration(int duration)
1679 {
1680     Q_D(QDeclarativeGridView);
1681     if (d->highlightMoveDuration != duration) {
1682         d->highlightMoveDuration = duration;
1683         if (d->highlightYAnimator) {
1684             d->highlightXAnimator->userDuration = d->highlightMoveDuration;
1685             d->highlightYAnimator->userDuration = d->highlightMoveDuration;
1686         }
1687         emit highlightMoveDurationChanged();
1688     }
1689 }
1690
1691
1692 /*!
1693     \qmlproperty real GridView::preferredHighlightBegin
1694     \qmlproperty real GridView::preferredHighlightEnd
1695     \qmlproperty enumeration GridView::highlightRangeMode
1696
1697     These properties define the preferred range of the highlight (for the current item)
1698     within the view. The \c preferredHighlightBegin value must be less than the
1699     \c preferredHighlightEnd value. 
1700
1701     These properties affect the position of the current item when the view is scrolled.
1702     For example, if the currently selected item should stay in the middle of the
1703     view when it is scrolled, set the \c preferredHighlightBegin and 
1704     \c preferredHighlightEnd values to the top and bottom coordinates of where the middle 
1705     item would be. If the \c currentItem is changed programmatically, the view will
1706     automatically scroll so that the current item is in the middle of the view.
1707     Furthermore, the behavior of the current item index will occur whether or not a
1708     highlight exists.
1709
1710     Valid values for \c highlightRangeMode are:
1711
1712     \list
1713     \o GridView.ApplyRange - the view attempts to maintain the highlight within the range.
1714        However, the highlight can move outside of the range at the ends of the view or due
1715        to mouse interaction.
1716     \o GridView.StrictlyEnforceRange - the highlight never moves outside of the range.
1717        The current item changes if a keyboard or mouse action would cause the highlight to move
1718        outside of the range.
1719     \o GridView.NoHighlightRange - this is the default value.
1720     \endlist
1721 */
1722 qreal QDeclarativeGridView::preferredHighlightBegin() const
1723 {
1724     Q_D(const QDeclarativeGridView);
1725     return d->highlightRangeStart;
1726 }
1727
1728 void QDeclarativeGridView::setPreferredHighlightBegin(qreal start)
1729 {
1730     Q_D(QDeclarativeGridView);
1731     d->highlightRangeStartValid = true;
1732     if (d->highlightRangeStart == start)
1733         return;
1734     d->highlightRangeStart = start;
1735     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1736     emit preferredHighlightBeginChanged();
1737 }
1738
1739 void QDeclarativeGridView::resetPreferredHighlightBegin()
1740 {
1741     Q_D(QDeclarativeGridView);
1742     d->highlightRangeStartValid = false;
1743     if (d->highlightRangeStart == 0)
1744         return;
1745     d->highlightRangeStart = 0;
1746     emit preferredHighlightBeginChanged();
1747 }
1748
1749 qreal QDeclarativeGridView::preferredHighlightEnd() const
1750 {
1751     Q_D(const QDeclarativeGridView);
1752     return d->highlightRangeEnd;
1753 }
1754
1755 void QDeclarativeGridView::setPreferredHighlightEnd(qreal end)
1756 {
1757     Q_D(QDeclarativeGridView);
1758     d->highlightRangeEndValid = true;
1759     if (d->highlightRangeEnd == end)
1760         return;
1761     d->highlightRangeEnd = end;
1762     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1763     emit preferredHighlightEndChanged();
1764 }
1765
1766 void QDeclarativeGridView::resetPreferredHighlightEnd()
1767 {
1768     Q_D(QDeclarativeGridView);
1769     d->highlightRangeEndValid = false;
1770     if (d->highlightRangeEnd == 0)
1771         return;
1772     d->highlightRangeEnd = 0;
1773     emit preferredHighlightEndChanged();
1774 }
1775
1776 QDeclarativeGridView::HighlightRangeMode QDeclarativeGridView::highlightRangeMode() const
1777 {
1778     Q_D(const QDeclarativeGridView);
1779     return d->highlightRange;
1780 }
1781
1782 void QDeclarativeGridView::setHighlightRangeMode(HighlightRangeMode mode)
1783 {
1784     Q_D(QDeclarativeGridView);
1785     if (d->highlightRange == mode)
1786         return;
1787     d->highlightRange = mode;
1788     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1789     emit highlightRangeModeChanged();
1790 }
1791
1792 /*!
1793   \qmlproperty enumeration GridView::layoutDirection
1794   This property holds the layout direction of the grid.
1795
1796     Possible values:
1797
1798   \list
1799   \o Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is
1800   dependent on the \l GridView::flow property.
1801   \o Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent
1802   on the \l GridView:flow property.
1803   \endlist
1804
1805   \bold Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if
1806   GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply
1807   indicates that the flow is horizontal.
1808 */
1809
1810 Qt::LayoutDirection QDeclarativeGridView::layoutDirection() const
1811 {
1812     Q_D(const QDeclarativeGridView);
1813     return d->layoutDirection;
1814 }
1815
1816 void QDeclarativeGridView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1817 {
1818     Q_D(QDeclarativeGridView);
1819     if (d->layoutDirection != layoutDirection) {
1820         d->layoutDirection = layoutDirection;
1821         d->regenerate();
1822         emit layoutDirectionChanged();
1823         emit effectiveLayoutDirectionChanged();
1824     }
1825 }
1826
1827 /*!
1828     \qmlproperty enumeration GridView::effectiveLayoutDirection
1829     This property holds the effective layout direction of the grid.
1830
1831     When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1832     the visual layout direction of the grid will be mirrored. However, the
1833     property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged.
1834
1835     \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1836 */
1837
1838 Qt::LayoutDirection QDeclarativeGridView::effectiveLayoutDirection() const
1839 {
1840     Q_D(const QDeclarativeGridView);
1841     if (d->effectiveLayoutMirror)
1842         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
1843     else
1844         return d->layoutDirection;
1845 }
1846
1847 /*!
1848   \qmlproperty enumeration GridView::flow
1849   This property holds the flow of the grid.
1850
1851     Possible values:
1852
1853     \list
1854     \o GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically
1855     \o GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally
1856     \endlist
1857 */
1858 QDeclarativeGridView::Flow QDeclarativeGridView::flow() const
1859 {
1860     Q_D(const QDeclarativeGridView);
1861     return d->flow;
1862 }
1863
1864 void QDeclarativeGridView::setFlow(Flow flow)
1865 {
1866     Q_D(QDeclarativeGridView);
1867     if (d->flow != flow) {
1868         d->flow = flow;
1869         if (d->flow == LeftToRight) {
1870             setContentWidth(-1);
1871             setFlickableDirection(QDeclarativeFlickable::VerticalFlick);
1872         } else {
1873             setContentHeight(-1);
1874             setFlickableDirection(QDeclarativeFlickable::HorizontalFlick);
1875         }
1876         setContentX(0);
1877         setContentY(0);
1878         d->regenerate();
1879         emit flowChanged();
1880     }
1881 }
1882
1883 /*!
1884   \qmlproperty bool GridView::keyNavigationWraps
1885   This property holds whether the grid wraps key navigation
1886
1887     If this is true, key navigation that would move the current item selection
1888     past one end of the view instead wraps around and moves the selection to
1889     the other end of the view.
1890
1891     By default, key navigation is not wrapped.
1892 */
1893 bool QDeclarativeGridView::isWrapEnabled() const
1894 {
1895     Q_D(const QDeclarativeGridView);
1896     return d->wrap;
1897 }
1898
1899 void QDeclarativeGridView::setWrapEnabled(bool wrap)
1900 {
1901     Q_D(QDeclarativeGridView);
1902     if (d->wrap == wrap)
1903         return;
1904     d->wrap = wrap;
1905     emit keyNavigationWrapsChanged();
1906 }
1907
1908 /*!
1909     \qmlproperty int GridView::cacheBuffer
1910     This property determines whether delegates are retained outside the
1911     visible area of the view.
1912
1913     If non-zero the view will keep as many delegates
1914     instantiated as will fit within the buffer specified.  For example,
1915     if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
1916     set to 40, then up to 2 delegates above and 2 delegates below the visible
1917     area may be retained.
1918
1919     Note that cacheBuffer is not a pixel buffer - it only maintains additional
1920     instantiated delegates.
1921
1922     Setting this value can make scrolling the list smoother at the expense
1923     of additional memory usage.  It is not a substitute for creating efficient
1924     delegates; the fewer elements in a delegate, the faster a view may be
1925     scrolled.
1926 */
1927 int QDeclarativeGridView::cacheBuffer() const
1928 {
1929     Q_D(const QDeclarativeGridView);
1930     return d->buffer;
1931 }
1932
1933 void QDeclarativeGridView::setCacheBuffer(int buffer)
1934 {
1935     Q_D(QDeclarativeGridView);
1936     if (d->buffer != buffer) {
1937         d->buffer = buffer;
1938         if (isComponentComplete())
1939             refill();
1940         emit cacheBufferChanged();
1941     }
1942 }
1943
1944 /*!
1945   \qmlproperty int GridView::cellWidth
1946   \qmlproperty int GridView::cellHeight
1947
1948   These properties holds the width and height of each cell in the grid.
1949
1950   The default cell size is 100x100.
1951 */
1952 int QDeclarativeGridView::cellWidth() const
1953 {
1954     Q_D(const QDeclarativeGridView);
1955     return d->cellWidth;
1956 }
1957
1958 void QDeclarativeGridView::setCellWidth(int cellWidth)
1959 {
1960     Q_D(QDeclarativeGridView);
1961     if (cellWidth != d->cellWidth && cellWidth > 0) {
1962         d->cellWidth = qMax(1, cellWidth);
1963         d->updateGrid();
1964         emit cellWidthChanged();
1965         d->layout();
1966     }
1967 }
1968
1969 int QDeclarativeGridView::cellHeight() const
1970 {
1971     Q_D(const QDeclarativeGridView);
1972     return d->cellHeight;
1973 }
1974
1975 void QDeclarativeGridView::setCellHeight(int cellHeight)
1976 {
1977     Q_D(QDeclarativeGridView);
1978     if (cellHeight != d->cellHeight && cellHeight > 0) {
1979         d->cellHeight = qMax(1, cellHeight);
1980         d->updateGrid();
1981         emit cellHeightChanged();
1982         d->layout();
1983     }
1984 }
1985 /*!
1986     \qmlproperty enumeration GridView::snapMode
1987
1988     This property determines how the view scrolling will settle following a drag or flick.
1989     The possible values are:
1990
1991     \list
1992     \o GridView.NoSnap (default) - the view stops anywhere within the visible area.
1993     \o GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow)
1994     aligned with the start of the view.
1995     \o GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow)
1996     away from the first visible row at the time the mouse button is released.
1997     This mode is particularly useful for moving one page at a time.
1998     \endlist
1999
2000 */
2001 QDeclarativeGridView::SnapMode QDeclarativeGridView::snapMode() const
2002 {
2003     Q_D(const QDeclarativeGridView);
2004     return d->snapMode;
2005 }
2006
2007 void QDeclarativeGridView::setSnapMode(SnapMode mode)
2008 {
2009     Q_D(QDeclarativeGridView);
2010     if (d->snapMode != mode) {
2011         d->snapMode = mode;
2012         emit snapModeChanged();
2013     }
2014 }
2015
2016 /*!
2017     \qmlproperty Component GridView::footer
2018     This property holds the component to use as the footer.
2019
2020     An instance of the footer component is created for each view.  The
2021     footer is positioned at the end of the view, after any items.
2022
2023     \sa header
2024 */
2025 QDeclarativeComponent *QDeclarativeGridView::footer() const
2026 {
2027     Q_D(const QDeclarativeGridView);
2028     return d->footerComponent;
2029 }
2030
2031 void QDeclarativeGridView::setFooter(QDeclarativeComponent *footer)
2032 {
2033     Q_D(QDeclarativeGridView);
2034     if (d->footerComponent != footer) {
2035         if (d->footer) {
2036             if (scene())
2037                 scene()->removeItem(d->footer->item);
2038             d->footer->item->deleteLater();
2039             delete d->footer;
2040             d->footer = 0;
2041         }
2042         d->footerComponent = footer;
2043         if (isComponentComplete()) {
2044             d->updateFooter();
2045             d->updateGrid();
2046             d->fixupPosition();
2047         }
2048         emit footerChanged();
2049     }
2050 }
2051
2052 /*!
2053     \qmlproperty Component GridView::header
2054     This property holds the component to use as the header.
2055
2056     An instance of the header component is created for each view.  The
2057     header is positioned at the beginning of the view, before any items.
2058
2059     \sa footer
2060 */
2061 QDeclarativeComponent *QDeclarativeGridView::header() const
2062 {
2063     Q_D(const QDeclarativeGridView);
2064     return d->headerComponent;
2065 }
2066
2067 void QDeclarativeGridView::setHeader(QDeclarativeComponent *header)
2068 {
2069     Q_D(QDeclarativeGridView);
2070     if (d->headerComponent != header) {
2071         if (d->header) {
2072             if (scene())
2073                 scene()->removeItem(d->header->item);
2074             d->header->item->deleteLater();
2075             delete d->header;
2076             d->header = 0;
2077         }
2078         d->headerComponent = header;
2079         if (isComponentComplete()) {
2080             d->updateHeader();
2081             d->updateFooter();
2082             d->updateGrid();
2083             d->fixupPosition();
2084         }
2085         emit headerChanged();
2086     }
2087 }
2088
2089 void QDeclarativeGridView::setContentX(qreal pos)
2090 {
2091     Q_D(QDeclarativeGridView);
2092     // Positioning the view manually should override any current movement state
2093     d->moveReason = QDeclarativeGridViewPrivate::Other;
2094     QDeclarativeFlickable::setContentX(pos);
2095 }
2096
2097 void QDeclarativeGridView::setContentY(qreal pos)
2098 {
2099     Q_D(QDeclarativeGridView);
2100     // Positioning the view manually should override any current movement state
2101     d->moveReason = QDeclarativeGridViewPrivate::Other;
2102     QDeclarativeFlickable::setContentY(pos);
2103 }
2104
2105 bool QDeclarativeGridView::event(QEvent *event)
2106 {
2107     Q_D(QDeclarativeGridView);
2108     if (event->type() == QEvent::User) {
2109         d->layout();
2110         return true;
2111     }
2112
2113     return QDeclarativeFlickable::event(event);
2114 }
2115
2116 void QDeclarativeGridView::viewportMoved()
2117 {
2118     Q_D(QDeclarativeGridView);
2119     QDeclarativeFlickable::viewportMoved();
2120     if (!d->itemCount)
2121         return;
2122     d->lazyRelease = true;
2123     if (d->flickingHorizontally || d->flickingVertically) {
2124         if (yflick()) {
2125             if (d->vData.velocity > 0)
2126                 d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore;
2127             else if (d->vData.velocity < 0)
2128                 d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter;
2129         }
2130
2131         if (xflick()) {
2132             if (d->hData.velocity > 0)
2133                 d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore;
2134             else if (d->hData.velocity < 0)
2135                 d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter;
2136         }
2137     }
2138     refill();
2139     if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
2140         d->moveReason = QDeclarativeGridViewPrivate::Mouse;
2141     if (d->moveReason != QDeclarativeGridViewPrivate::SetIndex) {
2142         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2143             // reposition highlight
2144             qreal pos = d->highlight->rowPos();
2145             qreal viewPos;
2146             qreal highlightStart;
2147             qreal highlightEnd;
2148             if (d->isRightToLeftTopToBottom()) {
2149                 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2150                 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2151                 viewPos = -d->position()-d->size();
2152             } else {
2153                 highlightStart = d->highlightRangeStart;
2154                 highlightEnd = d->highlightRangeEnd;
2155                 viewPos = d->position();
2156             }
2157             if (pos > viewPos + highlightEnd - d->rowSize())
2158                 pos = viewPos + highlightEnd - d->rowSize();
2159             if (pos < viewPos + highlightStart)
2160                 pos = viewPos + highlightStart;
2161
2162             d->highlight->setPosition(d->highlight->colPos(), qRound(pos));
2163
2164             // update current index
2165             int idx = d->snapIndex();
2166             if (idx >= 0 && idx != d->currentIndex) {
2167                 d->updateCurrent(idx);
2168                 if (d->currentItem && d->currentItem->colPos() != d->highlight->colPos() && d->autoHighlight) {
2169                     if (d->flow == LeftToRight)
2170                         d->highlightXAnimator->to = d->currentItem->item->x();
2171                     else
2172                         d->highlightYAnimator->to = d->currentItem->item->y();
2173                 }
2174             }
2175         }
2176     }
2177 }
2178
2179 qreal QDeclarativeGridView::minYExtent() const
2180 {
2181     Q_D(const QDeclarativeGridView);
2182     if (d->flow == QDeclarativeGridView::TopToBottom)
2183         return QDeclarativeFlickable::minYExtent();
2184     qreal extent = -d->startPosition();
2185     if (d->header && d->visibleItems.count())
2186         extent += d->header->item->height();
2187     if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2188         extent += d->highlightRangeStart;
2189         extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd));
2190     }
2191     return extent;
2192 }
2193
2194 qreal QDeclarativeGridView::maxYExtent() const
2195 {
2196     Q_D(const QDeclarativeGridView);
2197     if (d->flow == QDeclarativeGridView::TopToBottom)
2198         return QDeclarativeFlickable::maxYExtent();
2199     qreal extent;
2200     if (!d->model || !d->model->count()) {
2201         extent = 0;
2202     } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2203         extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart);
2204         if (d->highlightRangeEnd != d->highlightRangeStart)
2205             extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1));
2206     } else {
2207         extent = -(d->endPosition() - height());
2208     }
2209     if (d->footer)
2210         extent -= d->footer->item->height();
2211     const qreal minY = minYExtent();
2212     if (extent > minY)
2213         extent = minY;
2214     return extent;
2215 }
2216
2217 qreal QDeclarativeGridView::minXExtent() const
2218 {
2219     Q_D(const QDeclarativeGridView);
2220     if (d->flow == QDeclarativeGridView::LeftToRight)
2221         return QDeclarativeFlickable::minXExtent();
2222     qreal extent = -d->startPosition();
2223     qreal highlightStart;
2224     qreal highlightEnd;
2225     qreal endPositionFirstItem;
2226     if (d->isRightToLeftTopToBottom()) {
2227         endPositionFirstItem = d->rowPosAt(d->model->count()-1);
2228         highlightStart = d->highlightRangeStartValid
2229                 ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2230                 : d->size() - (d->lastPosition()-endPositionFirstItem);
2231         highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2232         if (d->footer && d->visibleItems.count())
2233             extent += d->footer->item->width();
2234     } else {
2235         endPositionFirstItem = d->rowPosAt(0)+d->rowSize();
2236         highlightStart = d->highlightRangeStart;
2237         highlightEnd = d->highlightRangeEnd;
2238         if (d->header && d->visibleItems.count())
2239             extent += d->header->item->width();
2240     }
2241     if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2242         extent += highlightStart;
2243         extent = qMax(extent, -(endPositionFirstItem - highlightEnd));
2244     }
2245     return extent;
2246 }
2247
2248 qreal QDeclarativeGridView::maxXExtent() const
2249 {
2250     Q_D(const QDeclarativeGridView);
2251     if (d->flow == QDeclarativeGridView::LeftToRight)
2252         return QDeclarativeFlickable::maxXExtent();
2253     qreal extent;
2254     qreal highlightStart;
2255     qreal highlightEnd;
2256     qreal lastItemPosition = 0;
2257     if (d->isRightToLeftTopToBottom()){
2258         highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2259         highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2260         lastItemPosition = d->endPosition();
2261     } else {
2262         highlightStart = d->highlightRangeStart;
2263         highlightEnd = d->highlightRangeEnd;
2264         lastItemPosition = 0;
2265         if (d->model && d->model->count())
2266             lastItemPosition = d->rowPosAt(d->model->count()-1);
2267     }
2268     if (!d->model || !d->model->count()) {
2269         extent = 0;
2270     } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2271         extent = -(lastItemPosition - highlightStart);
2272         if (highlightEnd != highlightStart)
2273             extent = d->isRightToLeftTopToBottom()
2274                     ? qMax(extent, -(d->endPosition() - highlightEnd + 1))
2275                     : qMin(extent, -(d->endPosition() - highlightEnd + 1));
2276     } else {
2277         extent = -(d->endPosition() - width());
2278     }
2279     if (d->isRightToLeftTopToBottom()) {
2280         if (d->header)
2281             extent -= d->header->item->width();
2282     } else {
2283         if (d->footer)
2284             extent -= d->footer->item->width();
2285     }
2286
2287     const qreal minX = minXExtent();
2288     if (extent > minX)
2289         extent = minX;
2290     return extent;
2291 }
2292
2293 void QDeclarativeGridView::keyPressEvent(QKeyEvent *event)
2294 {
2295     Q_D(QDeclarativeGridView);
2296     keyPressPreHandler(event);
2297     if (event->isAccepted())
2298         return;
2299     if (d->model && d->model->count() && d->interactive) {
2300         d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
2301         int oldCurrent = currentIndex();
2302         switch (event->key()) {
2303         case Qt::Key_Up:
2304             moveCurrentIndexUp();
2305             break;
2306         case Qt::Key_Down:
2307             moveCurrentIndexDown();
2308             break;
2309         case Qt::Key_Left:
2310             moveCurrentIndexLeft();
2311             break;
2312         case Qt::Key_Right:
2313             moveCurrentIndexRight();
2314             break;
2315         default:
2316             break;
2317         }
2318         if (oldCurrent != currentIndex()) {
2319             event->accept();
2320             return;
2321         }
2322     }
2323     d->moveReason = QDeclarativeGridViewPrivate::Other;
2324     event->ignore();
2325     QDeclarativeFlickable::keyPressEvent(event);
2326 }
2327
2328 /*!
2329     \qmlmethod GridView::moveCurrentIndexUp()
2330
2331     Move the currentIndex up one item in the view.
2332     The current index will wrap if keyNavigationWraps is true and it
2333     is currently at the end. This method has no effect if the \l count is zero.
2334
2335     \bold Note: methods should only be called after the Component has completed.
2336 */
2337 void QDeclarativeGridView::moveCurrentIndexUp()
2338 {
2339     Q_D(QDeclarativeGridView);
2340     const int count = d->model ? d->model->count() : 0;
2341     if (!count)
2342         return;
2343     if (d->flow == QDeclarativeGridView::LeftToRight) {
2344         if (currentIndex() >= d->columns || d->wrap) {
2345             int index = currentIndex() - d->columns;
2346             setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2347         }
2348     } else {
2349         if (currentIndex() > 0 || d->wrap) {
2350             int index = currentIndex() - 1;
2351             setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2352         }
2353     }
2354 }
2355
2356 /*!
2357     \qmlmethod GridView::moveCurrentIndexDown()
2358
2359     Move the currentIndex down one item in the view.
2360     The current index will wrap if keyNavigationWraps is true and it
2361     is currently at the end. This method has no effect if the \l count is zero.
2362
2363     \bold Note: methods should only be called after the Component has completed.
2364 */
2365 void QDeclarativeGridView::moveCurrentIndexDown()
2366 {
2367     Q_D(QDeclarativeGridView);
2368     const int count = d->model ? d->model->count() : 0;
2369     if (!count)
2370         return;
2371     if (d->flow == QDeclarativeGridView::LeftToRight) {
2372         if (currentIndex() < count - d->columns || d->wrap) {
2373             int index = currentIndex()+d->columns;
2374             setCurrentIndex((index >= 0 && index < count) ? index : 0);
2375         }
2376     } else {
2377         if (currentIndex() < count - 1 || d->wrap) {
2378             int index = currentIndex() + 1;
2379             setCurrentIndex((index >= 0 && index < count) ? index : 0);
2380         }
2381     }
2382 }
2383
2384 /*!
2385     \qmlmethod GridView::moveCurrentIndexLeft()
2386
2387     Move the currentIndex left one item in the view.
2388     The current index will wrap if keyNavigationWraps is true and it
2389     is currently at the end. This method has no effect if the \l count is zero.
2390
2391     \bold Note: methods should only be called after the Component has completed.
2392 */
2393 void QDeclarativeGridView::moveCurrentIndexLeft()
2394 {
2395     Q_D(QDeclarativeGridView);
2396     const int count = d->model ? d->model->count() : 0;
2397     if (!count)
2398         return;
2399
2400     if (effectiveLayoutDirection() == Qt::LeftToRight) {
2401         if (d->flow == QDeclarativeGridView::LeftToRight) {
2402             if (currentIndex() > 0 || d->wrap) {
2403                 int index = currentIndex() - 1;
2404                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2405             }
2406         } else {
2407             if (currentIndex() >= d->columns || d->wrap) {
2408                 int index = currentIndex() - d->columns;
2409                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2410             }
2411         }
2412     } else {
2413         if (d->flow == QDeclarativeGridView::LeftToRight) {
2414             if (currentIndex() < count - 1 || d->wrap) {
2415                 int index = currentIndex() + 1;
2416                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2417             }
2418         } else {
2419             if (currentIndex() < count - d->columns || d->wrap) {
2420                 int index = currentIndex() + d->columns;
2421                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2422             }
2423         }
2424     }
2425 }
2426
2427 /*!
2428     \qmlmethod GridView::moveCurrentIndexRight()
2429
2430     Move the currentIndex right one item in the view.
2431     The current index will wrap if keyNavigationWraps is true and it
2432     is currently at the end. This method has no effect if the \l count is zero.
2433
2434     \bold Note: methods should only be called after the Component has completed.
2435 */
2436 void QDeclarativeGridView::moveCurrentIndexRight()
2437 {
2438     Q_D(QDeclarativeGridView);
2439     const int count = d->model ? d->model->count() : 0;
2440     if (!count)
2441         return;
2442
2443     if (effectiveLayoutDirection() == Qt::LeftToRight) {
2444         if (d->flow == QDeclarativeGridView::LeftToRight) {
2445             if (currentIndex() < count - 1 || d->wrap) {
2446                 int index = currentIndex() + 1;
2447                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2448             }
2449         } else {
2450             if (currentIndex() < count - d->columns || d->wrap) {
2451                 int index = currentIndex()+d->columns;
2452                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2453             }
2454         }
2455     } else {
2456         if (d->flow == QDeclarativeGridView::LeftToRight) {
2457             if (currentIndex() > 0 || d->wrap) {
2458                 int index = currentIndex() - 1;
2459                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2460             }
2461         } else {
2462             if (currentIndex() >= d->columns || d->wrap) {
2463                 int index = currentIndex() - d->columns;
2464                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2465             }
2466         }
2467     }
2468 }
2469
2470 void QDeclarativeGridViewPrivate::positionViewAtIndex(int index, int mode)
2471 {
2472     Q_Q(QDeclarativeGridView);
2473     if (!isValid())
2474         return;
2475     if (mode < QDeclarativeGridView::Beginning || mode > QDeclarativeGridView::Contain)
2476         return;
2477
2478     int idx = qMax(qMin(index, model->count()-1), 0);
2479
2480     if (layoutScheduled)
2481         layout();
2482     qreal pos = isRightToLeftTopToBottom() ? -position() - size() : position();
2483     FxGridItem *item = visibleItem(idx);
2484     qreal maxExtent;
2485     if (flow == QDeclarativeGridView::LeftToRight)
2486         maxExtent = -q->maxYExtent();
2487     else
2488         maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent();
2489
2490     if (!item) {
2491         int itemPos = rowPosAt(idx);
2492         // save the currently visible items in case any of them end up visible again
2493         QList<FxGridItem*> oldVisible = visibleItems;
2494         visibleItems.clear();
2495         visibleIndex = idx - idx % columns;
2496         if (flow == QDeclarativeGridView::LeftToRight)
2497             maxExtent = -q->maxYExtent();
2498         else
2499             maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent();
2500         setPosition(qMin(qreal(itemPos), maxExtent));
2501         // now release the reference to all the old visible items.
2502         for (int i = 0; i < oldVisible.count(); ++i)
2503             releaseItem(oldVisible.at(i));
2504         item = visibleItem(idx);
2505     }
2506     if (item) {
2507         qreal itemPos = item->rowPos();
2508         switch (mode) {
2509         case QDeclarativeGridView::Beginning:
2510             pos = itemPos;
2511             if (index < 0 && header) {
2512                 pos -= flow == QDeclarativeGridView::LeftToRight
2513                             ? header->item->height()
2514                             : header->item->width();
2515             }
2516             break;
2517         case QDeclarativeGridView::Center:
2518             pos = itemPos - (size() - rowSize())/2;
2519             break;
2520         case QDeclarativeGridView::End:
2521             pos = itemPos - size() + rowSize();
2522             if (index >= model->count() && footer) {
2523                 pos += flow == QDeclarativeGridView::LeftToRight
2524                             ? footer->item->height()
2525                             : footer->item->width();
2526             }
2527             break;
2528         case QDeclarativeGridView::Visible:
2529             if (itemPos > pos + size())
2530                 pos = itemPos - size() + rowSize();
2531             else if (item->endRowPos() < pos)
2532                 pos = itemPos;
2533             break;
2534         case QDeclarativeGridView::Contain:
2535             if (item->endRowPos() > pos + size())
2536                 pos = itemPos - size() + rowSize();
2537             if (itemPos < pos)
2538                 pos = itemPos;
2539         }
2540
2541         pos = qMin(pos, maxExtent);
2542         qreal minExtent;
2543         if (flow == QDeclarativeGridView::LeftToRight)
2544             minExtent = -q->minYExtent();
2545         else
2546             minExtent = isRightToLeftTopToBottom() ? q->maxXExtent()-size() : -q->minXExtent();
2547         pos = qMax(pos, minExtent);
2548         moveReason = QDeclarativeGridViewPrivate::Other;
2549         q->cancelFlick();
2550         setPosition(pos);
2551     }
2552     fixupPosition();
2553 }
2554
2555 /*!
2556     \qmlmethod GridView::positionViewAtIndex(int index, PositionMode mode)
2557
2558     Positions the view such that the \a index is at the position specified by
2559     \a mode:
2560
2561     \list
2562     \o GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view.
2563     \o GridView.Center - position item in the center of the view.
2564     \o GridView.End - position item at bottom (or right for horizontal orientation) of the view.
2565     \o GridView.Visible - if any part of the item is visible then take no action, otherwise
2566     bring the item into view.
2567     \o GridView.Contain - ensure the entire item is visible.  If the item is larger than
2568     the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view.
2569     \endlist
2570
2571     If positioning the view at the index would cause empty space to be displayed at
2572     the beginning or end of the view, the view will be positioned at the boundary.
2573
2574     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2575     at a particular index.  This is unreliable since removing items from the start
2576     of the view does not cause all other items to be repositioned.
2577     The correct way to bring an item into view is with \c positionViewAtIndex.
2578
2579     \bold Note: methods should only be called after the Component has completed.  To position
2580     the view at startup, this method should be called by Component.onCompleted.  For
2581     example, to position the view at the end:
2582
2583     \code
2584     Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning)
2585     \endcode
2586 */
2587 void QDeclarativeGridView::positionViewAtIndex(int index, int mode)
2588 {
2589     Q_D(QDeclarativeGridView);
2590     if (!d->isValid() || index < 0 || index >= d->model->count())
2591         return;
2592     d->positionViewAtIndex(index, mode);
2593 }
2594
2595 /*!
2596     \qmlmethod GridView::positionViewAtBeginning()
2597     \qmlmethod GridView::positionViewAtEnd()
2598     \since Quick 1.1
2599
2600     Positions the view at the beginning or end, taking into account any header or footer.
2601
2602     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2603     at a particular index.  This is unreliable since removing items from the start
2604     of the list does not cause all other items to be repositioned, and because
2605     the actual start of the view can vary based on the size of the delegates.
2606
2607     \bold Note: methods should only be called after the Component has completed.  To position
2608     the view at startup, this method should be called by Component.onCompleted.  For
2609     example, to position the view at the end on startup:
2610
2611     \code
2612     Component.onCompleted: positionViewAtEnd()
2613     \endcode
2614 */
2615 void QDeclarativeGridView::positionViewAtBeginning()
2616 {
2617     Q_D(QDeclarativeGridView);
2618     if (!d->isValid())
2619         return;
2620     d->positionViewAtIndex(-1, Beginning);
2621 }
2622
2623 void QDeclarativeGridView::positionViewAtEnd()
2624 {
2625     Q_D(QDeclarativeGridView);
2626     if (!d->isValid())
2627         return;
2628     d->positionViewAtIndex(d->model->count(), End);
2629 }
2630
2631 /*!
2632     \qmlmethod int GridView::indexAt(int x, int y)
2633
2634     Returns the index of the visible item containing the point \a x, \a y in content
2635     coordinates.  If there is no item at the point specified, or the item is
2636     not visible -1 is returned.
2637
2638     If the item is outside the visible area, -1 is returned, regardless of
2639     whether an item will exist at that point when scrolled into view.
2640
2641     \bold Note: methods should only be called after the Component has completed.
2642 */
2643 int QDeclarativeGridView::indexAt(qreal x, qreal y) const
2644 {
2645     Q_D(const QDeclarativeGridView);
2646     for (int i = 0; i < d->visibleItems.count(); ++i) {
2647         const FxGridItem *listItem = d->visibleItems.at(i);
2648         if(listItem->contains(x, y))
2649             return listItem->index;
2650     }
2651
2652     return -1;
2653 }
2654
2655 void QDeclarativeGridView::componentComplete()
2656 {
2657     Q_D(QDeclarativeGridView);
2658     QDeclarativeFlickable::componentComplete();
2659     d->updateHeader();
2660     d->updateFooter();
2661     d->updateGrid();
2662     if (d->isValid()) {
2663         refill();
2664         d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
2665         if (d->currentIndex < 0 && !d->currentIndexCleared)
2666             d->updateCurrent(0);
2667         else
2668             d->updateCurrent(d->currentIndex);
2669         if (d->highlight && d->currentItem) {
2670             if (d->autoHighlight)
2671                 d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
2672             d->updateTrackedItem();
2673         }
2674         d->moveReason = QDeclarativeGridViewPrivate::Other;
2675         d->fixupPosition();
2676     }
2677 }
2678
2679 void QDeclarativeGridView::trackedPositionChanged()
2680 {
2681     Q_D(QDeclarativeGridView);
2682     if (!d->trackedItem || !d->currentItem)
2683         return;
2684     if (d->moveReason == QDeclarativeGridViewPrivate::SetIndex) {
2685         const qreal trackedPos = d->trackedItem->rowPos();
2686         qreal viewPos;
2687         qreal highlightStart;
2688         qreal highlightEnd;
2689         if (d->isRightToLeftTopToBottom()) {
2690             viewPos = -d->position()-d->size();
2691             highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2692             highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2693         } else {
2694             viewPos = d->position();
2695             highlightStart = d->highlightRangeStart;
2696             highlightEnd = d->highlightRangeEnd;
2697         }
2698         qreal pos = viewPos;
2699         if (d->haveHighlightRange) {
2700             if (d->highlightRange == StrictlyEnforceRange) {
2701                 if (trackedPos > pos + highlightEnd - d->rowSize())
2702                     pos = trackedPos - highlightEnd + d->rowSize();
2703                 if (trackedPos < pos + highlightStart)
2704                     pos = trackedPos - highlightStart;
2705             } else {
2706                 if (trackedPos < d->startPosition() + highlightStart) {
2707                     pos = d->startPosition();
2708                 } else if (d->trackedItem->endRowPos() > d->endPosition() - d->size() + highlightEnd) {
2709                     pos = d->endPosition() - d->size() + 1;
2710                     if (pos < d->startPosition())
2711                         pos = d->startPosition();
2712                 } else {
2713                     if (trackedPos < viewPos + highlightStart) {
2714                         pos = trackedPos - highlightStart;
2715                     } else if (trackedPos > viewPos + highlightEnd - d->rowSize()) {
2716                         pos = trackedPos - highlightEnd + d->rowSize();
2717                     }
2718                 }
2719             }
2720         } else {
2721             if (trackedPos < viewPos && d->currentItem->rowPos() < viewPos) {
2722                 pos = qMax(trackedPos, d->currentItem->rowPos());
2723             } else if (d->trackedItem->endRowPos() >= viewPos + d->size()
2724                 && d->currentItem->endRowPos() >= viewPos + d->size()) {
2725                 if (d->trackedItem->endRowPos() <= d->currentItem->endRowPos()) {
2726                     pos = d->trackedItem->endRowPos() - d->size() + 1;
2727                     if (d->rowSize() > d->size())
2728                         pos = trackedPos;
2729                 } else {
2730                     pos = d->currentItem->endRowPos() - d->size() + 1;
2731                     if (d->rowSize() > d->size())
2732                         pos = d->currentItem->rowPos();
2733                 }
2734             }
2735         }
2736         if (viewPos != pos) {
2737             cancelFlick();
2738             d->calcVelocity = true;
2739             d->setPosition(pos);
2740             d->calcVelocity = false;
2741         }
2742     }
2743 }
2744
2745 void QDeclarativeGridView::itemsInserted(int modelIndex, int count)
2746 {
2747     Q_D(QDeclarativeGridView);
2748     if (!isComponentComplete())
2749         return;
2750
2751     int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
2752     if (index < 0) {
2753         int i = d->visibleItems.count() - 1;
2754         while (i > 0 && d->visibleItems.at(i)->index == -1)
2755             --i;
2756         if (d->visibleItems.at(i)->index + 1 == modelIndex) {
2757             // Special case of appending an item to the model.
2758             index = d->visibleIndex + d->visibleItems.count();
2759         } else {
2760             if (modelIndex <= d->visibleIndex) {
2761                 // Insert before visible items
2762                 d->visibleIndex += count;
2763                 for (int i = 0; i < d->visibleItems.count(); ++i) {
2764                     FxGridItem *listItem = d->visibleItems.at(i);
2765                     if (listItem->index != -1 && listItem->index >= modelIndex)
2766                         listItem->index += count;
2767                 }
2768             }
2769             if (d->currentIndex >= modelIndex) {
2770                 // adjust current item index
2771                 d->currentIndex += count;
2772                 if (d->currentItem)
2773                     d->currentItem->index = d->currentIndex;
2774                 emit currentIndexChanged();
2775             }
2776             d->scheduleLayout();
2777             d->itemCount += count;
2778             emit countChanged();
2779             return;
2780         }
2781     }
2782
2783     int insertCount = count;
2784     if (index < d->visibleIndex && d->visibleItems.count()) {
2785         insertCount -= d->visibleIndex - index;
2786         index = d->visibleIndex;
2787         modelIndex = d->visibleIndex;
2788     }
2789
2790     qreal tempPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size()+d->width()+1 : d->position();
2791     int to = d->buffer+tempPos+d->size()-1;
2792     int colPos = 0;
2793     int rowPos = 0;
2794     if (d->visibleItems.count()) {
2795         index -= d->visibleIndex;
2796         if (index < d->visibleItems.count()) {
2797             colPos = d->visibleItems.at(index)->colPos();
2798             rowPos = d->visibleItems.at(index)->rowPos();
2799         } else {
2800             // appending items to visible list
2801             colPos = d->visibleItems.at(index-1)->colPos() + d->colSize();
2802             rowPos = d->visibleItems.at(index-1)->rowPos();
2803             if (colPos > d->colSize() * (d->columns-1)) {
2804                 colPos = 0;
2805                 rowPos += d->rowSize();
2806             }
2807         }
2808     } else if (d->itemCount == 0 && d->header) {
2809         rowPos = d->headerSize();
2810     }
2811
2812     // Update the indexes of the following visible items.
2813     for (int i = 0; i < d->visibleItems.count(); ++i) {
2814         FxGridItem *listItem = d->visibleItems.at(i);
2815         if (listItem->index != -1 && listItem->index >= modelIndex)
2816             listItem->index += count;
2817     }
2818
2819     bool addedVisible = false;
2820     QList<FxGridItem*> added;
2821     int i = 0;
2822     while (i < insertCount && rowPos <= to + d->rowSize()*(d->columns - (colPos/d->colSize()))/qreal(d->columns)) {
2823         if (!addedVisible) {
2824             d->scheduleLayout();
2825             addedVisible = true;
2826         }
2827         FxGridItem *item = d->createItem(modelIndex + i);
2828         d->visibleItems.insert(index, item);
2829         item->setPosition(colPos, rowPos);
2830         added.append(item);
2831         colPos += d->colSize();
2832         if (colPos > d->colSize() * (d->columns-1)) {
2833             colPos = 0;
2834             rowPos += d->rowSize();
2835         }
2836         ++index;
2837         ++i;
2838     }
2839     if (i < insertCount) {
2840         // We didn't insert all our new items, which means anything
2841         // beyond the current index is not visible - remove it.
2842         while (d->visibleItems.count() > index) {
2843             d->releaseItem(d->visibleItems.takeLast());
2844         }
2845     }
2846
2847     // update visibleIndex
2848     d->visibleIndex = 0;
2849     for (QList<FxGridItem*>::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
2850         if ((*it)->index != -1) {
2851             d->visibleIndex = (*it)->index;
2852             break;
2853         }
2854     }
2855
2856     if (d->itemCount && d->currentIndex >= modelIndex) {
2857         // adjust current item index
2858         d->currentIndex += count;
2859         if (d->currentItem) {
2860             d->currentItem->index = d->currentIndex;
2861             d->currentItem->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex));
2862         }
2863         emit currentIndexChanged();
2864     } else if (d->itemCount == 0 && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
2865         setCurrentIndex(0);
2866     }
2867
2868     // everything is in order now - emit add() signal
2869     for (int j = 0; j < added.count(); ++j)
2870         added.at(j)->attached->emitAdd();
2871
2872     d->itemCount += count;
2873     emit countChanged();
2874 }
2875
2876 void QDeclarativeGridView::itemsRemoved(int modelIndex, int count)
2877 {
2878     Q_D(QDeclarativeGridView);
2879     if (!isComponentComplete())
2880         return;
2881
2882     d->itemCount -= count;
2883     bool currentRemoved = d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count;
2884     bool removedVisible = false;
2885
2886     // Remove the items from the visible list, skipping anything already marked for removal
2887     QList<FxGridItem*>::Iterator it = d->visibleItems.begin();
2888     while (it != d->visibleItems.end()) {
2889         FxGridItem *item = *it;
2890         if (item->index == -1 || item->index < modelIndex) {
2891             // already removed, or before removed items
2892             if (item->index < modelIndex && !removedVisible) {
2893                 d->scheduleLayout();
2894                 removedVisible = true;
2895             }
2896             ++it;
2897         } else if (item->index >= modelIndex + count) {
2898             // after removed items
2899             item->index -= count;
2900             ++it;
2901         } else {
2902             // removed item
2903             if (!removedVisible) {
2904                 d->scheduleLayout();
2905                 removedVisible = true;
2906             }
2907             item->attached->emitRemove();
2908             if (item->attached->delayRemove()) {
2909                 item->index = -1;
2910                 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
2911                 ++it;
2912             } else {
2913                 it = d->visibleItems.erase(it);
2914                 d->releaseItem(item);
2915             }
2916         }
2917     }
2918
2919     // fix current
2920     if (d->currentIndex >= modelIndex + count) {
2921         d->currentIndex -= count;
2922         if (d->currentItem)
2923             d->currentItem->index -= count;
2924         emit currentIndexChanged();
2925     } else if (currentRemoved) {
2926         // current item has been removed.
2927         d->releaseItem(d->currentItem);
2928         d->currentItem = 0;
2929         d->currentIndex = -1;
2930         if (d->itemCount)
2931             d->updateCurrent(qMin(modelIndex, d->itemCount-1));
2932         else
2933             emit currentIndexChanged();
2934     }
2935
2936     // update visibleIndex
2937     d->visibleIndex = 0;
2938     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
2939         if ((*it)->index != -1) {
2940             d->visibleIndex = (*it)->index;
2941             break;
2942         }
2943     }
2944
2945     if (removedVisible && d->visibleItems.isEmpty()) {
2946         d->timeline.clear();
2947         if (d->itemCount == 0) {
2948             d->setPosition(0);
2949             d->updateHeader();
2950             d->updateFooter();
2951             update();
2952         }
2953     }
2954
2955     emit countChanged();
2956 }
2957
2958 void QDeclarativeGridView::destroyRemoved()
2959 {
2960     Q_D(QDeclarativeGridView);
2961     for (QList<FxGridItem*>::Iterator it = d->visibleItems.begin();
2962             it != d->visibleItems.end();) {
2963         FxGridItem *listItem = *it;
2964         if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
2965             d->releaseItem(listItem);
2966             it = d->visibleItems.erase(it);
2967         } else {
2968             ++it;
2969         }
2970     }
2971
2972     // Correct the positioning of the items
2973     d->layout();
2974 }
2975
2976 void QDeclarativeGridView::itemsMoved(int from, int to, int count)
2977 {
2978     Q_D(QDeclarativeGridView);
2979     if (!isComponentComplete())
2980         return;
2981     QHash<int,FxGridItem*> moved;
2982
2983     FxGridItem *firstItem = d->firstVisibleItem();
2984
2985     QList<FxGridItem*>::Iterator it = d->visibleItems.begin();
2986     while (it != d->visibleItems.end()) {
2987         FxGridItem *item = *it;
2988         if (item->index >= from && item->index < from + count) {
2989             // take the items that are moving
2990             item->index += (to-from);
2991             moved.insert(item->index, item);
2992             it = d->visibleItems.erase(it);
2993         } else {
2994             if (item->index > from && item->index != -1) {
2995                 // move everything after the moved items.
2996                 item->index -= count;
2997                 if (item->index < d->visibleIndex)
2998                     d->visibleIndex = item->index;
2999             }
3000             ++it;
3001         }
3002     }
3003
3004     int remaining = count;
3005     int endIndex = d->visibleIndex;
3006     it = d->visibleItems.begin();
3007     while (it != d->visibleItems.end()) {
3008         FxGridItem *item = *it;
3009         if (remaining && item->index >= to && item->index < to + count) {
3010             // place items in the target position, reusing any existing items
3011             FxGridItem *movedItem = moved.take(item->index);
3012             if (!movedItem)
3013                 movedItem = d->createItem(item->index);
3014             it = d->visibleItems.insert(it, movedItem);
3015             if (it == d->visibleItems.begin() && firstItem)
3016                 movedItem->setPosition(firstItem->colPos(), firstItem->rowPos());
3017             ++it;
3018             --remaining;
3019         } else {
3020             if (item->index != -1) {
3021                 if (item->index >= to) {
3022                     // update everything after the moved items.
3023                     item->index += count;
3024                 }
3025                 endIndex = item->index;
3026             }
3027             ++it;
3028         }
3029     }
3030
3031     // If we have moved items to the end of the visible items
3032     // then add any existing moved items that we have
3033     while (FxGridItem *item = moved.take(endIndex+1)) {
3034         d->visibleItems.append(item);
3035         ++endIndex;
3036     }
3037
3038     // update visibleIndex
3039     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3040         if ((*it)->index != -1) {
3041             d->visibleIndex = (*it)->index;
3042             break;
3043         }
3044     }
3045
3046     // Fix current index
3047     if (d->currentIndex >= 0 && d->currentItem) {
3048         int oldCurrent = d->currentIndex;
3049         d->currentIndex = d->model->indexOf(d->currentItem->item, this);
3050         if (oldCurrent != d->currentIndex) {
3051             d->currentItem->index = d->currentIndex;
3052             emit currentIndexChanged();
3053         }
3054     }
3055
3056     // Whatever moved items remain are no longer visible items.
3057     while (moved.count()) {
3058         int idx = moved.begin().key();
3059         FxGridItem *item = moved.take(idx);
3060         if (d->currentItem && item->item == d->currentItem->item)
3061             item->setPosition(d->colPosAt(idx), d->rowPosAt(idx));
3062         d->releaseItem(item);
3063     }
3064
3065     d->layout();
3066 }
3067
3068 void QDeclarativeGridView::modelReset()
3069 {
3070     Q_D(QDeclarativeGridView);
3071     d->clear();
3072     refill();
3073     d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
3074     d->updateCurrent(d->currentIndex);
3075     if (d->highlight && d->currentItem) {
3076         if (d->autoHighlight)
3077             d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
3078         d->updateTrackedItem();
3079     }
3080     d->moveReason = QDeclarativeGridViewPrivate::Other;
3081
3082     emit countChanged();
3083 }
3084
3085 void QDeclarativeGridView::createdItem(int index, QDeclarativeItem *item)
3086 {
3087     Q_D(QDeclarativeGridView);
3088     if (d->requestedIndex != index) {
3089         item->setParentItem(this);
3090         d->unrequestedItems.insert(item, index);
3091         if (d->flow == QDeclarativeGridView::LeftToRight) {
3092             item->setPos(QPointF(d->colPosAt(index), d->rowPosAt(index)));
3093         } else {
3094             item->setPos(QPointF(d->rowPosAt(index), d->colPosAt(index)));
3095         }
3096     }
3097 }
3098
3099 void QDeclarativeGridView::destroyingItem(QDeclarativeItem *item)
3100 {
3101     Q_D(QDeclarativeGridView);
3102     d->unrequestedItems.remove(item);
3103 }
3104
3105 void QDeclarativeGridView::animStopped()
3106 {
3107     Q_D(QDeclarativeGridView);
3108     d->bufferMode = QDeclarativeGridViewPrivate::NoBuffer;
3109     if (d->haveHighlightRange && d->highlightRange == QDeclarativeGridView::StrictlyEnforceRange)
3110         d->updateHighlight();
3111 }
3112
3113 void QDeclarativeGridView::refill()
3114 {
3115     Q_D(QDeclarativeGridView);
3116     if (d->isRightToLeftTopToBottom())
3117         d->refill(-d->position()-d->size()+1, -d->position());
3118     else
3119         d->refill(d->position(), d->position()+d->size()-1);
3120 }
3121
3122
3123 QDeclarativeGridViewAttached *QDeclarativeGridView::qmlAttachedProperties(QObject *obj)
3124 {
3125     return new QDeclarativeGridViewAttached(obj);
3126 }
3127
3128 QT_END_NAMESPACE