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