Fix various QtQuick.* submodule docs
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickitemanimation.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 "qquickitemanimation_p.h"
43 #include "qquickitemanimation_p_p.h"
44 #include "qquickstateoperations_p.h"
45
46 #include <private/qqmlproperty_p.h>
47 #include <private/qquickpath_p.h>
48
49 #include <QtQml/qqmlinfo.h>
50 #include <QtCore/qmath.h>
51 #include "private/qsequentialanimationgroupjob_p.h"
52 #include "private/qparallelanimationgroupjob_p.h"
53 #include <QtGui/qtransform.h>
54
55 QT_BEGIN_NAMESPACE
56
57 /*!
58     \qmlclass ParentAnimation QQuickParentAnimation
59     \inqmlmodule QtQuick 2
60     \ingroup qtquick-animation-properties
61     \since QtQuick 2.0
62     \inherits Animation
63     \brief Animates changes in parent values
64
65     ParentAnimation is used to animate a parent change for an \l Item.
66
67     For example, the following ParentChange changes \c blueRect to become
68     a child of \c redRect when it is clicked. The inclusion of the
69     ParentAnimation, which defines a NumberAnimation to be applied during
70     the transition, ensures the item animates smoothly as it moves to
71     its new parent:
72
73     \snippet qml/parentanimation.qml 0
74
75     A ParentAnimation can contain any number of animations. These animations will
76     be run in parallel; to run them sequentially, define them within a
77     SequentialAnimation.
78
79     In some cases, such as when reparenting between items with clipping enabled, it is useful
80     to animate the parent change via another item that does not have clipping
81     enabled. Such an item can be set using the \l via property.
82
83     For convenience, when a ParentAnimation is used in a \l Transition, it will
84     animate any ParentChange that has occurred during the state change.
85     This can be overridden by setting a specific target item using the
86     \l target property.
87
88     Like any other animation element, a ParentAnimation can be applied in a
89     number of ways, including transitions, behaviors and property value
90     sources. The \l {QML Animation and Transitions} documentation shows a
91     variety of methods for creating animations.
92
93     \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}
94 */
95 QQuickParentAnimation::QQuickParentAnimation(QObject *parent)
96     : QQuickAnimationGroup(*(new QQuickParentAnimationPrivate), parent)
97 {
98 }
99
100 QQuickParentAnimation::~QQuickParentAnimation()
101 {
102 }
103
104 /*!
105     \qmlproperty Item QtQuick2::ParentAnimation::target
106     The item to reparent.
107
108     When used in a transition, if no target is specified, all
109     ParentChange occurrences are animated by the ParentAnimation.
110 */
111 QQuickItem *QQuickParentAnimation::target() const
112 {
113     Q_D(const QQuickParentAnimation);
114     return d->target;
115 }
116
117 void QQuickParentAnimation::setTargetObject(QQuickItem *target)
118 {
119     Q_D(QQuickParentAnimation);
120     if (target == d->target)
121         return;
122
123     d->target = target;
124     emit targetChanged();
125 }
126
127 /*!
128     \qmlproperty Item QtQuick2::ParentAnimation::newParent
129     The new parent to animate to.
130
131     If the ParentAnimation is defined within a \l Transition or \l Behavior,
132     this value defaults to the value defined in the end state of the
133     \l Transition, or the value of the property change that triggered the
134     \l Behavior.
135 */
136 QQuickItem *QQuickParentAnimation::newParent() const
137 {
138     Q_D(const QQuickParentAnimation);
139     return d->newParent;
140 }
141
142 void QQuickParentAnimation::setNewParent(QQuickItem *newParent)
143 {
144     Q_D(QQuickParentAnimation);
145     if (newParent == d->newParent)
146         return;
147
148     d->newParent = newParent;
149     emit newParentChanged();
150 }
151
152 /*!
153     \qmlproperty Item QtQuick2::ParentAnimation::via
154     The item to reparent via. This provides a way to do an unclipped animation
155     when both the old parent and new parent are clipped.
156
157     \qml
158     ParentAnimation {
159         target: myItem
160         via: topLevelItem
161         // ...
162     }
163     \endqml
164 */
165 QQuickItem *QQuickParentAnimation::via() const
166 {
167     Q_D(const QQuickParentAnimation);
168     return d->via;
169 }
170
171 void QQuickParentAnimation::setVia(QQuickItem *via)
172 {
173     Q_D(QQuickParentAnimation);
174     if (via == d->via)
175         return;
176
177     d->via = via;
178     emit viaChanged();
179 }
180
181 //### mirrors same-named function in QQuickItem
182 QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const
183 {
184     switch (origin) {
185     default:
186     case QQuickItem::TopLeft:
187         return QPointF(0, 0);
188     case QQuickItem::Top:
189         return QPointF(width / 2., 0);
190     case QQuickItem::TopRight:
191         return QPointF(width, 0);
192     case QQuickItem::Left:
193         return QPointF(0, height / 2.);
194     case QQuickItem::Center:
195         return QPointF(width / 2., height / 2.);
196     case QQuickItem::Right:
197         return QPointF(width, height / 2.);
198     case QQuickItem::BottomLeft:
199         return QPointF(0, height);
200     case QQuickItem::Bottom:
201         return QPointF(width / 2., height);
202     case QQuickItem::BottomRight:
203         return QPointF(width, height);
204     }
205 }
206
207 QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &actions,
208                         QQmlProperties &modified,
209                         TransitionDirection direction,
210                         QObject *defaultTarget)
211 {
212     Q_D(QQuickParentAnimation);
213
214     struct QQuickParentAnimationData : public QAbstractAnimationAction
215     {
216         QQuickParentAnimationData() : reverse(false) {}
217         ~QQuickParentAnimationData() { qDeleteAll(pc); }
218
219         QQuickStateActions actions;
220         //### reverse should probably apply on a per-action basis
221         bool reverse;
222         QList<QQuickParentChange *> pc;
223         virtual void doAction()
224         {
225             for (int ii = 0; ii < actions.count(); ++ii) {
226                 const QQuickAction &action = actions.at(ii);
227                 if (reverse)
228                     action.event->reverse();
229                 else
230                     action.event->execute();
231             }
232         }
233     };
234
235     QQuickParentAnimationData *data = new QQuickParentAnimationData;
236     QQuickParentAnimationData *viaData = new QQuickParentAnimationData;
237
238     bool hasExplicit = false;
239     if (d->target && d->newParent) {
240         data->reverse = false;
241         QQuickAction myAction;
242         QQuickParentChange *pc = new QQuickParentChange;
243         pc->setObject(d->target);
244         pc->setParent(d->newParent);
245         myAction.event = pc;
246         data->pc << pc;
247         data->actions << myAction;
248         hasExplicit = true;
249         if (d->via) {
250             viaData->reverse = false;
251             QQuickAction myVAction;
252             QQuickParentChange *vpc = new QQuickParentChange;
253             vpc->setObject(d->target);
254             vpc->setParent(d->via);
255             myVAction.event = vpc;
256             viaData->pc << vpc;
257             viaData->actions << myVAction;
258         }
259         //### once actions have concept of modified,
260         //    loop to match appropriate ParentChanges and mark as modified
261     }
262
263     if (!hasExplicit)
264     for (int i = 0; i < actions.size(); ++i) {
265         QQuickAction &action = actions[i];
266         if (action.event && action.event->type() == QQuickActionEvent::ParentChange
267             && (!d->target || static_cast<QQuickParentChange*>(action.event)->object() == d->target)) {
268
269             QQuickParentChange *pc = static_cast<QQuickParentChange*>(action.event);
270             QQuickAction myAction = action;
271             data->reverse = action.reverseEvent;
272
273             //### this logic differs from PropertyAnimation
274             //    (probably a result of modified vs. done)
275             if (d->newParent) {
276                 QQuickParentChange *epc = new QQuickParentChange;
277                 epc->setObject(static_cast<QQuickParentChange*>(action.event)->object());
278                 epc->setParent(d->newParent);
279                 myAction.event = epc;
280                 data->pc << epc;
281                 data->actions << myAction;
282                 pc = epc;
283             } else {
284                 action.actionDone = true;
285                 data->actions << myAction;
286             }
287
288             if (d->via) {
289                 viaData->reverse = false;
290                 QQuickAction myAction;
291                 QQuickParentChange *vpc = new QQuickParentChange;
292                 vpc->setObject(pc->object());
293                 vpc->setParent(d->via);
294                 myAction.event = vpc;
295                 viaData->pc << vpc;
296                 viaData->actions << myAction;
297                 QQuickAction dummyAction;
298                 QQuickAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
299                 QQuickAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
300                 QQuickAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
301                 QQuickAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
302                 QQuickItem *target = pc->object();
303                 QQuickItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();
304
305                 //### this mirrors the logic in QQuickParentChange.
306                 bool ok;
307                 const QTransform &transform = targetParent->itemTransform(d->via, &ok);
308                 if (transform.type() >= QTransform::TxShear || !ok) {
309                     qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under complex transform");
310                     ok = false;
311                 }
312
313                 qreal scale = 1;
314                 qreal rotation = 0;
315                 bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
316                 if (ok && !isRotate) {
317                     if (transform.m11() == transform.m22())
318                         scale = transform.m11();
319                     else {
320                         qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
321                         ok = false;
322                     }
323                 } else if (ok && isRotate) {
324                     if (transform.m11() == transform.m22())
325                         scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
326                     else {
327                         qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
328                         ok = false;
329                     }
330
331                     if (scale != 0)
332                         rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
333                     else {
334                         qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0");
335                         ok = false;
336                     }
337                 }
338
339                 const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal()));
340                 qreal x = point.x();
341                 qreal y = point.y();
342                 if (ok && target->transformOrigin() != QQuickItem::TopLeft) {
343                     qreal w = target->width();
344                     qreal h = target->height();
345                     if (pc->widthIsSet() && i < actions.size() - 1)
346                         w = actions[++i].toValue.toReal();
347                     if (pc->heightIsSet() && i < actions.size() - 1)
348                         h = actions[++i].toValue.toReal();
349                     const QPointF &transformOrigin
350                             = d->computeTransformOrigin(target->transformOrigin(), w,h);
351                     qreal tempxt = transformOrigin.x();
352                     qreal tempyt = transformOrigin.y();
353                     QTransform t;
354                     t.translate(-tempxt, -tempyt);
355                     t.rotate(rotation);
356                     t.scale(scale, scale);
357                     t.translate(tempxt, tempyt);
358                     const QPointF &offset = t.map(QPointF(0,0));
359                     x += offset.x();
360                     y += offset.y();
361                 }
362
363                 if (ok) {
364                     //qDebug() << x << y << rotation << scale;
365                     xAction.toValue = x;
366                     yAction.toValue = y;
367                     sAction.toValue = sAction.toValue.toReal() * scale;
368                     rAction.toValue = rAction.toValue.toReal() + rotation;
369                 }
370             }
371         }
372     }
373
374     if (data->actions.count()) {
375         QSequentialAnimationGroupJob *topLevelGroup = new QSequentialAnimationGroupJob;
376         QActionAnimation *viaAction = d->via ? new QActionAnimation : 0;
377         QActionAnimation *targetAction = new QActionAnimation;
378         //we'll assume the common case by far is to have children, and always create ag
379         QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob;
380
381         if (d->via)
382             viaAction->setAnimAction(viaData);
383         targetAction->setAnimAction(data);
384
385         //take care of any child animations
386         bool valid = d->defaultProperty.isValid();
387         QAbstractAnimationJob* anim;
388         for (int ii = 0; ii < d->animations.count(); ++ii) {
389             if (valid)
390                 d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
391             anim = d->animations.at(ii)->transition(actions, modified, direction, defaultTarget);
392             if (anim)
393                 ag->appendAnimation(anim);
394         }
395
396         //TODO: simplify/clarify logic
397         bool forwards = direction == QQuickAbstractAnimation::Forward;
398         if (forwards) {
399             topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
400             topLevelGroup->appendAnimation(ag);
401             if (d->via)
402                 topLevelGroup->appendAnimation(targetAction);
403         } else {
404             if (d->via)
405                 topLevelGroup->appendAnimation(targetAction);
406             topLevelGroup->appendAnimation(ag);
407             topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
408         }
409         return initInstance(topLevelGroup);
410     } else {
411         delete data;
412         delete viaData;
413     }
414     return 0;
415 }
416
417 /*!
418     \qmlclass AnchorAnimation QQuickAnchorAnimation
419     \inqmlmodule QtQuick 2
420     \ingroup qtquick-animation-properties
421     \inherits Animation
422     \brief Animates changes in anchor values
423
424     AnchorAnimation is used to animate an anchor change.
425
426     In the following snippet we animate the addition of a right anchor to a \l Rectangle:
427
428     \snippet qml/anchoranimation.qml 0
429
430     For convenience, when an AnchorAnimation is used in a \l Transition, it will
431     animate any AnchorChanges that have occurred during the state change.
432     This can be overridden by setting a specific target item using the
433     \l target property.
434
435     Like any other animation element, an AnchorAnimation can be applied in a
436     number of ways, including transitions, behaviors and property value
437     sources. The \l {QML Animation and Transitions} documentation shows a
438     variety of methods for creating animations.
439
440     \sa {QML Animation and Transitions}, AnchorChanges
441 */
442 QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent)
443 : QQuickAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
444 {
445 }
446
447 QQuickAnchorAnimation::~QQuickAnchorAnimation()
448 {
449 }
450
451 /*!
452     \qmlproperty list<Item> QtQuick2::AnchorAnimation::targets
453     The items to reanchor.
454
455     If no targets are specified all AnchorChanges will be
456     animated by the AnchorAnimation.
457 */
458 QQmlListProperty<QQuickItem> QQuickAnchorAnimation::targets()
459 {
460     Q_D(QQuickAnchorAnimation);
461     return QQmlListProperty<QQuickItem>(this, d->targets);
462 }
463
464 /*!
465     \qmlproperty int QtQuick2::AnchorAnimation::duration
466     This property holds the duration of the animation, in milliseconds.
467
468     The default value is 250.
469 */
470 int QQuickAnchorAnimation::duration() const
471 {
472     Q_D(const QQuickAnchorAnimation);
473     return d->duration;
474 }
475
476 void QQuickAnchorAnimation::setDuration(int duration)
477 {
478     if (duration < 0) {
479         qmlInfo(this) << tr("Cannot set a duration of < 0");
480         return;
481     }
482
483     Q_D(QQuickAnchorAnimation);
484     if (d->duration == duration)
485         return;
486     d->duration = duration;
487     emit durationChanged(duration);
488 }
489
490 /*!
491     \qmlproperty enumeration QtQuick2::AnchorAnimation::easing.type
492     \qmlproperty real QtQuick2::AnchorAnimation::easing.amplitude
493     \qmlproperty real QtQuick2::AnchorAnimation::easing.overshoot
494     \qmlproperty real QtQuick2::AnchorAnimation::easing.period
495     \brief Specifies the easing curve used for the animation
496
497     To specify an easing curve you need to specify at least the type. For some curves you can also specify
498     amplitude, period and/or overshoot. The default easing curve is
499     Linear.
500
501     \qml
502     AnchorAnimation { easing.type: Easing.InOutQuad }
503     \endqml
504
505     See the \l{PropertyAnimation::easing.type} documentation for information
506     about the different types of easing curves.
507 */
508 QEasingCurve QQuickAnchorAnimation::easing() const
509 {
510     Q_D(const QQuickAnchorAnimation);
511     return d->easing;
512 }
513
514 void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
515 {
516     Q_D(QQuickAnchorAnimation);
517     if (d->easing == e)
518         return;
519
520     d->easing = e;
521     emit easingChanged(e);
522 }
523
524 QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &actions,
525                         QQmlProperties &modified,
526                         TransitionDirection direction,
527                         QObject *defaultTarget)
528 {
529     Q_UNUSED(modified);
530     Q_UNUSED(defaultTarget);
531     Q_D(QQuickAnchorAnimation);
532     QQuickAnimationPropertyUpdater *data = new QQuickAnimationPropertyUpdater;
533     data->interpolatorType = QMetaType::QReal;
534     data->interpolator = d->interpolator;
535     data->reverse = direction == Backward ? true : false;
536     data->fromSourced = false;
537     data->fromDefined = false;
538
539     for (int ii = 0; ii < actions.count(); ++ii) {
540         QQuickAction &action = actions[ii];
541         if (action.event && action.event->type() == QQuickActionEvent::AnchorChanges
542             && (d->targets.isEmpty() || d->targets.contains(static_cast<QQuickAnchorChanges*>(action.event)->object()))) {
543             data->actions << static_cast<QQuickAnchorChanges*>(action.event)->additionalActions();
544         }
545     }
546
547     QQuickBulkValueAnimator *animator = new QQuickBulkValueAnimator;
548     if (data->actions.count()) {
549         animator->setAnimValue(data);
550         animator->setFromSourcedValue(&data->fromSourced);
551     } else {
552         delete data;
553     }
554
555     animator->setDuration(d->duration);
556     animator->setEasingCurve(d->easing);
557     return initInstance(animator);
558 }
559
560 /*!
561     \qmlclass PathAnimation QQuickPathAnimation
562     \inqmlmodule QtQuick 2
563     \ingroup qtquick-animation-properties
564     \inherits Animation
565     \since QtQuick 2.0
566     \brief Animates an item along a path
567
568     When used in a transition, the path can be specified without start
569     or end points, for example:
570     \qml
571     PathAnimation {
572         path: Path {
573             //no startX, startY
574             PathCurve { x: 100; y: 100}
575             PathCurve {}    //last element is empty with no end point specified
576         }
577     }
578     \endqml
579
580     In the above case, the path start will be the item's current position, and the
581     path end will be the item's target position in the target state.
582
583     \sa {QML Animation and Transitions}, PathInterpolator
584 */
585 QQuickPathAnimation::QQuickPathAnimation(QObject *parent)
586 : QQuickAbstractAnimation(*(new QQuickPathAnimationPrivate), parent)
587 {
588 }
589
590 QQuickPathAnimation::~QQuickPathAnimation()
591 {
592     Q_D(QQuickPathAnimation);
593     QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it;
594     for (it = d->activeAnimations.begin(); it != d->activeAnimations.end(); ++it) {
595         it.value()->clearTemplate();
596     }
597 }
598
599 /*!
600     \qmlproperty int QtQuick2::PathAnimation::duration
601     This property holds the duration of the animation, in milliseconds.
602
603     The default value is 250.
604 */
605 int QQuickPathAnimation::duration() const
606 {
607     Q_D(const QQuickPathAnimation);
608     return d->duration;
609 }
610
611 void QQuickPathAnimation::setDuration(int duration)
612 {
613     if (duration < 0) {
614         qmlInfo(this) << tr("Cannot set a duration of < 0");
615         return;
616     }
617
618     Q_D(QQuickPathAnimation);
619     if (d->duration == duration)
620         return;
621     d->duration = duration;
622     emit durationChanged(duration);
623 }
624
625 /*!
626     \qmlproperty enumeration QtQuick2::PathAnimation::easing.type
627     \qmlproperty real QtQuick2::PathAnimation::easing.amplitude
628     \qmlproperty list<real> QtQuick2::PathAnimation::easing.bezierCurve
629     \qmlproperty real QtQuick2::PathAnimation::easing.overshoot
630     \qmlproperty real QtQuick2::PathAnimation::easing.period
631     \brief the easing curve used for the animation.
632
633     To specify an easing curve you need to specify at least the type. For some curves you can also specify
634     amplitude, period, overshoot or custom bezierCurve data. The default easing curve is \c Easing.Linear.
635
636     See the \l{PropertyAnimation::easing.type} documentation for information
637     about the different types of easing curves.
638 */
639 QEasingCurve QQuickPathAnimation::easing() const
640 {
641     Q_D(const QQuickPathAnimation);
642     return d->easingCurve;
643 }
644
645 void QQuickPathAnimation::setEasing(const QEasingCurve &e)
646 {
647     Q_D(QQuickPathAnimation);
648     if (d->easingCurve == e)
649         return;
650
651     d->easingCurve = e;
652     emit easingChanged(e);
653 }
654
655 /*!
656     \qmlproperty Path QtQuick2::PathAnimation::path
657     This property holds the path to animate along.
658
659     For more information on defining a path see the \l Path documentation.
660 */
661 QQuickPath *QQuickPathAnimation::path() const
662 {
663     Q_D(const QQuickPathAnimation);
664     return d->path;
665 }
666
667 void QQuickPathAnimation::setPath(QQuickPath *path)
668 {
669     Q_D(QQuickPathAnimation);
670     if (d->path == path)
671         return;
672
673     d->path = path;
674     emit pathChanged();
675 }
676
677 /*!
678     \qmlproperty Item QtQuick2::PathAnimation::target
679     This property holds the item to animate.
680 */
681 QQuickItem *QQuickPathAnimation::target() const
682 {
683     Q_D(const QQuickPathAnimation);
684     return d->target;
685 }
686
687 void QQuickPathAnimation::setTargetObject(QQuickItem *target)
688 {
689     Q_D(QQuickPathAnimation);
690     if (d->target == target)
691         return;
692
693     d->target = target;
694     emit targetChanged();
695 }
696
697 /*!
698     \qmlproperty enumeration QtQuick2::PathAnimation::orientation
699     This property controls the rotation of the item as it animates along the path.
700
701     If a value other than \c Fixed is specified, the PathAnimation will rotate the
702     item to achieve the specified orientation as it travels along the path.
703
704     \list
705     \li PathAnimation.Fixed (default) - the PathAnimation will not control
706        the rotation of the item.
707     \li PathAnimation.RightFirst - The right side of the item will lead along the path.
708     \li PathAnimation.LeftFirst - The left side of the item will lead along the path.
709     \li PathAnimation.BottomFirst - The bottom of the item will lead along the path.
710     \li PathAnimation.TopFirst - The top of the item will lead along the path.
711     \endlist
712 */
713 QQuickPathAnimation::Orientation QQuickPathAnimation::orientation() const
714 {
715     Q_D(const QQuickPathAnimation);
716     return d->orientation;
717 }
718
719 void QQuickPathAnimation::setOrientation(Orientation orientation)
720 {
721     Q_D(QQuickPathAnimation);
722     if (d->orientation == orientation)
723         return;
724
725     d->orientation = orientation;
726     emit orientationChanged(d->orientation);
727 }
728
729 /*!
730     \qmlproperty point QtQuick2::PathAnimation::anchorPoint
731     This property holds the anchor point for the item being animated.
732
733     By default, the upper-left corner of the target (its 0,0 point)
734     will be anchored to (or follow) the path. The anchorPoint property can be used to
735     specify a different point for anchoring. For example, specifying an anchorPoint of
736     5,5 for a 10x10 item means the center of the item will follow the path.
737 */
738 QPointF QQuickPathAnimation::anchorPoint() const
739 {
740     Q_D(const QQuickPathAnimation);
741     return d->anchorPoint;
742 }
743
744 void QQuickPathAnimation::setAnchorPoint(const QPointF &point)
745 {
746     Q_D(QQuickPathAnimation);
747     if (d->anchorPoint == point)
748         return;
749
750     d->anchorPoint = point;
751     emit anchorPointChanged(point);
752 }
753
754 /*!
755     \qmlproperty real QtQuick2::PathAnimation::orientationEntryDuration
756     This property holds the duration (in milliseconds) of the transition in to the orientation.
757
758     If an orientation has been specified for the PathAnimation, and the starting
759     rotation of the item does not match that given by the orientation,
760     orientationEntryDuration can be used to smoothly transition from the item's
761     starting rotation to the rotation given by the path orientation.
762 */
763 int QQuickPathAnimation::orientationEntryDuration() const
764 {
765     Q_D(const QQuickPathAnimation);
766     return d->entryDuration;
767 }
768
769 void QQuickPathAnimation::setOrientationEntryDuration(int duration)
770 {
771     Q_D(QQuickPathAnimation);
772     if (d->entryDuration == duration)
773         return;
774     d->entryDuration = duration;
775     emit orientationEntryDurationChanged(duration);
776 }
777
778 /*!
779     \qmlproperty real QtQuick2::PathAnimation::orientationExitDuration
780     This property holds the duration (in milliseconds) of the transition out of the orientation.
781
782     If an orientation and endRotation have been specified for the PathAnimation,
783     orientationExitDuration can be used to smoothly transition from the rotation given
784     by the path orientation to the specified endRotation.
785 */
786 int QQuickPathAnimation::orientationExitDuration() const
787 {
788     Q_D(const QQuickPathAnimation);
789     return d->exitDuration;
790 }
791
792 void QQuickPathAnimation::setOrientationExitDuration(int duration)
793 {
794     Q_D(QQuickPathAnimation);
795     if (d->exitDuration == duration)
796         return;
797     d->exitDuration = duration;
798     emit orientationExitDurationChanged(duration);
799 }
800
801 /*!
802     \qmlproperty real QtQuick2::PathAnimation::endRotation
803     This property holds the ending rotation for the target.
804
805     If an orientation has been specified for the PathAnimation,
806     and the path doesn't end with the item at the desired rotation,
807     the endRotation property can be used to manually specify an end
808     rotation.
809
810     This property is typically used with orientationExitDuration, as specifying
811     an endRotation without an orientationExitDuration may cause a jump to
812     the final rotation, rather than a smooth transition.
813 */
814 qreal QQuickPathAnimation::endRotation() const
815 {
816     Q_D(const QQuickPathAnimation);
817     return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
818 }
819
820 void QQuickPathAnimation::setEndRotation(qreal rotation)
821 {
822     Q_D(QQuickPathAnimation);
823     if (!d->endRotation.isNull && d->endRotation == rotation)
824         return;
825
826     d->endRotation = rotation;
827     emit endRotationChanged(d->endRotation);
828 }
829
830 QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actions,
831                                            QQmlProperties &modified,
832                                            TransitionDirection direction,
833                                            QObject *defaultTarget)
834 {
835     Q_D(QQuickPathAnimation);
836
837     QQuickItem *target = d->target ? d->target : qobject_cast<QQuickItem*>(defaultTarget);
838
839     QQuickPathAnimationUpdater prevData;
840     bool havePrevData = false;
841     if (d->activeAnimations.contains(target)) {
842         havePrevData = true;
843         prevData = *d->activeAnimations[target]->pathUpdater();
844     }
845
846     QList<QQuickItem*> keys = d->activeAnimations.keys();
847     foreach (QQuickItem *item, keys) {
848         QQuickPathAnimationAnimator *anim = d->activeAnimations.value(item);
849         if (anim->state() == QAbstractAnimationJob::Stopped) {
850             anim->clearTemplate();
851             d->activeAnimations.remove(item);
852         }
853     }
854
855     QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater();
856     QQuickPathAnimationAnimator *pa = new QQuickPathAnimationAnimator(d);
857
858     d->activeAnimations[target] = pa;
859
860     data->orientation = d->orientation;
861     data->anchorPoint = d->anchorPoint;
862     data->entryInterval = d->duration ? qreal(d->entryDuration) / d->duration : qreal(0);
863     data->exitInterval = d->duration ? qreal(d->exitDuration) / d->duration : qreal(0);
864     data->endRotation = d->endRotation;
865     data->reverse = direction == Backward ? true : false;
866     data->fromSourced = false;
867     data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
868     data->toDefined = d->path ? d->path->hasEnd() : false;
869     int origModifiedSize = modified.count();
870
871     for (int i = 0; i < actions.count(); ++i) {
872         QQuickAction &action = actions[i];
873         if (action.event)
874             continue;
875         if (action.specifiedObject == target && action.property.name() == QLatin1String("x")) {
876             data->toX = action.toValue.toReal();
877             modified << action.property;
878             action.fromValue = action.toValue;
879         }
880         if (action.specifiedObject == target && action.property.name() == QLatin1String("y")) {
881             data->toY = action.toValue.toReal();
882             modified << action.property;
883             action.fromValue = action.toValue;
884         }
885     }
886
887     if (target && d->path &&
888         (modified.count() > origModifiedSize || data->toDefined)) {
889         data->target = target;
890         data->path = d->path;
891         data->path->invalidateSequentialHistory();
892
893         if (havePrevData) {
894             // get the original start angle that was used (so we can exactly reverse).
895             data->startRotation = prevData.startRotation;
896
897             // treat interruptions specially, otherwise we end up with strange paths
898             if ((data->reverse || prevData.reverse) && prevData.currentV > 0 && prevData.currentV < 1) {
899                 if (!data->fromDefined && !data->toDefined && !prevData.painterPath.isEmpty()) {
900                     QPointF pathPos = QQuickPath::sequentialPointAt(prevData.painterPath, prevData.pathLength, prevData.attributePoints, prevData.prevBez, prevData.currentV);
901                     if (!prevData.anchorPoint.isNull())
902                         pathPos -= prevData.anchorPoint;
903                     if (pathPos == data->target->pos()) {   //only treat as interruption if we interrupted ourself
904                         data->painterPath = prevData.painterPath;
905                         data->toDefined = data->fromDefined = data->fromSourced = true;
906                         data->prevBez.isValid = false;
907                         data->interruptStart = prevData.currentV;
908                         data->startRotation = prevData.startRotation;
909                         data->pathLength = prevData.pathLength;
910                         data->attributePoints = prevData.attributePoints;
911                     }
912                 }
913             }
914         }
915         pa->setFromSourcedValue(&data->fromSourced);
916         pa->setAnimValue(data);
917         pa->setDuration(d->duration);
918         pa->setEasingCurve(d->easingCurve);
919         return initInstance(pa);
920     } else {
921         pa->setFromSourcedValue(0);
922         pa->setAnimValue(0);
923         delete pa;
924         delete data;
925     }
926     return 0;
927 }
928
929 void QQuickPathAnimationUpdater::setValue(qreal v)
930 {
931     v = qMin(qMax(v, (qreal)0.0), (qreal)1.0);;
932
933     if (interruptStart.isValid()) {
934         if (reverse)
935             v = 1 - v;
936         qreal end = reverse ? 0.0 : 1.0;
937         v = interruptStart + v * (end-interruptStart);
938     }
939     currentV = v;
940     bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
941     if (!fromSourced && (!fromDefined || !toDefined)) {
942         qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
943         qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
944         qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
945         qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y();
946
947         prevBez.isValid = false;
948         painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
949         fromSourced = true;
950     }
951
952     qreal angle;
953     bool fixed = orientation == QQuickPathAnimation::Fixed;
954     QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle);
955
956     //adjust position according to anchor point
957     if (!anchorPoint.isNull()) {
958         currentPos -= anchorPoint;
959         if (atStart) {
960             if (!anchorPoint.isNull() && !fixed)
961                 target->setTransformOriginPoint(anchorPoint);
962         }
963     }
964
965     target->setPos(currentPos);
966
967     //adjust angle according to orientation
968     if (!fixed) {
969         switch (orientation) {
970             case QQuickPathAnimation::RightFirst:
971                 angle = -angle;
972                 break;
973             case QQuickPathAnimation::TopFirst:
974                 angle = -angle + 90;
975                 break;
976             case QQuickPathAnimation::LeftFirst:
977                 angle = -angle + 180;
978                 break;
979             case QQuickPathAnimation::BottomFirst:
980                 angle = -angle + 270;
981                 break;
982             default:
983                 angle = 0;
984                 break;
985         }
986
987         if (atStart && !reverse) {
988             startRotation = target->rotation();
989
990             //shortest distance to correct orientation
991             qreal diff = angle - startRotation;
992             while (diff > 180.0) {
993                 startRotation.value += 360.0;
994                 diff -= 360.0;
995             }
996             while (diff < -180.0) {
997                 startRotation.value -= 360.0;
998                 diff += 360.0;
999             }
1000         }
1001
1002         //smoothly transition to the desired orientation
1003         //TODO: shortest distance calculations
1004         if (startRotation.isValid()) {
1005             if (reverse && v == 0.0)
1006                 angle = startRotation;
1007             else if (v < entryInterval)
1008                 angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval;
1009         }
1010         if (endRotation.isValid()) {
1011             qreal exitStart = 1 - entryInterval;
1012             if (!reverse && v == 1.0)
1013                 angle = endRotation;
1014             else if (v > exitStart)
1015                 angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
1016         }
1017         target->setRotation(angle);
1018     }
1019
1020     /*
1021         NOTE: we don't always reset the transform origin, as it can cause a
1022         visual jump if ending on an angle. This means that in some cases
1023         (anchor point and orientation both specified, and ending at an angle)
1024         the transform origin will always be set after running the path animation.
1025      */
1026     if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
1027         if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle))
1028             target->setTransformOriginPoint(QPointF());
1029     }
1030 }
1031
1032 QQuickPathAnimationAnimator::QQuickPathAnimationAnimator(QQuickPathAnimationPrivate *priv)
1033     : animationTemplate(priv)
1034 {
1035 }
1036
1037 QQuickPathAnimationAnimator::~QQuickPathAnimationAnimator()
1038 {
1039     if (animationTemplate && pathUpdater()) {
1040         QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it =
1041                 animationTemplate->activeAnimations.find(pathUpdater()->target);
1042         if (it != animationTemplate->activeAnimations.end() && it.value() == this)
1043             animationTemplate->activeAnimations.erase(it);
1044     }
1045 }
1046
1047 QT_END_NAMESPACE