Doc: Sanitized QML types
[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 qml-animation-transition
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 doc/snippets/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() {}
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     QSequentialAnimationGroupJob *topLevelGroup = new QSequentialAnimationGroupJob;
375     QActionAnimation *viaAction = d->via ? new QActionAnimation : 0;
376     QActionAnimation *targetAction = new QActionAnimation;
377     //we'll assume the common case by far is to have children, and always create ag
378     QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob;
379
380     if (data->actions.count()) {
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             ag->appendAnimation(anim);
393         }
394
395         //TODO: simplify/clarify logic
396         bool forwards = direction == QQuickAbstractAnimation::Forward;
397         if (forwards) {
398             topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
399             topLevelGroup->appendAnimation(ag);
400             if (d->via)
401                 topLevelGroup->appendAnimation(targetAction);
402         } else {
403             if (d->via)
404                 topLevelGroup->appendAnimation(targetAction);
405             topLevelGroup->appendAnimation(ag);
406             topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
407         }
408     } else {
409         delete data;
410         delete viaData;
411     }
412
413     return initInstance(topLevelGroup);
414 }
415
416 /*!
417     \qmlclass AnchorAnimation QQuickAnchorAnimation
418     \inqmlmodule QtQuick 2
419     \ingroup qml-animation-transition
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 doc/snippets/qml/anchoranimation.qml 0
428
429     For convenience, 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     Like any other animation element, an AnchorAnimation can be applied in a
435     number of ways, including transitions, behaviors and property value
436     sources. The \l {QML Animation and Transitions} documentation shows a
437     variety of methods for creating animations.
438
439     \sa {QML Animation and Transitions}, AnchorChanges
440 */
441 QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent)
442 : QQuickAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
443 {
444 }
445
446 QQuickAnchorAnimation::~QQuickAnchorAnimation()
447 {
448 }
449
450 /*!
451     \qmlproperty list<Item> QtQuick2::AnchorAnimation::targets
452     The items to reanchor.
453
454     If no targets are specified all AnchorChanges will be
455     animated by the AnchorAnimation.
456 */
457 QQmlListProperty<QQuickItem> QQuickAnchorAnimation::targets()
458 {
459     Q_D(QQuickAnchorAnimation);
460     return QQmlListProperty<QQuickItem>(this, d->targets);
461 }
462
463 /*!
464     \qmlproperty int QtQuick2::AnchorAnimation::duration
465     This property holds the duration of the animation, in milliseconds.
466
467     The default value is 250.
468 */
469 int QQuickAnchorAnimation::duration() const
470 {
471     Q_D(const QQuickAnchorAnimation);
472     return d->duration;
473 }
474
475 void QQuickAnchorAnimation::setDuration(int duration)
476 {
477     if (duration < 0) {
478         qmlInfo(this) << tr("Cannot set a duration of < 0");
479         return;
480     }
481
482     Q_D(QQuickAnchorAnimation);
483     if (d->duration == duration)
484         return;
485     d->duration = duration;
486     emit durationChanged(duration);
487 }
488
489 /*!
490     \qmlproperty enumeration QtQuick2::AnchorAnimation::easing.type
491     \qmlproperty real QtQuick2::AnchorAnimation::easing.amplitude
492     \qmlproperty real QtQuick2::AnchorAnimation::easing.overshoot
493     \qmlproperty real QtQuick2::AnchorAnimation::easing.period
494     \brief Specifies the easing curve used for the animation
495
496     To specify an easing curve you need to specify at least the type. For some curves you can also specify
497     amplitude, period and/or overshoot. The default easing curve is
498     Linear.
499
500     \qml
501     AnchorAnimation { easing.type: Easing.InOutQuad }
502     \endqml
503
504     See the \l{PropertyAnimation::easing.type} documentation for information
505     about the different types of easing curves.
506 */
507 QEasingCurve QQuickAnchorAnimation::easing() const
508 {
509     Q_D(const QQuickAnchorAnimation);
510     return d->easing;
511 }
512
513 void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
514 {
515     Q_D(QQuickAnchorAnimation);
516     if (d->easing == e)
517         return;
518
519     d->easing = e;
520     emit easingChanged(e);
521 }
522
523 QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &actions,
524                         QQmlProperties &modified,
525                         TransitionDirection direction,
526                         QObject *defaultTarget)
527 {
528     Q_UNUSED(modified);
529     Q_UNUSED(defaultTarget);
530     Q_D(QQuickAnchorAnimation);
531     QQuickAnimationPropertyUpdater *data = new QQuickAnimationPropertyUpdater;
532     data->interpolatorType = QMetaType::QReal;
533     data->interpolator = d->interpolator;
534     data->reverse = direction == Backward ? true : false;
535     data->fromSourced = false;
536     data->fromDefined = false;
537
538     for (int ii = 0; ii < actions.count(); ++ii) {
539         QQuickAction &action = actions[ii];
540         if (action.event && action.event->type() == QQuickActionEvent::AnchorChanges
541             && (d->targets.isEmpty() || d->targets.contains(static_cast<QQuickAnchorChanges*>(action.event)->object()))) {
542             data->actions << static_cast<QQuickAnchorChanges*>(action.event)->additionalActions();
543         }
544     }
545
546     QQuickBulkValueAnimator *animator = new QQuickBulkValueAnimator;
547     if (data->actions.count()) {
548         animator->setAnimValue(data);
549         animator->setFromSourcedValue(&data->fromSourced);
550     } else {
551         delete data;
552     }
553
554     animator->setDuration(d->duration);
555     animator->setEasingCurve(d->easing);
556     return initInstance(animator);
557 }
558
559 /*!
560     \qmlclass PathAnimation QQuickPathAnimation
561     \inqmlmodule QtQuick 2
562     \ingroup qml-animation-transition
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 {QML Animation and Transitions}, 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     } else {
917         pa->setFromSourcedValue(0);
918         pa->setAnimValue(0);
919         delete pa;
920         delete data;
921     }
922
923     pa->setDuration(d->duration);
924     pa->setEasingCurve(d->easingCurve);
925     return initInstance(pa);
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