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