From 39604894946c3c0d623c0073ccf027a8e6df120a Mon Sep 17 00:00:00 2001
From: Kent Hansen <khansen@trolltech.com>
Date: Tue, 29 Sep 2009 11:59:01 +0200
Subject: Do synchronous processing of events in state machine if possible

Avoid delayed scheduling in the cases where there's no need to
delay it (e.g. when the state machine intercepts a signal or event).

Task-number: QTBUG-4491
Reviewed-by: Eskil Abrahamsen Blomfeldt
---
 src/corelib/statemachine/qstatemachine.cpp     | 46 +++++++++++++++++++-------
 src/corelib/statemachine/qstatemachine_p.h     |  7 +++-
 tests/auto/qstatemachine/tst_qstatemachine.cpp | 33 ++++++++++++++++++
 3 files changed, 73 insertions(+), 13 deletions(-)

diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp
index 503eec0..8d50870c 100644
--- a/src/corelib/statemachine/qstatemachine.cpp
+++ b/src/corelib/statemachine/qstatemachine.cpp
@@ -804,6 +804,14 @@ void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &tr
         }
 
         if (hasValidEndValue) {
+            if (anim->state() == QAbstractAnimation::Running) {
+                // The animation is still running. This can happen if the
+                // animation is a group, and one of its children just finished,
+                // and that caused a state to emit its polished() signal, and
+                // that triggered a transition in the machine.
+                // Just stop the animation so it is correctly restarted again.
+                anim->stop();
+            }
             anim->start();
         }
     }
@@ -1268,12 +1276,19 @@ void QStateMachinePrivate::_q_process()
     }
 }
 
-void QStateMachinePrivate::scheduleProcess()
+void QStateMachinePrivate::processEvents(EventProcessingMode processingMode)
 {
     if ((state != Running) || processing || processingScheduled)
         return;
-    processingScheduled = true;
-    QMetaObject::invokeMethod(q_func(), "_q_process", Qt::QueuedConnection);
+    switch (processingMode) {
+    case DirectProcessing:
+        _q_process();
+        break;
+    case QueuedProcessing:
+        processingScheduled = true;
+        QMetaObject::invokeMethod(q_func(), "_q_process", Qt::QueuedConnection);
+        break;
+    }
 }
 
 namespace {
@@ -1335,7 +1350,7 @@ void QStateMachinePrivate::goToState(QAbstractState *targetState)
         trans->setTargetState(targetState);
     }
 
-    scheduleProcess();
+    processEvents(QueuedProcessing);
 }
 
 void QStateMachinePrivate::registerTransitions(QAbstractState *state)
@@ -1512,6 +1527,15 @@ void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transitio
     }
     QEventTransitionPrivate::get(transition)->registered = false;
 }
+
+void QStateMachinePrivate::handleFilteredEvent(QObject *watched, QEvent *event)
+{
+    Q_ASSERT(qobjectEvents.contains(watched));
+    if (qobjectEvents[watched].contains(event->type())) {
+        internalEventQueue.append(new QStateMachine::WrappedEvent(watched, handler->cloneEvent(event)));
+        processEvents(DirectProcessing);
+    }
+}
 #endif
 
 void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalIndex,
@@ -1533,7 +1557,7 @@ void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalInd
              << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ')';
 #endif
     internalEventQueue.append(new QStateMachine::SignalEvent(sender, signalIndex, vargs));
-    scheduleProcess();
+    processEvents(DirectProcessing);
 }
 
 /*!
@@ -1768,7 +1792,7 @@ void QStateMachine::stop()
         break;
     case QStateMachinePrivate::Running:
         d->stop = true;
-        d->scheduleProcess();
+        d->processEvents(QStateMachinePrivate::QueuedProcessing);
         break;
     }
 }
@@ -1798,7 +1822,7 @@ void QStateMachine::postEvent(QEvent *event, int delay)
         d->delayedEvents[tid] = event;
     } else {
         d->externalEventQueue.append(event);
-        d->scheduleProcess();
+        d->processEvents(QStateMachinePrivate::QueuedProcessing);
     }
 }
 
@@ -1814,7 +1838,7 @@ void QStateMachine::postInternalEvent(QEvent *event)
     qDebug() << this << ": posting internal event" << event;
 #endif
     d->internalEventQueue.append(event);
-    d->scheduleProcess();
+    d->processEvents(QStateMachinePrivate::QueuedProcessing);
 }
 
 /*!
@@ -1862,7 +1886,7 @@ bool QStateMachine::event(QEvent *e)
             killTimer(tid);
             QEvent *ee = d->delayedEvents.take(tid);
             d->externalEventQueue.append(ee);
-            d->scheduleProcess();
+            d->processEvents(QStateMachinePrivate::DirectProcessing);
             return true;
         }
     }
@@ -1876,9 +1900,7 @@ bool QStateMachine::event(QEvent *e)
 bool QStateMachine::eventFilter(QObject *watched, QEvent *event)
 {
     Q_D(QStateMachine);
-    Q_ASSERT(d->qobjectEvents.contains(watched));
-    if (d->qobjectEvents[watched].contains(event->type()))
-        postEvent(new QStateMachine::WrappedEvent(watched, d->handler->cloneEvent(event)));
+    d->handleFilteredEvent(watched, event);
     return false;
 }
 #endif
diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h
index a1b6de2..141bc5c 100644
--- a/src/corelib/statemachine/qstatemachine_p.h
+++ b/src/corelib/statemachine/qstatemachine_p.h
@@ -88,6 +88,10 @@ public:
         Starting,
         Running
     };
+    enum EventProcessingMode {
+        DirectProcessing,
+        QueuedProcessing
+    };
     enum StopProcessingReason {
         EventQueueEmpty,
         Finished,
@@ -149,12 +153,13 @@ public:
 #ifndef QT_NO_STATEMACHINE_EVENTFILTER
     void registerEventTransition(QEventTransition *transition);
     void unregisterEventTransition(QEventTransition *transition);
+    void handleFilteredEvent(QObject *watched, QEvent *event);
 #endif
     void unregisterTransition(QAbstractTransition *transition);
     void unregisterAllTransitions();
     void handleTransitionSignal(QObject *sender, int signalIndex,
                                 void **args);    
-    void scheduleProcess();
+    void processEvents(EventProcessingMode processingMode);
     
 #ifndef QT_NO_PROPERTIES
     typedef QPair<QObject *, QByteArray> RestorableId;
diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp
index 7244d72..37b34bf 100644
--- a/tests/auto/qstatemachine/tst_qstatemachine.cpp
+++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp
@@ -176,6 +176,7 @@ private slots:
     void twoAnimatedTransitions();
     void playAnimationTwice();
     void nestedTargetStateForAnimation();
+    void polishedSignalTransitionsReuseAnimationGroup();
     void animatedGlobalRestoreProperty();
     void specificTargetValueOfAnimation();
 
@@ -3115,6 +3116,38 @@ void tst_QStateMachine::nestedTargetStateForAnimation()
     QCOMPARE(counter.counter, 2);
 }
 
+void tst_QStateMachine::polishedSignalTransitionsReuseAnimationGroup()
+{
+    QStateMachine machine;
+    QObject *object = new QObject(&machine);
+    object->setProperty("foo", 0);
+
+    QState *s1 = new QState(&machine);
+    s1->assignProperty(object, "foo", 123);
+    QState *s2 = new QState(&machine);
+    s2->assignProperty(object, "foo", 456);
+    QState *s3 = new QState(&machine);
+    s3->assignProperty(object, "foo", 789);
+    QFinalState *s4 = new QFinalState(&machine);
+
+    QParallelAnimationGroup animationGroup;
+    animationGroup.addAnimation(new QPropertyAnimation(object, "foo"));
+    QSignalSpy animationFinishedSpy(&animationGroup, SIGNAL(finished()));
+    s1->addTransition(s1, SIGNAL(polished()), s2)->addAnimation(&animationGroup);
+    s2->addTransition(s2, SIGNAL(polished()), s3)->addAnimation(&animationGroup);
+    s3->addTransition(s3, SIGNAL(polished()), s4);
+
+    machine.setInitialState(s1);
+    QSignalSpy machineFinishedSpy(&machine, SIGNAL(finished()));
+    machine.start();
+    QTRY_COMPARE(machineFinishedSpy.count(), 1);
+    QCOMPARE(machine.configuration().size(), 1);
+    QVERIFY(machine.configuration().contains(s4));
+    QCOMPARE(object->property("foo").toInt(), 789);
+
+    QCOMPARE(animationFinishedSpy.count(), 2);
+}
+
 void tst_QStateMachine::animatedGlobalRestoreProperty()
 {
     QStateMachine machine;
-- 
cgit v0.12