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 
49 QT_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 
QStatePrivate()147 QStatePrivate::QStatePrivate()
148     : QAbstractStatePrivate(StandardState),
149       errorState(nullptr), initialState(nullptr), childMode(QState::ExclusiveStates),
150       childStatesListNeedsRefresh(true), transitionsListNeedsRefresh(true)
151 {
152 }
153 
~QStatePrivate()154 QStatePrivate::~QStatePrivate()
155 {
156 }
157 
emitFinished()158 void QStatePrivate::emitFinished()
159 {
160     Q_Q(QState);
161     emit q->finished(QState::QPrivateSignal());
162 }
163 
emitPropertiesAssigned()164 void 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 */
QState(QState * parent)173 QState::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 */
QState(ChildMode childMode,QState * parent)182 QState::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 */
QState(QStatePrivate & dd,QState * parent)192 QState::QState(QStatePrivate &dd, QState *parent)
193     : QAbstractState(dd, parent)
194 {
195 }
196 
197 /*!
198   Destroys this state.
199 */
~QState()200 QState::~QState()
201 {
202 }
203 
childStates() const204 QList<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*>(*it);
211             if (!s || qobject_cast<QHistoryState*>(s))
212                 continue;
213             childStatesList.append(s);
214         }
215         childStatesListNeedsRefresh = false;
216     }
217     return childStatesList;
218 }
219 
historyStates() const220 QList<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*>(*it);
226         if (h)
227             result.append(h);
228     }
229     return result;
230 }
231 
transitions() const232 QList<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*>(*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 */
assignProperty(QObject * object,const char * name,const QVariant & value)255 void QState::assignProperty(QObject *object, const char *name,
256                             const QVariant &value)
257 {
258     Q_D(QState);
259     if (!object) {
260         qWarning("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(object, name)) {
266             assn.value = value;
267             return;
268         }
269     }
270     d->propertyAssignments.append(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 */
errorState() const280 QAbstractState *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 */
setErrorState(QAbstractState * state)293 void QState::setErrorState(QAbstractState *state)
294 {
295     Q_D(QState);
296     if (state != nullptr && qobject_cast<QStateMachine*>(state)) {
297         qWarning("QStateMachine::setErrorState: root state cannot be error state");
298         return;
299     }
300     if (state != nullptr && (!state->machine() || ((state->machine() != machine()) && !qobject_cast<QStateMachine*>(this)))) {
301         qWarning("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 */
addTransition(QAbstractTransition * transition)316 void QState::addTransition(QAbstractTransition *transition)
317 {
318     Q_D(QState);
319     if (!transition) {
320         qWarning("QState::addTransition: cannot add null transition");
321         return ;
322     }
323 
324     transition->setParent(this);
325     const QVector<QPointer<QAbstractState> > &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
326     for (int i = 0; i < targets.size(); ++i) {
327         QAbstractState *t = targets.at(i).data();
328         if (!t) {
329             qWarning("QState::addTransition: cannot add transition to null state");
330             return ;
331         }
332         if ((QAbstractStatePrivate::get(t)->machine() != d->machine())
333             && QAbstractStatePrivate::get(t)->machine() && d->machine()) {
334             qWarning("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(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 */
addTransition(const QObject * sender,const char * signal,QAbstractState * target)358 QSignalTransition *QState::addTransition(const QObject *sender, const char *signal,
359                                          QAbstractState *target)
360 {
361     if (!sender) {
362         qWarning("QState::addTransition: sender cannot be null");
363         return nullptr;
364     }
365     if (!signal) {
366         qWarning("QState::addTransition: signal cannot be null");
367         return nullptr;
368     }
369     if (!target) {
370         qWarning("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+offset) == -1) {
376         if (meta->indexOfSignal(QMetaObject::normalizedSignature(signal+offset)) == -1) {
377             qWarning("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(trans);
385     return trans;
386 }
387 
388 namespace {
389 
390 // ### Make public?
391 class UnconditionalTransition : public QAbstractTransition
392 {
393 public:
UnconditionalTransition(QAbstractState * target)394     UnconditionalTransition(QAbstractState *target)
395         : QAbstractTransition()
396     { setTargetState(target); }
397 protected:
onTransition(QEvent *)398     void onTransition(QEvent *) override {}
eventTest(QEvent *)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 */
addTransition(QAbstractState * target)408 QAbstractTransition *QState::addTransition(QAbstractState *target)
409 {
410     if (!target) {
411         qWarning("QState::addTransition: cannot add transition to null state");
412         return nullptr;
413     }
414     UnconditionalTransition *trans = new UnconditionalTransition(target);
415     addTransition(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 */
removeTransition(QAbstractTransition * transition)425 void QState::removeTransition(QAbstractTransition *transition)
426 {
427     Q_D(QState);
428     if (!transition) {
429         qWarning("QState::removeTransition: cannot remove null transition");
430         return;
431     }
432     if (transition->sourceState() != this) {
433         qWarning("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(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 */
transitions() const453 QList<QAbstractTransition*> QState::transitions() const
454 {
455     Q_D(const QState);
456     return d->transitions();
457 }
458 
459 /*!
460   \reimp
461 */
onEntry(QEvent * event)462 void QState::onEntry(QEvent *event)
463 {
464     Q_UNUSED(event);
465 }
466 
467 /*!
468   \reimp
469 */
onExit(QEvent * event)470 void 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 */
initialState() const479 QAbstractState *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 */
setInitialState(QAbstractState * state)489 void QState::setInitialState(QAbstractState *state)
490 {
491     Q_D(QState);
492     if (d->childMode == QState::ParallelStates) {
493         qWarning("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("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 */
childMode() const511 QState::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 */
setChildMode(ChildMode mode)520 void QState::setChildMode(ChildMode mode)
521 {
522     Q_D(QState);
523 
524     if (mode == QState::ParallelStates && d->initialState) {
525         qWarning("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 */
event(QEvent * e)540 bool 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 
601 QT_END_NAMESPACE
602 
603 #include "moc_qstate.cpp"
604