Fix test fails related to QTBUG-22237
[profile/ivi/qtdeclarative.git] / src / declarative / items / qquickanimation.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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 QQuickParentAnimation::QQuickParentAnimation(QObject *parent)
58     : QDeclarativeAnimationGroup(*(new QQuickParentAnimationPrivate), parent)
59 {
60     Q_D(QQuickParentAnimation);
61     d->topLevelGroup = new QSequentialAnimationGroup;
62     QDeclarative_setParent_noEvent(d->topLevelGroup, this);
63
64     d->startAction = new QActionAnimation;
65     QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup);
66     d->topLevelGroup->addAnimation(d->startAction);
67
68     d->ag = new QParallelAnimationGroup;
69     QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup);
70     d->topLevelGroup->addAnimation(d->ag);
71
72     d->endAction = new QActionAnimation;
73     QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup);
74     d->topLevelGroup->addAnimation(d->endAction);
75 }
76
77 QQuickParentAnimation::~QQuickParentAnimation()
78 {
79 }
80
81 QQuickItem *QQuickParentAnimation::target() const
82 {
83     Q_D(const QQuickParentAnimation);
84     return d->target;
85 }
86
87 void QQuickParentAnimation::setTarget(QQuickItem *target)
88 {
89     Q_D(QQuickParentAnimation);
90     if (target == d->target)
91         return;
92
93     d->target = target;
94     emit targetChanged();
95 }
96
97 QQuickItem *QQuickParentAnimation::newParent() const
98 {
99     Q_D(const QQuickParentAnimation);
100     return d->newParent;
101 }
102
103 void QQuickParentAnimation::setNewParent(QQuickItem *newParent)
104 {
105     Q_D(QQuickParentAnimation);
106     if (newParent == d->newParent)
107         return;
108
109     d->newParent = newParent;
110     emit newParentChanged();
111 }
112
113 QQuickItem *QQuickParentAnimation::via() const
114 {
115     Q_D(const QQuickParentAnimation);
116     return d->via;
117 }
118
119 void QQuickParentAnimation::setVia(QQuickItem *via)
120 {
121     Q_D(QQuickParentAnimation);
122     if (via == d->via)
123         return;
124
125     d->via = via;
126     emit viaChanged();
127 }
128
129 //### mirrors same-named function in QQuickItem
130 QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const
131 {
132     switch (origin) {
133     default:
134     case QQuickItem::TopLeft:
135         return QPointF(0, 0);
136     case QQuickItem::Top:
137         return QPointF(width / 2., 0);
138     case QQuickItem::TopRight:
139         return QPointF(width, 0);
140     case QQuickItem::Left:
141         return QPointF(0, height / 2.);
142     case QQuickItem::Center:
143         return QPointF(width / 2., height / 2.);
144     case QQuickItem::Right:
145         return QPointF(width, height / 2.);
146     case QQuickItem::BottomLeft:
147         return QPointF(0, height);
148     case QQuickItem::Bottom:
149         return QPointF(width / 2., height);
150     case QQuickItem::BottomRight:
151         return QPointF(width, height);
152     }
153 }
154
155 void QQuickParentAnimation::transition(QDeclarativeStateActions &actions,
156                         QDeclarativeProperties &modified,
157                         TransitionDirection direction)
158 {
159     Q_D(QQuickParentAnimation);
160
161     struct QQuickParentAnimationData : public QAbstractAnimationAction
162     {
163         QQuickParentAnimationData() {}
164         ~QQuickParentAnimationData() { qDeleteAll(pc); }
165
166         QDeclarativeStateActions actions;
167         //### reverse should probably apply on a per-action basis
168         bool reverse;
169         QList<QQuickParentChange *> pc;
170         virtual void doAction()
171         {
172             for (int ii = 0; ii < actions.count(); ++ii) {
173                 const QDeclarativeAction &action = actions.at(ii);
174                 if (reverse)
175                     action.event->reverse();
176                 else
177                     action.event->execute();
178             }
179         }
180     };
181
182     QQuickParentAnimationData *data = new QQuickParentAnimationData;
183     QQuickParentAnimationData *viaData = new QQuickParentAnimationData;
184
185     bool hasExplicit = false;
186     if (d->target && d->newParent) {
187         data->reverse = false;
188         QDeclarativeAction myAction;
189         QQuickParentChange *pc = new QQuickParentChange;
190         pc->setObject(d->target);
191         pc->setParent(d->newParent);
192         myAction.event = pc;
193         data->pc << pc;
194         data->actions << myAction;
195         hasExplicit = true;
196         if (d->via) {
197             viaData->reverse = false;
198             QDeclarativeAction myVAction;
199             QQuickParentChange *vpc = new QQuickParentChange;
200             vpc->setObject(d->target);
201             vpc->setParent(d->via);
202             myVAction.event = vpc;
203             viaData->pc << vpc;
204             viaData->actions << myVAction;
205         }
206         //### once actions have concept of modified,
207         //    loop to match appropriate ParentChanges and mark as modified
208     }
209
210     if (!hasExplicit)
211     for (int i = 0; i < actions.size(); ++i) {
212         QDeclarativeAction &action = actions[i];
213         if (action.event && action.event->typeName() == QLatin1String("ParentChange")
214             && (!d->target || static_cast<QQuickParentChange*>(action.event)->object() == d->target)) {
215
216             QQuickParentChange *pc = static_cast<QQuickParentChange*>(action.event);
217             QDeclarativeAction myAction = action;
218             data->reverse = action.reverseEvent;
219
220             //### this logic differs from PropertyAnimation
221             //    (probably a result of modified vs. done)
222             if (d->newParent) {
223                 QQuickParentChange *epc = new QQuickParentChange;
224                 epc->setObject(static_cast<QQuickParentChange*>(action.event)->object());
225                 epc->setParent(d->newParent);
226                 myAction.event = epc;
227                 data->pc << epc;
228                 data->actions << myAction;
229                 pc = epc;
230             } else {
231                 action.actionDone = true;
232                 data->actions << myAction;
233             }
234
235             if (d->via) {
236                 viaData->reverse = false;
237                 QDeclarativeAction myAction;
238                 QQuickParentChange *vpc = new QQuickParentChange;
239                 vpc->setObject(pc->object());
240                 vpc->setParent(d->via);
241                 myAction.event = vpc;
242                 viaData->pc << vpc;
243                 viaData->actions << myAction;
244                 QDeclarativeAction dummyAction;
245                 QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
246                 QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
247                 QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
248                 QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
249                 QQuickItem *target = pc->object();
250                 QQuickItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();
251
252                 //### this mirrors the logic in QQuickParentChange.
253                 bool ok;
254                 const QTransform &transform = targetParent->itemTransform(d->via, &ok);
255                 if (transform.type() >= QTransform::TxShear || !ok) {
256                     qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under complex transform");
257                     ok = false;
258                 }
259
260                 qreal scale = 1;
261                 qreal rotation = 0;
262                 bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
263                 if (ok && !isRotate) {
264                     if (transform.m11() == transform.m22())
265                         scale = transform.m11();
266                     else {
267                         qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
268                         ok = false;
269                     }
270                 } else if (ok && isRotate) {
271                     if (transform.m11() == transform.m22())
272                         scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
273                     else {
274                         qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
275                         ok = false;
276                     }
277
278                     if (scale != 0)
279                         rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
280                     else {
281                         qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0");
282                         ok = false;
283                     }
284                 }
285
286                 const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal()));
287                 qreal x = point.x();
288                 qreal y = point.y();
289                 if (ok && target->transformOrigin() != QQuickItem::TopLeft) {
290                     qreal w = target->width();
291                     qreal h = target->height();
292                     if (pc->widthIsSet() && i < actions.size() - 1)
293                         w = actions[++i].toValue.toReal();
294                     if (pc->heightIsSet() && i < actions.size() - 1)
295                         h = actions[++i].toValue.toReal();
296                     const QPointF &transformOrigin
297                             = d->computeTransformOrigin(target->transformOrigin(), w,h);
298                     qreal tempxt = transformOrigin.x();
299                     qreal tempyt = transformOrigin.y();
300                     QTransform t;
301                     t.translate(-tempxt, -tempyt);
302                     t.rotate(rotation);
303                     t.scale(scale, scale);
304                     t.translate(tempxt, tempyt);
305                     const QPointF &offset = t.map(QPointF(0,0));
306                     x += offset.x();
307                     y += offset.y();
308                 }
309
310                 if (ok) {
311                     //qDebug() << x << y << rotation << scale;
312                     xAction.toValue = x;
313                     yAction.toValue = y;
314                     sAction.toValue = sAction.toValue.toReal() * scale;
315                     rAction.toValue = rAction.toValue.toReal() + rotation;
316                 }
317             }
318         }
319     }
320
321     if (data->actions.count()) {
322         if (direction == QDeclarativeAbstractAnimation::Forward) {
323             d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
324             d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
325         } else {
326             d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
327             d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
328         }
329     } else {
330         delete data;
331         delete viaData;
332     }
333
334     //take care of any child animations
335     bool valid = d->defaultProperty.isValid();
336     for (int ii = 0; ii < d->animations.count(); ++ii) {
337         if (valid)
338             d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
339         d->animations.at(ii)->transition(actions, modified, direction);
340     }
341
342 }
343
344 QAbstractAnimation *QQuickParentAnimation::qtAnimation()
345 {
346     Q_D(QQuickParentAnimation);
347     return d->topLevelGroup;
348 }
349
350 QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent)
351 : QDeclarativeAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
352 {
353     Q_D(QQuickAnchorAnimation);
354     d->va = new QDeclarativeBulkValueAnimator;
355     QDeclarative_setParent_noEvent(d->va, this);
356 }
357
358 QQuickAnchorAnimation::~QQuickAnchorAnimation()
359 {
360 }
361
362 QAbstractAnimation *QQuickAnchorAnimation::qtAnimation()
363 {
364     Q_D(QQuickAnchorAnimation);
365     return d->va;
366 }
367
368 QDeclarativeListProperty<QQuickItem> QQuickAnchorAnimation::targets()
369 {
370     Q_D(QQuickAnchorAnimation);
371     return QDeclarativeListProperty<QQuickItem>(this, d->targets);
372 }
373
374 int QQuickAnchorAnimation::duration() const
375 {
376     Q_D(const QQuickAnchorAnimation);
377     return d->va->duration();
378 }
379
380 void QQuickAnchorAnimation::setDuration(int duration)
381 {
382     if (duration < 0) {
383         qmlInfo(this) << tr("Cannot set a duration of < 0");
384         return;
385     }
386
387     Q_D(QQuickAnchorAnimation);
388     if (d->va->duration() == duration)
389         return;
390     d->va->setDuration(duration);
391     emit durationChanged(duration);
392 }
393
394 QEasingCurve QQuickAnchorAnimation::easing() const
395 {
396     Q_D(const QQuickAnchorAnimation);
397     return d->va->easingCurve();
398 }
399
400 void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
401 {
402     Q_D(QQuickAnchorAnimation);
403     if (d->va->easingCurve() == e)
404         return;
405
406     d->va->setEasingCurve(e);
407     emit easingChanged(e);
408 }
409
410 void QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions,
411                         QDeclarativeProperties &modified,
412                         TransitionDirection direction)
413 {
414     Q_UNUSED(modified);
415     Q_D(QQuickAnchorAnimation);
416     QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater;
417     data->interpolatorType = QMetaType::QReal;
418     data->interpolator = d->interpolator;
419
420     data->reverse = direction == Backward ? true : false;
421     data->fromSourced = false;
422     data->fromDefined = false;
423
424     for (int ii = 0; ii < actions.count(); ++ii) {
425         QDeclarativeAction &action = actions[ii];
426         if (action.event && action.event->typeName() == QLatin1String("AnchorChanges")
427             && (d->targets.isEmpty() || d->targets.contains(static_cast<QQuickAnchorChanges*>(action.event)->object()))) {
428             data->actions << static_cast<QQuickAnchorChanges*>(action.event)->additionalActions();
429         }
430     }
431
432     if (data->actions.count()) {
433         if (!d->rangeIsSet) {
434             d->va->setStartValue(qreal(0));
435             d->va->setEndValue(qreal(1));
436             d->rangeIsSet = true;
437         }
438         d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
439         d->va->setFromSourcedValue(&data->fromSourced);
440     } else {
441         delete data;
442     }
443 }
444
445 QQuickPathAnimation::QQuickPathAnimation(QObject *parent)
446 : QDeclarativeAbstractAnimation(*(new QQuickPathAnimationPrivate), parent)
447 {
448     Q_D(QQuickPathAnimation);
449     d->pa = new QDeclarativeBulkValueAnimator;
450     QDeclarative_setParent_noEvent(d->pa, this);
451 }
452
453 QQuickPathAnimation::~QQuickPathAnimation()
454 {
455 }
456
457 int QQuickPathAnimation::duration() const
458 {
459     Q_D(const QQuickPathAnimation);
460     return d->pa->duration();
461 }
462
463 void QQuickPathAnimation::setDuration(int duration)
464 {
465     if (duration < 0) {
466         qmlInfo(this) << tr("Cannot set a duration of < 0");
467         return;
468     }
469
470     Q_D(QQuickPathAnimation);
471     if (d->pa->duration() == duration)
472         return;
473     d->pa->setDuration(duration);
474     emit durationChanged(duration);
475 }
476
477 QEasingCurve QQuickPathAnimation::easing() const
478 {
479     Q_D(const QQuickPathAnimation);
480     return d->pa->easingCurve();
481 }
482
483 void QQuickPathAnimation::setEasing(const QEasingCurve &e)
484 {
485     Q_D(QQuickPathAnimation);
486     if (d->pa->easingCurve() == e)
487         return;
488
489     d->pa->setEasingCurve(e);
490     emit easingChanged(e);
491 }
492
493 QDeclarativePath *QQuickPathAnimation::path() const
494 {
495     Q_D(const QQuickPathAnimation);
496     return d->path;
497 }
498
499 void QQuickPathAnimation::setPath(QDeclarativePath *path)
500 {
501     Q_D(QQuickPathAnimation);
502     if (d->path == path)
503         return;
504
505     d->path = path;
506     emit pathChanged();
507 }
508
509 QQuickItem *QQuickPathAnimation::target() const
510 {
511     Q_D(const QQuickPathAnimation);
512     return d->target;
513 }
514
515 void QQuickPathAnimation::setTarget(QQuickItem *target)
516 {
517     Q_D(QQuickPathAnimation);
518     if (d->target == target)
519         return;
520
521     d->target = target;
522     emit targetChanged();
523 }
524
525 QQuickPathAnimation::Orientation QQuickPathAnimation::orientation() const
526 {
527     Q_D(const QQuickPathAnimation);
528     return d->orientation;
529 }
530
531 void QQuickPathAnimation::setOrientation(Orientation orientation)
532 {
533     Q_D(QQuickPathAnimation);
534     if (d->orientation == orientation)
535         return;
536
537     d->orientation = orientation;
538     emit orientationChanged(d->orientation);
539 }
540
541 QPointF QQuickPathAnimation::anchorPoint() const
542 {
543     Q_D(const QQuickPathAnimation);
544     return d->anchorPoint;
545 }
546
547 void QQuickPathAnimation::setAnchorPoint(const QPointF &point)
548 {
549     Q_D(QQuickPathAnimation);
550     if (d->anchorPoint == point)
551         return;
552
553     d->anchorPoint = point;
554     emit anchorPointChanged(point);
555 }
556
557 qreal QQuickPathAnimation::orientationEntryInterval() const
558 {
559     Q_D(const QQuickPathAnimation);
560     return d->entryInterval;
561 }
562
563 void QQuickPathAnimation::setOrientationEntryInterval(qreal interval)
564 {
565     Q_D(QQuickPathAnimation);
566     if (d->entryInterval == interval)
567         return;
568     d->entryInterval = interval;
569     emit orientationEntryIntervalChanged(interval);
570 }
571
572 qreal QQuickPathAnimation::orientationExitInterval() const
573 {
574     Q_D(const QQuickPathAnimation);
575     return d->exitInterval;
576 }
577
578 void QQuickPathAnimation::setOrientationExitInterval(qreal interval)
579 {
580     Q_D(QQuickPathAnimation);
581     if (d->exitInterval == interval)
582         return;
583     d->exitInterval = interval;
584     emit orientationExitIntervalChanged(interval);
585 }
586
587 qreal QQuickPathAnimation::endRotation() const
588 {
589     Q_D(const QQuickPathAnimation);
590     return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
591 }
592
593 void QQuickPathAnimation::setEndRotation(qreal rotation)
594 {
595     Q_D(QQuickPathAnimation);
596     if (!d->endRotation.isNull && d->endRotation == rotation)
597         return;
598
599     d->endRotation = rotation;
600     emit endRotationChanged(d->endRotation);
601 }
602
603
604 QAbstractAnimation *QQuickPathAnimation::qtAnimation()
605 {
606     Q_D(QQuickPathAnimation);
607     return d->pa;
608 }
609
610 void QQuickPathAnimation::transition(QDeclarativeStateActions &actions,
611                                            QDeclarativeProperties &modified,
612                                            TransitionDirection direction)
613 {
614     Q_D(QQuickPathAnimation);
615     QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater;
616
617     data->orientation = d->orientation;
618     data->anchorPoint = d->anchorPoint;
619     data->entryInterval = d->entryInterval;
620     data->exitInterval = d->exitInterval;
621     data->endRotation = d->endRotation;
622     data->reverse = direction == Backward ? true : false;
623     data->fromSourced = false;
624     data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
625     data->toDefined = d->path ? d->path->hasEnd() : false;
626     int origModifiedSize = modified.count();
627
628     for (int i = 0; i < actions.count(); ++i) {
629         QDeclarativeAction &action = actions[i];
630         if (action.event)
631             continue;
632         if (action.specifiedObject == d->target && action.property.name() == QLatin1String("x")) {
633             data->toX = action.toValue.toReal();
634             modified << action.property;
635             action.fromValue = action.toValue;
636         }
637         if (action.specifiedObject == d->target && action.property.name() == QLatin1String("y")) {
638             data->toY = action.toValue.toReal();
639             modified << action.property;
640             action.fromValue = action.toValue;
641         }
642     }
643
644     if (d->target && d->path &&
645         (modified.count() > origModifiedSize || data->toDefined)) {
646         data->target = d->target;
647         data->path = d->path;
648         if (!d->rangeIsSet) {
649             d->pa->setStartValue(qreal(0));
650             d->pa->setEndValue(qreal(1));
651             d->rangeIsSet = true;
652         }
653         /*
654             NOTE: The following block relies on the fact that the previous value hasn't
655             yet been deleted, and has the same target, etc, which may be a bit fragile.
656          */
657         if (d->pa->getAnimValue()) {
658             QQuickPathAnimationUpdater *prevData = static_cast<QQuickPathAnimationUpdater*>(d->pa->getAnimValue());
659
660             // get the original start angle that was used (so we can exactly reverse).
661             data->startRotation = prevData->startRotation;
662
663             // treat interruptions specially, otherwise we end up with strange paths
664             if ((data->reverse || prevData->reverse) && prevData->currentV > 0 && prevData->currentV < 1) {
665                 if (!data->fromDefined && !data->toDefined && !prevData->painterPath.isEmpty()) {
666                     QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData->painterPath, prevData->pathLength, prevData->attributePoints, prevData->prevBez, prevData->currentV);
667                     if (!prevData->anchorPoint.isNull())
668                         pathPos -= prevData->anchorPoint;
669                     if (pathPos == data->target->pos()) {   //only treat as interruption if we interrupted ourself
670                         data->painterPath = prevData->painterPath;
671                         data->toDefined = data->fromDefined = data->fromSourced = true;
672                         data->prevBez.isValid = false;
673                         data->interruptStart = prevData->currentV;
674                         data->startRotation = prevData->startRotation;
675                         data->pathLength = prevData->pathLength;
676                         data->attributePoints = prevData->attributePoints;
677                     }
678                 }
679             }
680         }
681         d->pa->setFromSourcedValue(&data->fromSourced);
682         d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
683     } else {
684         d->pa->setFromSourcedValue(0);
685         d->pa->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped);
686         delete data;
687     }
688 }
689
690 void QQuickPathAnimationUpdater::setValue(qreal v)
691 {
692     if (interruptStart.isValid()) {
693         if (reverse)
694             v = 1 - v;
695         qreal end = reverse ? 0.0 : 1.0;
696         v = interruptStart + v * (end-interruptStart);
697     }
698     currentV = v;
699     bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
700     if (!fromSourced && (!fromDefined || !toDefined)) {
701         qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
702         qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
703         qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
704         qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y();
705
706         prevBez.isValid = false;
707         painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
708         fromSourced = true;
709     }
710
711     qreal angle;
712     bool fixed = orientation == QQuickPathAnimation::Fixed;
713     QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle);
714
715     //adjust position according to anchor point
716     if (!anchorPoint.isNull()) {
717         currentPos -= anchorPoint;
718         if (atStart) {
719             if (!anchorPoint.isNull() && !fixed)
720                 target->setTransformOriginPoint(anchorPoint);
721         }
722     }
723
724     //### could cache properties rather than reconstructing each time
725     QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("x")), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
726     QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("y")), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
727
728     //adjust angle according to orientation
729     if (!fixed) {
730         switch (orientation) {
731             case QQuickPathAnimation::RightFirst:
732                 angle = -angle;
733                 break;
734             case QQuickPathAnimation::TopFirst:
735                 angle = -angle + 90;
736                 break;
737             case QQuickPathAnimation::LeftFirst:
738                 angle = -angle + 180;
739                 break;
740             case QQuickPathAnimation::BottomFirst:
741                 angle = -angle + 270;
742                 break;
743             default:
744                 angle = 0;
745                 break;
746         }
747
748         if (atStart && !reverse) {
749             startRotation = target->rotation();
750
751             //shortest distance to correct orientation
752             qreal diff = angle - startRotation;
753             while (diff > 180.0) {
754                 startRotation.value += 360.0;
755                 diff -= 360.0;
756             }
757             while (diff < -180.0) {
758                 startRotation.value -= 360.0;
759                 diff += 360.0;
760             }
761         }
762
763         //smoothly transition to the desired orientation
764         if (startRotation.isValid()) {
765             if (reverse && v == 0.0)
766                 angle = startRotation;
767             else if (v < entryInterval)
768                 angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval;
769         }
770         if (endRotation.isValid()) {
771             qreal exitStart = 1 - exitInterval;
772             if (!reverse && v == 1.0)
773                 angle = endRotation;
774             else if (v > exitStart)
775                 angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
776         }
777         QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("rotation")), angle, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
778     }
779
780     /*
781         NOTE: we don't always reset the transform origin, as it can cause a
782         visual jump if ending on an angle. This means that in some cases
783         (anchor point and orientation both specified, and ending at an angle)
784         the transform origin will always be set after running the path animation.
785      */
786     if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
787         if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle))
788             target->setTransformOriginPoint(QPointF());
789     }
790 }
791
792 QT_END_NAMESPACE