81826d704df45c71c3bbbb98eed8028280fd1a90
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlbinding.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 "qqmlbinding_p.h"
43
44 #include "qqml.h"
45 #include "qqmlcontext.h"
46 #include "qqmlinfo.h"
47 #include "qqmlcompiler_p.h"
48 #include "qqmldata_p.h"
49 #include <private/qqmlprofilerservice_p.h>
50 #include <private/qqmltrace_p.h>
51 #include <private/qqmlexpression_p.h>
52 #include <private/qqmlrewrite_p.h>
53
54 #include <QVariant>
55 #include <QtCore/qdebug.h>
56
57 QT_BEGIN_NAMESPACE
58
59 // Used in qqmlabstractbinding.cpp
60 QQmlAbstractBinding::VTable QQmlBinding_vtable = {
61     QQmlAbstractBinding::default_destroy<QQmlBinding>,
62     QQmlBinding::expression,
63     QQmlBinding::propertyIndex,
64     QQmlBinding::object,
65     QQmlBinding::setEnabled,
66     QQmlBinding::update,
67     QQmlBinding::retargetBinding
68 };
69
70 QQmlBinding::Identifier QQmlBinding::Invalid = -1;
71
72 QQmlBinding *
73 QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt,
74                                    const QString &url, int lineNumber)
75 {
76     if (id < 0)
77         return 0;
78
79     QQmlBinding *rv = 0;
80
81     QQmlContextData *ctxtdata = QQmlContextData::get(ctxt);
82     QQmlEnginePrivate *engine = QQmlEnginePrivate::get(ctxt->engine());
83     if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
84         QQmlTypeData *typeData = engine->typeLoader.getType(ctxtdata->url);
85         Q_ASSERT(typeData);
86
87         if (QQmlCompiledData *cdata = typeData->compiledData()) {
88             rv = new QQmlBinding(cdata->primitives.at(id), true, obj, ctxtdata, url, lineNumber, 0);
89         }
90
91         typeData->release();
92     }
93
94     return rv;
95 }
96
97 static QQmlJavaScriptExpression::VTable QQmlBinding_jsvtable = {
98     QQmlBinding::expressionIdentifier,
99     QQmlBinding::expressionChanged
100 };
101
102 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt)
103 : QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding),
104   m_lineNumber(-1), m_columnNumber(-1)
105 {
106     setNotifyOnValueChanged(true);
107     QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt));
108     setScopeObject(obj);
109
110     QQmlRewrite::RewriteBinding rewriteBinding;
111     QString code = rewriteBinding(str);
112
113     m_expression = str.toUtf8();
114     v8function = evalFunction(context(), obj, code, QString(), 0);
115 }
116
117 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt)
118 : QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding),
119   m_lineNumber(-1), m_columnNumber(-1)
120 {
121     setNotifyOnValueChanged(true);
122     QQmlAbstractExpression::setContext(ctxt);
123     setScopeObject(obj);
124
125     QQmlRewrite::RewriteBinding rewriteBinding;
126     QString code = rewriteBinding(str);
127
128     m_expression = str.toUtf8();
129     v8function = evalFunction(ctxt, obj, code, QString(), 0);
130 }
131
132 QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj, 
133                          QQmlContextData *ctxt,
134                          const QString &url, int lineNumber, int columnNumber)
135 : QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding),
136   m_lineNumber(-1), m_columnNumber(-1)
137 {
138     setNotifyOnValueChanged(true);
139     QQmlAbstractExpression::setContext(ctxt);
140     setScopeObject(obj);
141
142     QString code;
143     if (isRewritten) {
144         code = str;
145     } else {
146         QQmlRewrite::RewriteBinding rewriteBinding;
147         code = rewriteBinding(str);
148     }
149
150     m_url = url;
151     m_lineNumber = lineNumber;
152     m_columnNumber = columnNumber;
153     m_expression = str.toUtf8();
154
155     v8function = evalFunction(ctxt, obj, code, url, lineNumber);
156 }
157
158 /*!  
159     \internal 
160
161     To avoid exposing v8 in the public API, functionPtr must be a pointer to a v8::Handle<v8::Function>.  
162     For example:
163         v8::Handle<v8::Function> function;
164         new QQmlBinding(&function, scope, ctxt);
165  */
166 QQmlBinding::QQmlBinding(void *functionPtr, QObject *obj, QQmlContextData *ctxt,
167                          const QString &url, int lineNumber, int columnNumber)
168 : QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding),
169   m_url(url), m_lineNumber(lineNumber), m_columnNumber(columnNumber)
170 {
171     setNotifyOnValueChanged(true);
172     QQmlAbstractExpression::setContext(ctxt);
173     setScopeObject(obj);
174
175     v8function = qPersistentNew<v8::Function>(*(v8::Handle<v8::Function> *)functionPtr);
176 }
177
178 QQmlBinding::~QQmlBinding()
179 {
180     qPersistentDispose(v8function);
181 }
182
183 void QQmlBinding::setEvaluateFlags(EvaluateFlags flags)
184 {
185     setRequiresThisObject(flags & RequiresThisObject);
186 }
187
188 QQmlBinding::EvaluateFlags QQmlBinding::evaluateFlags() const
189 {
190     return requiresThisObject()?RequiresThisObject:None;
191 }
192
193 void QQmlBinding::setNotifyOnValueChanged(bool v)
194 {
195     QQmlJavaScriptExpression::setNotifyOnValueChanged(v);
196 }
197
198 void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
199 {
200     if (!enabledFlag() || !context() || !context()->isValid())
201         return;
202
203     // Check that the target has not been deleted
204     if (QQmlData::wasDeleted(object()))
205         return;
206
207     QQmlTrace trace("General Binding Update");
208     trace.addDetail("URL", m_url);
209     trace.addDetail("Line", m_lineNumber);
210     trace.addDetail("Column", m_columnNumber);
211
212     if (!updatingFlag()) {
213         QQmlBindingProfiler prof(m_url, m_lineNumber, m_columnNumber, QQmlProfilerService::QmlBinding);
214         setUpdatingFlag(true);
215
216         QQmlAbstractExpression::DeleteWatcher watcher(this);
217
218         if (m_core.propType == qMetaTypeId<QQmlBinding *>()) {
219
220             int idx = m_core.coreIndex;
221             Q_ASSERT(idx != -1);
222
223             QQmlBinding *t = this;
224             int status = -1;
225             void *a[] = { &t, 0, &status, &flags };
226             QMetaObject::metacall(*m_coreObject, QMetaObject::WriteProperty, idx, a);
227
228         } else {
229             QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine);
230             ep->referenceScarceResources(); 
231
232             bool isUndefined = false;
233
234             v8::HandleScope handle_scope;
235             v8::Context::Scope scope(ep->v8engine()->context());
236             v8::Local<v8::Value> result =
237                 QQmlJavaScriptExpression::evaluate(context(), v8function, &isUndefined);
238
239             trace.event("writing binding result");
240
241             bool needsErrorData = false;
242             if (!watcher.wasDeleted() && !hasError())
243                 needsErrorData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(),
244                                                                     this, result, isUndefined, flags);
245
246             if (!watcher.wasDeleted()) {
247                
248                 if (needsErrorData) {
249                     QUrl url = QUrl(m_url);
250
251                     delayedError()->error.setUrl(url);
252                     delayedError()->error.setLine(m_lineNumber);
253                     delayedError()->error.setColumn(m_columnNumber);
254                 }
255
256                 if (hasError()) {
257                     if (!delayedError()->addError(ep)) ep->warning(this->error());
258                 } else {
259                     clearError();
260                 }
261
262             }
263
264             ep->dereferenceScarceResources(); 
265         }
266
267         if (!watcher.wasDeleted())
268             setUpdatingFlag(false);
269     } else {
270         QQmlProperty p = property();
271         QQmlAbstractBinding::printBindingLoopError(p);
272     }
273 }
274
275 QVariant QQmlBinding::evaluate()
276 {
277     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine);
278     ep->referenceScarceResources();
279
280     bool isUndefined = false;
281
282     v8::HandleScope handle_scope;
283     v8::Context::Scope scope(ep->v8engine()->context());
284     v8::Local<v8::Value> result =
285         QQmlJavaScriptExpression::evaluate(context(), v8function, &isUndefined);
286
287     ep->dereferenceScarceResources();
288
289     return ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >());
290 }
291
292 QString QQmlBinding::expressionIdentifier(QQmlJavaScriptExpression *e)
293 {
294     QQmlBinding *This = static_cast<QQmlBinding *>(e);
295
296     return QLatin1Char('"') + QString::fromUtf8(This->m_expression) + QLatin1Char('"');
297 }
298
299 void QQmlBinding::expressionChanged(QQmlJavaScriptExpression *e)
300 {
301     QQmlBinding *This = static_cast<QQmlBinding *>(e);
302     This->update();
303 }
304
305 void QQmlBinding::refresh()
306 {
307     update();
308 }
309
310 QString QQmlBinding::expression(const QQmlAbstractBinding *This)
311 {
312     return static_cast<const QQmlBinding *>(This)->expression();
313 }
314
315 int QQmlBinding::propertyIndex(const QQmlAbstractBinding *This)
316 {
317     return static_cast<const QQmlBinding *>(This)->propertyIndex();
318 }
319
320 QObject *QQmlBinding::object(const QQmlAbstractBinding *This)
321 {
322     return static_cast<const QQmlBinding *>(This)->object();
323 }
324
325 void QQmlBinding::setEnabled(QQmlAbstractBinding *This, bool e, QQmlPropertyPrivate::WriteFlags f)
326 {
327     static_cast<QQmlBinding *>(This)->setEnabled(e, f);
328 }
329
330 void QQmlBinding::update(QQmlAbstractBinding *This , QQmlPropertyPrivate::WriteFlags f)
331 {
332     static_cast<QQmlBinding *>(This)->update(f);
333 }
334
335 void QQmlBinding::retargetBinding(QQmlAbstractBinding *This, QObject *o, int i)
336 {
337     static_cast<QQmlBinding *>(This)->retargetBinding(o, i);
338 }
339
340 void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
341 {
342     setEnabledFlag(e);
343     setNotifyOnValueChanged(e);
344
345     if (e) 
346         update(flags);
347 }
348
349 QString QQmlBinding::expression() const
350 {
351     return QString::fromUtf8(m_expression);
352 }
353
354 QObject *QQmlBinding::object() const
355 {
356     if (m_coreObject.hasValue()) return m_coreObject.constValue()->target;
357     else return *m_coreObject;
358 }
359
360 int QQmlBinding::propertyIndex() const
361 {
362     if (m_coreObject.hasValue()) return m_coreObject.constValue()->targetProperty;
363     else return m_core.encodedIndex();
364 }
365
366 void QQmlBinding::retargetBinding(QObject *t, int i)
367 {
368     m_coreObject.value().target = t;
369     m_coreObject.value().targetProperty = i;
370 }
371
372 void QQmlBinding::setTarget(const QQmlProperty &prop)
373 {
374     setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core,
375               QQmlPropertyPrivate::get(prop)->context);
376 }
377
378 void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, QQmlContextData *ctxt)
379 {
380     m_coreObject = object;
381     m_core = core;
382     m_ctxt = ctxt;
383 }
384
385 QQmlProperty QQmlBinding::property() const
386 {
387     return QQmlPropertyPrivate::restore(object(), m_core, *m_ctxt);
388 }
389
390 QT_END_NAMESPACE