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 "qabstracttransition.h"
5#include "qabstracttransition_p.h"
6#include "qabstractstate.h"
7#include "qhistorystate.h"
8#include "qstate.h"
9#include "qstatemachine.h"
10
11QT_BEGIN_NAMESPACE
12
13/*!
14 \class QAbstractTransition
15 \inmodule QtStateMachine
16
17 \brief The QAbstractTransition class is the base class of transitions between QAbstractState objects.
18
19 \since 4.6
20 \ingroup statemachine
21
22 The QAbstractTransition class is the abstract base class of transitions
23 between states (QAbstractState objects) of a
24 QStateMachine. QAbstractTransition is part of \l{Qt State Machine Overview}
25 {Qt State Machine Framework}.
26
27 The sourceState() function returns the source of the transition. The
28 targetStates() function returns the targets of the transition. The machine()
29 function returns the state machine that the transition is part of.
30
31 The triggered() signal is emitted when the transition has been triggered.
32
33 Transitions can cause animations to be played. Use the addAnimation()
34 function to add an animation to the transition.
35
36 \section1 Subclassing
37
38 The eventTest() function is called by the state machine to determine whether
39 an event should trigger the transition. In your reimplementation you
40 typically check the event type and cast the event object to the proper type,
41 and check that one or more properties of the event meet your criteria.
42
43 The onTransition() function is called when the transition is triggered;
44 reimplement this function to perform custom processing for the transition.
45*/
46
47/*!
48 \property QAbstractTransition::sourceState
49
50 \brief the source state (parent) of this transition
51*/
52
53/*!
54 \property QAbstractTransition::targetState
55
56 \brief the target state of this transition
57
58 If a transition has no target state, the transition may still be
59 triggered, but this will not cause the state machine's configuration to
60 change (i.e. the current state will not be exited and re-entered).
61*/
62
63/*!
64 \property QAbstractTransition::targetStates
65
66 \brief the target states of this transition
67
68 If multiple states are specified, all must be descendants of the same
69 parallel group state.
70*/
71
72/*!
73 \property QAbstractTransition::transitionType
74
75 \brief indicates whether this transition is an internal transition, or an external transition.
76
77 Internal and external transitions behave the same, except for the case of a transition whose
78 source state is a compound state and whose target(s) is a descendant of the source. In such a
79 case, an internal transition will not exit and re-enter its source state, while an external one
80 will.
81
82 By default, the type is an external transition.
83*/
84
85/*!
86 \enum QAbstractTransition::TransitionType
87
88 This enum specifies the kind of transition. By default, the type is an external transition.
89
90 \value ExternalTransition Any state that is the source state of a transition (which is not a
91 target-less transition) is left, and re-entered when necessary.
92 \value InternalTransition If the target state of a transition is a sub-state of a compound state,
93 and that compound state is the source state, an internal transition will
94 not leave the source state.
95
96 \sa QAbstractTransition::transitionType
97*/
98
99QStateMachine *QAbstractTransitionPrivate::machine() const
100{
101 if (QState *source = sourceState())
102 return source->machine();
103 Q_Q(const QAbstractTransition);
104 if (QHistoryState *parent = qobject_cast<QHistoryState *>(object: q->parent()))
105 return parent->machine();
106 return nullptr;
107}
108
109bool QAbstractTransitionPrivate::callEventTest(QEvent *e)
110{
111 Q_Q(QAbstractTransition);
112 return q->eventTest(event: e);
113}
114
115void QAbstractTransitionPrivate::callOnTransition(QEvent *e)
116{
117 Q_Q(QAbstractTransition);
118 q->onTransition(event: e);
119}
120
121QState *QAbstractTransitionPrivate::sourceState() const
122{
123 return qobject_cast<QState*>(object: parent);
124}
125
126void QAbstractTransitionPrivate::emitTriggered()
127{
128 Q_Q(QAbstractTransition);
129 emit q->triggered(QAbstractTransition::QPrivateSignal());
130}
131
132/*!
133 Constructs a new QAbstractTransition object with the given \a sourceState.
134*/
135QAbstractTransition::QAbstractTransition(QState *sourceState)
136 : QObject(*new QAbstractTransitionPrivate, sourceState)
137{
138}
139
140/*!
141 \internal
142*/
143QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd,
144 QState *parent)
145 : QObject(dd, parent)
146{
147}
148
149/*!
150 Destroys this transition.
151*/
152QAbstractTransition::~QAbstractTransition()
153{
154}
155
156/*!
157 Returns the source state of this transition, or \nullptr if this
158 transition has no source state.
159*/
160QState *QAbstractTransition::sourceState() const
161{
162 Q_D(const QAbstractTransition);
163 return d->sourceState();
164}
165
166/*!
167 Returns the target state of this transition, or \nullptr if the
168 transition has no target.
169*/
170QAbstractState *QAbstractTransition::targetState() const
171{
172 Q_D(const QAbstractTransition);
173 if (d->targetStates.isEmpty())
174 return nullptr;
175 return d->targetStates.first().data();
176}
177
178/*!
179 Sets the \a target state of this transition.
180*/
181void QAbstractTransition::setTargetState(QAbstractState* target)
182{
183 Q_D(QAbstractTransition);
184 if ((d->targetStates.size() == 1 && target == d->targetStates.at(i: 0).data()) ||
185 (d->targetStates.isEmpty() && target == nullptr)) {
186 return;
187 }
188 if (!target)
189 d->targetStates.clear();
190 else
191 setTargetStates(QList<QAbstractState*>() << target);
192 emit targetStateChanged(QPrivateSignal());
193}
194
195/*!
196 Returns the target states of this transition, or an empty list if this
197 transition has no target states.
198*/
199QList<QAbstractState*> QAbstractTransition::targetStates() const
200{
201 Q_D(const QAbstractTransition);
202 QList<QAbstractState*> result;
203 for (int i = 0; i < d->targetStates.size(); ++i) {
204 QAbstractState *target = d->targetStates.at(i).data();
205 if (target)
206 result.append(t: target);
207 }
208 return result;
209}
210
211/*!
212 Sets the target states of this transition to be the given \a targets.
213*/
214void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets)
215{
216 Q_D(QAbstractTransition);
217
218 // Verify if any of the new target states is a null-pointer:
219 for (int i = 0; i < targets.size(); ++i) {
220 if (targets.at(i) == nullptr) {
221 qWarning(msg: "QAbstractTransition::setTargetStates: target state(s) cannot be null");
222 return;
223 }
224 }
225
226 // First clean out any target states that got destroyed, but for which we still have a QPointer
227 // around.
228 for (int i = 0; i < d->targetStates.size(); ) {
229 if (d->targetStates.at(i).isNull()) {
230 d->targetStates.remove(i);
231 } else {
232 ++i;
233 }
234 }
235
236 // Easy check: if both lists are empty, we're done.
237 if (targets.isEmpty() && d->targetStates.isEmpty())
238 return;
239
240 bool sameList = true;
241
242 if (targets.size() != d->targetStates.size()) {
243 // If the sizes of the lists are different, we don't need to be smart: they're different. So
244 // we can just set the new list as the targetStates.
245 sameList = false;
246 } else {
247 QList<QPointer<QAbstractState>> copy(d->targetStates);
248 for (int i = 0; i < targets.size(); ++i) {
249 sameList &= copy.removeOne(t: targets.at(i));
250 if (!sameList)
251 break; // ok, we now know the lists are not the same, so stop the loop.
252 }
253
254 sameList &= copy.isEmpty();
255 }
256
257 if (sameList)
258 return;
259
260 d->targetStates.resize(size: targets.size());
261 for (int i = 0; i < targets.size(); ++i) {
262 d->targetStates[i] = targets.at(i);
263 }
264
265 emit targetStatesChanged(QPrivateSignal());
266}
267
268/*!
269 Returns the type of the transition.
270*/
271QAbstractTransition::TransitionType QAbstractTransition::transitionType() const
272{
273 Q_D(const QAbstractTransition);
274 return d->transitionType;
275}
276
277/*!
278 Sets the type of the transition to \a type.
279*/
280void QAbstractTransition::setTransitionType(TransitionType type)
281{
282 Q_D(QAbstractTransition);
283 d->transitionType = type;
284}
285
286QBindable<QAbstractTransition::TransitionType> QAbstractTransition::bindableTransitionType()
287{
288 Q_D(QAbstractTransition);
289 return &d->transitionType;
290}
291
292/*!
293 Returns the state machine that this transition is part of, or
294 \nullptr if the transition is not part of a state machine.
295*/
296QStateMachine *QAbstractTransition::machine() const
297{
298 Q_D(const QAbstractTransition);
299 return d->machine();
300}
301
302#if QT_CONFIG(animation)
303
304/*!
305 Adds the given \a animation to this transition.
306 The transition does not take ownership of the animation.
307
308 \sa removeAnimation(), animations()
309*/
310void QAbstractTransition::addAnimation(QAbstractAnimation *animation)
311{
312 Q_D(QAbstractTransition);
313 if (!animation) {
314 qWarning(msg: "QAbstractTransition::addAnimation: cannot add null animation");
315 return;
316 }
317 d->animations.append(t: animation);
318}
319
320/*!
321 Removes the given \a animation from this transition.
322
323 \sa addAnimation()
324*/
325void QAbstractTransition::removeAnimation(QAbstractAnimation *animation)
326{
327 Q_D(QAbstractTransition);
328 if (!animation) {
329 qWarning(msg: "QAbstractTransition::removeAnimation: cannot remove null animation");
330 return;
331 }
332 d->animations.removeOne(t: animation);
333}
334
335/*!
336 Returns the list of animations associated with this transition, or an empty
337 list if it has no animations.
338
339 \sa addAnimation()
340*/
341QList<QAbstractAnimation*> QAbstractTransition::animations() const
342{
343 Q_D(const QAbstractTransition);
344 return d->animations;
345}
346
347#endif
348
349/*!
350 \fn QAbstractTransition::eventTest(QEvent *event)
351
352 This function is called to determine whether the given \a event should cause
353 this transition to trigger. Reimplement this function and return true if the
354 event should trigger the transition, otherwise return false.
355*/
356
357/*!
358 \fn QAbstractTransition::onTransition(QEvent *event)
359
360 This function is called when the transition is triggered. The given \a event
361 is what caused the transition to trigger. Reimplement this function to
362 perform custom processing when the transition is triggered.
363*/
364
365/*!
366 \fn QAbstractTransition::triggered()
367
368 This signal is emitted when the transition has been triggered (after
369 onTransition() has been called).
370*/
371
372/*!
373 \fn QAbstractTransition::targetStateChanged()
374 \since 5.4
375
376 This signal is emitted when the targetState property is changed.
377
378 \sa QAbstractTransition::targetState
379*/
380
381/*!
382 \fn QAbstractTransition::targetStatesChanged()
383 \since 5.4
384
385 This signal is emitted when the targetStates property is changed.
386
387 \sa QAbstractTransition::targetStates
388*/
389
390/*!
391 \reimp
392*/
393bool QAbstractTransition::event(QEvent *e)
394{
395 return QObject::event(event: e);
396}
397
398QT_END_NAMESPACE
399
400#include "moc_qabstracttransition.cpp"
401

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