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

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

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 78.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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::clearHistory()
1179{
1180 Q_Q(QStateMachine);
1181 QList<QHistoryState*> historyStates = qFindChildren<QHistoryState*>(q);
1182 for (int i = 0; i < historyStates.size(); ++i) {
1183 QHistoryState *h = historyStates.at(i);
1184 QHistoryStatePrivate::get(h)->configuration.clear();
1185 }
1186}
1187
1188void QStateMachinePrivate::_q_start()
1189{
1190 Q_Q(QStateMachine);
1191 Q_ASSERT(state == Starting);
1192 Q_ASSERT(rootState() != 0);
1193 QAbstractState *initial = rootState()->initialState();
1194 configuration.clear();
1195 qDeleteAll(internalEventQueue);
1196 internalEventQueue.clear();
1197 qDeleteAll(externalEventQueue);
1198 externalEventQueue.clear();
1199 clearHistory();
1200
1201#ifdef QSTATEMACHINE_DEBUG
1202 qDebug() << q << ": starting";
1203#endif
1204 state = Running;
1205 processingScheduled = true; // we call _q_process() below
1206 emit q->started();
1207
1208 QState *start = startState();
1209 Q_ASSERT(start != 0);
1210
1211 QList<QAbstractTransition*> transitions = QStatePrivate::get(start)->transitions();
1212
1213 // If a transition has already been added, then we skip this step, as the
1214 // initial transition in that case has been overridden.
1215 if (transitions.isEmpty()) {
1216 QAbstractTransition *initialTransition = new InitialTransition(initial);
1217 start->addTransition(initialTransition);
1218 transitions.append(initialTransition);
1219 }
1220
1221 QEvent nullEvent(QEvent::None);
1222 executeTransitionContent(&nullEvent, transitions);
1223 QList<QAbstractState*> enteredStates = enterStates(&nullEvent, transitions);
1224#ifndef QT_NO_PROPERTIES
1225 applyProperties(transitions, QList<QAbstractState*>() << start,
1226 enteredStates);
1227#endif
1228 removeStartState();
1229
1230#ifdef QSTATEMACHINE_DEBUG
1231 qDebug() << q << ": initial configuration:" << configuration;
1232#endif
1233 _q_process();
1234}
1235
1236void QStateMachinePrivate::_q_process()
1237{
1238 Q_Q(QStateMachine);
1239 Q_ASSERT(state == Running);
1240 Q_ASSERT(!processing);
1241 processing = true;
1242 processingScheduled = false;
1243#ifdef QSTATEMACHINE_DEBUG
1244 qDebug() << q << ": starting the event processing loop";
1245#endif
1246 while (processing) {
1247 if (stop) {
1248 stop = false;
1249 processing = false;
1250 stopProcessingReason = Stopped;
1251 break;
1252 }
1253 QSet<QAbstractTransition*> enabledTransitions;
1254 QEvent *e = new QEvent(QEvent::None);
1255 enabledTransitions = selectTransitions(e);
1256 if (enabledTransitions.isEmpty()) {
1257 delete e;
1258 e = 0;
1259 }
1260 if (enabledTransitions.isEmpty() && ((e = dequeueInternalEvent()) != 0)) {
1261#ifdef QSTATEMACHINE_DEBUG
1262 qDebug() << q << ": dequeued internal event" << e << "of type" << e->type();
1263#endif
1264 enabledTransitions = selectTransitions(e);
1265 if (enabledTransitions.isEmpty()) {
1266 delete e;
1267 e = 0;
1268 }
1269 }
1270 if (enabledTransitions.isEmpty()) {
1271 if ((e = dequeueExternalEvent()) != 0) {
1272#ifdef QSTATEMACHINE_DEBUG
1273 qDebug() << q << ": dequeued external event" << e << "of type" << e->type();
1274#endif
1275 enabledTransitions = selectTransitions(e);
1276 if (enabledTransitions.isEmpty()) {
1277 delete e;
1278 e = 0;
1279 }
1280 } else {
1281 if (isInternalEventQueueEmpty()) {
1282 processing = false;
1283 stopProcessingReason = EventQueueEmpty;
1284 }
1285 }
1286 }
1287 if (!enabledTransitions.isEmpty()) {
1288 q->beginMicrostep(e);
1289 microstep(e, enabledTransitions.toList());
1290 q->endMicrostep(e);
1291 }
1292#ifdef QSTATEMACHINE_DEBUG
1293 else {
1294 qDebug() << q << ": no transitions enabled";
1295 }
1296#endif
1297 delete e;
1298 }
1299#ifdef QSTATEMACHINE_DEBUG
1300 qDebug() << q << ": finished the event processing loop";
1301#endif
1302 switch (stopProcessingReason) {
1303 case EventQueueEmpty:
1304 break;
1305 case Finished:
1306 state = NotRunning;
1307 cancelAllDelayedEvents();
1308 unregisterAllTransitions();
1309 emit q->finished();
1310 break;
1311 case Stopped:
1312 state = NotRunning;
1313 cancelAllDelayedEvents();
1314 unregisterAllTransitions();
1315 emit q->stopped();
1316 break;
1317 }
1318}
1319
1320void QStateMachinePrivate::postInternalEvent(QEvent *e)
1321{
1322 QMutexLocker locker(&internalEventMutex);
1323 internalEventQueue.append(e);
1324}
1325
1326void QStateMachinePrivate::postExternalEvent(QEvent *e)
1327{
1328 QMutexLocker locker(&externalEventMutex);
1329 externalEventQueue.append(e);
1330}
1331
1332QEvent *QStateMachinePrivate::dequeueInternalEvent()
1333{
1334 QMutexLocker locker(&internalEventMutex);
1335 if (internalEventQueue.isEmpty())
1336 return 0;
1337 return internalEventQueue.takeFirst();
1338}
1339
1340QEvent *QStateMachinePrivate::dequeueExternalEvent()
1341{
1342 QMutexLocker locker(&externalEventMutex);
1343 if (externalEventQueue.isEmpty())
1344 return 0;
1345 return externalEventQueue.takeFirst();
1346}
1347
1348bool QStateMachinePrivate::isInternalEventQueueEmpty()
1349{
1350 QMutexLocker locker(&internalEventMutex);
1351 return internalEventQueue.isEmpty();
1352}
1353
1354bool QStateMachinePrivate::isExternalEventQueueEmpty()
1355{
1356 QMutexLocker locker(&externalEventMutex);
1357 return externalEventQueue.isEmpty();
1358}
1359
1360void QStateMachinePrivate::processEvents(EventProcessingMode processingMode)
1361{
1362 Q_Q(QStateMachine);
1363 if ((state != Running) || processing || processingScheduled)
1364 return;
1365 switch (processingMode) {
1366 case DirectProcessing:
1367 if (QThread::currentThread() == q->thread()) {
1368 _q_process();
1369 break;
1370 } // fallthrough -- processing must be done in the machine thread
1371 case QueuedProcessing:
1372 processingScheduled = true;
1373 QMetaObject::invokeMethod(q, "_q_process", Qt::QueuedConnection);
1374 break;
1375 }
1376}
1377
1378void QStateMachinePrivate::cancelAllDelayedEvents()
1379{
1380 Q_Q(QStateMachine);
1381 QMutexLocker locker(&delayedEventsMutex);
1382 QHash<int, QEvent*>::const_iterator it;
1383 for (it = delayedEvents.constBegin(); it != delayedEvents.constEnd(); ++it) {
1384 int id = it.key();
1385 QEvent *e = it.value();
1386 q->killTimer(id);
1387 delete e;
1388 }
1389 delayedEvents.clear();
1390}
1391
1392namespace {
1393
1394class GoToStateTransition : public QAbstractTransition
1395{
1396public:
1397 GoToStateTransition(QAbstractState *target)
1398 : QAbstractTransition()
1399 { setTargetState(target); }
1400protected:
1401 void onTransition(QEvent *) { deleteLater(); }
1402 bool eventTest(QEvent *) { return true; }
1403};
1404
1405} // namespace
1406
1407/*!
1408 \internal
1409
1410 Causes this state machine to unconditionally transition to the given
1411 \a targetState.
1412
1413 Provides a backdoor for using the state machine "imperatively"; i.e. rather
1414 than defining explicit transitions, you drive the machine's execution by
1415 calling this function. It breaks the whole integrity of the
1416 transition-driven model, but is provided for pragmatic reasons.
1417*/
1418void QStateMachinePrivate::goToState(QAbstractState *targetState)
1419{
1420 if (!targetState) {
1421 qWarning("QStateMachine::goToState(): cannot go to null state");
1422 return;
1423 }
1424
1425 if (configuration.contains(targetState))
1426 return;
1427
1428 QState *sourceState = 0;
1429 if (state == Running) {
1430 QSet<QAbstractState*>::const_iterator it;
1431 for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
1432 sourceState = toStandardState(*it);
1433 if (sourceState != 0)
1434 break;
1435 }
1436 } else {
1437 sourceState = startState();
1438 }
1439
1440 Q_ASSERT(sourceState != 0);
1441 // Reuse previous GoToStateTransition in case of several calls to
1442 // goToState() in a row.
1443 GoToStateTransition *trans = qFindChild<GoToStateTransition*>(sourceState);
1444 if (!trans) {
1445 trans = new GoToStateTransition(targetState);
1446 sourceState->addTransition(trans);
1447 } else {
1448 trans->setTargetState(targetState);
1449 }
1450
1451 processEvents(QueuedProcessing);
1452}
1453
1454void QStateMachinePrivate::registerTransitions(QAbstractState *state)
1455{
1456 QState *group = toStandardState(state);
1457 if (!group)
1458 return;
1459 QList<QAbstractTransition*> transitions = QStatePrivate::get(group)->transitions();
1460 for (int i = 0; i < transitions.size(); ++i) {
1461 QAbstractTransition *t = transitions.at(i);
1462 if (QSignalTransition *st = qobject_cast<QSignalTransition*>(t)) {
1463 registerSignalTransition(st);
1464 }
1465#ifndef QT_NO_STATEMACHINE_EVENTFILTER
1466 else if (QEventTransition *oet = qobject_cast<QEventTransition*>(t)) {
1467 registerEventTransition(oet);
1468 }
1469#endif
1470 }
1471}
1472
1473void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition)
1474{
1475 if (QSignalTransition *st = qobject_cast<QSignalTransition*>(transition)) {
1476 unregisterSignalTransition(st);
1477 }
1478#ifndef QT_NO_STATEMACHINE_EVENTFILTER
1479 else if (QEventTransition *oet = qobject_cast<QEventTransition*>(transition)) {
1480 unregisterEventTransition(oet);
1481 }
1482#endif
1483}
1484
1485void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transition)
1486{
1487 Q_Q(QStateMachine);
1488 if (QSignalTransitionPrivate::get(transition)->signalIndex != -1)
1489 return; // already registered
1490 QObject *sender = QSignalTransitionPrivate::get(transition)->sender;
1491 if (!sender)
1492 return;
1493 QByteArray signal = QSignalTransitionPrivate::get(transition)->signal;
1494 if (signal.startsWith('0'+QSIGNAL_CODE))
1495 signal.remove(0, 1);
1496 const QMetaObject *meta = sender->metaObject();
1497 int signalIndex = meta->indexOfSignal(signal);
1498 int originalSignalIndex = signalIndex;
1499 if (signalIndex == -1) {
1500 signalIndex = meta->indexOfSignal(QMetaObject::normalizedSignature(signal));
1501 if (signalIndex == -1) {
1502 qWarning("QSignalTransition: no such signal: %s::%s",
1503 meta->className(), signal.constData());
1504 return;
1505 }
1506 }
1507 // The signal index we actually want to connect to is the one
1508 // that is going to be sent, i.e. the non-cloned original index.
1509 while (meta->method(signalIndex).attributes() & QMetaMethod::Cloned)
1510 --signalIndex;
1511
1512 QVector<int> &connectedSignalIndexes = connections[sender];
1513 if (connectedSignalIndexes.size() <= signalIndex)
1514 connectedSignalIndexes.resize(signalIndex+1);
1515 if (connectedSignalIndexes.at(signalIndex) == 0) {
1516 if (!signalEventGenerator)
1517 signalEventGenerator = new QSignalEventGenerator(q);
1518 bool ok = QMetaObject::connect(sender, signalIndex, signalEventGenerator,
1519 signalEventGenerator->metaObject()->methodOffset());
1520 if (!ok) {
1521#ifdef QSTATEMACHINE_DEBUG
1522 qDebug() << q << ": FAILED to add signal transition from" << transition->sourceState()
1523 << ": ( sender =" << sender << ", signal =" << signal
1524 << ", targets =" << transition->targetStates() << ')';
1525#endif
1526 return;
1527 }
1528 }
1529 ++connectedSignalIndexes[signalIndex];
1530 QSignalTransitionPrivate::get(transition)->signalIndex = signalIndex;
1531 QSignalTransitionPrivate::get(transition)->originalSignalIndex = originalSignalIndex;
1532#ifdef QSTATEMACHINE_DEBUG
1533 qDebug() << q << ": added signal transition from" << transition->sourceState()
1534 << ": ( sender =" << sender << ", signal =" << signal
1535 << ", targets =" << transition->targetStates() << ')';
1536#endif
1537}
1538
1539void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transition)
1540{
1541 int signalIndex = QSignalTransitionPrivate::get(transition)->signalIndex;
1542 if (signalIndex == -1)
1543 return; // not registered
1544 QSignalTransitionPrivate::get(transition)->signalIndex = -1;
1545 const QObject *sender = QSignalTransitionPrivate::get(transition)->sender;
1546 QVector<int> &connectedSignalIndexes = connections[sender];
1547 Q_ASSERT(connectedSignalIndexes.size() > signalIndex);
1548 Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0);
1549 if (--connectedSignalIndexes[signalIndex] == 0) {
1550 Q_ASSERT(signalEventGenerator != 0);
1551 QMetaObject::disconnect(sender, signalIndex, signalEventGenerator,
1552 signalEventGenerator->metaObject()->methodOffset());
1553 int sum = 0;
1554 for (int i = 0; i < connectedSignalIndexes.size(); ++i)
1555 sum += connectedSignalIndexes.at(i);
1556 if (sum == 0)
1557 connections.remove(sender);
1558 }
1559}
1560
1561void QStateMachinePrivate::unregisterAllTransitions()
1562{
1563 Q_Q(QStateMachine);
1564 {
1565 QList<QSignalTransition*> transitions = qFindChildren<QSignalTransition*>(rootState());
1566 for (int i = 0; i < transitions.size(); ++i) {
1567 QSignalTransition *t = transitions.at(i);
1568 if (t->machine() == q)
1569 unregisterSignalTransition(t);
1570 }
1571 }
1572 {
1573 QList<QEventTransition*> transitions = qFindChildren<QEventTransition*>(rootState());
1574 for (int i = 0; i < transitions.size(); ++i) {
1575 QEventTransition *t = transitions.at(i);
1576 if (t->machine() == q)
1577 unregisterEventTransition(t);
1578 }
1579 }
1580}
1581
1582#ifndef QT_NO_STATEMACHINE_EVENTFILTER
1583void QStateMachinePrivate::registerEventTransition(QEventTransition *transition)
1584{
1585 Q_Q(QStateMachine);
1586 if (QEventTransitionPrivate::get(transition)->registered)
1587 return;
1588 if (transition->eventType() >= QEvent::User) {
1589 qWarning("QObject event transitions are not supported for custom types");
1590 return;
1591 }
1592 QObject *object = QEventTransitionPrivate::get(transition)->object;
1593 if (!object)
1594 return;
1595 QObjectPrivate *od = QObjectPrivate::get(object);
1596 if (!od->eventFilters.contains(q))
1597 object->installEventFilter(q);
1598 ++qobjectEvents[object][transition->eventType()];
1599 QEventTransitionPrivate::get(transition)->registered = true;
1600#ifdef QSTATEMACHINE_DEBUG
1601 qDebug() << q << ": added event transition from" << transition->sourceState()
1602 << ": ( object =" << object << ", event =" << transition->eventType()
1603 << ", targets =" << transition->targetStates() << ')';
1604#endif
1605}
1606
1607void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transition)
1608{
1609 Q_Q(QStateMachine);
1610 if (!QEventTransitionPrivate::get(transition)->registered)
1611 return;
1612 QObject *object = QEventTransitionPrivate::get(transition)->object;
1613 QHash<QEvent::Type, int> &events = qobjectEvents[object];
1614 Q_ASSERT(events.value(transition->eventType()) > 0);
1615 if (--events[transition->eventType()] == 0) {
1616 events.remove(transition->eventType());
1617 int sum = 0;
1618 QHash<QEvent::Type, int>::const_iterator it;
1619 for (it = events.constBegin(); it != events.constEnd(); ++it)
1620 sum += it.value();
1621 if (sum == 0) {
1622 qobjectEvents.remove(object);
1623 object->removeEventFilter(q);
1624 }
1625 }
1626 QEventTransitionPrivate::get(transition)->registered = false;
1627}
1628
1629void QStateMachinePrivate::handleFilteredEvent(QObject *watched, QEvent *event)
1630{
1631 if (qobjectEvents.value(watched).contains(event->type())) {
1632 postInternalEvent(new QStateMachine::WrappedEvent(watched, handler->cloneEvent(event)));
1633 processEvents(DirectProcessing);
1634 }
1635}
1636#endif
1637
1638void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalIndex,
1639 void **argv)
1640{
1641 Q_ASSERT(connections[sender].at(signalIndex) != 0);
1642 const QMetaObject *meta = sender->metaObject();
1643 QMetaMethod method = meta->method(signalIndex);
1644 QList<QByteArray> parameterTypes = method.parameterTypes();
1645 int argc = parameterTypes.count();
1646 QList<QVariant> vargs;
1647 for (int i = 0; i < argc; ++i) {
1648 int type = QMetaType::type(parameterTypes.at(i));
1649 vargs.append(QVariant(type, argv[i+1]));
1650 }
1651
1652#ifdef QSTATEMACHINE_DEBUG
1653 qDebug() << q_func() << ": sending signal event ( sender =" << sender
1654 << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ')';
1655#endif
1656 postInternalEvent(new QStateMachine::SignalEvent(sender, signalIndex, vargs));
1657 processEvents(DirectProcessing);
1658}
1659
1660/*!
1661 Constructs a new state machine with the given \a parent.
1662*/
1663QStateMachine::QStateMachine(QObject *parent)
1664 : QState(*new QStateMachinePrivate, /*parentState=*/0)
1665{
1666 // Can't pass the parent to the QState constructor, as it expects a QState
1667 // But this works as expected regardless of whether parent is a QState or not
1668 setParent(parent);
1669}
1670
1671/*!
1672 \internal
1673*/
1674QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent)
1675 : QState(dd, /*parentState=*/0)
1676{
1677 setParent(parent);
1678}
1679
1680/*!
1681 Destroys this state machine.
1682*/
1683QStateMachine::~QStateMachine()
1684{
1685}
1686
1687/*!
1688 \enum QStateMachine::EventPriority
1689
1690 This enum type specifies the priority of an event posted to the state
1691 machine using postEvent().
1692
1693 Events of high priority are processed before events of normal priority.
1694
1695 \value NormalPriority The event has normal priority.
1696 \value HighPriority The event has high priority.
1697*/
1698
1699/*! \enum QStateMachine::Error
1700
1701 This enum type defines errors that can occur in the state machine at run time. When the state
1702 machine encounters an unrecoverable error at run time, it will set the error code returned
1703 by error(), the error message returned by errorString(), and enter an error state based on
1704 the context of the error.
1705
1706 \value NoError No error has occurred.
1707 \value NoInitialStateError The machine has entered a QState with children which does not have an
1708 initial state set. The context of this error is the state which is missing an initial
1709 state.
1710 \value NoDefaultStateInHistoryStateError The machine has entered a QHistoryState which does not have
1711 a default state set. The context of this error is the QHistoryState which is missing a
1712 default state.
1713 \value NoCommonAncestorForTransitionError The machine has selected a transition whose source
1714 and targets are not part of the same tree of states, and thus are not part of the same
1715 state machine. Commonly, this could mean that one of the states has not been given
1716 any parent or added to any machine. The context of this error is the source state of
1717 the transition.
1718
1719 \sa setErrorState()
1720*/
1721
1722/*!
1723 \enum QStateMachine::RestorePolicy
1724
1725 This enum specifies the restore policy type. The restore policy
1726 takes effect when the machine enters a state which sets one or more
1727 properties. If the restore policy is set to RestoreProperties,
1728 the state machine will save the original value of the property before the
1729 new value is set.
1730
1731 Later, when the machine either enters a state which does not set
1732 a value for the given property, the property will automatically be restored
1733 to its initial value.
1734
1735 Only one initial value will be saved for any given property. If a value for a property has
1736 already been saved by the state machine, it will not be overwritten until the property has been
1737 successfully restored.
1738
1739 \value DontRestoreProperties The state machine should not save the initial values of properties
1740 and restore them later.
1741 \value RestoreProperties The state machine should save the initial values of properties
1742 and restore them later.
1743
1744 \sa QStateMachine::globalRestorePolicy QState::assignProperty()
1745*/
1746
1747
1748/*!
1749 Returns the error code of the last error that occurred in the state machine.
1750*/
1751QStateMachine::Error QStateMachine::error() const
1752{
1753 Q_D(const QStateMachine);
1754 return d->error;
1755}
1756
1757/*!
1758 Returns the error string of the last error that occurred in the state machine.
1759*/
1760QString QStateMachine::errorString() const
1761{
1762 Q_D(const QStateMachine);
1763 return d->errorString;
1764}
1765
1766/*!
1767 Clears the error string and error code of the state machine.
1768*/
1769void QStateMachine::clearError()
1770{
1771 Q_D(QStateMachine);
1772 d->errorString.clear();
1773 d->error = NoError;
1774}
1775
1776/*!
1777 Returns the restore policy of the state machine.
1778
1779 \sa setGlobalRestorePolicy()
1780*/
1781QStateMachine::RestorePolicy QStateMachine::globalRestorePolicy() const
1782{
1783 Q_D(const QStateMachine);
1784 return d->globalRestorePolicy;
1785}
1786
1787/*!
1788 Sets the restore policy of the state machine to \a restorePolicy. The default
1789 restore policy is QAbstractState::DontRestoreProperties.
1790
1791 \sa globalRestorePolicy()
1792*/
1793void QStateMachine::setGlobalRestorePolicy(QStateMachine::RestorePolicy restorePolicy)
1794{
1795 Q_D(QStateMachine);
1796 d->globalRestorePolicy = restorePolicy;
1797}
1798
1799/*!
1800 Adds the given \a state to this state machine. The state becomes a top-level
1801 state.
1802
1803 If the state is already in a different machine, it will first be removed
1804 from its old machine, and then added to this machine.
1805
1806 \sa removeState(), setInitialState()
1807*/
1808void QStateMachine::addState(QAbstractState *state)
1809{
1810 if (!state) {
1811 qWarning("QStateMachine::addState: cannot add null state");
1812 return;
1813 }
1814 if (QAbstractStatePrivate::get(state)->machine() == this) {
1815 qWarning("QStateMachine::addState: state has already been added to this machine");
1816 return;
1817 }
1818 state->setParent(this);
1819}
1820
1821/*!
1822 Removes the given \a state from this state machine. The state machine
1823 releases ownership of the state.
1824
1825 \sa addState()
1826*/
1827void QStateMachine::removeState(QAbstractState *state)
1828{
1829 if (!state) {
1830 qWarning("QStateMachine::removeState: cannot remove null state");
1831 return;
1832 }
1833 if (QAbstractStatePrivate::get(state)->machine() != this) {
1834 qWarning("QStateMachine::removeState: state %p's machine (%p)"
1835 " is different from this machine (%p)",
1836 state, QAbstractStatePrivate::get(state)->machine(), this);
1837 return;
1838 }
1839 state->setParent(0);
1840}
1841
1842/*!
1843 Returns whether this state machine is running.
1844
1845 start(), stop()
1846*/
1847bool QStateMachine::isRunning() const
1848{
1849 Q_D(const QStateMachine);
1850 return (d->state == QStateMachinePrivate::Running);
1851}
1852
1853/*!
1854 Starts this state machine. The machine will reset its configuration and
1855 transition to the initial state. When a final top-level state (QFinalState)
1856 is entered, the machine will emit the finished() signal.
1857
1858 \note A state machine will not run without a running event loop, such as
1859 the main application event loop started with QCoreApplication::exec() or
1860 QApplication::exec().
1861
1862 \sa started(), finished(), stop(), initialState()
1863*/
1864void QStateMachine::start()
1865{
1866 Q_D(QStateMachine);
1867
1868 if (initialState() == 0) {
1869 qWarning("QStateMachine::start: No initial state set for machine. Refusing to start.");
1870 return;
1871 }
1872
1873 switch (d->state) {
1874 case QStateMachinePrivate::NotRunning:
1875 d->state = QStateMachinePrivate::Starting;
1876 QMetaObject::invokeMethod(this, "_q_start", Qt::QueuedConnection);
1877 break;
1878 case QStateMachinePrivate::Starting:
1879 break;
1880 case QStateMachinePrivate::Running:
1881 qWarning("QStateMachine::start(): already running");
1882 break;
1883 }
1884}
1885
1886/*!
1887 Stops this state machine. The state machine will stop processing events and
1888 then emit the stopped() signal.
1889
1890 \sa stopped(), start()
1891*/
1892void QStateMachine::stop()
1893{
1894 Q_D(QStateMachine);
1895 switch (d->state) {
1896 case QStateMachinePrivate::NotRunning:
1897 break;
1898 case QStateMachinePrivate::Starting:
1899 // the machine will exit as soon as it enters the event processing loop
1900 d->stop = true;
1901 break;
1902 case QStateMachinePrivate::Running:
1903 d->stop = true;
1904 d->processEvents(QStateMachinePrivate::QueuedProcessing);
1905 break;
1906 }
1907}
1908
1909/*!
1910 \threadsafe
1911
1912 Posts the given \a event of the given \a priority for processing by this
1913 state machine.
1914
1915 This function returns immediately. The event is added to the state machine's
1916 event queue. Events are processed in the order posted. The state machine
1917 takes ownership of the event and deletes it once it has been processed.
1918
1919 You can only post events when the state machine is running.
1920
1921 \sa postDelayedEvent()
1922*/
1923void QStateMachine::postEvent(QEvent *event, EventPriority priority)
1924{
1925 Q_D(QStateMachine);
1926 if (d->state != QStateMachinePrivate::Running) {
1927 qWarning("QStateMachine::postEvent: cannot post event when the state machine is not running");
1928 return;
1929 }
1930 if (!event) {
1931 qWarning("QStateMachine::postEvent: cannot post null event");
1932 return;
1933 }
1934#ifdef QSTATEMACHINE_DEBUG
1935 qDebug() << this << ": posting event" << event;
1936#endif
1937 switch (priority) {
1938 case NormalPriority:
1939 d->postExternalEvent(event);
1940 break;
1941 case HighPriority:
1942 d->postInternalEvent(event);
1943 break;
1944 }
1945 d->processEvents(QStateMachinePrivate::QueuedProcessing);
1946}
1947
1948/*!
1949 \threadsafe
1950
1951 Posts the given \a event for processing by this state machine, with the
1952 given \a delay in milliseconds. Returns an identifier associated with the
1953 delayed event, or -1 if the event could not be posted.
1954
1955 This function returns immediately. When the delay has expired, the event
1956 will be added to the state machine's event queue for processing. The state
1957 machine takes ownership of the event and deletes it once it has been
1958 processed.
1959
1960 You can only post events when the state machine is running.
1961
1962 \sa cancelDelayedEvent(), postEvent()
1963*/
1964int QStateMachine::postDelayedEvent(QEvent *event, int delay)
1965{
1966 Q_D(QStateMachine);
1967 if (d->state != QStateMachinePrivate::Running) {
1968 qWarning("QStateMachine::postDelayedEvent: cannot post event when the state machine is not running");
1969 return -1;
1970 }
1971 if (!event) {
1972 qWarning("QStateMachine::postDelayedEvent: cannot post null event");
1973 return -1;
1974 }
1975 if (delay < 0) {
1976 qWarning("QStateMachine::postDelayedEvent: delay cannot be negative");
1977 return -1;
1978 }
1979#ifdef QSTATEMACHINE_DEBUG
1980 qDebug() << this << ": posting event" << event << "with delay" << delay;
1981#endif
1982 QMutexLocker locker(&d->delayedEventsMutex);
1983 int tid = startTimer(delay);
1984 d->delayedEvents[tid] = event;
1985 return tid;
1986}
1987
1988/*!
1989 \threadsafe
1990
1991 Cancels the delayed event identified by the given \a id. The id should be a
1992 value returned by a call to postDelayedEvent(). Returns true if the event
1993 was successfully cancelled, otherwise returns false.
1994
1995 \sa postDelayedEvent()
1996*/
1997bool QStateMachine::cancelDelayedEvent(int id)
1998{
1999 Q_D(QStateMachine);
2000 if (d->state != QStateMachinePrivate::Running) {
2001 qWarning("QStateMachine::cancelDelayedEvent: the machine is not running");
2002 return false;
2003 }
2004 QMutexLocker locker(&d->delayedEventsMutex);
2005 QEvent *e = d->delayedEvents.take(id);
2006 if (!e)
2007 return false;
2008 killTimer(id);
2009 delete e;
2010 return true;
2011}
2012
2013/*!
2014 Returns the maximal consistent set of states (including parallel and final
2015 states) that this state machine is currently in. If a state \c s is in the
2016 configuration, it is always the case that the parent of \c s is also in
2017 c. Note, however, that the machine itself is not an explicit member of the
2018 configuration.
2019*/
2020QSet<QAbstractState*> QStateMachine::configuration() const
2021{
2022 Q_D(const QStateMachine);
2023 return d->configuration;
2024}
2025
2026/*!
2027 \fn QStateMachine::started()
2028
2029 This signal is emitted when the state machine has entered its initial state
2030 (QStateMachine::initialState).
2031
2032 \sa QStateMachine::finished(), QStateMachine::start()
2033*/
2034
2035/*!
2036 \fn QStateMachine::stopped()
2037
2038 This signal is emitted when the state machine has stopped.
2039
2040 \sa QStateMachine::stop(), QStateMachine::finished()
2041*/
2042
2043/*!
2044 \reimp
2045*/
2046bool QStateMachine::event(QEvent *e)
2047{
2048 Q_D(QStateMachine);
2049 if (e->type() == QEvent::Timer) {
2050 QTimerEvent *te = static_cast<QTimerEvent*>(e);
2051 int tid = te->timerId();
2052 if (d->state != QStateMachinePrivate::Running) {
2053 // This event has been cancelled already
2054 QMutexLocker locker(&d->delayedEventsMutex);
2055 Q_ASSERT(!d->delayedEvents.contains(tid));
2056 return true;
2057 }
2058 d->delayedEventsMutex.lock();
2059 QEvent *ee = d->delayedEvents.take(tid);
2060 if (ee != 0) {
2061 killTimer(tid);
2062 d->delayedEventsMutex.unlock();
2063 d->postExternalEvent(ee);
2064 d->processEvents(QStateMachinePrivate::DirectProcessing);
2065 return true;
2066 } else {
2067 d->delayedEventsMutex.unlock();
2068 }
2069 }
2070 return QState::event(e);
2071}
2072
2073#ifndef QT_NO_STATEMACHINE_EVENTFILTER
2074/*!
2075 \reimp
2076*/
2077bool QStateMachine::eventFilter(QObject *watched, QEvent *event)
2078{
2079 Q_D(QStateMachine);
2080 d->handleFilteredEvent(watched, event);
2081 return false;
2082}
2083#endif
2084
2085/*!
2086 \internal
2087
2088 This function is called when the state machine is about to select
2089 transitions based on the given \a event.
2090
2091 The default implementation does nothing.
2092*/
2093void QStateMachine::beginSelectTransitions(QEvent *event)
2094{
2095 Q_UNUSED(event);
2096}
2097
2098/*!
2099 \internal
2100
2101 This function is called when the state machine has finished selecting
2102 transitions based on the given \a event.
2103
2104 The default implementation does nothing.
2105*/
2106void QStateMachine::endSelectTransitions(QEvent *event)
2107{
2108 Q_UNUSED(event);
2109}
2110
2111/*!
2112 \internal
2113
2114 This function is called when the state machine is about to do a microstep.
2115
2116 The default implementation does nothing.
2117*/
2118void QStateMachine::beginMicrostep(QEvent *event)
2119{
2120 Q_UNUSED(event);
2121}
2122
2123/*!
2124 \internal
2125
2126 This function is called when the state machine has finished doing a
2127 microstep.
2128
2129 The default implementation does nothing.
2130*/
2131void QStateMachine::endMicrostep(QEvent *event)
2132{
2133 Q_UNUSED(event);
2134}
2135
2136/*!
2137 \reimp
2138*/
2139void QStateMachine::onEntry(QEvent *event)
2140{
2141 start();
2142 QState::onEntry(event);
2143}
2144
2145/*!
2146 \reimp
2147*/
2148void QStateMachine::onExit(QEvent *event)
2149{
2150 stop();
2151 QState::onExit(event);
2152}
2153
2154#ifndef QT_NO_ANIMATION
2155
2156/*!
2157 Returns whether animations are enabled for this state machine.
2158*/
2159bool QStateMachine::isAnimated() const
2160{
2161 Q_D(const QStateMachine);
2162 return d->animated;
2163}
2164
2165/*!
2166 Sets whether animations are \a enabled for this state machine.
2167*/
2168void QStateMachine::setAnimated(bool enabled)
2169{
2170 Q_D(QStateMachine);
2171 d->animated = enabled;
2172}
2173
2174/*!
2175 Adds a default \a animation to be considered for any transition.
2176*/
2177void QStateMachine::addDefaultAnimation(QAbstractAnimation *animation)
2178{
2179 Q_D(QStateMachine);
2180 d->defaultAnimations.append(animation);
2181}
2182
2183/*!
2184 Returns the list of default animations that will be considered for any transition.
2185*/
2186QList<QAbstractAnimation*> QStateMachine::defaultAnimations() const
2187{
2188 Q_D(const QStateMachine);
2189 return d->defaultAnimations;
2190}
2191
2192/*!
2193 Removes \a animation from the list of default animations.
2194*/
2195void QStateMachine::removeDefaultAnimation(QAbstractAnimation *animation)
2196{
2197 Q_D(QStateMachine);
2198 d->defaultAnimations.removeAll(animation);
2199}
2200
2201#endif // QT_NO_ANIMATION
2202
2203
2204static const uint qt_meta_data_QSignalEventGenerator[] = {
2205
2206 // content:
2207 2, // revision
2208 0, // classname
2209 0, 0, // classinfo
2210 1, 12, // methods
2211 0, 0, // properties
2212 0, 0, // enums/sets
2213 0, 0, // constructors
2214
2215 // slots: signature, parameters, type, tag, flags
2216 23, 22, 22, 22, 0x0a,
2217
2218 0 // eod
2219};
2220
2221static const char qt_meta_stringdata_QSignalEventGenerator[] = {
2222 "QSignalEventGenerator\0\0execute()\0"
2223};
2224
2225const QMetaObject QSignalEventGenerator::staticMetaObject = {
2226 { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator,
2227 qt_meta_data_QSignalEventGenerator, 0 }
2228};
2229
2230const QMetaObject *QSignalEventGenerator::metaObject() const
2231{
2232 return &staticMetaObject;
2233}
2234
2235void *QSignalEventGenerator::qt_metacast(const char *_clname)
2236{
2237 if (!_clname) return 0;
2238 if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator))
2239 return static_cast<void*>(const_cast< QSignalEventGenerator*>(this));
2240 return QObject::qt_metacast(_clname);
2241}
2242
2243int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
2244{
2245 _id = QObject::qt_metacall(_c, _id, _a);
2246 if (_id < 0)
2247 return _id;
2248 if (_c == QMetaObject::InvokeMetaMethod) {
2249 switch (_id) {
2250 case 0: {
2251// ### in Qt 4.6 we can use QObject::senderSignalIndex()
2252 QObjectPrivate *d = static_cast<QObjectPrivate *>(d_ptr.data());
2253 int signalIndex = -1;
2254 QObject *sender = this->sender();
2255 if (sender && d->currentSender)
2256 signalIndex = d->currentSender->signal;
2257
2258 Q_ASSERT(signalIndex != -1);
2259 QStateMachine *machine = qobject_cast<QStateMachine*>(parent());
2260 QStateMachinePrivate::get(machine)->handleTransitionSignal(sender, signalIndex, _a);
2261 break;
2262 }
2263 default: ;
2264 }
2265 _id -= 1;
2266 }
2267 return _id;
2268}
2269
2270QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent)
2271 : QObject(parent)
2272{
2273}
2274
2275/*!
2276 \class QStateMachine::SignalEvent
2277
2278 \brief The SignalEvent class represents a Qt signal event.
2279
2280 \since 4.6
2281 \ingroup statemachine
2282
2283 A signal event is generated by a QStateMachine in response to a Qt
2284 signal. The QSignalTransition class provides a transition associated with a
2285 signal event. QStateMachine::SignalEvent is part of \l{The State Machine Framework}.
2286
2287 The sender() function returns the object that generated the signal. The
2288 signalIndex() function returns the index of the signal. The arguments()
2289 function returns the arguments of the signal.
2290
2291 \sa QSignalTransition
2292*/
2293
2294/*!
2295 \internal
2296
2297 Constructs a new SignalEvent object with the given \a sender, \a
2298 signalIndex and \a arguments.
2299*/
2300QStateMachine::SignalEvent::SignalEvent(QObject *sender, int signalIndex,
2301 const QList<QVariant> &arguments)
2302 : QEvent(QEvent::StateMachineSignal), m_sender(sender),
2303 m_signalIndex(signalIndex), m_arguments(arguments)
2304{
2305}
2306
2307/*!
2308 Destroys this SignalEvent.
2309*/
2310QStateMachine::SignalEvent::~SignalEvent()
2311{
2312}
2313
2314/*!
2315 \fn QStateMachine::SignalEvent::sender() const
2316
2317 Returns the object that emitted the signal.
2318
2319 \sa QObject::sender()
2320*/
2321
2322/*!
2323 \fn QStateMachine::SignalEvent::signalIndex() const
2324
2325 Returns the index of the signal.
2326
2327 \sa QMetaObject::indexOfSignal(), QMetaObject::method()
2328*/
2329
2330/*!
2331 \fn QStateMachine::SignalEvent::arguments() const
2332
2333 Returns the arguments of the signal.
2334*/
2335
2336
2337/*!
2338 \class QStateMachine::WrappedEvent
2339
2340 \brief The WrappedEvent class holds a clone of an event associated with a QObject.
2341
2342 \since 4.6
2343 \ingroup statemachine
2344
2345 A wrapped event is generated by a QStateMachine in response to a Qt
2346 event. The QEventTransition class provides a transition associated with a
2347 such an event. QStateMachine::WrappedEvent is part of \l{The State Machine
2348 Framework}.
2349
2350 The object() function returns the object that generated the event. The
2351 event() function returns a clone of the original event.
2352
2353 \sa QEventTransition
2354*/
2355
2356/*!
2357 \internal
2358
2359 Constructs a new WrappedEvent object with the given \a object
2360 and \a event.
2361
2362 The WrappedEvent object takes ownership of \a event.
2363*/
2364QStateMachine::WrappedEvent::WrappedEvent(QObject *object, QEvent *event)
2365 : QEvent(QEvent::StateMachineWrapped), m_object(object), m_event(event)
2366{
2367}
2368
2369/*!
2370 Destroys this WrappedEvent.
2371*/
2372QStateMachine::WrappedEvent::~WrappedEvent()
2373{
2374 delete m_event;
2375}
2376
2377/*!
2378 \fn QStateMachine::WrappedEvent::object() const
2379
2380 Returns the object that the event is associated with.
2381*/
2382
2383/*!
2384 \fn QStateMachine::WrappedEvent::event() const
2385
2386 Returns a clone of the original event.
2387*/
2388
2389QT_END_NAMESPACE
2390
2391#include "moc_qstatemachine.cpp"
2392
2393#endif //QT_NO_STATEMACHINE
Note: See TracBrowser for help on using the repository browser.