1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qstatemachine.h"
41#include "qstate.h"
42#include "qstate_p.h"
43#include "qstatemachine_p.h"
44#include "qabstracttransition.h"
45#include "qabstracttransition_p.h"
46#include "qsignaltransition.h"
47#include "qsignaltransition_p.h"
48#include "qsignaleventgenerator_p.h"
49#include "qabstractstate.h"
50#include "qabstractstate_p.h"
51#include "qfinalstate.h"
52#include "qhistorystate.h"
53#include "qhistorystate_p.h"
54#include "private/qobject_p.h"
55#include "private/qthread_p.h"
56
57#if QT_CONFIG(qeventtransition)
58#include "qeventtransition.h"
59#include "qeventtransition_p.h"
60#endif
61
62#if QT_CONFIG(animation)
63#include "qpropertyanimation.h"
64#include "qanimationgroup.h"
65#include <private/qvariantanimation_p.h>
66#endif
67
68#include <QtCore/qmetaobject.h>
69#include <qdebug.h>
70
71#include <algorithm>
72
73QT_BEGIN_NAMESPACE
74
75/*!
76 \class QStateMachine
77 \inmodule QtCore
78 \reentrant
79
80 \brief The QStateMachine class provides a hierarchical finite state machine.
81
82 \since 4.6
83 \ingroup statemachine
84
85 QStateMachine is based on the concepts and notation of
86 \l{http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf}{Statecharts}.
87 QStateMachine is part of \l{The State Machine Framework}.
88
89 A state machine manages a set of states (classes that inherit from
90 QAbstractState) and transitions (descendants of
91 QAbstractTransition) between those states; these states and
92 transitions define a state graph. Once a state graph has been
93 built, the state machine can execute it. QStateMachine's
94 execution algorithm is based on the \l{http://www.w3.org/TR/scxml/}{State Chart XML (SCXML)}
95 algorithm. The framework's \l{The State Machine
96 Framework}{overview} gives several state graphs and the code to
97 build them.
98
99 Use the addState() function to add a top-level state to the state machine.
100 States are removed with the removeState() function. Removing states while
101 the machine is running is discouraged.
102
103 Before the machine can be started, the \l{initialState}{initial
104 state} must be set. The initial state is the state that the
105 machine enters when started. You can then start() the state
106 machine. The started() signal is emitted when the initial state is
107 entered.
108
109 The machine is event driven and keeps its own event loop. Events
110 are posted to the machine through postEvent(). Note that this
111 means that it executes asynchronously, and that it will not
112 progress without a running event loop. You will normally not have
113 to post events to the machine directly as Qt's transitions, e.g.,
114 QEventTransition and its subclasses, handle this. But for custom
115 transitions triggered by events, postEvent() is useful.
116
117 The state machine processes events and takes transitions until a
118 top-level final state is entered; the state machine then emits the
119 finished() signal. You can also stop() the state machine
120 explicitly. The stopped() signal is emitted in this case.
121
122 The following snippet shows a state machine that will finish when a button
123 is clicked:
124
125 \snippet code/src_corelib_statemachine_qstatemachine.cpp simple state machine
126
127 This code example uses QState, which inherits QAbstractState. The
128 QState class provides a state that you can use to set properties
129 and invoke methods on \l{QObject}s when the state is entered or
130 exited. It also contains convenience functions for adding
131 transitions, e.g., \l{QSignalTransition}s as in this example. See
132 the QState class description for further details.
133
134 If an error is encountered, the machine will look for an
135 \l{errorState}{error state}, and if one is available, it will
136 enter this state. The types of errors possible are described by the
137 \l{QStateMachine::}{Error} enum. After the error state is entered,
138 the type of the error can be retrieved with error(). The execution
139 of the state graph will not stop when the error state is entered. If
140 no error state applies to the erroneous state, the machine will stop
141 executing and an error message will be printed to the console.
142
143 \note Important: setting the \l{ChildMode} of a state machine to parallel (\l{ParallelStates})
144 results in an invalid state machine. It can only be set to (or kept as)
145 \l{ExclusiveStates}.
146
147 \sa QAbstractState, QAbstractTransition, QState, {The State Machine Framework}
148*/
149
150/*!
151 \property QStateMachine::errorString
152
153 \brief the error string of this state machine
154*/
155
156/*!
157 \property QStateMachine::globalRestorePolicy
158
159 \brief the restore policy for states of this state machine.
160
161 The default value of this property is
162 QState::DontRestoreProperties.
163*/
164
165/*!
166 \property QStateMachine::running
167 \since 5.4
168
169 \brief the running state of this state machine
170
171 \sa start(), stop(), started(), stopped(), runningChanged()
172*/
173
174#if QT_CONFIG(animation)
175/*!
176 \property QStateMachine::animated
177
178 \brief whether animations are enabled
179
180 The default value of this property is \c true.
181
182 \sa QAbstractTransition::addAnimation()
183*/
184#endif
185
186// #define QSTATEMACHINE_DEBUG
187// #define QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
188
189struct CalculationCache {
190 struct TransitionInfo {
191 QList<QAbstractState*> effectiveTargetStates;
192 QSet<QAbstractState*> exitSet;
193 QAbstractState *transitionDomain;
194
195 bool effectiveTargetStatesIsKnown: 1;
196 bool exitSetIsKnown : 1;
197 bool transitionDomainIsKnown : 1;
198
199 TransitionInfo()
200 : transitionDomain(nullptr)
201 , effectiveTargetStatesIsKnown(false)
202 , exitSetIsKnown(false)
203 , transitionDomainIsKnown(false)
204 {}
205 };
206
207 typedef QHash<QAbstractTransition *, TransitionInfo> TransitionInfoCache;
208 TransitionInfoCache cache;
209
210 bool effectiveTargetStates(QAbstractTransition *t, QList<QAbstractState *> *targets) const
211 {
212 Q_ASSERT(targets);
213
214 TransitionInfoCache::const_iterator cacheIt = cache.find(akey: t);
215 if (cacheIt == cache.end() || !cacheIt->effectiveTargetStatesIsKnown)
216 return false;
217
218 *targets = cacheIt->effectiveTargetStates;
219 return true;
220 }
221
222 void insert(QAbstractTransition *t, const QList<QAbstractState *> &targets)
223 {
224 TransitionInfoCache::iterator cacheIt = cache.find(akey: t);
225 TransitionInfo &ti = cacheIt == cache.end()
226 ? *cache.insert(akey: t, avalue: TransitionInfo())
227 : *cacheIt;
228
229 Q_ASSERT(!ti.effectiveTargetStatesIsKnown);
230 ti.effectiveTargetStates = targets;
231 ti.effectiveTargetStatesIsKnown = true;
232 }
233
234 bool exitSet(QAbstractTransition *t, QSet<QAbstractState *> *exits) const
235 {
236 Q_ASSERT(exits);
237
238 TransitionInfoCache::const_iterator cacheIt = cache.find(akey: t);
239 if (cacheIt == cache.end() || !cacheIt->exitSetIsKnown)
240 return false;
241
242 *exits = cacheIt->exitSet;
243 return true;
244 }
245
246 void insert(QAbstractTransition *t, const QSet<QAbstractState *> &exits)
247 {
248 TransitionInfoCache::iterator cacheIt = cache.find(akey: t);
249 TransitionInfo &ti = cacheIt == cache.end()
250 ? *cache.insert(akey: t, avalue: TransitionInfo())
251 : *cacheIt;
252
253 Q_ASSERT(!ti.exitSetIsKnown);
254 ti.exitSet = exits;
255 ti.exitSetIsKnown = true;
256 }
257
258 bool transitionDomain(QAbstractTransition *t, QAbstractState **domain) const
259 {
260 Q_ASSERT(domain);
261
262 TransitionInfoCache::const_iterator cacheIt = cache.find(akey: t);
263 if (cacheIt == cache.end() || !cacheIt->transitionDomainIsKnown)
264 return false;
265
266 *domain = cacheIt->transitionDomain;
267 return true;
268 }
269
270 void insert(QAbstractTransition *t, QAbstractState *domain)
271 {
272 TransitionInfoCache::iterator cacheIt = cache.find(akey: t);
273 TransitionInfo &ti = cacheIt == cache.end()
274 ? *cache.insert(akey: t, avalue: TransitionInfo())
275 : *cacheIt;
276
277 Q_ASSERT(!ti.transitionDomainIsKnown);
278 ti.transitionDomain = domain;
279 ti.transitionDomainIsKnown = true;
280 }
281};
282
283/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
284
285function isDescendant(state1, state2)
286
287Returns 'true' if state1 is a descendant of state2 (a child, or a child of a child, or a child of a
288child of a child, etc.) Otherwise returns 'false'.
289*/
290static inline bool isDescendant(const QAbstractState *state1, const QAbstractState *state2)
291{
292 Q_ASSERT(state1 != nullptr);
293
294 for (QAbstractState *it = state1->parentState(); it != nullptr; it = it->parentState()) {
295 if (it == state2)
296 return true;
297 }
298
299 return false;
300}
301
302static bool containsDecendantOf(const QSet<QAbstractState *> &states, const QAbstractState *node)
303{
304 for (QAbstractState *s : states)
305 if (isDescendant(state1: s, state2: node))
306 return true;
307
308 return false;
309}
310
311static int descendantDepth(const QAbstractState *state, const QAbstractState *ancestor)
312{
313 int depth = 0;
314 for (const QAbstractState *it = state; it != nullptr; it = it->parentState()) {
315 if (it == ancestor)
316 break;
317 ++depth;
318 }
319 return depth;
320}
321
322/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
323
324function getProperAncestors(state1, state2)
325
326If state2 is null, returns the set of all ancestors of state1 in ancestry order (state1's parent
327followed by the parent's parent, etc. up to an including the <scxml> element). If state2 is
328non-null, returns in ancestry order the set of all ancestors of state1, up to but not including
329state2. (A "proper ancestor" of a state is its parent, or the parent's parent, or the parent's
330parent's parent, etc.))If state2 is state1's parent, or equal to state1, or a descendant of state1,
331this returns the empty set.
332*/
333static QVector<QState*> getProperAncestors(const QAbstractState *state, const QAbstractState *upperBound)
334{
335 Q_ASSERT(state != nullptr);
336 QVector<QState*> result;
337 result.reserve(asize: 16);
338 for (QState *it = state->parentState(); it && it != upperBound; it = it->parentState()) {
339 result.append(t: it);
340 }
341 return result;
342}
343
344/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
345
346function getEffectiveTargetStates(transition)
347
348Returns the states that will be the target when 'transition' is taken, dereferencing any history states.
349
350function getEffectiveTargetStates(transition)
351 targets = new OrderedSet()
352 for s in transition.target
353 if isHistoryState(s):
354 if historyValue[s.id]:
355 targets.union(historyValue[s.id])
356 else:
357 targets.union(getEffectiveTargetStates(s.transition))
358 else:
359 targets.add(s)
360 return targets
361*/
362static QList<QAbstractState *> getEffectiveTargetStates(QAbstractTransition *transition, CalculationCache *cache)
363{
364 Q_ASSERT(cache);
365
366 QList<QAbstractState *> targetsList;
367 if (cache->effectiveTargetStates(t: transition, targets: &targetsList))
368 return targetsList;
369
370 QSet<QAbstractState *> targets;
371 const auto targetStates = transition->targetStates();
372 for (QAbstractState *s : targetStates) {
373 if (QHistoryState *historyState = QStateMachinePrivate::toHistoryState(state: s)) {
374 QList<QAbstractState*> historyConfiguration = QHistoryStatePrivate::get(q: historyState)->configuration;
375 if (!historyConfiguration.isEmpty()) {
376 // There is a saved history, so apply that.
377 targets.unite(other: QSet<QAbstractState *>(historyConfiguration.constBegin(), historyConfiguration.constEnd()));
378 } else if (QAbstractTransition *defaultTransition = historyState->defaultTransition()) {
379 // No saved history, take all default transition targets.
380 const auto &targetStates = defaultTransition->targetStates();
381 targets.unite(other: QSet<QAbstractState *>(targetStates.constBegin(), targetStates.constEnd()));
382 } else {
383 // Woops, we found a history state without a default state. That's not valid!
384 QStateMachinePrivate *m = QStateMachinePrivate::get(q: historyState->machine());
385 m->setError(error: QStateMachine::NoDefaultStateInHistoryStateError, currentContext: historyState);
386 }
387 } else {
388 targets.insert(value: s);
389 }
390 }
391
392 targetsList = targets.values();
393 cache->insert(t: transition, targets: targetsList);
394 return targetsList;
395}
396
397QStateMachinePrivate::QStateMachinePrivate()
398{
399 isMachine = true;
400
401 state = NotRunning;
402 processing = false;
403 processingScheduled = false;
404 stop = false;
405 stopProcessingReason = EventQueueEmpty;
406 error = QStateMachine::NoError;
407 globalRestorePolicy = QState::DontRestoreProperties;
408 signalEventGenerator = nullptr;
409#if QT_CONFIG(animation)
410 animated = true;
411#endif
412}
413
414QStateMachinePrivate::~QStateMachinePrivate()
415{
416 qDeleteAll(c: internalEventQueue);
417 qDeleteAll(c: externalEventQueue);
418
419 for (QHash<int, DelayedEvent>::const_iterator it = delayedEvents.cbegin(), eit = delayedEvents.cend(); it != eit; ++it) {
420 delete it.value().event;
421 }
422}
423
424QState *QStateMachinePrivate::rootState() const
425{
426 return const_cast<QStateMachine*>(q_func());
427}
428
429static QEvent *cloneEvent(QEvent *e)
430{
431 switch (e->type()) {
432 case QEvent::None:
433 return new QEvent(*e);
434 case QEvent::Timer:
435 return new QTimerEvent(*static_cast<QTimerEvent*>(e));
436 default:
437 Q_ASSERT_X(false, "cloneEvent()", "not implemented");
438 break;
439 }
440 return nullptr;
441}
442
443const QStateMachinePrivate::Handler qt_kernel_statemachine_handler = {
444 .cloneEvent: cloneEvent
445};
446
447const QStateMachinePrivate::Handler *QStateMachinePrivate::handler = &qt_kernel_statemachine_handler;
448
449Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler()
450{
451 return &qt_kernel_statemachine_handler;
452}
453
454static int indexOfDescendant(QState *s, QAbstractState *desc)
455{
456 QList<QAbstractState*> childStates = QStatePrivate::get(q: s)->childStates();
457 for (int i = 0; i < childStates.size(); ++i) {
458 QAbstractState *c = childStates.at(i);
459 if ((c == desc) || isDescendant(state1: desc, state2: c)) {
460 return i;
461 }
462 }
463 return -1;
464}
465
466bool QStateMachinePrivate::transitionStateEntryLessThan(QAbstractTransition *t1, QAbstractTransition *t2)
467{
468 QState *s1 = t1->sourceState(), *s2 = t2->sourceState();
469 if (s1 == s2) {
470 QList<QAbstractTransition*> transitions = QStatePrivate::get(q: s1)->transitions();
471 return transitions.indexOf(t: t1) < transitions.indexOf(t: t2);
472 } else if (isDescendant(state1: s1, state2: s2)) {
473 return true;
474 } else if (isDescendant(state1: s2, state2: s1)) {
475 return false;
476 } else {
477 Q_ASSERT(s1->machine() != nullptr);
478 QStateMachinePrivate *mach = QStateMachinePrivate::get(q: s1->machine());
479 QState *lca = mach->findLCA(states: QList<QAbstractState*>() << s1 << s2);
480 Q_ASSERT(lca != nullptr);
481 int s1Depth = descendantDepth(state: s1, ancestor: lca);
482 int s2Depth = descendantDepth(state: s2, ancestor: lca);
483 if (s1Depth == s2Depth)
484 return (indexOfDescendant(s: lca, desc: s1) < indexOfDescendant(s: lca, desc: s2));
485 else
486 return s1Depth > s2Depth;
487 }
488}
489
490bool QStateMachinePrivate::stateEntryLessThan(QAbstractState *s1, QAbstractState *s2)
491{
492 if (s1->parent() == s2->parent()) {
493 return s1->parent()->children().indexOf(t: s1)
494 < s2->parent()->children().indexOf(t: s2);
495 } else if (isDescendant(state1: s1, state2: s2)) {
496 return false;
497 } else if (isDescendant(state1: s2, state2: s1)) {
498 return true;
499 } else {
500 Q_ASSERT(s1->machine() != nullptr);
501 QStateMachinePrivate *mach = QStateMachinePrivate::get(q: s1->machine());
502 QState *lca = mach->findLCA(states: QList<QAbstractState*>() << s1 << s2);
503 Q_ASSERT(lca != nullptr);
504 return (indexOfDescendant(s: lca, desc: s1) < indexOfDescendant(s: lca, desc: s2));
505 }
506}
507
508bool QStateMachinePrivate::stateExitLessThan(QAbstractState *s1, QAbstractState *s2)
509{
510 if (s1->parent() == s2->parent()) {
511 return s2->parent()->children().indexOf(t: s2)
512 < s1->parent()->children().indexOf(t: s1);
513 } else if (isDescendant(state1: s1, state2: s2)) {
514 return true;
515 } else if (isDescendant(state1: s2, state2: s1)) {
516 return false;
517 } else {
518 Q_ASSERT(s1->machine() != nullptr);
519 QStateMachinePrivate *mach = QStateMachinePrivate::get(q: s1->machine());
520 QState *lca = mach->findLCA(states: QList<QAbstractState*>() << s1 << s2);
521 Q_ASSERT(lca != nullptr);
522 return (indexOfDescendant(s: lca, desc: s2) < indexOfDescendant(s: lca, desc: s1));
523 }
524}
525
526QState *QStateMachinePrivate::findLCA(const QList<QAbstractState*> &states, bool onlyCompound)
527{
528 if (states.isEmpty())
529 return nullptr;
530 QVector<QState*> ancestors = getProperAncestors(state: states.at(i: 0), upperBound: rootState()->parentState());
531 for (int i = 0; i < ancestors.size(); ++i) {
532 QState *anc = ancestors.at(i);
533 if (onlyCompound && !isCompound(s: anc))
534 continue;
535
536 bool ok = true;
537 for (int j = states.size() - 1; (j > 0) && ok; --j) {
538 const QAbstractState *s = states.at(i: j);
539 if (!isDescendant(state1: s, state2: anc))
540 ok = false;
541 }
542 if (ok)
543 return anc;
544 }
545
546 // Oops, this should never happen! The state machine itself is a common ancestor of all states,
547 // no matter what. But, for the onlyCompound case: we probably have a state machine whose
548 // childMode is set to parallel, which is illegal. However, we're stuck with it (and with
549 // exposing this invalid/dangerous API to users), so recover in the least horrible way.
550 setError(error: QStateMachine::StateMachineChildModeSetToParallelError, currentContext: q_func());
551 return q_func(); // make the statemachine the LCA/LCCA (which it should have been anyway)
552}
553
554QState *QStateMachinePrivate::findLCCA(const QList<QAbstractState*> &states)
555{
556 return findLCA(states, onlyCompound: true);
557}
558
559QList<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *event, CalculationCache *cache)
560{
561 Q_ASSERT(cache);
562 Q_Q(const QStateMachine);
563
564 QVarLengthArray<QAbstractState *> configuration_sorted;
565 for (QAbstractState *s : qAsConst(t&: configuration)) {
566 if (isAtomic(s))
567 configuration_sorted.append(t: s);
568 }
569 std::sort(first: configuration_sorted.begin(), last: configuration_sorted.end(), comp: stateEntryLessThan);
570
571 QList<QAbstractTransition*> enabledTransitions;
572 const_cast<QStateMachine*>(q)->beginSelectTransitions(event);
573 for (QAbstractState *state : qAsConst(t&: configuration_sorted)) {
574 QVector<QState*> lst = getProperAncestors(state, upperBound: nullptr);
575 if (QState *grp = toStandardState(state))
576 lst.prepend(t: grp);
577 bool found = false;
578 for (int j = 0; (j < lst.size()) && !found; ++j) {
579 QState *s = lst.at(i: j);
580 QList<QAbstractTransition*> transitions = QStatePrivate::get(q: s)->transitions();
581 for (int k = 0; k < transitions.size(); ++k) {
582 QAbstractTransition *t = transitions.at(i: k);
583 if (QAbstractTransitionPrivate::get(q: t)->callEventTest(e: event)) {
584#ifdef QSTATEMACHINE_DEBUG
585 qDebug() << q << ": selecting transition" << t;
586#endif
587 enabledTransitions.append(t);
588 found = true;
589 break;
590 }
591 }
592 }
593 }
594
595 if (!enabledTransitions.isEmpty()) {
596 removeConflictingTransitions(enabledTransitions, cache);
597#ifdef QSTATEMACHINE_DEBUG
598 qDebug() << q << ": enabled transitions after removing conflicts:" << enabledTransitions;
599#endif
600 }
601 const_cast<QStateMachine*>(q)->endSelectTransitions(event);
602 return enabledTransitions;
603}
604
605/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
606
607function removeConflictingTransitions(enabledTransitions):
608 filteredTransitions = new OrderedSet()
609 // toList sorts the transitions in the order of the states that selected them
610 for t1 in enabledTransitions.toList():
611 t1Preempted = false;
612 transitionsToRemove = new OrderedSet()
613 for t2 in filteredTransitions.toList():
614 if computeExitSet([t1]).hasIntersection(computeExitSet([t2])):
615 if isDescendant(t1.source, t2.source):
616 transitionsToRemove.add(t2)
617 else:
618 t1Preempted = true
619 break
620 if not t1Preempted:
621 for t3 in transitionsToRemove.toList():
622 filteredTransitions.delete(t3)
623 filteredTransitions.add(t1)
624
625 return filteredTransitions
626
627Note: the implementation below does not build the transitionsToRemove, but removes them in-place.
628*/
629void QStateMachinePrivate::removeConflictingTransitions(QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache)
630{
631 Q_ASSERT(cache);
632
633 if (enabledTransitions.size() < 2)
634 return; // There is no transition to conflict with.
635
636 QList<QAbstractTransition*> filteredTransitions;
637 filteredTransitions.reserve(alloc: enabledTransitions.size());
638 std::sort(first: enabledTransitions.begin(), last: enabledTransitions.end(), comp: transitionStateEntryLessThan);
639
640 for (QAbstractTransition *t1 : qAsConst(t&: enabledTransitions)) {
641 bool t1Preempted = false;
642 const QSet<QAbstractState*> exitSetT1 = computeExitSet_Unordered(t: t1, cache);
643 QList<QAbstractTransition*>::iterator t2It = filteredTransitions.begin();
644 while (t2It != filteredTransitions.end()) {
645 QAbstractTransition *t2 = *t2It;
646 if (t1 == t2) {
647 // Special case: someone added the same transition object to a state twice. In this
648 // case, t2 (which is already in the list) "preempts" t1.
649 t1Preempted = true;
650 break;
651 }
652
653 QSet<QAbstractState*> exitSetT2 = computeExitSet_Unordered(t: t2, cache);
654 if (!exitSetT1.intersects(other: exitSetT2)) {
655 // No conflict, no cry. Next patient please.
656 ++t2It;
657 } else {
658 // Houston, we have a conflict. Check which transition can be removed.
659 if (isDescendant(state1: t1->sourceState(), state2: t2->sourceState())) {
660 // t1 preempts t2, so we can remove t2
661 t2It = filteredTransitions.erase(it: t2It);
662 } else {
663 // t2 preempts t1, so there's no use in looking further and we don't need to add
664 // t1 to the list.
665 t1Preempted = true;
666 break;
667 }
668 }
669 }
670 if (!t1Preempted)
671 filteredTransitions.append(t: t1);
672 }
673
674 enabledTransitions = filteredTransitions;
675}
676
677void QStateMachinePrivate::microstep(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions,
678 CalculationCache *cache)
679{
680 Q_ASSERT(cache);
681
682#ifdef QSTATEMACHINE_DEBUG
683 qDebug() << q_func() << ": begin microstep( enabledTransitions:" << enabledTransitions << ')';
684 qDebug() << q_func() << ": configuration before exiting states:" << configuration;
685#endif
686 QList<QAbstractState*> exitedStates = computeExitSet(enabledTransitions, cache);
687 QHash<RestorableId, QVariant> pendingRestorables = computePendingRestorables(statesToExit_sorted: exitedStates);
688
689 QSet<QAbstractState*> statesForDefaultEntry;
690 QList<QAbstractState*> enteredStates = computeEntrySet(enabledTransitions, statesForDefaultEntry, cache);
691
692#ifdef QSTATEMACHINE_DEBUG
693 qDebug() << q_func() << ": computed exit set:" << exitedStates;
694 qDebug() << q_func() << ": computed entry set:" << enteredStates;
695#endif
696
697 QHash<QAbstractState*, QVector<QPropertyAssignment> > assignmentsForEnteredStates =
698 computePropertyAssignments(statesToEnter_sorted: enteredStates, pendingRestorables);
699 if (!pendingRestorables.isEmpty()) {
700 // Add "implicit" assignments for restored properties to the first
701 // (outermost) entered state
702 Q_ASSERT(!enteredStates.isEmpty());
703 QAbstractState *s = enteredStates.constFirst();
704 assignmentsForEnteredStates[s] << restorablesToPropertyList(restorables: pendingRestorables);
705 }
706
707 exitStates(event, statesToExit_sorted: exitedStates, assignmentsForEnteredStates);
708#ifdef QSTATEMACHINE_DEBUG
709 qDebug() << q_func() << ": configuration after exiting states:" << configuration;
710#endif
711
712 executeTransitionContent(event, transitionList: enabledTransitions);
713
714#if QT_CONFIG(animation)
715 QList<QAbstractAnimation *> selectedAnimations = selectAnimations(transitionList: enabledTransitions);
716#endif
717
718 enterStates(event, exitedStates_sorted: exitedStates, statesToEnter_sorted: enteredStates, statesForDefaultEntry, propertyAssignmentsForState&: assignmentsForEnteredStates
719#if QT_CONFIG(animation)
720 , selectedAnimations
721#endif
722 );
723#ifdef QSTATEMACHINE_DEBUG
724 qDebug() << q_func() << ": configuration after entering states:" << configuration;
725 qDebug() << q_func() << ": end microstep";
726#endif
727}
728
729/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
730
731procedure computeExitSet(enabledTransitions)
732
733For each transition t in enabledTransitions, if t is targetless then do nothing, else compute the
734transition's domain. (This will be the source state in the case of internal transitions) or the
735least common compound ancestor state of the source state and target states of t (in the case of
736external transitions. Add to the statesToExit set all states in the configuration that are
737descendants of the domain.
738
739function computeExitSet(transitions)
740 statesToExit = new OrderedSet
741 for t in transitions:
742 if (t.target):
743 domain = getTransitionDomain(t)
744 for s in configuration:
745 if isDescendant(s,domain):
746 statesToExit.add(s)
747 return statesToExit
748*/
749QList<QAbstractState*> QStateMachinePrivate::computeExitSet(const QList<QAbstractTransition*> &enabledTransitions,
750 CalculationCache *cache)
751{
752 Q_ASSERT(cache);
753
754 QList<QAbstractState*> statesToExit_sorted = computeExitSet_Unordered(enabledTransitions, cache).values();
755 std::sort(first: statesToExit_sorted.begin(), last: statesToExit_sorted.end(), comp: stateExitLessThan);
756 return statesToExit_sorted;
757}
758
759QSet<QAbstractState*> QStateMachinePrivate::computeExitSet_Unordered(const QList<QAbstractTransition*> &enabledTransitions,
760 CalculationCache *cache)
761{
762 Q_ASSERT(cache);
763
764 QSet<QAbstractState*> statesToExit;
765 for (QAbstractTransition *t : enabledTransitions)
766 statesToExit.unite(other: computeExitSet_Unordered(t, cache));
767 return statesToExit;
768}
769
770QSet<QAbstractState*> QStateMachinePrivate::computeExitSet_Unordered(QAbstractTransition *t,
771 CalculationCache *cache)
772{
773 Q_ASSERT(cache);
774
775 QSet<QAbstractState*> statesToExit;
776 if (cache->exitSet(t, exits: &statesToExit))
777 return statesToExit;
778
779 QList<QAbstractState *> effectiveTargetStates = getEffectiveTargetStates(transition: t, cache);
780 QAbstractState *domain = getTransitionDomain(t, effectiveTargetStates, cache);
781 if (domain == nullptr && !t->targetStates().isEmpty()) {
782 // So we didn't find the least common ancestor for the source and target states of the
783 // transition. If there were not target states, that would be fine: then the transition
784 // will fire any events or signals, but not exit the state.
785 //
786 // However, there are target states, so it's either a node without a parent (or parent's
787 // parent, etc), or the state belongs to a different state machine. Either way, this
788 // makes the state machine invalid.
789 if (error == QStateMachine::NoError)
790 setError(error: QStateMachine::NoCommonAncestorForTransitionError, currentContext: t->sourceState());
791 QList<QAbstractState *> lst = pendingErrorStates.values();
792 lst.prepend(t: t->sourceState());
793
794 domain = findLCCA(states: lst);
795 Q_ASSERT(domain != nullptr);
796 }
797
798 for (QAbstractState* s : qAsConst(t&: configuration)) {
799 if (isDescendant(state1: s, state2: domain))
800 statesToExit.insert(value: s);
801 }
802
803 cache->insert(t, exits: statesToExit);
804 return statesToExit;
805}
806
807void QStateMachinePrivate::exitStates(QEvent *event, const QList<QAbstractState*> &statesToExit_sorted,
808 const QHash<QAbstractState*, QVector<QPropertyAssignment> > &assignmentsForEnteredStates)
809{
810 for (int i = 0; i < statesToExit_sorted.size(); ++i) {
811 QAbstractState *s = statesToExit_sorted.at(i);
812 if (QState *grp = toStandardState(state: s)) {
813 QList<QHistoryState*> hlst = QStatePrivate::get(q: grp)->historyStates();
814 for (int j = 0; j < hlst.size(); ++j) {
815 QHistoryState *h = hlst.at(i: j);
816 QHistoryStatePrivate::get(q: h)->configuration.clear();
817 QSet<QAbstractState*>::const_iterator it;
818 for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
819 QAbstractState *s0 = *it;
820 if (QHistoryStatePrivate::get(q: h)->historyType == QHistoryState::DeepHistory) {
821 if (isAtomic(s: s0) && isDescendant(state1: s0, state2: s))
822 QHistoryStatePrivate::get(q: h)->configuration.append(t: s0);
823 } else if (s0->parentState() == s) {
824 QHistoryStatePrivate::get(q: h)->configuration.append(t: s0);
825 }
826 }
827#ifdef QSTATEMACHINE_DEBUG
828 qDebug() << q_func() << ": recorded" << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) ? "deep" : "shallow")
829 << "history for" << s << "in" << h << ':' << QHistoryStatePrivate::get(h)->configuration;
830#endif
831 }
832 }
833 }
834 for (int i = 0; i < statesToExit_sorted.size(); ++i) {
835 QAbstractState *s = statesToExit_sorted.at(i);
836#ifdef QSTATEMACHINE_DEBUG
837 qDebug() << q_func() << ": exiting" << s;
838#endif
839 QAbstractStatePrivate::get(q: s)->callOnExit(e: event);
840
841#if QT_CONFIG(animation)
842 terminateActiveAnimations(state: s, assignmentsForEnteredStates);
843#else
844 Q_UNUSED(assignmentsForEnteredStates);
845#endif
846
847 configuration.remove(value: s);
848 QAbstractStatePrivate::get(q: s)->emitExited();
849 }
850}
851
852void QStateMachinePrivate::executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
853{
854 for (int i = 0; i < enabledTransitions.size(); ++i) {
855 QAbstractTransition *t = enabledTransitions.at(i);
856#ifdef QSTATEMACHINE_DEBUG
857 qDebug() << q_func() << ": triggering" << t;
858#endif
859 QAbstractTransitionPrivate::get(q: t)->callOnTransition(e: event);
860 QAbstractTransitionPrivate::get(q: t)->emitTriggered();
861 }
862}
863
864QList<QAbstractState*> QStateMachinePrivate::computeEntrySet(const QList<QAbstractTransition *> &enabledTransitions,
865 QSet<QAbstractState *> &statesForDefaultEntry,
866 CalculationCache *cache)
867{
868 Q_ASSERT(cache);
869
870 QSet<QAbstractState*> statesToEnter;
871 if (pendingErrorStates.isEmpty()) {
872 for (QAbstractTransition *t : enabledTransitions) {
873 const auto targetStates = t->targetStates();
874 for (QAbstractState *s : targetStates)
875 addDescendantStatesToEnter(state: s, statesToEnter, statesForDefaultEntry);
876
877 const QList<QAbstractState *> effectiveTargetStates = getEffectiveTargetStates(transition: t, cache);
878 QAbstractState *ancestor = getTransitionDomain(t, effectiveTargetStates, cache);
879 for (QAbstractState *s : effectiveTargetStates)
880 addAncestorStatesToEnter(s, ancestor, statesToEnter, statesForDefaultEntry);
881 }
882 }
883
884 // Did an error occur while selecting transitions? Then we enter the error state.
885 if (!pendingErrorStates.isEmpty()) {
886 statesToEnter.clear();
887 statesToEnter = pendingErrorStates;
888 statesForDefaultEntry = pendingErrorStatesForDefaultEntry;
889 pendingErrorStates.clear();
890 pendingErrorStatesForDefaultEntry.clear();
891 }
892
893 QList<QAbstractState*> statesToEnter_sorted = statesToEnter.values();
894 std::sort(first: statesToEnter_sorted.begin(), last: statesToEnter_sorted.end(), comp: stateEntryLessThan);
895 return statesToEnter_sorted;
896}
897
898/* The algorithm as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
899
900function getTransitionDomain(transition)
901
902Return the compound state such that 1) all states that are exited or entered as a result of taking
903'transition' are descendants of it 2) no descendant of it has this property.
904
905function getTransitionDomain(t)
906 tstates = getEffectiveTargetStates(t)
907 if not tstates:
908 return null
909 elif t.type == "internal" and isCompoundState(t.source) and tstates.every(lambda s: isDescendant(s,t.source)):
910 return t.source
911 else:
912 return findLCCA([t.source].append(tstates))
913*/
914QAbstractState *QStateMachinePrivate::getTransitionDomain(QAbstractTransition *t,
915 const QList<QAbstractState *> &effectiveTargetStates,
916 CalculationCache *cache)
917{
918 Q_ASSERT(cache);
919
920 if (effectiveTargetStates.isEmpty())
921 return nullptr;
922
923 QAbstractState *domain = nullptr;
924 if (cache->transitionDomain(t, domain: &domain))
925 return domain;
926
927 if (t->transitionType() == QAbstractTransition::InternalTransition) {
928 if (QState *tSource = t->sourceState()) {
929 if (isCompound(s: tSource)) {
930 bool allDescendants = true;
931 for (QAbstractState *s : effectiveTargetStates) {
932 if (!isDescendant(state1: s, state2: tSource)) {
933 allDescendants = false;
934 break;
935 }
936 }
937
938 if (allDescendants)
939 return tSource;
940 }
941 }
942 }
943
944 QList<QAbstractState *> states(effectiveTargetStates);
945 if (QAbstractState *src = t->sourceState())
946 states.prepend(t: src);
947 domain = findLCCA(states);
948 cache->insert(t, domain);
949 return domain;
950}
951
952void QStateMachinePrivate::enterStates(QEvent *event, const QList<QAbstractState*> &exitedStates_sorted,
953 const QList<QAbstractState*> &statesToEnter_sorted,
954 const QSet<QAbstractState*> &statesForDefaultEntry,
955 QHash<QAbstractState*, QVector<QPropertyAssignment> > &propertyAssignmentsForState
956#if QT_CONFIG(animation)
957 , const QList<QAbstractAnimation *> &selectedAnimations
958#endif
959 )
960{
961#ifdef QSTATEMACHINE_DEBUG
962 Q_Q(QStateMachine);
963#endif
964 for (int i = 0; i < statesToEnter_sorted.size(); ++i) {
965 QAbstractState *s = statesToEnter_sorted.at(i);
966#ifdef QSTATEMACHINE_DEBUG
967 qDebug() << q << ": entering" << s;
968#endif
969 configuration.insert(value: s);
970 registerTransitions(state: s);
971
972#if QT_CONFIG(animation)
973 initializeAnimations(state: s, selectedAnimations, exitedStates_sorted, assignmentsForEnteredStates&: propertyAssignmentsForState);
974#endif
975
976 // Immediately set the properties that are not animated.
977 {
978 QVector<QPropertyAssignment> assignments = propertyAssignmentsForState.value(akey: s);
979 for (int i = 0; i < assignments.size(); ++i) {
980 const QPropertyAssignment &assn = assignments.at(i);
981 if (globalRestorePolicy == QState::RestoreProperties) {
982 if (assn.explicitlySet) {
983 if (!hasRestorable(state: s, object: assn.object, propertyName: assn.propertyName)) {
984 QVariant value = savedValueForRestorable(exitedStates_sorted, object: assn.object, propertyName: assn.propertyName);
985 unregisterRestorables(states: exitedStates_sorted, object: assn.object, propertyName: assn.propertyName);
986 registerRestorable(state: s, object: assn.object, propertyName: assn.propertyName, value);
987 }
988 } else {
989 // The property is being restored, hence no need to
990 // save the current value. Discard any saved values in
991 // exited states, since those are now stale.
992 unregisterRestorables(states: exitedStates_sorted, object: assn.object, propertyName: assn.propertyName);
993 }
994 }
995 assn.write();
996 }
997 }
998
999 QAbstractStatePrivate::get(q: s)->callOnEntry(e: event);
1000 QAbstractStatePrivate::get(q: s)->emitEntered();
1001
1002 // FIXME:
1003 // See the "initial transitions" comment in addDescendantStatesToEnter first, then implement:
1004// if (statesForDefaultEntry.contains(s)) {
1005// // ### executeContent(s.initial.transition.children())
1006// }
1007 Q_UNUSED(statesForDefaultEntry);
1008
1009 if (QHistoryState *h = toHistoryState(state: s))
1010 QAbstractTransitionPrivate::get(q: h->defaultTransition())->callOnTransition(e: event);
1011
1012 // Emit propertiesAssigned signal if the state has no animated properties.
1013 {
1014 QState *ss = toStandardState(state: s);
1015 if (ss
1016 #if QT_CONFIG(animation)
1017 && !animationsForState.contains(akey: s)
1018 #endif
1019 )
1020 QStatePrivate::get(q: ss)->emitPropertiesAssigned();
1021 }
1022
1023 if (isFinal(s)) {
1024 QState *parent = s->parentState();
1025 if (parent) {
1026 if (parent != rootState()) {
1027 QFinalState *finalState = qobject_cast<QFinalState *>(object: s);
1028 Q_ASSERT(finalState);
1029 emitStateFinished(forState: parent, guiltyState: finalState);
1030 }
1031 QState *grandparent = parent->parentState();
1032 if (grandparent && isParallel(s: grandparent)) {
1033 bool allChildStatesFinal = true;
1034 QList<QAbstractState*> childStates = QStatePrivate::get(q: grandparent)->childStates();
1035 for (int j = 0; j < childStates.size(); ++j) {
1036 QAbstractState *cs = childStates.at(i: j);
1037 if (!isInFinalState(s: cs)) {
1038 allChildStatesFinal = false;
1039 break;
1040 }
1041 }
1042 if (allChildStatesFinal && (grandparent != rootState())) {
1043 QFinalState *finalState = qobject_cast<QFinalState *>(object: s);
1044 Q_ASSERT(finalState);
1045 emitStateFinished(forState: grandparent, guiltyState: finalState);
1046 }
1047 }
1048 }
1049 }
1050 }
1051 {
1052 QSet<QAbstractState*>::const_iterator it;
1053 for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
1054 if (isFinal(s: *it)) {
1055 QState *parent = (*it)->parentState();
1056 if (((parent == rootState())
1057 && (rootState()->childMode() == QState::ExclusiveStates))
1058 || ((parent->parentState() == rootState())
1059 && (rootState()->childMode() == QState::ParallelStates)
1060 && isInFinalState(s: rootState()))) {
1061 processing = false;
1062 stopProcessingReason = Finished;
1063 break;
1064 }
1065 }
1066 }
1067 }
1068// qDebug() << "configuration:" << configuration.toList();
1069}
1070
1071/* The algorithm as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ has a bug. See
1072 * QTBUG-44963 for details. The algorithm here is as described in
1073 * http://www.w3.org/Voice/2013/scxml-irp/SCXML.htm as of Friday March 13, 2015.
1074
1075procedure addDescendantStatesToEnter(state,statesToEnter,statesForDefaultEntry, defaultHistoryContent):
1076 if isHistoryState(state):
1077 if historyValue[state.id]:
1078 for s in historyValue[state.id]:
1079 addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
1080 for s in historyValue[state.id]:
1081 addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
1082 else:
1083 defaultHistoryContent[state.parent.id] = state.transition.content
1084 for s in state.transition.target:
1085 addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
1086 for s in state.transition.target:
1087 addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
1088 else:
1089 statesToEnter.add(state)
1090 if isCompoundState(state):
1091 statesForDefaultEntry.add(state)
1092 for s in state.initial.transition.target:
1093 addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
1094 for s in state.initial.transition.target:
1095 addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
1096 else:
1097 if isParallelState(state):
1098 for child in getChildStates(state):
1099 if not statesToEnter.some(lambda s: isDescendant(s,child)):
1100 addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
1101*/
1102void QStateMachinePrivate::addDescendantStatesToEnter(QAbstractState *state,
1103 QSet<QAbstractState*> &statesToEnter,
1104 QSet<QAbstractState*> &statesForDefaultEntry)
1105{
1106 if (QHistoryState *h = toHistoryState(state)) {
1107 const QList<QAbstractState*> historyConfiguration = QHistoryStatePrivate::get(q: h)->configuration;
1108 if (!historyConfiguration.isEmpty()) {
1109 for (QAbstractState *s : historyConfiguration)
1110 addDescendantStatesToEnter(state: s, statesToEnter, statesForDefaultEntry);
1111 for (QAbstractState *s : historyConfiguration)
1112 addAncestorStatesToEnter(s, ancestor: state->parentState(), statesToEnter, statesForDefaultEntry);
1113
1114#ifdef QSTATEMACHINE_DEBUG
1115 qDebug() << q_func() << ": restoring"
1116 << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) ? "deep" : "shallow")
1117 << "history from" << state << ':' << historyConfiguration;
1118#endif
1119 } else {
1120 QList<QAbstractState*> defaultHistoryContent;
1121 if (QAbstractTransition *t = QHistoryStatePrivate::get(q: h)->defaultTransition)
1122 defaultHistoryContent = t->targetStates();
1123
1124 if (defaultHistoryContent.isEmpty()) {
1125 setError(error: QStateMachine::NoDefaultStateInHistoryStateError, currentContext: h);
1126 } else {
1127 for (QAbstractState *s : qAsConst(t&: defaultHistoryContent))
1128 addDescendantStatesToEnter(state: s, statesToEnter, statesForDefaultEntry);
1129 for (QAbstractState *s : qAsConst(t&: defaultHistoryContent))
1130 addAncestorStatesToEnter(s, ancestor: state->parentState(), statesToEnter, statesForDefaultEntry);
1131#ifdef QSTATEMACHINE_DEBUG
1132 qDebug() << q_func() << ": initial history targets for" << state << ':' << defaultHistoryContent;
1133#endif
1134 }
1135 }
1136 } else {
1137 if (state == rootState()) {
1138 // Error has already been set by exitStates().
1139 Q_ASSERT(error != QStateMachine::NoError);
1140 return;
1141 }
1142 statesToEnter.insert(value: state);
1143 if (isCompound(s: state)) {
1144 statesForDefaultEntry.insert(value: state);
1145 if (QAbstractState *initial = toStandardState(state)->initialState()) {
1146 Q_ASSERT(initial->machine() == q_func());
1147
1148 // FIXME:
1149 // Qt does not support initial transitions (which is a problem for parallel states).
1150 // The way it simulates this for other states, is by having a single initial state.
1151 // See also the FIXME in enterStates.
1152 statesForDefaultEntry.insert(value: initial);
1153
1154 addDescendantStatesToEnter(state: initial, statesToEnter, statesForDefaultEntry);
1155 addAncestorStatesToEnter(s: initial, ancestor: state, statesToEnter, statesForDefaultEntry);
1156 } else {
1157 setError(error: QStateMachine::NoInitialStateError, currentContext: state);
1158 return;
1159 }
1160 } else if (isParallel(s: state)) {
1161 QState *grp = toStandardState(state);
1162 const auto childStates = QStatePrivate::get(q: grp)->childStates();
1163 for (QAbstractState *child : childStates) {
1164 if (!containsDecendantOf(states: statesToEnter, node: child))
1165 addDescendantStatesToEnter(state: child, statesToEnter, statesForDefaultEntry);
1166 }
1167 }
1168 }
1169}
1170
1171
1172/* The algorithm as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
1173
1174procedure addAncestorStatesToEnter(state, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
1175 for anc in getProperAncestors(state,ancestor):
1176 statesToEnter.add(anc)
1177 if isParallelState(anc):
1178 for child in getChildStates(anc):
1179 if not statesToEnter.some(lambda s: isDescendant(s,child)):
1180 addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
1181*/
1182void QStateMachinePrivate::addAncestorStatesToEnter(QAbstractState *s, QAbstractState *ancestor,
1183 QSet<QAbstractState*> &statesToEnter,
1184 QSet<QAbstractState*> &statesForDefaultEntry)
1185{
1186 const auto properAncestors = getProperAncestors(state: s, upperBound: ancestor);
1187 for (QState *anc : properAncestors) {
1188 if (!anc->parentState())
1189 continue;
1190 statesToEnter.insert(value: anc);
1191 if (isParallel(s: anc)) {
1192 const auto childStates = QStatePrivate::get(q: anc)->childStates();
1193 for (QAbstractState *child : childStates) {
1194 if (!containsDecendantOf(states: statesToEnter, node: child))
1195 addDescendantStatesToEnter(state: child, statesToEnter, statesForDefaultEntry);
1196 }
1197 }
1198 }
1199}
1200
1201bool QStateMachinePrivate::isFinal(const QAbstractState *s)
1202{
1203 return s && (QAbstractStatePrivate::get(q: s)->stateType == QAbstractStatePrivate::FinalState);
1204}
1205
1206bool QStateMachinePrivate::isParallel(const QAbstractState *s)
1207{
1208 const QState *ss = toStandardState(state: s);
1209 return ss && (QStatePrivate::get(q: ss)->childMode == QState::ParallelStates);
1210}
1211
1212bool QStateMachinePrivate::isCompound(const QAbstractState *s) const
1213{
1214 const QState *group = toStandardState(state: s);
1215 if (!group)
1216 return false;
1217 bool isMachine = QStatePrivate::get(q: group)->isMachine;
1218 // Don't treat the machine as compound if it's a sub-state of this machine
1219 if (isMachine && (group != rootState()))
1220 return false;
1221 return (!isParallel(s: group) && !QStatePrivate::get(q: group)->childStates().isEmpty());
1222}
1223
1224bool QStateMachinePrivate::isAtomic(const QAbstractState *s) const
1225{
1226 const QState *ss = toStandardState(state: s);
1227 return (ss && QStatePrivate::get(q: ss)->childStates().isEmpty())
1228 || isFinal(s)
1229 // Treat the machine as atomic if it's a sub-state of this machine
1230 || (ss && QStatePrivate::get(q: ss)->isMachine && (ss != rootState()));
1231}
1232
1233QState *QStateMachinePrivate::toStandardState(QAbstractState *state)
1234{
1235 if (state && (QAbstractStatePrivate::get(q: state)->stateType == QAbstractStatePrivate::StandardState))
1236 return static_cast<QState*>(state);
1237 return nullptr;
1238}
1239
1240const QState *QStateMachinePrivate::toStandardState(const QAbstractState *state)
1241{
1242 if (state && (QAbstractStatePrivate::get(q: state)->stateType == QAbstractStatePrivate::StandardState))
1243 return static_cast<const QState*>(state);
1244 return nullptr;
1245}
1246
1247QFinalState *QStateMachinePrivate::toFinalState(QAbstractState *state)
1248{
1249 if (state && (QAbstractStatePrivate::get(q: state)->stateType == QAbstractStatePrivate::FinalState))
1250 return static_cast<QFinalState*>(state);
1251 return nullptr;
1252}
1253
1254QHistoryState *QStateMachinePrivate::toHistoryState(QAbstractState *state)
1255{
1256 if (state && (QAbstractStatePrivate::get(q: state)->stateType == QAbstractStatePrivate::HistoryState))
1257 return static_cast<QHistoryState*>(state);
1258 return nullptr;
1259}
1260
1261bool QStateMachinePrivate::isInFinalState(QAbstractState* s) const
1262{
1263 if (isCompound(s)) {
1264 QState *grp = toStandardState(state: s);
1265 QList<QAbstractState*> lst = QStatePrivate::get(q: grp)->childStates();
1266 for (int i = 0; i < lst.size(); ++i) {
1267 QAbstractState *cs = lst.at(i);
1268 if (isFinal(s: cs) && configuration.contains(value: cs))
1269 return true;
1270 }
1271 return false;
1272 } else if (isParallel(s)) {
1273 QState *grp = toStandardState(state: s);
1274 QList<QAbstractState*> lst = QStatePrivate::get(q: grp)->childStates();
1275 for (int i = 0; i < lst.size(); ++i) {
1276 QAbstractState *cs = lst.at(i);
1277 if (!isInFinalState(s: cs))
1278 return false;
1279 }
1280 return true;
1281 }
1282 else
1283 return false;
1284}
1285
1286#ifndef QT_NO_PROPERTIES
1287
1288/*!
1289 \internal
1290 Returns \c true if the given state has saved the value of the given property,
1291 otherwise returns \c false.
1292*/
1293bool QStateMachinePrivate::hasRestorable(QAbstractState *state, QObject *object,
1294 const QByteArray &propertyName) const
1295{
1296 RestorableId id(object, propertyName);
1297 return registeredRestorablesForState.value(akey: state).contains(akey: id);
1298}
1299
1300/*!
1301 \internal
1302 Returns the value to save for the property identified by \a id.
1303 If an exited state (member of \a exitedStates_sorted) has saved a value for
1304 the property, the saved value from the last (outermost) state that will be
1305 exited is returned (in practice carrying the saved value on to the next
1306 state). Otherwise, the current value of the property is returned.
1307*/
1308QVariant QStateMachinePrivate::savedValueForRestorable(const QList<QAbstractState*> &exitedStates_sorted,
1309 QObject *object, const QByteArray &propertyName) const
1310{
1311#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
1312 qDebug() << q_func() << ": savedValueForRestorable(" << exitedStates_sorted << object << propertyName << ')';
1313#endif
1314 for (int i = exitedStates_sorted.size() - 1; i >= 0; --i) {
1315 QAbstractState *s = exitedStates_sorted.at(i);
1316 QHash<RestorableId, QVariant> restorables = registeredRestorablesForState.value(akey: s);
1317 QHash<RestorableId, QVariant>::const_iterator it = restorables.constFind(akey: RestorableId(object, propertyName));
1318 if (it != restorables.constEnd()) {
1319#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
1320 qDebug() << q_func() << ": using" << it.value() << "from" << s;
1321#endif
1322 return it.value();
1323 }
1324 }
1325#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
1326 qDebug() << q_func() << ": falling back to current value";
1327#endif
1328 return object->property(name: propertyName);
1329}
1330
1331void QStateMachinePrivate::registerRestorable(QAbstractState *state, QObject *object, const QByteArray &propertyName,
1332 const QVariant &value)
1333{
1334#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
1335 qDebug() << q_func() << ": registerRestorable(" << state << object << propertyName << value << ')';
1336#endif
1337 RestorableId id(object, propertyName);
1338 QHash<RestorableId, QVariant> &restorables = registeredRestorablesForState[state];
1339 if (!restorables.contains(akey: id))
1340 restorables.insert(akey: id, avalue: value);
1341#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
1342 else
1343 qDebug() << q_func() << ": (already registered)";
1344#endif
1345}
1346
1347void QStateMachinePrivate::unregisterRestorables(const QList<QAbstractState *> &states, QObject *object,
1348 const QByteArray &propertyName)
1349{
1350#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
1351 qDebug() << q_func() << ": unregisterRestorables(" << states << object << propertyName << ')';
1352#endif
1353 RestorableId id(object, propertyName);
1354 for (int i = 0; i < states.size(); ++i) {
1355 QAbstractState *s = states.at(i);
1356 QHash<QAbstractState*, QHash<RestorableId, QVariant> >::iterator it;
1357 it = registeredRestorablesForState.find(akey: s);
1358 if (it == registeredRestorablesForState.end())
1359 continue;
1360 QHash<RestorableId, QVariant> &restorables = it.value();
1361 const auto it2 = restorables.constFind(akey: id);
1362 if (it2 == restorables.cend())
1363 continue;
1364#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
1365 qDebug() << q_func() << ": unregistered for" << s;
1366#endif
1367 restorables.erase(it: it2);
1368 if (restorables.isEmpty())
1369 registeredRestorablesForState.erase(it);
1370 }
1371}
1372
1373QVector<QPropertyAssignment> QStateMachinePrivate::restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const
1374{
1375 QVector<QPropertyAssignment> result;
1376 QHash<RestorableId, QVariant>::const_iterator it;
1377 for (it = restorables.constBegin(); it != restorables.constEnd(); ++it) {
1378 const RestorableId &id = it.key();
1379 if (!id.object()) {
1380 // Property object was deleted
1381 continue;
1382 }
1383#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
1384 qDebug() << q_func() << ": restoring" << id.object() << id.proertyName() << "to" << it.value();
1385#endif
1386 result.append(t: QPropertyAssignment(id.object(), id.propertyName(), it.value(), /*explicitlySet=*/false));
1387 }
1388 return result;
1389}
1390
1391/*!
1392 \internal
1393 Computes the set of properties whose values should be restored given that
1394 the states \a statesToExit_sorted will be exited.
1395
1396 If a particular (object, propertyName) pair occurs more than once (i.e.,
1397 because nested states are being exited), the value from the last (outermost)
1398 exited state takes precedence.
1399
1400 The result of this function must be filtered according to the explicit
1401 property assignments (QState::assignProperty()) of the entered states
1402 before the property restoration is actually performed; i.e., if an entered
1403 state assigns to a property that would otherwise be restored, that property
1404 should not be restored after all, but the saved value from the exited state
1405 should be remembered by the entered state (see registerRestorable()).
1406*/
1407QHash<QStateMachinePrivate::RestorableId, QVariant> QStateMachinePrivate::computePendingRestorables(
1408 const QList<QAbstractState*> &statesToExit_sorted) const
1409{
1410 QHash<QStateMachinePrivate::RestorableId, QVariant> restorables;
1411 for (int i = statesToExit_sorted.size() - 1; i >= 0; --i) {
1412 QAbstractState *s = statesToExit_sorted.at(i);
1413 QHash<QStateMachinePrivate::RestorableId, QVariant> rs = registeredRestorablesForState.value(akey: s);
1414 QHash<QStateMachinePrivate::RestorableId, QVariant>::const_iterator it;
1415 for (it = rs.constBegin(); it != rs.constEnd(); ++it) {
1416 if (!restorables.contains(akey: it.key()))
1417 restorables.insert(akey: it.key(), avalue: it.value());
1418 }
1419 }
1420 return restorables;
1421}
1422
1423/*!
1424 \internal
1425 Computes the ordered sets of property assignments for the states to be
1426 entered, \a statesToEnter_sorted. Also filters \a pendingRestorables (removes
1427 properties that should not be restored because they are assigned by an
1428 entered state).
1429*/
1430QHash<QAbstractState*, QVector<QPropertyAssignment> > QStateMachinePrivate::computePropertyAssignments(
1431 const QList<QAbstractState*> &statesToEnter_sorted, QHash<RestorableId, QVariant> &pendingRestorables) const
1432{
1433 QHash<QAbstractState*, QVector<QPropertyAssignment> > assignmentsForState;
1434 for (int i = 0; i < statesToEnter_sorted.size(); ++i) {
1435 QState *s = toStandardState(state: statesToEnter_sorted.at(i));
1436 if (!s)
1437 continue;
1438
1439 QVector<QPropertyAssignment> &assignments = QStatePrivate::get(q: s)->propertyAssignments;
1440 for (int j = 0; j < assignments.size(); ++j) {
1441 const QPropertyAssignment &assn = assignments.at(i: j);
1442 if (assn.objectDeleted()) {
1443 assignments.removeAt(i: j--);
1444 } else {
1445 pendingRestorables.remove(akey: RestorableId(assn.object, assn.propertyName));
1446 assignmentsForState[s].append(t: assn);
1447 }
1448 }
1449 }
1450 return assignmentsForState;
1451}
1452
1453#endif // QT_NO_PROPERTIES
1454
1455QAbstractState *QStateMachinePrivate::findErrorState(QAbstractState *context)
1456{
1457 // Find error state recursively in parent hierarchy if not set explicitly for context state
1458 QAbstractState *errorState = nullptr;
1459 if (context != nullptr) {
1460 QState *s = toStandardState(state: context);
1461 if (s != nullptr)
1462 errorState = s->errorState();
1463
1464 if (errorState == nullptr)
1465 errorState = findErrorState(context: context->parentState());
1466 }
1467
1468 return errorState;
1469}
1470
1471void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractState *currentContext)
1472{
1473 Q_Q(QStateMachine);
1474
1475 error = errorCode;
1476 switch (errorCode) {
1477 case QStateMachine::NoInitialStateError:
1478 Q_ASSERT(currentContext != nullptr);
1479
1480 errorString = QStateMachine::tr(s: "Missing initial state in compound state '%1'")
1481 .arg(a: currentContext->objectName());
1482
1483 break;
1484 case QStateMachine::NoDefaultStateInHistoryStateError:
1485 Q_ASSERT(currentContext != nullptr);
1486
1487 errorString = QStateMachine::tr(s: "Missing default state in history state '%1'")
1488 .arg(a: currentContext->objectName());
1489 break;
1490
1491 case QStateMachine::NoCommonAncestorForTransitionError:
1492 Q_ASSERT(currentContext != nullptr);
1493
1494 errorString = QStateMachine::tr(s: "No common ancestor for targets and source of transition from state '%1'")
1495 .arg(a: currentContext->objectName());
1496 break;
1497
1498 case QStateMachine::StateMachineChildModeSetToParallelError:
1499 Q_ASSERT(currentContext != nullptr);
1500
1501 errorString = QStateMachine::tr(s: "Child mode of state machine '%1' is not 'ExclusiveStates'.")
1502 .arg(a: currentContext->objectName());
1503 break;
1504
1505 default:
1506 errorString = QStateMachine::tr(s: "Unknown error");
1507 };
1508
1509 pendingErrorStates.clear();
1510 pendingErrorStatesForDefaultEntry.clear();
1511
1512 QAbstractState *currentErrorState = findErrorState(context: currentContext);
1513
1514 // Avoid infinite loop if the error state itself has an error
1515 if (currentContext == currentErrorState)
1516 currentErrorState = nullptr;
1517
1518 Q_ASSERT(currentErrorState != rootState());
1519
1520 if (currentErrorState != nullptr) {
1521#ifdef QSTATEMACHINE_DEBUG
1522 qDebug() << q << ": entering error state" << currentErrorState << "from" << currentContext;
1523#endif
1524 pendingErrorStates.insert(value: currentErrorState);
1525 addDescendantStatesToEnter(state: currentErrorState, statesToEnter&: pendingErrorStates, statesForDefaultEntry&: pendingErrorStatesForDefaultEntry);
1526 addAncestorStatesToEnter(s: currentErrorState, ancestor: rootState(), statesToEnter&: pendingErrorStates, statesForDefaultEntry&: pendingErrorStatesForDefaultEntry);
1527 pendingErrorStates -= configuration;
1528 } else {
1529 qWarning(msg: "Unrecoverable error detected in running state machine: %ls",
1530 qUtf16Printable(errorString));
1531 q->stop();
1532 }
1533}
1534
1535#if QT_CONFIG(animation)
1536
1537QStateMachinePrivate::InitializeAnimationResult
1538QStateMachinePrivate::initializeAnimation(QAbstractAnimation *abstractAnimation,
1539 const QPropertyAssignment &prop)
1540{
1541 InitializeAnimationResult result;
1542 QAnimationGroup *group = qobject_cast<QAnimationGroup*>(object: abstractAnimation);
1543 if (group) {
1544 for (int i = 0; i < group->animationCount(); ++i) {
1545 QAbstractAnimation *animationChild = group->animationAt(index: i);
1546 const auto ret = initializeAnimation(abstractAnimation: animationChild, prop);
1547 result.handledAnimations << ret.handledAnimations;
1548 result.localResetEndValues << ret.localResetEndValues;
1549 }
1550 } else {
1551 QPropertyAnimation *animation = qobject_cast<QPropertyAnimation *>(object: abstractAnimation);
1552 if (animation != nullptr
1553 && prop.object == animation->targetObject()
1554 && prop.propertyName == animation->propertyName()) {
1555
1556 // Only change end value if it is undefined
1557 if (!animation->endValue().isValid()) {
1558 animation->setEndValue(prop.value);
1559 result.localResetEndValues.append(t: animation);
1560 }
1561 result.handledAnimations.append(t: animation);
1562 }
1563 }
1564 return result;
1565}
1566
1567void QStateMachinePrivate::_q_animationFinished()
1568{
1569 Q_Q(QStateMachine);
1570 QAbstractAnimation *anim = qobject_cast<QAbstractAnimation*>(object: q->sender());
1571 Q_ASSERT(anim != nullptr);
1572 QObject::disconnect(sender: anim, SIGNAL(finished()), receiver: q, SLOT(_q_animationFinished()));
1573 if (resetAnimationEndValues.contains(value: anim)) {
1574 qobject_cast<QVariantAnimation*>(object: anim)->setEndValue(QVariant()); // ### generalize
1575 resetAnimationEndValues.remove(value: anim);
1576 }
1577
1578 QAbstractState *state = stateForAnimation.take(akey: anim);
1579 Q_ASSERT(state != nullptr);
1580
1581#ifndef QT_NO_PROPERTIES
1582 // Set the final property value.
1583 QPropertyAssignment assn = propertyForAnimation.take(akey: anim);
1584 assn.write();
1585 if (!assn.explicitlySet)
1586 unregisterRestorables(states: QList<QAbstractState*>() << state, object: assn.object, propertyName: assn.propertyName);
1587#endif
1588
1589 QHash<QAbstractState*, QList<QAbstractAnimation*> >::iterator it;
1590 it = animationsForState.find(akey: state);
1591 Q_ASSERT(it != animationsForState.end());
1592 QList<QAbstractAnimation*> &animations = it.value();
1593 animations.removeOne(t: anim);
1594 if (animations.isEmpty()) {
1595 animationsForState.erase(it);
1596 QStatePrivate::get(q: toStandardState(state))->emitPropertiesAssigned();
1597 }
1598}
1599
1600QList<QAbstractAnimation *> QStateMachinePrivate::selectAnimations(const QList<QAbstractTransition *> &transitionList) const
1601{
1602 QList<QAbstractAnimation *> selectedAnimations;
1603 if (animated) {
1604 for (int i = 0; i < transitionList.size(); ++i) {
1605 QAbstractTransition *transition = transitionList.at(i);
1606
1607 selectedAnimations << transition->animations();
1608 selectedAnimations << defaultAnimationsForSource.values(akey: transition->sourceState());
1609
1610 QList<QAbstractState *> targetStates = transition->targetStates();
1611 for (int j=0; j<targetStates.size(); ++j)
1612 selectedAnimations << defaultAnimationsForTarget.values(akey: targetStates.at(i: j));
1613 }
1614 selectedAnimations << defaultAnimations;
1615 }
1616 return selectedAnimations;
1617}
1618
1619void QStateMachinePrivate::terminateActiveAnimations(QAbstractState *state,
1620 const QHash<QAbstractState*, QVector<QPropertyAssignment> > &assignmentsForEnteredStates)
1621{
1622 Q_Q(QStateMachine);
1623 QList<QAbstractAnimation*> animations = animationsForState.take(akey: state);
1624 for (int i = 0; i < animations.size(); ++i) {
1625 QAbstractAnimation *anim = animations.at(i);
1626 QObject::disconnect(sender: anim, SIGNAL(finished()), receiver: q, SLOT(_q_animationFinished()));
1627 stateForAnimation.remove(akey: anim);
1628
1629 // Stop the (top-level) animation.
1630 // ### Stopping nested animation has weird behavior.
1631 QAbstractAnimation *topLevelAnim = anim;
1632 while (QAnimationGroup *group = topLevelAnim->group())
1633 topLevelAnim = group;
1634 topLevelAnim->stop();
1635
1636 if (resetAnimationEndValues.contains(value: anim)) {
1637 qobject_cast<QVariantAnimation*>(object: anim)->setEndValue(QVariant()); // ### generalize
1638 resetAnimationEndValues.remove(value: anim);
1639 }
1640 QPropertyAssignment assn = propertyForAnimation.take(akey: anim);
1641 Q_ASSERT(assn.object != nullptr);
1642 // If there is no property assignment that sets this property,
1643 // set the property to its target value.
1644 bool found = false;
1645 QHash<QAbstractState*, QVector<QPropertyAssignment> >::const_iterator it;
1646 for (it = assignmentsForEnteredStates.constBegin(); it != assignmentsForEnteredStates.constEnd(); ++it) {
1647 const QVector<QPropertyAssignment> &assignments = it.value();
1648 for (int j = 0; j < assignments.size(); ++j) {
1649 if (assignments.at(i: j).hasTarget(o: assn.object, pn: assn.propertyName)) {
1650 found = true;
1651 break;
1652 }
1653 }
1654 }
1655 if (!found) {
1656 assn.write();
1657 if (!assn.explicitlySet)
1658 unregisterRestorables(states: QList<QAbstractState*>() << state, object: assn.object, propertyName: assn.propertyName);
1659 }
1660 }
1661}
1662
1663void QStateMachinePrivate::initializeAnimations(QAbstractState *state, const QList<QAbstractAnimation *> &selectedAnimations,
1664 const QList<QAbstractState*> &exitedStates_sorted,
1665 QHash<QAbstractState*, QVector<QPropertyAssignment> > &assignmentsForEnteredStates)
1666{
1667 Q_Q(QStateMachine);
1668 if (!assignmentsForEnteredStates.contains(akey: state))
1669 return;
1670 QVector<QPropertyAssignment> &assignments = assignmentsForEnteredStates[state];
1671 for (int i = 0; i < selectedAnimations.size(); ++i) {
1672 QAbstractAnimation *anim = selectedAnimations.at(i);
1673 QVector<QPropertyAssignment>::iterator it;
1674 for (it = assignments.begin(); it != assignments.end(); ) {
1675 const QPropertyAssignment &assn = *it;
1676 const auto ret = initializeAnimation(abstractAnimation: anim, prop: assn);
1677 if (!ret.handledAnimations.isEmpty()) {
1678 for (int j = 0; j < ret.handledAnimations.size(); ++j) {
1679 QAbstractAnimation *a = ret.handledAnimations.at(i: j);
1680 propertyForAnimation.insert(akey: a, avalue: assn);
1681 stateForAnimation.insert(akey: a, avalue: state);
1682 animationsForState[state].append(t: a);
1683 // ### connect to just the top-level animation?
1684 QObject::connect(sender: a, SIGNAL(finished()), receiver: q, SLOT(_q_animationFinished()), Qt::UniqueConnection);
1685 }
1686 if ((globalRestorePolicy == QState::RestoreProperties)
1687 && !hasRestorable(state, object: assn.object, propertyName: assn.propertyName)) {
1688 QVariant value = savedValueForRestorable(exitedStates_sorted, object: assn.object, propertyName: assn.propertyName);
1689 unregisterRestorables(states: exitedStates_sorted, object: assn.object, propertyName: assn.propertyName);
1690 registerRestorable(state, object: assn.object, propertyName: assn.propertyName, value);
1691 }
1692 it = assignments.erase(pos: it);
1693 } else {
1694 ++it;
1695 }
1696 for (int j = 0; j < ret.localResetEndValues.size(); ++j)
1697 resetAnimationEndValues.insert(value: ret.localResetEndValues.at(i: j));
1698 }
1699 // We require that at least one animation is valid.
1700 // ### generalize
1701 QList<QVariantAnimation*> variantAnims = anim->findChildren<QVariantAnimation*>();
1702 if (QVariantAnimation *va = qobject_cast<QVariantAnimation*>(object: anim))
1703 variantAnims.append(t: va);
1704
1705 bool hasValidEndValue = false;
1706 for (int j = 0; j < variantAnims.size(); ++j) {
1707 if (variantAnims.at(i: j)->endValue().isValid()) {
1708 hasValidEndValue = true;
1709 break;
1710 }
1711 }
1712
1713 if (hasValidEndValue) {
1714 if (anim->state() == QAbstractAnimation::Running) {
1715 // The animation is still running. This can happen if the
1716 // animation is a group, and one of its children just finished,
1717 // and that caused a state to emit its propertiesAssigned() signal, and
1718 // that triggered a transition in the machine.
1719 // Just stop the animation so it is correctly restarted again.
1720 anim->stop();
1721 }
1722 anim->start();
1723 }
1724
1725 if (assignments.isEmpty()) {
1726 assignmentsForEnteredStates.remove(akey: state);
1727 break;
1728 }
1729 }
1730}
1731
1732#endif // animation
1733
1734QAbstractTransition *QStateMachinePrivate::createInitialTransition() const
1735{
1736 class InitialTransition : public QAbstractTransition
1737 {
1738 public:
1739 InitialTransition(const QList<QAbstractState *> &targets)
1740 : QAbstractTransition()
1741 { setTargetStates(targets); }
1742 protected:
1743 bool eventTest(QEvent *) override { return true; }
1744 void onTransition(QEvent *) override {}
1745 };
1746
1747 QState *root = rootState();
1748 Q_ASSERT(root != nullptr);
1749 QList<QAbstractState *> targets;
1750 switch (root->childMode()) {
1751 case QState::ExclusiveStates:
1752 targets.append(t: root->initialState());
1753 break;
1754 case QState::ParallelStates:
1755 targets = QStatePrivate::get(q: root)->childStates();
1756 break;
1757 }
1758 return new InitialTransition(targets);
1759}
1760
1761void QStateMachinePrivate::clearHistory()
1762{
1763 Q_Q(QStateMachine);
1764 QList<QHistoryState*> historyStates = q->findChildren<QHistoryState*>();
1765 for (int i = 0; i < historyStates.size(); ++i) {
1766 QHistoryState *h = historyStates.at(i);
1767 QHistoryStatePrivate::get(q: h)->configuration.clear();
1768 }
1769}
1770
1771/*!
1772 \internal
1773
1774 Registers all signal transitions whose sender object lives in another thread.
1775
1776 Normally, signal transitions are lazily registered (when a state becomes
1777 active). But if the sender is in a different thread, the transition must be
1778 registered early to keep the state machine from "dropping" signals; e.g.,
1779 a second (transition-bound) signal could be emitted on the sender thread
1780 before the state machine gets to process the first signal.
1781*/
1782void QStateMachinePrivate::registerMultiThreadedSignalTransitions()
1783{
1784 Q_Q(QStateMachine);
1785 QList<QSignalTransition*> transitions = rootState()->findChildren<QSignalTransition*>();
1786 for (int i = 0; i < transitions.size(); ++i) {
1787 QSignalTransition *t = transitions.at(i);
1788 if ((t->machine() == q) && t->senderObject() && (t->senderObject()->thread() != q->thread()))
1789 registerSignalTransition(transition: t);
1790 }
1791}
1792
1793void QStateMachinePrivate::_q_start()
1794{
1795 Q_Q(QStateMachine);
1796 Q_ASSERT(state == Starting);
1797 // iterate over a copy, since we emit signals which may cause
1798 // 'configuration' to change, resulting in undefined behavior when
1799 // iterating at the same time:
1800 const auto config = configuration;
1801 for (QAbstractState *state : config) {
1802 QAbstractStatePrivate *abstractStatePrivate = QAbstractStatePrivate::get(q: state);
1803 abstractStatePrivate->active = false;
1804 emit state->activeChanged(active: false);
1805 }
1806 configuration.clear();
1807 qDeleteAll(c: internalEventQueue);
1808 internalEventQueue.clear();
1809 qDeleteAll(c: externalEventQueue);
1810 externalEventQueue.clear();
1811 clearHistory();
1812
1813 registerMultiThreadedSignalTransitions();
1814
1815 startupHook();
1816
1817#ifdef QSTATEMACHINE_DEBUG
1818 qDebug() << q << ": starting";
1819#endif
1820 state = Running;
1821 processingScheduled = true; // we call _q_process() below
1822
1823 QList<QAbstractTransition*> transitions;
1824 CalculationCache calculationCache;
1825 QAbstractTransition *initialTransition = createInitialTransition();
1826 transitions.append(t: initialTransition);
1827
1828 QEvent nullEvent(QEvent::None);
1829 executeTransitionContent(event: &nullEvent, enabledTransitions: transitions);
1830 QList<QAbstractState*> exitedStates = QList<QAbstractState*>();
1831 QSet<QAbstractState*> statesForDefaultEntry;
1832 QList<QAbstractState*> enteredStates = computeEntrySet(enabledTransitions: transitions, statesForDefaultEntry, cache: &calculationCache);
1833 QHash<RestorableId, QVariant> pendingRestorables;
1834 QHash<QAbstractState*, QVector<QPropertyAssignment> > assignmentsForEnteredStates =
1835 computePropertyAssignments(statesToEnter_sorted: enteredStates, pendingRestorables);
1836#if QT_CONFIG(animation)
1837 QList<QAbstractAnimation*> selectedAnimations = selectAnimations(transitionList: transitions);
1838#endif
1839 // enterStates() will set stopProcessingReason to Finished if a final
1840 // state is entered.
1841 stopProcessingReason = EventQueueEmpty;
1842 enterStates(event: &nullEvent, exitedStates_sorted: exitedStates, statesToEnter_sorted: enteredStates, statesForDefaultEntry,
1843 propertyAssignmentsForState&: assignmentsForEnteredStates
1844#if QT_CONFIG(animation)
1845 , selectedAnimations
1846#endif
1847 );
1848 delete initialTransition;
1849
1850#ifdef QSTATEMACHINE_DEBUG
1851 qDebug() << q << ": initial configuration:" << configuration;
1852#endif
1853
1854 emit q->started(QStateMachine::QPrivateSignal());
1855 emit q->runningChanged(running: true);
1856
1857 if (stopProcessingReason == Finished) {
1858 // The state machine immediately reached a final state.
1859 processingScheduled = false;
1860 state = NotRunning;
1861 unregisterAllTransitions();
1862 emitFinished();
1863 emit q->runningChanged(running: false);
1864 exitInterpreter();
1865 } else {
1866 _q_process();
1867 }
1868}
1869
1870void QStateMachinePrivate::_q_process()
1871{
1872 Q_Q(QStateMachine);
1873 Q_ASSERT(state == Running);
1874 Q_ASSERT(!processing);
1875 processing = true;
1876 processingScheduled = false;
1877 beginMacrostep();
1878#ifdef QSTATEMACHINE_DEBUG
1879 qDebug() << q << ": starting the event processing loop";
1880#endif
1881 bool didChange = false;
1882 while (processing) {
1883 if (stop) {
1884 processing = false;
1885 break;
1886 }
1887 QList<QAbstractTransition*> enabledTransitions;
1888 CalculationCache calculationCache;
1889
1890 QEvent *e = new QEvent(QEvent::None);
1891 enabledTransitions = selectTransitions(event: e, cache: &calculationCache);
1892 if (enabledTransitions.isEmpty()) {
1893 delete e;
1894 e = nullptr;
1895 }
1896 while (enabledTransitions.isEmpty() && ((e = dequeueInternalEvent()) != nullptr)) {
1897#ifdef QSTATEMACHINE_DEBUG
1898 qDebug() << q << ": dequeued internal event" << e << "of type" << e->type();
1899#endif
1900 enabledTransitions = selectTransitions(event: e, cache: &calculationCache);
1901 if (enabledTransitions.isEmpty()) {
1902 delete e;
1903 e = nullptr;
1904 }
1905 }
1906 while (enabledTransitions.isEmpty() && ((e = dequeueExternalEvent()) != nullptr)) {
1907#ifdef QSTATEMACHINE_DEBUG
1908 qDebug() << q << ": dequeued external event" << e << "of type" << e->type();
1909#endif
1910 enabledTransitions = selectTransitions(event: e, cache: &calculationCache);
1911 if (enabledTransitions.isEmpty()) {
1912 delete e;
1913 e = nullptr;
1914 }
1915 }
1916 if (enabledTransitions.isEmpty()) {
1917 if (isInternalEventQueueEmpty()) {
1918 processing = false;
1919 stopProcessingReason = EventQueueEmpty;
1920 noMicrostep();
1921#ifdef QSTATEMACHINE_DEBUG
1922 qDebug() << q << ": no transitions enabled";
1923#endif
1924 }
1925 } else {
1926 didChange = true;
1927 q->beginMicrostep(event: e);
1928 microstep(event: e, enabledTransitions, cache: &calculationCache);
1929 q->endMicrostep(event: e);
1930 }
1931 delete e;
1932 }
1933#ifdef QSTATEMACHINE_DEBUG
1934 qDebug() << q << ": finished the event processing loop";
1935#endif
1936 if (stop) {
1937 stop = false;
1938 stopProcessingReason = Stopped;
1939 }
1940
1941 switch (stopProcessingReason) {
1942 case EventQueueEmpty:
1943 processedPendingEvents(didChange);
1944 break;
1945 case Finished:
1946 state = NotRunning;
1947 cancelAllDelayedEvents();
1948 unregisterAllTransitions();
1949 emitFinished();
1950 emit q->runningChanged(running: false);
1951 break;
1952 case Stopped:
1953 state = NotRunning;
1954 cancelAllDelayedEvents();
1955 unregisterAllTransitions();
1956 emit q->stopped(QStateMachine::QPrivateSignal());
1957 emit q->runningChanged(running: false);
1958 break;
1959 }
1960 endMacrostep(didChange);
1961 if (stopProcessingReason == Finished)
1962 exitInterpreter();
1963}
1964
1965void QStateMachinePrivate::_q_startDelayedEventTimer(int id, int delay)
1966{
1967 Q_Q(QStateMachine);
1968 QMutexLocker locker(&delayedEventsMutex);
1969 QHash<int, DelayedEvent>::iterator it = delayedEvents.find(akey: id);
1970 if (it != delayedEvents.end()) {
1971 DelayedEvent &e = it.value();
1972 Q_ASSERT(!e.timerId);
1973 e.timerId = q->startTimer(interval: delay);
1974 if (!e.timerId) {
1975 qWarning(msg: "QStateMachine::postDelayedEvent: failed to start timer (id=%d, delay=%d)", id, delay);
1976 delete e.event;
1977 delayedEvents.erase(it);
1978 delayedEventIdFreeList.release(id);
1979 } else {
1980 timerIdToDelayedEventId.insert(akey: e.timerId, avalue: id);
1981 }
1982 } else {
1983 // It's been cancelled already
1984 delayedEventIdFreeList.release(id);
1985 }
1986}
1987
1988void QStateMachinePrivate::_q_killDelayedEventTimer(int id, int timerId)
1989{
1990 Q_Q(QStateMachine);
1991 q->killTimer(id: timerId);
1992 QMutexLocker locker(&delayedEventsMutex);
1993 delayedEventIdFreeList.release(id);
1994}
1995
1996void QStateMachinePrivate::postInternalEvent(QEvent *e)
1997{
1998 QMutexLocker locker(&internalEventMutex);
1999 internalEventQueue.append(t: e);
2000}
2001
2002void QStateMachinePrivate::postExternalEvent(QEvent *e)
2003{
2004 QMutexLocker locker(&externalEventMutex);
2005 externalEventQueue.append(t: e);
2006}
2007
2008QEvent *QStateMachinePrivate::dequeueInternalEvent()
2009{
2010 QMutexLocker locker(&internalEventMutex);
2011 if (internalEventQueue.isEmpty())
2012 return nullptr;
2013 return internalEventQueue.takeFirst();
2014}
2015
2016QEvent *QStateMachinePrivate::dequeueExternalEvent()
2017{
2018 QMutexLocker locker(&externalEventMutex);
2019 if (externalEventQueue.isEmpty())
2020 return nullptr;
2021 return externalEventQueue.takeFirst();
2022}
2023
2024bool QStateMachinePrivate::isInternalEventQueueEmpty()
2025{
2026 QMutexLocker locker(&internalEventMutex);
2027 return internalEventQueue.isEmpty();
2028}
2029
2030bool QStateMachinePrivate::isExternalEventQueueEmpty()
2031{
2032 QMutexLocker locker(&externalEventMutex);
2033 return externalEventQueue.isEmpty();
2034}
2035
2036void QStateMachinePrivate::processEvents(EventProcessingMode processingMode)
2037{
2038 Q_Q(QStateMachine);
2039 if ((state != Running) || processing || processingScheduled)
2040 return;
2041 switch (processingMode) {
2042 case DirectProcessing:
2043 if (QThread::currentThread() == q->thread()) {
2044 _q_process();
2045 break;
2046 }
2047 // processing must be done in the machine thread, so:
2048 Q_FALLTHROUGH();
2049 case QueuedProcessing:
2050 processingScheduled = true;
2051 QMetaObject::invokeMethod(obj: q, member: "_q_process", type: Qt::QueuedConnection);
2052 break;
2053 }
2054}
2055
2056void QStateMachinePrivate::cancelAllDelayedEvents()
2057{
2058 Q_Q(QStateMachine);
2059 QMutexLocker locker(&delayedEventsMutex);
2060 QHash<int, DelayedEvent>::const_iterator it;
2061 for (it = delayedEvents.constBegin(); it != delayedEvents.constEnd(); ++it) {
2062 const DelayedEvent &e = it.value();
2063 if (e.timerId) {
2064 timerIdToDelayedEventId.remove(akey: e.timerId);
2065 q->killTimer(id: e.timerId);
2066 delayedEventIdFreeList.release(id: it.key());
2067 } else {
2068 // Cancellation will be detected in pending _q_startDelayedEventTimer() call
2069 }
2070 delete e.event;
2071 }
2072 delayedEvents.clear();
2073}
2074
2075/*
2076 This function is called when the state machine is performing no
2077 microstep because no transition is enabled (i.e. an event is ignored).
2078
2079 The default implementation does nothing.
2080*/
2081void QStateMachinePrivate::noMicrostep()
2082{ }
2083
2084/*
2085 This function is called when the state machine has reached a stable
2086 state (no pending events), and has not finished yet.
2087 For each event the state machine receives it is guaranteed that
2088 1) beginMacrostep is called
2089 2) selectTransition is called at least once
2090 3) begin/endMicrostep is called at least once or noMicrostep is called
2091 at least once (possibly both, but at least one)
2092 4) the state machine either enters an infinite loop, or stops (runningChanged(false),
2093 and either finished or stopped are emitted), or processedPendingEvents() is called.
2094 5) if the machine is not in an infinite loop endMacrostep is called
2095 6) when the machine is finished and all processing (like signal emission) is done,
2096 exitInterpreter() is called. (This is the same name as the SCXML specification uses.)
2097
2098 didChange is set to true if at least one microstep was performed, it is possible
2099 that the machine returned to exactly the same state as before, but some transitions
2100 were triggered.
2101
2102 The default implementation does nothing.
2103*/
2104void QStateMachinePrivate::processedPendingEvents(bool didChange)
2105{
2106 Q_UNUSED(didChange);
2107}
2108
2109void QStateMachinePrivate::beginMacrostep()
2110{ }
2111
2112void QStateMachinePrivate::endMacrostep(bool didChange)
2113{
2114 Q_UNUSED(didChange);
2115}
2116
2117void QStateMachinePrivate::exitInterpreter()
2118{
2119}
2120
2121void QStateMachinePrivate::emitStateFinished(QState *forState, QFinalState *guiltyState)
2122{
2123 Q_UNUSED(guiltyState);
2124 Q_ASSERT(guiltyState);
2125
2126#ifdef QSTATEMACHINE_DEBUG
2127 Q_Q(QStateMachine);
2128 qDebug() << q << ": emitting finished signal for" << forState;
2129#endif
2130
2131 QStatePrivate::get(q: forState)->emitFinished();
2132}
2133
2134void QStateMachinePrivate::startupHook()
2135{
2136}
2137
2138namespace _QStateMachine_Internal{
2139
2140class GoToStateTransition : public QAbstractTransition
2141{
2142 Q_OBJECT
2143public:
2144 GoToStateTransition(QAbstractState *target)
2145 : QAbstractTransition()
2146 { setTargetState(target); }
2147protected:
2148 void onTransition(QEvent *) override { deleteLater(); }
2149 bool eventTest(QEvent *) override { return true; }
2150};
2151
2152} // namespace
2153// mingw compiler tries to export QObject::findChild<GoToStateTransition>(),
2154// which doesn't work if its in an anonymous namespace.
2155using namespace _QStateMachine_Internal;
2156/*!
2157 \internal
2158
2159 Causes this state machine to unconditionally transition to the given
2160 \a targetState.
2161
2162 Provides a backdoor for using the state machine "imperatively"; i.e. rather
2163 than defining explicit transitions, you drive the machine's execution by
2164 calling this function. It breaks the whole integrity of the
2165 transition-driven model, but is provided for pragmatic reasons.
2166*/
2167void QStateMachinePrivate::goToState(QAbstractState *targetState)
2168{
2169 if (!targetState) {
2170 qWarning(msg: "QStateMachine::goToState(): cannot go to null state");
2171 return;
2172 }
2173
2174 if (configuration.contains(value: targetState))
2175 return;
2176
2177 Q_ASSERT(state == Running);
2178 QState *sourceState = nullptr;
2179 QSet<QAbstractState*>::const_iterator it;
2180 for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
2181 sourceState = toStandardState(state: *it);
2182 if (sourceState != nullptr)
2183 break;
2184 }
2185
2186 Q_ASSERT(sourceState != nullptr);
2187 // Reuse previous GoToStateTransition in case of several calls to
2188 // goToState() in a row.
2189 GoToStateTransition *trans = sourceState->findChild<GoToStateTransition*>();
2190 if (!trans) {
2191 trans = new GoToStateTransition(targetState);
2192 sourceState->addTransition(transition: trans);
2193 } else {
2194 trans->setTargetState(targetState);
2195 }
2196
2197 processEvents(processingMode: QueuedProcessing);
2198}
2199
2200void QStateMachinePrivate::registerTransitions(QAbstractState *state)
2201{
2202 QState *group = toStandardState(state);
2203 if (!group)
2204 return;
2205 QList<QAbstractTransition*> transitions = QStatePrivate::get(q: group)->transitions();
2206 for (int i = 0; i < transitions.size(); ++i) {
2207 QAbstractTransition *t = transitions.at(i);
2208 registerTransition(transition: t);
2209 }
2210}
2211
2212void QStateMachinePrivate::maybeRegisterTransition(QAbstractTransition *transition)
2213{
2214 if (QSignalTransition *st = qobject_cast<QSignalTransition*>(object: transition)) {
2215 maybeRegisterSignalTransition(transition: st);
2216 }
2217#if QT_CONFIG(qeventtransition)
2218 else if (QEventTransition *et = qobject_cast<QEventTransition*>(object: transition)) {
2219 maybeRegisterEventTransition(transition: et);
2220 }
2221#endif
2222}
2223
2224void QStateMachinePrivate::registerTransition(QAbstractTransition *transition)
2225{
2226 if (QSignalTransition *st = qobject_cast<QSignalTransition*>(object: transition)) {
2227 registerSignalTransition(transition: st);
2228 }
2229#if QT_CONFIG(qeventtransition)
2230 else if (QEventTransition *oet = qobject_cast<QEventTransition*>(object: transition)) {
2231 registerEventTransition(transition: oet);
2232 }
2233#endif
2234}
2235
2236void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition)
2237{
2238 if (QSignalTransition *st = qobject_cast<QSignalTransition*>(object: transition)) {
2239 unregisterSignalTransition(transition: st);
2240 }
2241#if QT_CONFIG(qeventtransition)
2242 else if (QEventTransition *oet = qobject_cast<QEventTransition*>(object: transition)) {
2243 unregisterEventTransition(transition: oet);
2244 }
2245#endif
2246}
2247
2248void QStateMachinePrivate::maybeRegisterSignalTransition(QSignalTransition *transition)
2249{
2250 Q_Q(QStateMachine);
2251 if ((state == Running) && (configuration.contains(value: transition->sourceState())
2252 || (transition->senderObject() && (transition->senderObject()->thread() != q->thread())))) {
2253 registerSignalTransition(transition);
2254 }
2255}
2256
2257void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transition)
2258{
2259 Q_Q(QStateMachine);
2260 if (QSignalTransitionPrivate::get(q: transition)->signalIndex != -1)
2261 return; // already registered
2262 const QObject *sender = QSignalTransitionPrivate::get(q: transition)->sender;
2263 if (!sender)
2264 return;
2265 QByteArray signal = QSignalTransitionPrivate::get(q: transition)->signal;
2266 if (signal.isEmpty())
2267 return;
2268 if (signal.startsWith(c: '0'+QSIGNAL_CODE))
2269 signal.remove(index: 0, len: 1);
2270 const QMetaObject *meta = sender->metaObject();
2271 int signalIndex = meta->indexOfSignal(signal);
2272 int originalSignalIndex = signalIndex;
2273 if (signalIndex == -1) {
2274 signalIndex = meta->indexOfSignal(signal: QMetaObject::normalizedSignature(method: signal));
2275 if (signalIndex == -1) {
2276 qWarning(msg: "QSignalTransition: no such signal: %s::%s",
2277 meta->className(), signal.constData());
2278 return;
2279 }
2280 originalSignalIndex = signalIndex;
2281 }
2282 // The signal index we actually want to connect to is the one
2283 // that is going to be sent, i.e. the non-cloned original index.
2284 while (meta->method(index: signalIndex).attributes() & QMetaMethod::Cloned)
2285 --signalIndex;
2286
2287 connectionsMutex.lock();
2288 QVector<int> &connectedSignalIndexes = connections[sender];
2289 if (connectedSignalIndexes.size() <= signalIndex)
2290 connectedSignalIndexes.resize(asize: signalIndex+1);
2291 if (connectedSignalIndexes.at(i: signalIndex) == 0) {
2292 if (!signalEventGenerator)
2293 signalEventGenerator = new QSignalEventGenerator(q);
2294 static const int generatorMethodOffset = QSignalEventGenerator::staticMetaObject.methodOffset();
2295 bool ok = QMetaObject::connect(sender, signal_index: signalIndex, receiver: signalEventGenerator, method_index: generatorMethodOffset);
2296 if (!ok) {
2297#ifdef QSTATEMACHINE_DEBUG
2298 qDebug() << q << ": FAILED to add signal transition from" << transition->sourceState()
2299 << ": ( sender =" << sender << ", signal =" << signal
2300 << ", targets =" << transition->targetStates() << ')';
2301#endif
2302 return;
2303 }
2304 }
2305 ++connectedSignalIndexes[signalIndex];
2306 connectionsMutex.unlock();
2307
2308 QSignalTransitionPrivate::get(q: transition)->signalIndex = signalIndex;
2309 QSignalTransitionPrivate::get(q: transition)->originalSignalIndex = originalSignalIndex;
2310#ifdef QSTATEMACHINE_DEBUG
2311 qDebug() << q << ": added signal transition from" << transition->sourceState()
2312 << ": ( sender =" << sender << ", signal =" << signal
2313 << ", targets =" << transition->targetStates() << ')';
2314#endif
2315}
2316
2317void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transition)
2318{
2319 int signalIndex = QSignalTransitionPrivate::get(q: transition)->signalIndex;
2320 if (signalIndex == -1)
2321 return; // not registered
2322 const QObject *sender = QSignalTransitionPrivate::get(q: transition)->sender;
2323 QSignalTransitionPrivate::get(q: transition)->signalIndex = -1;
2324
2325 connectionsMutex.lock();
2326 QVector<int> &connectedSignalIndexes = connections[sender];
2327 Q_ASSERT(connectedSignalIndexes.size() > signalIndex);
2328 Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0);
2329 if (--connectedSignalIndexes[signalIndex] == 0) {
2330 Q_ASSERT(signalEventGenerator != nullptr);
2331 static const int generatorMethodOffset = QSignalEventGenerator::staticMetaObject.methodOffset();
2332 QMetaObject::disconnect(sender, signal_index: signalIndex, receiver: signalEventGenerator, method_index: generatorMethodOffset);
2333 int sum = 0;
2334 for (int i = 0; i < connectedSignalIndexes.size(); ++i)
2335 sum += connectedSignalIndexes.at(i);
2336 if (sum == 0)
2337 connections.remove(akey: sender);
2338 }
2339 connectionsMutex.unlock();
2340}
2341
2342void QStateMachinePrivate::unregisterAllTransitions()
2343{
2344 Q_Q(QStateMachine);
2345 {
2346 QList<QSignalTransition*> transitions = rootState()->findChildren<QSignalTransition*>();
2347 for (int i = 0; i < transitions.size(); ++i) {
2348 QSignalTransition *t = transitions.at(i);
2349 if (t->machine() == q)
2350 unregisterSignalTransition(transition: t);
2351 }
2352 }
2353#if QT_CONFIG(qeventtransition)
2354 {
2355 QList<QEventTransition*> transitions = rootState()->findChildren<QEventTransition*>();
2356 for (int i = 0; i < transitions.size(); ++i) {
2357 QEventTransition *t = transitions.at(i);
2358 if (t->machine() == q)
2359 unregisterEventTransition(transition: t);
2360 }
2361 }
2362#endif
2363}
2364
2365#if QT_CONFIG(qeventtransition)
2366void QStateMachinePrivate::maybeRegisterEventTransition(QEventTransition *transition)
2367{
2368 if ((state == Running) && configuration.contains(value: transition->sourceState()))
2369 registerEventTransition(transition);
2370}
2371
2372void QStateMachinePrivate::registerEventTransition(QEventTransition *transition)
2373{
2374 Q_Q(QStateMachine);
2375 if (QEventTransitionPrivate::get(q: transition)->registered)
2376 return;
2377 if (transition->eventType() >= QEvent::User) {
2378 qWarning(msg: "QObject event transitions are not supported for custom types");
2379 return;
2380 }
2381 QObject *object = QEventTransitionPrivate::get(q: transition)->object;
2382 if (!object)
2383 return;
2384 QObjectPrivate *od = QObjectPrivate::get(o: object);
2385 if (!od->extraData || !od->extraData->eventFilters.contains(t: q))
2386 object->installEventFilter(filterObj: q);
2387 ++qobjectEvents[object][transition->eventType()];
2388 QEventTransitionPrivate::get(q: transition)->registered = true;
2389#ifdef QSTATEMACHINE_DEBUG
2390 qDebug() << q << ": added event transition from" << transition->sourceState()
2391 << ": ( object =" << object << ", event =" << transition->eventType()
2392 << ", targets =" << transition->targetStates() << ')';
2393#endif
2394}
2395
2396void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transition)
2397{
2398 Q_Q(QStateMachine);
2399 if (!QEventTransitionPrivate::get(q: transition)->registered)
2400 return;
2401 QObject *object = QEventTransitionPrivate::get(q: transition)->object;
2402 QHash<QEvent::Type, int> &events = qobjectEvents[object];
2403 Q_ASSERT(events.value(transition->eventType()) > 0);
2404 if (--events[transition->eventType()] == 0) {
2405 events.remove(akey: transition->eventType());
2406 int sum = 0;
2407 QHash<QEvent::Type, int>::const_iterator it;
2408 for (it = events.constBegin(); it != events.constEnd(); ++it)
2409 sum += it.value();
2410 if (sum == 0) {
2411 qobjectEvents.remove(akey: object);
2412 object->removeEventFilter(obj: q);
2413 }
2414 }
2415 QEventTransitionPrivate::get(q: transition)->registered = false;
2416}
2417
2418void QStateMachinePrivate::handleFilteredEvent(QObject *watched, QEvent *event)
2419{
2420 if (qobjectEvents.value(akey: watched).contains(akey: event->type())) {
2421 postInternalEvent(e: new QStateMachine::WrappedEvent(watched, handler->cloneEvent(event)));
2422 processEvents(processingMode: DirectProcessing);
2423 }
2424}
2425#endif
2426
2427void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalIndex,
2428 void **argv)
2429{
2430#ifndef QT_NO_DEBUG
2431 connectionsMutex.lock();
2432 Q_ASSERT(connections[sender].at(signalIndex) != 0);
2433 connectionsMutex.unlock();
2434#endif
2435 const QMetaObject *meta = sender->metaObject();
2436 QMetaMethod method = meta->method(index: signalIndex);
2437 int argc = method.parameterCount();
2438 QList<QVariant> vargs;
2439 vargs.reserve(alloc: argc);
2440 for (int i = 0; i < argc; ++i) {
2441 int type = method.parameterType(index: i);
2442 vargs.append(t: QVariant(type, argv[i+1]));
2443 }
2444
2445#ifdef QSTATEMACHINE_DEBUG
2446 qDebug() << q_func() << ": sending signal event ( sender =" << sender
2447 << ", signal =" << method.methodSignature().constData() << ')';
2448#endif
2449 postInternalEvent(e: new QStateMachine::SignalEvent(sender, signalIndex, vargs));
2450 processEvents(processingMode: DirectProcessing);
2451}
2452
2453/*!
2454 Constructs a new state machine with the given \a parent.
2455*/
2456QStateMachine::QStateMachine(QObject *parent)
2457 : QState(*new QStateMachinePrivate, /*parentState=*/nullptr)
2458{
2459 // Can't pass the parent to the QState constructor, as it expects a QState
2460 // But this works as expected regardless of whether parent is a QState or not
2461 setParent(parent);
2462}
2463
2464/*!
2465 \since 5.0
2466 \deprecated
2467
2468 Constructs a new state machine with the given \a childMode
2469 and \a parent.
2470
2471 \warning Do not set the \a childMode to anything else than \l{ExclusiveStates}, otherwise the
2472 state machine is invalid, and might work incorrectly.
2473*/
2474QStateMachine::QStateMachine(QState::ChildMode childMode, QObject *parent)
2475 : QState(*new QStateMachinePrivate, /*parentState=*/nullptr)
2476{
2477 Q_D(QStateMachine);
2478 d->childMode = childMode;
2479 setParent(parent); // See comment in constructor above
2480
2481 if (childMode != ExclusiveStates) {
2482 //### FIXME for Qt6: remove this constructor completely, and hide the childMode property.
2483 // Yes, the StateMachine itself is conceptually a state, but it should only expose a limited
2484 // number of properties. The execution algorithm (in the URL below) treats a state machine
2485 // as a state, but from an API point of view, it's questionable if the QStateMachine should
2486 // inherit from QState.
2487 //
2488 // See function findLCCA in https://www.w3.org/TR/2014/WD-scxml-20140529/#AlgorithmforSCXMLInterpretation
2489 // to see where setting childMode to parallel will break down.
2490 qWarning() << "Invalid childMode for QStateMachine" << this;
2491 }
2492}
2493
2494/*!
2495 \internal
2496*/
2497QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent)
2498 : QState(dd, /*parentState=*/nullptr)
2499{
2500 setParent(parent);
2501}
2502
2503/*!
2504 Destroys this state machine.
2505*/
2506QStateMachine::~QStateMachine()
2507{
2508}
2509
2510/*!
2511 \enum QStateMachine::EventPriority
2512
2513 This enum type specifies the priority of an event posted to the state
2514 machine using postEvent().
2515
2516 Events of high priority are processed before events of normal priority.
2517
2518 \value NormalPriority The event has normal priority.
2519 \value HighPriority The event has high priority.
2520*/
2521
2522/*! \enum QStateMachine::Error
2523
2524 This enum type defines errors that can occur in the state machine at run time. When the state
2525 machine encounters an unrecoverable error at run time, it will set the error code returned
2526 by error(), the error message returned by errorString(), and enter an error state based on
2527 the context of the error.
2528
2529 \value NoError No error has occurred.
2530 \value NoInitialStateError The machine has entered a QState with children which does not have an
2531 initial state set. The context of this error is the state which is missing an initial
2532 state.
2533 \value NoDefaultStateInHistoryStateError The machine has entered a QHistoryState which does not have
2534 a default state set. The context of this error is the QHistoryState which is missing a
2535 default state.
2536 \value NoCommonAncestorForTransitionError The machine has selected a transition whose source
2537 and targets are not part of the same tree of states, and thus are not part of the same
2538 state machine. Commonly, this could mean that one of the states has not been given
2539 any parent or added to any machine. The context of this error is the source state of
2540 the transition.
2541 \value StateMachineChildModeSetToParallelError The machine's \l childMode
2542 property was set to \l{QState::ParallelStates}. This is illegal.
2543 Only states may be declared as parallel, not the state machine
2544 itself. This enum value was added in Qt 5.14.
2545
2546 \sa setErrorState()
2547*/
2548
2549/*!
2550 Returns the error code of the last error that occurred in the state machine.
2551*/
2552QStateMachine::Error QStateMachine::error() const
2553{
2554 Q_D(const QStateMachine);
2555 return d->error;
2556}
2557
2558/*!
2559 Returns the error string of the last error that occurred in the state machine.
2560*/
2561QString QStateMachine::errorString() const
2562{
2563 Q_D(const QStateMachine);
2564 return d->errorString;
2565}
2566
2567/*!
2568 Clears the error string and error code of the state machine.
2569*/
2570void QStateMachine::clearError()
2571{
2572 Q_D(QStateMachine);
2573 d->errorString.clear();
2574 d->error = NoError;
2575}
2576
2577/*!
2578 Returns the restore policy of the state machine.
2579
2580 \sa setGlobalRestorePolicy()
2581*/
2582QState::RestorePolicy QStateMachine::globalRestorePolicy() const
2583{
2584 Q_D(const QStateMachine);
2585 return d->globalRestorePolicy;
2586}
2587
2588/*!
2589 Sets the restore policy of the state machine to \a restorePolicy. The default
2590 restore policy is QState::DontRestoreProperties.
2591
2592 \sa globalRestorePolicy()
2593*/
2594void QStateMachine::setGlobalRestorePolicy(QState::RestorePolicy restorePolicy)
2595{
2596 Q_D(QStateMachine);
2597 d->globalRestorePolicy = restorePolicy;
2598}
2599
2600/*!
2601 Adds the given \a state to this state machine. The state becomes a top-level
2602 state and the state machine takes ownership of the state.
2603
2604 If the state is already in a different machine, it will first be removed
2605 from its old machine, and then added to this machine.
2606
2607 \sa removeState(), setInitialState()
2608*/
2609void QStateMachine::addState(QAbstractState *state)
2610{
2611 if (!state) {
2612 qWarning(msg: "QStateMachine::addState: cannot add null state");
2613 return;
2614 }
2615 if (QAbstractStatePrivate::get(q: state)->machine() == this) {
2616 qWarning(msg: "QStateMachine::addState: state has already been added to this machine");
2617 return;
2618 }
2619 state->setParent(this);
2620}
2621
2622/*!
2623 Removes the given \a state from this state machine. The state machine
2624 releases ownership of the state.
2625
2626 \sa addState()
2627*/
2628void QStateMachine::removeState(QAbstractState *state)
2629{
2630 if (!state) {
2631 qWarning(msg: "QStateMachine::removeState: cannot remove null state");
2632 return;
2633 }
2634 if (QAbstractStatePrivate::get(q: state)->machine() != this) {
2635 qWarning(msg: "QStateMachine::removeState: state %p's machine (%p)"
2636 " is different from this machine (%p)",
2637 state, QAbstractStatePrivate::get(q: state)->machine(), this);
2638 return;
2639 }
2640 state->setParent(nullptr);
2641}
2642
2643bool QStateMachine::isRunning() const
2644{
2645 Q_D(const QStateMachine);
2646 return (d->state == QStateMachinePrivate::Running);
2647}
2648
2649/*!
2650 Starts this state machine. The machine will reset its configuration and
2651 transition to the initial state. When a final top-level state (QFinalState)
2652 is entered, the machine will emit the finished() signal.
2653
2654 \note A state machine will not run without a running event loop, such as
2655 the main application event loop started with QCoreApplication::exec() or
2656 QApplication::exec().
2657
2658 \sa started(), finished(), stop(), initialState(), setRunning()
2659*/
2660void QStateMachine::start()
2661{
2662 Q_D(QStateMachine);
2663
2664 if ((childMode() == QState::ExclusiveStates) && (initialState() == nullptr)) {
2665 qWarning(msg: "QStateMachine::start: No initial state set for machine. Refusing to start.");
2666 return;
2667 }
2668
2669 switch (d->state) {
2670 case QStateMachinePrivate::NotRunning:
2671 d->state = QStateMachinePrivate::Starting;
2672 QMetaObject::invokeMethod(obj: this, member: "_q_start", type: Qt::QueuedConnection);
2673 break;
2674 case QStateMachinePrivate::Starting:
2675 break;
2676 case QStateMachinePrivate::Running:
2677 qWarning(msg: "QStateMachine::start(): already running");
2678 break;
2679 }
2680}
2681
2682/*!
2683 Stops this state machine. The state machine will stop processing events and
2684 then emit the stopped() signal.
2685
2686 \sa stopped(), start(), setRunning()
2687*/
2688void QStateMachine::stop()
2689{
2690 Q_D(QStateMachine);
2691 switch (d->state) {
2692 case QStateMachinePrivate::NotRunning:
2693 break;
2694 case QStateMachinePrivate::Starting:
2695 // the machine will exit as soon as it enters the event processing loop
2696 d->stop = true;
2697 break;
2698 case QStateMachinePrivate::Running:
2699 d->stop = true;
2700 d->processEvents(processingMode: QStateMachinePrivate::QueuedProcessing);
2701 break;
2702 }
2703}
2704
2705void QStateMachine::setRunning(bool running)
2706{
2707 if (running)
2708 start();
2709 else
2710 stop();
2711}
2712
2713/*!
2714 \threadsafe
2715
2716 Posts the given \a event of the given \a priority for processing by this
2717 state machine.
2718
2719 This function returns immediately. The event is added to the state machine's
2720 event queue. Events are processed in the order posted. The state machine
2721 takes ownership of the event and deletes it once it has been processed.
2722
2723 You can only post events when the state machine is running or when it is starting up.
2724
2725 \sa postDelayedEvent()
2726*/
2727void QStateMachine::postEvent(QEvent *event, EventPriority priority)
2728{
2729 Q_D(QStateMachine);
2730 switch (d->state) {
2731 case QStateMachinePrivate::Running:
2732 case QStateMachinePrivate::Starting:
2733 break;
2734 default:
2735 qWarning(msg: "QStateMachine::postEvent: cannot post event when the state machine is not running");
2736 return;
2737 }
2738 if (!event) {
2739 qWarning(msg: "QStateMachine::postEvent: cannot post null event");
2740 return;
2741 }
2742#ifdef QSTATEMACHINE_DEBUG
2743 qDebug() << this << ": posting event" << event;
2744#endif
2745 switch (priority) {
2746 case NormalPriority:
2747 d->postExternalEvent(e: event);
2748 break;
2749 case HighPriority:
2750 d->postInternalEvent(e: event);
2751 break;
2752 }
2753 d->processEvents(processingMode: QStateMachinePrivate::QueuedProcessing);
2754}
2755
2756/*!
2757 \threadsafe
2758
2759 Posts the given \a event for processing by this state machine, with the
2760 given \a delay in milliseconds. Returns an identifier associated with the
2761 delayed event, or -1 if the event could not be posted.
2762
2763 This function returns immediately. When the delay has expired, the event
2764 will be added to the state machine's event queue for processing. The state
2765 machine takes ownership of the event and deletes it once it has been
2766 processed.
2767
2768 You can only post events when the state machine is running.
2769
2770 \sa cancelDelayedEvent(), postEvent()
2771*/
2772int QStateMachine::postDelayedEvent(QEvent *event, int delay)
2773{
2774 Q_D(QStateMachine);
2775 if (d->state != QStateMachinePrivate::Running) {
2776 qWarning(msg: "QStateMachine::postDelayedEvent: cannot post event when the state machine is not running");
2777 return -1;
2778 }
2779 if (!event) {
2780 qWarning(msg: "QStateMachine::postDelayedEvent: cannot post null event");
2781 return -1;
2782 }
2783 if (delay < 0) {
2784 qWarning(msg: "QStateMachine::postDelayedEvent: delay cannot be negative");
2785 return -1;
2786 }
2787#ifdef QSTATEMACHINE_DEBUG
2788 qDebug() << this << ": posting event" << event << "with delay" << delay;
2789#endif
2790 QMutexLocker locker(&d->delayedEventsMutex);
2791 int id = d->delayedEventIdFreeList.next();
2792 bool inMachineThread = (QThread::currentThread() == thread());
2793 int timerId = inMachineThread ? startTimer(interval: delay) : 0;
2794 if (inMachineThread && !timerId) {
2795 qWarning(msg: "QStateMachine::postDelayedEvent: failed to start timer with interval %d", delay);
2796 d->delayedEventIdFreeList.release(id);
2797 return -1;
2798 }
2799 QStateMachinePrivate::DelayedEvent delayedEvent(event, timerId);
2800 d->delayedEvents.insert(akey: id, avalue: delayedEvent);
2801 if (timerId) {
2802 d->timerIdToDelayedEventId.insert(akey: timerId, avalue: id);
2803 } else {
2804 Q_ASSERT(!inMachineThread);
2805 QMetaObject::invokeMethod(obj: this, member: "_q_startDelayedEventTimer",
2806 type: Qt::QueuedConnection,
2807 Q_ARG(int, id),
2808 Q_ARG(int, delay));
2809 }
2810 return id;
2811}
2812
2813/*!
2814 \threadsafe
2815
2816 Cancels the delayed event identified by the given \a id. The id should be a
2817 value returned by a call to postDelayedEvent(). Returns \c true if the event
2818 was successfully cancelled, otherwise returns \c false.
2819
2820 \sa postDelayedEvent()
2821*/
2822bool QStateMachine::cancelDelayedEvent(int id)
2823{
2824 Q_D(QStateMachine);
2825 if (d->state != QStateMachinePrivate::Running) {
2826 qWarning(msg: "QStateMachine::cancelDelayedEvent: the machine is not running");
2827 return false;
2828 }
2829 QMutexLocker locker(&d->delayedEventsMutex);
2830 QStateMachinePrivate::DelayedEvent e = d->delayedEvents.take(akey: id);
2831 if (!e.event)
2832 return false;
2833 if (e.timerId) {
2834 d->timerIdToDelayedEventId.remove(akey: e.timerId);
2835 bool inMachineThread = (QThread::currentThread() == thread());
2836 if (inMachineThread) {
2837 killTimer(id: e.timerId);
2838 d->delayedEventIdFreeList.release(id);
2839 } else {
2840 QMetaObject::invokeMethod(obj: this, member: "_q_killDelayedEventTimer",
2841 type: Qt::QueuedConnection,
2842 Q_ARG(int, id),
2843 Q_ARG(int, e.timerId));
2844 }
2845 } else {
2846 // Cancellation will be detected in pending _q_startDelayedEventTimer() call
2847 }
2848 delete e.event;
2849 return true;
2850}
2851
2852/*!
2853 Returns the maximal consistent set of states (including parallel and final
2854 states) that this state machine is currently in. If a state \c s is in the
2855 configuration, it is always the case that the parent of \c s is also in
2856 c. Note, however, that the machine itself is not an explicit member of the
2857 configuration.
2858*/
2859QSet<QAbstractState*> QStateMachine::configuration() const
2860{
2861 Q_D(const QStateMachine);
2862 return d->configuration;
2863}
2864
2865/*!
2866 \fn QStateMachine::started()
2867
2868 This signal is emitted when the state machine has entered its initial state
2869 (QStateMachine::initialState).
2870
2871 \sa QStateMachine::finished(), QStateMachine::start()
2872*/
2873
2874/*!
2875 \fn QStateMachine::stopped()
2876
2877 This signal is emitted when the state machine has stopped.
2878
2879 \sa QStateMachine::stop(), QStateMachine::finished()
2880*/
2881
2882/*!
2883 \reimp
2884*/
2885bool QStateMachine::event(QEvent *e)
2886{
2887 Q_D(QStateMachine);
2888 if (e->type() == QEvent::Timer) {
2889 QTimerEvent *te = static_cast<QTimerEvent*>(e);
2890 int tid = te->timerId();
2891 if (d->state != QStateMachinePrivate::Running) {
2892 // This event has been cancelled already
2893 QMutexLocker locker(&d->delayedEventsMutex);
2894 Q_ASSERT(!d->timerIdToDelayedEventId.contains(tid));
2895 return true;
2896 }
2897 d->delayedEventsMutex.lock();
2898 int id = d->timerIdToDelayedEventId.take(akey: tid);
2899 QStateMachinePrivate::DelayedEvent ee = d->delayedEvents.take(akey: id);
2900 if (ee.event != nullptr) {
2901 Q_ASSERT(ee.timerId == tid);
2902 killTimer(id: tid);
2903 d->delayedEventIdFreeList.release(id);
2904 d->delayedEventsMutex.unlock();
2905 d->postExternalEvent(e: ee.event);
2906 d->processEvents(processingMode: QStateMachinePrivate::DirectProcessing);
2907 return true;
2908 } else {
2909 d->delayedEventsMutex.unlock();
2910 }
2911 }
2912 return QState::event(e);
2913}
2914
2915#if QT_CONFIG(qeventtransition)
2916/*!
2917 \reimp
2918*/
2919bool QStateMachine::eventFilter(QObject *watched, QEvent *event)
2920{
2921 Q_D(QStateMachine);
2922 d->handleFilteredEvent(watched, event);
2923 return false;
2924}
2925#endif
2926
2927/*!
2928 \internal
2929
2930 This function is called when the state machine is about to select
2931 transitions based on the given \a event.
2932
2933 The default implementation does nothing.
2934*/
2935void QStateMachine::beginSelectTransitions(QEvent *event)
2936{
2937 Q_UNUSED(event);
2938}
2939
2940/*!
2941 \internal
2942
2943 This function is called when the state machine has finished selecting
2944 transitions based on the given \a event.
2945
2946 The default implementation does nothing.
2947*/
2948void QStateMachine::endSelectTransitions(QEvent *event)
2949{
2950 Q_UNUSED(event);
2951}
2952
2953/*!
2954 \internal
2955
2956 This function is called when the state machine is about to do a microstep.
2957
2958 The default implementation does nothing.
2959*/
2960void QStateMachine::beginMicrostep(QEvent *event)
2961{
2962 Q_UNUSED(event);
2963}
2964
2965/*!
2966 \internal
2967
2968 This function is called when the state machine has finished doing a
2969 microstep.
2970
2971 The default implementation does nothing.
2972*/
2973void QStateMachine::endMicrostep(QEvent *event)
2974{
2975 Q_UNUSED(event);
2976}
2977
2978/*!
2979 \reimp
2980 This function will call start() to start the state machine.
2981*/
2982void QStateMachine::onEntry(QEvent *event)
2983{
2984 start();
2985 QState::onEntry(event);
2986}
2987
2988/*!
2989 \reimp
2990 This function will call stop() to stop the state machine and
2991 subsequently emit the stopped() signal.
2992*/
2993void QStateMachine::onExit(QEvent *event)
2994{
2995 stop();
2996 QState::onExit(event);
2997}
2998
2999#if QT_CONFIG(animation)
3000
3001/*!
3002 Returns whether animations are enabled for this state machine.
3003*/
3004bool QStateMachine::isAnimated() const
3005{
3006 Q_D(const QStateMachine);
3007 return d->animated;
3008}
3009
3010/*!
3011 Sets whether animations are \a enabled for this state machine.
3012*/
3013void QStateMachine::setAnimated(bool enabled)
3014{
3015 Q_D(QStateMachine);
3016 d->animated = enabled;
3017}
3018
3019/*!
3020 Adds a default \a animation to be considered for any transition.
3021*/
3022void QStateMachine::addDefaultAnimation(QAbstractAnimation *animation)
3023{
3024 Q_D(QStateMachine);
3025 d->defaultAnimations.append(t: animation);
3026}
3027
3028/*!
3029 Returns the list of default animations that will be considered for any transition.
3030*/
3031QList<QAbstractAnimation*> QStateMachine::defaultAnimations() const
3032{
3033 Q_D(const QStateMachine);
3034 return d->defaultAnimations;
3035}
3036
3037/*!
3038 Removes \a animation from the list of default animations.
3039*/
3040void QStateMachine::removeDefaultAnimation(QAbstractAnimation *animation)
3041{
3042 Q_D(QStateMachine);
3043 d->defaultAnimations.removeAll(t: animation);
3044}
3045
3046#endif // animation
3047
3048
3049// Begin moc-generated code -- modify carefully (check "HAND EDIT" parts)!
3050struct qt_meta_stringdata_QSignalEventGenerator_t {
3051 QByteArrayData data[3];
3052 char stringdata[32];
3053};
3054#define QT_MOC_LITERAL(idx, ofs, len) \
3055 Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
3056 offsetof(qt_meta_stringdata_QSignalEventGenerator_t, stringdata) + ofs \
3057 - idx * sizeof(QByteArrayData) \
3058 )
3059static const qt_meta_stringdata_QSignalEventGenerator_t qt_meta_stringdata_QSignalEventGenerator = {
3060 .data: {
3061QT_MOC_LITERAL(0, 0, 21),
3062QT_MOC_LITERAL(1, 22, 7),
3063QT_MOC_LITERAL(2, 30, 0)
3064 },
3065 .stringdata: "QSignalEventGenerator\0execute\0\0"
3066};
3067#undef QT_MOC_LITERAL
3068
3069static const uint qt_meta_data_QSignalEventGenerator[] = {
3070
3071 // content:
3072 7, // revision
3073 0, // classname
3074 0, 0, // classinfo
3075 1, 14, // methods
3076 0, 0, // properties
3077 0, 0, // enums/sets
3078 0, 0, // constructors
3079 0, // flags
3080 0, // signalCount
3081
3082 // slots: name, argc, parameters, tag, flags
3083 1, 0, 19, 2, 0x0a,
3084
3085 // slots: parameters
3086 QMetaType::Void,
3087
3088 0 // eod
3089};
3090
3091void QSignalEventGenerator::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
3092{
3093 if (_c == QMetaObject::InvokeMetaMethod) {
3094 Q_ASSERT(staticMetaObject.cast(_o));
3095 QSignalEventGenerator *_t = static_cast<QSignalEventGenerator *>(_o);
3096 switch (_id) {
3097 case 0: _t->execute(_a); break; // HAND EDIT: add the _a parameter
3098 default: ;
3099 }
3100 }
3101 Q_UNUSED(_a);
3102}
3103
3104const QMetaObject QSignalEventGenerator::staticMetaObject = {
3105 .d: { .superdata: &QObject::staticMetaObject, .stringdata: qt_meta_stringdata_QSignalEventGenerator.data,
3106 .data: qt_meta_data_QSignalEventGenerator, .static_metacall: qt_static_metacall, .relatedMetaObjects: nullptr, .extradata: nullptr }
3107};
3108
3109const QMetaObject *QSignalEventGenerator::metaObject() const
3110{
3111 return &staticMetaObject;
3112}
3113
3114void *QSignalEventGenerator::qt_metacast(const char *_clname)
3115{
3116 if (!_clname) return nullptr;
3117 if (!strcmp(s1: _clname, s2: qt_meta_stringdata_QSignalEventGenerator.stringdata))
3118 return static_cast<void*>(const_cast< QSignalEventGenerator*>(this));
3119 return QObject::qt_metacast(_clname);
3120}
3121
3122int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
3123{
3124 _id = QObject::qt_metacall(_c, _id, _a);
3125 if (_id < 0)
3126 return _id;
3127 if (_c == QMetaObject::InvokeMetaMethod) {
3128 if (_id < 1)
3129 qt_static_metacall(o: this, _c, _id, _a);
3130 _id -= 1;
3131 }
3132 return _id;
3133}
3134// End moc-generated code
3135
3136void QSignalEventGenerator::execute(void **_a)
3137{
3138 auto machinePrivate = QStateMachinePrivate::get(q: qobject_cast<QStateMachine*>(object: parent()));
3139 if (machinePrivate->state != QStateMachinePrivate::Running)
3140 return;
3141 int signalIndex = senderSignalIndex();
3142 if (signalIndex == -1) {
3143 qWarning() << "StateMachine: Could not execute transition because originating object has been deleted";
3144 return;
3145 }
3146 machinePrivate->handleTransitionSignal(sender: sender(), signalIndex, argv: _a);
3147}
3148
3149QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent)
3150 : QObject(parent)
3151{
3152}
3153
3154/*!
3155 \class QStateMachine::SignalEvent
3156 \inmodule QtCore
3157
3158 \brief The SignalEvent class represents a Qt signal event.
3159
3160 \since 4.6
3161 \ingroup statemachine
3162
3163 A signal event is generated by a QStateMachine in response to a Qt
3164 signal. The QSignalTransition class provides a transition associated with a
3165 signal event. QStateMachine::SignalEvent is part of \l{The State Machine Framework}.
3166
3167 The sender() function returns the object that generated the signal. The
3168 signalIndex() function returns the index of the signal. The arguments()
3169 function returns the arguments of the signal.
3170
3171 \sa QSignalTransition
3172*/
3173
3174/*!
3175 \internal
3176
3177 Constructs a new SignalEvent object with the given \a sender, \a
3178 signalIndex and \a arguments.
3179*/
3180QStateMachine::SignalEvent::SignalEvent(QObject *sender, int signalIndex,
3181 const QList<QVariant> &arguments)
3182 : QEvent(QEvent::StateMachineSignal), m_sender(sender),
3183 m_signalIndex(signalIndex), m_arguments(arguments)
3184{
3185}
3186
3187/*!
3188 Destroys this SignalEvent.
3189*/
3190QStateMachine::SignalEvent::~SignalEvent()
3191{
3192}
3193
3194/*!
3195 \fn QStateMachine::SignalEvent::sender() const
3196
3197 Returns the object that emitted the signal.
3198
3199 \sa QObject::sender()
3200*/
3201
3202/*!
3203 \fn QStateMachine::SignalEvent::signalIndex() const
3204
3205 Returns the index of the signal.
3206
3207 \sa QMetaObject::indexOfSignal(), QMetaObject::method()
3208*/
3209
3210/*!
3211 \fn QStateMachine::SignalEvent::arguments() const
3212
3213 Returns the arguments of the signal.
3214*/
3215
3216
3217/*!
3218 \class QStateMachine::WrappedEvent
3219 \inmodule QtCore
3220
3221 \brief The WrappedEvent class inherits QEvent and holds a clone of an event associated with a QObject.
3222
3223 \since 4.6
3224 \ingroup statemachine
3225
3226 A wrapped event is generated by a QStateMachine in response to a Qt
3227 event. The QEventTransition class provides a transition associated with a
3228 such an event. QStateMachine::WrappedEvent is part of \l{The State Machine
3229 Framework}.
3230
3231 The object() function returns the object that generated the event. The
3232 event() function returns a clone of the original event.
3233
3234 \sa QEventTransition
3235*/
3236
3237/*!
3238 \internal
3239
3240 Constructs a new WrappedEvent object with the given \a object
3241 and \a event.
3242
3243 The WrappedEvent object takes ownership of \a event.
3244*/
3245QStateMachine::WrappedEvent::WrappedEvent(QObject *object, QEvent *event)
3246 : QEvent(QEvent::StateMachineWrapped), m_object(object), m_event(event)
3247{
3248}
3249
3250/*!
3251 Destroys this WrappedEvent.
3252*/
3253QStateMachine::WrappedEvent::~WrappedEvent()
3254{
3255 delete m_event;
3256}
3257
3258/*!
3259 \fn QStateMachine::WrappedEvent::object() const
3260
3261 Returns the object that the event is associated with.
3262*/
3263
3264/*!
3265 \fn QStateMachine::WrappedEvent::event() const
3266
3267 Returns a clone of the original event.
3268*/
3269
3270/*!
3271 \fn QStateMachine::runningChanged(bool running)
3272 \since 5.4
3273
3274 This signal is emitted when the running property is changed with \a running as argument.
3275
3276 \sa QStateMachine::running
3277*/
3278
3279/*!
3280 \fn QStateMachine::postDelayedEvent(QEvent *event, std::chrono::milliseconds delay)
3281 \since 5.15
3282 \overload
3283 \threadsafe
3284
3285 Posts the given \a event for processing by this state machine, with the
3286 given \a delay in milliseconds. Returns an identifier associated with the
3287 delayed event, or -1 if the event could not be posted.
3288
3289 This function returns immediately. When the delay has expired, the event
3290 will be added to the state machine's event queue for processing. The state
3291 machine takes ownership of the event and deletes it once it has been
3292 processed.
3293
3294 You can only post events when the state machine is running.
3295
3296 \sa cancelDelayedEvent(), postEvent()
3297*/
3298
3299QT_END_NAMESPACE
3300
3301#include "qstatemachine.moc"
3302#include "moc_qstatemachine.cpp"
3303

source code of qtbase/src/corelib/statemachine/qstatemachine.cpp