Initial import from the monolithic Qt.
[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         dataModel->setDelegate(delegate);
1503         if (isComponentComplete()) {
1504             for (int i = 0; i < d->visibleItems.count(); ++i)
1505                 d->releaseItem(d->visibleItems.at(i));
1506             d->visibleItems.clear();
1507             d->releaseItem(d->currentItem);
1508             d->currentItem = 0;
1509             refill();
1510             d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
1511             d->updateCurrent(d->currentIndex);
1512             if (d->highlight && d->currentItem) {
1513                 if (d->autoHighlight)
1514                     d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
1515                 d->updateTrackedItem();
1516             }
1517             d->moveReason = QDeclarativeGridViewPrivate::Other;
1518         }
1519         emit delegateChanged();
1520     }
1521 }
1522
1523 /*!
1524   \qmlproperty int GridView::currentIndex
1525   \qmlproperty Item GridView::currentItem
1526
1527     The \c currentIndex property holds the index of the current item, and
1528     \c currentItem holds the current item.  Setting the currentIndex to -1
1529     will clear the highlight and set currentItem to null.
1530
1531     If highlightFollowsCurrentItem is \c true, setting either of these 
1532     properties will smoothly scroll the GridView so that the current 
1533     item becomes visible.
1534     
1535     Note that the position of the current item
1536     may only be approximate until it becomes visible in the view.
1537 */
1538 int QDeclarativeGridView::currentIndex() const
1539 {
1540     Q_D(const QDeclarativeGridView);
1541     return d->currentIndex;
1542 }
1543
1544 void QDeclarativeGridView::setCurrentIndex(int index)
1545 {
1546     Q_D(QDeclarativeGridView);
1547     if (d->requestedIndex >= 0) // currently creating item
1548         return;
1549     d->currentIndexCleared = (index == -1);
1550     if (index == d->currentIndex)
1551         return;
1552     if (isComponentComplete() && d->isValid()) {
1553         d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
1554         d->updateCurrent(index);
1555     } else {
1556         d->currentIndex = index;
1557         emit currentIndexChanged();
1558     }
1559 }
1560
1561 QDeclarativeItem *QDeclarativeGridView::currentItem()
1562 {
1563     Q_D(QDeclarativeGridView);
1564     if (!d->currentItem)
1565         return 0;
1566     return d->currentItem->item;
1567 }
1568
1569 /*!
1570   \qmlproperty Item GridView::highlightItem
1571
1572   This holds the highlight item created from the \l highlight component.
1573
1574   The highlightItem is managed by the view unless
1575   \l highlightFollowsCurrentItem is set to false.
1576
1577   \sa highlight, highlightFollowsCurrentItem
1578 */
1579 QDeclarativeItem *QDeclarativeGridView::highlightItem()
1580 {
1581     Q_D(QDeclarativeGridView);
1582     if (!d->highlight)
1583         return 0;
1584     return d->highlight->item;
1585 }
1586
1587 /*!
1588   \qmlproperty int GridView::count
1589   This property holds the number of items in the view.
1590 */
1591 int QDeclarativeGridView::count() const
1592 {
1593     Q_D(const QDeclarativeGridView);
1594     if (d->model)
1595         return d->model->count();
1596     return 0;
1597 }
1598
1599 /*!
1600   \qmlproperty Component GridView::highlight
1601   This property holds the component to use as the highlight.
1602
1603   An instance of the highlight component is created for each view.
1604   The geometry of the resulting component instance will be managed by the view
1605   so as to stay with the current item, unless the highlightFollowsCurrentItem property is false.
1606
1607   \sa highlightItem, highlightFollowsCurrentItem
1608 */
1609 QDeclarativeComponent *QDeclarativeGridView::highlight() const
1610 {
1611     Q_D(const QDeclarativeGridView);
1612     return d->highlightComponent;
1613 }
1614
1615 void QDeclarativeGridView::setHighlight(QDeclarativeComponent *highlight)
1616 {
1617     Q_D(QDeclarativeGridView);
1618     if (highlight != d->highlightComponent) {
1619         d->highlightComponent = highlight;
1620         d->updateCurrent(d->currentIndex);
1621         emit highlightChanged();
1622     }
1623 }
1624
1625 /*!
1626   \qmlproperty bool GridView::highlightFollowsCurrentItem
1627   This property sets whether the highlight is managed by the view.
1628
1629     If this property is true (the default value), the highlight is moved smoothly
1630     to follow the current item.  Otherwise, the
1631     highlight is not moved by the view, and any movement must be implemented
1632     by the highlight.  
1633     
1634     Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1635
1636     \snippet doc/src/snippets/declarative/gridview/gridview.qml highlightFollowsCurrentItem
1637 */
1638 bool QDeclarativeGridView::highlightFollowsCurrentItem() const
1639 {
1640     Q_D(const QDeclarativeGridView);
1641     return d->autoHighlight;
1642 }
1643
1644 void QDeclarativeGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
1645 {
1646     Q_D(QDeclarativeGridView);
1647     if (d->autoHighlight != autoHighlight) {
1648         d->autoHighlight = autoHighlight;
1649         if (autoHighlight) {
1650             d->updateHighlight();
1651         } else if (d->highlightXAnimator) {
1652             d->highlightXAnimator->stop();
1653             d->highlightYAnimator->stop();
1654         }
1655     }
1656 }
1657
1658 /*!
1659     \qmlproperty int GridView::highlightMoveDuration
1660     This property holds the move animation duration of the highlight delegate.
1661
1662     highlightFollowsCurrentItem must be true for this property
1663     to have effect.
1664
1665     The default value for the duration is 150ms.
1666
1667     \sa highlightFollowsCurrentItem
1668 */
1669 int QDeclarativeGridView::highlightMoveDuration() const
1670 {
1671     Q_D(const QDeclarativeGridView);
1672     return d->highlightMoveDuration;
1673 }
1674
1675 void QDeclarativeGridView::setHighlightMoveDuration(int duration)
1676 {
1677     Q_D(QDeclarativeGridView);
1678     if (d->highlightMoveDuration != duration) {
1679         d->highlightMoveDuration = duration;
1680         if (d->highlightYAnimator) {
1681             d->highlightXAnimator->userDuration = d->highlightMoveDuration;
1682             d->highlightYAnimator->userDuration = d->highlightMoveDuration;
1683         }
1684         emit highlightMoveDurationChanged();
1685     }
1686 }
1687
1688
1689 /*!
1690     \qmlproperty real GridView::preferredHighlightBegin
1691     \qmlproperty real GridView::preferredHighlightEnd
1692     \qmlproperty enumeration GridView::highlightRangeMode
1693
1694     These properties define the preferred range of the highlight (for the current item)
1695     within the view. The \c preferredHighlightBegin value must be less than the
1696     \c preferredHighlightEnd value. 
1697
1698     These properties affect the position of the current item when the view is scrolled.
1699     For example, if the currently selected item should stay in the middle of the
1700     view when it is scrolled, set the \c preferredHighlightBegin and 
1701     \c preferredHighlightEnd values to the top and bottom coordinates of where the middle 
1702     item would be. If the \c currentItem is changed programmatically, the view will
1703     automatically scroll so that the current item is in the middle of the view.
1704     Furthermore, the behavior of the current item index will occur whether or not a
1705     highlight exists.
1706
1707     Valid values for \c highlightRangeMode are:
1708
1709     \list
1710     \o GridView.ApplyRange - the view attempts to maintain the highlight within the range.
1711        However, the highlight can move outside of the range at the ends of the view or due
1712        to mouse interaction.
1713     \o GridView.StrictlyEnforceRange - the highlight never moves outside of the range.
1714        The current item changes if a keyboard or mouse action would cause the highlight to move
1715        outside of the range.
1716     \o GridView.NoHighlightRange - this is the default value.
1717     \endlist
1718 */
1719 qreal QDeclarativeGridView::preferredHighlightBegin() const
1720 {
1721     Q_D(const QDeclarativeGridView);
1722     return d->highlightRangeStart;
1723 }
1724
1725 void QDeclarativeGridView::setPreferredHighlightBegin(qreal start)
1726 {
1727     Q_D(QDeclarativeGridView);
1728     d->highlightRangeStartValid = true;
1729     if (d->highlightRangeStart == start)
1730         return;
1731     d->highlightRangeStart = start;
1732     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1733     emit preferredHighlightBeginChanged();
1734 }
1735
1736 void QDeclarativeGridView::resetPreferredHighlightBegin()
1737 {
1738     Q_D(QDeclarativeGridView);
1739     d->highlightRangeStartValid = false;
1740     if (d->highlightRangeStart == 0)
1741         return;
1742     d->highlightRangeStart = 0;
1743     emit preferredHighlightBeginChanged();
1744 }
1745
1746 qreal QDeclarativeGridView::preferredHighlightEnd() const
1747 {
1748     Q_D(const QDeclarativeGridView);
1749     return d->highlightRangeEnd;
1750 }
1751
1752 void QDeclarativeGridView::setPreferredHighlightEnd(qreal end)
1753 {
1754     Q_D(QDeclarativeGridView);
1755     d->highlightRangeEndValid = true;
1756     if (d->highlightRangeEnd == end)
1757         return;
1758     d->highlightRangeEnd = end;
1759     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1760     emit preferredHighlightEndChanged();
1761 }
1762
1763 void QDeclarativeGridView::resetPreferredHighlightEnd()
1764 {
1765     Q_D(QDeclarativeGridView);
1766     d->highlightRangeEndValid = false;
1767     if (d->highlightRangeEnd == 0)
1768         return;
1769     d->highlightRangeEnd = 0;
1770     emit preferredHighlightEndChanged();
1771 }
1772
1773 QDeclarativeGridView::HighlightRangeMode QDeclarativeGridView::highlightRangeMode() const
1774 {
1775     Q_D(const QDeclarativeGridView);
1776     return d->highlightRange;
1777 }
1778
1779 void QDeclarativeGridView::setHighlightRangeMode(HighlightRangeMode mode)
1780 {
1781     Q_D(QDeclarativeGridView);
1782     if (d->highlightRange == mode)
1783         return;
1784     d->highlightRange = mode;
1785     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1786     emit highlightRangeModeChanged();
1787 }
1788
1789 /*!
1790   \qmlproperty enumeration GridView::layoutDirection
1791   This property holds the layout direction of the grid.
1792
1793     Possible values:
1794
1795   \list
1796   \o Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is
1797   dependent on the \l GridView::flow property.
1798   \o Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent
1799   on the \l GridView:flow property.
1800   \endlist
1801
1802   \bold Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if
1803   GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply
1804   indicates that the flow is horizontal.
1805 */
1806
1807 Qt::LayoutDirection QDeclarativeGridView::layoutDirection() const
1808 {
1809     Q_D(const QDeclarativeGridView);
1810     return d->layoutDirection;
1811 }
1812
1813 void QDeclarativeGridView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1814 {
1815     Q_D(QDeclarativeGridView);
1816     if (d->layoutDirection != layoutDirection) {
1817         d->layoutDirection = layoutDirection;
1818         d->regenerate();
1819         emit layoutDirectionChanged();
1820         emit effectiveLayoutDirectionChanged();
1821     }
1822 }
1823
1824 /*!
1825     \qmlproperty enumeration GridView::effectiveLayoutDirection
1826     This property holds the effective layout direction of the grid.
1827
1828     When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1829     the visual layout direction of the grid will be mirrored. However, the
1830     property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged.
1831
1832     \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1833 */
1834
1835 Qt::LayoutDirection QDeclarativeGridView::effectiveLayoutDirection() const
1836 {
1837     Q_D(const QDeclarativeGridView);
1838     if (d->effectiveLayoutMirror)
1839         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
1840     else
1841         return d->layoutDirection;
1842 }
1843
1844 /*!
1845   \qmlproperty enumeration GridView::flow
1846   This property holds the flow of the grid.
1847
1848     Possible values:
1849
1850     \list
1851     \o GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically
1852     \o GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally
1853     \endlist
1854 */
1855 QDeclarativeGridView::Flow QDeclarativeGridView::flow() const
1856 {
1857     Q_D(const QDeclarativeGridView);
1858     return d->flow;
1859 }
1860
1861 void QDeclarativeGridView::setFlow(Flow flow)
1862 {
1863     Q_D(QDeclarativeGridView);
1864     if (d->flow != flow) {
1865         d->flow = flow;
1866         if (d->flow == LeftToRight) {
1867             setContentWidth(-1);
1868             setFlickableDirection(QDeclarativeFlickable::VerticalFlick);
1869         } else {
1870             setContentHeight(-1);
1871             setFlickableDirection(QDeclarativeFlickable::HorizontalFlick);
1872         }
1873         setContentX(0);
1874         setContentY(0);
1875         d->regenerate();
1876         emit flowChanged();
1877     }
1878 }
1879
1880 /*!
1881   \qmlproperty bool GridView::keyNavigationWraps
1882   This property holds whether the grid wraps key navigation
1883
1884     If this is true, key navigation that would move the current item selection
1885     past one end of the view instead wraps around and moves the selection to
1886     the other end of the view.
1887
1888     By default, key navigation is not wrapped.
1889 */
1890 bool QDeclarativeGridView::isWrapEnabled() const
1891 {
1892     Q_D(const QDeclarativeGridView);
1893     return d->wrap;
1894 }
1895
1896 void QDeclarativeGridView::setWrapEnabled(bool wrap)
1897 {
1898     Q_D(QDeclarativeGridView);
1899     if (d->wrap == wrap)
1900         return;
1901     d->wrap = wrap;
1902     emit keyNavigationWrapsChanged();
1903 }
1904
1905 /*!
1906     \qmlproperty int GridView::cacheBuffer
1907     This property determines whether delegates are retained outside the
1908     visible area of the view.
1909
1910     If non-zero the view will keep as many delegates
1911     instantiated as will fit within the buffer specified.  For example,
1912     if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
1913     set to 40, then up to 2 delegates above and 2 delegates below the visible
1914     area may be retained.
1915
1916     Note that cacheBuffer is not a pixel buffer - it only maintains additional
1917     instantiated delegates.
1918
1919     Setting this value can make scrolling the list smoother at the expense
1920     of additional memory usage.  It is not a substitute for creating efficient
1921     delegates; the fewer elements in a delegate, the faster a view may be
1922     scrolled.
1923 */
1924 int QDeclarativeGridView::cacheBuffer() const
1925 {
1926     Q_D(const QDeclarativeGridView);
1927     return d->buffer;
1928 }
1929
1930 void QDeclarativeGridView::setCacheBuffer(int buffer)
1931 {
1932     Q_D(QDeclarativeGridView);
1933     if (d->buffer != buffer) {
1934         d->buffer = buffer;
1935         if (isComponentComplete())
1936             refill();
1937         emit cacheBufferChanged();
1938     }
1939 }
1940
1941 /*!
1942   \qmlproperty int GridView::cellWidth
1943   \qmlproperty int GridView::cellHeight
1944
1945   These properties holds the width and height of each cell in the grid.
1946
1947   The default cell size is 100x100.
1948 */
1949 int QDeclarativeGridView::cellWidth() const
1950 {
1951     Q_D(const QDeclarativeGridView);
1952     return d->cellWidth;
1953 }
1954
1955 void QDeclarativeGridView::setCellWidth(int cellWidth)
1956 {
1957     Q_D(QDeclarativeGridView);
1958     if (cellWidth != d->cellWidth && cellWidth > 0) {
1959         d->cellWidth = qMax(1, cellWidth);
1960         d->updateGrid();
1961         emit cellWidthChanged();
1962         d->layout();
1963     }
1964 }
1965
1966 int QDeclarativeGridView::cellHeight() const
1967 {
1968     Q_D(const QDeclarativeGridView);
1969     return d->cellHeight;
1970 }
1971
1972 void QDeclarativeGridView::setCellHeight(int cellHeight)
1973 {
1974     Q_D(QDeclarativeGridView);
1975     if (cellHeight != d->cellHeight && cellHeight > 0) {
1976         d->cellHeight = qMax(1, cellHeight);
1977         d->updateGrid();
1978         emit cellHeightChanged();
1979         d->layout();
1980     }
1981 }
1982 /*!
1983     \qmlproperty enumeration GridView::snapMode
1984
1985     This property determines how the view scrolling will settle following a drag or flick.
1986     The possible values are:
1987
1988     \list
1989     \o GridView.NoSnap (default) - the view stops anywhere within the visible area.
1990     \o GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow)
1991     aligned with the start of the view.
1992     \o GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow)
1993     away from the first visible row at the time the mouse button is released.
1994     This mode is particularly useful for moving one page at a time.
1995     \endlist
1996
1997 */
1998 QDeclarativeGridView::SnapMode QDeclarativeGridView::snapMode() const
1999 {
2000     Q_D(const QDeclarativeGridView);
2001     return d->snapMode;
2002 }
2003
2004 void QDeclarativeGridView::setSnapMode(SnapMode mode)
2005 {
2006     Q_D(QDeclarativeGridView);
2007     if (d->snapMode != mode) {
2008         d->snapMode = mode;
2009         emit snapModeChanged();
2010     }
2011 }
2012
2013 /*!
2014     \qmlproperty Component GridView::footer
2015     This property holds the component to use as the footer.
2016
2017     An instance of the footer component is created for each view.  The
2018     footer is positioned at the end of the view, after any items.
2019
2020     \sa header
2021 */
2022 QDeclarativeComponent *QDeclarativeGridView::footer() const
2023 {
2024     Q_D(const QDeclarativeGridView);
2025     return d->footerComponent;
2026 }
2027
2028 void QDeclarativeGridView::setFooter(QDeclarativeComponent *footer)
2029 {
2030     Q_D(QDeclarativeGridView);
2031     if (d->footerComponent != footer) {
2032         if (d->footer) {
2033             if (scene())
2034                 scene()->removeItem(d->footer->item);
2035             d->footer->item->deleteLater();
2036             delete d->footer;
2037             d->footer = 0;
2038         }
2039         d->footerComponent = footer;
2040         if (isComponentComplete()) {
2041             d->updateFooter();
2042             d->updateGrid();
2043             d->fixupPosition();
2044         }
2045         emit footerChanged();
2046     }
2047 }
2048
2049 /*!
2050     \qmlproperty Component GridView::header
2051     This property holds the component to use as the header.
2052
2053     An instance of the header component is created for each view.  The
2054     header is positioned at the beginning of the view, before any items.
2055
2056     \sa footer
2057 */
2058 QDeclarativeComponent *QDeclarativeGridView::header() const
2059 {
2060     Q_D(const QDeclarativeGridView);
2061     return d->headerComponent;
2062 }
2063
2064 void QDeclarativeGridView::setHeader(QDeclarativeComponent *header)
2065 {
2066     Q_D(QDeclarativeGridView);
2067     if (d->headerComponent != header) {
2068         if (d->header) {
2069             if (scene())
2070                 scene()->removeItem(d->header->item);
2071             d->header->item->deleteLater();
2072             delete d->header;
2073             d->header = 0;
2074         }
2075         d->headerComponent = header;
2076         if (isComponentComplete()) {
2077             d->updateHeader();
2078             d->updateFooter();
2079             d->updateGrid();
2080             d->fixupPosition();
2081         }
2082         emit headerChanged();
2083     }
2084 }
2085
2086 void QDeclarativeGridView::setContentX(qreal pos)
2087 {
2088     Q_D(QDeclarativeGridView);
2089     // Positioning the view manually should override any current movement state
2090     d->moveReason = QDeclarativeGridViewPrivate::Other;
2091     QDeclarativeFlickable::setContentX(pos);
2092 }
2093
2094 void QDeclarativeGridView::setContentY(qreal pos)
2095 {
2096     Q_D(QDeclarativeGridView);
2097     // Positioning the view manually should override any current movement state
2098     d->moveReason = QDeclarativeGridViewPrivate::Other;
2099     QDeclarativeFlickable::setContentY(pos);
2100 }
2101
2102 bool QDeclarativeGridView::event(QEvent *event)
2103 {
2104     Q_D(QDeclarativeGridView);
2105     if (event->type() == QEvent::User) {
2106         d->layout();
2107         return true;
2108     }
2109
2110     return QDeclarativeFlickable::event(event);
2111 }
2112
2113 void QDeclarativeGridView::viewportMoved()
2114 {
2115     Q_D(QDeclarativeGridView);
2116     QDeclarativeFlickable::viewportMoved();
2117     if (!d->itemCount)
2118         return;
2119     d->lazyRelease = true;
2120     if (d->flickingHorizontally || d->flickingVertically) {
2121         if (yflick()) {
2122             if (d->vData.velocity > 0)
2123                 d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore;
2124             else if (d->vData.velocity < 0)
2125                 d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter;
2126         }
2127
2128         if (xflick()) {
2129             if (d->hData.velocity > 0)
2130                 d->bufferMode = QDeclarativeGridViewPrivate::BufferBefore;
2131             else if (d->hData.velocity < 0)
2132                 d->bufferMode = QDeclarativeGridViewPrivate::BufferAfter;
2133         }
2134     }
2135     refill();
2136     if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
2137         d->moveReason = QDeclarativeGridViewPrivate::Mouse;
2138     if (d->moveReason != QDeclarativeGridViewPrivate::SetIndex) {
2139         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2140             // reposition highlight
2141             qreal pos = d->highlight->rowPos();
2142             qreal viewPos;
2143             qreal highlightStart;
2144             qreal highlightEnd;
2145             if (d->isRightToLeftTopToBottom()) {
2146                 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2147                 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2148                 viewPos = -d->position()-d->size();
2149             } else {
2150                 highlightStart = d->highlightRangeStart;
2151                 highlightEnd = d->highlightRangeEnd;
2152                 viewPos = d->position();
2153             }
2154             if (pos > viewPos + highlightEnd - d->rowSize())
2155                 pos = viewPos + highlightEnd - d->rowSize();
2156             if (pos < viewPos + highlightStart)
2157                 pos = viewPos + highlightStart;
2158
2159             d->highlight->setPosition(d->highlight->colPos(), qRound(pos));
2160
2161             // update current index
2162             int idx = d->snapIndex();
2163             if (idx >= 0 && idx != d->currentIndex) {
2164                 d->updateCurrent(idx);
2165                 if (d->currentItem && d->currentItem->colPos() != d->highlight->colPos() && d->autoHighlight) {
2166                     if (d->flow == LeftToRight)
2167                         d->highlightXAnimator->to = d->currentItem->item->x();
2168                     else
2169                         d->highlightYAnimator->to = d->currentItem->item->y();
2170                 }
2171             }
2172         }
2173     }
2174 }
2175
2176 qreal QDeclarativeGridView::minYExtent() const
2177 {
2178     Q_D(const QDeclarativeGridView);
2179     if (d->flow == QDeclarativeGridView::TopToBottom)
2180         return QDeclarativeFlickable::minYExtent();
2181     qreal extent = -d->startPosition();
2182     if (d->header && d->visibleItems.count())
2183         extent += d->header->item->height();
2184     if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2185         extent += d->highlightRangeStart;
2186         extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd));
2187     }
2188     return extent;
2189 }
2190
2191 qreal QDeclarativeGridView::maxYExtent() const
2192 {
2193     Q_D(const QDeclarativeGridView);
2194     if (d->flow == QDeclarativeGridView::TopToBottom)
2195         return QDeclarativeFlickable::maxYExtent();
2196     qreal extent;
2197     if (!d->model || !d->model->count()) {
2198         extent = 0;
2199     } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2200         extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart);
2201         if (d->highlightRangeEnd != d->highlightRangeStart)
2202             extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1));
2203     } else {
2204         extent = -(d->endPosition() - height());
2205     }
2206     if (d->footer)
2207         extent -= d->footer->item->height();
2208     const qreal minY = minYExtent();
2209     if (extent > minY)
2210         extent = minY;
2211     return extent;
2212 }
2213
2214 qreal QDeclarativeGridView::minXExtent() const
2215 {
2216     Q_D(const QDeclarativeGridView);
2217     if (d->flow == QDeclarativeGridView::LeftToRight)
2218         return QDeclarativeFlickable::minXExtent();
2219     qreal extent = -d->startPosition();
2220     qreal highlightStart;
2221     qreal highlightEnd;
2222     qreal endPositionFirstItem;
2223     if (d->isRightToLeftTopToBottom()) {
2224         endPositionFirstItem = d->rowPosAt(d->model->count()-1);
2225         highlightStart = d->highlightRangeStartValid
2226                 ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2227                 : d->size() - (d->lastPosition()-endPositionFirstItem);
2228         highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2229         if (d->footer && d->visibleItems.count())
2230             extent += d->footer->item->width();
2231     } else {
2232         endPositionFirstItem = d->rowPosAt(0)+d->rowSize();
2233         highlightStart = d->highlightRangeStart;
2234         highlightEnd = d->highlightRangeEnd;
2235         if (d->header && d->visibleItems.count())
2236             extent += d->header->item->width();
2237     }
2238     if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2239         extent += highlightStart;
2240         extent = qMax(extent, -(endPositionFirstItem - highlightEnd));
2241     }
2242     return extent;
2243 }
2244
2245 qreal QDeclarativeGridView::maxXExtent() const
2246 {
2247     Q_D(const QDeclarativeGridView);
2248     if (d->flow == QDeclarativeGridView::LeftToRight)
2249         return QDeclarativeFlickable::maxXExtent();
2250     qreal extent;
2251     qreal highlightStart;
2252     qreal highlightEnd;
2253     qreal lastItemPosition = 0;
2254     if (d->isRightToLeftTopToBottom()){
2255         highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2256         highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2257         lastItemPosition = d->endPosition();
2258     } else {
2259         highlightStart = d->highlightRangeStart;
2260         highlightEnd = d->highlightRangeEnd;
2261         lastItemPosition = 0;
2262         if (d->model && d->model->count())
2263             lastItemPosition = d->rowPosAt(d->model->count()-1);
2264     }
2265     if (!d->model || !d->model->count()) {
2266         extent = 0;
2267     } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2268         extent = -(lastItemPosition - highlightStart);
2269         if (highlightEnd != highlightStart)
2270             extent = d->isRightToLeftTopToBottom()
2271                     ? qMax(extent, -(d->endPosition() - highlightEnd + 1))
2272                     : qMin(extent, -(d->endPosition() - highlightEnd + 1));
2273     } else {
2274         extent = -(d->endPosition() - width());
2275     }
2276     if (d->isRightToLeftTopToBottom()) {
2277         if (d->header)
2278             extent -= d->header->item->width();
2279     } else {
2280         if (d->footer)
2281             extent -= d->footer->item->width();
2282     }
2283
2284     const qreal minX = minXExtent();
2285     if (extent > minX)
2286         extent = minX;
2287     return extent;
2288 }
2289
2290 void QDeclarativeGridView::keyPressEvent(QKeyEvent *event)
2291 {
2292     Q_D(QDeclarativeGridView);
2293     keyPressPreHandler(event);
2294     if (event->isAccepted())
2295         return;
2296     if (d->model && d->model->count() && d->interactive) {
2297         d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
2298         int oldCurrent = currentIndex();
2299         switch (event->key()) {
2300         case Qt::Key_Up:
2301             moveCurrentIndexUp();
2302             break;
2303         case Qt::Key_Down:
2304             moveCurrentIndexDown();
2305             break;
2306         case Qt::Key_Left:
2307             moveCurrentIndexLeft();
2308             break;
2309         case Qt::Key_Right:
2310             moveCurrentIndexRight();
2311             break;
2312         default:
2313             break;
2314         }
2315         if (oldCurrent != currentIndex()) {
2316             event->accept();
2317             return;
2318         }
2319     }
2320     d->moveReason = QDeclarativeGridViewPrivate::Other;
2321     event->ignore();
2322     QDeclarativeFlickable::keyPressEvent(event);
2323 }
2324
2325 /*!
2326     \qmlmethod GridView::moveCurrentIndexUp()
2327
2328     Move the currentIndex up one item in the view.
2329     The current index will wrap if keyNavigationWraps is true and it
2330     is currently at the end. This method has no effect if the \l count is zero.
2331
2332     \bold Note: methods should only be called after the Component has completed.
2333 */
2334 void QDeclarativeGridView::moveCurrentIndexUp()
2335 {
2336     Q_D(QDeclarativeGridView);
2337     const int count = d->model ? d->model->count() : 0;
2338     if (!count)
2339         return;
2340     if (d->flow == QDeclarativeGridView::LeftToRight) {
2341         if (currentIndex() >= d->columns || d->wrap) {
2342             int index = currentIndex() - d->columns;
2343             setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2344         }
2345     } else {
2346         if (currentIndex() > 0 || d->wrap) {
2347             int index = currentIndex() - 1;
2348             setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2349         }
2350     }
2351 }
2352
2353 /*!
2354     \qmlmethod GridView::moveCurrentIndexDown()
2355
2356     Move the currentIndex down one item in the view.
2357     The current index will wrap if keyNavigationWraps is true and it
2358     is currently at the end. This method has no effect if the \l count is zero.
2359
2360     \bold Note: methods should only be called after the Component has completed.
2361 */
2362 void QDeclarativeGridView::moveCurrentIndexDown()
2363 {
2364     Q_D(QDeclarativeGridView);
2365     const int count = d->model ? d->model->count() : 0;
2366     if (!count)
2367         return;
2368     if (d->flow == QDeclarativeGridView::LeftToRight) {
2369         if (currentIndex() < count - d->columns || d->wrap) {
2370             int index = currentIndex()+d->columns;
2371             setCurrentIndex((index >= 0 && index < count) ? index : 0);
2372         }
2373     } else {
2374         if (currentIndex() < count - 1 || d->wrap) {
2375             int index = currentIndex() + 1;
2376             setCurrentIndex((index >= 0 && index < count) ? index : 0);
2377         }
2378     }
2379 }
2380
2381 /*!
2382     \qmlmethod GridView::moveCurrentIndexLeft()
2383
2384     Move the currentIndex left one item in the view.
2385     The current index will wrap if keyNavigationWraps is true and it
2386     is currently at the end. This method has no effect if the \l count is zero.
2387
2388     \bold Note: methods should only be called after the Component has completed.
2389 */
2390 void QDeclarativeGridView::moveCurrentIndexLeft()
2391 {
2392     Q_D(QDeclarativeGridView);
2393     const int count = d->model ? d->model->count() : 0;
2394     if (!count)
2395         return;
2396
2397     if (effectiveLayoutDirection() == Qt::LeftToRight) {
2398         if (d->flow == QDeclarativeGridView::LeftToRight) {
2399             if (currentIndex() > 0 || d->wrap) {
2400                 int index = currentIndex() - 1;
2401                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2402             }
2403         } else {
2404             if (currentIndex() >= d->columns || d->wrap) {
2405                 int index = currentIndex() - d->columns;
2406                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2407             }
2408         }
2409     } else {
2410         if (d->flow == QDeclarativeGridView::LeftToRight) {
2411             if (currentIndex() < count - 1 || d->wrap) {
2412                 int index = currentIndex() + 1;
2413                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2414             }
2415         } else {
2416             if (currentIndex() < count - d->columns || d->wrap) {
2417                 int index = currentIndex() + d->columns;
2418                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2419             }
2420         }
2421     }
2422 }
2423
2424 /*!
2425     \qmlmethod GridView::moveCurrentIndexRight()
2426
2427     Move the currentIndex right one item in the view.
2428     The current index will wrap if keyNavigationWraps is true and it
2429     is currently at the end. This method has no effect if the \l count is zero.
2430
2431     \bold Note: methods should only be called after the Component has completed.
2432 */
2433 void QDeclarativeGridView::moveCurrentIndexRight()
2434 {
2435     Q_D(QDeclarativeGridView);
2436     const int count = d->model ? d->model->count() : 0;
2437     if (!count)
2438         return;
2439
2440     if (effectiveLayoutDirection() == Qt::LeftToRight) {
2441         if (d->flow == QDeclarativeGridView::LeftToRight) {
2442             if (currentIndex() < count - 1 || d->wrap) {
2443                 int index = currentIndex() + 1;
2444                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2445             }
2446         } else {
2447             if (currentIndex() < count - d->columns || d->wrap) {
2448                 int index = currentIndex()+d->columns;
2449                 setCurrentIndex((index >= 0 && index < count) ? index : 0);
2450             }
2451         }
2452     } else {
2453         if (d->flow == QDeclarativeGridView::LeftToRight) {
2454             if (currentIndex() > 0 || d->wrap) {
2455                 int index = currentIndex() - 1;
2456                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2457             }
2458         } else {
2459             if (currentIndex() >= d->columns || d->wrap) {
2460                 int index = currentIndex() - d->columns;
2461                 setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2462             }
2463         }
2464     }
2465 }
2466
2467 void QDeclarativeGridViewPrivate::positionViewAtIndex(int index, int mode)
2468 {
2469     Q_Q(QDeclarativeGridView);
2470     if (!isValid())
2471         return;
2472     if (mode < QDeclarativeGridView::Beginning || mode > QDeclarativeGridView::Contain)
2473         return;
2474
2475     int idx = qMax(qMin(index, model->count()-1), 0);
2476
2477     if (layoutScheduled)
2478         layout();
2479     qreal pos = isRightToLeftTopToBottom() ? -position() - size() : position();
2480     FxGridItem *item = visibleItem(idx);
2481     qreal maxExtent;
2482     if (flow == QDeclarativeGridView::LeftToRight)
2483         maxExtent = -q->maxYExtent();
2484     else
2485         maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent();
2486
2487     if (!item) {
2488         int itemPos = rowPosAt(idx);
2489         // save the currently visible items in case any of them end up visible again
2490         QList<FxGridItem*> oldVisible = visibleItems;
2491         visibleItems.clear();
2492         visibleIndex = idx - idx % columns;
2493         if (flow == QDeclarativeGridView::LeftToRight)
2494             maxExtent = -q->maxYExtent();
2495         else
2496             maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent();
2497         setPosition(qMin(qreal(itemPos), maxExtent));
2498         // now release the reference to all the old visible items.
2499         for (int i = 0; i < oldVisible.count(); ++i)
2500             releaseItem(oldVisible.at(i));
2501         item = visibleItem(idx);
2502     }
2503     if (item) {
2504         qreal itemPos = item->rowPos();
2505         switch (mode) {
2506         case QDeclarativeGridView::Beginning:
2507             pos = itemPos;
2508             if (index < 0 && header) {
2509                 pos -= flow == QDeclarativeGridView::LeftToRight
2510                             ? header->item->height()
2511                             : header->item->width();
2512             }
2513             break;
2514         case QDeclarativeGridView::Center:
2515             pos = itemPos - (size() - rowSize())/2;
2516             break;
2517         case QDeclarativeGridView::End:
2518             pos = itemPos - size() + rowSize();
2519             if (index >= model->count() && footer) {
2520                 pos += flow == QDeclarativeGridView::LeftToRight
2521                             ? footer->item->height()
2522                             : footer->item->width();
2523             }
2524             break;
2525         case QDeclarativeGridView::Visible:
2526             if (itemPos > pos + size())
2527                 pos = itemPos - size() + rowSize();
2528             else if (item->endRowPos() < pos)
2529                 pos = itemPos;
2530             break;
2531         case QDeclarativeGridView::Contain:
2532             if (item->endRowPos() > pos + size())
2533                 pos = itemPos - size() + rowSize();
2534             if (itemPos < pos)
2535                 pos = itemPos;
2536         }
2537
2538         pos = qMin(pos, maxExtent);
2539         qreal minExtent;
2540         if (flow == QDeclarativeGridView::LeftToRight)
2541             minExtent = -q->minYExtent();
2542         else
2543             minExtent = isRightToLeftTopToBottom() ? q->maxXExtent()-size() : -q->minXExtent();
2544         pos = qMax(pos, minExtent);
2545         moveReason = QDeclarativeGridViewPrivate::Other;
2546         q->cancelFlick();
2547         setPosition(pos);
2548     }
2549     fixupPosition();
2550 }
2551
2552 /*!
2553     \qmlmethod GridView::positionViewAtIndex(int index, PositionMode mode)
2554
2555     Positions the view such that the \a index is at the position specified by
2556     \a mode:
2557
2558     \list
2559     \o GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view.
2560     \o GridView.Center - position item in the center of the view.
2561     \o GridView.End - position item at bottom (or right for horizontal orientation) of the view.
2562     \o GridView.Visible - if any part of the item is visible then take no action, otherwise
2563     bring the item into view.
2564     \o GridView.Contain - ensure the entire item is visible.  If the item is larger than
2565     the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view.
2566     \endlist
2567
2568     If positioning the view at the index would cause empty space to be displayed at
2569     the beginning or end of the view, the view will be positioned at the boundary.
2570
2571     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2572     at a particular index.  This is unreliable since removing items from the start
2573     of the view does not cause all other items to be repositioned.
2574     The correct way to bring an item into view is with \c positionViewAtIndex.
2575
2576     \bold Note: methods should only be called after the Component has completed.  To position
2577     the view at startup, this method should be called by Component.onCompleted.  For
2578     example, to position the view at the end:
2579
2580     \code
2581     Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning)
2582     \endcode
2583 */
2584 void QDeclarativeGridView::positionViewAtIndex(int index, int mode)
2585 {
2586     Q_D(QDeclarativeGridView);
2587     if (!d->isValid() || index < 0 || index >= d->model->count())
2588         return;
2589     d->positionViewAtIndex(index, mode);
2590 }
2591
2592 /*!
2593     \qmlmethod GridView::positionViewAtBeginning()
2594     \qmlmethod GridView::positionViewAtEnd()
2595     \since Quick 1.1
2596
2597     Positions the view at the beginning or end, taking into account any header or footer.
2598
2599     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2600     at a particular index.  This is unreliable since removing items from the start
2601     of the list does not cause all other items to be repositioned, and because
2602     the actual start of the view can vary based on the size of the delegates.
2603
2604     \bold Note: methods should only be called after the Component has completed.  To position
2605     the view at startup, this method should be called by Component.onCompleted.  For
2606     example, to position the view at the end on startup:
2607
2608     \code
2609     Component.onCompleted: positionViewAtEnd()
2610     \endcode
2611 */
2612 void QDeclarativeGridView::positionViewAtBeginning()
2613 {
2614     Q_D(QDeclarativeGridView);
2615     if (!d->isValid())
2616         return;
2617     d->positionViewAtIndex(-1, Beginning);
2618 }
2619
2620 void QDeclarativeGridView::positionViewAtEnd()
2621 {
2622     Q_D(QDeclarativeGridView);
2623     if (!d->isValid())
2624         return;
2625     d->positionViewAtIndex(d->model->count(), End);
2626 }
2627
2628 /*!
2629     \qmlmethod int GridView::indexAt(int x, int y)
2630
2631     Returns the index of the visible item containing the point \a x, \a y in content
2632     coordinates.  If there is no item at the point specified, or the item is
2633     not visible -1 is returned.
2634
2635     If the item is outside the visible area, -1 is returned, regardless of
2636     whether an item will exist at that point when scrolled into view.
2637
2638     \bold Note: methods should only be called after the Component has completed.
2639 */
2640 int QDeclarativeGridView::indexAt(qreal x, qreal y) const
2641 {
2642     Q_D(const QDeclarativeGridView);
2643     for (int i = 0; i < d->visibleItems.count(); ++i) {
2644         const FxGridItem *listItem = d->visibleItems.at(i);
2645         if(listItem->contains(x, y))
2646             return listItem->index;
2647     }
2648
2649     return -1;
2650 }
2651
2652 void QDeclarativeGridView::componentComplete()
2653 {
2654     Q_D(QDeclarativeGridView);
2655     QDeclarativeFlickable::componentComplete();
2656     d->updateHeader();
2657     d->updateFooter();
2658     d->updateGrid();
2659     if (d->isValid()) {
2660         refill();
2661         d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
2662         if (d->currentIndex < 0 && !d->currentIndexCleared)
2663             d->updateCurrent(0);
2664         else
2665             d->updateCurrent(d->currentIndex);
2666         if (d->highlight && d->currentItem) {
2667             if (d->autoHighlight)
2668                 d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
2669             d->updateTrackedItem();
2670         }
2671         d->moveReason = QDeclarativeGridViewPrivate::Other;
2672         d->fixupPosition();
2673     }
2674 }
2675
2676 void QDeclarativeGridView::trackedPositionChanged()
2677 {
2678     Q_D(QDeclarativeGridView);
2679     if (!d->trackedItem || !d->currentItem)
2680         return;
2681     if (d->moveReason == QDeclarativeGridViewPrivate::SetIndex) {
2682         const qreal trackedPos = d->trackedItem->rowPos();
2683         qreal viewPos;
2684         qreal highlightStart;
2685         qreal highlightEnd;
2686         if (d->isRightToLeftTopToBottom()) {
2687             viewPos = -d->position()-d->size();
2688             highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2689             highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2690         } else {
2691             viewPos = d->position();
2692             highlightStart = d->highlightRangeStart;
2693             highlightEnd = d->highlightRangeEnd;
2694         }
2695         qreal pos = viewPos;
2696         if (d->haveHighlightRange) {
2697             if (d->highlightRange == StrictlyEnforceRange) {
2698                 if (trackedPos > pos + highlightEnd - d->rowSize())
2699                     pos = trackedPos - highlightEnd + d->rowSize();
2700                 if (trackedPos < pos + highlightStart)
2701                     pos = trackedPos - highlightStart;
2702             } else {
2703                 if (trackedPos < d->startPosition() + highlightStart) {
2704                     pos = d->startPosition();
2705                 } else if (d->trackedItem->endRowPos() > d->endPosition() - d->size() + highlightEnd) {
2706                     pos = d->endPosition() - d->size() + 1;
2707                     if (pos < d->startPosition())
2708                         pos = d->startPosition();
2709                 } else {
2710                     if (trackedPos < viewPos + highlightStart) {
2711                         pos = trackedPos - highlightStart;
2712                     } else if (trackedPos > viewPos + highlightEnd - d->rowSize()) {
2713                         pos = trackedPos - highlightEnd + d->rowSize();
2714                     }
2715                 }
2716             }
2717         } else {
2718             if (trackedPos < viewPos && d->currentItem->rowPos() < viewPos) {
2719                 pos = qMax(trackedPos, d->currentItem->rowPos());
2720             } else if (d->trackedItem->endRowPos() >= viewPos + d->size()
2721                 && d->currentItem->endRowPos() >= viewPos + d->size()) {
2722                 if (d->trackedItem->endRowPos() <= d->currentItem->endRowPos()) {
2723                     pos = d->trackedItem->endRowPos() - d->size() + 1;
2724                     if (d->rowSize() > d->size())
2725                         pos = trackedPos;
2726                 } else {
2727                     pos = d->currentItem->endRowPos() - d->size() + 1;
2728                     if (d->rowSize() > d->size())
2729                         pos = d->currentItem->rowPos();
2730                 }
2731             }
2732         }
2733         if (viewPos != pos) {
2734             cancelFlick();
2735             d->calcVelocity = true;
2736             d->setPosition(pos);
2737             d->calcVelocity = false;
2738         }
2739     }
2740 }
2741
2742 void QDeclarativeGridView::itemsInserted(int modelIndex, int count)
2743 {
2744     Q_D(QDeclarativeGridView);
2745     if (!isComponentComplete())
2746         return;
2747
2748     int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
2749     if (index < 0) {
2750         int i = d->visibleItems.count() - 1;
2751         while (i > 0 && d->visibleItems.at(i)->index == -1)
2752             --i;
2753         if (d->visibleItems.at(i)->index + 1 == modelIndex) {
2754             // Special case of appending an item to the model.
2755             index = d->visibleIndex + d->visibleItems.count();
2756         } else {
2757             if (modelIndex <= d->visibleIndex) {
2758                 // Insert before visible items
2759                 d->visibleIndex += count;
2760                 for (int i = 0; i < d->visibleItems.count(); ++i) {
2761                     FxGridItem *listItem = d->visibleItems.at(i);
2762                     if (listItem->index != -1 && listItem->index >= modelIndex)
2763                         listItem->index += count;
2764                 }
2765             }
2766             if (d->currentIndex >= modelIndex) {
2767                 // adjust current item index
2768                 d->currentIndex += count;
2769                 if (d->currentItem)
2770                     d->currentItem->index = d->currentIndex;
2771                 emit currentIndexChanged();
2772             }
2773             d->scheduleLayout();
2774             d->itemCount += count;
2775             emit countChanged();
2776             return;
2777         }
2778     }
2779
2780     int insertCount = count;
2781     if (index < d->visibleIndex && d->visibleItems.count()) {
2782         insertCount -= d->visibleIndex - index;
2783         index = d->visibleIndex;
2784         modelIndex = d->visibleIndex;
2785     }
2786
2787     qreal tempPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size()+d->width()+1 : d->position();
2788     int to = d->buffer+tempPos+d->size()-1;
2789     int colPos = 0;
2790     int rowPos = 0;
2791     if (d->visibleItems.count()) {
2792         index -= d->visibleIndex;
2793         if (index < d->visibleItems.count()) {
2794             colPos = d->visibleItems.at(index)->colPos();
2795             rowPos = d->visibleItems.at(index)->rowPos();
2796         } else {
2797             // appending items to visible list
2798             colPos = d->visibleItems.at(index-1)->colPos() + d->colSize();
2799             rowPos = d->visibleItems.at(index-1)->rowPos();
2800             if (colPos > d->colSize() * (d->columns-1)) {
2801                 colPos = 0;
2802                 rowPos += d->rowSize();
2803             }
2804         }
2805     } else if (d->itemCount == 0 && d->header) {
2806         rowPos = d->headerSize();
2807     }
2808
2809     // Update the indexes of the following visible items.
2810     for (int i = 0; i < d->visibleItems.count(); ++i) {
2811         FxGridItem *listItem = d->visibleItems.at(i);
2812         if (listItem->index != -1 && listItem->index >= modelIndex)
2813             listItem->index += count;
2814     }
2815
2816     bool addedVisible = false;
2817     QList<FxGridItem*> added;
2818     int i = 0;
2819     while (i < insertCount && rowPos <= to + d->rowSize()*(d->columns - (colPos/d->colSize()))/qreal(d->columns)) {
2820         if (!addedVisible) {
2821             d->scheduleLayout();
2822             addedVisible = true;
2823         }
2824         FxGridItem *item = d->createItem(modelIndex + i);
2825         d->visibleItems.insert(index, item);
2826         item->setPosition(colPos, rowPos);
2827         added.append(item);
2828         colPos += d->colSize();
2829         if (colPos > d->colSize() * (d->columns-1)) {
2830             colPos = 0;
2831             rowPos += d->rowSize();
2832         }
2833         ++index;
2834         ++i;
2835     }
2836     if (i < insertCount) {
2837         // We didn't insert all our new items, which means anything
2838         // beyond the current index is not visible - remove it.
2839         while (d->visibleItems.count() > index) {
2840             d->releaseItem(d->visibleItems.takeLast());
2841         }
2842     }
2843
2844     // update visibleIndex
2845     d->visibleIndex = 0;
2846     for (QList<FxGridItem*>::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
2847         if ((*it)->index != -1) {
2848             d->visibleIndex = (*it)->index;
2849             break;
2850         }
2851     }
2852
2853     if (d->itemCount && d->currentIndex >= modelIndex) {
2854         // adjust current item index
2855         d->currentIndex += count;
2856         if (d->currentItem) {
2857             d->currentItem->index = d->currentIndex;
2858             d->currentItem->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex));
2859         }
2860         emit currentIndexChanged();
2861     } else if (d->itemCount == 0 && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
2862         setCurrentIndex(0);
2863     }
2864
2865     // everything is in order now - emit add() signal
2866     for (int j = 0; j < added.count(); ++j)
2867         added.at(j)->attached->emitAdd();
2868
2869     d->itemCount += count;
2870     emit countChanged();
2871 }
2872
2873 void QDeclarativeGridView::itemsRemoved(int modelIndex, int count)
2874 {
2875     Q_D(QDeclarativeGridView);
2876     if (!isComponentComplete())
2877         return;
2878
2879     d->itemCount -= count;
2880     bool currentRemoved = d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count;
2881     bool removedVisible = false;
2882
2883     // Remove the items from the visible list, skipping anything already marked for removal
2884     QList<FxGridItem*>::Iterator it = d->visibleItems.begin();
2885     while (it != d->visibleItems.end()) {
2886         FxGridItem *item = *it;
2887         if (item->index == -1 || item->index < modelIndex) {
2888             // already removed, or before removed items
2889             if (item->index < modelIndex && !removedVisible) {
2890                 d->scheduleLayout();
2891                 removedVisible = true;
2892             }
2893             ++it;
2894         } else if (item->index >= modelIndex + count) {
2895             // after removed items
2896             item->index -= count;
2897             ++it;
2898         } else {
2899             // removed item
2900             if (!removedVisible) {
2901                 d->scheduleLayout();
2902                 removedVisible = true;
2903             }
2904             item->attached->emitRemove();
2905             if (item->attached->delayRemove()) {
2906                 item->index = -1;
2907                 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
2908                 ++it;
2909             } else {
2910                 it = d->visibleItems.erase(it);
2911                 d->releaseItem(item);
2912             }
2913         }
2914     }
2915
2916     // fix current
2917     if (d->currentIndex >= modelIndex + count) {
2918         d->currentIndex -= count;
2919         if (d->currentItem)
2920             d->currentItem->index -= count;
2921         emit currentIndexChanged();
2922     } else if (currentRemoved) {
2923         // current item has been removed.
2924         d->releaseItem(d->currentItem);
2925         d->currentItem = 0;
2926         d->currentIndex = -1;
2927         if (d->itemCount)
2928             d->updateCurrent(qMin(modelIndex, d->itemCount-1));
2929         else
2930             emit currentIndexChanged();
2931     }
2932
2933     // update visibleIndex
2934     d->visibleIndex = 0;
2935     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
2936         if ((*it)->index != -1) {
2937             d->visibleIndex = (*it)->index;
2938             break;
2939         }
2940     }
2941
2942     if (removedVisible && d->visibleItems.isEmpty()) {
2943         d->timeline.clear();
2944         if (d->itemCount == 0) {
2945             d->setPosition(0);
2946             d->updateHeader();
2947             d->updateFooter();
2948             update();
2949         }
2950     }
2951
2952     emit countChanged();
2953 }
2954
2955 void QDeclarativeGridView::destroyRemoved()
2956 {
2957     Q_D(QDeclarativeGridView);
2958     for (QList<FxGridItem*>::Iterator it = d->visibleItems.begin();
2959             it != d->visibleItems.end();) {
2960         FxGridItem *listItem = *it;
2961         if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
2962             d->releaseItem(listItem);
2963             it = d->visibleItems.erase(it);
2964         } else {
2965             ++it;
2966         }
2967     }
2968
2969     // Correct the positioning of the items
2970     d->layout();
2971 }
2972
2973 void QDeclarativeGridView::itemsMoved(int from, int to, int count)
2974 {
2975     Q_D(QDeclarativeGridView);
2976     if (!isComponentComplete())
2977         return;
2978     QHash<int,FxGridItem*> moved;
2979
2980     FxGridItem *firstItem = d->firstVisibleItem();
2981
2982     QList<FxGridItem*>::Iterator it = d->visibleItems.begin();
2983     while (it != d->visibleItems.end()) {
2984         FxGridItem *item = *it;
2985         if (item->index >= from && item->index < from + count) {
2986             // take the items that are moving
2987             item->index += (to-from);
2988             moved.insert(item->index, item);
2989             it = d->visibleItems.erase(it);
2990         } else {
2991             if (item->index > from && item->index != -1) {
2992                 // move everything after the moved items.
2993                 item->index -= count;
2994                 if (item->index < d->visibleIndex)
2995                     d->visibleIndex = item->index;
2996             }
2997             ++it;
2998         }
2999     }
3000
3001     int remaining = count;
3002     int endIndex = d->visibleIndex;
3003     it = d->visibleItems.begin();
3004     while (it != d->visibleItems.end()) {
3005         FxGridItem *item = *it;
3006         if (remaining && item->index >= to && item->index < to + count) {
3007             // place items in the target position, reusing any existing items
3008             FxGridItem *movedItem = moved.take(item->index);
3009             if (!movedItem)
3010                 movedItem = d->createItem(item->index);
3011             it = d->visibleItems.insert(it, movedItem);
3012             if (it == d->visibleItems.begin() && firstItem)
3013                 movedItem->setPosition(firstItem->colPos(), firstItem->rowPos());
3014             ++it;
3015             --remaining;
3016         } else {
3017             if (item->index != -1) {
3018                 if (item->index >= to) {
3019                     // update everything after the moved items.
3020                     item->index += count;
3021                 }
3022                 endIndex = item->index;
3023             }
3024             ++it;
3025         }
3026     }
3027
3028     // If we have moved items to the end of the visible items
3029     // then add any existing moved items that we have
3030     while (FxGridItem *item = moved.take(endIndex+1)) {
3031         d->visibleItems.append(item);
3032         ++endIndex;
3033     }
3034
3035     // update visibleIndex
3036     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3037         if ((*it)->index != -1) {
3038             d->visibleIndex = (*it)->index;
3039             break;
3040         }
3041     }
3042
3043     // Fix current index
3044     if (d->currentIndex >= 0 && d->currentItem) {
3045         int oldCurrent = d->currentIndex;
3046         d->currentIndex = d->model->indexOf(d->currentItem->item, this);
3047         if (oldCurrent != d->currentIndex) {
3048             d->currentItem->index = d->currentIndex;
3049             emit currentIndexChanged();
3050         }
3051     }
3052
3053     // Whatever moved items remain are no longer visible items.
3054     while (moved.count()) {
3055         int idx = moved.begin().key();
3056         FxGridItem *item = moved.take(idx);
3057         if (d->currentItem && item->item == d->currentItem->item)
3058             item->setPosition(d->colPosAt(idx), d->rowPosAt(idx));
3059         d->releaseItem(item);
3060     }
3061
3062     d->layout();
3063 }
3064
3065 void QDeclarativeGridView::modelReset()
3066 {
3067     Q_D(QDeclarativeGridView);
3068     d->clear();
3069     refill();
3070     d->moveReason = QDeclarativeGridViewPrivate::SetIndex;
3071     d->updateCurrent(d->currentIndex);
3072     if (d->highlight && d->currentItem) {
3073         if (d->autoHighlight)
3074             d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
3075         d->updateTrackedItem();
3076     }
3077     d->moveReason = QDeclarativeGridViewPrivate::Other;
3078
3079     emit countChanged();
3080 }
3081
3082 void QDeclarativeGridView::createdItem(int index, QDeclarativeItem *item)
3083 {
3084     Q_D(QDeclarativeGridView);
3085     if (d->requestedIndex != index) {
3086         item->setParentItem(this);
3087         d->unrequestedItems.insert(item, index);
3088         if (d->flow == QDeclarativeGridView::LeftToRight) {
3089             item->setPos(QPointF(d->colPosAt(index), d->rowPosAt(index)));
3090         } else {
3091             item->setPos(QPointF(d->rowPosAt(index), d->colPosAt(index)));
3092         }
3093     }
3094 }
3095
3096 void QDeclarativeGridView::destroyingItem(QDeclarativeItem *item)
3097 {
3098     Q_D(QDeclarativeGridView);
3099     d->unrequestedItems.remove(item);
3100 }
3101
3102 void QDeclarativeGridView::animStopped()
3103 {
3104     Q_D(QDeclarativeGridView);
3105     d->bufferMode = QDeclarativeGridViewPrivate::NoBuffer;
3106     if (d->haveHighlightRange && d->highlightRange == QDeclarativeGridView::StrictlyEnforceRange)
3107         d->updateHighlight();
3108 }
3109
3110 void QDeclarativeGridView::refill()
3111 {
3112     Q_D(QDeclarativeGridView);
3113     if (d->isRightToLeftTopToBottom())
3114         d->refill(-d->position()-d->size()+1, -d->position());
3115     else
3116         d->refill(d->position(), d->position()+d->size()-1);
3117 }
3118
3119
3120 QDeclarativeGridViewAttached *QDeclarativeGridView::qmlAttachedProperties(QObject *obj)
3121 {
3122     return new QDeclarativeGridViewAttached(obj);
3123 }
3124
3125 QT_END_NAMESPACE