src/corelib/statemachine/qstate.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtCore module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qstate.h"
       
    43 
       
    44 #ifndef QT_NO_STATEMACHINE
       
    45 
       
    46 #include "qstate_p.h"
       
    47 #include "qhistorystate.h"
       
    48 #include "qhistorystate_p.h"
       
    49 #include "qabstracttransition.h"
       
    50 #include "qabstracttransition_p.h"
       
    51 #include "qsignaltransition.h"
       
    52 #include "qstatemachine.h"
       
    53 #include "qstatemachine_p.h"
       
    54 
       
    55 QT_BEGIN_NAMESPACE
       
    56 
       
    57 /*!
       
    58   \class QState
       
    59 
       
    60   \brief The QState class provides a general-purpose state for QStateMachine.
       
    61 
       
    62   \since 4.6
       
    63   \ingroup statemachine
       
    64 
       
    65   QState objects can have child states, and can have transitions to other
       
    66   states. QState is part of \l{The State Machine Framework}.
       
    67 
       
    68   The addTransition() function adds a transition. The removeTransition()
       
    69   function removes a transition.
       
    70 
       
    71   The assignProperty() function is used for defining property assignments that
       
    72   should be performed when a state is entered.
       
    73 
       
    74   Top-level states must be passed a QStateMachine object as their parent
       
    75   state, or added to a state machine using QStateMachine::addState().
       
    76 
       
    77   \section1 States with Child States
       
    78 
       
    79   The childMode property determines how child states are treated. For
       
    80   non-parallel state groups, the setInitialState() function must be called to
       
    81   set the initial state. The child states are mutually exclusive states, and
       
    82   the state machine needs to know which child state to enter when the parent
       
    83   state is the target of a transition.
       
    84 
       
    85   The state emits the QState::finished() signal when a final child state
       
    86   (QFinalState) is entered.
       
    87 
       
    88   The setErrorState() sets the state's error state. The error state is the
       
    89   state that the state machine will transition to if an error is detected when
       
    90   attempting to enter the state (e.g. because no initial state has been set).
       
    91 
       
    92 */
       
    93 
       
    94 /*!
       
    95     \property QState::initialState
       
    96 
       
    97     \brief the initial state of this state (one of its child states)
       
    98 */
       
    99 
       
   100 /*!
       
   101     \property QState::errorState
       
   102 
       
   103     \brief the error state of this state
       
   104 */
       
   105 
       
   106 /*!
       
   107     \property QState::childMode
       
   108 
       
   109     \brief the child mode of this state
       
   110 
       
   111     The default value of this property is QState::ExclusiveStates.
       
   112 */
       
   113 
       
   114 /*!
       
   115   \enum QState::ChildMode
       
   116 
       
   117   This enum specifies how a state's child states are treated.
       
   118 
       
   119   \value ExclusiveStates The child states are mutually exclusive and an
       
   120   initial state must be set by calling QState::setInitialState().
       
   121 
       
   122   \value ParallelStates The child states are parallel. When the parent state
       
   123   is entered, all its child states are entered in parallel.
       
   124 */
       
   125 
       
   126 QStatePrivate::QStatePrivate()
       
   127     : errorState(0), initialState(0), childMode(QState::ExclusiveStates)
       
   128 {
       
   129 }
       
   130 
       
   131 QStatePrivate::~QStatePrivate()
       
   132 {
       
   133 }
       
   134 
       
   135 void QStatePrivate::emitFinished()
       
   136 {
       
   137     Q_Q(QState);
       
   138     emit q->finished();
       
   139 }
       
   140 
       
   141 void QStatePrivate::emitPolished()
       
   142 {
       
   143     Q_Q(QState);
       
   144     emit q->polished();
       
   145 }
       
   146 
       
   147 /*!
       
   148   Constructs a new state with the given \a parent state.
       
   149 */
       
   150 QState::QState(QState *parent)
       
   151     : QAbstractState(*new QStatePrivate, parent)
       
   152 {
       
   153 }
       
   154 
       
   155 /*!
       
   156   Constructs a new state with the given \a childMode and the given \a parent
       
   157   state.
       
   158 */
       
   159 QState::QState(ChildMode childMode, QState *parent)
       
   160     : QAbstractState(*new QStatePrivate, parent)
       
   161 {
       
   162     Q_D(QState);
       
   163     d->childMode = childMode;
       
   164 }
       
   165 
       
   166 /*!
       
   167   \internal
       
   168 */
       
   169 QState::QState(QStatePrivate &dd, QState *parent)
       
   170     : QAbstractState(dd, parent)
       
   171 {
       
   172 }
       
   173 
       
   174 /*!
       
   175   Destroys this state.
       
   176 */
       
   177 QState::~QState()
       
   178 {
       
   179 }
       
   180 
       
   181 QList<QAbstractState*> QStatePrivate::childStates() const
       
   182 {
       
   183     QList<QAbstractState*> result;
       
   184     QList<QObject*>::const_iterator it;
       
   185     for (it = children.constBegin(); it != children.constEnd(); ++it) {
       
   186         QAbstractState *s = qobject_cast<QAbstractState*>(*it);
       
   187         if (!s || qobject_cast<QHistoryState*>(s))
       
   188             continue;
       
   189         result.append(s);
       
   190     }
       
   191     return result;
       
   192 }
       
   193 
       
   194 QList<QHistoryState*> QStatePrivate::historyStates() const
       
   195 {
       
   196     QList<QHistoryState*> result;
       
   197     QList<QObject*>::const_iterator it;
       
   198     for (it = children.constBegin(); it != children.constEnd(); ++it) {
       
   199         QHistoryState *h = qobject_cast<QHistoryState*>(*it);
       
   200         if (h)
       
   201             result.append(h);
       
   202     }
       
   203     return result;
       
   204 }
       
   205 
       
   206 QList<QAbstractTransition*> QStatePrivate::transitions() const
       
   207 {
       
   208     QList<QAbstractTransition*> result;
       
   209     QList<QObject*>::const_iterator it;
       
   210     for (it = children.constBegin(); it != children.constEnd(); ++it) {
       
   211         QAbstractTransition *t = qobject_cast<QAbstractTransition*>(*it);
       
   212         if (t)
       
   213             result.append(t);
       
   214     }
       
   215     return result;
       
   216 }
       
   217 
       
   218 #ifndef QT_NO_PROPERTIES
       
   219 
       
   220 /*!
       
   221   Instructs this state to set the property with the given \a name of the given
       
   222   \a object to the given \a value when the state is entered.
       
   223 
       
   224   \sa polished()
       
   225 */
       
   226 void QState::assignProperty(QObject *object, const char *name,
       
   227                             const QVariant &value)
       
   228 {
       
   229     Q_D(QState);
       
   230     if (!object) {
       
   231         qWarning("QState::assignProperty: cannot assign property '%s' of null object", name);
       
   232         return;
       
   233     }
       
   234     for (int i = 0; i < d->propertyAssignments.size(); ++i) {
       
   235         QPropertyAssignment &assn = d->propertyAssignments[i];
       
   236         if ((assn.object == object) && (assn.propertyName == name)) {
       
   237             assn.value = value;
       
   238             return;
       
   239         }
       
   240     }
       
   241     d->propertyAssignments.append(QPropertyAssignment(object, name, value));
       
   242 }
       
   243 
       
   244 #endif // QT_NO_PROPERTIES
       
   245 
       
   246 /*!
       
   247   Returns this state's error state. 
       
   248 
       
   249   \sa QStateMachine::error()
       
   250 */
       
   251 QAbstractState *QState::errorState() const
       
   252 {
       
   253     Q_D(const QState);
       
   254     return d->errorState;
       
   255 }
       
   256 
       
   257 /*!
       
   258   Sets this state's error state to be the given \a state. If the error state
       
   259   is not set, or if it is set to 0, the state will inherit its parent's error
       
   260   state recursively. If no error state is set for the state itself or any of 
       
   261   its ancestors, an error will cause the machine to stop executing and an error
       
   262   will be printed to the console.
       
   263 */
       
   264 void QState::setErrorState(QAbstractState *state)
       
   265 {
       
   266     Q_D(QState);
       
   267     if (state != 0 && qobject_cast<QStateMachine*>(state)) {
       
   268         qWarning("QStateMachine::setErrorState: root state cannot be error state");
       
   269         return;
       
   270     }
       
   271     if (state != 0 && (!state->machine() || ((state->machine() != machine()) && !qobject_cast<QStateMachine*>(this)))) {
       
   272         qWarning("QState::setErrorState: error state cannot belong "
       
   273                  "to a different state machine");
       
   274         return;
       
   275     }
       
   276 
       
   277     d->errorState = state;
       
   278 }
       
   279 
       
   280 /*!
       
   281   Adds the given \a transition. The transition has this state as the source.
       
   282   This state takes ownership of the transition. If the transition is successfully
       
   283   added, the function will return the \a transition pointer. Otherwise it will return null.
       
   284 */
       
   285 QAbstractTransition *QState::addTransition(QAbstractTransition *transition)
       
   286 {
       
   287     Q_D(QState);
       
   288     if (!transition) {
       
   289         qWarning("QState::addTransition: cannot add null transition");
       
   290         return 0;
       
   291     }
       
   292 
       
   293     transition->setParent(this);
       
   294     const QList<QWeakPointer<QAbstractState> > &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
       
   295     for (int i = 0; i < targets.size(); ++i) {
       
   296         QAbstractState *t = targets.at(i).data();
       
   297         if (!t) {
       
   298             qWarning("QState::addTransition: cannot add transition to null state");
       
   299             return 0;
       
   300         }
       
   301         if ((QAbstractStatePrivate::get(t)->machine() != d->machine())
       
   302             && QAbstractStatePrivate::get(t)->machine() && d->machine()) {
       
   303             qWarning("QState::addTransition: cannot add transition "
       
   304                      "to a state in a different state machine");
       
   305             return 0;
       
   306         }
       
   307     }
       
   308     if (machine() != 0 && machine()->configuration().contains(this))
       
   309         QStateMachinePrivate::get(machine())->registerTransitions(this);
       
   310     return transition;
       
   311 }
       
   312 
       
   313 /*!
       
   314   Adds a transition associated with the given \a signal of the given \a sender
       
   315   object, and returns the new QSignalTransition object. The transition has
       
   316   this state as the source, and the given \a target as the target state.
       
   317 */
       
   318 QSignalTransition *QState::addTransition(QObject *sender, const char *signal,
       
   319                                          QAbstractState *target)
       
   320 {
       
   321     if (!sender) {
       
   322         qWarning("QState::addTransition: sender cannot be null");
       
   323         return 0;
       
   324     }
       
   325     if (!signal) {
       
   326         qWarning("QState::addTransition: signal cannot be null");
       
   327         return 0;
       
   328     }
       
   329     if (!target) {
       
   330         qWarning("QState::addTransition: cannot add transition to null state");
       
   331         return 0;
       
   332     }
       
   333     int offset = (*signal == '0'+QSIGNAL_CODE) ? 1 : 0;
       
   334     const QMetaObject *meta = sender->metaObject();
       
   335     if (meta->indexOfSignal(signal+offset) == -1) {
       
   336         if (meta->indexOfSignal(QMetaObject::normalizedSignature(signal+offset)) == -1) {
       
   337             qWarning("QState::addTransition: no such signal %s::%s",
       
   338                      meta->className(), signal+offset);
       
   339             return 0;
       
   340         }
       
   341     }
       
   342     QSignalTransition *trans = new QSignalTransition(sender, signal);
       
   343     trans->setTargetState(target);
       
   344     addTransition(trans);
       
   345     return trans;
       
   346 }
       
   347 
       
   348 namespace {
       
   349 
       
   350 // ### Make public?
       
   351 class UnconditionalTransition : public QAbstractTransition
       
   352 {
       
   353 public:
       
   354     UnconditionalTransition(QAbstractState *target)
       
   355         : QAbstractTransition()
       
   356     { setTargetState(target); }
       
   357 protected:
       
   358     void onTransition(QEvent *) {}
       
   359     bool eventTest(QEvent *) { return true; }
       
   360 };
       
   361 
       
   362 } // namespace
       
   363 
       
   364 /*!
       
   365   Adds an unconditional transition from this state to the given \a target
       
   366   state, and returns then new transition object.
       
   367 */
       
   368 QAbstractTransition *QState::addTransition(QAbstractState *target)
       
   369 {
       
   370     if (!target) {
       
   371         qWarning("QState::addTransition: cannot add transition to null state");
       
   372         return 0;
       
   373     }
       
   374     UnconditionalTransition *trans = new UnconditionalTransition(target);
       
   375     return addTransition(trans);
       
   376 }
       
   377 
       
   378 /*!
       
   379   Removes the given \a transition from this state.  The state releases
       
   380   ownership of the transition.
       
   381 
       
   382   \sa addTransition()
       
   383 */
       
   384 void QState::removeTransition(QAbstractTransition *transition)
       
   385 {
       
   386     Q_D(QState);
       
   387     if (!transition) {
       
   388         qWarning("QState::removeTransition: cannot remove null transition");
       
   389         return;
       
   390     }
       
   391     if (transition->sourceState() != this) {
       
   392         qWarning("QState::removeTransition: transition %p's source state (%p)"
       
   393                  " is different from this state (%p)",
       
   394                  transition, transition->sourceState(), this);
       
   395         return;
       
   396     }
       
   397     QStateMachinePrivate *mach = QStateMachinePrivate::get(d->machine());
       
   398     if (mach)
       
   399         mach->unregisterTransition(transition);
       
   400     transition->setParent(0);
       
   401 }
       
   402 
       
   403 /*!
       
   404   \reimp
       
   405 */
       
   406 void QState::onEntry(QEvent *event)
       
   407 {
       
   408     Q_UNUSED(event);
       
   409 }
       
   410 
       
   411 /*!
       
   412   \reimp
       
   413 */
       
   414 void QState::onExit(QEvent *event)
       
   415 {
       
   416     Q_UNUSED(event);
       
   417 }
       
   418 
       
   419 /*!
       
   420   Returns this state's initial state, or 0 if the state has no initial state.
       
   421 */
       
   422 QAbstractState *QState::initialState() const
       
   423 {
       
   424     Q_D(const QState);
       
   425     return d->initialState;
       
   426 }
       
   427 
       
   428 /*!
       
   429   Sets this state's initial state to be the given \a state.
       
   430   \a state has to be a child of this state.
       
   431 */
       
   432 void QState::setInitialState(QAbstractState *state)
       
   433 {
       
   434     Q_D(QState);
       
   435     if (d->childMode == QState::ParallelStates) {
       
   436         qWarning("QState::setInitialState: ignoring attempt to set initial state "
       
   437                  "of parallel state group %p", this);
       
   438         return;
       
   439     }
       
   440     if (state && (state->parentState() != this)) {
       
   441         qWarning("QState::setInitialState: state %p is not a child of this state (%p)",
       
   442                  state, this);
       
   443         return;
       
   444     }
       
   445     d->initialState = state;
       
   446 }
       
   447 
       
   448 /*!
       
   449   Returns the child mode of this state.
       
   450 */
       
   451 QState::ChildMode QState::childMode() const
       
   452 {
       
   453     Q_D(const QState);
       
   454     return d->childMode;
       
   455 }
       
   456 
       
   457 /*!
       
   458   Sets the child \a mode of this state.
       
   459 */
       
   460 void QState::setChildMode(ChildMode mode)
       
   461 {
       
   462     Q_D(QState);
       
   463     d->childMode = mode;
       
   464 }
       
   465 
       
   466 /*!
       
   467   \reimp
       
   468 */
       
   469 bool QState::event(QEvent *e)
       
   470 {
       
   471     return QAbstractState::event(e);
       
   472 }
       
   473 
       
   474 /*!
       
   475   \fn QState::finished()
       
   476 
       
   477   This signal is emitted when a final child state of this state is entered.
       
   478 
       
   479   \sa QFinalState
       
   480 */
       
   481 
       
   482 /*!
       
   483   \fn QState::polished()
       
   484 
       
   485   This signal is emitted when all properties have been assigned their final value.
       
   486 
       
   487   \sa QState::assignProperty(), QAbstractTransition::addAnimation()
       
   488 */
       
   489 
       
   490 QT_END_NAMESPACE
       
   491 
       
   492 #endif //QT_NO_STATEMACHINE