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 "qstate_p.h"
5#include "qhistorystate.h"
6#include "qhistorystate_p.h"
7#include "qabstracttransition.h"
8#include "qabstracttransition_p.h"
9#include "qsignaltransition.h"
10#include "qstatemachine.h"
11#include "qstatemachine_p.h"
12
13QT_BEGIN_NAMESPACE
14
15/*!
16 \class QState
17 \inmodule QtStateMachine
18
19 \brief The QState class provides a general-purpose state for QStateMachine.
20
21 \since 4.6
22 \ingroup statemachine
23
24 QState objects can have child states, and can have transitions to other
25 states. QState is part of \l{Qt State Machine Overview}{Qt State Machine Framework}.
26
27 The addTransition() function adds a transition. The removeTransition()
28 function removes a transition. The transitions() function returns the
29 state's outgoing transitions.
30
31 The assignProperty() function is used for defining property assignments that
32 should be performed when a state is entered.
33
34 Top-level states must be passed a QStateMachine object as their parent
35 state, or added to a state machine using QStateMachine::addState().
36
37 \section1 States with Child States
38
39 The childMode property determines how child states are treated. For
40 non-parallel state groups, the setInitialState() function must be called to
41 set the initial state. The child states are mutually exclusive states, and
42 the state machine needs to know which child state to enter when the parent
43 state is the target of a transition.
44
45 The state emits the QState::finished() signal when a final child state
46 (QFinalState) is entered.
47
48 The setErrorState() sets the state's error state. The error state is the
49 state that the state machine will transition to if an error is detected when
50 attempting to enter the state (e.g. because no initial state has been set).
51
52*/
53
54/*!
55 \property QState::initialState
56
57 \brief the initial state of this state (one of its child states)
58*/
59
60/*!
61 \property QState::errorState
62
63 \brief the error state of this state
64*/
65
66/*!
67 \property QState::childMode
68
69 \brief the child mode of this state
70
71 The default value of this property is QState::ExclusiveStates.
72*/
73
74/*!
75 \enum QState::ChildMode
76
77 This enum specifies how a state's child states are treated.
78
79 \value ExclusiveStates The child states are mutually exclusive and an
80 initial state must be set by calling QState::setInitialState().
81
82 \value ParallelStates The child states are parallel. When the parent state
83 is entered, all its child states are entered in parallel.
84*/
85
86/*!
87 \enum QState::RestorePolicy
88
89 This enum specifies the restore policy type. The restore policy
90 takes effect when the machine enters a state which sets one or more
91 properties. If the restore policy is set to RestoreProperties,
92 the state machine will save the original value of the property before the
93 new value is set.
94
95 Later, when the machine either enters a state which does not set
96 a value for the given property, the property will automatically be restored
97 to its initial value.
98
99 Only one initial value will be saved for any given property. If a value for a property has
100 already been saved by the state machine, it will not be overwritten until the property has been
101 successfully restored.
102
103 \value DontRestoreProperties The state machine should not save the initial values of properties
104 and restore them later.
105 \value RestoreProperties The state machine should save the initial values of properties
106 and restore them later.
107
108 \sa QStateMachine::globalRestorePolicy, QState::assignProperty()
109*/
110
111QStatePrivate::QStatePrivate()
112 : QAbstractStatePrivate(StandardState),
113 childStatesListNeedsRefresh(true), transitionsListNeedsRefresh(true)
114{
115}
116
117QStatePrivate::~QStatePrivate()
118{
119}
120
121void QStatePrivate::emitFinished()
122{
123 Q_Q(QState);
124 emit q->finished(QState::QPrivateSignal());
125}
126
127void QStatePrivate::emitPropertiesAssigned()
128{
129 Q_Q(QState);
130 emit q->propertiesAssigned(QState::QPrivateSignal());
131}
132
133/*!
134 Constructs a new state with the given \a parent state.
135*/
136QState::QState(QState *parent)
137 : QAbstractState(*new QStatePrivate, parent)
138{
139}
140
141/*!
142 Constructs a new state with the given \a childMode and the given \a parent
143 state.
144*/
145QState::QState(ChildMode childMode, QState *parent)
146 : QAbstractState(*new QStatePrivate, parent)
147{
148 Q_D(QState);
149 d->childMode = childMode;
150}
151
152/*!
153 \internal
154*/
155QState::QState(QStatePrivate &dd, QState *parent)
156 : QAbstractState(dd, parent)
157{
158}
159
160/*!
161 Destroys this state.
162*/
163QState::~QState()
164{
165}
166
167QList<QAbstractState*> QStatePrivate::childStates() const
168{
169 if (childStatesListNeedsRefresh) {
170 childStatesList.clear();
171 QList<QObject*>::const_iterator it;
172 for (it = children.constBegin(); it != children.constEnd(); ++it) {
173 QAbstractState *s = qobject_cast<QAbstractState*>(object: *it);
174 if (!s || qobject_cast<QHistoryState*>(object: s))
175 continue;
176 childStatesList.append(t: s);
177 }
178 childStatesListNeedsRefresh = false;
179 }
180 return childStatesList;
181}
182
183QList<QHistoryState*> QStatePrivate::historyStates() const
184{
185 QList<QHistoryState*> result;
186 QList<QObject*>::const_iterator it;
187 for (it = children.constBegin(); it != children.constEnd(); ++it) {
188 QHistoryState *h = qobject_cast<QHistoryState*>(object: *it);
189 if (h)
190 result.append(t: h);
191 }
192 return result;
193}
194
195QList<QAbstractTransition*> QStatePrivate::transitions() const
196{
197 if (transitionsListNeedsRefresh) {
198 transitionsList.clear();
199 QList<QObject*>::const_iterator it;
200 for (it = children.constBegin(); it != children.constEnd(); ++it) {
201 QAbstractTransition *t = qobject_cast<QAbstractTransition*>(object: *it);
202 if (t)
203 transitionsList.append(t);
204 }
205 transitionsListNeedsRefresh = false;
206 }
207 return transitionsList;
208}
209
210#ifndef QT_NO_PROPERTIES
211
212/*!
213 Instructs this state to set the property with the given \a name of the given
214 \a object to the given \a value when the state is entered.
215
216 \sa propertiesAssigned()
217*/
218void QState::assignProperty(QObject *object, const char *name,
219 const QVariant &value)
220{
221 Q_D(QState);
222 if (!object) {
223 qWarning(msg: "QState::assignProperty: cannot assign property '%s' of null object", name);
224 return;
225 }
226 for (int i = 0; i < d->propertyAssignments.size(); ++i) {
227 QPropertyAssignment &assn = d->propertyAssignments[i];
228 if (assn.hasTarget(o: object, pn: name)) {
229 assn.value = value;
230 return;
231 }
232 }
233 d->propertyAssignments.append(t: QPropertyAssignment(object, name, value));
234}
235
236#endif // QT_NO_PROPERTIES
237
238/*!
239 Returns this state's error state.
240
241 \sa QStateMachine::error()
242*/
243QAbstractState *QState::errorState() const
244{
245 Q_D(const QState);
246 return d->errorState;
247}
248
249/*!
250 Sets this state's error state to be the given \a state. If the error state
251 is not set, or if it is set to \nullptr, the state will inherit its parent's error
252 state recursively. If no error state is set for the state itself or any of
253 its ancestors, an error will cause the machine to stop executing and an error
254 will be printed to the console.
255*/
256void QState::setErrorState(QAbstractState *state)
257{
258 Q_D(QState);
259 if (state != nullptr && qobject_cast<QStateMachine*>(object: state)) {
260 qWarning(msg: "QStateMachine::setErrorState: root state cannot be error state");
261 return;
262 }
263 if (state != nullptr && (!state->machine() || ((state->machine() != machine()) && !qobject_cast<QStateMachine*>(object: this)))) {
264 qWarning(msg: "QState::setErrorState: error state cannot belong "
265 "to a different state machine");
266 return;
267 }
268 d->errorState = state;
269}
270
271QBindable<QAbstractState*> QState::bindableErrorState()
272{
273 Q_D(QState);
274 return &d->errorState;
275}
276
277/*!
278 Adds the given \a transition. The transition has this state as the source.
279 This state takes ownership of the transition.
280*/
281void QState::addTransition(QAbstractTransition *transition)
282{
283 Q_D(QState);
284 if (!transition) {
285 qWarning(msg: "QState::addTransition: cannot add null transition");
286 return ;
287 }
288
289 transition->setParent(this);
290 const QList<QPointer<QAbstractState>> &targets = QAbstractTransitionPrivate::get(q: transition)->targetStates;
291 for (int i = 0; i < targets.size(); ++i) {
292 QAbstractState *t = targets.at(i).data();
293 if (!t) {
294 qWarning(msg: "QState::addTransition: cannot add transition to null state");
295 return ;
296 }
297 if ((QAbstractStatePrivate::get(q: t)->machine() != d->machine())
298 && QAbstractStatePrivate::get(q: t)->machine() && d->machine()) {
299 qWarning(msg: "QState::addTransition: cannot add transition "
300 "to a state in a different state machine");
301 return ;
302 }
303 }
304 if (QStateMachine *mach = machine())
305 QStateMachinePrivate::get(q: mach)->maybeRegisterTransition(transition);
306}
307
308/*!
309 \fn template <typename PointerToMemberFunction> QState::addTransition(const QObject *sender, PointerToMemberFunction signal, QAbstractState *target);
310 \since 5.5
311 \overload
312
313 Adds a transition associated with the given \a signal of the given \a sender
314 object, and returns the new QSignalTransition object. The transition has
315 this state as the source, and the given \a target as the target state.
316*/
317
318/*!
319 Adds a transition associated with the given \a signal of the given \a sender
320 object, and returns the new QSignalTransition object. The transition has
321 this state as the source, and the given \a target as the target state.
322*/
323QSignalTransition *QState::addTransition(const QObject *sender, const char *signal,
324 QAbstractState *target)
325{
326 if (!sender) {
327 qWarning(msg: "QState::addTransition: sender cannot be null");
328 return nullptr;
329 }
330 if (!signal) {
331 qWarning(msg: "QState::addTransition: signal cannot be null");
332 return nullptr;
333 }
334 if (!target) {
335 qWarning(msg: "QState::addTransition: cannot add transition to null state");
336 return nullptr;
337 }
338 int offset = (*signal == '0'+QSIGNAL_CODE) ? 1 : 0;
339 const QMetaObject *meta = sender->metaObject();
340 if (meta->indexOfSignal(signal: signal+offset) == -1) {
341 if (meta->indexOfSignal(signal: QMetaObject::normalizedSignature(method: signal+offset)) == -1) {
342 qWarning(msg: "QState::addTransition: no such signal %s::%s",
343 meta->className(), signal+offset);
344 return nullptr;
345 }
346 }
347 QSignalTransition *trans = new QSignalTransition(sender, signal);
348 trans->setTargetState(target);
349 addTransition(transition: trans);
350 return trans;
351}
352
353namespace {
354
355// ### Make public?
356class UnconditionalTransition : public QAbstractTransition
357{
358public:
359 UnconditionalTransition(QAbstractState *target)
360 : QAbstractTransition()
361 { setTargetState(target); }
362protected:
363 void onTransition(QEvent *) override {}
364 bool eventTest(QEvent *) override { return true; }
365};
366
367} // namespace
368
369/*!
370 Adds an unconditional transition from this state to the given \a target
371 state, and returns then new transition object.
372*/
373QAbstractTransition *QState::addTransition(QAbstractState *target)
374{
375 if (!target) {
376 qWarning(msg: "QState::addTransition: cannot add transition to null state");
377 return nullptr;
378 }
379 UnconditionalTransition *trans = new UnconditionalTransition(target);
380 addTransition(transition: trans);
381 return trans;
382}
383
384/*!
385 Removes the given \a transition from this state. The state releases
386 ownership of the transition.
387
388 \sa addTransition()
389*/
390void QState::removeTransition(QAbstractTransition *transition)
391{
392 Q_D(QState);
393 if (!transition) {
394 qWarning(msg: "QState::removeTransition: cannot remove null transition");
395 return;
396 }
397 if (transition->sourceState() != this) {
398 qWarning(msg: "QState::removeTransition: transition %p's source state (%p)"
399 " is different from this state (%p)",
400 transition, transition->sourceState(), this);
401 return;
402 }
403 QStateMachinePrivate *mach = QStateMachinePrivate::get(q: d->machine());
404 if (mach)
405 mach->unregisterTransition(transition);
406 transition->setParent(nullptr);
407}
408
409/*!
410 \since 4.7
411
412 Returns this state's outgoing transitions (i.e. transitions where
413 this state is the \l [CPP] {QAbstractTransition::sourceState()}{source
414 state}), or an empty list if this state has no outgoing transitions.
415
416 \sa addTransition()
417*/
418QList<QAbstractTransition*> QState::transitions() const
419{
420 Q_D(const QState);
421 return d->transitions();
422}
423
424/*!
425 \reimp
426*/
427void QState::onEntry(QEvent *event)
428{
429 Q_UNUSED(event);
430}
431
432/*!
433 \reimp
434*/
435void QState::onExit(QEvent *event)
436{
437 Q_UNUSED(event);
438}
439
440/*!
441 Returns this state's initial state, or \nullptr if the state has no
442 initial state.
443*/
444QAbstractState *QState::initialState() const
445{
446 Q_D(const QState);
447 return d->initialState;
448}
449
450/*!
451 Sets this state's initial state to be the given \a state.
452 \a state has to be a child of this state.
453*/
454void QState::setInitialState(QAbstractState *state)
455{
456 Q_D(QState);
457 if (d->childMode == QState::ParallelStates) {
458 qWarning(msg: "QState::setInitialState: ignoring attempt to set initial state "
459 "of parallel state group %p", this);
460 return;
461 }
462 if (state && (state->parentState() != this)) {
463 qWarning(msg: "QState::setInitialState: state %p is not a child of this state (%p)",
464 state, this);
465 return;
466 }
467 d->initialState = state;
468}
469
470QBindable<QAbstractState*> QState::bindableInitialState()
471{
472 Q_D(QState);
473 return &d->initialState;
474}
475
476/*!
477 Returns the child mode of this state.
478*/
479QState::ChildMode QState::childMode() const
480{
481 Q_D(const QState);
482 return d->childMode;
483}
484
485/*!
486 Sets the child \a mode of this state.
487*/
488void QState::setChildMode(ChildMode mode)
489{
490 Q_D(QState);
491
492 if (mode == QState::ParallelStates && d->initialState.value()) {
493 qWarning(msg: "QState::setChildMode: setting the child-mode of state %p to "
494 "parallel removes the initial state", this);
495 d->initialState.setValue(nullptr);
496 }
497 d->childMode = mode;
498}
499
500QBindable<QState::ChildMode> QState::bindableChildMode()
501{
502 Q_D(QState);
503 return &d->childMode;
504}
505
506/*!
507 \reimp
508*/
509bool QState::event(QEvent *e)
510{
511 Q_D(QState);
512 if ((e->type() == QEvent::ChildAdded) || (e->type() == QEvent::ChildRemoved)) {
513 d->childStatesListNeedsRefresh = true;
514 d->transitionsListNeedsRefresh = true;
515 if ((e->type() == QEvent::ChildRemoved)
516 && (static_cast<QChildEvent *>(e)->child() == d->initialState.value())) {
517 d->initialState.setValue(nullptr);
518 }
519 }
520 return QAbstractState::event(e);
521}
522
523/*!
524 \fn QState::finished()
525
526 This signal is emitted when a final child state of this state is entered.
527
528 \sa QFinalState
529*/
530
531/*!
532 \fn QState::propertiesAssigned()
533
534 This signal is emitted when all properties have been assigned their final value. If the state
535 assigns a value to one or more properties for which an animation exists (either set on the
536 transition or as a default animation on the state machine), then the signal will not be emitted
537 until all such animations have finished playing.
538
539 If there are no relevant animations, or no property assignments defined for the state, then
540 the signal will be emitted immediately before the state is entered.
541
542 \b {See also} \l QState::assignProperty() and \l [CPP] {QAbstractTransition::addAnimation()}
543*/
544
545/*!
546 \fn QState::childModeChanged()
547 \since 5.4
548
549 This signal is emitted when the childMode property is changed.
550
551 \sa QState::childMode
552*/
553
554/*!
555 \fn QState::initialStateChanged()
556 \since 5.4
557
558 This signal is emitted when the initialState property is changed.
559
560 \sa QState::initialState
561*/
562
563/*!
564 \fn QState::errorStateChanged()
565 \since 5.4
566
567 This signal is emitted when the errorState property is changed.
568
569 \sa QState::errorState
570*/
571
572QT_END_NAMESPACE
573
574#include "moc_qstate.cpp"
575

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