source: trunk/src/corelib/statemachine/qstatemachine.cpp@ 624

Last change on this file since 624 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

  • Property svn:eol-style set to native
File size: 78.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qstatemachine.h"
43
44#ifndef QT_NO_STATEMACHINE
45
46#include "qstate.h"
47#include "qstate_p.h"
48#include "qstatemachine_p.h"
49#include "qabstracttransition.h"
50#include "qabstracttransition_p.h"
51#include "qsignaltransition.h"
52#include "qsignaltransition_p.h"
53#include "qsignaleventgenerator_p.h"
54#include "qabstractstate.h"
55#include "qabstractstate_p.h"
56#include "qfinalstate.h"
57#include "qhistorystate.h"
58#include "qhistorystate_p.h"
59#include "private/qobject_p.h"
60#include "private/qthread_p.h"
61
62#ifndef QT_NO_STATEMACHINE_EVENTFILTER
63#include "qeventtransition.h"
64#include "qeventtransition_p.h"
65#endif
66
67#ifndef QT_NO_ANIMATION
68#include "qpropertyanimation.h"
69#include "qanimationgroup.h"
70#include <private/qvariantanimation_p.h>
71#endif
72
73#include <QtCore/qmetaobject.h>
74#include <qdebug.h>
75
76QT_BEGIN_NAMESPACE
77
78/*!
79 \class QStateMachine
80 \reentrant
81
82 \brief The QStateMachine class provides a hierarchical finite state machine.
83
84 \since 4.6
85 \ingroup statemachine
86
87 QStateMachine is based on the concepts and notation of
88 \l{Statecharts: A visual formalism for complex
89 systems}{Statecharts}. QStateMachine is part of \l{The State
90 Machine Framework}.
91
92 A state machine manages a set of states (classes that inherit from
93 QAbstractState) and transitions (descendants of
94 QAbstractTransition) between those states; these states and
95 transitions define a state graph. Once a state graph has been
96 built, the state machine can execute it. QStateMachine's
97 execution algorithm is based on the \l{State Chart XML: State
98 Machine Notation for Control Abstraction}{State Chart XML (SCXML)}
99 algorithm. The framework's \l{The State Machine
100 Framework}{overview} gives several state graphs and the code to
101 build them.
102
103 Use the addState() function to add a top-level state to the state machine.
104 States are removed with the removeState() function. Removing states while
105 the machine is running is discouraged.
106
107 Before the machine can be started, the \l{initialState}{initial
108 state} must be set. The initial state is the state that the
109 machine enters when started. You can then start() the state
110 machine. The started() signal is emitted when the initial state is
111 entered.
112
113 The machine is event driven and keeps its own event loop. Events
114 are posted to the machine through postEvent(). Note that this
115 means that it executes asynchronously, and that it will not
116 progress without a running event loop. You will normally not have
117 to post events to the machine directly as Qt's transitions, e.g.,
118 QEventTransition and its subclasses, handle this. But for custom
119 transitions triggered by events, postEvent() is useful.
120
121 The state machine processes events and takes transitions until a
122 top-level final state is entered; the state machine then emits the
123 finished() signal. You can also stop() the state machine
124 explicitly. The stopped() signal is emitted in this case.
125
126 The following snippet shows a state machine that will finish when a button
127 is clicked:
128
129 \snippet doc/src/snippets/code/src_corelib_statemachine_qstatemachine.cpp simple state machine
130
131 This code example uses QState, which inherits QAbstractState. The
132 QState class provides a state that you can use to set properties
133 and invoke methods on \l{QObject}s when the state is entered or
134 exited. It also contains convenience functions for adding
135 transitions, e.g., \l{QSignalTransition}s as in this example. See
136 the QState class description for further details.
137
138 If an error is encountered, the machine will look for an
139 \l{errorState}{error state}, and if one is available, it will
140 enter this state. The types of errors possible are described by the
141 \l{QStateMachine::}{Error} enum. After the error state is entered,
142 the type of the error can be retrieved with error(). The execution
143 of the state graph will not stop when the error state is entered. If
144 no error state applies to the erroneous state, the machine will stop
145 executing and an error message will be printed to the console.
146
147 \sa QAbstractState, QAbstractTransition, QState, {The State Machine Framework}
148*/
149
150/*!
151 \property QStateMachine::errorString
152
153 \brief the error string of this state machine
154*/
155
156/*!
157 \property QStateMachine::globalRestorePolicy
158
159 \brief the restore policy for states of this state machine.
160
161 The default value of this property is
162 QStateMachine::DontRestoreProperties.
163*/
164
165#ifndef QT_NO_ANIMATION
166/*!
167 \property QStateMachine::animated
168
169 \brief whether animations are enabled
170
171 The default value of this property is true.
172
173 \sa QAbstractTransition::addAnimation()
174*/
175#endif
176
177// #define QSTATEMACHINE_DEBUG
178
179QStateMachinePrivate::QStateMachinePrivate()
180{
181 QAbstractStatePrivate::isMachine = true;
182
183 state = NotRunning;
184 _startState = 0;
185 processing = false;
186 processingScheduled = false;
187 stop = false;
188 stopProcessingReason = EventQueueEmpty;
189 error = QStateMachine::NoError;
190 globalRestorePolicy = QStateMachine::DontRestoreProperties;
191 signalEventGenerator = 0;
192#ifndef QT_NO_ANIMATION
193 animated = true;
194#endif
195}
196
197QStateMachinePrivate::~QStateMachinePrivate()
198{
199 qDeleteAll(internalEventQueue);
200 qDeleteAll(externalEventQueue);
201}
202
203QStateMachinePrivate *QStateMachinePrivate::get(QStateMachine *q)
204{
205 if (q)
206 return q->d_func();
207 return 0;
208}
209
210QState *QStateMachinePrivate::rootState() const
211{
212 return const_cast<QStateMachine*>(q_func());
213}
214
215static QEvent *cloneEvent(QEvent *e)
216{
217 switch (e->type()) {
218 case QEvent::None:
219 return new QEvent(*e);
220 case QEvent::Timer:
221 return new QTimerEvent(*static_cast<QTimerEvent*>(e));
222 default:
223 Q_ASSERT_X(false, "cloneEvent()", "not implemented");
224 break;
225 }
226 return 0;
227}
228
229const QStateMachinePrivate::Handler qt_kernel_statemachine_handler = {
230 cloneEvent
231};
232
233const QStateMachinePrivate::Handler *QStateMachinePrivate::handler = &qt_kernel_statemachine_handler;
234
235Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler()
236{
237 return &qt_kernel_statemachine_handler;
238}
239
240static int indexOfDescendant(QState *s, QAbstractState *desc)
241{
242 QList<QAbstractState*> childStates = QStatePrivate::get(s)->childStates();
243 for (int i = 0; i < childStates.size(); ++i) {
244 QAbstractState *c = childStates.at(i);
245 if ((c == desc) || QStateMachinePrivate::isDescendantOf(desc, c)) {
246 return i;
247 }
248 }
249 return -1;
250}
251
252bool QStateMachinePrivate::stateEntryLessThan(QAbstractState *s1, QAbstractState *s2)
253{
254 if (s1->parent() == s2->parent()) {
255 return s1->children().indexOf(s1)
256 < s2->children().indexOf(s2);
257 } else if (isDescendantOf(s1, s2)) {
258 return false;
259 } else if (isDescendantOf(s2, s1)) {
260 return true;
261 } else {
262 Q_ASSERT(s1->machine() != 0);
263 QStateMachinePrivate *mach = QStateMachinePrivate::get(s1->machine());
264 QState *lca = mach->findLCA(QList<QAbstractState*>() << s1 << s2);
265 Q_ASSERT(lca != 0);
266 return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2));
267 }
268}
269
270bool QStateMachinePrivate::stateExitLessThan(QAbstractState *s1, QAbstractState *s2)
271{
272 if (s1->parent() == s2->parent()) {
273 return s1->children().indexOf(s1)
274 < s2->children().indexOf(s2);
275 } else if (isDescendantOf(s1, s2)) {
276 return true;
277 } else if (isDescendantOf(s2, s1)) {
278 return false;
279 } else {
280 Q_ASSERT(s1->machine() != 0);
281 QStateMachinePrivate *mach = QStateMachinePrivate::get(s1->machine());
282 QState *lca = mach->findLCA(QList<QAbstractState*>() << s1 << s2);
283 Q_ASSERT(lca != 0);
284 return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2));
285 }
286}
287
288QState *QStateMachinePrivate::findLCA(const QList<QAbstractState*> &states) const
289{
290 if (states.isEmpty())
291 return 0;
292 QList<QState*> ancestors = properAncestors(states.at(0), rootState()->parentState());
293 for (int i = 0; i < ancestors.size(); ++i) {
294 QState *anc = ancestors.at(i);
295 bool ok = true;
296 for (int j = states.size() - 1; (j > 0) && ok; --j) {
297 const QAbstractState *s = states.at(j);
298 if (!isDescendantOf(s, anc))
299 ok = false;
300 }
301 if (ok)
302 return anc;
303 }
304 return 0;
305}
306
307bool QStateMachinePrivate::isPreempted(const QAbstractState *s, const QSet<QAbstractTransition*> &transitions) const
308{
309 QSet<QAbstractTransition*>::const_iterator it;
310 for (it = transitions.constBegin(); it != transitions.constEnd(); ++it) {
311 QAbstractTransition *t = *it;
312 QList<QAbstractState*> lst = t->targetStates();
313 if (!lst.isEmpty()) {
314 lst.prepend(t->sourceState());
315 QAbstractState *lca = findLCA(lst);
316 if (isDescendantOf(s, lca)) {
317#ifdef QSTATEMACHINE_DEBUG
318 qDebug() << q_func() << ':' << transitions << "preempts selection of a transition from"
319 << s << "because" << s << "is a descendant of" << lca;
320#endif
321 return true;
322 }
323 }
324 }
325 return false;
326}
327
328QSet<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *event) const
329{
330 Q_Q(const QStateMachine);
331 QSet<QAbstractTransition*> enabledTransitions;
332 QSet<QAbstractState*>::const_iterator it;
333 const_cast<QStateMachine*>(q)->beginSelectTransitions(event);
334 for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
335 QAbstractState *state = *it;
336 if (!isAtomic(state))
337 continue;
338 if (isPreempted(state, enabledTransitions))
339 continue;
340 QList<QState*> lst = properAncestors(state, rootState()->parentState());
341 if (QState *grp = toStandardState(state))
342 lst.prepend(grp);
343 bool found = false;
344 for (int j = 0; (j < lst.size()) && !found; ++j) {
345 QState *s = lst.at(j);
346 QList<QAbstractTransition*> transitions = QStatePrivate::get(s)->transitions();
347 for (int k = 0; k < transitions.size(); ++k) {
348 QAbstractTransition *t = transitions.at(k);
349 if (QAbstractTransitionPrivate::get(t)->callEventTest(event)) {
350#ifdef QSTATEMACHINE_DEBUG
351 qDebug() << q << ": selecting transition" << t;
352#endif
353 enabledTransitions.insert(t);
354 found = true;
355 break;
356 }
357 }
358 }
359 }
360 const_cast<QStateMachine*>(q)->endSelectTransitions(event);
361 return enabledTransitions;
362}
363
364void QStateMachinePrivate::microstep(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
365{
366#ifdef QSTATEMACHINE_DEBUG
367 qDebug() << q_func() << ": begin microstep( enabledTransitions:" << enabledTransitions << ')';
368 qDebug() << q_func() << ": configuration before exiting states:" << configuration;
369#endif
370 QList<QAbstractState*> exitedStates = exitStates(event, enabledTransitions);
371#ifdef QSTATEMACHINE_DEBUG
372 qDebug() << q_func() << ": configuration after exiting states:" << configuration;
373#endif
374 executeTransitionContent(event, enabledTransitions);
375 QList<QAbstractState*> enteredStates = enterStates(event, enabledTransitions);
376#ifndef QT_NO_PROPERTIES
377 applyProperties(enabledTransitions, exitedStates, enteredStates);
378#endif
379#ifdef QSTATEMACHINE_DEBUG
380 qDebug() << q_func() << ": configuration after entering states:" << configuration;
381 qDebug() << q_func() << ": end microstep";
382#endif
383}
384
385QList<QAbstractState*> QStateMachinePrivate::exitStates(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
386{
387// qDebug() << "exitStates(" << enabledTransitions << ')';
388 QSet<QAbstractState*> statesToExit;
389// QSet<QAbstractState*> statesToSnapshot;
390 for (int i = 0; i < enabledTransitions.size(); ++i) {
391 QAbstractTransition *t = enabledTransitions.at(i);
392 QList<QAbstractState*> lst = t->targetStates();
393 if (lst.isEmpty())
394 continue;
395 lst.prepend(t->sourceState());
396 QAbstractState *lca = findLCA(lst);
397 if (lca == 0) {
398 setError(QStateMachine::NoCommonAncestorForTransitionError, t->sourceState());
399 lst = pendingErrorStates.toList();
400 lst.prepend(t->sourceState());
401
402 lca = findLCA(lst);
403 Q_ASSERT(lca != 0);
404 }
405
406 {
407 QSet<QAbstractState*>::const_iterator it;
408 for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
409 QAbstractState *s = *it;
410 if (isDescendantOf(s, lca))
411 statesToExit.insert(s);
412 }
413 }
414 }
415 QList<QAbstractState*> statesToExit_sorted = statesToExit.toList();
416 qSort(statesToExit_sorted.begin(), statesToExit_sorted.end(), stateExitLessThan);
417 for (int i = 0; i < statesToExit_sorted.size(); ++i) {
418 QAbstractState *s = statesToExit_sorted.at(i);
419 if (QState *grp = toStandardState(s)) {
420 QList<QHistoryState*> hlst = QStatePrivate::get(grp)->historyStates();
421 for (int j = 0; j < hlst.size(); ++j) {
422 QHistoryState *h = hlst.at(j);
423 QHistoryStatePrivate::get(h)->configuration.clear();
424 QSet<QAbstractState*>::const_iterator it;
425 for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
426 QAbstractState *s0 = *it;
427 if (QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) {
428 if (isAtomic(s0) && isDescendantOf(s0, s))
429 QHistoryStatePrivate::get(h)->configuration.append(s0);
430 } else if (s0->parentState() == s) {
431 QHistoryStatePrivate::get(h)->configuration.append(s0);
432 }
433 }
434#ifdef QSTATEMACHINE_DEBUG
435 qDebug() << q_func() << ": recorded" << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) ? "deep" : "shallow")
436 << "history for" << s << "in" << h << ':' << QHistoryStatePrivate::get(h)->configuration;
437#endif
438 }
439 }
440 }
441 for (int i = 0; i < statesToExit_sorted.size(); ++i) {
442 QAbstractState *s = statesToExit_sorted.at(i);
443#ifdef QSTATEMACHINE_DEBUG
444 qDebug() << q_func() << ": exiting" << s;
445#endif
446 QAbstractStatePrivate::get(s)->callOnExit(event);
447 configuration.remove(s);
448 QAbstractStatePrivate::get(s)->emitExited();
449 }
450 return statesToExit_sorted;
451}
452
453void QStateMachinePrivate::executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
454{
455 for (int i = 0; i < enabledTransitions.size(); ++i) {
456 QAbstractTransition *t = enabledTransitions.at(i);
457#ifdef QSTATEMACHINE_DEBUG
458 qDebug() << q_func() << ": triggering" << t;
459#endif
460 QAbstractTransitionPrivate::get(t)->callOnTransition(event);
461 QAbstractTransitionPrivate::get(t)->emitTriggered();
462 }
463}
464
465QList<QAbstractState*> QStateMachinePrivate::enterStates(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
466{
467#ifdef QSTATEMACHINE_DEBUG
468 Q_Q(QStateMachine);
469#endif
470// qDebug() << "enterStates(" << enabledTransitions << ')';
471 QSet<QAbstractState*> statesToEnter;
472 QSet<QAbstractState*> statesForDefaultEntry;
473
474 if (pendingErrorStates.isEmpty()) {
475 for (int i = 0; i < enabledTransitions.size(); ++i) {
476 QAbstractTransition *t = enabledTransitions.at(i);
477 QList<QAbstractState*> lst = t->targetStates();
478 if (lst.isEmpty())
479 continue;
480 lst.prepend(t->sourceState());
481 QState *lca = findLCA(lst);
482 for (int j = 1; j < lst.size(); ++j) {
483 QAbstractState *s = lst.at(j);
484 addStatesToEnter(s, lca, statesToEnter, statesForDefaultEntry);
485 if (isParallel(lca)) {
486 QList<QAbstractState*> lcac = QStatePrivate::get(lca)->childStates();
487 foreach (QAbstractState* child,lcac) {
488 if (!statesToEnter.contains(child))
489 addStatesToEnter(child,lca,statesToEnter,statesForDefaultEntry);
490 }
491 }
492 }
493 }
494 }
495
496 // Did an error occur while selecting transitions? Then we enter the error state.
497 if (!pendingErrorStates.isEmpty()) {
498 statesToEnter.clear();
499 statesToEnter = pendingErrorStates;
500 statesForDefaultEntry = pendingErrorStatesForDefaultEntry;
501 pendingErrorStates.clear();
502 pendingErrorStatesForDefaultEntry.clear();
503 }
504
505 QList<QAbstractState*> statesToEnter_sorted = statesToEnter.toList();
506 qSort(statesToEnter_sorted.begin(), statesToEnter_sorted.end(), stateEntryLessThan);
507
508 for (int i = 0; i < statesToEnter_sorted.size(); ++i) {
509 QAbstractState *s = statesToEnter_sorted.at(i);
510#ifdef QSTATEMACHINE_DEBUG
511 qDebug() << q << ": entering" << s;
512#endif
513 configuration.insert(s);
514 registerTransitions(s);
515 QAbstractStatePrivate::get(s)->callOnEntry(event);
516 QAbstractStatePrivate::get(s)->emitEntered();
517 if (statesForDefaultEntry.contains(s)) {
518 // ### executeContent(s.initial.transition.children())
519 }
520 if (isFinal(s)) {
521 QState *parent = s->parentState();
522 if (parent) {
523 if (parent != rootState()) {
524#ifdef QSTATEMACHINE_DEBUG
525 qDebug() << q << ": emitting finished signal for" << parent;
526#endif
527 QStatePrivate::get(parent)->emitFinished();
528 }
529 QState *grandparent = parent->parentState();
530 if (grandparent && isParallel(grandparent)) {
531 bool allChildStatesFinal = true;
532 QList<QAbstractState*> childStates = QStatePrivate::get(grandparent)->childStates();
533 for (int j = 0; j < childStates.size(); ++j) {
534 QAbstractState *cs = childStates.at(j);
535 if (!isInFinalState(cs)) {
536 allChildStatesFinal = false;
537 break;
538 }
539 }
540 if (allChildStatesFinal && (grandparent != rootState())) {
541#ifdef QSTATEMACHINE_DEBUG
542 qDebug() << q << ": emitting finished signal for" << grandparent;
543#endif
544 QStatePrivate::get(grandparent)->emitFinished();
545 }
546 }
547 }
548 }
549 }
550 {
551 QSet<QAbstractState*>::const_iterator it;
552 for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
553 if (isFinal(*it) && (*it)->parentState() == rootState()) {
554 processing = false;
555 stopProcessingReason = Finished;
556 break;
557 }
558 }
559 }
560// qDebug() << "configuration:" << configuration.toList();
561 return statesToEnter_sorted;
562}
563
564void QStateMachinePrivate::addStatesToEnter(QAbstractState *s, QState *root,
565 QSet<QAbstractState*> &statesToEnter,
566 QSet<QAbstractState*> &statesForDefaultEntry)
567{
568 if (QHistoryState *h = toHistoryState(s)) {
569 QList<QAbstractState*> hconf = QHistoryStatePrivate::get(h)->configuration;
570 if (!hconf.isEmpty()) {
571 for (int k = 0; k < hconf.size(); ++k) {
572 QAbstractState *s0 = hconf.at(k);
573 addStatesToEnter(s0, root, statesToEnter, statesForDefaultEntry);
574 }
575 #ifdef QSTATEMACHINE_DEBUG
576 qDebug() <<q_func() << ": restoring"
577 << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) ? "deep" : "shallow")
578 << "history from" << s << ':' << hconf;
579 #endif
580 } else {
581 QList<QAbstractState*> hlst;
582 if (QHistoryStatePrivate::get(h)->defaultState)
583 hlst.append(QHistoryStatePrivate::get(h)->defaultState);
584
585 if (hlst.isEmpty()) {
586 setError(QStateMachine::NoDefaultStateInHistoryStateError, h);
587 } else {
588 for (int k = 0; k < hlst.size(); ++k) {
589 QAbstractState *s0 = hlst.at(k);
590 addStatesToEnter(s0, root, statesToEnter, statesForDefaultEntry);
591 }
592 #ifdef QSTATEMACHINE_DEBUG
593 qDebug() << q_func() << ": initial history targets for" << s << ':' << hlst;
594 #endif
595 }
596 }
597 } else {
598 if (s == rootState()) {
599 // Error has already been set by exitStates().
600 Q_ASSERT(error != QStateMachine::NoError);
601 return;
602 }
603 statesToEnter.insert(s);
604 if (isParallel(s)) {
605 QState *grp = toStandardState(s);
606 QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates();
607 for (int i = 0; i < lst.size(); ++i) {
608 QAbstractState *child = lst.at(i);
609 addStatesToEnter(child, grp, statesToEnter, statesForDefaultEntry);
610 }
611 } else if (isCompound(s)) {
612 statesForDefaultEntry.insert(s);
613 QState *grp = toStandardState(s);
614 QAbstractState *initial = grp->initialState();
615 if (initial != 0) {
616 Q_ASSERT(initial->machine() == q_func());
617 addStatesToEnter(initial, grp, statesToEnter, statesForDefaultEntry);
618 } else {
619 setError(QStateMachine::NoInitialStateError, grp);
620 return;
621 }
622 }
623 QList<QState*> ancs = properAncestors(s, root);
624 for (int i = 0; i < ancs.size(); ++i) {
625 QState *anc = ancs.at(i);
626 if (!anc->parentState())
627 continue;
628 statesToEnter.insert(anc);
629 if (isParallel(anc)) {
630 QList<QAbstractState*> lst = QStatePrivate::get(anc)->childStates();
631 for (int j = 0; j < lst.size(); ++j) {
632 QAbstractState *child = lst.at(j);
633 bool hasDescendantInList = false;
634 QSet<QAbstractState*>::const_iterator it;
635 for (it = statesToEnter.constBegin(); it != statesToEnter.constEnd(); ++it) {
636 if (isDescendantOf(*it, child)) {
637 hasDescendantInList = true;
638 break;
639 }
640 }
641 if (!hasDescendantInList)
642 addStatesToEnter(child, anc, statesToEnter, statesForDefaultEntry);
643 }
644 }
645 }
646 }
647}
648
649#ifndef QT_NO_PROPERTIES
650
651void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &transitionList,
652 const QList<QAbstractState*> &exitedStates,
653 const QList<QAbstractState*> &enteredStates)
654{
655#ifdef QT_NO_ANIMATION
656 Q_UNUSED(transitionList);
657 Q_UNUSED(exitedStates);
658#else
659 Q_Q(QStateMachine);
660#endif
661 // Process the property assignments of the entered states.
662 QHash<QAbstractState*, QList<QPropertyAssignment> > propertyAssignmentsForState;
663 QHash<RestorableId, QVariant> pendingRestorables = registeredRestorables;
664 for (int i = 0; i < enteredStates.size(); ++i) {
665 QState *s = toStandardState(enteredStates.at(i));
666 if (!s)
667 continue;
668
669 QList<QPropertyAssignment> assignments = QStatePrivate::get(s)->propertyAssignments;
670 for (int j = 0; j < assignments.size(); ++j) {
671 const QPropertyAssignment &assn = assignments.at(j);
672 if (globalRestorePolicy == QStateMachine::RestoreProperties) {
673 registerRestorable(assn.object, assn.propertyName);
674 }
675 pendingRestorables.remove(RestorableId(assn.object, assn.propertyName));
676 propertyAssignmentsForState[s].append(assn);
677 }
678
679 // Remove pending restorables for all parent states to avoid restoring properties
680 // before the state that assigned them is exited. If state does not explicitly
681 // assign a property which is assigned by the parent, it inherits the parent's assignment.
682 QState *parentState = s;
683 while ((parentState = parentState->parentState()) != 0) {
684 assignments = QStatePrivate::get(parentState)->propertyAssignments;
685 for (int j=0; j<assignments.size(); ++j) {
686 const QPropertyAssignment &assn = assignments.at(j);
687 int c = pendingRestorables.remove(RestorableId(assn.object, assn.propertyName));
688 if (c > 0)
689 propertyAssignmentsForState[s].append(assn);
690 }
691 }
692 }
693 if (!pendingRestorables.isEmpty()) {
694 QAbstractState *s;
695 if (!enteredStates.isEmpty())
696 s = enteredStates.last(); // ### handle if parallel
697 else
698 s = 0;
699 propertyAssignmentsForState[s] << restorablesToPropertyList(pendingRestorables);
700 }
701
702#ifndef QT_NO_ANIMATION
703 // Gracefully terminate playing animations for states that are exited.
704 for (int i = 0; i < exitedStates.size(); ++i) {
705 QAbstractState *s = exitedStates.at(i);
706 QList<QAbstractAnimation*> animations = animationsForState.take(s);
707 for (int j = 0; j < animations.size(); ++j) {
708 QAbstractAnimation *anim = animations.at(j);
709 QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
710 stateForAnimation.remove(anim);
711
712 // Stop the (top-level) animation.
713 // ### Stopping nested animation has weird behavior.
714 QAbstractAnimation *topLevelAnim = anim;
715 while (QAnimationGroup *group = topLevelAnim->group())
716 topLevelAnim = group;
717 topLevelAnim->stop();
718
719 if (resetAnimationEndValues.contains(anim)) {
720 qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize
721 resetAnimationEndValues.remove(anim);
722 }
723 QPropertyAssignment assn = propertyForAnimation.take(anim);
724 Q_ASSERT(assn.object != 0);
725 // If there is no property assignment that sets this property,
726 // set the property to its target value.
727 bool found = false;
728 QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it;
729 for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) {
730 const QList<QPropertyAssignment> &assignments = it.value();
731 for (int k = 0; k < assignments.size(); ++k) {
732 if ((assignments.at(k).object == assn.object)
733 && (assignments.at(k).propertyName == assn.propertyName)) {
734 found = true;
735 break;
736 }
737 }
738 }
739 if (!found) {
740 assn.object->setProperty(assn.propertyName, assn.value);
741 }
742 }
743 }
744
745 // Find the animations to use for the state change.
746 QList<QAbstractAnimation*> selectedAnimations;
747 if (animated) {
748 for (int i = 0; i < transitionList.size(); ++i) {
749 QAbstractTransition *transition = transitionList.at(i);
750
751 selectedAnimations << transition->animations();
752 selectedAnimations << defaultAnimationsForSource.values(transition->sourceState());
753
754 QList<QAbstractState *> targetStates = transition->targetStates();
755 for (int j=0; j<targetStates.size(); ++j)
756 selectedAnimations << defaultAnimationsForTarget.values(targetStates.at(j));
757 }
758 selectedAnimations << defaultAnimations;
759 }
760
761 // Initialize animations from property assignments.
762 for (int i = 0; i < selectedAnimations.size(); ++i) {
763 QAbstractAnimation *anim = selectedAnimations.at(i);
764 QHash<QAbstractState*, QList<QPropertyAssignment> >::iterator it;
765 for (it = propertyAssignmentsForState.begin(); it != propertyAssignmentsForState.end(); ) {
766 QList<QPropertyAssignment>::iterator it2;
767 QAbstractState *s = it.key();
768 QList<QPropertyAssignment> &assignments = it.value();
769 for (it2 = assignments.begin(); it2 != assignments.end(); ) {
770 QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret;
771 ret = initializeAnimation(anim, *it2);
772 QList<QAbstractAnimation*> handlers = ret.first;
773 if (!handlers.isEmpty()) {
774 for (int j = 0; j < handlers.size(); ++j) {
775 QAbstractAnimation *a = handlers.at(j);
776 propertyForAnimation.insert(a, *it2);
777 stateForAnimation.insert(a, s);
778 animationsForState[s].append(a);
779 // ### connect to just the top-level animation?
780 QObject::connect(a, SIGNAL(finished()), q, SLOT(_q_animationFinished()), Qt::UniqueConnection);
781 }
782 it2 = assignments.erase(it2);
783 } else {
784 ++it2;
785 }
786 for (int j = 0; j < ret.second.size(); ++j)
787 resetAnimationEndValues.insert(ret.second.at(j));
788 }
789 if (assignments.isEmpty())
790 it = propertyAssignmentsForState.erase(it);
791 else
792 ++it;
793 }
794 // We require that at least one animation is valid.
795 // ### generalize
796 QList<QVariantAnimation*> variantAnims = qFindChildren<QVariantAnimation*>(anim);
797 if (QVariantAnimation *va = qobject_cast<QVariantAnimation*>(anim))
798 variantAnims.append(va);
799
800 bool hasValidEndValue = false;
801 for (int j = 0; j < variantAnims.size(); ++j) {
802 if (variantAnims.at(j)->endValue().isValid()) {
803 hasValidEndValue = true;
804 break;
805 }
806 }
807
808 if (hasValidEndValue) {
809 if (anim->state() == QAbstractAnimation::Running) {
810 // The animation is still running. This can happen if the
811 // animation is a group, and one of its children just finished,
812 // and that caused a state to emit its propertiesAssigned() signal, and
813 // that triggered a transition in the machine.
814 // Just stop the animation so it is correctly restarted again.
815 anim->stop();
816 }
817 anim->start();
818 }
819 }
820#endif // !QT_NO_ANIMATION
821
822 // Immediately set the properties that are not animated.
823 {
824 QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it;
825 for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) {
826 const QList<QPropertyAssignment> &assignments = it.value();
827 for (int i = 0; i < assignments.size(); ++i) {
828 const QPropertyAssignment &assn = assignments.at(i);
829 assn.object->setProperty(assn.propertyName, assn.value);
830 }
831 }
832 }
833
834 // Emit propertiesAssigned signal for entered states that have no animated properties.
835 for (int i = 0; i < enteredStates.size(); ++i) {
836 QState *s = toStandardState(enteredStates.at(i));
837 if (s
838#ifndef QT_NO_ANIMATION
839 && !animationsForState.contains(s)
840#endif
841 )
842 QStatePrivate::get(s)->emitPropertiesAssigned();
843 }
844}
845
846#endif // QT_NO_PROPERTIES
847
848bool QStateMachinePrivate::isFinal(const QAbstractState *s)
849{
850 return s && (QAbstractStatePrivate::get(s)->stateType == QAbstractStatePrivate::FinalState);
851}
852
853bool QStateMachinePrivate::isParallel(const QAbstractState *s)
854{
855 const QState *ss = toStandardState(s);
856 return ss && (QStatePrivate::get(ss)->childMode == QState::ParallelStates);
857}
858
859bool QStateMachinePrivate::isCompound(const QAbstractState *s) const
860{
861 const QState *group = toStandardState(s);
862 if (!group)
863 return false;
864 bool isMachine = QStatePrivate::get(group)->isMachine;
865 // Don't treat the machine as compound if it's a sub-state of this machine
866 if (isMachine && (group != rootState()))
867 return false;
868 return (!isParallel(group) && !QStatePrivate::get(group)->childStates().isEmpty())
869 || isMachine;
870}
871
872bool QStateMachinePrivate::isAtomic(const QAbstractState *s) const
873{
874 const QState *ss = toStandardState(s);
875 return (ss && QStatePrivate::get(ss)->childStates().isEmpty())
876 || isFinal(s)
877 // Treat the machine as atomic if it's a sub-state of this machine
878 || (ss && QStatePrivate::get(ss)->isMachine && (ss != rootState()));
879}
880
881
882bool QStateMachinePrivate::isDescendantOf(const QAbstractState *state, const QAbstractState *other)
883{
884 Q_ASSERT(state != 0);
885 for (QAbstractState *s = state->parentState(); s != 0; s = s->parentState()) {
886 if (s == other)
887 return true;
888 }
889 return false;
890}
891
892QList<QState*> QStateMachinePrivate::properAncestors(const QAbstractState *state, const QState *upperBound)
893{
894 Q_ASSERT(state != 0);
895 QList<QState*> result;
896 for (QState *s = state->parentState(); s && s != upperBound; s = s->parentState()) {
897 result.append(s);
898 }
899 return result;
900}
901
902QState *QStateMachinePrivate::toStandardState(QAbstractState *state)
903{
904 if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::StandardState))
905 return static_cast<QState*>(state);
906 return 0;
907}
908
909const QState *QStateMachinePrivate::toStandardState(const QAbstractState *state)
910{
911 if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::StandardState))
912 return static_cast<const QState*>(state);
913 return 0;
914}
915
916QFinalState *QStateMachinePrivate::toFinalState(QAbstractState *state)
917{
918 if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::FinalState))
919 return static_cast<QFinalState*>(state);
920 return 0;
921}
922
923QHistoryState *QStateMachinePrivate::toHistoryState(QAbstractState *state)
924{
925 if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::HistoryState))
926 return static_cast<QHistoryState*>(state);
927 return 0;
928}
929
930bool QStateMachinePrivate::isInFinalState(QAbstractState* s) const
931{
932 if (isCompound(s)) {
933 QState *grp = toStandardState(s);
934 QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates();
935 for (int i = 0; i < lst.size(); ++i) {
936 QAbstractState *cs = lst.at(i);
937 if (isFinal(cs) && configuration.contains(cs))
938 return true;
939 }
940 return false;
941 } else if (isParallel(s)) {
942 QState *grp = toStandardState(s);
943 QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates();
944 for (int i = 0; i < lst.size(); ++i) {
945 QAbstractState *cs = lst.at(i);
946 if (!isInFinalState(cs))
947 return false;
948 }
949 return true;
950 }
951 else
952 return false;
953}
954
955#ifndef QT_NO_PROPERTIES
956
957void QStateMachinePrivate::registerRestorable(QObject *object, const QByteArray &propertyName)
958{
959 RestorableId id(object, propertyName);
960 if (!registeredRestorables.contains(id))
961 registeredRestorables.insert(id, object->property(propertyName));
962}
963
964QList<QPropertyAssignment> QStateMachinePrivate::restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const
965{
966 QList<QPropertyAssignment> result;
967 QHash<RestorableId, QVariant>::const_iterator it;
968 for (it = restorables.constBegin(); it != restorables.constEnd(); ++it) {
969// qDebug() << "restorable:" << it.key().first << it.key().second << it.value();
970 result.append(QPropertyAssignment(it.key().first, it.key().second, it.value(), /*explicitlySet=*/false));
971 }
972 return result;
973}
974
975/*!
976 \internal
977 Returns true if the variable with the given \a id has been registered for restoration.
978*/
979bool QStateMachinePrivate::hasRestorable(QObject *object, const QByteArray &propertyName) const
980{
981 return registeredRestorables.contains(RestorableId(object, propertyName));
982}
983
984QVariant QStateMachinePrivate::restorableValue(QObject *object, const QByteArray &propertyName) const
985{
986 return registeredRestorables.value(RestorableId(object, propertyName), QVariant());
987}
988
989
990/*!
991 \internal
992 Unregisters the variable identified by \a id
993*/
994void QStateMachinePrivate::unregisterRestorable(QObject *object, const QByteArray &propertyName)
995{
996// qDebug() << "unregisterRestorable(" << object << propertyName << ')';
997 RestorableId id(object, propertyName);
998 registeredRestorables.remove(id);
999}
1000
1001#endif // QT_NO_PROPERTIES
1002
1003QAbstractState *QStateMachinePrivate::findErrorState(QAbstractState *context)
1004{
1005 // Find error state recursively in parent hierarchy if not set explicitly for context state
1006 QAbstractState *errorState = 0;
1007 if (context != 0) {
1008 QState *s = toStandardState(context);
1009 if (s != 0)
1010 errorState = s->errorState();
1011
1012 if (errorState == 0)
1013 errorState = findErrorState(context->parentState());
1014 }
1015
1016 return errorState;
1017}
1018
1019void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractState *currentContext)
1020{
1021 Q_Q(QStateMachine);
1022
1023 error = errorCode;
1024 switch (errorCode) {
1025 case QStateMachine::NoInitialStateError:
1026 Q_ASSERT(currentContext != 0);
1027
1028 errorString = QStateMachine::tr("Missing initial state in compound state '%1'")
1029 .arg(currentContext->objectName());
1030
1031 break;
1032 case QStateMachine::NoDefaultStateInHistoryStateError:
1033 Q_ASSERT(currentContext != 0);
1034
1035 errorString = QStateMachine::tr("Missing default state in history state '%1'")
1036 .arg(currentContext->objectName());
1037 break;
1038
1039 case QStateMachine::NoCommonAncestorForTransitionError:
1040 Q_ASSERT(currentContext != 0);
1041
1042 errorString = QStateMachine::tr("No common ancestor for targets and source of transition from state '%1'")
1043 .arg(currentContext->objectName());
1044 break;
1045 default:
1046 errorString = QStateMachine::tr("Unknown error");
1047 };
1048
1049 pendingErrorStates.clear();
1050 pendingErrorStatesForDefaultEntry.clear();
1051
1052 QAbstractState *currentErrorState = findErrorState(currentContext);
1053
1054 // Avoid infinite loop if the error state itself has an error
1055 if (currentContext == currentErrorState)
1056 currentErrorState = 0;
1057
1058 Q_ASSERT(currentErrorState != rootState());
1059
1060 if (currentErrorState != 0) {
1061 QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext);
1062 addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry);
1063 } else {
1064 qWarning("Unrecoverable error detected in running state machine: %s",
1065 qPrintable(errorString));
1066 q->stop();
1067 }
1068}
1069
1070#ifndef QT_NO_ANIMATION
1071
1072QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> >
1073QStateMachinePrivate::initializeAnimation(QAbstractAnimation *abstractAnimation,
1074 const QPropertyAssignment &prop)
1075{
1076 QList<QAbstractAnimation*> handledAnimations;
1077 QList<QAbstractAnimation*> localResetEndValues;
1078 QAnimationGroup *group = qobject_cast<QAnimationGroup*>(abstractAnimation);
1079 if (group) {
1080 for (int i = 0; i < group->animationCount(); ++i) {
1081 QAbstractAnimation *animationChild = group->animationAt(i);
1082 QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret;
1083 ret = initializeAnimation(animationChild, prop);
1084 handledAnimations << ret.first;
1085 localResetEndValues << ret.second;
1086 }
1087 } else {
1088 QPropertyAnimation *animation = qobject_cast<QPropertyAnimation *>(abstractAnimation);
1089 if (animation != 0
1090 && prop.object == animation->targetObject()
1091 && prop.propertyName == animation->propertyName()) {
1092
1093 // Only change end value if it is undefined
1094 if (!animation->endValue().isValid()) {
1095 animation->setEndValue(prop.value);
1096 localResetEndValues.append(animation);
1097 }
1098 handledAnimations.append(animation);
1099 }
1100 }
1101 return qMakePair(handledAnimations, localResetEndValues);
1102}
1103
1104void QStateMachinePrivate::_q_animationFinished()
1105{
1106 Q_Q(QStateMachine);
1107 QAbstractAnimation *anim = qobject_cast<QAbstractAnimation*>(q->sender());
1108 Q_ASSERT(anim != 0);
1109 QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
1110 if (resetAnimationEndValues.contains(anim)) {
1111 qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize
1112 resetAnimationEndValues.remove(anim);
1113 }
1114
1115#ifndef QT_NO_PROPERTIES
1116 // Set the final property value.
1117 QPropertyAssignment assn = propertyForAnimation.take(anim);
1118 Q_ASSERT(assn.object != 0);
1119 assn.object->setProperty(assn.propertyName, assn.value);
1120 if (!assn.explicitlySet)
1121 unregisterRestorable(assn.object, assn.propertyName);
1122#endif
1123
1124 QAbstractState *state = stateForAnimation.take(anim);
1125 Q_ASSERT(state != 0);
1126 QHash<QAbstractState*, QList<QAbstractAnimation*> >::iterator it;
1127 it = animationsForState.find(state);
1128 Q_ASSERT(it != animationsForState.end());
1129 QList<QAbstractAnimation*> &animations = it.value();
1130 animations.removeOne(anim);
1131 if (animations.isEmpty()) {
1132 animationsForState.erase(it);
1133 QStatePrivate::get(toStandardState(state))->emitPropertiesAssigned();
1134 }
1135}
1136
1137#endif // !QT_NO_ANIMATION
1138
1139namespace {
1140
1141class StartState : public QState
1142{
1143public:
1144 StartState(QState *parent)
1145 : QState(parent) {}
1146protected:
1147 void onEntry(QEvent *) {}
1148 void onExit(QEvent *) {}
1149};
1150
1151class InitialTransition : public QAbstractTransition
1152{
1153public:
1154 InitialTransition(QAbstractState *target)
1155 : QAbstractTransition()
1156 { setTargetState(target); }
1157protected:
1158 virtual bool eventTest(QEvent *) { return true; }
1159 virtual void onTransition(QEvent *) {}
1160};
1161
1162} // namespace
1163
1164QState *QStateMachinePrivate::startState()
1165{
1166 Q_Q(QStateMachine);
1167 if (_startState == 0)
1168 _startState = new StartState(q);
1169 return _startState;
1170}
1171
1172void QStateMachinePrivate::removeStartState()
1173{
1174 delete _startState;
1175 _startState = 0;
1176}
1177
1178void QStateMachinePrivate::_q_start()
1179{
1180 Q_Q(QStateMachine);
1181 Q_ASSERT(state == Starting);
1182 Q_ASSERT(rootState() != 0);
1183 QAbstractState *initial = rootState()->initialState();
1184 configuration.clear();
1185 qDeleteAll(internalEventQueue);
1186 internalEventQueue.clear();
1187 qDeleteAll(externalEventQueue);
1188 externalEventQueue.clear();
1189
1190#ifdef QSTATEMACHINE_DEBUG
1191 qDebug() << q << ": starting";
1192#endif
1193 state = Running;
1194 processingScheduled = true; // we call _q_process() below
1195 emit q->started();
1196
1197 QState *start = startState();
1198 Q_ASSERT(start != 0);
1199
1200 QList<QAbstractTransition*> transitions = QStatePrivate::get(start)->transitions();
1201
1202 // If a transition has already been added, then we skip this step, as the
1203 // initial transition in that case has been overridden.
1204 if (transitions.isEmpty()) {
1205 QAbstractTransition *initialTransition = new InitialTransition(initial);
1206 start->addTransition(initialTransition);
1207 transitions.append(initialTransition);
1208 }
1209
1210 QEvent nullEvent(QEvent::None);
1211 executeTransitionContent(&nullEvent, transitions);
1212 QList<QAbstractState*> enteredStates = enterStates(&nullEvent, transitions);
1213#ifndef QT_NO_PROPERTIES
1214 applyProperties(transitions, QList<QAbstractState*>() << start,
1215 enteredStates);
1216#endif
1217 removeStartState();
1218
1219#ifdef QSTATEMACHINE_DEBUG
1220 qDebug() << q << ": initial configuration:" << configuration;
1221#endif
1222 _q_process();
1223}
1224
1225void QStateMachinePrivate::_q_process()
1226{
1227 Q_Q(QStateMachine);
1228 Q_ASSERT(state == Running);
1229 Q_ASSERT(!processing);
1230 processing = true;
1231 processingScheduled = false;
1232#ifdef QSTATEMACHINE_DEBUG
1233 qDebug() << q << ": starting the event processing loop";
1234#endif
1235 while (processing) {
1236 if (stop) {
1237 stop = false;
1238 processing = false;
1239 stopProcessingReason = Stopped;
1240 break;
1241 }
1242 QSet<QAbstractTransition*> enabledTransitions;
1243 QEvent *e = new QEvent(QEvent::None);
1244 enabledTransitions = selectTransitions(e);
1245 if (enabledTransitions.isEmpty()) {
1246 delete e;
1247 e = 0;
1248 }
1249 if (enabledTransitions.isEmpty() && ((e = dequeueInternalEvent()) != 0)) {
1250#ifdef QSTATEMACHINE_DEBUG
1251 qDebug() << q << ": dequeued internal event" << e << "of type" << e->type();
1252#endif
1253 enabledTransitions = selectTransitions(e);
1254 if (enabledTransitions.isEmpty()) {
1255 delete e;
1256 e = 0;
1257 }
1258 }
1259 if (enabledTransitions.isEmpty()) {
1260 if ((e = dequeueExternalEvent()) != 0) {
1261#ifdef QSTATEMACHINE_DEBUG
1262 qDebug() << q << ": dequeued external event" << e << "of type" << e->type();
1263#endif
1264 enabledTransitions = selectTransitions(e);
1265 if (enabledTransitions.isEmpty()) {
1266 delete e;
1267 e = 0;
1268 }
1269 } else {
1270 if (isInternalEventQueueEmpty()) {
1271 processing = false;
1272 stopProcessingReason = EventQueueEmpty;
1273 }
1274 }
1275 }
1276 if (!enabledTransitions.isEmpty()) {
1277 q->beginMicrostep(e);
1278 microstep(e, enabledTransitions.toList());
1279 q->endMicrostep(e);
1280 }
1281#ifdef QSTATEMACHINE_DEBUG
1282 else {
1283 qDebug() << q << ": no transitions enabled";
1284 }
1285#endif
1286 delete e;
1287 }
1288#ifdef QSTATEMACHINE_DEBUG
1289 qDebug() << q << ": finished the event processing loop";
1290#endif
1291 switch (stopProcessingReason) {
1292 case EventQueueEmpty:
1293 break;
1294 case Finished:
1295 state = NotRunning;
1296 cancelAllDelayedEvents();
1297 unregisterAllTransitions();
1298 emit q->finished();
1299 break;
1300 case Stopped:
1301 state = NotRunning;
1302 cancelAllDelayedEvents();
1303 unregisterAllTransitions();
1304 emit q->stopped();
1305 break;
1306 }
1307}
1308
1309void QStateMachinePrivate::postInternalEvent(QEvent *e)
1310{
1311 QMutexLocker locker(&internalEventMutex);
1312 internalEventQueue.append(e);
1313}
1314
1315void QStateMachinePrivate::postExternalEvent(QEvent *e)
1316{
1317 QMutexLocker locker(&externalEventMutex);
1318 externalEventQueue.append(e);
1319}
1320
1321QEvent *QStateMachinePrivate::dequeueInternalEvent()
1322{
1323 QMutexLocker locker(&internalEventMutex);
1324 if (internalEventQueue.isEmpty())
1325 return 0;
1326 return internalEventQueue.takeFirst();
1327}
1328
1329QEvent *QStateMachinePrivate::dequeueExternalEvent()
1330{
1331 QMutexLocker locker(&externalEventMutex);
1332 if (externalEventQueue.isEmpty())
1333 return 0;
1334 return externalEventQueue.takeFirst();
1335}
1336
1337bool QStateMachinePrivate::isInternalEventQueueEmpty()
1338{
1339 QMutexLocker locker(&internalEventMutex);
1340 return internalEventQueue.isEmpty();
1341}
1342
1343bool QStateMachinePrivate::isExternalEventQueueEmpty()
1344{
1345 QMutexLocker locker(&externalEventMutex);
1346 return externalEventQueue.isEmpty();
1347}
1348
1349void QStateMachinePrivate::processEvents(EventProcessingMode processingMode)
1350{
1351 Q_Q(QStateMachine);
1352 if ((state != Running) || processing || processingScheduled)
1353 return;
1354 switch (processingMode) {
1355 case DirectProcessing:
1356 if (QThread::currentThread() == q->thread()) {
1357 _q_process();
1358 break;
1359 } // fallthrough -- processing must be done in the machine thread
1360 case QueuedProcessing:
1361 processingScheduled = true;
1362 QMetaObject::invokeMethod(q, "_q_process", Qt::QueuedConnection);
1363 break;
1364 }
1365}
1366
1367void QStateMachinePrivate::cancelAllDelayedEvents()
1368{
1369 Q_Q(QStateMachine);
1370 QMutexLocker locker(&delayedEventsMutex);
1371 QHash<int, QEvent*>::const_iterator it;
1372 for (it = delayedEvents.constBegin(); it != delayedEvents.constEnd(); ++it) {
1373 int id = it.key();
1374 QEvent *e = it.value();
1375 q->killTimer(id);
1376 delete e;
1377 }
1378 delayedEvents.clear();
1379}
1380
1381namespace {
1382
1383class GoToStateTransition : public QAbstractTransition
1384{
1385public:
1386 GoToStateTransition(QAbstractState *target)
1387 : QAbstractTransition()
1388 { setTargetState(target); }
1389protected:
1390 void onTransition(QEvent *) { deleteLater(); }
1391 bool eventTest(QEvent *) { return true; }
1392};
1393
1394} // namespace
1395
1396/*!
1397 \internal
1398
1399 Causes this state machine to unconditionally transition to the given
1400 \a targetState.
1401
1402 Provides a backdoor for using the state machine "imperatively"; i.e. rather
1403 than defining explicit transitions, you drive the machine's execution by
1404 calling this function. It breaks the whole integrity of the
1405 transition-driven model, but is provided for pragmatic reasons.
1406*/
1407void QStateMachinePrivate::goToState(QAbstractState *targetState)
1408{
1409 if (!targetState) {
1410 qWarning("QStateMachine::goToState(): cannot go to null state");
1411 return;
1412 }
1413
1414 if (configuration.contains(targetState))
1415 return;
1416
1417 QState *sourceState = 0;
1418 if (state == Running) {
1419 QSet<QAbstractState*>::const_iterator it;
1420 for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
1421 sourceState = toStandardState(*it);
1422 if (sourceState != 0)
1423 break;
1424 }
1425 } else {
1426 sourceState = startState();
1427 }
1428
1429 Q_ASSERT(sourceState != 0);
1430 // Reuse previous GoToStateTransition in case of several calls to
1431 // goToState() in a row.
1432 GoToStateTransition *trans = qFindChild<GoToStateTransition*>(sourceState);
1433 if (!trans) {
1434 trans = new GoToStateTransition(targetState);
1435 sourceState->addTransition(trans);
1436 } else {
1437 trans->setTargetState(targetState);
1438 }
1439
1440 processEvents(QueuedProcessing);
1441}
1442
1443void QStateMachinePrivate::registerTransitions(QAbstractState *state)
1444{
1445 QState *group = toStandardState(state);
1446 if (!group)
1447 return;
1448 QList<QAbstractTransition*> transitions = QStatePrivate::get(group)->transitions();
1449 for (int i = 0; i < transitions.size(); ++i) {
1450 QAbstractTransition *t = transitions.at(i);
1451 if (QSignalTransition *st = qobject_cast<QSignalTransition*>(t)) {
1452 registerSignalTransition(st);
1453 }
1454#ifndef QT_NO_STATEMACHINE_EVENTFILTER
1455 else if (QEventTransition *oet = qobject_cast<QEventTransition*>(t)) {
1456 registerEventTransition(oet);
1457 }
1458#endif
1459 }
1460}
1461
1462void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition)
1463{
1464 if (QSignalTransition *st = qobject_cast<QSignalTransition*>(transition)) {
1465 unregisterSignalTransition(st);
1466 }
1467#ifndef QT_NO_STATEMACHINE_EVENTFILTER
1468 else if (QEventTransition *oet = qobject_cast<QEventTransition*>(transition)) {
1469 unregisterEventTransition(oet);
1470 }
1471#endif
1472}
1473
1474void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transition)
1475{
1476 Q_Q(QStateMachine);
1477 if (QSignalTransitionPrivate::get(transition)->signalIndex != -1)
1478 return; // already registered
1479 QObject *sender = QSignalTransitionPrivate::get(transition)->sender;
1480 if (!sender)
1481 return;
1482 QByteArray signal = QSignalTransitionPrivate::get(transition)->signal;
1483 if (signal.startsWith('0'+QSIGNAL_CODE))
1484 signal.remove(0, 1);
1485 const QMetaObject *meta = sender->metaObject();
1486 int signalIndex = meta->indexOfSignal(signal);
1487 int originalSignalIndex = signalIndex;
1488 if (signalIndex == -1) {
1489 signalIndex = meta->indexOfSignal(QMetaObject::normalizedSignature(signal));
1490 if (signalIndex == -1) {
1491 qWarning("QSignalTransition: no such signal: %s::%s",
1492 meta->className(), signal.constData());
1493 return;
1494 }
1495 }
1496 // The signal index we actually want to connect to is the one
1497 // that is going to be sent, i.e. the non-cloned original index.
1498 while (meta->method(signalIndex).attributes() & QMetaMethod::Cloned)
1499 --signalIndex;
1500
1501 QVector<int> &connectedSignalIndexes = connections[sender];
1502 if (connectedSignalIndexes.size() <= signalIndex)
1503 connectedSignalIndexes.resize(signalIndex+1);
1504 if (connectedSignalIndexes.at(signalIndex) == 0) {
1505 if (!signalEventGenerator)
1506 signalEventGenerator = new QSignalEventGenerator(q);
1507 bool ok = QMetaObject::connect(sender, signalIndex, signalEventGenerator,
1508 signalEventGenerator->metaObject()->methodOffset());
1509 if (!ok) {
1510#ifdef QSTATEMACHINE_DEBUG
1511 qDebug() << q << ": FAILED to add signal transition from" << transition->sourceState()
1512 << ": ( sender =" << sender << ", signal =" << signal
1513 << ", targets =" << transition->targetStates() << ')';
1514#endif
1515 return;
1516 }
1517 }
1518 ++connectedSignalIndexes[signalIndex];
1519 QSignalTransitionPrivate::get(transition)->signalIndex = signalIndex;
1520 QSignalTransitionPrivate::get(transition)->originalSignalIndex = originalSignalIndex;
1521#ifdef QSTATEMACHINE_DEBUG
1522 qDebug() << q << ": added signal transition from" << transition->sourceState()
1523 << ": ( sender =" << sender << ", signal =" << signal
1524 << ", targets =" << transition->targetStates() << ')';
1525#endif
1526}
1527
1528void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transition)
1529{
1530 int signalIndex = QSignalTransitionPrivate::get(transition)->signalIndex;
1531 if (signalIndex == -1)
1532 return; // not registered
1533 QSignalTransitionPrivate::get(transition)->signalIndex = -1;
1534 const QObject *sender = QSignalTransitionPrivate::get(transition)->sender;
1535 QVector<int> &connectedSignalIndexes = connections[sender];
1536 Q_ASSERT(connectedSignalIndexes.size() > signalIndex);
1537 Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0);
1538 if (--connectedSignalIndexes[signalIndex] == 0) {
1539 Q_ASSERT(signalEventGenerator != 0);
1540 QMetaObject::disconnect(sender, signalIndex, signalEventGenerator,
1541 signalEventGenerator->metaObject()->methodOffset());
1542 int sum = 0;
1543 for (int i = 0; i < connectedSignalIndexes.size(); ++i)
1544 sum += connectedSignalIndexes.at(i);
1545 if (sum == 0)
1546 connections.remove(sender);
1547 }
1548}
1549
1550void QStateMachinePrivate::unregisterAllTransitions()
1551{
1552 Q_Q(QStateMachine);
1553 {
1554 QList<QSignalTransition*> transitions = qFindChildren<QSignalTransition*>(rootState());
1555 for (int i = 0; i < transitions.size(); ++i) {
1556 QSignalTransition *t = transitions.at(i);
1557 if (t->machine() == q)
1558 unregisterSignalTransition(t);
1559 }
1560 }
1561 {
1562 QList<QEventTransition*> transitions = qFindChildren<QEventTransition*>(rootState());
1563 for (int i = 0; i < transitions.size(); ++i) {
1564 QEventTransition *t = transitions.at(i);
1565 if (t->machine() == q)
1566 unregisterEventTransition(t);
1567 }
1568 }
1569}
1570
1571#ifndef QT_NO_STATEMACHINE_EVENTFILTER
1572void QStateMachinePrivate::registerEventTransition(QEventTransition *transition)
1573{
1574 Q_Q(QStateMachine);
1575 if (QEventTransitionPrivate::get(transition)->registered)
1576 return;
1577 if (transition->eventType() >= QEvent::User) {
1578 qWarning("QObject event transitions are not supported for custom types");
1579 return;
1580 }
1581 QObject *object = QEventTransitionPrivate::get(transition)->object;
1582 if (!object)
1583 return;
1584 QObjectPrivate *od = QObjectPrivate::get(object);
1585 if (!od->eventFilters.contains(q))
1586 object->installEventFilter(q);
1587 ++qobjectEvents[object][transition->eventType()];
1588 QEventTransitionPrivate::get(transition)->registered = true;
1589#ifdef QSTATEMACHINE_DEBUG
1590 qDebug() << q << ": added event transition from" << transition->sourceState()
1591 << ": ( object =" << object << ", event =" << transition->eventType()
1592 << ", targets =" << transition->targetStates() << ')';
1593#endif
1594}
1595
1596void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transition)
1597{
1598 Q_Q(QStateMachine);
1599 if (!QEventTransitionPrivate::get(transition)->registered)
1600 return;
1601 QObject *object = QEventTransitionPrivate::get(transition)->object;
1602 QHash<QEvent::Type, int> &events = qobjectEvents[object];
1603 Q_ASSERT(events.value(transition->eventType()) > 0);
1604 if (--events[transition->eventType()] == 0) {
1605 events.remove(transition->eventType());
1606 int sum = 0;
1607 QHash<QEvent::Type, int>::const_iterator it;
1608 for (it = events.constBegin(); it != events.constEnd(); ++it)
1609 sum += it.value();
1610 if (sum == 0) {
1611 qobjectEvents.remove(object);
1612 object->removeEventFilter(q);
1613 }
1614 }
1615 QEventTransitionPrivate::get(transition)->registered = false;
1616}
1617
1618void QStateMachinePrivate::handleFilteredEvent(QObject *watched, QEvent *event)
1619{
1620 if (qobjectEvents.value(watched).contains(event->type())) {
1621 postInternalEvent(new QStateMachine::WrappedEvent(watched, handler->cloneEvent(event)));
1622 processEvents(DirectProcessing);
1623 }
1624}
1625#endif
1626
1627void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalIndex,
1628 void **argv)
1629{
1630 Q_ASSERT(connections[sender].at(signalIndex) != 0);
1631 const QMetaObject *meta = sender->metaObject();
1632 QMetaMethod method = meta->method(signalIndex);
1633 QList<QByteArray> parameterTypes = method.parameterTypes();
1634 int argc = parameterTypes.count();
1635 QList<QVariant> vargs;
1636 for (int i = 0; i < argc; ++i) {
1637 int type = QMetaType::type(parameterTypes.at(i));
1638 vargs.append(QVariant(type, argv[i+1]));
1639 }
1640
1641#ifdef QSTATEMACHINE_DEBUG
1642 qDebug() << q_func() << ": sending signal event ( sender =" << sender
1643 << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ')';
1644#endif
1645 postInternalEvent(new QStateMachine::SignalEvent(sender, signalIndex, vargs));
1646 processEvents(DirectProcessing);
1647}
1648
1649/*!
1650 Constructs a new state machine with the given \a parent.
1651*/
1652QStateMachine::QStateMachine(QObject *parent)
1653 : QState(*new QStateMachinePrivate, /*parentState=*/0)
1654{
1655 // Can't pass the parent to the QState constructor, as it expects a QState
1656 // But this works as expected regardless of whether parent is a QState or not
1657 setParent(parent);
1658}
1659
1660/*!
1661 \internal
1662*/
1663QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent)
1664 : QState(dd, /*parentState=*/0)
1665{
1666 setParent(parent);
1667}
1668
1669/*!
1670 Destroys this state machine.
1671*/
1672QStateMachine::~QStateMachine()
1673{
1674}
1675
1676/*!
1677 \enum QStateMachine::EventPriority
1678
1679 This enum type specifies the priority of an event posted to the state
1680 machine using postEvent().
1681
1682 Events of high priority are processed before events of normal priority.
1683
1684 \value NormalPriority The event has normal priority.
1685 \value HighPriority The event has high priority.
1686*/
1687
1688/*! \enum QStateMachine::Error
1689
1690 This enum type defines errors that can occur in the state machine at run time. When the state
1691 machine encounters an unrecoverable error at run time, it will set the error code returned
1692 by error(), the error message returned by errorString(), and enter an error state based on
1693 the context of the error.
1694
1695 \value NoError No error has occurred.
1696 \value NoInitialStateError The machine has entered a QState with children which does not have an
1697 initial state set. The context of this error is the state which is missing an initial
1698 state.
1699 \value NoDefaultStateInHistoryStateError The machine has entered a QHistoryState which does not have
1700 a default state set. The context of this error is the QHistoryState which is missing a
1701 default state.
1702 \value NoCommonAncestorForTransitionError The machine has selected a transition whose source
1703 and targets are not part of the same tree of states, and thus are not part of the same
1704 state machine. Commonly, this could mean that one of the states has not been given
1705 any parent or added to any machine. The context of this error is the source state of
1706 the transition.
1707
1708 \sa setErrorState()
1709*/
1710
1711/*!
1712 \enum QStateMachine::RestorePolicy
1713
1714 This enum specifies the restore policy type. The restore policy
1715 takes effect when the machine enters a state which sets one or more
1716 properties. If the restore policy is set to RestoreProperties,
1717 the state machine will save the original value of the property before the
1718 new value is set.
1719
1720 Later, when the machine either enters a state which does not set
1721 a value for the given property, the property will automatically be restored
1722 to its initial value.
1723
1724 Only one initial value will be saved for any given property. If a value for a property has
1725 already been saved by the state machine, it will not be overwritten until the property has been
1726 successfully restored.
1727
1728 \value DontRestoreProperties The state machine should not save the initial values of properties
1729 and restore them later.
1730 \value RestoreProperties The state machine should save the initial values of properties
1731 and restore them later.
1732
1733 \sa QStateMachine::globalRestorePolicy QState::assignProperty()
1734*/
1735
1736
1737/*!
1738 Returns the error code of the last error that occurred in the state machine.
1739*/
1740QStateMachine::Error QStateMachine::error() const
1741{
1742 Q_D(const QStateMachine);
1743 return d->error;
1744}
1745
1746/*!
1747 Returns the error string of the last error that occurred in the state machine.
1748*/
1749QString QStateMachine::errorString() const
1750{
1751 Q_D(const QStateMachine);
1752 return d->errorString;
1753}
1754
1755/*!
1756 Clears the error string and error code of the state machine.
1757*/
1758void QStateMachine::clearError()
1759{
1760 Q_D(QStateMachine);
1761 d->errorString.clear();
1762 d->error = NoError;
1763}
1764
1765/*!
1766 Returns the restore policy of the state machine.
1767
1768 \sa setGlobalRestorePolicy()
1769*/
1770QStateMachine::RestorePolicy QStateMachine::globalRestorePolicy() const
1771{
1772 Q_D(const QStateMachine);
1773 return d->globalRestorePolicy;
1774}
1775
1776/*!
1777 Sets the restore policy of the state machine to \a restorePolicy. The default
1778 restore policy is QAbstractState::DontRestoreProperties.
1779
1780 \sa globalRestorePolicy()
1781*/
1782void QStateMachine::setGlobalRestorePolicy(QStateMachine::RestorePolicy restorePolicy)
1783{
1784 Q_D(QStateMachine);
1785 d->globalRestorePolicy = restorePolicy;
1786}
1787
1788/*!
1789 Adds the given \a state to this state machine. The state becomes a top-level
1790 state.
1791
1792 If the state is already in a different machine, it will first be removed
1793 from its old machine, and then added to this machine.
1794
1795 \sa removeState(), setInitialState()
1796*/
1797void QStateMachine::addState(QAbstractState *state)
1798{
1799 if (!state) {
1800 qWarning("QStateMachine::addState: cannot add null state");
1801 return;
1802 }
1803 if (QAbstractStatePrivate::get(state)->machine() == this) {
1804 qWarning("QStateMachine::addState: state has already been added to this machine");
1805 return;
1806 }
1807 state->setParent(this);
1808}
1809
1810/*!
1811 Removes the given \a state from this state machine. The state machine
1812 releases ownership of the state.
1813
1814 \sa addState()
1815*/
1816void QStateMachine::removeState(QAbstractState *state)
1817{
1818 if (!state) {
1819 qWarning("QStateMachine::removeState: cannot remove null state");
1820 return;
1821 }
1822 if (QAbstractStatePrivate::get(state)->machine() != this) {
1823 qWarning("QStateMachine::removeState: state %p's machine (%p)"
1824 " is different from this machine (%p)",
1825 state, QAbstractStatePrivate::get(state)->machine(), this);
1826 return;
1827 }
1828 state->setParent(0);
1829}
1830
1831/*!
1832 Returns whether this state machine is running.
1833
1834 start(), stop()
1835*/
1836bool QStateMachine::isRunning() const
1837{
1838 Q_D(const QStateMachine);
1839 return (d->state == QStateMachinePrivate::Running);
1840}
1841
1842/*!
1843 Starts this state machine. The machine will reset its configuration and
1844 transition to the initial state. When a final top-level state (QFinalState)
1845 is entered, the machine will emit the finished() signal.
1846
1847 \note A state machine will not run without a running event loop, such as
1848 the main application event loop started with QCoreApplication::exec() or
1849 QApplication::exec().
1850
1851 \sa started(), finished(), stop(), initialState()
1852*/
1853void QStateMachine::start()
1854{
1855 Q_D(QStateMachine);
1856
1857 if (initialState() == 0) {
1858 qWarning("QStateMachine::start: No initial state set for machine. Refusing to start.");
1859 return;
1860 }
1861
1862 switch (d->state) {
1863 case QStateMachinePrivate::NotRunning:
1864 d->state = QStateMachinePrivate::Starting;
1865 QMetaObject::invokeMethod(this, "_q_start", Qt::QueuedConnection);
1866 break;
1867 case QStateMachinePrivate::Starting:
1868 break;
1869 case QStateMachinePrivate::Running:
1870 qWarning("QStateMachine::start(): already running");
1871 break;
1872 }
1873}
1874
1875/*!
1876 Stops this state machine. The state machine will stop processing events and
1877 then emit the stopped() signal.
1878
1879 \sa stopped(), start()
1880*/
1881void QStateMachine::stop()
1882{
1883 Q_D(QStateMachine);
1884 switch (d->state) {
1885 case QStateMachinePrivate::NotRunning:
1886 break;
1887 case QStateMachinePrivate::Starting:
1888 // the machine will exit as soon as it enters the event processing loop
1889 d->stop = true;
1890 break;
1891 case QStateMachinePrivate::Running:
1892 d->stop = true;
1893 d->processEvents(QStateMachinePrivate::QueuedProcessing);
1894 break;
1895 }
1896}
1897
1898/*!
1899 \threadsafe
1900
1901 Posts the given \a event of the given \a priority for processing by this
1902 state machine.
1903
1904 This function returns immediately. The event is added to the state machine's
1905 event queue. Events are processed in the order posted. The state machine
1906 takes ownership of the event and deletes it once it has been processed.
1907
1908 You can only post events when the state machine is running.
1909
1910 \sa postDelayedEvent()
1911*/
1912void QStateMachine::postEvent(QEvent *event, EventPriority priority)
1913{
1914 Q_D(QStateMachine);
1915 if (d->state != QStateMachinePrivate::Running) {
1916 qWarning("QStateMachine::postEvent: cannot post event when the state machine is not running");
1917 return;
1918 }
1919 if (!event) {
1920 qWarning("QStateMachine::postEvent: cannot post null event");
1921 return;
1922 }
1923#ifdef QSTATEMACHINE_DEBUG
1924 qDebug() << this << ": posting event" << event;
1925#endif
1926 switch (priority) {
1927 case NormalPriority:
1928 d->postExternalEvent(event);
1929 break;
1930 case HighPriority:
1931 d->postInternalEvent(event);
1932 break;
1933 }
1934 d->processEvents(QStateMachinePrivate::QueuedProcessing);
1935}
1936
1937/*!
1938 \threadsafe
1939
1940 Posts the given \a event for processing by this state machine, with the
1941 given \a delay in milliseconds. Returns an identifier associated with the
1942 delayed event, or -1 if the event could not be posted.
1943
1944 This function returns immediately. When the delay has expired, the event
1945 will be added to the state machine's event queue for processing. The state
1946 machine takes ownership of the event and deletes it once it has been
1947 processed.
1948
1949 You can only post events when the state machine is running.
1950
1951 \sa cancelDelayedEvent(), postEvent()
1952*/
1953int QStateMachine::postDelayedEvent(QEvent *event, int delay)
1954{
1955 Q_D(QStateMachine);
1956 if (d->state != QStateMachinePrivate::Running) {
1957 qWarning("QStateMachine::postDelayedEvent: cannot post event when the state machine is not running");
1958 return -1;
1959 }
1960 if (!event) {
1961 qWarning("QStateMachine::postDelayedEvent: cannot post null event");
1962 return -1;
1963 }
1964 if (delay < 0) {
1965 qWarning("QStateMachine::postDelayedEvent: delay cannot be negative");
1966 return -1;
1967 }
1968#ifdef QSTATEMACHINE_DEBUG
1969 qDebug() << this << ": posting event" << event << "with delay" << delay;
1970#endif
1971 QMutexLocker locker(&d->delayedEventsMutex);
1972 int tid = startTimer(delay);
1973 d->delayedEvents[tid] = event;
1974 return tid;
1975}
1976
1977/*!
1978 \threadsafe
1979
1980 Cancels the delayed event identified by the given \a id. The id should be a
1981 value returned by a call to postDelayedEvent(). Returns true if the event
1982 was successfully cancelled, otherwise returns false.
1983
1984 \sa postDelayedEvent()
1985*/
1986bool QStateMachine::cancelDelayedEvent(int id)
1987{
1988 Q_D(QStateMachine);
1989 if (d->state != QStateMachinePrivate::Running) {
1990 qWarning("QStateMachine::cancelDelayedEvent: the machine is not running");
1991 return false;
1992 }
1993 QMutexLocker locker(&d->delayedEventsMutex);
1994 QEvent *e = d->delayedEvents.take(id);
1995 if (!e)
1996 return false;
1997 killTimer(id);
1998 delete e;
1999 return true;
2000}
2001
2002/*!
2003 Returns the maximal consistent set of states (including parallel and final
2004 states) that this state machine is currently in. If a state \c s is in the
2005 configuration, it is always the case that the parent of \c s is also in
2006 c. Note, however, that the machine itself is not an explicit member of the
2007 configuration.
2008*/
2009QSet<QAbstractState*> QStateMachine::configuration() const
2010{
2011 Q_D(const QStateMachine);
2012 return d->configuration;
2013}
2014
2015/*!
2016 \fn QStateMachine::started()
2017
2018 This signal is emitted when the state machine has entered its initial state
2019 (QStateMachine::initialState).
2020
2021 \sa QStateMachine::finished(), QStateMachine::start()
2022*/
2023
2024/*!
2025 \fn QStateMachine::stopped()
2026
2027 This signal is emitted when the state machine has stopped.
2028
2029 \sa QStateMachine::stop(), QStateMachine::finished()
2030*/
2031
2032/*!
2033 \reimp
2034*/
2035bool QStateMachine::event(QEvent *e)
2036{
2037 Q_D(QStateMachine);
2038 if (e->type() == QEvent::Timer) {
2039 QTimerEvent *te = static_cast<QTimerEvent*>(e);
2040 int tid = te->timerId();
2041 if (d->state != QStateMachinePrivate::Running) {
2042 // This event has been cancelled already
2043 QMutexLocker locker(&d->delayedEventsMutex);
2044 Q_ASSERT(!d->delayedEvents.contains(tid));
2045 return true;
2046 }
2047 d->delayedEventsMutex.lock();
2048 QEvent *ee = d->delayedEvents.take(tid);
2049 if (ee != 0) {
2050 killTimer(tid);
2051 d->delayedEventsMutex.unlock();
2052 d->postExternalEvent(ee);
2053 d->processEvents(QStateMachinePrivate::DirectProcessing);
2054 return true;
2055 } else {
2056 d->delayedEventsMutex.unlock();
2057 }
2058 }
2059 return QState::event(e);
2060}
2061
2062#ifndef QT_NO_STATEMACHINE_EVENTFILTER
2063/*!
2064 \reimp
2065*/
2066bool QStateMachine::eventFilter(QObject *watched, QEvent *event)
2067{
2068 Q_D(QStateMachine);
2069 d->handleFilteredEvent(watched, event);
2070 return false;
2071}
2072#endif
2073
2074/*!
2075 \internal
2076
2077 This function is called when the state machine is about to select
2078 transitions based on the given \a event.
2079
2080 The default implementation does nothing.
2081*/
2082void QStateMachine::beginSelectTransitions(QEvent *event)
2083{
2084 Q_UNUSED(event);
2085}
2086
2087/*!
2088 \internal
2089
2090 This function is called when the state machine has finished selecting
2091 transitions based on the given \a event.
2092
2093 The default implementation does nothing.
2094*/
2095void QStateMachine::endSelectTransitions(QEvent *event)
2096{
2097 Q_UNUSED(event);
2098}
2099
2100/*!
2101 \internal
2102
2103 This function is called when the state machine is about to do a microstep.
2104
2105 The default implementation does nothing.
2106*/
2107void QStateMachine::beginMicrostep(QEvent *event)
2108{
2109 Q_UNUSED(event);
2110}
2111
2112/*!
2113 \internal
2114
2115 This function is called when the state machine has finished doing a
2116 microstep.
2117
2118 The default implementation does nothing.
2119*/
2120void QStateMachine::endMicrostep(QEvent *event)
2121{
2122 Q_UNUSED(event);
2123}
2124
2125/*!
2126 \reimp
2127*/
2128void QStateMachine::onEntry(QEvent *event)
2129{
2130 start();
2131 QState::onEntry(event);
2132}
2133
2134/*!
2135 \reimp
2136*/
2137void QStateMachine::onExit(QEvent *event)
2138{
2139 stop();
2140 QState::onExit(event);
2141}
2142
2143#ifndef QT_NO_ANIMATION
2144
2145/*!
2146 Returns whether animations are enabled for this state machine.
2147*/
2148bool QStateMachine::isAnimated() const
2149{
2150 Q_D(const QStateMachine);
2151 return d->animated;
2152}
2153
2154/*!
2155 Sets whether animations are \a enabled for this state machine.
2156*/
2157void QStateMachine::setAnimated(bool enabled)
2158{
2159 Q_D(QStateMachine);
2160 d->animated = enabled;
2161}
2162
2163/*!
2164 Adds a default \a animation to be considered for any transition.
2165*/
2166void QStateMachine::addDefaultAnimation(QAbstractAnimation *animation)
2167{
2168 Q_D(QStateMachine);
2169 d->defaultAnimations.append(animation);
2170}
2171
2172/*!
2173 Returns the list of default animations that will be considered for any transition.
2174*/
2175QList<QAbstractAnimation*> QStateMachine::defaultAnimations() const
2176{
2177 Q_D(const QStateMachine);
2178 return d->defaultAnimations;
2179}
2180
2181/*!
2182 Removes \a animation from the list of default animations.
2183*/
2184void QStateMachine::removeDefaultAnimation(QAbstractAnimation *animation)
2185{
2186 Q_D(QStateMachine);
2187 d->defaultAnimations.removeAll(animation);
2188}
2189
2190#endif // QT_NO_ANIMATION
2191
2192
2193static const uint qt_meta_data_QSignalEventGenerator[] = {
2194
2195 // content:
2196 2, // revision
2197 0, // classname
2198 0, 0, // classinfo
2199 1, 12, // methods
2200 0, 0, // properties
2201 0, 0, // enums/sets
2202 0, 0, // constructors
2203
2204 // slots: signature, parameters, type, tag, flags
2205 23, 22, 22, 22, 0x0a,
2206
2207 0 // eod
2208};
2209
2210static const char qt_meta_stringdata_QSignalEventGenerator[] = {
2211 "QSignalEventGenerator\0\0execute()\0"
2212};
2213
2214const QMetaObject QSignalEventGenerator::staticMetaObject = {
2215 { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator,
2216 qt_meta_data_QSignalEventGenerator, 0 }
2217};
2218
2219const QMetaObject *QSignalEventGenerator::metaObject() const
2220{
2221 return &staticMetaObject;
2222}
2223
2224void *QSignalEventGenerator::qt_metacast(const char *_clname)
2225{
2226 if (!_clname) return 0;
2227 if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator))
2228 return static_cast<void*>(const_cast< QSignalEventGenerator*>(this));
2229 return QObject::qt_metacast(_clname);
2230}
2231
2232int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
2233{
2234 _id = QObject::qt_metacall(_c, _id, _a);
2235 if (_id < 0)
2236 return _id;
2237 if (_c == QMetaObject::InvokeMetaMethod) {
2238 switch (_id) {
2239 case 0: {
2240// ### in Qt 4.6 we can use QObject::senderSignalIndex()
2241 QObjectPrivate *d = static_cast<QObjectPrivate *>(d_ptr.data());
2242 int signalIndex = -1;
2243 QObject *sender = this->sender();
2244 if (sender && d->currentSender)
2245 signalIndex = d->currentSender->signal;
2246
2247 Q_ASSERT(signalIndex != -1);
2248 QStateMachine *machine = qobject_cast<QStateMachine*>(parent());
2249 QStateMachinePrivate::get(machine)->handleTransitionSignal(sender, signalIndex, _a);
2250 break;
2251 }
2252 default: ;
2253 }
2254 _id -= 1;
2255 }
2256 return _id;
2257}
2258
2259QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent)
2260 : QObject(parent)
2261{
2262}
2263
2264/*!
2265 \class QStateMachine::SignalEvent
2266
2267 \brief The SignalEvent class represents a Qt signal event.
2268
2269 \since 4.6
2270 \ingroup statemachine
2271
2272 A signal event is generated by a QStateMachine in response to a Qt
2273 signal. The QSignalTransition class provides a transition associated with a
2274 signal event. QStateMachine::SignalEvent is part of \l{The State Machine Framework}.
2275
2276 The sender() function returns the object that generated the signal. The
2277 signalIndex() function returns the index of the signal. The arguments()
2278 function returns the arguments of the signal.
2279
2280 \sa QSignalTransition
2281*/
2282
2283/*!
2284 \internal
2285
2286 Constructs a new SignalEvent object with the given \a sender, \a
2287 signalIndex and \a arguments.
2288*/
2289QStateMachine::SignalEvent::SignalEvent(QObject *sender, int signalIndex,
2290 const QList<QVariant> &arguments)
2291 : QEvent(QEvent::StateMachineSignal), m_sender(sender),
2292 m_signalIndex(signalIndex), m_arguments(arguments)
2293{
2294}
2295
2296/*!
2297 Destroys this SignalEvent.
2298*/
2299QStateMachine::SignalEvent::~SignalEvent()
2300{
2301}
2302
2303/*!
2304 \fn QStateMachine::SignalEvent::sender() const
2305
2306 Returns the object that emitted the signal.
2307
2308 \sa QObject::sender()
2309*/
2310
2311/*!
2312 \fn QStateMachine::SignalEvent::signalIndex() const
2313
2314 Returns the index of the signal.
2315
2316 \sa QMetaObject::indexOfSignal(), QMetaObject::method()
2317*/
2318
2319/*!
2320 \fn QStateMachine::SignalEvent::arguments() const
2321
2322 Returns the arguments of the signal.
2323*/
2324
2325
2326/*!
2327 \class QStateMachine::WrappedEvent
2328
2329 \brief The WrappedEvent class holds a clone of an event associated with a QObject.
2330
2331 \since 4.6
2332 \ingroup statemachine
2333
2334 A wrapped event is generated by a QStateMachine in response to a Qt
2335 event. The QEventTransition class provides a transition associated with a
2336 such an event. QStateMachine::WrappedEvent is part of \l{The State Machine
2337 Framework}.
2338
2339 The object() function returns the object that generated the event. The
2340 event() function returns a clone of the original event.
2341
2342 \sa QEventTransition
2343*/
2344
2345/*!
2346 \internal
2347
2348 Constructs a new WrappedEvent object with the given \a object
2349 and \a event.
2350
2351 The WrappedEvent object takes ownership of \a event.
2352*/
2353QStateMachine::WrappedEvent::WrappedEvent(QObject *object, QEvent *event)
2354 : QEvent(QEvent::StateMachineWrapped), m_object(object), m_event(event)
2355{
2356}
2357
2358/*!
2359 Destroys this WrappedEvent.
2360*/
2361QStateMachine::WrappedEvent::~WrappedEvent()
2362{
2363 delete m_event;
2364}
2365
2366/*!
2367 \fn QStateMachine::WrappedEvent::object() const
2368
2369 Returns the object that the event is associated with.
2370*/
2371
2372/*!
2373 \fn QStateMachine::WrappedEvent::event() const
2374
2375 Returns a clone of the original event.
2376*/
2377
2378QT_END_NAMESPACE
2379
2380#include "moc_qstatemachine.cpp"
2381
2382#endif //QT_NO_STATEMACHINE
Note: See TracBrowser for help on using the repository browser.