b6d63eec351e68a83e22b584976a908fd8c1cef1
[profile/ivi/qtdeclarative.git] / src / qtquick1 / util / qdeclarativestategroup.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "QtQuick1/private/qdeclarativestategroup_p.h"
43
44 #include "QtQuick1/private/qdeclarativetransition_p.h"
45 #include "QtQuick1/private/qdeclarativestate_p_p.h"
46
47 #include <QtDeclarative/private/qdeclarativebinding_p.h>
48 #include <QtDeclarative/private/qdeclarativeglobal_p.h>
49
50 #include <QtCore/qstringbuilder.h>
51 #include <QtCore/qdebug.h>
52
53 #include <private/qobject_p.h>
54 #include <QtDeclarative/qdeclarativeinfo.h>
55
56 QT_BEGIN_NAMESPACE
57
58
59
60 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
61
62 class QDeclarative1StateGroupPrivate : public QObjectPrivate
63 {
64     Q_DECLARE_PUBLIC(QDeclarative1StateGroup)
65 public:
66     QDeclarative1StateGroupPrivate()
67     : nullState(0), componentComplete(true),
68       ignoreTrans(false), applyingState(false), unnamedCount(0) {}
69
70     QString currentState;
71     QDeclarative1State *nullState;
72
73     static void append_state(QDeclarativeListProperty<QDeclarative1State> *list, QDeclarative1State *state);
74     static int count_state(QDeclarativeListProperty<QDeclarative1State> *list);
75     static QDeclarative1State *at_state(QDeclarativeListProperty<QDeclarative1State> *list, int index);
76     static void clear_states(QDeclarativeListProperty<QDeclarative1State> *list);
77
78     static void append_transition(QDeclarativeListProperty<QDeclarative1Transition> *list, QDeclarative1Transition *state);
79     static int count_transitions(QDeclarativeListProperty<QDeclarative1Transition> *list);
80     static QDeclarative1Transition *at_transition(QDeclarativeListProperty<QDeclarative1Transition> *list, int index);
81     static void clear_transitions(QDeclarativeListProperty<QDeclarative1Transition> *list);
82
83     QList<QDeclarative1State *> states;
84     QList<QDeclarative1Transition *> transitions;
85
86     bool componentComplete;
87     bool ignoreTrans;
88     bool applyingState;
89     int unnamedCount;
90
91     QDeclarative1Transition *findTransition(const QString &from, const QString &to);
92     void setCurrentStateInternal(const QString &state, bool = false);
93     bool updateAutoState();
94 };
95
96 /*!
97    \qmlclass StateGroup QDeclarative1StateGroup
98     \inqmlmodule QtQuick 1
99    \ingroup qml-state-elements
100    \since QtQuick 1.0
101    \brief The StateGroup element provides state support for non-Item elements.
102
103    Item (and all derived elements) provides built in support for states and transitions
104    via its \l{Item::state}{state}, \l{Item::states}{states} and \l{Item::transitions}{transitions} properties. StateGroup provides an easy way to
105    use this support in other (non-Item-derived) elements.
106
107    \qml
108    MyCustomObject {
109        StateGroup {
110            id: myStateGroup
111            states: State {
112                name: "state1"
113                // ...
114            }
115            transitions: Transition {
116                // ...
117            }
118        }
119
120        onSomethingHappened: myStateGroup.state = "state1";
121    }
122    \endqml
123
124    \sa {qmlstate}{States} {QML Animation and Transitions}{Transitions}, {QtDeclarative}
125 */
126
127 QDeclarative1StateGroup::QDeclarative1StateGroup(QObject *parent)
128     : QObject(*(new QDeclarative1StateGroupPrivate), parent)
129 {
130 }
131
132 QDeclarative1StateGroup::~QDeclarative1StateGroup()
133 {
134     Q_D(const QDeclarative1StateGroup);
135     for (int i = 0; i < d->states.count(); ++i)
136         d->states.at(i)->setStateGroup(0);
137 }
138
139 QList<QDeclarative1State *> QDeclarative1StateGroup::states() const
140 {
141     Q_D(const QDeclarative1StateGroup);
142     return d->states;
143 }
144
145 /*!
146   \qmlproperty list<State> QtQuick1::StateGroup::states
147   This property holds a list of states defined by the state group.
148
149   \qml
150   StateGroup {
151       states: [
152           State {
153               // State definition...
154           },
155           State {
156               // ...
157           }
158           // Other states...
159       ]
160   }
161   \endqml
162
163   \sa {qmlstate}{States}
164 */
165 QDeclarativeListProperty<QDeclarative1State> QDeclarative1StateGroup::statesProperty()
166 {
167     Q_D(QDeclarative1StateGroup);
168     return QDeclarativeListProperty<QDeclarative1State>(this, &d->states, &QDeclarative1StateGroupPrivate::append_state,
169                                                        &QDeclarative1StateGroupPrivate::count_state,
170                                                        &QDeclarative1StateGroupPrivate::at_state,
171                                                        &QDeclarative1StateGroupPrivate::clear_states);
172 }
173
174 void QDeclarative1StateGroupPrivate::append_state(QDeclarativeListProperty<QDeclarative1State> *list, QDeclarative1State *state)
175 {
176     QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object);
177     if (state) {
178         _this->d_func()->states.append(state);
179         state->setStateGroup(_this);
180     }
181
182 }
183
184 int QDeclarative1StateGroupPrivate::count_state(QDeclarativeListProperty<QDeclarative1State> *list)
185 {
186     QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object);
187     return _this->d_func()->states.count();
188 }
189
190 QDeclarative1State *QDeclarative1StateGroupPrivate::at_state(QDeclarativeListProperty<QDeclarative1State> *list, int index)
191 {
192     QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object);
193     return _this->d_func()->states.at(index);
194 }
195
196 void QDeclarative1StateGroupPrivate::clear_states(QDeclarativeListProperty<QDeclarative1State> *list)
197 {
198     QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object);
199     _this->d_func()->setCurrentStateInternal(QString(), true);
200     for (int i = 0; i < _this->d_func()->states.count(); ++i) {
201         _this->d_func()->states.at(i)->setStateGroup(0);
202     }
203     _this->d_func()->states.clear();
204 }
205
206 /*!
207   \qmlproperty list<Transition> QtQuick1::StateGroup::transitions
208   This property holds a list of transitions defined by the state group.
209
210   \qml
211   StateGroup {
212       transitions: [
213           Transition {
214             // ...
215           },
216           Transition {
217             // ...
218           }
219           // ...
220       ]
221   }
222   \endqml
223
224   \sa {QML Animation and Transitions}{Transitions}
225 */
226 QDeclarativeListProperty<QDeclarative1Transition> QDeclarative1StateGroup::transitionsProperty()
227 {
228     Q_D(QDeclarative1StateGroup);
229     return QDeclarativeListProperty<QDeclarative1Transition>(this, &d->transitions, &QDeclarative1StateGroupPrivate::append_transition,
230                                                        &QDeclarative1StateGroupPrivate::count_transitions,
231                                                        &QDeclarative1StateGroupPrivate::at_transition,
232                                                        &QDeclarative1StateGroupPrivate::clear_transitions);
233 }
234
235 void QDeclarative1StateGroupPrivate::append_transition(QDeclarativeListProperty<QDeclarative1Transition> *list, QDeclarative1Transition *trans)
236 {
237     QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object);
238     if (trans)
239         _this->d_func()->transitions.append(trans);
240 }
241
242 int QDeclarative1StateGroupPrivate::count_transitions(QDeclarativeListProperty<QDeclarative1Transition> *list)
243 {
244     QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object);
245     return _this->d_func()->transitions.count();
246 }
247
248 QDeclarative1Transition *QDeclarative1StateGroupPrivate::at_transition(QDeclarativeListProperty<QDeclarative1Transition> *list, int index)
249 {
250     QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object);
251     return _this->d_func()->transitions.at(index);
252 }
253
254 void QDeclarative1StateGroupPrivate::clear_transitions(QDeclarativeListProperty<QDeclarative1Transition> *list)
255 {
256     QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object);
257     _this->d_func()->transitions.clear();
258 }
259
260 /*!
261   \qmlproperty string QtQuick1::StateGroup::state
262
263   This property holds the name of the current state of the state group.
264
265   This property is often used in scripts to change between states. For
266   example:
267
268   \js
269   function toggle() {
270       if (button.state == 'On')
271           button.state = 'Off';
272       else
273           button.state = 'On';
274   }
275   \endjs
276
277   If the state group is in its base state (i.e. no explicit state has been
278   set), \c state will be a blank string. Likewise, you can return a
279   state group to its base state by setting its current state to \c ''.
280
281   \sa {qmlstates}{States}
282 */
283 QString QDeclarative1StateGroup::state() const
284 {
285     Q_D(const QDeclarative1StateGroup);
286     return d->currentState;
287 }
288
289 void QDeclarative1StateGroup::setState(const QString &state)
290 {
291     Q_D(QDeclarative1StateGroup);
292     if (d->currentState == state)
293         return;
294
295     d->setCurrentStateInternal(state);
296 }
297
298 void QDeclarative1StateGroup::classBegin()
299 {
300     Q_D(QDeclarative1StateGroup);
301     d->componentComplete = false;
302 }
303
304 void QDeclarative1StateGroup::componentComplete()
305 {
306     Q_D(QDeclarative1StateGroup);
307     d->componentComplete = true;
308
309     for (int ii = 0; ii < d->states.count(); ++ii) {
310         QDeclarative1State *state = d->states.at(ii);
311         if (!state->isNamed())
312             state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount));
313     }
314
315     if (d->updateAutoState()) {
316         return;
317     } else if (!d->currentState.isEmpty()) {
318         QString cs = d->currentState;
319         d->currentState.clear();
320         d->setCurrentStateInternal(cs, true);
321     }
322 }
323
324 /*!
325     Returns true if the state was changed, otherwise false.
326 */
327 bool QDeclarative1StateGroup::updateAutoState()
328 {
329     Q_D(QDeclarative1StateGroup);
330     return d->updateAutoState();
331 }
332
333 bool QDeclarative1StateGroupPrivate::updateAutoState()
334 {
335     Q_Q(QDeclarative1StateGroup);
336     if (!componentComplete)
337         return false;
338
339     bool revert = false;
340     for (int ii = 0; ii < states.count(); ++ii) {
341         QDeclarative1State *state = states.at(ii);
342         if (state->isWhenKnown()) {
343             if (state->isNamed()) {
344                 if (state->when() && state->when()->evaluate().toBool()) {
345                     if (stateChangeDebug()) 
346                         qWarning() << "Setting auto state due to:" 
347                                    << state->when()->expression();
348                     if (currentState != state->name()) {
349                         q->setState(state->name());
350                         return true;
351                     } else {
352                         return false;
353                     }
354                 } else if (state->name() == currentState) {
355                     revert = true;
356                 }
357             }
358         }
359     }
360     if (revert) {
361         bool rv = !currentState.isEmpty();
362         q->setState(QString());
363         return rv;
364     } else {
365         return false;
366     }
367 }
368
369 QDeclarative1Transition *QDeclarative1StateGroupPrivate::findTransition(const QString &from, const QString &to)
370 {
371     QDeclarative1Transition *highest = 0;
372     int score = 0;
373     bool reversed = false;
374     bool done = false;
375
376     for (int ii = 0; !done && ii < transitions.count(); ++ii) {
377         QDeclarative1Transition *t = transitions.at(ii);
378         for (int ii = 0; ii < 2; ++ii)
379         {
380             if (ii && (!t->reversible() ||
381                       (t->fromState() == QLatin1String("*") && 
382                        t->toState() == QLatin1String("*"))))
383                 break;
384             QStringList fromState;
385             QStringList toState;
386
387             fromState = t->fromState().split(QLatin1Char(','));
388             for (int jj = 0; jj < fromState.count(); ++jj)
389                 fromState[jj] = fromState.at(jj).trimmed();
390             toState = t->toState().split(QLatin1Char(','));
391             for (int jj = 0; jj < toState.count(); ++jj)
392                 toState[jj] = toState.at(jj).trimmed();
393             if (ii == 1)
394                 qSwap(fromState, toState);
395             int tScore = 0;
396             if (fromState.contains(from))
397                 tScore += 2;
398             else if (fromState.contains(QLatin1String("*")))
399                 tScore += 1;
400             else
401                 continue;
402
403             if (toState.contains(to))
404                 tScore += 2;
405             else if (toState.contains(QLatin1String("*")))
406                 tScore += 1;
407             else
408                 continue;
409
410             if (ii == 1)
411                 reversed = true;
412             else
413                 reversed = false;
414
415             if (tScore == 4) {
416                 highest = t;
417                 done = true;
418                 break;
419             } else if (tScore > score) {
420                 score = tScore;
421                 highest = t;
422             }
423         }
424     }
425
426     if (highest)
427         highest->setReversed(reversed);
428
429     return highest;
430 }
431
432 void QDeclarative1StateGroupPrivate::setCurrentStateInternal(const QString &state, 
433                                                    bool ignoreTrans)
434 {
435     Q_Q(QDeclarative1StateGroup);
436     if (!componentComplete) {
437         currentState = state;
438         return;
439     }
440
441     if (applyingState) {
442         qmlInfo(q) << "Can't apply a state change as part of a state definition.";
443         return;
444     }
445
446     applyingState = true;
447
448     QDeclarative1Transition *transition = ignoreTrans ? 0 : findTransition(currentState, state);
449     if (stateChangeDebug()) {
450         qWarning() << this << "Changing state.  From" << currentState << ". To" << state;
451         if (transition)
452             qWarning() << "   using transition" << transition->fromState() 
453                        << transition->toState();
454     }
455
456     QDeclarative1State *oldState = 0;
457     if (!currentState.isEmpty()) {
458         for (int ii = 0; ii < states.count(); ++ii) {
459             if (states.at(ii)->name() == currentState) {
460                 oldState = states.at(ii);
461                 break;
462             }
463         }
464     }
465
466     currentState = state;
467     emit q->stateChanged(currentState);
468
469     QDeclarative1State *newState = 0;
470     for (int ii = 0; ii < states.count(); ++ii) {
471         if (states.at(ii)->name() == currentState) {
472             newState = states.at(ii);
473             break;
474         }
475     }
476
477     if (oldState == 0 || newState == 0) {
478         if (!nullState) { nullState = new QDeclarative1State; QDeclarative_setParent_noEvent(nullState, q); }
479         if (!oldState) oldState = nullState;
480         if (!newState) newState = nullState;
481     }
482
483     newState->apply(q, transition, oldState);
484     applyingState = false;
485     if (!transition)
486         static_cast<QDeclarative1StatePrivate*>(QObjectPrivate::get(newState))->complete();
487 }
488
489 QDeclarative1State *QDeclarative1StateGroup::findState(const QString &name) const
490 {
491     Q_D(const QDeclarative1StateGroup);
492     for (int i = 0; i < d->states.count(); ++i) {
493         QDeclarative1State *state = d->states.at(i);
494         if (state->name() == name)
495             return state;
496     }
497
498     return 0;
499 }
500
501 void QDeclarative1StateGroup::removeState(QDeclarative1State *state)
502 {
503     Q_D(QDeclarative1StateGroup);
504     d->states.removeOne(state);
505 }
506
507
508
509 QT_END_NAMESPACE
510
511