src/corelib/animation/qparallelanimationgroup.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/corelib/animation/qparallelanimationgroup.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,344 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+    \class QParallelAnimationGroup
+    \brief The QParallelAnimationGroup class provides a parallel group of animations.
+    \since 4.6
+    \ingroup animation
+
+    QParallelAnimationGroup--a \l{QAnimationGroup}{container for
+    animations}--starts all its animations when it is
+    \l{QAbstractAnimation::start()}{started} itself, i.e., runs all
+    animations in parallel. The animation group finishes when the
+    longest lasting animation has finished.
+
+    You can treat QParallelAnimation as any other QAbstractAnimation,
+    e.g., pause, resume, or add it to other animation groups.
+
+    \code
+        QParallelAnimationGroup *group = new QParallelAnimationGroup;
+        group->addAnimation(anim1);
+        group->addAnimation(anim2);
+
+        group->start();
+    \endcode
+
+    In this example, \c anim1 and \c anim2 are two
+    \l{QPropertyAnimation}s that have already been set up.
+
+    \sa QAnimationGroup, QPropertyAnimation, {The Animation Framework}
+*/
+
+
+#include "qparallelanimationgroup.h"
+#include "qparallelanimationgroup_p.h"
+//#define QANIMATION_DEBUG
+
+#ifndef QT_NO_ANIMATION
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    Constructs a QParallelAnimationGroup.
+    \a parent is passed to QObject's constructor.
+*/
+QParallelAnimationGroup::QParallelAnimationGroup(QObject *parent)
+    : QAnimationGroup(*new QParallelAnimationGroupPrivate, parent)
+{
+}
+
+/*!
+    \internal
+*/
+QParallelAnimationGroup::QParallelAnimationGroup(QParallelAnimationGroupPrivate &dd,
+                                                 QObject *parent)
+    : QAnimationGroup(dd, parent)
+{
+}
+
+/*!
+    Destroys the animation group. It will also destroy all its animations.
+*/
+QParallelAnimationGroup::~QParallelAnimationGroup()
+{
+}
+
+/*!
+    \reimp
+*/
+int QParallelAnimationGroup::duration() const
+{
+    Q_D(const QParallelAnimationGroup);
+    int ret = 0;
+
+    for (int i = 0; i < d->animations.size(); ++i) {
+        QAbstractAnimation *animation = d->animations.at(i);
+        const int currentDuration = animation->totalDuration();
+        if (currentDuration == -1)
+            return -1; // Undetermined length
+
+        ret = qMax(ret, currentDuration);
+    }
+
+    return ret;
+}
+
+/*!
+    \reimp
+*/
+void QParallelAnimationGroup::updateCurrentTime(int currentTime)
+{
+    Q_D(QParallelAnimationGroup);
+    if (d->animations.isEmpty())
+        return;
+
+    if (d->currentLoop > d->lastLoop) {
+        // simulate completion of the loop
+        int dura = duration();
+        if (dura > 0) {
+            for (int i = 0; i < d->animations.size(); ++i) {
+                QAbstractAnimation *animation = d->animations.at(i);
+                if (animation->state() != QAbstractAnimation::Stopped)
+                    d->animations.at(i)->setCurrentTime(dura);   // will stop
+            }
+        }
+    } else if (d->currentLoop < d->lastLoop) {
+        // simulate completion of the loop seeking backwards
+        for (int i = 0; i < d->animations.size(); ++i) {
+            QAbstractAnimation *animation = d->animations.at(i);
+            //we need to make sure the animation is in the right state
+            //and then rewind it
+            d->applyGroupState(animation);
+            animation->setCurrentTime(0);
+            animation->stop();
+        }
+    }
+
+#ifdef QANIMATION_DEBUG
+    qDebug("QParallellAnimationGroup %5d: setCurrentTime(%d), loop:%d, last:%d, timeFwd:%d, lastcurrent:%d, %d",
+        __LINE__, d->currentTime, d->currentLoop, d->lastLoop, timeFwd, d->lastCurrentTime, state());
+#endif
+    // finally move into the actual time of the current loop
+    for (int i = 0; i < d->animations.size(); ++i) {
+        QAbstractAnimation *animation = d->animations.at(i);
+        const int dura = animation->totalDuration();
+        //if the loopcount is bigger we should always start all animations
+        if (d->currentLoop > d->lastLoop
+            //if we're at the end of the animation, we need to start it if it wasn't already started in this loop
+            //this happens in Backward direction where not all animations are started at the same time
+            || d->shouldAnimationStart(animation, d->lastCurrentTime > dura /*startIfAtEnd*/)) {
+            d->applyGroupState(animation);
+        }
+
+        if (animation->state() == state()) {
+            animation->setCurrentTime(currentTime);
+            if (dura > 0 && currentTime > dura)
+                animation->stop();
+        }
+    }
+    d->lastLoop = d->currentLoop;
+    d->lastCurrentTime = currentTime;
+}
+
+/*!
+    \reimp
+*/
+void QParallelAnimationGroup::updateState(QAbstractAnimation::State oldState,
+                                          QAbstractAnimation::State newState)
+{
+    Q_D(QParallelAnimationGroup);
+    QAnimationGroup::updateState(oldState, newState);
+
+    switch (newState) {
+    case Stopped:
+        for (int i = 0; i < d->animations.size(); ++i)
+            d->animations.at(i)->stop();
+        d->disconnectUncontrolledAnimations();
+        break;
+    case Paused:
+        for (int i = 0; i < d->animations.size(); ++i)
+            if (d->animations.at(i)->state() == Running)
+                d->animations.at(i)->pause();
+        break;
+    case Running:
+        d->connectUncontrolledAnimations();
+        for (int i = 0; i < d->animations.size(); ++i) {
+            QAbstractAnimation *animation = d->animations.at(i);
+            if (oldState == Stopped)
+                animation->stop();
+            animation->setDirection(d->direction);
+            if (d->shouldAnimationStart(animation, oldState == Stopped))
+                animation->start();
+        }
+        break;
+    }
+}
+
+void QParallelAnimationGroupPrivate::_q_uncontrolledAnimationFinished()
+{
+    Q_Q(QParallelAnimationGroup);
+
+    QAbstractAnimation *animation = qobject_cast<QAbstractAnimation *>(q->sender());
+    Q_ASSERT(animation);
+
+    int uncontrolledRunningCount = 0;
+    if (animation->duration() == -1 || animation->loopCount() < 0) {
+        QHash<QAbstractAnimation *, int>::iterator it = uncontrolledFinishTime.begin();
+        while (it != uncontrolledFinishTime.end()) {
+            if (it.key() == animation) {
+                *it = animation->currentTime();
+            }
+            if (it.value() == -1)
+                ++uncontrolledRunningCount;
+            ++it;
+        }
+    }
+
+    if (uncontrolledRunningCount > 0)
+        return;
+
+    int maxDuration = 0;
+    for (int i = 0; i < animations.size(); ++i)
+        maxDuration = qMax(maxDuration, animations.at(i)->totalDuration());
+
+    if (currentTime >= maxDuration)
+        q->stop();
+}
+
+void QParallelAnimationGroupPrivate::disconnectUncontrolledAnimations()
+{
+    Q_Q(QParallelAnimationGroup);
+
+    QHash<QAbstractAnimation *, int>::iterator it = uncontrolledFinishTime.begin();
+    while (it != uncontrolledFinishTime.end()) {
+        QObject::disconnect(it.key(), SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished()));
+        ++it;
+    }
+
+    uncontrolledFinishTime.clear();
+}
+
+void QParallelAnimationGroupPrivate::connectUncontrolledAnimations()
+{
+    Q_Q(QParallelAnimationGroup);
+
+    for (int i = 0; i < animations.size(); ++i) {
+        QAbstractAnimation *animation = animations.at(i);
+        if (animation->duration() == -1 || animation->loopCount() < 0) {
+            uncontrolledFinishTime[animation] = -1;
+            QObject::connect(animation, SIGNAL(finished()), q, SLOT(_q_uncontrolledAnimationFinished()));
+        }
+    }
+}
+
+bool QParallelAnimationGroupPrivate::shouldAnimationStart(QAbstractAnimation *animation, bool startIfAtEnd) const
+{
+    const int dura = animation->totalDuration();
+    if (dura == -1)
+        return !isUncontrolledAnimationFinished(animation);
+    if (startIfAtEnd)
+        return currentTime <= dura;
+    if (direction == QAbstractAnimation::Forward)
+        return currentTime < dura;
+    else //direction == QAbstractAnimation::Backward
+        return currentTime && currentTime <= dura;
+}
+
+void QParallelAnimationGroupPrivate::applyGroupState(QAbstractAnimation *animation)
+{
+    switch (state)
+    {
+    case QAbstractAnimation::Running:
+        animation->start();
+        break;
+    case QAbstractAnimation::Paused:
+        animation->pause();
+        break;
+    case QAbstractAnimation::Stopped:
+    default:
+        break;
+    }
+}
+
+
+bool QParallelAnimationGroupPrivate::isUncontrolledAnimationFinished(QAbstractAnimation *anim) const
+{
+    return uncontrolledFinishTime.value(anim, -1) >= 0;
+}
+
+/*!
+    \reimp
+*/
+void QParallelAnimationGroup::updateDirection(QAbstractAnimation::Direction direction)
+{
+    Q_D(QParallelAnimationGroup);
+    //we need to update the direction of the current animation
+    if (state() != Stopped) {
+        for (int i = 0; i < d->animations.size(); ++i) {
+            QAbstractAnimation *animation = d->animations.at(i);
+            animation->setDirection(direction);
+        }
+    } else {
+        if (direction == Forward) {
+            d->lastLoop = 0;
+            d->lastCurrentTime = 0;
+        } else {
+            // Looping backwards with loopCount == -1 does not really work well...
+            d->lastLoop = (d->loopCount == -1 ? 0 : d->loopCount - 1);
+            d->lastCurrentTime = duration();
+        }
+    }
+}
+
+/*!
+    \reimp
+*/
+bool QParallelAnimationGroup::event(QEvent *event)
+{
+    return QAnimationGroup::event(event);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qparallelanimationgroup.cpp"
+
+#endif //QT_NO_ANIMATION