Improve documentation.
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlbinding.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 #include <private/qqmlscriptstring_p.h>
54
55 #include <QVariant>
56 #include <QtCore/qdebug.h>
57
58 QT_BEGIN_NAMESPACE
59
60 // Used in qqmlabstractbinding.cpp
61 QQmlAbstractBinding::VTable QQmlBinding_vtable = {
62     QQmlAbstractBinding::default_destroy<QQmlBinding>,
63     QQmlBinding::expression,
64     QQmlBinding::propertyIndex,
65     QQmlBinding::object,
66     QQmlBinding::setEnabled,
67     QQmlBinding::update,
68     QQmlBinding::retargetBinding
69 };
70
71 QQmlBinding::Identifier QQmlBinding::Invalid = -1;
72
73 QQmlBinding *
74 QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt,
75                                    const QString &url, quint16 lineNumber)
76 {
77     if (id < 0)
78         return 0;
79
80     QQmlBinding *rv = 0;
81
82     QQmlContextData *ctxtdata = QQmlContextData::get(ctxt);
83     QQmlEnginePrivate *engine = QQmlEnginePrivate::get(ctxt->engine());
84     if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
85         QQmlTypeData *typeData = engine->typeLoader.getType(ctxtdata->url);
86         Q_ASSERT(typeData);
87
88         if (QQmlCompiledData *cdata = typeData->compiledData()) {
89             rv = new QQmlBinding(cdata->primitives.at(id), true, obj, ctxtdata, url, lineNumber, 0);
90         }
91
92         typeData->release();
93     }
94
95     return rv;
96 }
97
98 static QQmlJavaScriptExpression::VTable QQmlBinding_jsvtable = {
99     QQmlBinding::expressionIdentifier,
100     QQmlBinding::expressionChanged
101 };
102
103 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt)
104 : QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding),
105   m_lineNumber(0), m_columnNumber(0)
106 {
107     setNotifyOnValueChanged(true);
108     QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt));
109     setScopeObject(obj);
110
111     QQmlRewrite::RewriteBinding rewriteBinding;
112     QString code = rewriteBinding(str);
113
114     m_expression = str.toUtf8();
115     v8function = evalFunction(context(), obj, code, QString(), 0);
116 }
117
118 QQmlBinding::QQmlBinding(const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
119 : QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding)
120 {
121     if (ctxt && !ctxt->isValid())
122         return;
123
124     const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
125     if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
126         return;
127
128     bool needRewrite = true;
129     QString code;
130
131     int id = scriptPrivate->bindingId;
132     if (id >= 0) {
133         QQmlContextData *ctxtdata = QQmlContextData::get(scriptPrivate->context);
134         QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
135         if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
136             QQmlTypeData *typeData = engine->typeLoader.getType(ctxtdata->url);
137             Q_ASSERT(typeData);
138
139             if (QQmlCompiledData *cdata = typeData->compiledData()) {
140                 needRewrite = false;
141                 code = cdata->primitives.at(id);
142                 m_url = cdata->name;
143             }
144
145             typeData->release();
146         }
147     }
148
149     if (needRewrite) {
150         QQmlRewrite::RewriteBinding rewriteBinding;
151         code = rewriteBinding(scriptPrivate->script);
152     }
153
154     setNotifyOnValueChanged(true);
155     QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context));
156     setScopeObject(obj ? obj : scriptPrivate->scope);
157
158     m_expression = scriptPrivate->script.toUtf8();
159     m_lineNumber = scriptPrivate->lineNumber;
160     m_columnNumber = scriptPrivate->columnNumber;
161
162     v8function = evalFunction(context(), scopeObject(), code, QString(), m_lineNumber);
163 }
164
165 QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt)
166 : QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding),
167   m_lineNumber(0), m_columnNumber(0)
168 {
169     setNotifyOnValueChanged(true);
170     QQmlAbstractExpression::setContext(ctxt);
171     setScopeObject(obj);
172
173     QQmlRewrite::RewriteBinding rewriteBinding;
174     QString code = rewriteBinding(str);
175
176     m_expression = str.toUtf8();
177     v8function = evalFunction(ctxt, obj, code, QString(), 0);
178 }
179
180 QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj,
181                          QQmlContextData *ctxt,
182                          const QString &url, quint16 lineNumber, quint16 columnNumber)
183 : QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding),
184   m_url(url), m_lineNumber(lineNumber), m_columnNumber(columnNumber)
185 {
186     setNotifyOnValueChanged(true);
187     QQmlAbstractExpression::setContext(ctxt);
188     setScopeObject(obj);
189
190     QString code;
191     if (isRewritten) {
192         code = str;
193     } else {
194         QQmlRewrite::RewriteBinding rewriteBinding;
195         code = rewriteBinding(str);
196     }
197
198     m_expression = str.toUtf8();
199
200     v8function = evalFunction(ctxt, obj, code, url, m_lineNumber);
201 }
202
203 /*!
204     \internal
205
206     To avoid exposing v8 in the public API, functionPtr must be a pointer to a v8::Handle<v8::Function>.
207     For example:
208         v8::Handle<v8::Function> function;
209         new QQmlBinding(&function, scope, ctxt);
210  */
211 QQmlBinding::QQmlBinding(void *functionPtr, QObject *obj, QQmlContextData *ctxt,
212                          const QString &url, quint16 lineNumber, quint16 columnNumber)
213 : QQmlJavaScriptExpression(&QQmlBinding_jsvtable), QQmlAbstractBinding(Binding),
214   m_url(url), m_lineNumber(lineNumber), m_columnNumber(columnNumber)
215 {
216     setNotifyOnValueChanged(true);
217     QQmlAbstractExpression::setContext(ctxt);
218     setScopeObject(obj);
219
220     v8function = qPersistentNew<v8::Function>(*(v8::Handle<v8::Function> *)functionPtr);
221 }
222
223 QQmlBinding::~QQmlBinding()
224 {
225     qPersistentDispose(v8function);
226 }
227
228 void QQmlBinding::setEvaluateFlags(EvaluateFlags flags)
229 {
230     setRequiresThisObject(flags & RequiresThisObject);
231 }
232
233 QQmlBinding::EvaluateFlags QQmlBinding::evaluateFlags() const
234 {
235     return requiresThisObject()?RequiresThisObject:None;
236 }
237
238 void QQmlBinding::setNotifyOnValueChanged(bool v)
239 {
240     QQmlJavaScriptExpression::setNotifyOnValueChanged(v);
241 }
242
243 void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
244 {
245     if (!enabledFlag() || !context() || !context()->isValid())
246         return;
247
248     // Check that the target has not been deleted
249     if (QQmlData::wasDeleted(object()))
250         return;
251
252     int lineNo = qmlSourceCoordinate(m_lineNumber);
253     int columnNo = qmlSourceCoordinate(m_columnNumber);
254
255     QQmlTrace trace("General Binding Update");
256     trace.addDetail("URL", m_url);
257     trace.addDetail("Line", lineNo);
258     trace.addDetail("Column", columnNo);
259
260     if (!updatingFlag()) {
261         QQmlBindingProfiler prof(m_url, lineNo, columnNo, QQmlProfilerService::QmlBinding);
262         setUpdatingFlag(true);
263
264         QQmlAbstractExpression::DeleteWatcher watcher(this);
265
266         if (m_core.propType == qMetaTypeId<QQmlBinding *>()) {
267
268             int idx = m_core.coreIndex;
269             Q_ASSERT(idx != -1);
270
271             QQmlBinding *t = this;
272             int status = -1;
273             void *a[] = { &t, 0, &status, &flags };
274             QMetaObject::metacall(*m_coreObject, QMetaObject::WriteProperty, idx, a);
275
276         } else {
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             trace.event("writing binding result");
288
289             bool needsErrorLocationData = false;
290             if (!watcher.wasDeleted() && !hasError())
291                 needsErrorLocationData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(),
292                                                                     this, result, isUndefined, flags);
293
294             if (!watcher.wasDeleted()) {
295
296                 if (needsErrorLocationData)
297                     delayedError()->setErrorLocation(QUrl(m_url), m_lineNumber, m_columnNumber);
298
299                 if (hasError()) {
300                     if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine));
301                 } else {
302                     clearError();
303                 }
304
305             }
306
307             ep->dereferenceScarceResources();
308         }
309
310         if (!watcher.wasDeleted())
311             setUpdatingFlag(false);
312     } else {
313         QQmlProperty p = property();
314         QQmlAbstractBinding::printBindingLoopError(p);
315     }
316 }
317
318 QVariant QQmlBinding::evaluate()
319 {
320     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine);
321     ep->referenceScarceResources();
322
323     bool isUndefined = false;
324
325     v8::HandleScope handle_scope;
326     v8::Context::Scope scope(ep->v8engine()->context());
327     v8::Local<v8::Value> result =
328         QQmlJavaScriptExpression::evaluate(context(), v8function, &isUndefined);
329
330     ep->dereferenceScarceResources();
331
332     return ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >());
333 }
334
335 QString QQmlBinding::expressionIdentifier(QQmlJavaScriptExpression *e)
336 {
337     QQmlBinding *This = static_cast<QQmlBinding *>(e);
338
339     return QLatin1Char('"') + QString::fromUtf8(This->m_expression) + QLatin1Char('"');
340 }
341
342 void QQmlBinding::expressionChanged(QQmlJavaScriptExpression *e)
343 {
344     QQmlBinding *This = static_cast<QQmlBinding *>(e);
345     This->update();
346 }
347
348 void QQmlBinding::refresh()
349 {
350     update();
351 }
352
353 QString QQmlBinding::expression(const QQmlAbstractBinding *This)
354 {
355     return static_cast<const QQmlBinding *>(This)->expression();
356 }
357
358 int QQmlBinding::propertyIndex(const QQmlAbstractBinding *This)
359 {
360     return static_cast<const QQmlBinding *>(This)->propertyIndex();
361 }
362
363 QObject *QQmlBinding::object(const QQmlAbstractBinding *This)
364 {
365     return static_cast<const QQmlBinding *>(This)->object();
366 }
367
368 void QQmlBinding::setEnabled(QQmlAbstractBinding *This, bool e, QQmlPropertyPrivate::WriteFlags f)
369 {
370     static_cast<QQmlBinding *>(This)->setEnabled(e, f);
371 }
372
373 void QQmlBinding::update(QQmlAbstractBinding *This , QQmlPropertyPrivate::WriteFlags f)
374 {
375     static_cast<QQmlBinding *>(This)->update(f);
376 }
377
378 void QQmlBinding::retargetBinding(QQmlAbstractBinding *This, QObject *o, int i)
379 {
380     static_cast<QQmlBinding *>(This)->retargetBinding(o, i);
381 }
382
383 void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags)
384 {
385     setEnabledFlag(e);
386     setNotifyOnValueChanged(e);
387
388     if (e)
389         update(flags);
390 }
391
392 QString QQmlBinding::expression() const
393 {
394     return QString::fromUtf8(m_expression);
395 }
396
397 QObject *QQmlBinding::object() const
398 {
399     if (m_coreObject.hasValue()) return m_coreObject.constValue()->target;
400     else return *m_coreObject;
401 }
402
403 int QQmlBinding::propertyIndex() const
404 {
405     if (m_coreObject.hasValue()) return m_coreObject.constValue()->targetProperty;
406     else return m_core.encodedIndex();
407 }
408
409 void QQmlBinding::retargetBinding(QObject *t, int i)
410 {
411     m_coreObject.value().target = t;
412     m_coreObject.value().targetProperty = i;
413 }
414
415 void QQmlBinding::setTarget(const QQmlProperty &prop)
416 {
417     setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core,
418               QQmlPropertyPrivate::get(prop)->context);
419 }
420
421 void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, QQmlContextData *ctxt)
422 {
423     m_coreObject = object;
424     m_core = core;
425     m_ctxt = ctxt;
426 }
427
428 QQmlProperty QQmlBinding::property() const
429 {
430     return QQmlPropertyPrivate::restore(object(), m_core, *m_ctxt);
431 }
432
433 QT_END_NAMESPACE