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 | |
47 | QT_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 | |
135 | QAbstractTransitionPrivate::QAbstractTransitionPrivate() |
136 | : transitionType(QAbstractTransition::ExternalTransition) |
137 | { |
138 | } |
139 | |
140 | QStateMachine *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 | |
150 | bool QAbstractTransitionPrivate::callEventTest(QEvent *e) |
151 | { |
152 | Q_Q(QAbstractTransition); |
153 | return q->eventTest(event: e); |
154 | } |
155 | |
156 | void QAbstractTransitionPrivate::callOnTransition(QEvent *e) |
157 | { |
158 | Q_Q(QAbstractTransition); |
159 | q->onTransition(event: e); |
160 | } |
161 | |
162 | QState *QAbstractTransitionPrivate::sourceState() const |
163 | { |
164 | return qobject_cast<QState*>(object: parent); |
165 | } |
166 | |
167 | void 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 | */ |
176 | QAbstractTransition::QAbstractTransition(QState *sourceState) |
177 | : QObject(*new QAbstractTransitionPrivate, sourceState) |
178 | { |
179 | } |
180 | |
181 | /*! |
182 | \internal |
183 | */ |
184 | QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, |
185 | QState *parent) |
186 | : QObject(dd, parent) |
187 | { |
188 | } |
189 | |
190 | /*! |
191 | Destroys this transition. |
192 | */ |
193 | QAbstractTransition::~QAbstractTransition() |
194 | { |
195 | } |
196 | |
197 | /*! |
198 | Returns the source state of this transition, or \nullptr if this |
199 | transition has no source state. |
200 | */ |
201 | QState *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 | */ |
211 | QAbstractState *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 | */ |
222 | void 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 | */ |
240 | QList<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 | */ |
255 | void 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 | */ |
312 | QAbstractTransition::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 | */ |
321 | void 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 | */ |
331 | QStateMachine *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 | */ |
345 | void 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 | */ |
360 | void 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 | */ |
376 | QList<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 | */ |
428 | bool QAbstractTransition::event(QEvent *e) |
429 | { |
430 | return QObject::event(event: e); |
431 | } |
432 | |
433 | QT_END_NAMESPACE |
434 | |
435 | #include "moc_qabstracttransition.cpp" |
436 | |