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