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

source code of qtscxml/src/statemachine/qstatemachine.cpp