Create a forwarding declarative module.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickitemview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickitemview_p_p.h"
43 #include <QtQuick/private/qquicktransition_p.h>
44
45 QT_BEGIN_NAMESPACE
46
47
48 FxViewItem::FxViewItem(QQuickItem *i, bool own)
49     : item(i), ownItem(own), index(-1), releaseAfterTransition(false)
50     , transition(0)
51     , nextTransitionType(FxViewItemTransitionManager::NoTransition)
52     , isTransitionTarget(false)
53     , nextTransitionToSet(false)
54 {
55 }
56
57 FxViewItem::~FxViewItem()
58 {
59     if (transition)
60         transition->m_item = 0;
61     delete transition;
62
63     if (ownItem && item) {
64         item->setParentItem(0);
65         item->deleteLater();
66         item = 0;
67     }
68 }
69
70 qreal FxViewItem::itemX() const
71 {
72     if (nextTransitionType != FxViewItemTransitionManager::NoTransition)
73         return nextTransitionToSet ? nextTransitionTo.x() : item->x();
74     else if (transition && transition->isActive())
75         return transition->m_toPos.x();
76     else
77         return item->x();
78 }
79
80 qreal FxViewItem::itemY() const
81 {
82     // If item is transitioning to some pos, return that dest pos.
83     // If item was redirected to some new pos before the current transition finished,
84     // return that new pos.
85     if (nextTransitionType != FxViewItemTransitionManager::NoTransition)
86         return nextTransitionToSet ? nextTransitionTo.y() : item->y();
87     else if (transition && transition->isActive())
88         return transition->m_toPos.y();
89     else
90         return item->y();
91 }
92
93 void FxViewItem::setVisible(bool visible)
94 {
95     if (!visible && transitionScheduledOrRunning())
96         return;
97     item->setVisible(visible);
98 }
99
100 void FxViewItem::setNextTransition(FxViewItemTransitionManager::TransitionType type, bool isTargetItem)
101 {
102     // Don't reset nextTransitionToSet - once it is set, it cannot be changed
103     // until the animation finishes since the itemX() and itemY() may be used
104     // to calculate positions for transitions for other items in the view.
105     nextTransitionType = type;
106     isTransitionTarget = isTargetItem;
107 }
108
109 bool FxViewItem::transitionScheduledOrRunning() const
110 {
111     return (transition && transition->isActive())
112             || nextTransitionType != FxViewItemTransitionManager::NoTransition;
113 }
114
115 bool FxViewItem::prepareTransition(const QRectF &viewBounds)
116 {
117     bool doTransition = false;
118
119     switch (nextTransitionType) {
120     case FxViewItemTransitionManager::NoTransition:
121     {
122         return false;
123     }
124     case FxViewItemTransitionManager::PopulateTransition:
125     {
126         return true;
127     }
128     case FxViewItemTransitionManager::AddTransition:
129     case FxViewItemTransitionManager::RemoveTransition:
130         // For Add targets, do transition if item is moving into visible area
131         // For Remove targets, do transition if item is currently in visible area
132         if (isTransitionTarget) {
133             doTransition = (nextTransitionType == FxViewItemTransitionManager::AddTransition)
134                     ? viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()))
135                     : viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height()));
136             if (!doTransition)
137                 item->setPos(nextTransitionTo);
138         } else {
139             if (viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height()))
140                     || viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()))) {
141                 doTransition = (nextTransitionTo != item->pos());
142             } else {
143                 item->setPos(nextTransitionTo);
144             }
145         }
146         break;
147     case FxViewItemTransitionManager::MoveTransition:
148         // do transition if moving from or into visible area
149         if (nextTransitionTo != item->pos()) {
150             doTransition = viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height()))
151                     || viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()));
152             if (!doTransition)
153                 item->setPos(nextTransitionTo);
154         }
155         break;
156     }
157
158     if (!doTransition)
159         resetTransitionData();
160     return doTransition;
161 }
162
163 void FxViewItem::startTransition()
164 {
165     if (nextTransitionType == FxViewItemTransitionManager::NoTransition)
166         return;
167
168     if (!transition || transition->m_type != nextTransitionType || transition->m_isTarget != isTransitionTarget) {
169         delete transition;
170         transition = new FxViewItemTransitionManager;
171     }
172
173     // if item is not already moving somewhere, set it to not move anywhere
174     // so that removed items do not move to the default (0,0)
175     if (!nextTransitionToSet)
176         moveTo(item->pos());
177
178     transition->startTransition(this, nextTransitionType, nextTransitionTo, isTransitionTarget);
179     nextTransitionType = FxViewItemTransitionManager::NoTransition;
180 }
181
182 void FxViewItem::stopTransition()
183 {
184     if (transition) {
185         transition->cancel();
186         delete transition;
187         transition = 0;
188     }
189     resetTransitionData();
190     finishedTransition();
191 }
192
193 void FxViewItem::finishedTransition()
194 {
195     nextTransitionToSet = false;
196     nextTransitionTo = QPointF();
197
198     if (releaseAfterTransition) {
199         QQuickItemViewPrivate *vp = static_cast<QQuickItemViewPrivate*>(QObjectPrivate::get(itemView()));
200         vp->releasePendingTransition.removeOne(this);
201         vp->releaseItem(this);
202     }
203 }
204
205 void FxViewItem::resetTransitionData()
206 {
207     nextTransitionType = FxViewItemTransitionManager::NoTransition;
208     isTransitionTarget = false;
209     nextTransitionTo = QPointF();
210     nextTransitionToSet = false;
211 }
212
213 bool FxViewItem::isPendingRemoval() const
214 {
215     if (nextTransitionType == FxViewItemTransitionManager::RemoveTransition)
216         return isTransitionTarget;
217     if (transition && transition->isActive() && transition->m_type == FxViewItemTransitionManager::RemoveTransition)
218         return transition->m_isTarget;
219     return false;
220 }
221
222 void FxViewItem::moveTo(const QPointF &pos)
223 {
224     if (transitionScheduledOrRunning()) {
225         nextTransitionTo = pos;
226         nextTransitionToSet = true;
227     } else {
228         item->setPos(pos);
229     }
230 }
231
232
233 FxViewItemTransitionManager::FxViewItemTransitionManager()
234     : m_active(false), m_item(0), m_type(FxViewItemTransitionManager::NoTransition), m_isTarget(false)
235 {
236 }
237
238 FxViewItemTransitionManager::~FxViewItemTransitionManager()
239 {
240 }
241
242 bool FxViewItemTransitionManager::isActive() const
243 {
244     return m_active;
245 }
246
247 void FxViewItemTransitionManager::startTransition(FxViewItem *item, FxViewItemTransitionManager::TransitionType type, const QPointF &to, bool isTargetItem)
248 {
249     if (!item) {
250         qWarning("startTransition(): invalid item");
251         return;
252     }
253
254     QQuickItemViewPrivate *vp = static_cast<QQuickItemViewPrivate*>(QObjectPrivate::get(item->itemView()));
255
256     QQuickTransition *trans = 0;
257     switch (type) {
258     case NoTransition:
259         break;
260     case PopulateTransition:
261         trans = vp->populateTransition;
262         break;
263     case AddTransition:
264         trans = isTargetItem ? vp->addTransition : vp->addDisplacedTransition;
265         break;
266     case MoveTransition:
267         trans = isTargetItem ? vp->moveTransition : vp->moveDisplacedTransition;
268         break;
269     case RemoveTransition:
270         trans = isTargetItem ? vp->removeTransition : vp->removeDisplacedTransition;
271         break;
272     }
273
274     if (!trans) {
275         qWarning("QQuickItemView: invalid view transition!");
276         return;
277     }
278
279     m_active = true;
280     m_item = item;
281     m_toPos = to;
282     m_type = type;
283     m_isTarget = isTargetItem;
284
285     QQuickViewTransitionAttached *attached =
286             static_cast<QQuickViewTransitionAttached*>(qmlAttachedPropertiesObject<QQuickViewTransitionAttached>(trans));
287     if (attached) {
288         attached->m_index = item->index;
289         attached->m_item = item->item;
290         attached->m_destination = to;
291         switch (type) {
292         case NoTransition:
293             break;
294         case PopulateTransition:
295         case AddTransition:
296             attached->m_targetIndexes = vp->addTransitionIndexes;
297             attached->m_targetItems = vp->addTransitionTargets;
298             break;
299         case MoveTransition:
300             attached->m_targetIndexes = vp->moveTransitionIndexes;
301             attached->m_targetItems = vp->moveTransitionTargets;
302             break;
303         case RemoveTransition:
304             attached->m_targetIndexes = vp->removeTransitionIndexes;
305             attached->m_targetItems = vp->removeTransitionTargets;
306             break;
307         }
308         emit attached->indexChanged();
309         emit attached->itemChanged();
310         emit attached->destinationChanged();
311         emit attached->targetIndexesChanged();
312         emit attached->targetItemsChanged();
313     }
314
315     QQuickStateOperation::ActionList actions;
316     actions << QQuickAction(item->item, QLatin1String("x"), QVariant(to.x()));
317     actions << QQuickAction(item->item, QLatin1String("y"), QVariant(to.y()));
318
319     QQuickTransitionManager::transition(actions, trans, item->item);
320 }
321
322 void FxViewItemTransitionManager::finished()
323 {
324     QQuickTransitionManager::finished();
325
326     m_active = false;
327
328     if (m_item)
329         m_item->finishedTransition();
330     m_item = 0;
331     m_toPos.setX(0);
332     m_toPos.setY(0);
333     m_type = NoTransition;
334     m_isTarget = false;
335 }
336
337
338 QQuickItemViewChangeSet::QQuickItemViewChangeSet()
339     : active(false)
340 {
341     reset();
342 }
343
344 bool QQuickItemViewChangeSet::hasPendingChanges() const
345 {
346     return !pendingChanges.isEmpty();
347 }
348
349 void QQuickItemViewChangeSet::applyChanges(const QQuickChangeSet &changeSet)
350 {
351     pendingChanges.apply(changeSet);
352
353     int moveId = -1;
354     int moveOffset = 0;
355
356     foreach (const QQuickChangeSet::Remove &r, changeSet.removes()) {
357         itemCount -= r.count;
358         if (moveId == -1 && newCurrentIndex >= r.index + r.count) {
359             newCurrentIndex -= r.count;
360             currentChanged = true;
361         } else if (moveId == -1 && newCurrentIndex >= r.index && newCurrentIndex < r.index + r.count) {
362             // current item has been removed.
363             if (r.isMove()) {
364                 moveId = r.moveId;
365                 moveOffset = newCurrentIndex - r.index;
366             } else {
367                 currentRemoved = true;
368                 newCurrentIndex = -1;
369                 if (itemCount)
370                     newCurrentIndex = qMin(r.index, itemCount - 1);
371             }
372             currentChanged = true;
373         }
374     }
375     foreach (const QQuickChangeSet::Insert &i, changeSet.inserts()) {
376         if (moveId == -1) {
377             if (itemCount && newCurrentIndex >= i.index) {
378                 newCurrentIndex += i.count;
379                 currentChanged = true;
380             } else if (newCurrentIndex < 0) {
381                 newCurrentIndex = 0;
382                 currentChanged = true;
383             } else if (newCurrentIndex == 0 && !itemCount) {
384                 // this is the first item, set the initial current index
385                 currentChanged = true;
386             }
387         } else if (moveId == i.moveId) {
388             newCurrentIndex = i.index + moveOffset;
389         }
390         itemCount += i.count;
391     }
392 }
393
394 void QQuickItemViewChangeSet::prepare(int currentIndex, int count)
395 {
396     if (active)
397         return;
398     reset();
399     active = true;
400     itemCount = count;
401     newCurrentIndex = currentIndex;
402 }
403
404 void QQuickItemViewChangeSet::reset()
405 {
406     itemCount = 0;
407     newCurrentIndex = -1;
408     pendingChanges.clear();
409     removedItems.clear();
410     active = false;
411     currentChanged = false;
412     currentRemoved = false;
413 }
414
415
416 QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
417     : QObject(parent), m_index(-1), m_item(0)
418 {
419 }
420 /*!
421     \qmlclass ViewTransition QQuickViewTransitionAttached
422     \inqmlmodule QtQuick 2
423     \ingroup qml-view-elements
424     \brief The ViewTransition attached property provides details on items under transition in a view.
425
426     With ListView and GridView, it is possible to specify transitions that should be applied whenever
427     the items in the view change as a result of modifications to the view's model. They both have the
428     following properties that can be set to the appropriate transitions to be run for various
429     operations:
430
431     \list
432     \o \c add and \c addDisplaced - the transitions to run when items are added to the view
433     \o \c remove and \c removeDisplaced - the transitions to run when items are removed from the view
434     \o \c move and \c moveDisplaced - the transitions to run when items are moved within the view
435        (i.e. as a result of a move operation in the model)
436     \o \c populate - the transition to run when a view is created, or when the model changes
437     \endlist
438
439     Such view transitions additionally have access to a ViewTransition attached property that
440     provides details of the items that are under transition and the operation that triggered the
441     transition. Since view transitions are run once per item, these details can be used to customise
442     each transition for each individual item.
443
444     The ViewTransition attached property provides the following properties specific to the item to
445     which the transition is applied:
446
447     \list
448     \o ViewTransition.item - the item that is under transition
449     \o ViewTransition.index - the index of this item
450     \o ViewTransition.destination - the (x,y) point to which this item is moving for the relevant view operation
451     \endlist
452
453     In addition, ViewTransition provides properties specific to the items which are the target
454     of the operation that triggered the transition:
455
456     \list
457     \o ViewTransition.targetIndexes - the indexes of the target items
458     \o ViewTransition.targetItems - the target items themselves
459     \endlist
460
461     View transitions can be written without referring to any of the attributes listed
462     above. These attributes merely provide extra details that are useful for customising view
463     transitions.
464
465     Following is an introduction to view transitions and the ways in which the ViewTransition
466     attached property can be used to augment view transitions.
467
468
469     \section2 View transitions: a simple example
470
471     Here is a basic example of the use of view transitions. The view below specifies transitions for
472     the \c add and \c addDisplaced properties, which will be run when items are added to the view:
473
474     \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-basic.qml 0
475
476     When the space key is pressed, adding an item to the model, the new item will fade in and
477     increase in scale over 400 milliseconds as it is added to the view. Also, any item that is
478     displaced by the addition of a new item will animate to its new position in the view over
479     400 milliseconds, as specified by the \c addDisplaced transition.
480
481     If five items were inserted in succession at index 0, the effect would be this:
482
483     \image viewtransitions-basic.gif
484
485     Notice that the NumberAnimation objects above do not need to specify a \c target to animate
486     the appropriate item. Also, the NumberAnimation in the \c addTransition does not need to specify
487     the \c to value to move the item to its correct position in the view. This is because the view
488     implicitly sets the \c target and \c to values with the correct item and final item position
489     values if these properties are not explicitly defined.
490
491     At its simplest, a view transition may just animate an item to its new position following a
492     view operation, just as the \c addDisplaced transition does above, or animate some item properties,
493     as in the \c add transition above. Additionally, a view transition may make use of the
494     ViewTransition attached property to customise animation behavior for different items. Following
495     are some examples of how this can be achieved.
496
497
498     \section2 Using the ViewTransition attached property
499
500     As stated, the various ViewTransition properties provide details specific to the individual item
501     being transitioned as well as the operation that triggered the transition. In the animation above,
502     five items are inserted in succession at index 0. When the fifth and final insertion takes place,
503     adding "Item 4" to the view, the \c add transition is run once (for the inserted item) and the
504     \c addDisplaced transition is run four times (once for each of the four existing items in the view).
505
506     At this point, if we examined the \c addDisplaced transition that was run for the bottom displaced
507     item ("Item 0"), the ViewTransition property values provided to this transition would be as follows:
508
509     \table
510     \header
511         \o Property
512         \o Value
513         \o Explanation
514     \row
515         \o ViewTransition.item
516         \o "Item 0" delegate instance
517         \o The "Item 0" \l Rectangle object itself
518     \row
519         \o ViewTransition.index
520         \o \c int value of 4
521         \o The index of "Item 0" within the model following the add operation
522     \row
523         \o ViewTransition.destination
524         \o \l point value of (0, 120)
525         \o The position that "Item 0" is moving to
526     \row
527         \o ViewTransition.targetIndexes
528         \o \c int array, just contains the integer "0" (zero)
529         \o The index of "Item 4", the new item added to the view
530     \row
531         \o ViewTransition.targetItems
532         \o object array, just contains the "Item 4" delegate instance
533         \o The "Item 4" \l Rectangle object - the new item added to the view
534     \endtable
535
536     The ViewTransition.targetIndexes and ViewTransition.targetItems lists provide the items and
537     indexes of all delegate instances that are the targets of the relevant operation. For an add
538     operation, these are all the items that are added into the view; for a remove, these are all
539     the items removed from the view, and so on. (Note these lists will only contain references to
540     items that have been created within the view or its cached items; targets that are not within
541     the visible area of the view or within the item cache will not be accessible.)
542
543     So, while the ViewTransition.item, ViewTransition.index and ViewTransition.destination values
544     vary for each individual transition that is run, the ViewTransition.targetIndexes and
545     ViewTransition.targetItems values are the same for every \c add and \c addDisplaced transition
546     that is triggered by a particular add operation.
547
548
549     \section3 Delaying animations based on index
550
551     Since each view transition is run once for each item affected by the transition, the ViewTransition
552     properties can be used within a transition to define custom behavior for each item's transition.
553     For example, the ListView in the previous example could use this information to create a ripple-type
554     effect on the movement of the displaced items.
555
556     This can be achieved by modifying the \c addDisplaced transition so that it delays the animation of
557     each displaced item based on the difference between its index (provided by ViewTransition.index)
558     and the first removed index (provided by ViewTransition.targetIndexes):
559
560     \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-delayedbyindex.qml 0
561
562     Each displaced item delays its animation by an additional 100 milliseconds, producing a subtle
563     ripple-type effect when items are displaced by the add, like this:
564
565     \image viewtransitions-delayedbyindex.gif
566
567
568     \section3 Animating items to intermediate positions
569
570     The ViewTransition.item property gives a reference to the item to which the transition is being
571     applied. This can be used to access any of the item's attributes, custom \c property values,
572     and so on.
573
574     Below is a modification of the \c addDisplaced transition from the previous example. It adds a
575     ParallelAnimation with nested NumberAnimation objects that reference ViewTransition.item to access
576     each item's \c x and \c y values at the start of their transitions. This allows each item to
577     animate to an intermediate position relative to its starting point for the transition, before
578     animating to its final position in the view:
579
580     \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-intermediatemove.qml 0
581
582     Now, a displaced item will first move to a position of (20, 50) relative to its starting
583     position, and then to its final, correct position in the view:
584
585     \image viewtransitions-intermediatemove.gif
586
587     Since the final NumberAnimation does not specify a \c to value, the view implicitly sets this
588     value to the item's final position in the view, and so this last animation will move this item
589     to the correct place. If the transition requires the final position of the item for some calculation,
590     this is accessible through ViewTransition.destination.
591
592     Instead of using multiple NumberAnimations, you could use a PathAnimation to animate an item over
593     a curved path. For example, the \c add transition in the previous example could be augmented with
594     a PathAnimation as follows: to animate newly added items along a path:
595
596     \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-pathanim.qml 0
597
598     This animates newly added items along a path. Notice that each path is specified relative to
599     each item's final destination point, so that items inserted at different indexes start their
600     paths from different positions:
601
602     \image viewtransitions-pathanim.gif
603
604
605     \section2 Handling interrupted animations
606
607     A view transition may be interrupted at any time if a different view transition needs to be
608     applied while the original transition is in progress. For example, say Item A is inserted at index 0
609     and undergoes an "add" transition; then, Item B is inserted at index 0 in quick succession before
610     Item A's transition has finished. Since Item B is inserted before Item A, it will displace Item
611     A, causing the view to interrupt Item A's "add" transition mid-way and start an "addDisplaced"
612     transition on Item A instead.
613
614     For simple animations that simply animate an item's movement to its final destination, this
615     interruption is unlikely to require additional consideration. However, if a transition changes other
616     properties, this interruption may cause unwanted side effects. Consider the first example on this
617     page, repeated below for convenience:
618
619     \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-basic.qml 0
620
621     If multiple items are added in rapid succession, without waiting for a previous transition
622     to finish, this is the result:
623
624     \image viewtransitions-interruptedbad.gif
625
626     Each newly added item undergoes an \c add transition, but before the transition can finish,
627     another item is added, displacing the previously added item. Because of this, the \c add
628     transition on the previously added item is interrupted and an \c addDisplaced transition is
629     started on the item instead. Due to the interruption, the \c opacity and \c scale animations
630     have not completed, thus producing items with opacity and scale that are below 1.0.
631
632     To fix this, the \c addDisplaced transition should additionally ensure the item properties are
633     set to the end values specified in the \c add transition, effectively resetting these values
634     whenever an item is displaced. In this case, it means setting the item opacity and scale to 1.0:
635
636     \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-interruptedgood.qml 0
637
638     Now, when an item's \c add transition is interrupted, its opacity and scale are animated to 1.0
639     upon displacement, avoiding the erroneous visual effects from before:
640
641     \image viewtransitions-interruptedgood.gif
642
643     The same principle applies to any combination of view transitions. An added item may be moved
644     before its add transition finishes, or a moved item may be removed before its moved transition
645     finishes, and so on; so, the rule of thumb is that every transition should handle the same set of
646     properties.
647
648
649     \section2 Restrictions regarding ScriptAction
650
651     When a view transition is initialized, any property bindings that refer to the ViewTransition
652     attached property are evaluated in preparation for the transition. Due to the nature of the
653     internal construction of a view transition, the attributes of the ViewTransition attached
654     property are only valid for the relevant item when the transition is initialized, and may not be
655     valid when the transition is actually run.
656
657     Therefore, a ScriptAction within a view transition should not refer to the ViewTransition
658     attached property, as it may not refer to the expected values at the time that the ScriptAction
659     is actually invoked. Consider the following example:
660
661     \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactionbad.qml 0
662
663     When the space key is pressed, three items are moved from index 5 to index 1. For each moved
664     item, the \c moveTransition sequence presumably animates the item's color to "yellow", then
665     animates it to its final position, then changes the item color back to "lightsteelblue" using a
666     ScriptAction. However, when run, the transition does not produce the intended result:
667
668     \image viewtransitions-scriptactionbad.gif
669
670     Only the last moved item is returned to the "lightsteelblue" color; the others remain yellow. This
671     is because the ScriptAction is not run until after the transition has already been initialized, by
672     which time the ViewTransition.item value has changed to refer to a different item; the item that
673     the script had intended to refer to is not the one held by ViewTransition.item at the time the
674     ScriptAction is actually invoked.
675
676     In this instance, to avoid this issue, the view could set the property using a PropertyAction
677     instead:
678
679     \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactiongood.qml 0
680
681     When the transition is initialized, the PropertyAction \c target will be set to the respective
682     ViewTransition.item for the transition and will later run with the correct item target as
683     expected.
684   */
685
686 /*!
687     \qmlattachedproperty list QtQuick2::ViewTransition::index
688
689     This attached property holds the index of the item that is being
690     transitioned.
691
692     Note that if the item is being moved, this property holds the index that
693     the item is moving to, not from.
694 */
695
696 /*!
697     \qmlattachedproperty list QtQuick2::ViewTransition::item
698
699     This attached property holds the the item that is being transitioned.
700
701     \warning This item should not be kept and referred to outside of the transition
702     as it may become invalid as the view changes.
703 */
704
705 /*!
706     \qmlattachedproperty list QtQuick2::ViewTransition::destination
707
708     This attached property holds the final destination position for the transitioned
709     item within the view.
710
711     This property value is a \l point with \c x and \c y properties.
712 */
713
714 /*!
715     \qmlattachedproperty list QtQuick2::ViewTransition::targetIndexes
716
717     This attached property holds a list of the indexes of the items in view
718     that are the target of the relevant operation.
719
720     The targets are the items that are the subject of the operation. For
721     an add operation, these are the items being added; for a remove, these
722     are the items being removed; for a move, these are the items being
723     moved.
724
725     For example, if the transition was triggered by an insert operation
726     that added two items at index 1 and 2, this targetIndexes list would
727     have the value [1,2].
728
729     \note The targetIndexes list only contains the indexes of items that are actually
730     in view, or will be in the view once the relevant operation completes.
731
732     \sa QtQuick2::ViewTransition::targetIndexes
733 */
734
735 /*!
736     \qmlattachedproperty list QtQuick2::ViewTransition::targetItems
737
738     This attached property holds the list of items in view that are the
739     target of the relevant operation.
740
741     The targets are the items that are the subject of the operation. For
742     an add operation, these are the items being added; for a remove, these
743     are the items being removed; for a move, these are the items being
744     moved.
745
746     For example, if the transition was triggered by an insert operation
747     that added two items at index 1 and 2, this targetItems list would
748     contain these two items.
749
750     \note The targetItems list only contains items that are actually
751     in view, or will be in the view once the relevant operation completes.
752
753     \warning The objects in this list should not be kept and referred to
754     outside of the transition as the items may become invalid. The targetItems
755     are only valid when the Transition is initially created; this also means
756     they should not be used by ScriptAction objects in the Transition, which are
757     not evaluated until the transition is run.
758
759     \sa QtQuick2::ViewTransition::targetIndexes
760 */
761 QQmlListProperty<QObject> QQuickViewTransitionAttached::targetItems()
762 {
763     return QQmlListProperty<QObject>(this, m_targetItems);
764 }
765
766 QQuickViewTransitionAttached *QQuickViewTransitionAttached::qmlAttachedProperties(QObject *obj)
767 {
768     return new QQuickViewTransitionAttached(obj);
769 }
770
771
772 //-----------------------------------
773
774 QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent)
775     : QQuickFlickable(dd, parent)
776 {
777     Q_D(QQuickItemView);
778     d->init();
779 }
780
781 QQuickItemView::~QQuickItemView()
782 {
783     Q_D(QQuickItemView);
784     d->clear();
785     if (d->ownModel)
786         delete d->model;
787     delete d->header;
788     delete d->footer;
789 }
790
791
792 QQuickItem *QQuickItemView::currentItem() const
793 {
794     Q_D(const QQuickItemView);
795     if (!d->currentItem)
796         return 0;
797     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
798     return d->currentItem->item;
799 }
800
801 QVariant QQuickItemView::model() const
802 {
803     Q_D(const QQuickItemView);
804     return d->modelVariant;
805 }
806
807 void QQuickItemView::setModel(const QVariant &model)
808 {
809     Q_D(QQuickItemView);
810     if (d->modelVariant == model)
811         return;
812     if (d->model) {
813         disconnect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
814                 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
815         disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
816         disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
817         disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
818     }
819
820     QQuickVisualModel *oldModel = d->model;
821
822     d->clear();
823     d->setPosition(d->contentStartOffset());
824     d->model = 0;
825     d->modelVariant = model;
826
827     QObject *object = qvariant_cast<QObject*>(model);
828     QQuickVisualModel *vim = 0;
829     if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
830         if (d->ownModel) {
831             delete oldModel;
832             d->ownModel = false;
833         }
834         d->model = vim;
835     } else {
836         if (!d->ownModel) {
837             d->model = new QQuickVisualDataModel(qmlContext(this), this);
838             d->ownModel = true;
839             if (isComponentComplete())
840                 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
841         } else {
842             d->model = oldModel;
843         }
844         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
845             dataModel->setModel(model);
846     }
847
848     if (d->model) {
849         d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
850         connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
851         connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
852         connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
853         if (isComponentComplete()) {
854             updateSections();
855             d->refill();
856             if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
857                 setCurrentIndex(0);
858             } else {
859                 d->moveReason = QQuickItemViewPrivate::SetIndex;
860                 d->updateCurrent(d->currentIndex);
861                 if (d->highlight && d->currentItem) {
862                     if (d->autoHighlight)
863                         d->resetHighlightPosition();
864                     d->updateTrackedItem();
865                 }
866                 d->moveReason = QQuickItemViewPrivate::Other;
867             }
868             d->updateViewport();
869
870             if (d->populateTransition) {
871                 d->forceLayout = true;
872                 d->usePopulateTransition = true;
873                 polish();
874             }
875         }
876         connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
877                 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
878         emit countChanged();
879     }
880     emit modelChanged();
881 }
882
883 QQmlComponent *QQuickItemView::delegate() const
884 {
885     Q_D(const QQuickItemView);
886     if (d->model) {
887         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
888             return dataModel->delegate();
889     }
890
891     return 0;
892 }
893
894 void QQuickItemView::setDelegate(QQmlComponent *delegate)
895 {
896     Q_D(QQuickItemView);
897     if (delegate == this->delegate())
898         return;
899     if (!d->ownModel) {
900         d->model = new QQuickVisualDataModel(qmlContext(this));
901         d->ownModel = true;
902     }
903     if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
904         int oldCount = dataModel->count();
905         dataModel->setDelegate(delegate);
906         if (isComponentComplete()) {
907             for (int i = 0; i < d->visibleItems.count(); ++i)
908                 d->releaseItem(d->visibleItems.at(i));
909             d->visibleItems.clear();
910             d->releaseItem(d->currentItem);
911             d->currentItem = 0;
912             updateSections();
913             d->refill();
914             d->moveReason = QQuickItemViewPrivate::SetIndex;
915             d->updateCurrent(d->currentIndex);
916             if (d->highlight && d->currentItem) {
917                 if (d->autoHighlight)
918                     d->resetHighlightPosition();
919                 d->updateTrackedItem();
920             }
921             d->moveReason = QQuickItemViewPrivate::Other;
922             d->updateViewport();
923         }
924         if (oldCount != dataModel->count())
925             emit countChanged();
926     }
927     emit delegateChanged();
928 }
929
930
931 int QQuickItemView::count() const
932 {
933     Q_D(const QQuickItemView);
934     if (!d->model)
935         return 0;
936     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
937     return d->model->count();
938 }
939
940 int QQuickItemView::currentIndex() const
941 {
942     Q_D(const QQuickItemView);
943     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
944     return d->currentIndex;
945 }
946
947 void QQuickItemView::setCurrentIndex(int index)
948 {
949     Q_D(QQuickItemView);
950     if (d->requestedIndex >= 0 && !d->requestedAsync)  // currently creating item
951         return;
952     d->currentIndexCleared = (index == -1);
953
954     d->applyPendingChanges();
955     if (index == d->currentIndex)
956         return;
957     if (isComponentComplete() && d->isValid()) {
958         d->moveReason = QQuickItemViewPrivate::SetIndex;
959         d->updateCurrent(index);
960     } else if (d->currentIndex != index) {
961         d->currentIndex = index;
962         emit currentIndexChanged();
963     }
964 }
965
966
967 bool QQuickItemView::isWrapEnabled() const
968 {
969     Q_D(const QQuickItemView);
970     return d->wrap;
971 }
972
973 void QQuickItemView::setWrapEnabled(bool wrap)
974 {
975     Q_D(QQuickItemView);
976     if (d->wrap == wrap)
977         return;
978     d->wrap = wrap;
979     emit keyNavigationWrapsChanged();
980 }
981
982 int QQuickItemView::cacheBuffer() const
983 {
984     Q_D(const QQuickItemView);
985     return d->buffer;
986 }
987
988 void QQuickItemView::setCacheBuffer(int b)
989 {
990     Q_D(QQuickItemView);
991     if (d->buffer != b) {
992         d->buffer = b;
993         if (isComponentComplete()) {
994             d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
995             d->refill();
996         }
997         emit cacheBufferChanged();
998     }
999 }
1000
1001
1002 Qt::LayoutDirection QQuickItemView::layoutDirection() const
1003 {
1004     Q_D(const QQuickItemView);
1005     return d->layoutDirection;
1006 }
1007
1008 void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1009 {
1010     Q_D(QQuickItemView);
1011     if (d->layoutDirection != layoutDirection) {
1012         d->layoutDirection = layoutDirection;
1013         d->regenerate();
1014         emit layoutDirectionChanged();
1015         emit effectiveLayoutDirectionChanged();
1016     }
1017 }
1018
1019 Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const
1020 {
1021     Q_D(const QQuickItemView);
1022     if (d->effectiveLayoutMirror)
1023         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
1024     else
1025         return d->layoutDirection;
1026 }
1027
1028
1029 QQmlComponent *QQuickItemView::header() const
1030 {
1031     Q_D(const QQuickItemView);
1032     return d->headerComponent;
1033 }
1034
1035 QQuickItem *QQuickItemView::headerItem() const
1036 {
1037     Q_D(const QQuickItemView);
1038     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
1039     return d->header ? d->header->item : 0;
1040 }
1041
1042 void QQuickItemView::setHeader(QQmlComponent *headerComponent)
1043 {
1044     Q_D(QQuickItemView);
1045     if (d->headerComponent != headerComponent) {
1046         d->applyPendingChanges();
1047         delete d->header;
1048         d->header = 0;
1049         d->headerComponent = headerComponent;
1050
1051         d->markExtentsDirty();
1052
1053         if (isComponentComplete()) {
1054             d->updateHeader();
1055             d->updateFooter();
1056             d->updateViewport();
1057             d->fixupPosition();
1058         } else {
1059             emit headerItemChanged();
1060         }
1061         emit headerChanged();
1062     }
1063 }
1064
1065 QQmlComponent *QQuickItemView::footer() const
1066 {
1067     Q_D(const QQuickItemView);
1068     return d->footerComponent;
1069 }
1070
1071 QQuickItem *QQuickItemView::footerItem() const
1072 {
1073     Q_D(const QQuickItemView);
1074     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
1075     return d->footer ? d->footer->item : 0;
1076 }
1077
1078 void QQuickItemView::setFooter(QQmlComponent *footerComponent)
1079 {
1080     Q_D(QQuickItemView);
1081     if (d->footerComponent != footerComponent) {
1082         d->applyPendingChanges();
1083         delete d->footer;
1084         d->footer = 0;
1085         d->footerComponent = footerComponent;
1086
1087         if (isComponentComplete()) {
1088             d->updateFooter();
1089             d->updateViewport();
1090             d->fixupPosition();
1091         } else {
1092             emit footerItemChanged();
1093         }
1094         emit footerChanged();
1095     }
1096 }
1097
1098 QQmlComponent *QQuickItemView::highlight() const
1099 {
1100     Q_D(const QQuickItemView);
1101     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
1102     return d->highlightComponent;
1103 }
1104
1105 void QQuickItemView::setHighlight(QQmlComponent *highlightComponent)
1106 {
1107     Q_D(QQuickItemView);
1108     if (highlightComponent != d->highlightComponent) {
1109         d->applyPendingChanges();
1110         d->highlightComponent = highlightComponent;
1111         d->createHighlight();
1112         if (d->currentItem)
1113             d->updateHighlight();
1114         emit highlightChanged();
1115     }
1116 }
1117
1118 QQuickItem *QQuickItemView::highlightItem() const
1119 {
1120     Q_D(const QQuickItemView);
1121     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
1122     return d->highlight ? d->highlight->item : 0;
1123 }
1124
1125 bool QQuickItemView::highlightFollowsCurrentItem() const
1126 {
1127     Q_D(const QQuickItemView);
1128     return d->autoHighlight;
1129 }
1130
1131 void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
1132 {
1133     Q_D(QQuickItemView);
1134     if (d->autoHighlight != autoHighlight) {
1135         d->autoHighlight = autoHighlight;
1136         if (autoHighlight)
1137             d->updateHighlight();
1138         emit highlightFollowsCurrentItemChanged();
1139     }
1140 }
1141
1142 QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const
1143 {
1144     Q_D(const QQuickItemView);
1145     return static_cast<QQuickItemView::HighlightRangeMode>(d->highlightRange);
1146 }
1147
1148 void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
1149 {
1150     Q_D(QQuickItemView);
1151     if (d->highlightRange == mode)
1152         return;
1153     d->highlightRange = mode;
1154     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1155     emit highlightRangeModeChanged();
1156 }
1157
1158 //###Possibly rename these properties, since they are very useful even without a highlight?
1159 qreal QQuickItemView::preferredHighlightBegin() const
1160 {
1161     Q_D(const QQuickItemView);
1162     return d->highlightRangeStart;
1163 }
1164
1165 void QQuickItemView::setPreferredHighlightBegin(qreal start)
1166 {
1167     Q_D(QQuickItemView);
1168     d->highlightRangeStartValid = true;
1169     if (d->highlightRangeStart == start)
1170         return;
1171     d->highlightRangeStart = start;
1172     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1173     emit preferredHighlightBeginChanged();
1174 }
1175
1176 void QQuickItemView::resetPreferredHighlightBegin()
1177 {
1178     Q_D(QQuickItemView);
1179     d->highlightRangeStartValid = false;
1180     if (d->highlightRangeStart == 0)
1181         return;
1182     d->highlightRangeStart = 0;
1183     emit preferredHighlightBeginChanged();
1184 }
1185
1186 qreal QQuickItemView::preferredHighlightEnd() const
1187 {
1188     Q_D(const QQuickItemView);
1189     return d->highlightRangeEnd;
1190 }
1191
1192 void QQuickItemView::setPreferredHighlightEnd(qreal end)
1193 {
1194     Q_D(QQuickItemView);
1195     d->highlightRangeEndValid = true;
1196     if (d->highlightRangeEnd == end)
1197         return;
1198     d->highlightRangeEnd = end;
1199     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1200     emit preferredHighlightEndChanged();
1201 }
1202
1203 void QQuickItemView::resetPreferredHighlightEnd()
1204 {
1205     Q_D(QQuickItemView);
1206     d->highlightRangeEndValid = false;
1207     if (d->highlightRangeEnd == 0)
1208         return;
1209     d->highlightRangeEnd = 0;
1210     emit preferredHighlightEndChanged();
1211 }
1212
1213 int QQuickItemView::highlightMoveDuration() const
1214 {
1215     Q_D(const QQuickItemView);
1216     return d->highlightMoveDuration;
1217 }
1218
1219 void QQuickItemView::setHighlightMoveDuration(int duration)
1220 {
1221     Q_D(QQuickItemView);
1222     if (d->highlightMoveDuration != duration) {
1223         d->highlightMoveDuration = duration;
1224         emit highlightMoveDurationChanged();
1225     }
1226 }
1227
1228 QQuickTransition *QQuickItemView::populateTransition() const
1229 {
1230     Q_D(const QQuickItemView);
1231     return d->populateTransition;
1232 }
1233
1234 void QQuickItemView::setPopulateTransition(QQuickTransition *transition)
1235 {
1236     Q_D(QQuickItemView);
1237     if (d->populateTransition != transition) {
1238         d->populateTransition = transition;
1239         emit populateTransitionChanged();
1240     }
1241 }
1242
1243 QQuickTransition *QQuickItemView::addTransition() const
1244 {
1245     Q_D(const QQuickItemView);
1246     return d->addTransition;
1247 }
1248
1249 void QQuickItemView::setAddTransition(QQuickTransition *transition)
1250 {
1251     Q_D(QQuickItemView);
1252     if (d->addTransition != transition) {
1253         d->addTransition = transition;
1254         emit addTransitionChanged();
1255     }
1256 }
1257
1258 QQuickTransition *QQuickItemView::addDisplacedTransition() const
1259 {
1260     Q_D(const QQuickItemView);
1261     return d->addDisplacedTransition;
1262 }
1263
1264 void QQuickItemView::setAddDisplacedTransition(QQuickTransition *transition)
1265 {
1266     Q_D(QQuickItemView);
1267     if (d->addDisplacedTransition != transition) {
1268         d->addDisplacedTransition = transition;
1269         emit addDisplacedTransitionChanged();
1270     }
1271 }
1272
1273 QQuickTransition *QQuickItemView::moveTransition() const
1274 {
1275     Q_D(const QQuickItemView);
1276     return d->moveTransition;
1277 }
1278
1279 void QQuickItemView::setMoveTransition(QQuickTransition *transition)
1280 {
1281     Q_D(QQuickItemView);
1282     if (d->moveTransition != transition) {
1283         d->moveTransition = transition;
1284         emit moveTransitionChanged();
1285     }
1286 }
1287
1288 QQuickTransition *QQuickItemView::moveDisplacedTransition() const
1289 {
1290     Q_D(const QQuickItemView);
1291     return d->moveDisplacedTransition;
1292 }
1293
1294 void QQuickItemView::setMoveDisplacedTransition(QQuickTransition *transition)
1295 {
1296     Q_D(QQuickItemView);
1297     if (d->moveDisplacedTransition != transition) {
1298         d->moveDisplacedTransition = transition;
1299         emit moveDisplacedTransitionChanged();
1300     }
1301 }
1302
1303 QQuickTransition *QQuickItemView::removeTransition() const
1304 {
1305     Q_D(const QQuickItemView);
1306     return d->removeTransition;
1307 }
1308
1309 void QQuickItemView::setRemoveTransition(QQuickTransition *transition)
1310 {
1311     Q_D(QQuickItemView);
1312     if (d->removeTransition != transition) {
1313         d->removeTransition = transition;
1314         emit removeTransitionChanged();
1315     }
1316 }
1317
1318 QQuickTransition *QQuickItemView::removeDisplacedTransition() const
1319 {
1320     Q_D(const QQuickItemView);
1321     return d->removeDisplacedTransition;
1322 }
1323
1324 void QQuickItemView::setRemoveDisplacedTransition(QQuickTransition *transition)
1325 {
1326     Q_D(QQuickItemView);
1327     if (d->removeDisplacedTransition != transition) {
1328         d->removeDisplacedTransition = transition;
1329         emit removeDisplacedTransitionChanged();
1330     }
1331 }
1332
1333 void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
1334 {
1335     Q_Q(QQuickItemView);
1336     if (!isValid())
1337         return;
1338     if (mode < QQuickItemView::Beginning || mode > QQuickItemView::Contain)
1339         return;
1340
1341     applyPendingChanges();
1342     int idx = qMax(qMin(index, model->count()-1), 0);
1343
1344     qreal pos = isContentFlowReversed() ? -position() - size() : position();
1345     FxViewItem *item = visibleItem(idx);
1346     qreal maxExtent;
1347     if (layoutOrientation() == Qt::Vertical)
1348         maxExtent = -q->maxYExtent();
1349     else
1350         maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
1351     if (!item) {
1352         int itemPos = positionAt(idx);
1353         changedVisibleIndex(idx);
1354         // save the currently visible items in case any of them end up visible again
1355         QList<FxViewItem *> oldVisible = visibleItems;
1356         visibleItems.clear();
1357         setPosition(qMin(qreal(itemPos), maxExtent));
1358         // now release the reference to all the old visible items.
1359         for (int i = 0; i < oldVisible.count(); ++i)
1360             releaseItem(oldVisible.at(i));
1361         item = visibleItem(idx);
1362     }
1363     if (item) {
1364         const qreal itemPos = item->position();
1365         switch (mode) {
1366         case QQuickItemView::Beginning:
1367             pos = itemPos;
1368             if (index < 0 && header)
1369                 pos -= headerSize();
1370             break;
1371         case QQuickItemView::Center:
1372             pos = itemPos - (size() - item->size())/2;
1373             break;
1374         case QQuickItemView::End:
1375             pos = itemPos - size() + item->size();
1376             if (index >= model->count() && footer)
1377                 pos += footerSize();
1378             break;
1379         case QQuickItemView::Visible:
1380             if (itemPos > pos + size())
1381                 pos = itemPos - size() + item->size();
1382             else if (item->endPosition() <= pos)
1383                 pos = itemPos;
1384             break;
1385         case QQuickItemView::Contain:
1386             if (item->endPosition() >= pos + size())
1387                 pos = itemPos - size() + item->size();
1388             if (itemPos < pos)
1389                 pos = itemPos;
1390         }
1391         pos = qMin(pos, maxExtent);
1392         qreal minExtent;
1393         if (layoutOrientation() == Qt::Vertical)
1394             minExtent = -q->minYExtent();
1395         else
1396             minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
1397         pos = qMax(pos, minExtent);
1398         moveReason = QQuickItemViewPrivate::Other;
1399         q->cancelFlick();
1400         setPosition(pos);
1401
1402         if (highlight) {
1403             if (autoHighlight)
1404                 resetHighlightPosition();
1405             updateHighlight();
1406         }
1407     }
1408     fixupPosition();
1409 }
1410
1411 void QQuickItemView::positionViewAtIndex(int index, int mode)
1412 {
1413     Q_D(QQuickItemView);
1414     if (!d->isValid() || index < 0 || index >= d->model->count())
1415         return;
1416     d->positionViewAtIndex(index, mode);
1417 }
1418
1419
1420 void QQuickItemView::positionViewAtBeginning()
1421 {
1422     Q_D(QQuickItemView);
1423     if (!d->isValid())
1424         return;
1425     d->positionViewAtIndex(-1, Beginning);
1426 }
1427
1428 void QQuickItemView::positionViewAtEnd()
1429 {
1430     Q_D(QQuickItemView);
1431     if (!d->isValid())
1432         return;
1433     d->positionViewAtIndex(d->model->count(), End);
1434 }
1435
1436 int QQuickItemView::indexAt(qreal x, qreal y) const
1437 {
1438     Q_D(const QQuickItemView);
1439     for (int i = 0; i < d->visibleItems.count(); ++i) {
1440         const FxViewItem *item = d->visibleItems.at(i);
1441         if (item->contains(x, y))
1442             return item->index;
1443     }
1444
1445     return -1;
1446 }
1447
1448 QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const
1449 {
1450     Q_D(const QQuickItemView);
1451     for (int i = 0; i < d->visibleItems.count(); ++i) {
1452         const FxViewItem *item = d->visibleItems.at(i);
1453         if (item->contains(x, y))
1454             return item->item;
1455     }
1456
1457     return 0;
1458 }
1459
1460 void QQuickItemViewPrivate::applyPendingChanges()
1461 {
1462     Q_Q(QQuickItemView);
1463     if (q->isComponentComplete() && currentChanges.hasPendingChanges())
1464         layout();
1465 }
1466
1467 bool QQuickItemViewPrivate::canTransition(FxViewItemTransitionManager::TransitionType type, bool asTarget) const
1468 {
1469     switch (type) {
1470     case FxViewItemTransitionManager::NoTransition:
1471         break;
1472     case FxViewItemTransitionManager::PopulateTransition:
1473         return usePopulateTransition
1474                 && populateTransition && populateTransition->enabled();
1475     case FxViewItemTransitionManager::AddTransition:
1476         if (asTarget)
1477             return addTransition && addTransition->enabled();
1478         else
1479             return addDisplacedTransition && addDisplacedTransition->enabled();
1480     case FxViewItemTransitionManager::MoveTransition:
1481         if (asTarget)
1482             return moveTransition && moveTransition->enabled();
1483         else
1484             return moveDisplacedTransition && moveDisplacedTransition->enabled();
1485     case FxViewItemTransitionManager::RemoveTransition:
1486         if (asTarget)
1487             return removeTransition && removeTransition->enabled();
1488         else
1489             return removeDisplacedTransition && removeDisplacedTransition->enabled();
1490     }
1491     return false;
1492 }
1493
1494 bool QQuickItemViewPrivate::hasItemTransitions() const
1495 {
1496     return canTransition(FxViewItemTransitionManager::PopulateTransition, true)
1497             || canTransition(FxViewItemTransitionManager::AddTransition, true)
1498             || canTransition(FxViewItemTransitionManager::AddTransition, false)
1499             || canTransition(FxViewItemTransitionManager::MoveTransition, true)
1500             || canTransition(FxViewItemTransitionManager::MoveTransition, false)
1501             || canTransition(FxViewItemTransitionManager::RemoveTransition, true)
1502             || canTransition(FxViewItemTransitionManager::RemoveTransition, false);
1503 }
1504
1505 void QQuickItemViewPrivate::transitionNextReposition(FxViewItem *item, FxViewItemTransitionManager::TransitionType type, bool isTarget)
1506 {
1507     bool matchedTransition = false;
1508     if (type == FxViewItemTransitionManager::AddTransition) {
1509         // don't run add transitions for added items while populating
1510         matchedTransition = !usePopulateTransition && canTransition(type, isTarget);
1511     } else {
1512         matchedTransition = canTransition(type, isTarget);
1513     }
1514
1515     if (matchedTransition) {
1516         item->setNextTransition(type, isTarget);
1517     } else {
1518         // the requested transition type is not valid, but the item is scheduled/in another
1519         // transition, so cancel it to allow the item to move directly to the correct pos
1520         if (item->transitionScheduledOrRunning())
1521             item->stopTransition();
1522     }
1523 }
1524
1525 int QQuickItemViewPrivate::findMoveKeyIndex(QQuickChangeSet::MoveKey key, const QVector<QQuickChangeSet::Remove> &changes) const
1526 {
1527     for (int i=0; i<changes.count(); i++) {
1528         for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) {
1529             if (changes[i].moveKey(j) == key)
1530                 return j;
1531         }
1532     }
1533     return -1;
1534 }
1535
1536 // for debugging only
1537 void QQuickItemViewPrivate::checkVisible() const
1538 {
1539     int skip = 0;
1540     for (int i = 0; i < visibleItems.count(); ++i) {
1541         FxViewItem *item = visibleItems.at(i);
1542         if (item->index == -1) {
1543             ++skip;
1544         } else if (item->index != visibleIndex + i - skip) {
1545             qFatal("index %d %d %d", visibleIndex, i, item->index);
1546         }
1547     }
1548 }
1549
1550 // for debugging only
1551 void QQuickItemViewPrivate::showVisibleItems() const
1552 {
1553     qDebug() << "Visible items:";
1554     for (int i = 0; i < visibleItems.count(); ++i) {
1555         qDebug() << "\t" << visibleItems[i]->index
1556                  << visibleItems[i]->item->objectName()
1557                  << visibleItems[i]->position();
1558     }
1559 }
1560
1561 void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1562 {
1563     Q_Q(QQuickItemView);
1564     QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1565     if (!q->isComponentComplete())
1566         return;
1567
1568     if (header && header->item == item) {
1569         updateHeader();
1570         markExtentsDirty();
1571         if (!q->isMoving() && !q->isFlicking())
1572             fixupPosition();
1573     } else if (footer && footer->item == item) {
1574         updateFooter();
1575         markExtentsDirty();
1576         if (!q->isMoving() && !q->isFlicking())
1577             fixupPosition();
1578     }
1579
1580     if (currentItem && currentItem->item == item) {
1581         // don't allow item movement transitions to trigger a re-layout and
1582         // start new transitions
1583         bool prevDisableLayout = disableLayout;
1584         if (!disableLayout) {
1585             FxViewItem *actualItem = hasItemTransitions() ? visibleItem(currentIndex) : 0;
1586             if (actualItem && actualItem->transition && actualItem->transition->isRunning())
1587                 disableLayout = true;
1588         }
1589         updateHighlight();
1590         disableLayout = prevDisableLayout;
1591     }
1592
1593     if (trackedItem && trackedItem->item == item)
1594         q->trackedPositionChanged();
1595 }
1596
1597 void QQuickItemView::destroyRemoved()
1598 {
1599     Q_D(QQuickItemView);
1600     for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1601             it != d->visibleItems.end();) {
1602         FxViewItem *item = *it;
1603         if (item->index == -1 && item->attached->delayRemove() == false) {
1604             if (d->canTransition(FxViewItemTransitionManager::RemoveTransition, true)) {
1605                 // don't remove from visibleItems until next layout()
1606                 d->runDelayedRemoveTransition = true;
1607                 QObject::disconnect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()));
1608                 ++it;
1609             } else {
1610                 d->releaseItem(item);
1611                 it = d->visibleItems.erase(it);
1612             }
1613         } else {
1614             ++it;
1615         }
1616     }
1617
1618     // Correct the positioning of the items
1619     d->updateSections();
1620     d->forceLayout = true;
1621     polish();
1622 }
1623
1624 void QQuickItemView::modelUpdated(const QQuickChangeSet &changeSet, bool reset)
1625 {
1626     Q_D(QQuickItemView);
1627     if (reset) {
1628         d->usePopulateTransition = true;
1629         d->moveReason = QQuickItemViewPrivate::SetIndex;
1630         d->regenerate();
1631         if (d->highlight && d->currentItem) {
1632             if (d->autoHighlight)
1633                 d->resetHighlightPosition();
1634             d->updateTrackedItem();
1635         }
1636         d->moveReason = QQuickItemViewPrivate::Other;
1637         emit countChanged();
1638         if (d->populateTransition) {
1639             d->forceLayout = true;
1640             polish();
1641         }
1642     } else {
1643         d->currentChanges.prepare(d->currentIndex, d->itemCount);
1644         d->currentChanges.applyChanges(changeSet);
1645         polish();
1646     }
1647 }
1648
1649 void QQuickItemView::animStopped()
1650 {
1651     Q_D(QQuickItemView);
1652     d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1653     d->refill();
1654     if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange)
1655         d->updateHighlight();
1656 }
1657
1658
1659 void QQuickItemView::trackedPositionChanged()
1660 {
1661     Q_D(QQuickItemView);
1662     if (!d->trackedItem || !d->currentItem)
1663         return;
1664     if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
1665         qreal trackedPos = d->trackedItem->position();
1666         qreal trackedSize = d->trackedItem->size();
1667         qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1668         qreal pos = viewPos;
1669         if (d->haveHighlightRange) {
1670             if (trackedPos > pos + d->highlightRangeEnd - trackedSize)
1671                 pos = trackedPos - d->highlightRangeEnd + trackedSize;
1672             if (trackedPos < pos + d->highlightRangeStart)
1673                 pos = trackedPos - d->highlightRangeStart;
1674             if (d->highlightRange != StrictlyEnforceRange) {
1675                 if (pos > d->endPosition() - d->size())
1676                     pos = d->endPosition() - d->size();
1677                 if (pos < d->startPosition())
1678                     pos = d->startPosition();
1679             }
1680         } else {
1681             if (d->trackedItem != d->currentItem) {
1682                 // also make section header visible
1683                 trackedPos -= d->currentItem->sectionSize();
1684                 trackedSize += d->currentItem->sectionSize();
1685             }
1686             qreal trackedEndPos = d->trackedItem->endPosition();
1687             qreal toItemPos = d->currentItem->position();
1688             qreal toItemEndPos = d->currentItem->endPosition();
1689             if (d->showHeaderForIndex(d->currentIndex)) {
1690                 qreal startOffset = -d->contentStartOffset();
1691                 trackedPos -= startOffset;
1692                 trackedEndPos -= startOffset;
1693                 toItemPos -= startOffset;
1694                 toItemEndPos -= startOffset;
1695             } else if (d->showFooterForIndex(d->currentIndex)) {
1696                 qreal endOffset = d->footerSize();
1697                 if (d->layoutOrientation() == Qt::Vertical)
1698                     endOffset += d->vData.endMargin;
1699                 else if (d->isContentFlowReversed())
1700                     endOffset += d->hData.endMargin;
1701                 else
1702                     endOffset += d->hData.startMargin;
1703                 trackedPos += endOffset;
1704                 trackedEndPos += endOffset;
1705                 toItemPos += endOffset;
1706                 toItemEndPos += endOffset;
1707             }
1708
1709             if (trackedEndPos >= viewPos + d->size()
1710                 && toItemEndPos >= viewPos + d->size()) {
1711                 if (trackedEndPos <= toItemEndPos) {
1712                     pos = trackedEndPos - d->size();
1713                     if (trackedSize > d->size())
1714                         pos = trackedPos;
1715                 } else {
1716                     pos = toItemEndPos - d->size();
1717                     if (d->currentItem->size() > d->size())
1718                         pos = d->currentItem->position();
1719                 }
1720             }
1721             if (trackedPos < pos && toItemPos < pos)
1722                 pos = qMax(trackedPos, toItemPos);
1723         }
1724         if (viewPos != pos) {
1725             cancelFlick();
1726             d->calcVelocity = true;
1727             d->setPosition(pos);
1728             d->calcVelocity = false;
1729         }
1730     }
1731 }
1732
1733 void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1734 {
1735     Q_D(QQuickItemView);
1736     d->markExtentsDirty();
1737     if (isComponentComplete() && d->isValid()) {
1738         d->forceLayout = true;
1739         polish();
1740     }
1741     QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
1742 }
1743
1744
1745 qreal QQuickItemView::minYExtent() const
1746 {
1747     Q_D(const QQuickItemView);
1748     if (d->layoutOrientation() == Qt::Horizontal)
1749         return QQuickFlickable::minYExtent();
1750
1751     if (d->vData.minExtentDirty) {
1752         d->minExtent = d->vData.startMargin-d->startPosition();
1753         if (d->header)
1754             d->minExtent += d->headerSize();
1755         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
1756             d->minExtent += d->highlightRangeStart;
1757             if (d->visibleItem(0))
1758                 d->minExtent -= d->visibleItem(0)->sectionSize();
1759             d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd));
1760         }
1761         d->vData.minExtentDirty = false;
1762     }
1763
1764     return d->minExtent;
1765 }
1766
1767 qreal QQuickItemView::maxYExtent() const
1768 {
1769     Q_D(const QQuickItemView);
1770     if (d->layoutOrientation() == Qt::Horizontal)
1771         return height();
1772
1773     if (d->vData.maxExtentDirty) {
1774         if (!d->model || !d->model->count()) {
1775             d->maxExtent = d->header ? -d->headerSize() : 0;
1776             d->maxExtent += height();
1777         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
1778             d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
1779             if (d->highlightRangeEnd != d->highlightRangeStart)
1780                 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd));
1781         } else {
1782             d->maxExtent = -(d->endPosition() - height());
1783         }
1784
1785         if (d->footer)
1786             d->maxExtent -= d->footerSize();
1787         d->maxExtent -= d->vData.endMargin;
1788         qreal minY = minYExtent();
1789         if (d->maxExtent > minY)
1790             d->maxExtent = minY;
1791         d->vData.maxExtentDirty = false;
1792     }
1793     return d->maxExtent;
1794 }
1795
1796 qreal QQuickItemView::minXExtent() const
1797 {
1798     Q_D(const QQuickItemView);
1799     if (d->layoutOrientation() == Qt::Vertical)
1800         return QQuickFlickable::minXExtent();
1801
1802     if (d->hData.minExtentDirty) {
1803         d->minExtent = -d->startPosition();
1804         qreal highlightStart;
1805         qreal highlightEnd;
1806         qreal endPositionFirstItem = 0;
1807         if (d->isContentFlowReversed()) {
1808             d->minExtent += d->hData.endMargin;
1809             if (d->model && d->model->count())
1810                 endPositionFirstItem = d->positionAt(d->model->count()-1);
1811             else if (d->header)
1812                 d->minExtent += d->headerSize();
1813             highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size();
1814             highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size();
1815             if (d->footer)
1816                 d->minExtent += d->footerSize();
1817             qreal maxX = maxXExtent();
1818             if (d->minExtent < maxX)
1819                 d->minExtent = maxX;
1820         } else {
1821             d->minExtent += d->hData.startMargin;
1822             endPositionFirstItem = d->endPositionAt(0);
1823             highlightStart = d->highlightRangeStart;
1824             highlightEnd = d->highlightRangeEnd;
1825             if (d->header)
1826                 d->minExtent += d->headerSize();
1827         }
1828         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
1829             d->minExtent += highlightStart;
1830             d->minExtent = d->isContentFlowReversed()
1831                                 ? qMin(d->minExtent, endPositionFirstItem + highlightEnd)
1832                                 : qMax(d->minExtent, -(endPositionFirstItem - highlightEnd));
1833         }
1834         d->hData.minExtentDirty = false;
1835     }
1836
1837     return d->minExtent;
1838 }
1839
1840 qreal QQuickItemView::maxXExtent() const
1841 {
1842     Q_D(const QQuickItemView);
1843     if (d->layoutOrientation() == Qt::Vertical)
1844         return width();
1845
1846     if (d->hData.maxExtentDirty) {
1847         qreal highlightStart;
1848         qreal highlightEnd;
1849         qreal lastItemPosition = 0;
1850         d->maxExtent = 0;
1851         if (d->isContentFlowReversed()) {
1852             highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size();
1853             highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size();
1854             lastItemPosition = d->endPosition();
1855         } else {
1856             highlightStart = d->highlightRangeStart;
1857             highlightEnd = d->highlightRangeEnd;
1858             if (d->model && d->model->count())
1859                 lastItemPosition = d->positionAt(d->model->count()-1);
1860         }
1861         if (!d->model || !d->model->count()) {
1862             if (!d->isContentFlowReversed())
1863                 d->maxExtent = d->header ? -d->headerSize() : 0;
1864             d->maxExtent += width();
1865         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
1866             d->maxExtent = -(lastItemPosition - highlightStart);
1867             if (highlightEnd != highlightStart) {
1868                 d->maxExtent = d->isContentFlowReversed()
1869                         ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd))
1870                         : qMin(d->maxExtent, -(d->endPosition() - highlightEnd));
1871             }
1872         } else {
1873             d->maxExtent = -(d->endPosition() - width());
1874         }
1875         if (d->isContentFlowReversed()) {
1876             if (d->header)
1877                 d->maxExtent -= d->headerSize();
1878             d->maxExtent -= d->hData.startMargin;
1879         } else {
1880             if (d->footer)
1881                 d->maxExtent -= d->footerSize();
1882             d->maxExtent -= d->hData.endMargin;
1883             qreal minX = minXExtent();
1884             if (d->maxExtent > minX)
1885                 d->maxExtent = minX;
1886         }
1887         d->hData.maxExtentDirty = false;
1888     }
1889
1890     return d->maxExtent;
1891 }
1892
1893 void QQuickItemView::setContentX(qreal pos)
1894 {
1895     Q_D(QQuickItemView);
1896     // Positioning the view manually should override any current movement state
1897     d->moveReason = QQuickItemViewPrivate::Other;
1898     QQuickFlickable::setContentX(pos);
1899 }
1900
1901 void QQuickItemView::setContentY(qreal pos)
1902 {
1903     Q_D(QQuickItemView);
1904     // Positioning the view manually should override any current movement state
1905     d->moveReason = QQuickItemViewPrivate::Other;
1906     QQuickFlickable::setContentY(pos);
1907 }
1908
1909 qreal QQuickItemView::xOrigin() const
1910 {
1911     Q_D(const QQuickItemView);
1912     if (d->isContentFlowReversed())
1913         return -maxXExtent() + d->size() - d->hData.startMargin;
1914     else
1915         return -minXExtent() + d->hData.startMargin;
1916 }
1917
1918 void QQuickItemView::updatePolish()
1919 {
1920     Q_D(QQuickItemView);
1921     QQuickFlickable::updatePolish();
1922     d->layout();
1923 }
1924
1925 void QQuickItemView::componentComplete()
1926 {
1927     Q_D(QQuickItemView);
1928     if (d->model && d->ownModel)
1929         static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
1930
1931     QQuickFlickable::componentComplete();
1932
1933     updateSections();
1934     d->updateHeader();
1935     d->updateFooter();
1936     d->updateViewport();
1937     d->setPosition(d->contentStartOffset());
1938     d->usePopulateTransition = true;
1939
1940     if (d->isValid()) {
1941         d->refill();
1942         d->moveReason = QQuickItemViewPrivate::SetIndex;
1943         if (d->currentIndex < 0 && !d->currentIndexCleared)
1944             d->updateCurrent(0);
1945         else
1946             d->updateCurrent(d->currentIndex);
1947         if (d->highlight && d->currentItem) {
1948             if (d->autoHighlight)
1949                 d->resetHighlightPosition();
1950             d->updateTrackedItem();
1951         }
1952         d->moveReason = QQuickItemViewPrivate::Other;
1953         d->fixupPosition();
1954     }
1955     if (d->model && d->model->count())
1956         emit countChanged();
1957 }
1958
1959
1960
1961 QQuickItemViewPrivate::QQuickItemViewPrivate()
1962     : itemCount(0)
1963     , buffer(0), bufferMode(BufferBefore | BufferAfter)
1964     , layoutDirection(Qt::LeftToRight)
1965     , moveReason(Other)
1966     , visibleIndex(0)
1967     , currentIndex(-1), currentItem(0)
1968     , trackedItem(0), requestedIndex(-1), requestedItem(0)
1969     , highlightComponent(0), highlight(0)
1970     , highlightRange(QQuickItemView::NoHighlightRange)
1971     , highlightRangeStart(0), highlightRangeEnd(0)
1972     , highlightMoveDuration(150)
1973     , headerComponent(0), header(0), footerComponent(0), footer(0)
1974     , populateTransition(0)
1975     , addTransition(0), addDisplacedTransition(0)
1976     , moveTransition(0), moveDisplacedTransition(0)
1977     , removeTransition(0), removeDisplacedTransition(0)
1978     , minExtent(0), maxExtent(0)
1979     , ownModel(false), wrap(false)
1980     , disableLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
1981     , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1982     , fillCacheBuffer(false), inRequest(false), requestedAsync(false)
1983     , usePopulateTransition(false), runDelayedRemoveTransition(false)
1984 {
1985 }
1986
1987 bool QQuickItemViewPrivate::isValid() const
1988 {
1989     return model && model->count() && model->isValid();
1990 }
1991
1992 qreal QQuickItemViewPrivate::position() const
1993 {
1994     Q_Q(const QQuickItemView);
1995     return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1996 }
1997
1998 qreal QQuickItemViewPrivate::size() const
1999 {
2000     Q_Q(const QQuickItemView);
2001     return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
2002 }
2003
2004 qreal QQuickItemViewPrivate::startPosition() const
2005 {
2006     return isContentFlowReversed() ? -lastPosition() : originPosition();
2007 }
2008
2009 qreal QQuickItemViewPrivate::endPosition() const
2010 {
2011     return isContentFlowReversed() ? -originPosition() : lastPosition();
2012 }
2013
2014 qreal QQuickItemViewPrivate::contentStartOffset() const
2015 {
2016     qreal pos = -headerSize();
2017     if (layoutOrientation() == Qt::Vertical)
2018         pos -= vData.startMargin;
2019     else if (isContentFlowReversed())
2020         pos -= hData.endMargin;
2021     else
2022         pos -= hData.startMargin;
2023
2024     return pos;
2025 }
2026
2027 int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
2028 {
2029     if (visibleItems.count()) {
2030         int i = visibleItems.count() - 1;
2031         while (i > 0 && visibleItems.at(i)->index == -1)
2032             --i;
2033         if (visibleItems.at(i)->index != -1)
2034             return visibleItems.at(i)->index;
2035     }
2036     return defaultValue;
2037 }
2038
2039 FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
2040     if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
2041         for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
2042             FxViewItem *item = visibleItems.at(i);
2043             if (item->index == modelIndex)
2044                 return item;
2045         }
2046     }
2047     return 0;
2048 }
2049
2050 // should rename to firstItemInView() to avoid confusion with other "*visible*" methods
2051 // that don't look at the view position and size
2052 FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
2053     const qreal pos = isContentFlowReversed() ? -position()-size() : position();
2054     for (int i = 0; i < visibleItems.count(); ++i) {
2055         FxViewItem *item = visibleItems.at(i);
2056         if (item->index != -1 && item->endPosition() > pos)
2057             return item;
2058     }
2059     return visibleItems.count() ? visibleItems.first() : 0;
2060 }
2061
2062 int QQuickItemViewPrivate::findLastIndexInView() const
2063 {
2064     const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2065     for (int i=visibleItems.count() - 1; i>=0; i--) {
2066         if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1)
2067             return visibleItems.at(i)->index;
2068     }
2069     return -1;
2070 }
2071
2072 // Map a model index to visibleItems list index.
2073 // These may differ if removed items are still present in the visible list,
2074 // e.g. doing a removal animation
2075 int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
2076 {
2077     if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
2078         return -1;
2079     for (int i = 0; i < visibleItems.count(); ++i) {
2080         FxViewItem *item = visibleItems.at(i);
2081         if (item->index == modelIndex)
2082             return i;
2083         if (item->index > modelIndex)
2084             return -1;
2085     }
2086     return -1; // Not in visibleList
2087 }
2088
2089 void QQuickItemViewPrivate::init()
2090 {
2091     Q_Q(QQuickItemView);
2092     QQuickItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
2093     q->setFlag(QQuickItem::ItemIsFocusScope);
2094     QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
2095     q->setFlickableDirection(QQuickFlickable::VerticalFlick);
2096 }
2097
2098 void QQuickItemViewPrivate::updateCurrent(int modelIndex)
2099 {
2100     Q_Q(QQuickItemView);
2101     applyPendingChanges();
2102     if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
2103         if (currentItem) {
2104             currentItem->attached->setIsCurrentItem(false);
2105             releaseItem(currentItem);
2106             currentItem = 0;
2107             currentIndex = modelIndex;
2108             emit q->currentIndexChanged();
2109             emit q->currentItemChanged();
2110             updateHighlight();
2111         } else if (currentIndex != modelIndex) {
2112             currentIndex = modelIndex;
2113             emit q->currentIndexChanged();
2114         }
2115         return;
2116     }
2117
2118     if (currentItem && currentIndex == modelIndex) {
2119         updateHighlight();
2120         return;
2121     }
2122
2123     FxViewItem *oldCurrentItem = currentItem;
2124     int oldCurrentIndex = currentIndex;
2125     currentIndex = modelIndex;
2126     currentItem = createItem(modelIndex, false);
2127     if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
2128         oldCurrentItem->attached->setIsCurrentItem(false);
2129     if (currentItem) {
2130         currentItem->item->setFocus(true);
2131         currentItem->attached->setIsCurrentItem(true);
2132         initializeCurrentItem();
2133     }
2134
2135     updateHighlight();
2136     if (oldCurrentIndex != currentIndex)
2137         emit q->currentIndexChanged();
2138     if (oldCurrentItem != currentItem)
2139         emit q->currentItemChanged();
2140     releaseItem(oldCurrentItem);
2141 }
2142
2143 void QQuickItemViewPrivate::clear()
2144 {
2145     currentChanges.reset();
2146     timeline.clear();
2147
2148     for (int i = 0; i < visibleItems.count(); ++i)
2149         releaseItem(visibleItems.at(i));
2150     visibleItems.clear();
2151     visibleIndex = 0;
2152
2153     for (int i = 0; i < releasePendingTransition.count(); ++i) {
2154         releasePendingTransition.at(i)->releaseAfterTransition = false;
2155         releaseItem(releasePendingTransition.at(i));
2156     }
2157     releasePendingTransition.clear();
2158
2159     releaseItem(currentItem);
2160     currentItem = 0;
2161     createHighlight();
2162     trackedItem = 0;
2163
2164     markExtentsDirty();
2165     itemCount = 0;
2166 }
2167
2168
2169 void QQuickItemViewPrivate::mirrorChange()
2170 {
2171     Q_Q(QQuickItemView);
2172     regenerate();
2173     emit q->effectiveLayoutDirectionChanged();
2174 }
2175
2176 void QQuickItemViewPrivate::refill()
2177 {
2178     qreal s = qMax(size(), qreal(0.));
2179     if (isContentFlowReversed())
2180         refill(-position()-s, -position());
2181     else
2182         refill(position(), position()+s);
2183 }
2184
2185 void QQuickItemViewPrivate::refill(qreal from, qreal to)
2186 {
2187     Q_Q(QQuickItemView);
2188     if (!isValid() || !q->isComponentComplete())
2189         return;
2190
2191     currentChanges.reset();
2192
2193     int prevCount = itemCount;
2194     itemCount = model->count();
2195     qreal bufferFrom = from - buffer;
2196     qreal bufferTo = to + buffer;
2197     qreal fillFrom = from;
2198     qreal fillTo = to;
2199
2200     bool added = addVisibleItems(fillFrom, fillTo, false);
2201     bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
2202
2203     if (buffer && bufferMode != NoBuffer) {
2204         if (bufferMode & BufferAfter)
2205             fillTo = bufferTo;
2206         if (bufferMode & BufferBefore)
2207             fillFrom = bufferFrom;
2208         added |= addVisibleItems(fillFrom, fillTo, true);
2209     }
2210
2211     if (added || removed) {
2212         markExtentsDirty();
2213         updateBeginningEnd();
2214         visibleItemsChanged();
2215         updateHeader();
2216         updateFooter();
2217         updateViewport();
2218     }
2219
2220     if (prevCount != itemCount)
2221         emit q->countChanged();
2222 }
2223
2224 void QQuickItemViewPrivate::regenerate()
2225 {
2226     Q_Q(QQuickItemView);
2227     if (q->isComponentComplete()) {
2228         currentChanges.reset();
2229         delete header;
2230         header = 0;
2231         delete footer;
2232         footer = 0;
2233         updateHeader();
2234         updateFooter();
2235         clear();
2236         updateViewport();
2237         setPosition(contentStartOffset());
2238         refill();
2239         updateCurrent(currentIndex);
2240     }
2241 }
2242
2243 void QQuickItemViewPrivate::updateViewport()
2244 {
2245     Q_Q(QQuickItemView);
2246     if (isValid()) {
2247         if (layoutOrientation() == Qt::Vertical)
2248             q->setContentHeight(endPosition() - startPosition());
2249         else
2250             q->setContentWidth(endPosition() - startPosition());
2251     }
2252 }
2253
2254 void QQuickItemViewPrivate::layout()
2255 {
2256     Q_Q(QQuickItemView);
2257     if (disableLayout)
2258         return;
2259
2260     if (!isValid() && !visibleItems.count()) {
2261         clear();
2262         setPosition(contentStartOffset());
2263         usePopulateTransition = false;
2264         return;
2265     }
2266
2267     if (runDelayedRemoveTransition && canTransition(FxViewItemTransitionManager::RemoveTransition, false)) {
2268         // assume that any items moving now are moving due to the remove - if they schedule
2269         // a different transition, that will override this one anyway
2270         for (int i=0; i<visibleItems.count(); i++)
2271             transitionNextReposition(visibleItems[i], FxViewItemTransitionManager::RemoveTransition, false);
2272     }
2273
2274     ChangeResult insertionPosChanges;
2275     ChangeResult removalPosChanges;
2276     if (!applyModelChanges(&insertionPosChanges, &removalPosChanges) && !forceLayout) {
2277         if (fillCacheBuffer) {
2278             fillCacheBuffer = false;
2279             refill();
2280         }
2281         return;
2282     }
2283     forceLayout = false;
2284
2285     if (canTransition(FxViewItemTransitionManager::PopulateTransition, true)) {
2286         for (int i=0; i<visibleItems.count(); i++)
2287             transitionNextReposition(visibleItems.at(i), FxViewItemTransitionManager::PopulateTransition, true);
2288     }
2289     layoutVisibleItems();
2290
2291     int lastIndexInView = findLastIndexInView();
2292     refill();
2293     markExtentsDirty();
2294     updateHighlight();
2295
2296     if (!q->isMoving() && !q->isFlicking()) {
2297         fixupPosition();
2298         refill();
2299     }
2300
2301     updateHeader();
2302     updateFooter();
2303     updateViewport();
2304     updateUnrequestedPositions();
2305
2306     if (hasItemTransitions()) {
2307         // items added in the last refill() may need to be transitioned in - e.g. a remove
2308         // causes items to slide up into view
2309         if (canTransition(FxViewItemTransitionManager::MoveTransition, false)
2310                 || canTransition(FxViewItemTransitionManager::RemoveTransition, false)) {
2311             translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges);
2312         }
2313
2314         prepareVisibleItemTransitions();
2315
2316         QRectF viewBounds(0, position(), q->width(), q->height());
2317         for (QList<FxViewItem*>::Iterator it = releasePendingTransition.begin();
2318              it != releasePendingTransition.end(); ) {
2319             FxViewItem *item = *it;
2320             if ( (item->transition && item->transition->isActive())
2321                  || prepareNonVisibleItemTransition(item, viewBounds)) {
2322                 ++it;
2323             } else {
2324                 releaseItem(item);
2325                 it = releasePendingTransition.erase(it);
2326             }
2327         }
2328
2329         for (int i=0; i<visibleItems.count(); i++)
2330             visibleItems[i]->startTransition();
2331         for (int i=0; i<releasePendingTransition.count(); i++)
2332             releasePendingTransition[i]->startTransition();
2333     }
2334     usePopulateTransition = false;
2335     runDelayedRemoveTransition = false;
2336 }
2337
2338 bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult)
2339 {
2340     Q_Q(QQuickItemView);
2341     if (!q->isComponentComplete() || (!currentChanges.hasPendingChanges() && !runDelayedRemoveTransition) || disableLayout)
2342         return false;
2343
2344     disableLayout = true;
2345
2346     updateUnrequestedIndexes();
2347     moveReason = QQuickItemViewPrivate::Other;
2348
2349     FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? *visibleItems.constBegin() : 0;
2350     int prevItemCount = itemCount;
2351     int prevVisibleItemsCount = visibleItems.count();
2352     bool visibleAffected = false;
2353     bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
2354             || !currentChanges.pendingChanges.inserts().isEmpty();
2355
2356     FxViewItem *prevFirstVisible = firstVisibleItem();
2357     QQmlNullableValue<qreal> prevViewPos;
2358     int prevFirstVisibleIndex = -1;
2359     if (prevFirstVisible) {
2360         prevViewPos = prevFirstVisible->position();
2361         prevFirstVisibleIndex = prevFirstVisible->index;
2362     }
2363     qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0;
2364
2365     totalInsertionResult->visiblePos = prevViewPos;
2366     totalRemovalResult->visiblePos = prevViewPos;
2367
2368     const QVector<QQuickChangeSet::Remove> &removals = currentChanges.pendingChanges.removes();
2369     const QVector<QQuickChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
2370     ChangeResult insertionResult(prevViewPos);
2371     ChangeResult removalResult(prevViewPos);
2372
2373     int removedCount = 0;
2374     for (int i=0; i<removals.count(); i++) {
2375         itemCount -= removals[i].count;
2376         if (applyRemovalChange(removals[i], &removalResult, &removedCount))
2377             visibleAffected = true;
2378         if (!visibleAffected && needsRefillForAddedOrRemovedIndex(removals[i].index))
2379             visibleAffected = true;
2380         if (prevFirstVisibleIndex >= 0 && removals[i].index < prevFirstVisibleIndex) {
2381             if (removals[i].index + removals[i].count < prevFirstVisibleIndex)
2382                 removalResult.countChangeBeforeVisible += removals[i].count;
2383             else
2384                 removalResult.countChangeBeforeVisible += (prevFirstVisibleIndex - removals[i].index);
2385         }
2386     }
2387     if (runDelayedRemoveTransition) {
2388         QQuickChangeSet::Remove removal;
2389         for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
2390             FxViewItem *item = *it;
2391             if (item->index == -1 && !item->attached->delayRemove()) {
2392                 removeItem(item, removal, &removalResult);
2393                 removedCount++;
2394                 it = visibleItems.erase(it);
2395             } else {
2396                ++it;
2397             }
2398         }
2399     }
2400     *totalRemovalResult += removalResult;
2401     if (!removals.isEmpty()) {
2402         updateVisibleIndex();
2403
2404         // set positions correctly for the next insertion
2405         if (!insertions.isEmpty()) {
2406             repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
2407             layoutVisibleItems(removals.first().index);
2408         }
2409     }
2410
2411     QList<FxViewItem *> newItems;
2412     QList<MovedItem> movingIntoView;
2413
2414     for (int i=0; i<insertions.count(); i++) {
2415         bool wasEmpty = visibleItems.isEmpty();
2416         if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView))
2417             visibleAffected = true;
2418         if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
2419             visibleAffected = true;
2420         if (wasEmpty && !visibleItems.isEmpty())
2421             resetFirstItemPosition();
2422         *totalInsertionResult += insertionResult;
2423
2424         // set positions correctly for the next insertion
2425         if (i < insertions.count() - 1) {
2426             repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
2427             layoutVisibleItems(insertions[i].index);
2428         }
2429         itemCount += insertions[i].count;
2430     }
2431     for (int i=0; i<newItems.count(); i++)
2432         newItems.at(i)->attached->emitAdd();
2433
2434     // for each item that was moved directly into the view as a result of a move(),
2435     // find the index it was moved from in order to set its initial position, so that we
2436     // can transition it from this "original" position to its new position in the view
2437     if (canTransition(FxViewItemTransitionManager::MoveTransition, true)) {
2438         for (int i=0; i<movingIntoView.count(); i++) {
2439             int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals);
2440             if (fromIndex >= 0) {
2441                 if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex)
2442                     repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
2443                 else
2444                     repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
2445                 transitionNextReposition(movingIntoView[i].item, FxViewItemTransitionManager::MoveTransition, true);
2446             }
2447         }
2448     }
2449
2450     // reposition visibleItems.first() correctly so that the content y doesn't jump
2451     if (removedCount != prevVisibleItemsCount)
2452         repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
2453
2454     // Whatever removed/moved items remain are no longer visible items.
2455     prepareRemoveTransitions(&currentChanges.removedItems);
2456     for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin();
2457          it != currentChanges.removedItems.end(); ++it) {
2458         releaseItem(it.value());
2459     }
2460     currentChanges.removedItems.clear();
2461
2462     if (currentChanges.currentChanged) {
2463         if (currentChanges.currentRemoved && currentItem) {
2464             currentItem->attached->setIsCurrentItem(false);
2465             releaseItem(currentItem);
2466             currentItem = 0;
2467         }
2468         if (!currentIndexCleared)
2469             updateCurrent(currentChanges.newCurrentIndex);
2470     }
2471
2472     if (!visibleAffected)
2473         visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
2474     currentChanges.reset();
2475
2476     updateSections();
2477     if (prevItemCount != itemCount)
2478         emit q->countChanged();
2479     if (!visibleAffected && viewportChanged)
2480         updateViewport();
2481
2482     disableLayout = false;
2483     return visibleAffected;
2484 }
2485
2486 bool QQuickItemViewPrivate::applyRemovalChange(const QQuickChangeSet::Remove &removal, ChangeResult *removeResult, int *removedCount)
2487 {
2488     Q_Q(QQuickItemView);
2489     bool visibleAffected = false;
2490
2491     if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) {
2492         if (removal.index > visibleItems.last()->index)
2493             removeResult->countChangeAfterVisibleItems += removal.count;
2494         else
2495             removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index);
2496     }
2497
2498     QList<FxViewItem*>::Iterator it = visibleItems.begin();
2499     while (it != visibleItems.end()) {
2500         FxViewItem *item = *it;
2501         if (item->index == -1 || item->index < removal.index) {
2502             // already removed, or before removed items
2503             if (!visibleAffected && item->index < removal.index)
2504                 visibleAffected = true;
2505             ++it;
2506         } else if (item->index >= removal.index + removal.count) {
2507             // after removed items
2508             item->index -= removal.count;
2509             if (removal.isMove())
2510                 transitionNextReposition(item, FxViewItemTransitionManager::MoveTransition, false);
2511             else
2512                 transitionNextReposition(item, FxViewItemTransitionManager::RemoveTransition, false);
2513             ++it;
2514         } else {
2515             // removed item
2516             visibleAffected = true;
2517             if (!removal.isMove())
2518                 item->attached->emitRemove();
2519
2520             if (item->attached->delayRemove() && !removal.isMove()) {
2521                 item->index = -1;
2522                 QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
2523                 ++it;
2524             } else {
2525                 removeItem(item, removal, removeResult);
2526                 if (!removal.isMove())
2527                     (*removedCount)++;
2528                 it = visibleItems.erase(it);
2529             }
2530         }
2531     }
2532
2533     return visibleAffected;
2534 }
2535
2536 void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQuickChangeSet::Remove &removal, ChangeResult *removeResult)
2537 {
2538     if (removeResult->visiblePos.isValid()) {
2539         if (item->position() < removeResult->visiblePos)
2540             removeResult->sizeChangesBeforeVisiblePos += item->size();
2541         else
2542             removeResult->sizeChangesAfterVisiblePos += item->size();
2543     }
2544     if (removal.isMove()) {
2545         currentChanges.removedItems.insert(removal.moveKey(item->index), item);
2546         transitionNextReposition(item, FxViewItemTransitionManager::MoveTransition, true);
2547     } else {
2548         // track item so it is released later
2549         currentChanges.removedItems.insertMulti(QQuickChangeSet::MoveKey(), item);
2550     }
2551     if (!removeResult->changedFirstItem && item == *visibleItems.constBegin())
2552         removeResult->changedFirstItem = true;
2553 }
2554
2555 void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
2556                                                    qreal prevVisibleItemsFirstPos,
2557                                                    FxViewItem *prevFirstVisible,
2558                                                    ChangeResult *insertionResult,
2559                                                    ChangeResult *removalResult)
2560 {
2561     const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
2562
2563     // reposition visibleItems.first() correctly so that the content y doesn't jump
2564     if (visibleItems.count()) {
2565         if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
2566             resetFirstItemPosition(prevVisibleItemsFirstPos);
2567
2568         if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
2569                 && prevFirstVisible != *visibleItems.constBegin()) {
2570             // the previous visibleItems.first() was also the first visible item, and it has been
2571             // moved/removed, so move the new visibleItems.first() to the pos of the previous one
2572             if (!insertionResult->changedFirstItem)
2573                 resetFirstItemPosition(prevVisibleItemsFirstPos);
2574
2575         } else if (prevViewPos.isValid()) {
2576             qreal moveForwardsBy = 0;
2577             qreal moveBackwardsBy = 0;
2578
2579             // shift visibleItems.first() relative to the number of added/removed items
2580             if (visibleItems.first()->position() > prevViewPos) {
2581                 moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
2582                 moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
2583             } else if (visibleItems.first()->position() < prevViewPos) {
2584                 moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
2585                 moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
2586             }
2587             adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible);
2588         }
2589         insertionResult->reset();
2590         removalResult->reset();
2591     }
2592 }
2593
2594 void QQuickItemViewPrivate::prepareVisibleItemTransitions()
2595 {
2596     Q_Q(QQuickItemView);
2597     if (!hasItemTransitions())
2598         return;
2599
2600     addTransitionIndexes.clear();
2601     addTransitionTargets.clear();
2602     moveTransitionIndexes.clear();
2603     moveTransitionTargets.clear();
2604
2605     QRectF viewBounds(0, position(), q->width(), q->height());
2606     for (int i=0; i<visibleItems.count(); i++) {
2607         // must call for every visible item to init or discard transitions
2608         if (!visibleItems[i]->prepareTransition(viewBounds))
2609             continue;
2610         if (visibleItems[i]->isTransitionTarget) {
2611             switch (visibleItems[i]->nextTransitionType) {
2612             case FxViewItemTransitionManager::NoTransition:
2613                 break;
2614             case FxViewItemTransitionManager::PopulateTransition:
2615             case FxViewItemTransitionManager::AddTransition:
2616                 addTransitionIndexes.append(visibleItems[i]->index);
2617                 addTransitionTargets.append(visibleItems[i]->item);
2618                 break;
2619             case FxViewItemTransitionManager::MoveTransition:
2620                 moveTransitionIndexes.append(visibleItems[i]->index);
2621                 moveTransitionTargets.append(visibleItems[i]->item);
2622                 break;
2623             case FxViewItemTransitionManager::RemoveTransition:
2624                 // removed targets won't be in visibleItems, handle these
2625                 // in prepareNonVisibleItemTransition()
2626                 break;
2627             }
2628         }
2629     }
2630 }
2631
2632 void QQuickItemViewPrivate::prepareRemoveTransitions(QHash<QQuickChangeSet::MoveKey, FxViewItem *> *removedItems)
2633 {
2634     removeTransitionIndexes.clear();
2635     removeTransitionTargets.clear();
2636
2637     if (canTransition(FxViewItemTransitionManager::RemoveTransition, true)) {
2638         for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = removedItems->begin();
2639              it != removedItems->end(); ) {
2640             bool isRemove = it.key().moveId < 0;
2641             if (isRemove) {
2642                 FxViewItem *item = *it;
2643                 item->releaseAfterTransition = true;
2644                 releasePendingTransition.append(item);
2645                 transitionNextReposition(item, FxViewItemTransitionManager::RemoveTransition, true);
2646                 it = removedItems->erase(it);
2647             } else {
2648                 ++it;
2649             }
2650         }
2651     }
2652 }
2653
2654 bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds)
2655 {
2656     // Called for items that have been removed from visibleItems and may now be
2657     // transitioned out of the view. This applies to items that are being directly
2658     // removed, or moved to outside of the view, as well as those that are
2659     // displaced to a position outside of the view due to an insert or move.
2660
2661     if (item->nextTransitionType == FxViewItemTransitionManager::MoveTransition)
2662         repositionItemAt(item, item->index, 0);
2663     if (!item->prepareTransition(viewBounds))
2664         return false;
2665
2666     if (item->isTransitionTarget) {
2667         if (item->nextTransitionType == FxViewItemTransitionManager::MoveTransition) {
2668             moveTransitionIndexes.append(item->index);
2669             moveTransitionTargets.append(item->item);
2670         } else if (item->nextTransitionType == FxViewItemTransitionManager::RemoveTransition) {
2671             removeTransitionIndexes.append(item->index);
2672             removeTransitionTargets.append(item->item);
2673         }
2674     }
2675
2676     item->releaseAfterTransition = true;
2677     return true;
2678 }
2679
2680 /*
2681   This may return 0 if the item is being created asynchronously.
2682   When the item becomes available, refill() will be called and the item
2683   will be returned on the next call to createItem().
2684 */
2685 FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous)
2686 {
2687     Q_Q(QQuickItemView);
2688
2689     if (requestedIndex == modelIndex && (asynchronous || requestedAsync == asynchronous))
2690         return 0;
2691
2692     if (requestedIndex != -1 && requestedIndex != modelIndex) {
2693         if (requestedItem && requestedItem->item)
2694             requestedItem->item->setParentItem(0);
2695         delete requestedItem;
2696         requestedItem = 0;
2697     }
2698
2699     for (int i=0; i<releasePendingTransition.count(); i++) {
2700         if (releasePendingTransition[i]->index == modelIndex
2701                 && !releasePendingTransition[i]->isPendingRemoval()) {
2702             releasePendingTransition[i]->releaseAfterTransition = false;
2703             return releasePendingTransition.takeAt(i);
2704         }
2705     }
2706
2707     requestedIndex = modelIndex;
2708     requestedAsync = asynchronous;
2709     inRequest = true;
2710
2711     if (QQuickItem *item = model->item(modelIndex, asynchronous)) {
2712         item->setParentItem(q->contentItem());
2713         QQml_setParent_noEvent(item, q->contentItem());
2714         requestedIndex = -1;
2715         FxViewItem *viewItem = requestedItem;
2716         if (!viewItem)
2717             viewItem = newViewItem(modelIndex, item); // already in cache, so viewItem not initialized in initItem()
2718         if (viewItem) {
2719             viewItem->index = modelIndex;
2720             // do other set up for the new item that should not happen
2721             // until after bindings are evaluated
2722             initializeViewItem(viewItem);
2723             unrequestedItems.remove(item);
2724         }
2725         requestedItem = 0;
2726         inRequest = false;
2727         return viewItem;
2728     }
2729
2730     inRequest = false;
2731     return 0;
2732 }
2733
2734 void QQuickItemView::createdItem(int index, QQuickItem *item)
2735 {
2736     Q_D(QQuickItemView);
2737     if (d->requestedIndex != index) {
2738         item->setParentItem(contentItem());
2739         d->unrequestedItems.insert(item, index);
2740         item->setVisible(false);
2741         d->repositionPackageItemAt(item, index);
2742     } else {
2743         d->requestedIndex = -1;
2744         if (!d->inRequest) {
2745             if (index == d->currentIndex)
2746                 d->updateCurrent(index);
2747             d->refill();
2748         }
2749     }
2750 }
2751
2752 void QQuickItemView::initItem(int index, QQuickItem *item)
2753 {
2754     Q_D(QQuickItemView);
2755     item->setZ(1);
2756     if (d->requestedIndex == index) {
2757         if (d->requestedAsync)
2758             item->setVisible(false);
2759         item->setParentItem(contentItem());
2760         QQml_setParent_noEvent(item, contentItem());
2761         d->requestedItem = d->newViewItem(index, item);
2762     }
2763 }
2764
2765 void QQuickItemView::destroyingItem(QQuickItem *item)
2766 {
2767     Q_D(QQuickItemView);
2768     d->unrequestedItems.remove(item);
2769 }
2770
2771 void QQuickItemViewPrivate::releaseItem(FxViewItem *item)
2772 {
2773     Q_Q(QQuickItemView);
2774     if (!item || !model)
2775         return;
2776     if (trackedItem == item)
2777         trackedItem = 0;
2778     QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
2779     itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
2780     if (model->release(item->item) == 0) {
2781         // item was not destroyed, and we no longer reference it.
2782         item->item->setVisible(false);
2783         unrequestedItems.insert(item->item, model->indexOf(item->item, q));
2784     }
2785     delete item;
2786 }
2787
2788 QQuickItem *QQuickItemViewPrivate::createHighlightItem()
2789 {
2790     return createComponentItem(highlightComponent, true, true);
2791 }
2792
2793 QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, bool receiveItemGeometryChanges, bool createDefault)
2794 {
2795     Q_Q(QQuickItemView);
2796
2797     QQuickItem *item = 0;
2798     if (component) {
2799         QQmlContext *creationContext = component->creationContext();
2800         QQmlContext *context = new QQmlContext(
2801                 creationContext ? creationContext : qmlContext(q));
2802         QObject *nobj = component->create(context);
2803         if (nobj) {
2804             QQml_setParent_noEvent(context, nobj);
2805             item = qobject_cast<QQuickItem *>(nobj);
2806             if (!item)
2807                 delete nobj;
2808         } else {
2809             delete context;
2810         }
2811     } else if (createDefault) {
2812         item = new QQuickItem;
2813     }
2814     if (item) {
2815         QQml_setParent_noEvent(item, q->contentItem());
2816         item->setParentItem(q->contentItem());
2817         if (receiveItemGeometryChanges) {
2818             QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
2819             itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
2820         }
2821     }
2822     return item;
2823 }
2824
2825 void QQuickItemViewPrivate::updateTrackedItem()
2826 {
2827     Q_Q(QQuickItemView);
2828     FxViewItem *item = currentItem;
2829     if (highlight)
2830         item = highlight;
2831     trackedItem = item;
2832
2833     if (trackedItem)
2834         q->trackedPositionChanged();
2835 }
2836
2837 void QQuickItemViewPrivate::updateUnrequestedIndexes()
2838 {
2839     Q_Q(QQuickItemView);
2840     for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2841         *it = model->indexOf(it.key(), q);
2842 }
2843
2844 void QQuickItemViewPrivate::updateUnrequestedPositions()
2845 {
2846     for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2847         repositionPackageItemAt(it.key(), it.value());
2848 }
2849
2850 void QQuickItemViewPrivate::updateVisibleIndex()
2851 {
2852     visibleIndex = 0;
2853     for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) {
2854         if ((*it)->index != -1) {
2855             visibleIndex = (*it)->index;
2856             break;
2857         }
2858     }
2859 }
2860
2861 QT_END_NAMESPACE