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