/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "private/qdeclarativestategroup_p.h" #include "private/qdeclarativetransition_p.h" #include "private/qdeclarativestate_p_p.h" #include #include #include #include #include #include QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); class QDeclarativeStateGroupPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QDeclarativeStateGroup) public: QDeclarativeStateGroupPrivate() : nullState(0), componentComplete(true), ignoreTrans(false), applyingState(false), unnamedCount(0) {} QString currentState; QDeclarativeState *nullState; static void append_state(QDeclarativeListProperty *list, QDeclarativeState *state); static int count_state(QDeclarativeListProperty *list); static QDeclarativeState *at_state(QDeclarativeListProperty *list, int index); static void clear_states(QDeclarativeListProperty *list); QList states; QList transitions; bool componentComplete; bool ignoreTrans; bool applyingState; int unnamedCount; QDeclarativeTransition *findTransition(const QString &from, const QString &to); void setCurrentStateInternal(const QString &state, bool = false); bool updateAutoState(); }; /*! \qmlclass StateGroup QDeclarativeStateGroup \ingroup qml-state-elements \since 4.7 \brief The StateGroup element provides state support for non-Item elements. Item (and all derived elements) provides built in support for states and transitions via its \l{Item::state}{state}, \l{Item::states}{states} and \l{Item::transitions}{transitions} properties. StateGroup provides an easy way to use this support in other (non-Item-derived) elements. \qml MyCustomObject { StateGroup { id: myStateGroup states: State { name: "state1" // ... } transitions: Transition { // ... } } onSomethingHappened: myStateGroup.state = "state1"; } \endqml \sa {qmlstate}{States} {QML Animation and Transitions}{Transitions}, {QtDeclarative} */ QDeclarativeStateGroup::QDeclarativeStateGroup(QObject *parent) : QObject(*(new QDeclarativeStateGroupPrivate), parent) { } QDeclarativeStateGroup::~QDeclarativeStateGroup() { Q_D(const QDeclarativeStateGroup); for (int i = 0; i < d->states.count(); ++i) d->states.at(i)->setStateGroup(0); } QList QDeclarativeStateGroup::states() const { Q_D(const QDeclarativeStateGroup); return d->states; } /*! \qmlproperty list StateGroup::states This property holds a list of states defined by the state group. \qml StateGroup { states: [ State { // State definition... }, State { // ... } // Other states... ] } \endqml \sa {qmlstate}{States} */ QDeclarativeListProperty QDeclarativeStateGroup::statesProperty() { Q_D(QDeclarativeStateGroup); return QDeclarativeListProperty(this, &d->states, &QDeclarativeStateGroupPrivate::append_state, &QDeclarativeStateGroupPrivate::count_state, &QDeclarativeStateGroupPrivate::at_state, &QDeclarativeStateGroupPrivate::clear_states); } void QDeclarativeStateGroupPrivate::append_state(QDeclarativeListProperty *list, QDeclarativeState *state) { QDeclarativeStateGroup *_this = static_cast(list->object); if (state) { _this->d_func()->states.append(state); state->setStateGroup(_this); } } int QDeclarativeStateGroupPrivate::count_state(QDeclarativeListProperty *list) { QDeclarativeStateGroup *_this = static_cast(list->object); return _this->d_func()->states.count(); } QDeclarativeState *QDeclarativeStateGroupPrivate::at_state(QDeclarativeListProperty *list, int index) { QDeclarativeStateGroup *_this = static_cast(list->object); return _this->d_func()->states.at(index); } void QDeclarativeStateGroupPrivate::clear_states(QDeclarativeListProperty *list) { QDeclarativeStateGroup *_this = static_cast(list->object); _this->d_func()->setCurrentStateInternal(QString(), true); for (int i = 0; i < _this->d_func()->states.count(); ++i) { _this->d_func()->states.at(i)->setStateGroup(0); } _this->d_func()->states.clear(); } /*! \qmlproperty list StateGroup::transitions This property holds a list of transitions defined by the state group. \qml StateGroup { transitions: [ Transition { // ... }, Transition { // ... } // ... ] } \endqml \sa {QML Animation and Transitions}{Transitions} */ QDeclarativeListProperty QDeclarativeStateGroup::transitionsProperty() { Q_D(QDeclarativeStateGroup); return QDeclarativeListProperty(this, d->transitions); } /*! \qmlproperty string StateGroup::state This property holds the name of the current state of the state group. This property is often used in scripts to change between states. For example: \js function toggle() { if (button.state == 'On') button.state = 'Off'; else button.state = 'On'; } \endjs If the state group is in its base state (i.e. no explicit state has been set), \c state will be a blank string. Likewise, you can return a state group to its base state by setting its current state to \c ''. \sa {qmlstates}{States} */ QString QDeclarativeStateGroup::state() const { Q_D(const QDeclarativeStateGroup); return d->currentState; } void QDeclarativeStateGroup::setState(const QString &state) { Q_D(QDeclarativeStateGroup); if (d->currentState == state) return; d->setCurrentStateInternal(state); } void QDeclarativeStateGroup::classBegin() { Q_D(QDeclarativeStateGroup); d->componentComplete = false; } void QDeclarativeStateGroup::componentComplete() { Q_D(QDeclarativeStateGroup); d->componentComplete = true; for (int ii = 0; ii < d->states.count(); ++ii) { QDeclarativeState *state = d->states.at(ii); if (!state->isNamed()) state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount)); } if (d->updateAutoState()) { return; } else if (!d->currentState.isEmpty()) { QString cs = d->currentState; d->currentState.clear(); d->setCurrentStateInternal(cs, true); } } /*! Returns true if the state was changed, otherwise false. */ bool QDeclarativeStateGroup::updateAutoState() { Q_D(QDeclarativeStateGroup); return d->updateAutoState(); } bool QDeclarativeStateGroupPrivate::updateAutoState() { Q_Q(QDeclarativeStateGroup); if (!componentComplete) return false; bool revert = false; for (int ii = 0; ii < states.count(); ++ii) { QDeclarativeState *state = states.at(ii); if (state->isWhenKnown()) { if (state->isNamed()) { if (state->when() && state->when()->evaluate().toBool()) { if (stateChangeDebug()) qWarning() << "Setting auto state due to:" << state->when()->expression(); if (currentState != state->name()) { q->setState(state->name()); return true; } else { return false; } } else if (state->name() == currentState) { revert = true; } } } } if (revert) { bool rv = !currentState.isEmpty(); q->setState(QString()); return rv; } else { return false; } } QDeclarativeTransition *QDeclarativeStateGroupPrivate::findTransition(const QString &from, const QString &to) { QDeclarativeTransition *highest = 0; int score = 0; bool reversed = false; bool done = false; for (int ii = 0; !done && ii < transitions.count(); ++ii) { QDeclarativeTransition *t = transitions.at(ii); for (int ii = 0; ii < 2; ++ii) { if (ii && (!t->reversible() || (t->fromState() == QLatin1String("*") && t->toState() == QLatin1String("*")))) break; QStringList fromState; QStringList toState; fromState = t->fromState().split(QLatin1Char(',')); toState = t->toState().split(QLatin1Char(',')); if (ii == 1) qSwap(fromState, toState); int tScore = 0; if (fromState.contains(from)) tScore += 2; else if (fromState.contains(QLatin1String("*"))) tScore += 1; else continue; if (toState.contains(to)) tScore += 2; else if (toState.contains(QLatin1String("*"))) tScore += 1; else continue; if (ii == 1) reversed = true; else reversed = false; if (tScore == 4) { highest = t; done = true; break; } else if (tScore > score) { score = tScore; highest = t; } } } if (highest) highest->setReversed(reversed); return highest; } void QDeclarativeStateGroupPrivate::setCurrentStateInternal(const QString &state, bool ignoreTrans) { Q_Q(QDeclarativeStateGroup); if (!componentComplete) { currentState = state; return; } if (applyingState) { qmlInfo(q) << "Can't apply a state change as part of a state definition."; return; } applyingState = true; QDeclarativeTransition *transition = (ignoreTrans || ignoreTrans) ? 0 : findTransition(currentState, state); if (stateChangeDebug()) { qWarning() << this << "Changing state. From" << currentState << ". To" << state; if (transition) qWarning() << " using transition" << transition->fromState() << transition->toState(); } QDeclarativeState *oldState = 0; if (!currentState.isEmpty()) { for (int ii = 0; ii < states.count(); ++ii) { if (states.at(ii)->name() == currentState) { oldState = states.at(ii); break; } } } currentState = state; emit q->stateChanged(currentState); QDeclarativeState *newState = 0; for (int ii = 0; ii < states.count(); ++ii) { if (states.at(ii)->name() == currentState) { newState = states.at(ii); break; } } if (oldState == 0 || newState == 0) { if (!nullState) { nullState = new QDeclarativeState; QDeclarative_setParent_noEvent(nullState, q); } if (!oldState) oldState = nullState; if (!newState) newState = nullState; } newState->apply(q, transition, oldState); applyingState = false; if (!transition) static_cast(QObjectPrivate::get(newState))->complete(); } QDeclarativeState *QDeclarativeStateGroup::findState(const QString &name) const { Q_D(const QDeclarativeStateGroup); for (int i = 0; i < d->states.count(); ++i) { QDeclarativeState *state = d->states.at(i); if (state->name() == name) return state; } return 0; } void QDeclarativeStateGroup::removeState(QDeclarativeState *state) { Q_D(QDeclarativeStateGroup); d->states.removeOne(state); } QT_END_NAMESPACE