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