source: trunk/src/declarative/graphicsitems/qdeclarativegridview.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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