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