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