diff options
author | Simon Hausmann <simon.hausmann@theqtcompany.com> | 2015-06-04 05:56:06 +0000 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2015-06-04 05:56:06 +0000 |
commit | ccad00121d0a9d703cf715c54347b32bfc33bbfc (patch) | |
tree | 8e492f00f9ea81d87ae9126ad06c3dbc21fee3a3 /src/corelib/statemachine | |
parent | dc2617f35be61b4827b8e3d192c85e2feacf7f6a (diff) | |
parent | e2f66f921594b7be4af4a058c959557489e86879 (diff) | |
download | qtbase-ccad00121d0a9d703cf715c54347b32bfc33bbfc.tar.gz |
Merge "Merge remote-tracking branch 'origin/5.5' into dev" into refs/staging/dev
Diffstat (limited to 'src/corelib/statemachine')
-rw-r--r-- | src/corelib/statemachine/qabstracttransition.cpp | 46 | ||||
-rw-r--r-- | src/corelib/statemachine/qabstracttransition.h | 10 | ||||
-rw-r--r-- | src/corelib/statemachine/qabstracttransition_p.h | 1 | ||||
-rw-r--r-- | src/corelib/statemachine/qeventtransition_p.h | 2 | ||||
-rw-r--r-- | src/corelib/statemachine/qstate_p.h | 2 | ||||
-rw-r--r-- | src/corelib/statemachine/qstatemachine.cpp | 343 | ||||
-rw-r--r-- | src/corelib/statemachine/qstatemachine_p.h | 21 |
7 files changed, 332 insertions, 93 deletions
diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp index f128acd54e..81b38ea4c4 100644 --- a/src/corelib/statemachine/qabstracttransition.cpp +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -101,7 +101,35 @@ QT_BEGIN_NAMESPACE parallel group state. */ +/*! + \property QAbstractTransition::transitionType + + \brief indicates whether this transition is an internal transition, or an external transition. + + Internal and external transitions behave the same, except for the case of a transition whose + source state is a compound state and whose target(s) is a descendant of the source. In such a + case, an internal transition will not exit and re-enter its source state, while an external one + will. + + By default, the type is an external transition. +*/ + +/*! + \enum QAbstractTransition::TransitionType + + This enum specifies the kind of transition. By default, the type is an external transition. + + \value ExternalTransition Any state that is the source state of a transition (which is not a + target-less transition) is left, and re-entered when necessary. + \value InternalTransition If the target state of a transition is a sub-state of a compound state, + and that compound state is the source state, an internal transition will + not leave the source state. + + \sa QAbstractTransition::transitionType +*/ + QAbstractTransitionPrivate::QAbstractTransitionPrivate() + : transitionType(QAbstractTransition::ExternalTransition) { } @@ -249,6 +277,24 @@ void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets) } /*! + Returns the type of the transition. +*/ +QAbstractTransition::TransitionType QAbstractTransition::transitionType() const +{ + Q_D(const QAbstractTransition); + return d->transitionType; +} + +/*! + Sets the type of the transition to \a type. +*/ +void QAbstractTransition::setTransitionType(TransitionType type) +{ + Q_D(QAbstractTransition); + d->transitionType = type; +} + +/*! Returns the state machine that this transition is part of, or 0 if the transition is not part of a state machine. */ diff --git a/src/corelib/statemachine/qabstracttransition.h b/src/corelib/statemachine/qabstracttransition.h index 768a364a4b..bf32b3e825 100644 --- a/src/corelib/statemachine/qabstracttransition.h +++ b/src/corelib/statemachine/qabstracttransition.h @@ -59,7 +59,14 @@ class Q_CORE_EXPORT QAbstractTransition : public QObject Q_PROPERTY(QState* sourceState READ sourceState) Q_PROPERTY(QAbstractState* targetState READ targetState WRITE setTargetState NOTIFY targetStateChanged) Q_PROPERTY(QList<QAbstractState*> targetStates READ targetStates WRITE setTargetStates NOTIFY targetStatesChanged) + Q_PROPERTY(TransitionType transitionType READ transitionType WRITE setTransitionType) public: + enum TransitionType { + ExternalTransition, + InternalTransition + }; + Q_ENUM(TransitionType) + QAbstractTransition(QState *sourceState = 0); virtual ~QAbstractTransition(); @@ -69,6 +76,9 @@ public: QList<QAbstractState*> targetStates() const; void setTargetStates(const QList<QAbstractState*> &targets); + TransitionType transitionType() const; + void setTransitionType(TransitionType type); + QStateMachine *machine() const; #ifndef QT_NO_ANIMATION diff --git a/src/corelib/statemachine/qabstracttransition_p.h b/src/corelib/statemachine/qabstracttransition_p.h index d89d057497..4b0644acd9 100644 --- a/src/corelib/statemachine/qabstracttransition_p.h +++ b/src/corelib/statemachine/qabstracttransition_p.h @@ -73,6 +73,7 @@ public: void emitTriggered(); QList<QPointer<QAbstractState> > targetStates; + QAbstractTransition::TransitionType transitionType; #ifndef QT_NO_ANIMATION QList<QAbstractAnimation*> animations; diff --git a/src/corelib/statemachine/qeventtransition_p.h b/src/corelib/statemachine/qeventtransition_p.h index 9e3ef111f9..3e430d86a9 100644 --- a/src/corelib/statemachine/qeventtransition_p.h +++ b/src/corelib/statemachine/qeventtransition_p.h @@ -62,8 +62,8 @@ public: void unregister(); void maybeRegister(); - bool registered; QObject *object; + bool registered; QEvent::Type eventType; }; diff --git a/src/corelib/statemachine/qstate_p.h b/src/corelib/statemachine/qstate_p.h index 28bb176b56..2ce0c13522 100644 --- a/src/corelib/statemachine/qstate_p.h +++ b/src/corelib/statemachine/qstate_p.h @@ -103,8 +103,8 @@ public: QAbstractState *initialState; QState::ChildMode childMode; mutable bool childStatesListNeedsRefresh; - mutable QList<QAbstractState*> childStatesList; mutable bool transitionsListNeedsRefresh; + mutable QList<QAbstractState*> childStatesList; mutable QList<QAbstractTransition*> transitionsList; #ifndef QT_NO_PROPERTIES diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index a8d07602b6..3a1a852ef5 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -177,6 +177,100 @@ QT_BEGIN_NAMESPACE // #define QSTATEMACHINE_DEBUG // #define QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG +struct CalculationCache { + struct TransitionInfo { + QList<QAbstractState*> effectiveTargetStates; + QSet<QAbstractState*> exitSet; + QAbstractState *transitionDomain; + + bool effectiveTargetStatesIsKnown: 1; + bool exitSetIsKnown : 1; + bool transitionDomainIsKnown : 1; + + TransitionInfo() + : transitionDomain(0) + , effectiveTargetStatesIsKnown(false) + , exitSetIsKnown(false) + , transitionDomainIsKnown(false) + {} + }; + + typedef QHash<QAbstractTransition *, TransitionInfo> TransitionInfoCache; + TransitionInfoCache cache; + + bool effectiveTargetStates(QAbstractTransition *t, QList<QAbstractState *> *targets) const + { + Q_ASSERT(targets); + + TransitionInfoCache::const_iterator cacheIt = cache.find(t); + if (cacheIt == cache.end() || !cacheIt->effectiveTargetStatesIsKnown) + return false; + + *targets = cacheIt->effectiveTargetStates; + return true; + } + + void insert(QAbstractTransition *t, const QList<QAbstractState *> &targets) + { + TransitionInfoCache::iterator cacheIt = cache.find(t); + TransitionInfo &ti = cacheIt == cache.end() + ? *cache.insert(t, TransitionInfo()) + : *cacheIt; + + Q_ASSERT(!ti.effectiveTargetStatesIsKnown); + ti.effectiveTargetStates = targets; + ti.effectiveTargetStatesIsKnown = true; + } + + bool exitSet(QAbstractTransition *t, QSet<QAbstractState *> *exits) const + { + Q_ASSERT(exits); + + TransitionInfoCache::const_iterator cacheIt = cache.find(t); + if (cacheIt == cache.end() || !cacheIt->exitSetIsKnown) + return false; + + *exits = cacheIt->exitSet; + return true; + } + + void insert(QAbstractTransition *t, const QSet<QAbstractState *> &exits) + { + TransitionInfoCache::iterator cacheIt = cache.find(t); + TransitionInfo &ti = cacheIt == cache.end() + ? *cache.insert(t, TransitionInfo()) + : *cacheIt; + + Q_ASSERT(!ti.exitSetIsKnown); + ti.exitSet = exits; + ti.exitSetIsKnown = true; + } + + bool transitionDomain(QAbstractTransition *t, QAbstractState **domain) const + { + Q_ASSERT(domain); + + TransitionInfoCache::const_iterator cacheIt = cache.find(t); + if (cacheIt == cache.end() || !cacheIt->transitionDomainIsKnown) + return false; + + *domain = cacheIt->transitionDomain; + return true; + } + + void insert(QAbstractTransition *t, QAbstractState *domain) + { + TransitionInfoCache::iterator cacheIt = cache.find(t); + TransitionInfo &ti = cacheIt == cache.end() + ? *cache.insert(t, TransitionInfo()) + : *cacheIt; + + Q_ASSERT(!ti.transitionDomainIsKnown); + ti.transitionDomain = domain; + ti.transitionDomainIsKnown = true; + } +}; + /* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ : function isDescendant(state1, state2) @@ -205,6 +299,17 @@ static bool containsDecendantOf(const QSet<QAbstractState *> &states, const QAbs return false; } +static int descendantDepth(const QAbstractState *state, const QAbstractState *ancestor) +{ + int depth = 0; + for (const QAbstractState *it = state; it != 0; it = it->parentState()) { + if (it == ancestor) + break; + ++depth; + } + return depth; +} + /* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ : function getProperAncestors(state1, state2) @@ -245,8 +350,14 @@ function getEffectiveTargetStates(transition) targets.add(s) return targets */ -static QSet<QAbstractState *> getEffectiveTargetStates(QAbstractTransition *transition) +static QList<QAbstractState *> getEffectiveTargetStates(QAbstractTransition *transition, CalculationCache *cache) { + Q_ASSERT(cache); + + QList<QAbstractState *> targetsList; + if (cache->effectiveTargetStates(transition, &targetsList)) + return targetsList; + QSet<QAbstractState *> targets; foreach (QAbstractState *s, transition->targetStates()) { if (QHistoryState *historyState = QStateMachinePrivate::toHistoryState(s)) { @@ -266,7 +377,10 @@ static QSet<QAbstractState *> getEffectiveTargetStates(QAbstractTransition *tran targets.insert(s); } } - return targets; + + targetsList = targets.toList(); + cache->insert(transition, targetsList); + return targetsList; } template <class T> @@ -348,10 +462,25 @@ static int indexOfDescendant(QState *s, QAbstractState *desc) bool QStateMachinePrivate::transitionStateEntryLessThan(QAbstractTransition *t1, QAbstractTransition *t2) { QState *s1 = t1->sourceState(), *s2 = t2->sourceState(); - if (s1 == s2) - return QStatePrivate::get(s1)->transitions().indexOf(t1) < QStatePrivate::get(s2)->transitions().indexOf(t2); - else - return stateEntryLessThan(t1->sourceState(), t2->sourceState()); + if (s1 == s2) { + QList<QAbstractTransition*> transitions = QStatePrivate::get(s1)->transitions(); + return transitions.indexOf(t1) < transitions.indexOf(t2); + } else if (isDescendant(s1, s2)) { + return true; + } else if (isDescendant(s2, s1)) { + return false; + } else { + Q_ASSERT(s1->machine() != 0); + QStateMachinePrivate *mach = QStateMachinePrivate::get(s1->machine()); + QState *lca = mach->findLCA(QList<QAbstractState*>() << s1 << s2); + Q_ASSERT(lca != 0); + int s1Depth = descendantDepth(s1, lca); + int s2Depth = descendantDepth(s2, lca); + if (s1Depth == s2Depth) + return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2)); + else + return s1Depth > s2Depth; + } } bool QStateMachinePrivate::stateEntryLessThan(QAbstractState *s1, QAbstractState *s2) @@ -417,8 +546,9 @@ QState *QStateMachinePrivate::findLCCA(const QList<QAbstractState*> &states) con return findLCA(states, true); } -QList<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *event) +QList<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *event, CalculationCache *cache) { + Q_ASSERT(cache); Q_Q(const QStateMachine); QVarLengthArray<QAbstractState *> configuration_sorted; @@ -453,7 +583,7 @@ QList<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *even } if (!enabledTransitions.isEmpty()) { - removeConflictingTransitions(enabledTransitions); + removeConflictingTransitions(enabledTransitions, cache); #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": enabled transitions after removing conflicts:" << enabledTransitions; #endif @@ -486,15 +616,20 @@ function removeConflictingTransitions(enabledTransitions): Note: the implementation below does not build the transitionsToRemove, but removes them in-place. */ -void QStateMachinePrivate::removeConflictingTransitions(QList<QAbstractTransition*> &enabledTransitions) +void QStateMachinePrivate::removeConflictingTransitions(QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache) { + Q_ASSERT(cache); + + if (enabledTransitions.size() < 2) + return; // There is no transition to conflict with. + QList<QAbstractTransition*> filteredTransitions; filteredTransitions.reserve(enabledTransitions.size()); std::sort(enabledTransitions.begin(), enabledTransitions.end(), transitionStateEntryLessThan); foreach (QAbstractTransition *t1, enabledTransitions) { bool t1Preempted = false; - QSet<QAbstractState*> exitSetT1 = computeExitSet_Unordered(QList<QAbstractTransition*>() << t1); + QSet<QAbstractState*> exitSetT1 = computeExitSet_Unordered(t1, cache); QList<QAbstractTransition*>::iterator t2It = filteredTransitions.begin(); while (t2It != filteredTransitions.end()) { QAbstractTransition *t2 = *t2It; @@ -505,8 +640,8 @@ void QStateMachinePrivate::removeConflictingTransitions(QList<QAbstractTransitio break; } - QSet<QAbstractState*> exitSetT2 = computeExitSet_Unordered(QList<QAbstractTransition*>() << t2); - if (!exitSetT1.intersects(exitSetT2)) { + QSet<QAbstractState*> exitSetT2 = computeExitSet_Unordered(t2, cache); + if (exitSetT1.intersect(exitSetT2).isEmpty()) { // No conflict, no cry. Next patient please. ++t2It; } else { @@ -529,17 +664,20 @@ void QStateMachinePrivate::removeConflictingTransitions(QList<QAbstractTransitio enabledTransitions = filteredTransitions; } -void QStateMachinePrivate::microstep(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions) +void QStateMachinePrivate::microstep(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions, + CalculationCache *cache) { + Q_ASSERT(cache); + #ifdef QSTATEMACHINE_DEBUG qDebug() << q_func() << ": begin microstep( enabledTransitions:" << enabledTransitions << ')'; qDebug() << q_func() << ": configuration before exiting states:" << configuration; #endif - QList<QAbstractState*> exitedStates = computeExitSet(enabledTransitions); + QList<QAbstractState*> exitedStates = computeExitSet(enabledTransitions, cache); QHash<RestorableId, QVariant> pendingRestorables = computePendingRestorables(exitedStates); QSet<QAbstractState*> statesForDefaultEntry; - QList<QAbstractState*> enteredStates = computeEntrySet(enabledTransitions, statesForDefaultEntry); + QList<QAbstractState*> enteredStates = computeEntrySet(enabledTransitions, statesForDefaultEntry, cache); #ifdef QSTATEMACHINE_DEBUG qDebug() << q_func() << ": computed exit set:" << exitedStates; @@ -598,42 +736,61 @@ function computeExitSet(transitions) statesToExit.add(s) return statesToExit */ -QList<QAbstractState*> QStateMachinePrivate::computeExitSet(const QList<QAbstractTransition*> &enabledTransitions) +QList<QAbstractState*> QStateMachinePrivate::computeExitSet(const QList<QAbstractTransition*> &enabledTransitions, + CalculationCache *cache) { - QList<QAbstractState*> statesToExit_sorted = computeExitSet_Unordered(enabledTransitions).toList(); + Q_ASSERT(cache); + + QList<QAbstractState*> statesToExit_sorted = computeExitSet_Unordered(enabledTransitions, cache).toList(); std::sort(statesToExit_sorted.begin(), statesToExit_sorted.end(), stateExitLessThan); return statesToExit_sorted; } -QSet<QAbstractState*> QStateMachinePrivate::computeExitSet_Unordered(const QList<QAbstractTransition*> &enabledTransitions) +QSet<QAbstractState*> QStateMachinePrivate::computeExitSet_Unordered(const QList<QAbstractTransition*> &enabledTransitions, + CalculationCache *cache) { + Q_ASSERT(cache); + QSet<QAbstractState*> statesToExit; - for (int i = 0; i < enabledTransitions.size(); ++i) { - QAbstractTransition *t = enabledTransitions.at(i); - QList<QAbstractState *> effectiveTargetStates = getEffectiveTargetStates(t).toList(); - QAbstractState *domain = getTransitionDomain(t, effectiveTargetStates); - if (domain == Q_NULLPTR && !t->targetStates().isEmpty()) { - // So we didn't find the least common ancestor for the source and target states of the - // transition. If there were not target states, that would be fine: then the transition - // will fire any events or signals, but not exit the state. - // - // However, there are target states, so it's either a node without a parent (or parent's - // parent, etc), or the state belongs to a different state machine. Either way, this - // makes the state machine invalid. - if (error == QStateMachine::NoError) - setError(QStateMachine::NoCommonAncestorForTransitionError, t->sourceState()); - QList<QAbstractState *> lst = pendingErrorStates.toList(); - lst.prepend(t->sourceState()); - - domain = findLCCA(lst); - Q_ASSERT(domain != 0); - } + foreach (QAbstractTransition *t, enabledTransitions) + statesToExit.unite(computeExitSet_Unordered(t, cache)); + return statesToExit; +} - foreach (QAbstractState* s, configuration) { - if (isDescendant(s, domain)) - statesToExit.insert(s); - } - } +QSet<QAbstractState*> QStateMachinePrivate::computeExitSet_Unordered(QAbstractTransition *t, + CalculationCache *cache) +{ + Q_ASSERT(cache); + + QSet<QAbstractState*> statesToExit; + if (cache->exitSet(t, &statesToExit)) + return statesToExit; + + QList<QAbstractState *> effectiveTargetStates = getEffectiveTargetStates(t, cache); + QAbstractState *domain = getTransitionDomain(t, effectiveTargetStates, cache); + if (domain == Q_NULLPTR && !t->targetStates().isEmpty()) { + // So we didn't find the least common ancestor for the source and target states of the + // transition. If there were not target states, that would be fine: then the transition + // will fire any events or signals, but not exit the state. + // + // However, there are target states, so it's either a node without a parent (or parent's + // parent, etc), or the state belongs to a different state machine. Either way, this + // makes the state machine invalid. + if (error == QStateMachine::NoError) + setError(QStateMachine::NoCommonAncestorForTransitionError, t->sourceState()); + QList<QAbstractState *> lst = pendingErrorStates.toList(); + lst.prepend(t->sourceState()); + + domain = findLCCA(lst); + Q_ASSERT(domain != 0); + } + + foreach (QAbstractState* s, configuration) { + if (isDescendant(s, domain)) + statesToExit.insert(s); + } + + cache->insert(t, statesToExit); return statesToExit; } @@ -695,8 +852,11 @@ void QStateMachinePrivate::executeTransitionContent(QEvent *event, const QList<Q } QList<QAbstractState*> QStateMachinePrivate::computeEntrySet(const QList<QAbstractTransition *> &enabledTransitions, - QSet<QAbstractState *> &statesForDefaultEntry) + QSet<QAbstractState *> &statesForDefaultEntry, + CalculationCache *cache) { + Q_ASSERT(cache); + QSet<QAbstractState*> statesToEnter; if (pendingErrorStates.isEmpty()) { foreach (QAbstractTransition *t, enabledTransitions) { @@ -704,8 +864,8 @@ QList<QAbstractState*> QStateMachinePrivate::computeEntrySet(const QList<QAbstra addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry); } - QList<QAbstractState *> effectiveTargetStates = getEffectiveTargetStates(t).toList(); - QAbstractState *ancestor = getTransitionDomain(t, effectiveTargetStates); + QList<QAbstractState *> effectiveTargetStates = getEffectiveTargetStates(t, cache); + QAbstractState *ancestor = getTransitionDomain(t, effectiveTargetStates, cache); foreach (QAbstractState *s, effectiveTargetStates) { addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry); } @@ -742,33 +902,42 @@ function getTransitionDomain(t) else: return findLCCA([t.source].append(tstates)) */ -QAbstractState *QStateMachinePrivate::getTransitionDomain(QAbstractTransition *t, const QList<QAbstractState *> &effectiveTargetStates) const +QAbstractState *QStateMachinePrivate::getTransitionDomain(QAbstractTransition *t, + const QList<QAbstractState *> &effectiveTargetStates, + CalculationCache *cache) const { + Q_ASSERT(cache); + if (effectiveTargetStates.isEmpty()) return 0; -#if 0 - // Qt only has external transitions, so skip the special case for the internal transitions - if (QState *tSource = t->sourceState()) { - if (isCompound(tSource)) { - bool allDescendants = true; - foreach (QAbstractState *s, effectiveTargetStates) { - if (!isDescendant(s, tSource)) { - allDescendants = false; - break; + QAbstractState *domain = Q_NULLPTR; + if (cache->transitionDomain(t, &domain)) + return domain; + + if (t->transitionType() == QAbstractTransition::InternalTransition) { + if (QState *tSource = t->sourceState()) { + if (isCompound(tSource)) { + bool allDescendants = true; + foreach (QAbstractState *s, effectiveTargetStates) { + if (!isDescendant(s, tSource)) { + allDescendants = false; + break; + } } - } - if (allDescendants) - return tSource; + if (allDescendants) + return tSource; + } } } -#endif QList<QAbstractState *> states(effectiveTargetStates); if (QAbstractState *src = t->sourceState()) states.prepend(src); - return findLCCA(states); + domain = findLCCA(states); + cache->insert(t, domain); + return domain; } void QStateMachinePrivate::enterStates(QEvent *event, const QList<QAbstractState*> &exitedStates_sorted, @@ -1676,6 +1845,8 @@ void QStateMachinePrivate::_q_start() registerMultiThreadedSignalTransitions(); + startupHook(); + #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": starting"; #endif @@ -1683,6 +1854,7 @@ void QStateMachinePrivate::_q_start() processingScheduled = true; // we call _q_process() below QList<QAbstractTransition*> transitions; + CalculationCache calculationCache; QAbstractTransition *initialTransition = createInitialTransition(); transitions.append(initialTransition); @@ -1690,7 +1862,7 @@ void QStateMachinePrivate::_q_start() executeTransitionContent(&nullEvent, transitions); QList<QAbstractState*> exitedStates = QList<QAbstractState*>(); QSet<QAbstractState*> statesForDefaultEntry; - QList<QAbstractState*> enteredStates = computeEntrySet(transitions, statesForDefaultEntry); + QList<QAbstractState*> enteredStates = computeEntrySet(transitions, statesForDefaultEntry, &calculationCache); QHash<RestorableId, QVariant> pendingRestorables; QHash<QAbstractState*, QList<QPropertyAssignment> > assignmentsForEnteredStates = computePropertyAssignments(enteredStates, pendingRestorables); @@ -1745,50 +1917,46 @@ void QStateMachinePrivate::_q_process() break; } QList<QAbstractTransition*> enabledTransitions; + CalculationCache calculationCache; + QEvent *e = new QEvent(QEvent::None); - enabledTransitions = selectTransitions(e); + enabledTransitions = selectTransitions(e, &calculationCache); if (enabledTransitions.isEmpty()) { delete e; e = 0; } - if (enabledTransitions.isEmpty() && ((e = dequeueInternalEvent()) != 0)) { + while (enabledTransitions.isEmpty() && ((e = dequeueInternalEvent()) != 0)) { #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": dequeued internal event" << e << "of type" << e->type(); #endif - enabledTransitions = selectTransitions(e); + enabledTransitions = selectTransitions(e, &calculationCache); if (enabledTransitions.isEmpty()) { delete e; e = 0; } } - if (enabledTransitions.isEmpty()) { - if ((e = dequeueExternalEvent()) != 0) { + while (enabledTransitions.isEmpty() && ((e = dequeueExternalEvent()) != 0)) { #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": dequeued external event" << e << "of type" << e->type(); #endif - enabledTransitions = selectTransitions(e); + enabledTransitions = selectTransitions(e, &calculationCache); if (enabledTransitions.isEmpty()) { delete e; e = 0; } - } else { - if (isInternalEventQueueEmpty()) { - processing = false; - stopProcessingReason = EventQueueEmpty; - } - } } - if (!enabledTransitions.isEmpty()) { - didChange = true; - q->beginMicrostep(e); - microstep(e, enabledTransitions); - q->endMicrostep(e); - } - else { + if (enabledTransitions.isEmpty()) { + processing = false; + stopProcessingReason = EventQueueEmpty; noMicrostep(); #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": no transitions enabled"; #endif + } else { + didChange = true; + q->beginMicrostep(e); + microstep(e, enabledTransitions, &calculationCache); + q->endMicrostep(e); } delete e; } @@ -1976,12 +2144,17 @@ void QStateMachinePrivate::emitStateFinished(QState *forState, QFinalState *guil Q_ASSERT(guiltyState); #ifdef QSTATEMACHINE_DEBUG + Q_Q(QStateMachine); qDebug() << q << ": emitting finished signal for" << forState; #endif QStatePrivate::get(forState)->emitFinished(); } +void QStateMachinePrivate::startupHook() +{ +} + namespace _QStateMachine_Internal{ class GoToStateTransition : public QAbstractTransition @@ -2554,14 +2727,18 @@ void QStateMachine::setRunning(bool running) event queue. Events are processed in the order posted. The state machine takes ownership of the event and deletes it once it has been processed. - You can only post events when the state machine is running. + You can only post events when the state machine is running or when it is starting up. \sa postDelayedEvent() */ void QStateMachine::postEvent(QEvent *event, EventPriority priority) { Q_D(QStateMachine); - if (d->state != QStateMachinePrivate::Running) { + switch (d->state) { + case QStateMachinePrivate::Running: + case QStateMachinePrivate::Starting: + break; + default: qWarning("QStateMachine::postEvent: cannot post event when the state machine is not running"); return; } diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index a66232ee88..426f2732df 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -75,6 +75,7 @@ class QState; class QAbstractAnimation; #endif +struct CalculationCache; class QStateMachine; class Q_CORE_EXPORT QStateMachinePrivate : public QStatePrivate { @@ -124,17 +125,18 @@ public: void clearHistory(); QAbstractTransition *createInitialTransition() const; - void removeConflictingTransitions(QList<QAbstractTransition*> &enabledTransitions); - void microstep(QEvent *event, const QList<QAbstractTransition*> &transitionList); + void removeConflictingTransitions(QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache); + void microstep(QEvent *event, const QList<QAbstractTransition*> &transitionList, CalculationCache *cache); + QList<QAbstractTransition *> selectTransitions(QEvent *event, CalculationCache *cache); virtual void noMicrostep(); virtual void processedPendingEvents(bool didChange); virtual void beginMacrostep(); virtual void endMacrostep(bool didChange); - QList<QAbstractTransition *> selectTransitions(QEvent *event); void exitStates(QEvent *event, const QList<QAbstractState *> &statesToExit_sorted, const QHash<QAbstractState*, QList<QPropertyAssignment> > &assignmentsForEnteredStates); - QList<QAbstractState*> computeExitSet(const QList<QAbstractTransition*> &enabledTransitions); - QSet<QAbstractState*> computeExitSet_Unordered(const QList<QAbstractTransition*> &enabledTransitions); + QList<QAbstractState*> computeExitSet(const QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache); + QSet<QAbstractState*> computeExitSet_Unordered(const QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache); + QSet<QAbstractState*> computeExitSet_Unordered(QAbstractTransition *t, CalculationCache *cache); void executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &transitionList); void enterStates(QEvent *event, const QList<QAbstractState*> &exitedStates_sorted, const QList<QAbstractState*> &statesToEnter_sorted, @@ -145,9 +147,10 @@ public: #endif ); QList<QAbstractState*> computeEntrySet(const QList<QAbstractTransition*> &enabledTransitions, - QSet<QAbstractState*> &statesForDefaultEntry); + QSet<QAbstractState*> &statesForDefaultEntry, CalculationCache *cache); QAbstractState *getTransitionDomain(QAbstractTransition *t, - const QList<QAbstractState *> &effectiveTargetStates) const; + const QList<QAbstractState *> &effectiveTargetStates, + CalculationCache *cache) const; void addDescendantStatesToEnter(QAbstractState *state, QSet<QAbstractState*> &statesToEnter, QSet<QAbstractState*> &statesForDefaultEntry); @@ -199,6 +202,7 @@ public: void cancelAllDelayedEvents(); virtual void emitStateFinished(QState *forState, QFinalState *guiltyState); + virtual void startupHook(); #ifndef QT_NO_PROPERTIES class RestorableId { @@ -206,7 +210,8 @@ public: QObject *obj; QByteArray prop; // two overloads because friends can't have default arguments - friend uint qHash(const RestorableId &key, uint seed) Q_DECL_NOEXCEPT_EXPR(noexcept(std::declval<QByteArray>())) + friend uint qHash(const RestorableId &key, uint seed) + Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(std::declval<QByteArray>()))) { return qHash(qMakePair(key.obj, key.prop), seed); } friend uint qHash(const RestorableId &key) Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(key, 0U))) { return qHash(key, 0U); } |