src/declarative/util/qdeclarativestategroup.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 QtDeclarative 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 "private/qdeclarativestategroup_p.h"
       
    43 
       
    44 #include "private/qdeclarativetransition_p.h"
       
    45 #include "private/qdeclarativestate_p_p.h"
       
    46 
       
    47 #include <qdeclarativebinding_p.h>
       
    48 #include <qdeclarativeglobal_p.h>
       
    49 
       
    50 #include <QtCore/qstringbuilder.h>
       
    51 #include <QtCore/qdebug.h>
       
    52 
       
    53 #include <private/qobject_p.h>
       
    54 #include <qdeclarativeinfo.h>
       
    55 
       
    56 QT_BEGIN_NAMESPACE
       
    57 
       
    58 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
       
    59 
       
    60 class QDeclarativeStateGroupPrivate : public QObjectPrivate
       
    61 {
       
    62     Q_DECLARE_PUBLIC(QDeclarativeStateGroup)
       
    63 public:
       
    64     QDeclarativeStateGroupPrivate()
       
    65     : nullState(0), componentComplete(true),
       
    66       ignoreTrans(false), applyingState(false), unnamedCount(0) {}
       
    67 
       
    68     QString currentState;
       
    69     QDeclarativeState *nullState;
       
    70 
       
    71     static void append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state);
       
    72     static int count_state(QDeclarativeListProperty<QDeclarativeState> *list);
       
    73     static QDeclarativeState *at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index);
       
    74     static void clear_states(QDeclarativeListProperty<QDeclarativeState> *list);
       
    75 
       
    76     QList<QDeclarativeState *> states;
       
    77     QList<QDeclarativeTransition *> transitions;
       
    78 
       
    79     bool componentComplete;
       
    80     bool ignoreTrans;
       
    81     bool applyingState;
       
    82     int unnamedCount;
       
    83 
       
    84     QDeclarativeTransition *findTransition(const QString &from, const QString &to);
       
    85     void setCurrentStateInternal(const QString &state, bool = false);
       
    86     bool updateAutoState();
       
    87 };
       
    88 
       
    89 /*!
       
    90    \qmlclass StateGroup QDeclarativeStateGroup
       
    91    \since 4.7
       
    92    \brief The StateGroup element provides state support for non-Item elements.
       
    93 
       
    94    Item (and all dervied elements) provides built in support for states and transitions
       
    95    via its state, states and transitions properties. StateGroup provides an easy way to
       
    96    use this support in other (non-Item-derived) elements.
       
    97 
       
    98    \qml
       
    99    MyCustomObject {
       
   100        StateGroup {
       
   101            id: myStateGroup
       
   102            states: State {
       
   103                name: "state1"
       
   104                ...
       
   105            }
       
   106            transitions: Transition {
       
   107                ...
       
   108            }
       
   109        }
       
   110 
       
   111        onSomethingHappened: myStateGroup.state = "state1";
       
   112    }
       
   113    \endqml
       
   114 
       
   115    \sa {qmlstate}{States} {state-transitions}{Transitions}, {QtDeclarative}
       
   116 */
       
   117 
       
   118 QDeclarativeStateGroup::QDeclarativeStateGroup(QObject *parent)
       
   119     : QObject(*(new QDeclarativeStateGroupPrivate), parent)
       
   120 {
       
   121 }
       
   122 
       
   123 QDeclarativeStateGroup::~QDeclarativeStateGroup()
       
   124 {
       
   125     Q_D(const QDeclarativeStateGroup);
       
   126     for (int i = 0; i < d->states.count(); ++i)
       
   127         d->states.at(i)->setStateGroup(0);
       
   128 }
       
   129 
       
   130 QList<QDeclarativeState *> QDeclarativeStateGroup::states() const
       
   131 {
       
   132     Q_D(const QDeclarativeStateGroup);
       
   133     return d->states;
       
   134 }
       
   135 
       
   136 /*!
       
   137   \qmlproperty list<State> StateGroup::states
       
   138   This property holds a list of states defined by the state group.
       
   139 
       
   140   \qml
       
   141   StateGroup {
       
   142     states: [
       
   143       State { ... },
       
   144       State { ... }
       
   145       ...
       
   146     ]
       
   147   }
       
   148   \endqml
       
   149 
       
   150   \sa {qmlstate}{States}
       
   151 */
       
   152 QDeclarativeListProperty<QDeclarativeState> QDeclarativeStateGroup::statesProperty()
       
   153 {
       
   154     Q_D(QDeclarativeStateGroup);
       
   155     return QDeclarativeListProperty<QDeclarativeState>(this, &d->states, &QDeclarativeStateGroupPrivate::append_state,
       
   156                                                        &QDeclarativeStateGroupPrivate::count_state,
       
   157                                                        &QDeclarativeStateGroupPrivate::at_state,
       
   158                                                        &QDeclarativeStateGroupPrivate::clear_states);
       
   159 }
       
   160 
       
   161 void QDeclarativeStateGroupPrivate::append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state)
       
   162 {
       
   163     QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
       
   164     if (state) {
       
   165         _this->d_func()->states.append(state);
       
   166         state->setStateGroup(_this);
       
   167     }
       
   168 
       
   169 }
       
   170 
       
   171 int QDeclarativeStateGroupPrivate::count_state(QDeclarativeListProperty<QDeclarativeState> *list)
       
   172 {
       
   173     QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
       
   174     return _this->d_func()->states.count();
       
   175 }
       
   176 
       
   177 QDeclarativeState *QDeclarativeStateGroupPrivate::at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index)
       
   178 {
       
   179     QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
       
   180     return _this->d_func()->states.at(index);
       
   181 }
       
   182 
       
   183 void QDeclarativeStateGroupPrivate::clear_states(QDeclarativeListProperty<QDeclarativeState> *list)
       
   184 {
       
   185     QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
       
   186     _this->d_func()->setCurrentStateInternal(QString(), true);
       
   187     for (int i = 0; i < _this->d_func()->states.count(); ++i) {
       
   188         _this->d_func()->states.at(i)->setStateGroup(0);
       
   189     }
       
   190     _this->d_func()->states.clear();
       
   191 }
       
   192 
       
   193 /*!
       
   194   \qmlproperty list<Transition> StateGroup::transitions
       
   195   This property holds a list of transitions defined by the state group.
       
   196 
       
   197   \qml
       
   198   StateGroup {
       
   199     transitions: [
       
   200       Transition { ... },
       
   201       Transition { ... }
       
   202       ...
       
   203     ]
       
   204   }
       
   205   \endqml
       
   206 
       
   207   \sa {state-transitions}{Transitions}
       
   208 */
       
   209 QDeclarativeListProperty<QDeclarativeTransition> QDeclarativeStateGroup::transitionsProperty()
       
   210 {
       
   211     Q_D(QDeclarativeStateGroup);
       
   212     return QDeclarativeListProperty<QDeclarativeTransition>(this, d->transitions);
       
   213 }
       
   214 
       
   215 /*!
       
   216   \qmlproperty string StateGroup::state
       
   217 
       
   218   This property holds the name of the current state of the state group.
       
   219 
       
   220   This property is often used in scripts to change between states. For
       
   221   example:
       
   222 
       
   223   \qml
       
   224     function toggle() {
       
   225         if (button.state == 'On')
       
   226             button.state = 'Off';
       
   227         else
       
   228             button.state = 'On';
       
   229     }
       
   230   \endqml
       
   231 
       
   232   If the state group is in its base state (i.e. no explicit state has been
       
   233   set), \c state will be a blank string. Likewise, you can return a
       
   234   state group to its base state by setting its current state to \c ''.
       
   235 
       
   236   \sa {qmlstates}{States}
       
   237 */
       
   238 QString QDeclarativeStateGroup::state() const
       
   239 {
       
   240     Q_D(const QDeclarativeStateGroup);
       
   241     return d->currentState;
       
   242 }
       
   243 
       
   244 void QDeclarativeStateGroup::setState(const QString &state)
       
   245 {
       
   246     Q_D(QDeclarativeStateGroup);
       
   247     if (d->currentState == state)
       
   248         return;
       
   249 
       
   250     d->setCurrentStateInternal(state);
       
   251 }
       
   252 
       
   253 void QDeclarativeStateGroup::classBegin()
       
   254 {
       
   255     Q_D(QDeclarativeStateGroup);
       
   256     d->componentComplete = false;
       
   257 }
       
   258 
       
   259 void QDeclarativeStateGroup::componentComplete()
       
   260 {
       
   261     Q_D(QDeclarativeStateGroup);
       
   262     d->componentComplete = true;
       
   263 
       
   264     for (int ii = 0; ii < d->states.count(); ++ii) {
       
   265         QDeclarativeState *state = d->states.at(ii);
       
   266         if (state->name().isEmpty())
       
   267             state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount));
       
   268     }
       
   269 
       
   270     if (d->updateAutoState()) {
       
   271         return;
       
   272     } else if (!d->currentState.isEmpty()) {
       
   273         QString cs = d->currentState;
       
   274         d->currentState = QString();
       
   275         d->setCurrentStateInternal(cs, true);
       
   276     }
       
   277 }
       
   278 
       
   279 /*!
       
   280     Returns true if the state was changed, otherwise false.
       
   281 */
       
   282 bool QDeclarativeStateGroup::updateAutoState()
       
   283 {
       
   284     Q_D(QDeclarativeStateGroup);
       
   285     return d->updateAutoState();
       
   286 }
       
   287 
       
   288 bool QDeclarativeStateGroupPrivate::updateAutoState()
       
   289 {
       
   290     Q_Q(QDeclarativeStateGroup);
       
   291     if (!componentComplete)
       
   292         return false;
       
   293 
       
   294     bool revert = false;
       
   295     for (int ii = 0; ii < states.count(); ++ii) {
       
   296         QDeclarativeState *state = states.at(ii);
       
   297         if (state->isWhenKnown()) {
       
   298             if (!state->name().isEmpty()) {
       
   299                 if (state->when() && state->when()->evaluate().toBool()) {
       
   300                     if (stateChangeDebug()) 
       
   301                         qWarning() << "Setting auto state due to:" 
       
   302                                    << state->when()->expression();
       
   303                     if (currentState != state->name()) {
       
   304                         q->setState(state->name());
       
   305                         return true;
       
   306                     } else {
       
   307                         return false;
       
   308                     }
       
   309                 } else if (state->name() == currentState) {
       
   310                     revert = true;
       
   311                 }
       
   312             }
       
   313         }
       
   314     }
       
   315     if (revert) {
       
   316         bool rv = currentState != QString();
       
   317         q->setState(QString());
       
   318         return rv;
       
   319     } else {
       
   320         return false;
       
   321     }
       
   322 }
       
   323 
       
   324 QDeclarativeTransition *QDeclarativeStateGroupPrivate::findTransition(const QString &from, const QString &to)
       
   325 {
       
   326     QDeclarativeTransition *highest = 0;
       
   327     int score = 0;
       
   328     bool reversed = false;
       
   329     bool done = false;
       
   330 
       
   331     for (int ii = 0; !done && ii < transitions.count(); ++ii) {
       
   332         QDeclarativeTransition *t = transitions.at(ii);
       
   333         for (int ii = 0; ii < 2; ++ii)
       
   334         {
       
   335             if (ii && (!t->reversible() ||
       
   336                       (t->fromState() == QLatin1String("*") && 
       
   337                        t->toState() == QLatin1String("*"))))
       
   338                 break;
       
   339             QStringList fromState;
       
   340             QStringList toState;
       
   341 
       
   342             fromState = t->fromState().split(QLatin1Char(','));
       
   343             toState = t->toState().split(QLatin1Char(','));
       
   344             if (ii == 1)
       
   345                 qSwap(fromState, toState);
       
   346             int tScore = 0;
       
   347             if (fromState.contains(from))
       
   348                 tScore += 2;
       
   349             else if (fromState.contains(QLatin1String("*")))
       
   350                 tScore += 1;
       
   351             else
       
   352                 continue;
       
   353 
       
   354             if (toState.contains(to))
       
   355                 tScore += 2;
       
   356             else if (toState.contains(QLatin1String("*")))
       
   357                 tScore += 1;
       
   358             else
       
   359                 continue;
       
   360 
       
   361             if (ii == 1)
       
   362                 reversed = true;
       
   363             else
       
   364                 reversed = false;
       
   365 
       
   366             if (tScore == 4) {
       
   367                 highest = t;
       
   368                 done = true;
       
   369                 break;
       
   370             } else if (tScore > score) {
       
   371                 score = tScore;
       
   372                 highest = t;
       
   373             }
       
   374         }
       
   375     }
       
   376 
       
   377     if (highest)
       
   378         highest->setReversed(reversed);
       
   379 
       
   380     return highest;
       
   381 }
       
   382 
       
   383 void QDeclarativeStateGroupPrivate::setCurrentStateInternal(const QString &state, 
       
   384                                                    bool ignoreTrans)
       
   385 {
       
   386     Q_Q(QDeclarativeStateGroup);
       
   387     if (!componentComplete) {
       
   388         currentState = state;
       
   389         return;
       
   390     }
       
   391 
       
   392     if (applyingState) {
       
   393         qmlInfo(q) << "Can't apply a state change as part of a state definition.";
       
   394         return;
       
   395     }
       
   396 
       
   397     applyingState = true;
       
   398 
       
   399     QDeclarativeTransition *transition = (ignoreTrans || ignoreTrans) ? 0 : findTransition(currentState, state);
       
   400     if (stateChangeDebug()) {
       
   401         qWarning() << this << "Changing state.  From" << currentState << ". To" << state;
       
   402         if (transition)
       
   403             qWarning() << "   using transition" << transition->fromState() 
       
   404                        << transition->toState();
       
   405     }
       
   406 
       
   407     QDeclarativeState *oldState = 0;
       
   408     if (!currentState.isEmpty()) {
       
   409         for (int ii = 0; ii < states.count(); ++ii) {
       
   410             if (states.at(ii)->name() == currentState) {
       
   411                 oldState = states.at(ii);
       
   412                 break;
       
   413             }
       
   414         }
       
   415     }
       
   416 
       
   417     currentState = state;
       
   418     emit q->stateChanged(currentState);
       
   419 
       
   420     QDeclarativeState *newState = 0;
       
   421     for (int ii = 0; ii < states.count(); ++ii) {
       
   422         if (states.at(ii)->name() == currentState) {
       
   423             newState = states.at(ii);
       
   424             break;
       
   425         }
       
   426     }
       
   427 
       
   428     if (oldState == 0 || newState == 0) {
       
   429         if (!nullState) { nullState = new QDeclarativeState; QDeclarative_setParent_noEvent(nullState, q); }
       
   430         if (!oldState) oldState = nullState;
       
   431         if (!newState) newState = nullState;
       
   432     }
       
   433 
       
   434     newState->apply(q, transition, oldState);
       
   435     applyingState = false;
       
   436     if (!transition)
       
   437         static_cast<QDeclarativeStatePrivate*>(QObjectPrivate::get(newState))->complete();
       
   438 }
       
   439 
       
   440 QDeclarativeState *QDeclarativeStateGroup::findState(const QString &name) const
       
   441 {
       
   442     Q_D(const QDeclarativeStateGroup);
       
   443     for (int i = 0; i < d->states.count(); ++i) {
       
   444         QDeclarativeState *state = d->states.at(i);
       
   445         if (state->name() == name)
       
   446             return state;
       
   447     }
       
   448 
       
   449     return 0;
       
   450 }
       
   451 
       
   452 void QDeclarativeStateGroup::removeState(QDeclarativeState *state)
       
   453 {
       
   454     Q_D(QDeclarativeStateGroup);
       
   455     d->states.removeOne(state);
       
   456 }
       
   457 
       
   458 QT_END_NAMESPACE
       
   459 
       
   460