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

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