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

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