Initial bundle support
[profile/ivi/qtdeclarative.git] / src / quick / util / qquicktransitionmanager.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 QtQml 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 "qquicktransitionmanager_p_p.h"
43
44 #include "qquicktransition_p.h"
45 #include "qquickstate_p_p.h"
46
47 #include <private/qqmlbinding_p.h>
48 #include <private/qqmlglobal_p.h>
49 #include <private/qqmlproperty_p.h>
50
51 #include <QtCore/qdebug.h>
52
53 QT_BEGIN_NAMESPACE
54
55 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
56
57 class QQuickTransitionManagerPrivate
58 {
59 public:
60     QQuickTransitionManagerPrivate()
61         : state(0), transitionInstance(0) {}
62
63     void applyBindings();
64     typedef QList<QQuickSimpleAction> SimpleActionList;
65     QQuickState *state;
66     QQuickTransitionInstance *transitionInstance;
67     QQuickStateOperation::ActionList bindingsList;
68     SimpleActionList completeList;
69 };
70
71 QQuickTransitionManager::QQuickTransitionManager()
72 : d(new QQuickTransitionManagerPrivate)
73 {
74 }
75
76 void QQuickTransitionManager::setState(QQuickState *s)
77 {
78     d->state = s;
79 }
80
81 QQuickTransitionManager::~QQuickTransitionManager()
82 {
83     delete d->transitionInstance;
84     delete d; d = 0;
85 }
86
87 bool QQuickTransitionManager::isRunning() const
88 {
89     return d->transitionInstance && d->transitionInstance->isRunning();
90 }
91
92 void QQuickTransitionManager::complete() 
93 {
94     d->applyBindings();
95
96     for (int ii = 0; ii < d->completeList.count(); ++ii) {
97         const QQmlProperty &prop = d->completeList.at(ii).property();
98         prop.write(d->completeList.at(ii).value());
99     }
100
101     d->completeList.clear();
102
103     if (d->state) 
104         static_cast<QQuickStatePrivate*>(QObjectPrivate::get(d->state))->complete();
105
106     finished();
107 }
108
109 void QQuickTransitionManagerPrivate::applyBindings()
110 {
111     foreach(const QQuickAction &action, bindingsList) {
112         if (!action.toBinding.isNull()) {
113             QQmlPropertyPrivate::setBinding(action.property, action.toBinding.data());
114         } else if (action.event) {
115             if (action.reverseEvent)
116                 action.event->reverse();
117             else
118                 action.event->execute();
119         }
120
121     }
122
123     bindingsList.clear();
124 }
125
126 void QQuickTransitionManager::finished()
127 {
128 }
129
130 void QQuickTransitionManager::transition(const QList<QQuickAction> &list,
131                                       QQuickTransition *transition,
132                                       QObject *defaultTarget)
133 {
134     cancel();
135
136     QQuickStateOperation::ActionList applyList = list;
137     // Determine which actions are binding changes and disable any current bindings
138     foreach(const QQuickAction &action, applyList) {
139         if (action.toBinding)
140             d->bindingsList << action;
141         if (action.fromBinding)
142             QQmlPropertyPrivate::setBinding(action.property, 0); // Disable current binding
143         if (action.event && action.event->changesBindings()) {  //### assume isReversable()?
144             d->bindingsList << action;
145             action.event->clearBindings();
146         }
147     }
148
149     // Animated transitions need both the start and the end value for
150     // each property change.  In the presence of bindings, the end values
151     // are non-trivial to calculate.  As a "best effort" attempt, we first
152     // apply all the property and binding changes, then read all the actual
153     // final values, then roll back the changes and proceed as normal.
154     //
155     // This doesn't catch everything, and it might be a little fragile in
156     // some cases - but whatcha going to do?
157     //
158     // Note that we only fast forward if both a transition and bindings are
159     // present, as it is unneccessary (and potentially expensive) otherwise.
160
161     if (transition && !d->bindingsList.isEmpty()) {
162
163         // Apply all the property and binding changes
164         for (int ii = 0; ii < applyList.size(); ++ii) {
165             const QQuickAction &action = applyList.at(ii);
166             if (!action.toBinding.isNull()) {
167                 QQmlPropertyPrivate::setBinding(action.property, action.toBinding.data(), QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
168             } else if (!action.event) {
169                 QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
170             } else if (action.event->isReversable()) {
171                 if (action.reverseEvent)
172                     action.event->reverse(QQuickActionEvent::FastForward);
173                 else
174                     action.event->execute(QQuickActionEvent::FastForward);
175             }
176         }
177
178         // Read all the end values for binding changes
179         for (int ii = 0; ii < applyList.size(); ++ii) {
180             QQuickAction *action = &applyList[ii];
181             if (action->event) {
182                 action->event->saveTargetValues();
183                 continue;
184             }
185             const QQmlProperty &prop = action->property;
186             if (!action->toBinding.isNull() || !action->toValue.isValid()) {
187                 action->toValue = prop.read();
188             }
189         }
190
191         // Revert back to the original values
192         foreach(const QQuickAction &action, applyList) {
193             if (action.event) {
194                 if (action.event->isReversable()) {
195                     action.event->clearBindings();
196                     action.event->rewind();
197                     action.event->clearBindings();  //### shouldn't be needed
198                 }
199                 continue;
200             }
201
202             if (action.toBinding)
203                 QQmlPropertyPrivate::setBinding(action.property, 0); // Make sure this is disabled during the transition
204
205             QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
206         }
207     }
208
209     if (transition) {
210         QList<QQmlProperty> touched;
211         QQuickTransitionInstance *oldInstance = d->transitionInstance;
212         d->transitionInstance = transition->prepare(applyList, touched, this, defaultTarget);
213         d->transitionInstance->start();
214         if (oldInstance && oldInstance != d->transitionInstance)
215             delete oldInstance;
216
217         // Modify the action list to remove actions handled in the transition
218         for (int ii = 0; ii < applyList.count(); ++ii) {
219             const QQuickAction &action = applyList.at(ii);
220
221             if (action.event) {
222
223                 if (action.actionDone) {
224                     applyList.removeAt(ii);
225                     --ii;
226                 }
227
228             } else {
229
230                 if (touched.contains(action.property)) {
231                     if (action.toValue != action.fromValue) 
232                         d->completeList << 
233                             QQuickSimpleAction(action, QQuickSimpleAction::EndState);
234
235                     applyList.removeAt(ii);
236                     --ii;
237                 }
238
239             }
240         }
241     }
242
243     // Any actions remaining have not been handled by the transition and should
244     // be applied immediately.  We skip applying bindings, as they are all
245     // applied at the end in applyBindings() to avoid any nastiness mid 
246     // transition
247     foreach(const QQuickAction &action, applyList) {
248         if (action.event && !action.event->changesBindings()) {
249             if (action.event->isReversable() && action.reverseEvent)
250                 action.event->reverse();
251             else
252                 action.event->execute();
253         } else if (!action.event && !action.toBinding) {
254             action.property.write(action.toValue);
255         }
256     }
257 #ifndef QT_NO_DEBUG_STREAM
258     if (stateChangeDebug()) {
259         foreach(const QQuickAction &action, applyList) {
260             if (action.event)
261                 qWarning() << "    No transition for event:" << action.event->type();
262             else
263                 qWarning() << "    No transition for:" << action.property.object()
264                            << action.property.name() << "From:" << action.fromValue 
265                            << "To:" << action.toValue;
266         }
267     }
268 #endif
269     if (!transition)
270         complete();
271 }
272
273 void QQuickTransitionManager::cancel()
274 {
275     if (d->transitionInstance && d->transitionInstance->isRunning())
276         d->transitionInstance->stop();
277
278     for(int i = 0; i < d->bindingsList.count(); ++i) {
279         QQuickAction action = d->bindingsList[i];
280         if (!action.toBinding.isNull() && action.deletableToBinding) {
281             QQmlPropertyPrivate::setBinding(action.property, 0);
282             action.toBinding.data()->destroy();
283             action.toBinding.clear();
284             action.deletableToBinding = false;
285         } else if (action.event) {
286             //### what do we do here?
287         }
288
289     }
290     d->bindingsList.clear();
291     d->completeList.clear();
292 }
293
294 QT_END_NAMESPACE